primo commit
This commit is contained in:
915
libraries/fof30/Utils/FEFHelper/BrowseView.php
Normal file
915
libraries/fof30/Utils/FEFHelper/BrowseView.php
Normal file
@ -0,0 +1,915 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FOF
|
||||
* @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd
|
||||
* @license GNU General Public License version 2, or later
|
||||
*/
|
||||
|
||||
namespace FOF30\Utils\FEFHelper;
|
||||
|
||||
defined('_JEXEC') || die;
|
||||
|
||||
use Exception;
|
||||
use FOF30\Container\Container;
|
||||
use FOF30\Model\DataModel;
|
||||
use FOF30\Utils\ArrayHelper;
|
||||
use FOF30\Utils\SelectOptions;
|
||||
use FOF30\View\DataView\DataViewInterface;
|
||||
use FOF30\View\View;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* An HTML helper for Browse views.
|
||||
*
|
||||
* It reintroduces a FEF-friendly of some of the functionality found in FOF 3's Header and Field classes. These
|
||||
* helpers are also accessible through Blade, making the transition from XML forms to Blade templates easier.
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
abstract class BrowseView
|
||||
{
|
||||
/**
|
||||
* Caches the results of getOptionsFromModel keyed by a hash. The hash is computed by the model
|
||||
* name, the model state and the options passed to getOptionsFromModel.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $cacheModelOptions = [];
|
||||
|
||||
/**
|
||||
* Get the translation key for a field's label
|
||||
*
|
||||
* @param string $fieldName The field name
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function fieldLabelKey($fieldName)
|
||||
{
|
||||
$view = self::getViewFromBacktrace();
|
||||
|
||||
try
|
||||
{
|
||||
$inflector = $view->getContainer()->inflector;
|
||||
$viewName = $inflector->singularize($view->getName());
|
||||
$altViewName = $inflector->pluralize($view->getName());
|
||||
$componentName = $view->getContainer()->componentName;
|
||||
|
||||
$keys = [
|
||||
strtoupper($componentName . '_' . $viewName . '_FIELD_' . $fieldName),
|
||||
strtoupper($componentName . '_' . $altViewName . '_FIELD_' . $fieldName),
|
||||
strtoupper($componentName . '_' . $viewName . '_' . $fieldName),
|
||||
strtoupper($componentName . '_' . $altViewName . '_' . $fieldName),
|
||||
];
|
||||
|
||||
foreach ($keys as $key)
|
||||
{
|
||||
if (Text::_($key) != $key)
|
||||
{
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
|
||||
return $keys[0];
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
return ucfirst($fieldName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the label for a field (translated)
|
||||
*
|
||||
* @param string $fieldName The field name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function fieldLabel($fieldName)
|
||||
{
|
||||
return Text::_(self::fieldLabelKey($fieldName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a table field header which sorts the table by that field upon clicking
|
||||
*
|
||||
* @param string $field The name of the field
|
||||
* @param string|null $langKey (optional) The language key for the header to be displayed
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function sortgrid($field, $langKey = null)
|
||||
{
|
||||
/** @var DataViewInterface $view */
|
||||
$view = self::getViewFromBacktrace();
|
||||
|
||||
if (is_null($langKey))
|
||||
{
|
||||
$langKey = self::fieldLabelKey($field);
|
||||
}
|
||||
|
||||
return HTMLHelper::_('FEFHelper.browse.sort', $langKey, $field, $view->getLists()->order_Dir, $view->getLists()->order, $view->getTask());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a browse view filter from values returned by a model
|
||||
*
|
||||
* @param string $localField Field name
|
||||
* @param string $modelTitleField Foreign model field for drop-down display values
|
||||
* @param null $modelName Foreign model name
|
||||
* @param string $placeholder Placeholder for no selection
|
||||
* @param array $params Generic select display parameters
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function modelFilter($localField, $modelTitleField = 'title', $modelName = null, $placeholder = null, array $params = [])
|
||||
{
|
||||
/** @var DataModel $model */
|
||||
$model = self::getViewFromBacktrace()->getModel();
|
||||
|
||||
if (empty($modelName))
|
||||
{
|
||||
$modelName = $model->getForeignModelNameFor($localField);
|
||||
}
|
||||
|
||||
if (is_null($placeholder))
|
||||
{
|
||||
$placeholder = self::fieldLabelKey($localField);
|
||||
}
|
||||
|
||||
$params = array_merge([
|
||||
'list.none' => '— ' . Text::_($placeholder) . ' —',
|
||||
'value_field' => $modelTitleField,
|
||||
'fof.autosubmit' => true,
|
||||
], $params);
|
||||
|
||||
return self::modelSelect($localField, $modelName, $model->getState($localField), $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a text filter (search box)
|
||||
*
|
||||
* @param string $localField The name of the model field. Used when getting the filter state.
|
||||
* @param string $searchField The INPUT element's name. Default: "filter_$localField".
|
||||
* @param string $placeholder The JText language key for the placeholder. Default: extrapolate from $localField.
|
||||
* @param array $attributes HTML attributes for the INPUT element.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function searchFilter($localField, $searchField = null, $placeholder = null, array $attributes = [])
|
||||
{
|
||||
/** @var DataModel $model */
|
||||
$view = self::getViewFromBacktrace();
|
||||
$model = $view->getModel();
|
||||
$searchField = empty($searchField) ? $localField : $searchField;
|
||||
$placeholder = empty($placeholder) ? self::fieldLabelKey($localField) : $placeholder;
|
||||
$attributes['type'] = $attributes['type'] ?? 'text';
|
||||
$attributes['name'] = $searchField;
|
||||
$attributes['id'] = !isset($attributes['id']) ? "filter_$localField" : $attributes['id'];
|
||||
$attributes['onchange'] = !isset($attributes['onchange']) ? 'document.adminForm.submit()' : null;
|
||||
$attributes['placeholder'] = !isset($attributes['placeholder']) ? $view->escape(Text::_($placeholder)) : $attributes['placeholder'];
|
||||
$attributes['title'] = $attributes['title'] ?? $attributes['placeholder'];
|
||||
$attributes['value'] = $view->escape($model->getState($localField));
|
||||
|
||||
// Remove null attributes and collapse into a string
|
||||
$attributes = array_filter($attributes, function ($v) {
|
||||
return !is_null($v);
|
||||
});
|
||||
$attributes = ArrayHelper::toString($attributes);
|
||||
|
||||
return "<input $attributes />";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a browse view filter with dropdown values
|
||||
*
|
||||
* @param string $localField Field name
|
||||
* @param array $options The JHtml options list to use
|
||||
* @param string $placeholder Placeholder for no selection
|
||||
* @param array $params Generic select display parameters
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function selectFilter($localField, array $options, $placeholder = null, array $params = [])
|
||||
{
|
||||
/** @var DataModel $model */
|
||||
$model = self::getViewFromBacktrace()->getModel();
|
||||
|
||||
if (is_null($placeholder))
|
||||
{
|
||||
$placeholder = self::fieldLabelKey($localField);
|
||||
}
|
||||
|
||||
$params = array_merge([
|
||||
'list.none' => '— ' . Text::_($placeholder) . ' —',
|
||||
'fof.autosubmit' => true,
|
||||
], $params);
|
||||
|
||||
return self::genericSelect($localField, $options, $model->getState($localField), $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* View access dropdown filter
|
||||
*
|
||||
* @param string $localField Field name
|
||||
* @param string $placeholder Placeholder for no selection
|
||||
* @param array $params Generic select display parameters
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function accessFilter($localField, $placeholder = null, array $params = [])
|
||||
{
|
||||
return self::selectFilter($localField, SelectOptions::getOptions('access', $params), $placeholder, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Published state dropdown filter
|
||||
*
|
||||
* @param string $localField Field name
|
||||
* @param string $placeholder Placeholder for no selection
|
||||
* @param array $params Generic select display parameters
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function publishedFilter($localField, $placeholder = null, array $params = [])
|
||||
{
|
||||
return self::selectFilter($localField, SelectOptions::getOptions('published', $params), $placeholder, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a select box from the values returned by a model
|
||||
*
|
||||
* @param string $name Field name
|
||||
* @param string $modelName The name of the model, e.g. "items" or "com_foobar.items"
|
||||
* @param string $currentValue The currently selected value
|
||||
* @param array $params Passed to optionsFromModel and genericSelect
|
||||
* @param array $modelState Optional state variables to pass to the model
|
||||
* @param array $options Any JHtml select options you want to add in front of the model's returned values
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @see self::getOptionsFromModel
|
||||
* @see self::getOptionsFromSource
|
||||
* @see self::genericSelect
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function modelSelect($name, $modelName, $currentValue, array $params = [], array $modelState = [], array $options = [])
|
||||
{
|
||||
$params = array_merge([
|
||||
'fof.autosubmit' => true,
|
||||
], $params);
|
||||
|
||||
$options = self::getOptionsFromModel($modelName, $params, $modelState, $options);
|
||||
|
||||
return self::genericSelect($name, $options, $currentValue, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a (human readable) title from a (typically numeric, foreign key) key value using the data
|
||||
* returned by a DataModel.
|
||||
*
|
||||
* @param string $value The key value
|
||||
* @param string $modelName The name of the model, e.g. "items" or "com_foobar.items"
|
||||
* @param array $params Passed to getOptionsFromModel
|
||||
* @param array $modelState Optional state variables to pass to the model
|
||||
* @param array $options Any JHtml select options you want to add in front of the model's returned values
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @see self::getOptionsFromModel
|
||||
* @see self::getOptionsFromSource
|
||||
* @see self::genericSelect
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function modelOptionName($value, $modelName = null, array $params = [], array $modelState = [], array $options = [])
|
||||
{
|
||||
if (!isset($params['cache']))
|
||||
{
|
||||
$params['cache'] = true;
|
||||
}
|
||||
|
||||
if (!isset($params['none_as_zero']))
|
||||
{
|
||||
$params['none_as_zero'] = true;
|
||||
}
|
||||
|
||||
$options = self::getOptionsFromModel($modelName, $params, $modelState, $options);
|
||||
|
||||
return self::getOptionName($value, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the active option's label given an array of JHtml options
|
||||
*
|
||||
* @param mixed $selected The currently selected value
|
||||
* @param array $data The JHtml options to parse
|
||||
* @param string $optKey Key name, default: value
|
||||
* @param string $optText Value name, default: text
|
||||
* @param bool $selectFirst Should I automatically select the first option? Default: true
|
||||
*
|
||||
* @return mixed The label of the currently selected option
|
||||
*/
|
||||
public static function getOptionName($selected, $data, $optKey = 'value', $optText = 'text', $selectFirst = true)
|
||||
{
|
||||
$ret = null;
|
||||
|
||||
foreach ($data as $elementKey => &$element)
|
||||
{
|
||||
if (is_array($element))
|
||||
{
|
||||
$key = $optKey === null ? $elementKey : $element[$optKey];
|
||||
$text = $element[$optText];
|
||||
}
|
||||
elseif (is_object($element))
|
||||
{
|
||||
$key = $optKey === null ? $elementKey : $element->$optKey;
|
||||
$text = $element->$optText;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is a simple associative array
|
||||
$key = $elementKey;
|
||||
$text = $element;
|
||||
}
|
||||
|
||||
if (is_null($ret) && $selectFirst && ($selected == $key))
|
||||
{
|
||||
$ret = $text;
|
||||
}
|
||||
elseif ($selected == $key)
|
||||
{
|
||||
$ret = $text;
|
||||
}
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a generic select list based on a bunch of options. Option sources will be merged into the provided
|
||||
* options automatically.
|
||||
*
|
||||
* Parameters:
|
||||
* - format.depth The current indent depth.
|
||||
* - format.eol The end of line string, default is linefeed.
|
||||
* - format.indent The string to use for indentation, default is tab.
|
||||
* - groups If set, looks for keys with the value "<optgroup>" and synthesizes groups from them. Deprecated.
|
||||
* Default: true.
|
||||
* - list.select Either the value of one selected option or an array of selected options. Default: $currentValue.
|
||||
* - list.translate If true, text and labels are translated via JText::_(). Default is false.
|
||||
* - list.attr HTML element attributes (key/value array or string)
|
||||
* - list.none Placeholder for no selection (creates an option with an empty string key)
|
||||
* - option.id The property in each option array to use as the selection id attribute. Defaults: null.
|
||||
* - option.key The property in each option array to use as the Default: "value". If set to null, the index of the
|
||||
* option array is used.
|
||||
* - option.label The property in each option array to use as the selection label attribute. Default: null
|
||||
* - option.text The property in each option array to use as the displayed text. Default: "text". If set to null,
|
||||
* the option array is assumed to be a list of displayable scalars.
|
||||
* - option.attr The property in each option array to use for additional selection attributes. Defaults: null.
|
||||
* - option.disable: The property that will hold the disabled state. Defaults to "disable".
|
||||
* - fof.autosubmit Should I auto-submit the form on change? Default: true
|
||||
* - fof.formname Form to auto-submit. Default: adminForm
|
||||
* - class CSS class to apply
|
||||
* - size Size attribute for the input
|
||||
* - multiple Is this a multiple select? Default: false.
|
||||
* - required Is this a required field? Default: false.
|
||||
* - autofocus Should I focus this field automatically? Default: false
|
||||
* - disabled Is this a disabled field? Default: false
|
||||
* - readonly Render as a readonly field with hidden inputs? Overrides 'disabled'. Default: false
|
||||
* - onchange Custom onchange handler. Overrides fof.autosubmit. Default: NULL (use fof.autosubmit).
|
||||
*
|
||||
* @param $name
|
||||
* @param array $options
|
||||
* @param $currentValue
|
||||
* @param array $params
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function genericSelect($name, array $options, $currentValue, array $params = [])
|
||||
{
|
||||
$params = array_merge([
|
||||
'format.depth' => 0,
|
||||
'format.eol' => "\n",
|
||||
'format.indent' => "\t",
|
||||
'groups' => true,
|
||||
'list.select' => $currentValue,
|
||||
'list.translate' => false,
|
||||
'option.id' => null,
|
||||
'option.key' => 'value',
|
||||
'option.label' => null,
|
||||
'option.text' => 'text',
|
||||
'option.attr' => null,
|
||||
'option.disable' => 'disable',
|
||||
'list.attr' => '',
|
||||
'list.none' => '',
|
||||
'id' => null,
|
||||
'fof.autosubmit' => true,
|
||||
'fof.formname' => 'adminForm',
|
||||
'class' => '',
|
||||
'size' => '',
|
||||
'multiple' => false,
|
||||
'required' => false,
|
||||
'autofocus' => false,
|
||||
'disabled' => false,
|
||||
'onchange' => null,
|
||||
'readonly' => false,
|
||||
], $params);
|
||||
|
||||
$currentValue = $params['list.select'];
|
||||
|
||||
// If fof.autosubmit is enabled and onchange is not set we will add our own handler
|
||||
if ($params['fof.autosubmit'] && is_null($params['onchange']))
|
||||
{
|
||||
$formName = $params['fof.formname'] ?: 'adminForm';
|
||||
$params['onchange'] = "document.{$formName}.submit()";
|
||||
}
|
||||
|
||||
// Construct SELECT element's attributes
|
||||
$attr = '';
|
||||
$attr .= $params['class'] ? ' class="' . $params['class'] . '"' : '';
|
||||
$attr .= !empty($params['size']) ? ' size="' . $params['size'] . '"' : '';
|
||||
$attr .= $params['multiple'] ? ' multiple' : '';
|
||||
$attr .= $params['required'] ? ' required aria-required="true"' : '';
|
||||
$attr .= $params['autofocus'] ? ' autofocus' : '';
|
||||
$attr .= ($params['disabled'] || $params['readonly']) ? ' disabled="disabled"' : '';
|
||||
$attr .= $params['onchange'] ? ' onchange="' . $params['onchange'] . '"' : '';
|
||||
|
||||
// We use the constructed SELECT element's attributes only if no 'attr' key was provided
|
||||
if (empty($params['list.attr']))
|
||||
{
|
||||
$params['list.attr'] = $attr;
|
||||
}
|
||||
|
||||
// Merge the options with those fetched from a source (e.g. another Helper object)
|
||||
$options = array_merge($options, self::getOptionsFromSource($params));
|
||||
|
||||
if (!empty($params['list.none']))
|
||||
{
|
||||
array_unshift($options, HTMLHelper::_('FEFHelper.select.option', '', Text::_($params['list.none'])));
|
||||
}
|
||||
|
||||
$html = [];
|
||||
|
||||
// Create a read-only list (no name) with hidden input(s) to store the value(s).
|
||||
if ($params['readonly'])
|
||||
{
|
||||
$html[] = HTMLHelper::_('FEFHelper.select.genericlist', $options, $name, $params);
|
||||
|
||||
// E.g. form field type tag sends $this->value as array
|
||||
if ($params['multiple'] && is_array($currentValue))
|
||||
{
|
||||
if (!count($currentValue))
|
||||
{
|
||||
$currentValue[] = '';
|
||||
}
|
||||
|
||||
foreach ($currentValue as $value)
|
||||
{
|
||||
$html[] = '<input type="hidden" name="' . $name . '" value="' . htmlspecialchars($value, ENT_COMPAT, 'UTF-8') . '"/>';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$html[] = '<input type="hidden" name="' . $name . '" value="' . htmlspecialchars($value, ENT_COMPAT, 'UTF-8') . '"/>';
|
||||
}
|
||||
}
|
||||
else
|
||||
// Create a regular list.
|
||||
{
|
||||
$html[] = HTMLHelper::_('FEFHelper.select.genericlist', $options, $name, $params);
|
||||
}
|
||||
|
||||
return implode($html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace tags that reference fields with their values
|
||||
*
|
||||
* @param string $text Text to process
|
||||
* @param DataModel $item The DataModel instance to get values from
|
||||
*
|
||||
* @return string Text with tags replace
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function parseFieldTags($text, DataModel $item)
|
||||
{
|
||||
$ret = $text;
|
||||
|
||||
if (empty($item))
|
||||
{
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace [ITEM:ID] in the URL with the item's key value (usually: the auto-incrementing numeric ID)
|
||||
*/
|
||||
$replace = $item->getId();
|
||||
$ret = str_replace('[ITEM:ID]', $replace, $ret);
|
||||
|
||||
// Replace the [ITEMID] in the URL with the current Itemid parameter
|
||||
$ret = str_replace('[ITEMID]', $item->getContainer()->input->getInt('Itemid', 0), $ret);
|
||||
|
||||
// Replace the [TOKEN] in the URL with the Joomla! form token
|
||||
$ret = str_replace('[TOKEN]', $item->getContainer()->platform->getToken(true), $ret);
|
||||
|
||||
// Replace other field variables in the URL
|
||||
$data = $item->getData();
|
||||
|
||||
foreach ($data as $field => $value)
|
||||
{
|
||||
// Skip non-processable values
|
||||
if (is_array($value) || is_object($value))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$search = '[ITEM:' . strtoupper($field) . ']';
|
||||
$ret = str_replace($search, $value, $ret);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the FOF View from the backtrace of the static call. MAGIC!
|
||||
*
|
||||
* @return View
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function getViewFromBacktrace()
|
||||
{
|
||||
// In case we are on a braindead host
|
||||
if (!function_exists('debug_backtrace'))
|
||||
{
|
||||
throw new RuntimeException("Your host has disabled the <code>debug_backtrace</code> PHP function. Please ask them to re-enable it. It's required for running this software.");
|
||||
}
|
||||
|
||||
/**
|
||||
* For performance reasons I look into the last 4 call stack entries. If I don't find a container I
|
||||
* will expand my search by another 2 entries and so on until I either find a container or I stop
|
||||
* finding new call stack entries.
|
||||
*/
|
||||
$lastNumberOfEntries = 0;
|
||||
$limit = 4;
|
||||
$skip = 0;
|
||||
$container = null;
|
||||
|
||||
while (true)
|
||||
{
|
||||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, $limit);
|
||||
|
||||
if (count($backtrace) == $lastNumberOfEntries)
|
||||
{
|
||||
throw new RuntimeException(__METHOD__ . ": Cannot retrieve FOF View from call stack. You are either calling me from a non-FEF extension or your PHP is broken.");
|
||||
}
|
||||
|
||||
$lastNumberOfEntries = count($backtrace);
|
||||
|
||||
if ($skip)
|
||||
{
|
||||
$backtrace = array_slice($backtrace, $skip);
|
||||
}
|
||||
|
||||
foreach ($backtrace as $bt)
|
||||
{
|
||||
if (!isset($bt['object']))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($bt['object'] instanceof View)
|
||||
{
|
||||
return $bt['object'];
|
||||
}
|
||||
}
|
||||
|
||||
$skip = $limit;
|
||||
$limit += 2;
|
||||
}
|
||||
|
||||
throw new RuntimeException(__METHOD__ . ": Cannot retrieve FOF View from call stack. You are either calling me from a non-FEF extension or your PHP is broken.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get JHtml options from an alternate source, e.g. a helper. This is useful for adding arbitrary options
|
||||
* which are either dynamic or you do not want to inline to your view, e.g. reusable options across
|
||||
* different views.
|
||||
*
|
||||
* The attribs can be:
|
||||
* source_file The file to load. You can use FOF's URIs such as 'admin:com_foobar/foo/bar'
|
||||
* source_class The class to use
|
||||
* source_method The static method to use on source_class
|
||||
* source_key Use * if you're returning a key/value array. Otherwise the array key for the key (ID)
|
||||
* value.
|
||||
* source_value Use * if you're returning a key/value array. Otherwise the array key for the displayed
|
||||
* value. source_translate Should I pass the value field through JText? Default: true source_format Set
|
||||
* to "optionsobject" if you're returning an array of JHtml options. Ignored otherwise.
|
||||
*
|
||||
* @param array $attribs
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
private static function getOptionsFromSource(array $attribs = [])
|
||||
{
|
||||
$options = [];
|
||||
|
||||
$container = self::getContainerFromBacktrace();
|
||||
|
||||
$attribs = array_merge([
|
||||
'source_file' => '',
|
||||
'source_class' => '',
|
||||
'source_method' => '',
|
||||
'source_key' => '*',
|
||||
'source_value' => '*',
|
||||
'source_translate' => true,
|
||||
'source_format' => '',
|
||||
], $attribs);
|
||||
|
||||
$source_file = $attribs['source_file'];
|
||||
$source_class = $attribs['source_class'];
|
||||
$source_method = $attribs['source_method'];
|
||||
$source_key = $attribs['source_key'];
|
||||
$source_value = $attribs['source_value'];
|
||||
$source_translate = $attribs['source_translate'];
|
||||
$source_format = $attribs['source_format'];
|
||||
|
||||
if ($source_class && $source_method)
|
||||
{
|
||||
// Maybe we have to load a file?
|
||||
if (!empty($source_file))
|
||||
{
|
||||
$source_file = $container->template->parsePath($source_file, true);
|
||||
|
||||
if ($container->filesystem->fileExists($source_file))
|
||||
{
|
||||
include $source_file;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the class exists
|
||||
if (class_exists($source_class, true))
|
||||
{
|
||||
// ...and so does the option
|
||||
if (in_array($source_method, get_class_methods($source_class)))
|
||||
{
|
||||
// Get the data from the class
|
||||
if ($source_format == 'optionsobject')
|
||||
{
|
||||
$options = array_merge($options, $source_class::$source_method());
|
||||
}
|
||||
else
|
||||
{
|
||||
$source_data = $source_class::$source_method();
|
||||
|
||||
// Loop through the data and prime the $options array
|
||||
foreach ($source_data as $k => $v)
|
||||
{
|
||||
$key = (empty($source_key) || ($source_key == '*')) ? $k : @$v[$source_key];
|
||||
$value = (empty($source_value) || ($source_value == '*')) ? $v : @$v[$source_value];
|
||||
|
||||
if ($source_translate)
|
||||
{
|
||||
$value = Text::_($value);
|
||||
}
|
||||
|
||||
$options[] = HTMLHelper::_('FEFHelper.select.option', $key, $value, 'value', 'text');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reset($options);
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get JHtml options from the values returned by a model.
|
||||
*
|
||||
* The params can be:
|
||||
* key_field The model field used for the OPTION's key. Default: the model's ID field.
|
||||
* value_field The model field used for the OPTION's displayed value. You must provide it.
|
||||
* apply_access Should I apply Joomla ACLs to the model? Default: FALSE.
|
||||
* none Placeholder for no selection. Default: NULL (no placeholder).
|
||||
* none_as_zero When true, the 'none' placeholder applies to values '' **AND** '0' (empty string and zero)
|
||||
* translate Should I pass the values through JText? Default: TRUE.
|
||||
* with Array of relation names for eager loading.
|
||||
* cache Cache the results for faster reuse
|
||||
*
|
||||
* @param string $modelName The name of the model, e.g. "items" or "com_foobar.items"
|
||||
* @param array $params Parameters which define which options to get from the model
|
||||
* @param array $modelState Optional state variables to pass to the model
|
||||
* @param array $options Any JHtml select options you want to add in front of the model's returned values
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
private static function getOptionsFromModel($modelName, array $params = [], array $modelState = [], array $options = [])
|
||||
{
|
||||
// Let's find the FOF DI container from the call stack
|
||||
$container = self::getContainerFromBacktrace();
|
||||
|
||||
// Explode model name into component name and prefix
|
||||
$componentName = $container->componentName;
|
||||
$mName = $modelName;
|
||||
|
||||
if (strpos($modelName, '.') !== false)
|
||||
{
|
||||
[$componentName, $mName] = explode('.', $mName, 2);
|
||||
}
|
||||
|
||||
if ($componentName != $container->componentName)
|
||||
{
|
||||
$container = Container::getInstance($componentName);
|
||||
}
|
||||
|
||||
/** @var DataModel $model */
|
||||
$model = $container->factory->model($mName)->setIgnoreRequest(true)->savestate(false);
|
||||
|
||||
$defaultParams = [
|
||||
'key_field' => $model->getKeyName(),
|
||||
'value_field' => 'title',
|
||||
'apply_access' => false,
|
||||
'none' => null,
|
||||
'none_as_zero' => false,
|
||||
'translate' => true,
|
||||
'with' => [],
|
||||
];
|
||||
|
||||
$params = array_merge($defaultParams, $params);
|
||||
|
||||
$cache = isset($params['cache']) && $params['cache'];
|
||||
$cacheKey = null;
|
||||
|
||||
if ($cache)
|
||||
{
|
||||
$cacheKey = sha1(print_r([
|
||||
$model->getContainer()->componentName,
|
||||
$model->getName(),
|
||||
$params['key_field'],
|
||||
$params['value_field'],
|
||||
$params['apply_access'],
|
||||
$params['none'],
|
||||
$params['translate'],
|
||||
$params['with'],
|
||||
$modelState,
|
||||
], true));
|
||||
}
|
||||
|
||||
if ($cache && isset(self::$cacheModelOptions[$cacheKey]))
|
||||
{
|
||||
return self::$cacheModelOptions[$cacheKey];
|
||||
}
|
||||
|
||||
if (empty($params['none']) && !is_null($params['none']))
|
||||
{
|
||||
$langKey = strtoupper($model->getContainer()->componentName . '_TITLE_' . $model->getName());
|
||||
$placeholder = Text::_($langKey);
|
||||
|
||||
if ($langKey != $placeholder)
|
||||
{
|
||||
$params['none'] = '— ' . $placeholder . ' —';
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($params['none']))
|
||||
{
|
||||
$options[] = HTMLHelper::_('FEFHelper.select.option', null, Text::_($params['none']));
|
||||
|
||||
if ($params['none_as_zero'])
|
||||
{
|
||||
$options[] = HTMLHelper::_('FEFHelper.select.option', 0, Text::_($params['none']));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($params['apply_access'])
|
||||
{
|
||||
$model->applyAccessFiltering();
|
||||
}
|
||||
|
||||
if (!is_null($params['with']))
|
||||
{
|
||||
$model->with($params['with']);
|
||||
}
|
||||
|
||||
// Set the model's state, if applicable
|
||||
foreach ($modelState as $stateKey => $stateValue)
|
||||
{
|
||||
$model->setState($stateKey, $stateValue);
|
||||
}
|
||||
|
||||
// Set the query and get the result list.
|
||||
$items = $model->get(true);
|
||||
|
||||
// Build the field options.
|
||||
if (!empty($items))
|
||||
{
|
||||
foreach ($items as $item)
|
||||
{
|
||||
$value = $item->{$params['value_field']};
|
||||
|
||||
if ($params['translate'])
|
||||
{
|
||||
$value = Text::_($value);
|
||||
}
|
||||
|
||||
$options[] = HTMLHelper::_('FEFHelper.select.option', $item->{$params['key_field']}, $value);
|
||||
}
|
||||
}
|
||||
|
||||
if ($cache)
|
||||
{
|
||||
self::$cacheModelOptions[$cacheKey] = $options;
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the FOF DI container from the backtrace of the static call. MAGIC!
|
||||
*
|
||||
* @return Container
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
private static function getContainerFromBacktrace()
|
||||
{
|
||||
// In case we are on a braindead host
|
||||
if (!function_exists('debug_backtrace'))
|
||||
{
|
||||
throw new RuntimeException("Your host has disabled the <code>debug_backtrace</code> PHP function. Please ask them to re-enable it. It's required for running this software.");
|
||||
}
|
||||
|
||||
/**
|
||||
* For performance reasons I look into the last 4 call stack entries. If I don't find a container I
|
||||
* will expand my search by another 2 entries and so on until I either find a container or I stop
|
||||
* finding new call stack entries.
|
||||
*/
|
||||
$lastNumberOfEntries = 0;
|
||||
$limit = 4;
|
||||
$skip = 0;
|
||||
$container = null;
|
||||
|
||||
while (true)
|
||||
{
|
||||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, $limit);
|
||||
|
||||
if (count($backtrace) == $lastNumberOfEntries)
|
||||
{
|
||||
throw new RuntimeException(__METHOD__ . ": Cannot retrieve FOF container from call stack. You are either calling me from a non-FEF extension or your PHP is broken.");
|
||||
}
|
||||
|
||||
$lastNumberOfEntries = count($backtrace);
|
||||
|
||||
if ($skip)
|
||||
{
|
||||
$backtrace = array_slice($backtrace, $skip);
|
||||
}
|
||||
|
||||
foreach ($backtrace as $bt)
|
||||
{
|
||||
if (!isset($bt['object']))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!method_exists($bt['object'], 'getContainer'))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return $bt['object']->getContainer();
|
||||
}
|
||||
|
||||
$skip = $limit;
|
||||
$limit += 2;
|
||||
}
|
||||
|
||||
throw new RuntimeException(__METHOD__ . ": Cannot retrieve FOF container from call stack. You are either calling me from a non-FEF extension or your PHP is broken.");
|
||||
}
|
||||
|
||||
}
|
||||
85
libraries/fof30/Utils/FEFHelper/Html.php
Normal file
85
libraries/fof30/Utils/FEFHelper/Html.php
Normal file
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FOF
|
||||
* @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd
|
||||
* @license GNU General Public License version 2, or later
|
||||
*/
|
||||
|
||||
namespace FOF30\Utils\FEFHelper;
|
||||
|
||||
defined('_JEXEC') || die;
|
||||
|
||||
use FOF30\View\DataView\DataViewInterface;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Pagination\Pagination;
|
||||
|
||||
/**
|
||||
* Interim FEF helper which was used in FOF 3.2. This is deprecated. Please use the FEFHelper.browse JHtml helper
|
||||
* instead. The implementation of this class should be a good hint on how you can do that.
|
||||
*
|
||||
* @deprecated 4.0
|
||||
*/
|
||||
abstract class Html
|
||||
{
|
||||
/**
|
||||
* Helper function to create Javascript code required for table ordering
|
||||
*
|
||||
* @param string $order Current order
|
||||
*
|
||||
* @return string Javascript to add to the page
|
||||
*/
|
||||
public static function jsOrderingBackend($order)
|
||||
{
|
||||
return HTMLHelper::_('FEFHelper.browse.orderjs', $order, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the required HTML code for backend pagination and sorting
|
||||
*
|
||||
* @param Pagination $pagination Pagination object
|
||||
* @param array $sortFields Fields allowed to be sorted
|
||||
* @param string $order Ordering field
|
||||
* @param string $order_Dir Ordering direction (ASC, DESC)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function selectOrderingBackend($pagination, $sortFields, $order, $order_Dir)
|
||||
{
|
||||
if (is_null($sortFields))
|
||||
{
|
||||
$sortFields = [];
|
||||
}
|
||||
|
||||
if (is_string($sortFields))
|
||||
{
|
||||
$sortFields = [$sortFields];
|
||||
}
|
||||
|
||||
if (!is_array($sortFields))
|
||||
{
|
||||
$sortFields = [];
|
||||
}
|
||||
|
||||
return
|
||||
'<div class="akeeba-filter-bar akeeba-filter-bar--right">' .
|
||||
HTMLHelper::_('FEFHelper.browse.orderheader', null, $sortFields, $pagination, $order, $order_Dir) .
|
||||
'</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the drag'n'drop reordering field for Browse views
|
||||
*
|
||||
* @param DataViewInterface $view The DataView you're rendering against
|
||||
* @param string $orderingField The name of the field you're ordering by
|
||||
* @param string $order The order value of the current row
|
||||
* @param string $class CSS class for the ordering value INPUT field
|
||||
* @param string $icon CSS class for the d'n'd handle icon
|
||||
* @param string $inactiveIcon CSS class for the d'n'd disabled icon
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function dragDropReordering(DataViewInterface $view, $orderingField, $order, $class = 'input-sm', $icon = 'akion-drag', $inactiveIcon = 'akion-android-more-vertical')
|
||||
{
|
||||
return HTMLHelper::_('FEFHelper.browse.order', $orderingField, $order, $class, $icon, $inactiveIcon, $view);
|
||||
}
|
||||
}
|
||||
831
libraries/fof30/Utils/FEFHelper/browse.php
Normal file
831
libraries/fof30/Utils/FEFHelper/browse.php
Normal file
@ -0,0 +1,831 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FOF
|
||||
* @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd
|
||||
* @license GNU General Public License version 2, or later
|
||||
*/
|
||||
|
||||
defined('_JEXEC') || die;
|
||||
|
||||
use FOF30\Model\DataModel;
|
||||
use FOF30\Utils\ArrayHelper;
|
||||
use FOF30\Utils\FEFHelper\BrowseView;
|
||||
use FOF30\View\DataView\DataViewInterface;
|
||||
use FOF30\View\DataView\Html;
|
||||
use FOF30\View\DataView\Raw as DataViewRaw;
|
||||
use Joomla\CMS\Factory;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use Joomla\CMS\Pagination\Pagination;
|
||||
|
||||
/**
|
||||
* Custom JHtml (HTMLHelper) class. Offers browse view controls compatible with Akeeba Frontend
|
||||
* Framework (FEF).
|
||||
*
|
||||
* Call these methods as JHtml::_('FEFHelper.browse.methodName', $parameter1, $parameter2, ...)
|
||||
*/
|
||||
abstract class FEFHelperBrowse
|
||||
{
|
||||
/**
|
||||
* Returns an action button on the browse view's table
|
||||
*
|
||||
* @param integer $i The row index
|
||||
* @param string $task The task to fire when the button is clicked
|
||||
* @param string|array $prefix An optional task prefix or an array of options
|
||||
* @param string $active_title An optional active tooltip to display if $enable is true
|
||||
* @param string $inactive_title An optional inactive tooltip to display if $enable is true
|
||||
* @param boolean $tip An optional setting for tooltip
|
||||
* @param string $active_class An optional active HTML class
|
||||
* @param string $inactive_class An optional inactive HTML class
|
||||
* @param boolean $enabled An optional setting for access control on the action.
|
||||
* @param boolean $translate An optional setting for translation.
|
||||
* @param string $checkbox An optional prefix for checkboxes.
|
||||
*
|
||||
* @return string The HTML markup
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function action($i, $task, $prefix = '', $active_title = '', $inactive_title = '', $tip = false, $active_class = '',
|
||||
$inactive_class = '', $enabled = true, $translate = true, $checkbox = 'cb')
|
||||
{
|
||||
if (is_array($prefix))
|
||||
{
|
||||
$options = $prefix;
|
||||
$active_title = array_key_exists('active_title', $options) ? $options['active_title'] : $active_title;
|
||||
$inactive_title = array_key_exists('inactive_title', $options) ? $options['inactive_title'] : $inactive_title;
|
||||
$tip = array_key_exists('tip', $options) ? $options['tip'] : $tip;
|
||||
$active_class = array_key_exists('active_class', $options) ? $options['active_class'] : $active_class;
|
||||
$inactive_class = array_key_exists('inactive_class', $options) ? $options['inactive_class'] : $inactive_class;
|
||||
$enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled;
|
||||
$translate = array_key_exists('translate', $options) ? $options['translate'] : $translate;
|
||||
$checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox;
|
||||
$prefix = array_key_exists('prefix', $options) ? $options['prefix'] : '';
|
||||
}
|
||||
|
||||
if ($tip)
|
||||
{
|
||||
$title = $enabled ? $active_title : $inactive_title;
|
||||
$title = $translate ? Text::_($title) : $title;
|
||||
$title = HTMLHelper::_('tooltipText', $title, '', 0);
|
||||
}
|
||||
|
||||
$html = [];
|
||||
|
||||
if ($enabled)
|
||||
{
|
||||
$btnColor = 'grey';
|
||||
|
||||
if (substr($active_class, 0, 2) == '--')
|
||||
{
|
||||
[$btnColor, $active_class] = explode(' ', $active_class, 2);
|
||||
$btnColor = ltrim($btnColor, '-');
|
||||
}
|
||||
|
||||
$html[] = '<a class="akeeba-btn--' . $btnColor . '--mini ' . ($active_class === 'publish' ? ' active' : '') . ($tip ? ' hasTooltip' : '') . '"';
|
||||
$html[] = ' href="javascript:void(0);" onclick="return Joomla.listItemTask(\'' . $checkbox . $i . '\',\'' . $prefix . $task . '\')"';
|
||||
$html[] = $tip ? ' title="' . $title . '"' : '';
|
||||
$html[] = '>';
|
||||
$html[] = '<span class="akion-' . $active_class . '" aria-hidden="true"></span> ';
|
||||
$html[] = '</a>';
|
||||
}
|
||||
else
|
||||
{
|
||||
$btnColor = 'grey';
|
||||
|
||||
if (substr($inactive_class, 0, 2) == '--')
|
||||
{
|
||||
[$btnColor, $inactive_class] = explode(' ', $inactive_class, 2);
|
||||
$btnColor = ltrim($btnColor, '-');
|
||||
}
|
||||
|
||||
$html[] = '<a class="akeeba-btn--' . $btnColor . '--mini disabled akeebagrid' . ($tip ? ' hasTooltip' : '') . '"';
|
||||
$html[] = $tip ? ' title="' . $title . '"' : '';
|
||||
$html[] = '>';
|
||||
|
||||
if ($active_class === 'protected')
|
||||
{
|
||||
$inactive_class = 'locked';
|
||||
}
|
||||
|
||||
$html[] = '<span class="akion-' . $inactive_class . '"></span> ';
|
||||
$html[] = '</a>';
|
||||
}
|
||||
|
||||
return implode($html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a state change button on the browse view's table
|
||||
*
|
||||
* @param array $states array of value/state. Each state is an array of the form
|
||||
* (task, text, active title, inactive title, tip (boolean), HTML active class,
|
||||
* HTML inactive class) or ('task'=>task, 'text'=>text, 'active_title'=>active
|
||||
* title,
|
||||
* 'inactive_title'=>inactive title, 'tip'=>boolean, 'active_class'=>html active
|
||||
* class,
|
||||
* 'inactive_class'=>html inactive class)
|
||||
* @param integer $value The state value.
|
||||
* @param integer $i The row index
|
||||
* @param string|array $prefix An optional task prefix or an array of options
|
||||
* @param boolean $enabled An optional setting for access control on the action.
|
||||
* @param boolean $translate An optional setting for translation.
|
||||
* @param string $checkbox An optional prefix for checkboxes.
|
||||
*
|
||||
* @return string The HTML markup
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function state($states, $value, $i, $prefix = '', $enabled = true, $translate = true, $checkbox = 'cb')
|
||||
{
|
||||
if (is_array($prefix))
|
||||
{
|
||||
$options = $prefix;
|
||||
$enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled;
|
||||
$translate = array_key_exists('translate', $options) ? $options['translate'] : $translate;
|
||||
$checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox;
|
||||
$prefix = array_key_exists('prefix', $options) ? $options['prefix'] : '';
|
||||
}
|
||||
|
||||
$state = ArrayHelper::getValue($states, (int) $value, $states[0]);
|
||||
$task = array_key_exists('task', $state) ? $state['task'] : $state[0];
|
||||
$text = array_key_exists('text', $state) ? $state['text'] : (array_key_exists(1, $state) ? $state[1] : '');
|
||||
$active_title = array_key_exists('active_title', $state) ? $state['active_title'] : (array_key_exists(2, $state) ? $state[2] : '');
|
||||
$inactive_title = array_key_exists('inactive_title', $state) ? $state['inactive_title'] : (array_key_exists(3, $state) ? $state[3] : '');
|
||||
$tip = array_key_exists('tip', $state) ? $state['tip'] : (array_key_exists(4, $state) ? $state[4] : false);
|
||||
$active_class = array_key_exists('active_class', $state) ? $state['active_class'] : (array_key_exists(5, $state) ? $state[5] : '');
|
||||
$inactive_class = array_key_exists('inactive_class', $state) ? $state['inactive_class'] : (array_key_exists(6, $state) ? $state[6] : '');
|
||||
|
||||
return static::action(
|
||||
$i, $task, $prefix, $active_title, $inactive_title, $tip,
|
||||
$active_class, $inactive_class, $enabled, $translate, $checkbox
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a published state on the browse view's table
|
||||
*
|
||||
* @param integer $value The state value.
|
||||
* @param integer $i The row index
|
||||
* @param string|array $prefix An optional task prefix or an array of options
|
||||
* @param boolean $enabled An optional setting for access control on the action.
|
||||
* @param string $checkbox An optional prefix for checkboxes.
|
||||
* @param string $publish_up An optional start publishing date.
|
||||
* @param string $publish_down An optional finish publishing date.
|
||||
*
|
||||
* @return string The HTML markup
|
||||
*
|
||||
* @see self::state()
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function published($value, $i, $prefix = '', $enabled = true, $checkbox = 'cb', $publish_up = null, $publish_down = null)
|
||||
{
|
||||
if (is_array($prefix))
|
||||
{
|
||||
$options = $prefix;
|
||||
$enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled;
|
||||
$checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox;
|
||||
$prefix = array_key_exists('prefix', $options) ? $options['prefix'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Format:
|
||||
*
|
||||
* (task, text, active title, inactive title, tip (boolean), active icon class (without akion-), inactive icon class (without akion-))
|
||||
*/
|
||||
$states = [
|
||||
1 => [
|
||||
'unpublish', 'JPUBLISHED', 'JLIB_HTML_UNPUBLISH_ITEM', 'JPUBLISHED', true, '--green checkmark',
|
||||
'--green checkmark',
|
||||
],
|
||||
0 => [
|
||||
'publish', 'JUNPUBLISHED', 'JLIB_HTML_PUBLISH_ITEM', 'JUNPUBLISHED', true, '--red close', '--red close',
|
||||
],
|
||||
2 => [
|
||||
'unpublish', 'JARCHIVED', 'JLIB_HTML_UNPUBLISH_ITEM', 'JARCHIVED', true, '--orange ion-ios-box',
|
||||
'--orange ion-ios-box',
|
||||
],
|
||||
-2 => [
|
||||
'publish', 'JTRASHED', 'JLIB_HTML_PUBLISH_ITEM', 'JTRASHED', true, '--dark trash-a', '--dark trash-a',
|
||||
],
|
||||
];
|
||||
|
||||
// Special state for dates
|
||||
if ($publish_up || $publish_down)
|
||||
{
|
||||
$nullDate = Factory::getDbo()->getNullDate();
|
||||
$nowDate = Factory::getDate()->toUnix();
|
||||
|
||||
$tz = Factory::getUser()->getTimezone();
|
||||
|
||||
$publish_up = (!empty($publish_up) && ($publish_up != $nullDate)) ? Factory::getDate($publish_up, 'UTC')->setTimeZone($tz) : false;
|
||||
$publish_down = (!empty($publish_down) && ($publish_down != $nullDate)) ? Factory::getDate($publish_down, 'UTC')->setTimeZone($tz) : false;
|
||||
|
||||
// Create tip text, only we have publish up or down settings
|
||||
$tips = [];
|
||||
|
||||
if ($publish_up)
|
||||
{
|
||||
$tips[] = Text::sprintf('JLIB_HTML_PUBLISHED_START', HTMLHelper::_('date', $publish_up, Text::_('DATE_FORMAT_LC5'), 'UTC'));
|
||||
}
|
||||
|
||||
if ($publish_down)
|
||||
{
|
||||
$tips[] = Text::sprintf('JLIB_HTML_PUBLISHED_FINISHED', HTMLHelper::_('date', $publish_down, Text::_('DATE_FORMAT_LC5'), 'UTC'));
|
||||
}
|
||||
|
||||
$tip = empty($tips) ? false : implode('<br />', $tips);
|
||||
|
||||
// Add tips and special titles
|
||||
foreach ($states as $key => $state)
|
||||
{
|
||||
// Create special titles for published items
|
||||
if ($key == 1)
|
||||
{
|
||||
$states[$key][2] = $states[$key][3] = 'JLIB_HTML_PUBLISHED_ITEM';
|
||||
|
||||
if (!empty($publish_up) && ($publish_up != $nullDate) && $nowDate < $publish_up->toUnix())
|
||||
{
|
||||
$states[$key][2] = $states[$key][3] = 'JLIB_HTML_PUBLISHED_PENDING_ITEM';
|
||||
$states[$key][5] = $states[$key][6] = 'android-time';
|
||||
}
|
||||
|
||||
if (!empty($publish_down) && ($publish_down != $nullDate) && $nowDate > $publish_down->toUnix())
|
||||
{
|
||||
$states[$key][2] = $states[$key][3] = 'JLIB_HTML_PUBLISHED_EXPIRED_ITEM';
|
||||
$states[$key][5] = $states[$key][6] = 'alert';
|
||||
}
|
||||
}
|
||||
|
||||
// Add tips to titles
|
||||
if ($tip)
|
||||
{
|
||||
$states[$key][1] = Text::_($states[$key][1]);
|
||||
$states[$key][2] = Text::_($states[$key][2]) . '<br />' . $tip;
|
||||
$states[$key][3] = Text::_($states[$key][3]) . '<br />' . $tip;
|
||||
$states[$key][4] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return static::state($states, $value, $i, [
|
||||
'prefix' => $prefix, 'translate' => !$tip,
|
||||
], $enabled, true, $checkbox);
|
||||
}
|
||||
|
||||
return static::state($states, $value, $i, $prefix, $enabled, true, $checkbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an isDefault state on the browse view's table
|
||||
*
|
||||
* @param integer $value The state value.
|
||||
* @param integer $i The row index
|
||||
* @param string|array $prefix An optional task prefix or an array of options
|
||||
* @param boolean $enabled An optional setting for access control on the action.
|
||||
* @param string $checkbox An optional prefix for checkboxes.
|
||||
*
|
||||
* @return string The HTML markup
|
||||
*
|
||||
* @see self::state()
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function isdefault($value, $i, $prefix = '', $enabled = true, $checkbox = 'cb')
|
||||
{
|
||||
if (is_array($prefix))
|
||||
{
|
||||
$options = $prefix;
|
||||
$enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled;
|
||||
$checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox;
|
||||
$prefix = array_key_exists('prefix', $options) ? $options['prefix'] : '';
|
||||
}
|
||||
|
||||
$states = [
|
||||
0 => ['setDefault', '', 'JLIB_HTML_SETDEFAULT_ITEM', '', 1, 'android-star-outline', 'android-star-outline'],
|
||||
1 => [
|
||||
'unsetDefault', 'JDEFAULT', 'JLIB_HTML_UNSETDEFAULT_ITEM', 'JDEFAULT', 1, 'android-star',
|
||||
'android-star',
|
||||
],
|
||||
];
|
||||
|
||||
return static::state($states, $value, $i, $prefix, $enabled, true, $checkbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a checked-out icon
|
||||
*
|
||||
* @param integer $i The row index.
|
||||
* @param string $editorName The name of the editor.
|
||||
* @param string $time The time that the object was checked out.
|
||||
* @param string|array $prefix An optional task prefix or an array of options
|
||||
* @param boolean $enabled True to enable the action.
|
||||
* @param string $checkbox An optional prefix for checkboxes.
|
||||
*
|
||||
* @return string The HTML markup
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function checkedout($i, $editorName, $time, $prefix = '', $enabled = false, $checkbox = 'cb')
|
||||
{
|
||||
HTMLHelper::_('bootstrap.tooltip');
|
||||
|
||||
if (is_array($prefix))
|
||||
{
|
||||
$options = $prefix;
|
||||
$enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled;
|
||||
$checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox;
|
||||
$prefix = array_key_exists('prefix', $options) ? $options['prefix'] : '';
|
||||
}
|
||||
|
||||
$text = $editorName . '<br />' . HTMLHelper::_('date', $time, Text::_('DATE_FORMAT_LC')) . '<br />' . HTMLHelper::_('date', $time, 'H:i');
|
||||
$active_title = HTMLHelper::_('tooltipText', Text::_('JLIB_HTML_CHECKIN'), $text, 0);
|
||||
$inactive_title = HTMLHelper::_('tooltipText', Text::_('JLIB_HTML_CHECKED_OUT'), $text, 0);
|
||||
|
||||
return static::action(
|
||||
$i, 'checkin', $prefix, html_entity_decode($active_title, ENT_QUOTES, 'UTF-8'),
|
||||
html_entity_decode($inactive_title, ENT_QUOTES, 'UTF-8'), true, 'locked', 'locked', $enabled, false, $checkbox
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the drag'n'drop reordering field for Browse views
|
||||
*
|
||||
* @param string $orderingField The name of the field you're ordering by
|
||||
* @param string $order The order value of the current row
|
||||
* @param string $class CSS class for the ordering value INPUT field
|
||||
* @param string $icon CSS class for the d'n'd handle icon
|
||||
* @param string $inactiveIcon CSS class for the d'n'd disabled icon
|
||||
* @param DataViewInterface $view The view you're rendering against. Leave null for auto-detection.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function order($orderingField, $order, $class = 'input-sm', $icon = 'akion-android-more-vertical', $inactiveIcon = 'akion-android-more-vertical', DataViewInterface $view = null)
|
||||
{
|
||||
/** @var Html $view */
|
||||
if (is_null($view))
|
||||
{
|
||||
$view = BrowseView::getViewFromBacktrace();
|
||||
}
|
||||
$dndOrderingActive = $view->getLists()->order == $orderingField;
|
||||
|
||||
// Default inactive ordering
|
||||
$html = '<span class="sortable-handler inactive" >';
|
||||
$html .= '<span class="' . $icon . '"></span>';
|
||||
$html .= '</span>';
|
||||
|
||||
// The modern drag'n'drop method
|
||||
if ($view->getPerms()->editstate)
|
||||
{
|
||||
$disableClassName = '';
|
||||
$disabledLabel = '';
|
||||
|
||||
// DO NOT REMOVE! It will initialize Joomla libraries and javascript functions
|
||||
$hasAjaxOrderingSupport = $view->hasAjaxOrderingSupport();
|
||||
|
||||
if (!is_array($hasAjaxOrderingSupport) || !$hasAjaxOrderingSupport['saveOrder'])
|
||||
{
|
||||
$disabledLabel = Text::_('JORDERINGDISABLED');
|
||||
$disableClassName = 'inactive tip-top hasTooltip';
|
||||
}
|
||||
|
||||
$orderClass = $dndOrderingActive ? 'order-enabled' : 'order-disabled';
|
||||
|
||||
$html = '<div class="' . $orderClass . '">';
|
||||
$html .= '<span class="sortable-handler ' . $disableClassName . '" title="' . $disabledLabel . '">';
|
||||
$html .= '<span class="' . ($disableClassName ? $inactiveIcon : $icon) . '"></span>';
|
||||
$html .= '</span>';
|
||||
|
||||
if ($dndOrderingActive)
|
||||
{
|
||||
$joomla35IsBroken = version_compare(JVERSION, '3.5.0', 'ge') ? 'style="display: none"' : '';
|
||||
|
||||
$html .= '<input type="text" name="order[]" ' . $joomla35IsBroken . ' size="5" class="' . $class . ' text-area-order" value="' . $order . '" />';
|
||||
}
|
||||
|
||||
$html .= '</div>';
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the drag'n'drop reordering table header for Browse views
|
||||
*
|
||||
* @param string $orderingField The name of the field you're ordering by
|
||||
* @param string $icon CSS class for the d'n'd handle icon
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function orderfield($orderingField = 'ordering', $icon = 'akion-stats-bars')
|
||||
{
|
||||
$title = Text::_('JGLOBAL_CLICK_TO_SORT_THIS_COLUMN');
|
||||
$orderingLabel = Text::_('JFIELD_ORDERING_LABEL');
|
||||
|
||||
return <<< HTML
|
||||
<a href="#"
|
||||
onclick="Joomla.tableOrdering('{$orderingField}','asc','');return false;"
|
||||
class="hasPopover"
|
||||
title="{$orderingLabel}"
|
||||
data-content="{$title}"
|
||||
data-placement="top"
|
||||
>
|
||||
<span class="{$icon}"></span>
|
||||
</a>
|
||||
|
||||
HTML;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates an order-up action icon.
|
||||
*
|
||||
* @param integer $i The row index.
|
||||
* @param string $task An optional task to fire.
|
||||
* @param string|array $prefix An optional task prefix or an array of options
|
||||
* @param string $text An optional text to display
|
||||
* @param boolean $enabled An optional setting for access control on the action.
|
||||
* @param string $checkbox An optional prefix for checkboxes.
|
||||
*
|
||||
* @return string The HTML markup
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function orderUp($i, $task = 'orderup', $prefix = '', $text = 'JLIB_HTML_MOVE_UP', $enabled = true, $checkbox = 'cb')
|
||||
{
|
||||
if (is_array($prefix))
|
||||
{
|
||||
$options = $prefix;
|
||||
$text = array_key_exists('text', $options) ? $options['text'] : $text;
|
||||
$enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled;
|
||||
$checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox;
|
||||
$prefix = array_key_exists('prefix', $options) ? $options['prefix'] : '';
|
||||
}
|
||||
|
||||
return static::action($i, $task, $prefix, $text, $text, false, 'arrow-up-b', 'arrow-up-b', $enabled, true, $checkbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an order-down action icon.
|
||||
*
|
||||
* @param integer $i The row index.
|
||||
* @param string $task An optional task to fire.
|
||||
* @param string|array $prefix An optional task prefix or an array of options
|
||||
* @param string $text An optional text to display
|
||||
* @param boolean $enabled An optional setting for access control on the action.
|
||||
* @param string $checkbox An optional prefix for checkboxes.
|
||||
*
|
||||
* @return string The HTML markup
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function orderDown($i, $task = 'orderdown', $prefix = '', $text = 'JLIB_HTML_MOVE_DOWN', $enabled = true, $checkbox = 'cb')
|
||||
{
|
||||
if (is_array($prefix))
|
||||
{
|
||||
$options = $prefix;
|
||||
$text = array_key_exists('text', $options) ? $options['text'] : $text;
|
||||
$enabled = array_key_exists('enabled', $options) ? $options['enabled'] : $enabled;
|
||||
$checkbox = array_key_exists('checkbox', $options) ? $options['checkbox'] : $checkbox;
|
||||
$prefix = array_key_exists('prefix', $options) ? $options['prefix'] : '';
|
||||
}
|
||||
|
||||
return static::action($i, $task, $prefix, $text, $text, false, 'arrow-down-b', 'arrow-down-b', $enabled, true, $checkbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Table header for a field which changes the sort order when clicked
|
||||
*
|
||||
* @param string $title The link title
|
||||
* @param string $order The order field for the column
|
||||
* @param string $direction The current direction
|
||||
* @param string $selected The selected ordering
|
||||
* @param string $task An optional task override
|
||||
* @param string $new_direction An optional direction for the new column
|
||||
* @param string $tip An optional text shown as tooltip title instead of $title
|
||||
* @param string $form An optional form selector
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function sort($title, $order, $direction = 'asc', $selected = '', $task = null, $new_direction = 'asc', $tip = '', $form = null)
|
||||
{
|
||||
HTMLHelper::_('behavior.core');
|
||||
HTMLHelper::_('bootstrap.popover');
|
||||
|
||||
$direction = strtolower($direction);
|
||||
$icon = ['akion-android-arrow-dropup', 'akion-android-arrow-dropdown'];
|
||||
$index = (int) ($direction === 'desc');
|
||||
|
||||
if ($order != $selected)
|
||||
{
|
||||
$direction = $new_direction;
|
||||
}
|
||||
else
|
||||
{
|
||||
$direction = $direction === 'desc' ? 'asc' : 'desc';
|
||||
}
|
||||
|
||||
if ($form)
|
||||
{
|
||||
$form = ', document.getElementById(\'' . $form . '\')';
|
||||
}
|
||||
|
||||
$html = '<a href="#" onclick="Joomla.tableOrdering(\'' . $order . '\',\'' . $direction . '\',\'' . $task . '\'' . $form . ');return false;"'
|
||||
. ' class="hasPopover" title="' . htmlspecialchars(Text::_($tip ?: $title)) . '"'
|
||||
. ' data-content="' . htmlspecialchars(Text::_('JGLOBAL_CLICK_TO_SORT_THIS_COLUMN')) . '" data-placement="top">';
|
||||
|
||||
if (isset($title['0']) && $title['0'] === '<')
|
||||
{
|
||||
$html .= $title;
|
||||
}
|
||||
else
|
||||
{
|
||||
$html .= Text::_($title);
|
||||
}
|
||||
|
||||
if ($order == $selected)
|
||||
{
|
||||
$html .= '<span class="' . $icon[$index] . '"></span>';
|
||||
}
|
||||
|
||||
$html .= '</a>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to check all checkboxes on the browse view's table
|
||||
*
|
||||
* @param string $name The name of the form element
|
||||
* @param string $tip The text shown as tooltip title instead of $tip
|
||||
* @param string $action The action to perform on clicking the checkbox
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function checkall($name = 'checkall-toggle', $tip = 'JGLOBAL_CHECK_ALL', $action = 'Joomla.checkAll(this)')
|
||||
{
|
||||
HTMLHelper::_('behavior.core');
|
||||
HTMLHelper::_('bootstrap.tooltip');
|
||||
|
||||
return '<input type="checkbox" name="' . $name . '" value="" class="hasTooltip" title="' . HTMLHelper::_('tooltipText', $tip)
|
||||
. '" onclick="' . $action . '" />';
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to create a checkbox for a grid row.
|
||||
*
|
||||
* @param integer $rowNum The row index
|
||||
* @param integer $recId The record id
|
||||
* @param boolean $checkedOut True if item is checked out
|
||||
* @param string $name The name of the form element
|
||||
* @param string $stub The name of stub identifier
|
||||
*
|
||||
* @return mixed String of html with a checkbox if item is not checked out, null if checked out.
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function id($rowNum, $recId, $checkedOut = false, $name = 'cid', $stub = 'cb')
|
||||
{
|
||||
return $checkedOut ? '' : '<input type="checkbox" id="' . $stub . $rowNum . '" name="' . $name . '[]" value="' . $recId
|
||||
. '" onclick="Joomla.isChecked(this.checked);" />';
|
||||
}
|
||||
|
||||
/**
|
||||
* Include the necessary JavaScript for the browse view's table order feature
|
||||
*
|
||||
* @param string $orderBy Filed by which we are currently sorting the table.
|
||||
* @param bool $return Should I return the JS? Default: false (= add to the page's head)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function orderjs($orderBy, $return = false)
|
||||
{
|
||||
$js = <<< JS
|
||||
|
||||
Joomla.orderTable = function()
|
||||
{
|
||||
var table = document.getElementById("sortTable");
|
||||
var direction = document.getElementById("directionTable");
|
||||
var order = table.options[table.selectedIndex].value;
|
||||
var dirn = 'asc';
|
||||
|
||||
if (order !== '$orderBy')
|
||||
{
|
||||
dirn = 'asc';
|
||||
}
|
||||
else {
|
||||
dirn = direction.options[direction.selectedIndex].value;
|
||||
}
|
||||
|
||||
Joomla.tableOrdering(order, dirn);
|
||||
};
|
||||
JS;
|
||||
|
||||
if ($return)
|
||||
{
|
||||
return $js;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Factory::getApplication()->getDocument()->addScriptDeclaration($js);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
// If we have no application, well, not having table sorting JS is the least of your worries...
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the table ordering / pagination header for a browse view: number of records to display, order direction,
|
||||
* order by field.
|
||||
*
|
||||
* @param DataViewRaw $view The view you're rendering against. If not provided we
|
||||
* will guess it using MAGIC.
|
||||
* @param array $sortFields Array of field name => description for the ordering
|
||||
* fields in the dropdown. If not provided we will use all
|
||||
* the fields available in the model.
|
||||
* @param Pagination $pagination The Joomla pagination object. If not provided we fetch
|
||||
* it from the view.
|
||||
* @param string $sortBy Order by field name. If not provided we fetch it from
|
||||
* the view.
|
||||
* @param string $order_Dir Order direction. If not provided we fetch it from the
|
||||
* view.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function orderheader(DataViewRaw $view = null, array $sortFields = [], Pagination $pagination = null, $sortBy = null, $order_Dir = null)
|
||||
{
|
||||
if (is_null($view))
|
||||
{
|
||||
$view = BrowseView::getViewFromBacktrace();
|
||||
}
|
||||
|
||||
if (empty($sortFields))
|
||||
{
|
||||
/** @var DataModel $model */
|
||||
$model = $view->getModel();
|
||||
$sortFields = $view->getLists()->sortFields ?? [];
|
||||
$sortFields = empty($sortFields) ? self::getSortFields($model) : $sortFields;
|
||||
}
|
||||
|
||||
if (empty($pagination))
|
||||
{
|
||||
$pagination = $view->getPagination();
|
||||
}
|
||||
|
||||
if (empty($sortBy))
|
||||
{
|
||||
$sortBy = $view->getLists()->order;
|
||||
}
|
||||
|
||||
if (empty($order_Dir))
|
||||
{
|
||||
$order_Dir = $view->getLists()->order_Dir;
|
||||
|
||||
if (empty($order_Dir))
|
||||
{
|
||||
$order_Dir = 'asc';
|
||||
}
|
||||
}
|
||||
|
||||
// Static hidden text labels
|
||||
$limitLabel = Text::_('JFIELD_PLG_SEARCH_SEARCHLIMIT_DESC');
|
||||
$orderingDescr = Text::_('JFIELD_ORDERING_DESC');
|
||||
$sortByLabel = Text::_('JGLOBAL_SORT_BY');
|
||||
|
||||
// Order direction dropdown
|
||||
$directionSelect = HTMLHelper::_('FEFHelper.select.genericlist', [
|
||||
'' => $orderingDescr,
|
||||
'asc' => Text::_('JGLOBAL_ORDER_ASCENDING'),
|
||||
'desc' => Text::_('JGLOBAL_ORDER_DESCENDING'),
|
||||
], 'directionTable', [
|
||||
'id' => 'directionTable',
|
||||
'list.select' => $order_Dir,
|
||||
'list.attr' => [
|
||||
'class' => 'input-medium custom-select',
|
||||
'onchange' => 'Joomla.orderTable()',
|
||||
],
|
||||
]);
|
||||
|
||||
// Sort by field dropdown
|
||||
|
||||
$sortTable = HTMLHelper::_('FEFHelper.select.genericlist', array_merge([
|
||||
'' => Text::_('JGLOBAL_SORT_BY'),
|
||||
], $sortFields), 'sortTable', [
|
||||
'id' => 'sortTable',
|
||||
'list.select' => $sortBy,
|
||||
'list.attr' => [
|
||||
'class' => 'input-medium custom-select',
|
||||
'onchange' => 'Joomla.orderTable()',
|
||||
],
|
||||
]);
|
||||
|
||||
|
||||
$html = <<<HTML
|
||||
<div class="akeeba-filter-element akeeba-form-group">
|
||||
<label for="limit" class="element-invisible">
|
||||
$limitLabel
|
||||
</label>
|
||||
{$pagination->getLimitBox()}
|
||||
</div>
|
||||
|
||||
<div class="akeeba-filter-element akeeba-form-group">
|
||||
<label for="directionTable" class="element-invisible">
|
||||
$orderingDescr
|
||||
</label>
|
||||
$directionSelect
|
||||
</div>
|
||||
|
||||
<div class="akeeba-filter-element akeeba-form-group">
|
||||
<label for="sortTable" class="element-invisible">
|
||||
{$sortByLabel}
|
||||
</label>
|
||||
$sortTable
|
||||
</div>
|
||||
|
||||
HTML;
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default sort fields from a model. It creates a hash array where the keys are the model's field names and
|
||||
* the values are the translation keys for their names, following FOF's naming conventions.
|
||||
*
|
||||
* @param DataModel $model The model for which we get the sort fields
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
private static function getSortFields(DataModel $model)
|
||||
{
|
||||
$sortFields = [];
|
||||
$idField = $model->getIdFieldName() ?: 'id';
|
||||
$defaultFieldLabels = [
|
||||
'publish_up' => 'JGLOBAL_FIELD_PUBLISH_UP_LABEL',
|
||||
'publish_down' => 'JGLOBAL_FIELD_PUBLISH_DOWN_LABEL',
|
||||
'created_by' => 'JGLOBAL_FIELD_CREATED_BY_LABEL',
|
||||
'created_on' => 'JGLOBAL_FIELD_CREATED_LABEL',
|
||||
'modified_by' => 'JGLOBAL_FIELD_MODIFIED_BY_LABEL',
|
||||
'modified_on' => 'JGLOBAL_FIELD_MODIFIED_LABEL',
|
||||
'ordering' => 'JGLOBAL_FIELD_FIELD_ORDERING_LABEL',
|
||||
'id' => 'JGLOBAL_FIELD_ID_LABEL',
|
||||
'hits' => 'JGLOBAL_HITS',
|
||||
'title' => 'JGLOBAL_TITLE',
|
||||
'user_id' => 'JGLOBAL_USERNAME',
|
||||
'username' => 'JGLOBAL_USERNAME',
|
||||
];
|
||||
$componentName = $model->getContainer()->componentName;
|
||||
$viewNameSingular = $model->getContainer()->inflector->singularize($model->getName());
|
||||
$viewNamePlural = $model->getContainer()->inflector->pluralize($model->getName());
|
||||
|
||||
foreach ($model->getFields() as $field => $fieldDescriptor)
|
||||
{
|
||||
$possibleKeys = [
|
||||
$componentName . '_' . $viewNamePlural . '_FIELD_' . $field,
|
||||
$componentName . '_' . $viewNamePlural . '_' . $field,
|
||||
$componentName . '_' . $viewNameSingular . '_FIELD_' . $field,
|
||||
$componentName . '_' . $viewNameSingular . '_' . $field,
|
||||
];
|
||||
|
||||
if (array_key_exists($field, $defaultFieldLabels))
|
||||
{
|
||||
$possibleKeys[] = $defaultFieldLabels[$field];
|
||||
}
|
||||
|
||||
if ($field === $idField)
|
||||
{
|
||||
$possibleKeys[] = $defaultFieldLabels['id'];
|
||||
}
|
||||
|
||||
$fieldLabel = '';
|
||||
|
||||
foreach ($possibleKeys as $langKey)
|
||||
{
|
||||
$langKey = strtoupper($langKey);
|
||||
$fieldLabel = Text::_($langKey);
|
||||
|
||||
if ($fieldLabel !== $langKey)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
$fieldLabel = '';
|
||||
}
|
||||
|
||||
if (!empty($fieldLabel))
|
||||
{
|
||||
$sortFields[$field] = (new Joomla\Filter\InputFilter())->clean($fieldLabel);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $sortFields;
|
||||
}
|
||||
}
|
||||
60
libraries/fof30/Utils/FEFHelper/edit.php
Normal file
60
libraries/fof30/Utils/FEFHelper/edit.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FOF
|
||||
* @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd
|
||||
* @license GNU General Public License version 2, or later
|
||||
*/
|
||||
|
||||
defined('_JEXEC') || die;
|
||||
|
||||
use Joomla\CMS\Editor\Editor;
|
||||
use Joomla\CMS\Factory;
|
||||
|
||||
/**
|
||||
* Custom JHtml (HTMLHelper) class. Offers edit (form) view controls compatible with Akeeba Frontend
|
||||
* Framework (FEF).
|
||||
*
|
||||
* Call these methods as JHtml::_('FEFHelper.edit.methodName', $parameter1, $parameter2, ...)
|
||||
*/
|
||||
abstract class FEFHelperEdit
|
||||
{
|
||||
public static function editor($fieldName, $value, array $params = [])
|
||||
{
|
||||
$params = array_merge([
|
||||
'id' => null,
|
||||
'editor' => null,
|
||||
'width' => '100%',
|
||||
'height' => 500,
|
||||
'columns' => 50,
|
||||
'rows' => 20,
|
||||
'created_by' => null,
|
||||
'asset_id' => null,
|
||||
'buttons' => true,
|
||||
'hide' => false,
|
||||
], $params);
|
||||
|
||||
$editorType = $params['editor'];
|
||||
|
||||
if (is_null($editorType))
|
||||
{
|
||||
$editorType = Factory::getConfig()->get('editor');
|
||||
$user = Factory::getUser();
|
||||
|
||||
if (!$user->guest)
|
||||
{
|
||||
$editorType = $user->getParam('editor', $editorType);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_null($params['id']))
|
||||
{
|
||||
$params['id'] = $fieldName;
|
||||
}
|
||||
|
||||
$editor = Editor::getInstance($editorType);
|
||||
|
||||
return $editor->display($fieldName, $value, $params['width'], $params['height'],
|
||||
$params['columns'], $params['rows'], $params['buttons'], $params['id'],
|
||||
$params['asset_id'], $params['created_by'], $params);
|
||||
}
|
||||
}
|
||||
887
libraries/fof30/Utils/FEFHelper/select.php
Normal file
887
libraries/fof30/Utils/FEFHelper/select.php
Normal file
@ -0,0 +1,887 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FOF
|
||||
* @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd
|
||||
* @license GNU General Public License version 2, or later
|
||||
*/
|
||||
|
||||
defined('_JEXEC') || die;
|
||||
|
||||
use FOF30\Utils\ArrayHelper;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Language\Text;
|
||||
|
||||
/**
|
||||
* Custom JHtml (HTMLHelper) class. Offers selects compatible with Akeeba Frontend Framework (FEF)
|
||||
*
|
||||
* Call these methods as JHtml::_('FEFHelper.select.methodName', $parameter1, $parameter2, ...)
|
||||
*/
|
||||
abstract class FEFHelperSelect
|
||||
{
|
||||
/**
|
||||
* Default values for options. Organized by option group.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $optionDefaults = [
|
||||
'option' => [
|
||||
'option.attr' => null,
|
||||
'option.disable' => 'disable',
|
||||
'option.id' => null,
|
||||
'option.key' => 'value',
|
||||
'option.key.toHtml' => true,
|
||||
'option.label' => null,
|
||||
'option.label.toHtml' => true,
|
||||
'option.text' => 'text',
|
||||
'option.text.toHtml' => true,
|
||||
'option.class' => 'class',
|
||||
'option.onclick' => 'onclick',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Generates a yes/no radio list.
|
||||
*
|
||||
* @param string $name The value of the HTML name attribute
|
||||
* @param array $attribs Additional HTML attributes for the `<select>` tag
|
||||
* @param string $selected The key that is selected
|
||||
* @param string $yes Language key for Yes
|
||||
* @param string $no Language key for no
|
||||
* @param mixed $id The id for the field or false for no id
|
||||
*
|
||||
* @return string HTML for the radio list
|
||||
*
|
||||
* @since 1.5
|
||||
* @see JFormFieldRadio
|
||||
*/
|
||||
public static function booleanlist($name, $attribs = [], $selected = null, $yes = 'JYES', $no = 'JNO', $id = false)
|
||||
{
|
||||
$options = [
|
||||
HTMLHelper::_('FEFHelper.select.option', '0', Text::_($no)),
|
||||
HTMLHelper::_('FEFHelper.select.option', '1', Text::_($yes)),
|
||||
];
|
||||
$attribs = array_merge(['forSelect' => 1], $attribs);
|
||||
|
||||
return HTMLHelper::_('FEFHelper.select.radiolist', $options, $name, $attribs, 'value', 'text', (int) $selected, $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a searchable HTML selection list (Chosen on J3, Choices.js on J4).
|
||||
*
|
||||
* @param array $data An array of objects, arrays, or scalars.
|
||||
* @param string $name The value of the HTML name attribute.
|
||||
* @param mixed $attribs Additional HTML attributes for the `<select>` tag. This
|
||||
* can be an array of attributes, or an array of options. Treated as options
|
||||
* if it is the last argument passed. Valid options are:
|
||||
* Format options, see {@see JHtml::$formatOptions}.
|
||||
* Selection options, see {@see JHtmlSelect::options()}.
|
||||
* list.attr, string|array: Additional attributes for the select
|
||||
* element.
|
||||
* id, string: Value to use as the select element id attribute.
|
||||
* Defaults to the same as the name.
|
||||
* list.select, string|array: Identifies one or more option elements
|
||||
* to be selected, based on the option key values.
|
||||
* @param string $optKey The name of the object variable for the option value. If
|
||||
* set to null, the index of the value array is used.
|
||||
* @param string $optText The name of the object variable for the option text.
|
||||
* @param mixed $selected The key that is selected (accepts an array or a string).
|
||||
* @param mixed $idtag Value of the field id or null by default
|
||||
* @param boolean $translate True to translate
|
||||
*
|
||||
* @return string HTML for the select list.
|
||||
*
|
||||
* @since 3.7.2
|
||||
*/
|
||||
public static function smartlist($data, $name, $attribs = null, $optKey = 'value', $optText = 'text', $selected = null, $idtag = false, $translate = false)
|
||||
{
|
||||
$innerList = self::genericlist($data, $name, $attribs, $optKey, $optText, $selected, $idtag, $translate);
|
||||
|
||||
// Joomla 3: Use Chosen
|
||||
if (version_compare(JVERSION, '3.999.999', 'le'))
|
||||
{
|
||||
HTMLHelper::_('formbehavior.chosen');
|
||||
|
||||
return $innerList;
|
||||
}
|
||||
|
||||
// Joomla 4: Use the joomla-field-fancy-select using choices.js
|
||||
try
|
||||
{
|
||||
\Joomla\CMS\Factory::getApplication()->getDocument()->getWebAssetManager()
|
||||
->usePreset('choicesjs')
|
||||
->useScript('webcomponent.field-fancy-select');
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
return $innerList;
|
||||
}
|
||||
|
||||
$j4Attr = array_filter([
|
||||
'class' => $attribs['class'] ?? null,
|
||||
'placeholder' => $attribs['placeholder'] ?? null,
|
||||
], function ($x) {
|
||||
return !empty($x);
|
||||
});
|
||||
|
||||
$dataAttribute = '';
|
||||
|
||||
if (isset($attribs['dataAttribute']))
|
||||
{
|
||||
$dataAttribute = is_string($attribs['dataAttribute']) ? $attribs['dataAttribute'] : '';
|
||||
}
|
||||
|
||||
if ((bool) ($attribs['allowCustom'] ?? false))
|
||||
{
|
||||
$dataAttribute .= ' allow-custom new-item-prefix="#new#"';
|
||||
}
|
||||
|
||||
$remoteSearchUrl = $attribs['remoteSearchURL'] ?? null;
|
||||
$remoteSearch = ((bool) ($attribs['remoteSearch'] ?? false)) && !empty($remoteSearchUrl);
|
||||
$termKey = $attribs['termKey'] ?? 'like';
|
||||
$minTermLength = $attribs['minTermLength'] ?? 3;
|
||||
|
||||
if ($remoteSearch)
|
||||
{
|
||||
$dataAttribute .= ' remote-search';
|
||||
$j4Attr['url'] = $remoteSearchUrl;
|
||||
$j4Attr['term-key'] = $termKey;
|
||||
$j4Attr['min-term-length'] = $minTermLength;
|
||||
}
|
||||
|
||||
if (isset($attribs['required']))
|
||||
{
|
||||
$j4Attr['class'] = ($j4Attr['class'] ?? '') . ' required';
|
||||
$dataAttribute .= ' required';
|
||||
}
|
||||
|
||||
if (isset($attribs['readonly']))
|
||||
{
|
||||
return $innerList;
|
||||
}
|
||||
|
||||
return sprintf("<joomla-field-fancy-select %s %s>%s</joomla-field-fancy-select>", ArrayHelper::toString($j4Attr), $dataAttribute, $innerList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an HTML selection list.
|
||||
*
|
||||
* @param array $data An array of objects, arrays, or scalars.
|
||||
* @param string $name The value of the HTML name attribute.
|
||||
* @param mixed $attribs Additional HTML attributes for the `<select>` tag. This
|
||||
* can be an array of attributes, or an array of options. Treated as options
|
||||
* if it is the last argument passed. Valid options are:
|
||||
* Format options, see {@see JHtml::$formatOptions}.
|
||||
* Selection options, see {@see JHtmlSelect::options()}.
|
||||
* list.attr, string|array: Additional attributes for the select
|
||||
* element.
|
||||
* id, string: Value to use as the select element id attribute.
|
||||
* Defaults to the same as the name.
|
||||
* list.select, string|array: Identifies one or more option elements
|
||||
* to be selected, based on the option key values.
|
||||
* @param string $optKey The name of the object variable for the option value. If
|
||||
* set to null, the index of the value array is used.
|
||||
* @param string $optText The name of the object variable for the option text.
|
||||
* @param mixed $selected The key that is selected (accepts an array or a string).
|
||||
* @param mixed $idtag Value of the field id or null by default
|
||||
* @param boolean $translate True to translate
|
||||
*
|
||||
* @return string HTML for the select list.
|
||||
*
|
||||
* @since 1.5
|
||||
*/
|
||||
public static function genericlist($data, $name, $attribs = null, $optKey = 'value', $optText = 'text', $selected = null, $idtag = false, $translate = false)
|
||||
{
|
||||
// Set default options
|
||||
$options = array_merge(HTMLHelper::$formatOptions, ['format.depth' => 0, 'id' => false]);
|
||||
|
||||
if (is_array($attribs) && func_num_args() === 3)
|
||||
{
|
||||
// Assume we have an options array
|
||||
$options = array_merge($options, $attribs);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get options from the parameters
|
||||
$options['id'] = $idtag;
|
||||
$options['list.attr'] = $attribs;
|
||||
$options['list.translate'] = $translate;
|
||||
$options['option.key'] = $optKey;
|
||||
$options['option.text'] = $optText;
|
||||
$options['list.select'] = $selected;
|
||||
}
|
||||
|
||||
$attribs = '';
|
||||
|
||||
if (isset($options['list.attr']))
|
||||
{
|
||||
if (is_array($options['list.attr']))
|
||||
{
|
||||
$attribs = ArrayHelper::toString($options['list.attr']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$attribs = $options['list.attr'];
|
||||
}
|
||||
|
||||
if ($attribs !== '')
|
||||
{
|
||||
$attribs = ' ' . $attribs;
|
||||
}
|
||||
}
|
||||
|
||||
$id = $options['id'] !== false ? $options['id'] : $name;
|
||||
$id = str_replace(['[', ']', ' '], '', $id);
|
||||
|
||||
$baseIndent = str_repeat($options['format.indent'], $options['format.depth']++);
|
||||
$html = $baseIndent . '<select' . ($id !== '' ? ' id="' . $id . '"' : '') . ' name="' . $name . '"' . $attribs . '>' . $options['format.eol']
|
||||
. static::options($data, $options) . $baseIndent . '</select>' . $options['format.eol'];
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a grouped HTML selection list from nested arrays.
|
||||
*
|
||||
* @param array $data An array of groups, each of which is an array of options.
|
||||
* @param string $name The value of the HTML name attribute
|
||||
* @param array $options Options, an array of key/value pairs. Valid options are:
|
||||
* Format options, {@see JHtml::$formatOptions}.
|
||||
* Selection options. See {@see JHtmlSelect::options()}.
|
||||
* group.id: The property in each group to use as the group id
|
||||
* attribute. Defaults to none.
|
||||
* group.label: The property in each group to use as the group
|
||||
* label. Defaults to "text". If set to null, the data array index key is
|
||||
* used.
|
||||
* group.items: The property in each group to use as the array of
|
||||
* items in the group. Defaults to "items". If set to null, group.id and
|
||||
* group. label are forced to null and the data element is assumed to be a
|
||||
* list of selections.
|
||||
* id: Value to use as the select element id attribute. Defaults to
|
||||
* the same as the name.
|
||||
* list.attr: Attributes for the select element. Can be a string or
|
||||
* an array of key/value pairs. Defaults to none.
|
||||
* list.select: either the value of one selected option or an array
|
||||
* of selected options. Default: none.
|
||||
* list.translate: Boolean. If set, text and labels are translated via
|
||||
* JText::_().
|
||||
*
|
||||
* @return string HTML for the select list
|
||||
*
|
||||
* @throws RuntimeException If a group has contents that cannot be processed.
|
||||
*/
|
||||
public static function groupedlist($data, $name, $options = [])
|
||||
{
|
||||
// Set default options and overwrite with anything passed in
|
||||
$options = array_merge(
|
||||
HTMLHelper::$formatOptions,
|
||||
[
|
||||
'format.depth' => 0, 'group.items' => 'items', 'group.label' => 'text', 'group.label.toHtml' => true,
|
||||
'id' => false,
|
||||
],
|
||||
$options
|
||||
);
|
||||
|
||||
// Apply option rules
|
||||
if ($options['group.items'] === null)
|
||||
{
|
||||
$options['group.label'] = null;
|
||||
}
|
||||
|
||||
$attribs = '';
|
||||
|
||||
if (isset($options['list.attr']))
|
||||
{
|
||||
if (is_array($options['list.attr']))
|
||||
{
|
||||
$attribs = ArrayHelper::toString($options['list.attr']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$attribs = $options['list.attr'];
|
||||
}
|
||||
|
||||
if ($attribs !== '')
|
||||
{
|
||||
$attribs = ' ' . $attribs;
|
||||
}
|
||||
}
|
||||
|
||||
$id = $options['id'] !== false ? $options['id'] : $name;
|
||||
$id = str_replace(['[', ']', ' '], '', $id);
|
||||
|
||||
// Disable groups in the options.
|
||||
$options['groups'] = false;
|
||||
|
||||
$baseIndent = str_repeat($options['format.indent'], $options['format.depth']++);
|
||||
$html = $baseIndent . '<select' . ($id !== '' ? ' id="' . $id . '"' : '') . ' name="' . $name . '"' . $attribs . '>' . $options['format.eol'];
|
||||
$groupIndent = str_repeat($options['format.indent'], $options['format.depth']++);
|
||||
|
||||
foreach ($data as $dataKey => $group)
|
||||
{
|
||||
$label = $dataKey;
|
||||
$id = '';
|
||||
$noGroup = is_int($dataKey);
|
||||
|
||||
if ($options['group.items'] == null)
|
||||
{
|
||||
// Sub-list is an associative array
|
||||
$subList = $group;
|
||||
}
|
||||
elseif (is_array($group))
|
||||
{
|
||||
// Sub-list is in an element of an array.
|
||||
$subList = $group[$options['group.items']];
|
||||
|
||||
if (isset($group[$options['group.label']]))
|
||||
{
|
||||
$label = $group[$options['group.label']];
|
||||
$noGroup = false;
|
||||
}
|
||||
|
||||
if (isset($options['group.id']) && isset($group[$options['group.id']]))
|
||||
{
|
||||
$id = $group[$options['group.id']];
|
||||
$noGroup = false;
|
||||
}
|
||||
}
|
||||
elseif (is_object($group))
|
||||
{
|
||||
// Sub-list is in a property of an object
|
||||
$subList = $group->{$options['group.items']};
|
||||
|
||||
if (isset($group->{$options['group.label']}))
|
||||
{
|
||||
$label = $group->{$options['group.label']};
|
||||
$noGroup = false;
|
||||
}
|
||||
|
||||
if (isset($options['group.id']) && isset($group->{$options['group.id']}))
|
||||
{
|
||||
$id = $group->{$options['group.id']};
|
||||
$noGroup = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new RuntimeException('Invalid group contents.', 1);
|
||||
}
|
||||
|
||||
if ($noGroup)
|
||||
{
|
||||
$html .= static::options($subList, $options);
|
||||
}
|
||||
else
|
||||
{
|
||||
$html .= $groupIndent . '<optgroup' . (empty($id) ? '' : ' id="' . $id . '"') . ' label="'
|
||||
. ($options['group.label.toHtml'] ? htmlspecialchars($label, ENT_COMPAT, 'UTF-8') : $label) . '">' . $options['format.eol']
|
||||
. static::options($subList, $options) . $groupIndent . '</optgroup>' . $options['format.eol'];
|
||||
}
|
||||
}
|
||||
|
||||
$html .= $baseIndent . '</select>' . $options['format.eol'];
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a selection list of integers.
|
||||
*
|
||||
* @param integer $start The start integer
|
||||
* @param integer $end The end integer
|
||||
* @param integer $inc The increment
|
||||
* @param string $name The value of the HTML name attribute
|
||||
* @param mixed $attribs Additional HTML attributes for the `<select>` tag, an array of
|
||||
* attributes, or an array of options. Treated as options if it is the last
|
||||
* argument passed.
|
||||
* @param mixed $selected The key that is selected
|
||||
* @param string $format The printf format to be applied to the number
|
||||
*
|
||||
* @return string HTML for the select list
|
||||
*/
|
||||
public static function integerlist($start, $end, $inc, $name, $attribs = null, $selected = null, $format = '')
|
||||
{
|
||||
// Set default options
|
||||
$options = array_merge(HTMLHelper::$formatOptions, [
|
||||
'format.depth' => 0, 'option.format' => '', 'id' => null,
|
||||
]);
|
||||
|
||||
if (is_array($attribs) && func_num_args() === 5)
|
||||
{
|
||||
// Assume we have an options array
|
||||
$options = array_merge($options, $attribs);
|
||||
|
||||
// Extract the format and remove it from downstream options
|
||||
$format = $options['option.format'];
|
||||
unset($options['option.format']);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get options from the parameters
|
||||
$options['list.attr'] = $attribs;
|
||||
$options['list.select'] = $selected;
|
||||
}
|
||||
|
||||
$start = (int) $start;
|
||||
$end = (int) $end;
|
||||
$inc = (int) $inc;
|
||||
|
||||
$data = [];
|
||||
|
||||
for ($i = $start; $i <= $end; $i += $inc)
|
||||
{
|
||||
$data[$i] = $format ? sprintf($format, $i) : $i;
|
||||
}
|
||||
|
||||
// Tell genericlist() to use array keys
|
||||
$options['option.key'] = null;
|
||||
|
||||
return HTMLHelper::_('FEFHelper.select.genericlist', $data, $name, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an object that represents an option in an option list.
|
||||
*
|
||||
* @param string $value The value of the option
|
||||
* @param string $text The text for the option
|
||||
* @param mixed $optKey If a string, the returned object property name for
|
||||
* the value. If an array, options. Valid options are:
|
||||
* attr: String|array. Additional attributes for this option.
|
||||
* Defaults to none.
|
||||
* disable: Boolean. If set, this option is disabled.
|
||||
* label: String. The value for the option label.
|
||||
* option.attr: The property in each option array to use for
|
||||
* additional selection attributes. Defaults to none.
|
||||
* option.disable: The property that will hold the disabled state.
|
||||
* Defaults to "disable".
|
||||
* option.key: The property that will hold the selection value.
|
||||
* Defaults to "value".
|
||||
* option.label: The property in each option array to use as the
|
||||
* selection label attribute. If a "label" option is provided, defaults to
|
||||
* "label", if no label is given, defaults to null (none).
|
||||
* option.text: The property that will hold the the displayed text.
|
||||
* Defaults to "text". If set to null, the option array is assumed to be a
|
||||
* list of displayable scalars.
|
||||
* @param string $optText The property that will hold the the displayed text. This
|
||||
* parameter is ignored if an options array is passed.
|
||||
* @param boolean $disable Not used.
|
||||
*
|
||||
* @return stdClass
|
||||
*/
|
||||
public static function option($value, $text = '', $optKey = 'value', $optText = 'text', $disable = false)
|
||||
{
|
||||
$options = [
|
||||
'attr' => null,
|
||||
'disable' => false,
|
||||
'option.attr' => null,
|
||||
'option.disable' => 'disable',
|
||||
'option.key' => 'value',
|
||||
'option.label' => null,
|
||||
'option.text' => 'text',
|
||||
];
|
||||
|
||||
if (is_array($optKey))
|
||||
{
|
||||
// Merge in caller's options
|
||||
$options = array_merge($options, $optKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get options from the parameters
|
||||
$options['option.key'] = $optKey;
|
||||
$options['option.text'] = $optText;
|
||||
$options['disable'] = $disable;
|
||||
}
|
||||
|
||||
$obj = new stdClass;
|
||||
$obj->{$options['option.key']} = $value;
|
||||
$obj->{$options['option.text']} = trim($text) ? $text : $value;
|
||||
|
||||
/*
|
||||
* If a label is provided, save it. If no label is provided and there is
|
||||
* a label name, initialise to an empty string.
|
||||
*/
|
||||
$hasProperty = $options['option.label'] !== null;
|
||||
|
||||
if (isset($options['label']))
|
||||
{
|
||||
$labelProperty = $hasProperty ? $options['option.label'] : 'label';
|
||||
$obj->$labelProperty = $options['label'];
|
||||
}
|
||||
elseif ($hasProperty)
|
||||
{
|
||||
$obj->{$options['option.label']} = '';
|
||||
}
|
||||
|
||||
// Set attributes only if there is a property and a value
|
||||
if ($options['attr'] !== null)
|
||||
{
|
||||
$obj->{$options['option.attr']} = $options['attr'];
|
||||
}
|
||||
|
||||
// Set disable only if it has a property and a value
|
||||
if ($options['disable'] !== null)
|
||||
{
|
||||
$obj->{$options['option.disable']} = $options['disable'];
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the option tags for an HTML select list (with no select tag
|
||||
* surrounding the options).
|
||||
*
|
||||
* @param array $arr An array of objects, arrays, or values.
|
||||
* @param mixed $optKey If a string, this is the name of the object variable for
|
||||
* the option value. If null, the index of the array of objects is used. If
|
||||
* an array, this is a set of options, as key/value pairs. Valid options are:
|
||||
* -Format options, {@see JHtml::$formatOptions}.
|
||||
* -groups: Boolean. If set, looks for keys with the value
|
||||
* "<optgroup>" and synthesizes groups from them. Deprecated. Defaults
|
||||
* true for backwards compatibility.
|
||||
* -list.select: either the value of one selected option or an array
|
||||
* of selected options. Default: none.
|
||||
* -list.translate: Boolean. If set, text and labels are translated via
|
||||
* JText::_(). Default is false.
|
||||
* -option.id: The property in each option array to use as the
|
||||
* selection id attribute. Defaults to none.
|
||||
* -option.key: The property in each option array to use as the
|
||||
* selection value. Defaults to "value". If set to null, the index of the
|
||||
* option array is used.
|
||||
* -option.label: The property in each option array to use as the
|
||||
* selection label attribute. Defaults to null (none).
|
||||
* -option.text: The property in each option array to use as the
|
||||
* displayed text. Defaults to "text". If set to null, the option array is
|
||||
* assumed to be a list of displayable scalars.
|
||||
* -option.attr: The property in each option array to use for
|
||||
* additional selection attributes. Defaults to none.
|
||||
* -option.disable: The property that will hold the disabled state.
|
||||
* Defaults to "disable".
|
||||
* -option.key: The property that will hold the selection value.
|
||||
* Defaults to "value".
|
||||
* -option.text: The property that will hold the the displayed text.
|
||||
* Defaults to "text". If set to null, the option array is assumed to be a
|
||||
* list of displayable scalars.
|
||||
* @param string $optText The name of the object variable for the option text.
|
||||
* @param mixed $selected The key that is selected (accepts an array or a string)
|
||||
* @param boolean $translate Translate the option values.
|
||||
*
|
||||
* @return string HTML for the select list
|
||||
*/
|
||||
public static function options($arr, $optKey = 'value', $optText = 'text', $selected = null, $translate = false)
|
||||
{
|
||||
$options = array_merge(
|
||||
HTMLHelper::$formatOptions,
|
||||
static::$optionDefaults['option'],
|
||||
['format.depth' => 0, 'groups' => true, 'list.select' => null, 'list.translate' => false]
|
||||
);
|
||||
|
||||
if (is_array($optKey))
|
||||
{
|
||||
// Set default options and overwrite with anything passed in
|
||||
$options = array_merge($options, $optKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get options from the parameters
|
||||
$options['option.key'] = $optKey;
|
||||
$options['option.text'] = $optText;
|
||||
$options['list.select'] = $selected;
|
||||
$options['list.translate'] = $translate;
|
||||
}
|
||||
|
||||
$html = '';
|
||||
$baseIndent = str_repeat($options['format.indent'], $options['format.depth']);
|
||||
|
||||
foreach ($arr as $elementKey => &$element)
|
||||
{
|
||||
$attr = '';
|
||||
$extra = '';
|
||||
$label = '';
|
||||
$id = '';
|
||||
|
||||
if (is_array($element))
|
||||
{
|
||||
$key = $options['option.key'] === null ? $elementKey : $element[$options['option.key']];
|
||||
$text = $element[$options['option.text']];
|
||||
|
||||
if (isset($element[$options['option.attr']]))
|
||||
{
|
||||
$attr = $element[$options['option.attr']];
|
||||
}
|
||||
|
||||
if (isset($element[$options['option.id']]))
|
||||
{
|
||||
$id = $element[$options['option.id']];
|
||||
}
|
||||
|
||||
if (isset($element[$options['option.label']]))
|
||||
{
|
||||
$label = $element[$options['option.label']];
|
||||
}
|
||||
|
||||
if (isset($element[$options['option.disable']]) && $element[$options['option.disable']])
|
||||
{
|
||||
$extra .= ' disabled="disabled"';
|
||||
}
|
||||
}
|
||||
elseif (is_object($element))
|
||||
{
|
||||
$key = $options['option.key'] === null ? $elementKey : $element->{$options['option.key']};
|
||||
$text = $element->{$options['option.text']};
|
||||
|
||||
if (isset($element->{$options['option.attr']}))
|
||||
{
|
||||
$attr = $element->{$options['option.attr']};
|
||||
}
|
||||
|
||||
if (isset($element->{$options['option.id']}))
|
||||
{
|
||||
$id = $element->{$options['option.id']};
|
||||
}
|
||||
|
||||
if (isset($element->{$options['option.label']}))
|
||||
{
|
||||
$label = $element->{$options['option.label']};
|
||||
}
|
||||
|
||||
if (isset($element->{$options['option.disable']}) && $element->{$options['option.disable']})
|
||||
{
|
||||
$extra .= ' disabled="disabled"';
|
||||
}
|
||||
|
||||
if (isset($element->{$options['option.class']}) && $element->{$options['option.class']})
|
||||
{
|
||||
$extra .= ' class="' . $element->{$options['option.class']} . '"';
|
||||
}
|
||||
|
||||
if (isset($element->{$options['option.onclick']}) && $element->{$options['option.onclick']})
|
||||
{
|
||||
$extra .= ' onclick="' . $element->{$options['option.onclick']} . '"';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is a simple associative array
|
||||
$key = $elementKey;
|
||||
$text = $element;
|
||||
}
|
||||
|
||||
/*
|
||||
* The use of options that contain optgroup HTML elements was
|
||||
* somewhat hacked for J1.5. J1.6 introduces the grouplist() method
|
||||
* to handle this better. The old solution is retained through the
|
||||
* "groups" option, which defaults true in J1.6, but should be
|
||||
* deprecated at some point in the future.
|
||||
*/
|
||||
|
||||
$key = (string) $key;
|
||||
|
||||
if ($key === '<OPTGROUP>' && $options['groups'])
|
||||
{
|
||||
$html .= $baseIndent . '<optgroup label="' . ($options['list.translate'] ? Text::_($text) : $text) . '">' . $options['format.eol'];
|
||||
$baseIndent = str_repeat($options['format.indent'], ++$options['format.depth']);
|
||||
}
|
||||
elseif ($key === '</OPTGROUP>' && $options['groups'])
|
||||
{
|
||||
$baseIndent = str_repeat($options['format.indent'], --$options['format.depth']);
|
||||
$html .= $baseIndent . '</optgroup>' . $options['format.eol'];
|
||||
}
|
||||
else
|
||||
{
|
||||
// If no string after hyphen - take hyphen out
|
||||
$splitText = explode(' - ', $text, 2);
|
||||
$text = $splitText[0];
|
||||
|
||||
if (isset($splitText[1]) && $splitText[1] !== '' && !preg_match('/^[\s]+$/', $splitText[1]))
|
||||
{
|
||||
$text .= ' - ' . $splitText[1];
|
||||
}
|
||||
|
||||
if (!empty($label) && $options['list.translate'])
|
||||
{
|
||||
$label = Text::_($label);
|
||||
}
|
||||
|
||||
if ($options['option.label.toHtml'])
|
||||
{
|
||||
$label = htmlentities($label);
|
||||
}
|
||||
|
||||
if (is_array($attr))
|
||||
{
|
||||
$attr = ArrayHelper::toString($attr);
|
||||
}
|
||||
else
|
||||
{
|
||||
$attr = trim($attr);
|
||||
}
|
||||
|
||||
$extra = ($id ? ' id="' . $id . '"' : '') . ($label ? ' label="' . $label . '"' : '') . ($attr ? ' ' . $attr : '') . $extra;
|
||||
|
||||
if (is_array($options['list.select']))
|
||||
{
|
||||
foreach ($options['list.select'] as $val)
|
||||
{
|
||||
$key2 = is_object($val) ? $val->{$options['option.key']} : $val;
|
||||
|
||||
if ($key == $key2)
|
||||
{
|
||||
$extra .= ' selected="selected"';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif ((string) $key === (string) $options['list.select'])
|
||||
{
|
||||
$extra .= ' selected="selected"';
|
||||
}
|
||||
|
||||
if ($options['list.translate'])
|
||||
{
|
||||
$text = Text::_($text);
|
||||
}
|
||||
|
||||
// Generate the option, encoding as required
|
||||
$html .= $baseIndent . '<option value="' . ($options['option.key.toHtml'] ? htmlspecialchars($key, ENT_COMPAT, 'UTF-8') : $key) . '"'
|
||||
. $extra . '>';
|
||||
$html .= $options['option.text.toHtml'] ? htmlentities(html_entity_decode($text, ENT_COMPAT, 'UTF-8'), ENT_COMPAT, 'UTF-8') : $text;
|
||||
$html .= '</option>' . $options['format.eol'];
|
||||
}
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an HTML radio list.
|
||||
*
|
||||
* @param array $data An array of objects
|
||||
* @param string $name The value of the HTML name attribute
|
||||
* @param string $attribs Additional HTML attributes for the `<select>` tag
|
||||
* @param mixed $optKey The key that is selected
|
||||
* @param string $optText The name of the object variable for the option value
|
||||
* @param string $selected The name of the object variable for the option text
|
||||
* @param boolean $idtag Value of the field id or null by default
|
||||
* @param boolean $translate True if options will be translated
|
||||
*
|
||||
* @return string HTML for the select list
|
||||
*/
|
||||
public static function radiolist($data, $name, $attribs = null, $optKey = 'value', $optText = 'text', $selected = null, $idtag = false,
|
||||
$translate = false)
|
||||
{
|
||||
|
||||
$forSelect = false;
|
||||
|
||||
if (isset($attribs['forSelect']))
|
||||
{
|
||||
$forSelect = (bool) ($attribs['forSelect']);
|
||||
unset($attribs['forSelect']);
|
||||
}
|
||||
|
||||
if (is_array($attribs))
|
||||
{
|
||||
$attribs = ArrayHelper::toString($attribs);
|
||||
}
|
||||
|
||||
$id_text = empty($idtag) ? $name : $idtag;
|
||||
|
||||
$html = '';
|
||||
|
||||
foreach ($data as $optionObject)
|
||||
{
|
||||
$optionValue = $optionObject->$optKey;
|
||||
$labelText = $translate ? Text::_($optionObject->$optText) : $optionObject->$optText;
|
||||
$id = ($optionObject->id ?? null);
|
||||
|
||||
$extra = '';
|
||||
$id = $id ? $optionObject->id : $id_text . $optionValue;
|
||||
|
||||
if (is_array($selected))
|
||||
{
|
||||
foreach ($selected as $val)
|
||||
{
|
||||
$k2 = is_object($val) ? $val->$optKey : $val;
|
||||
|
||||
if ($optionValue == $k2)
|
||||
{
|
||||
$extra .= ' selected="selected" ';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$extra .= ((string) $optionValue === (string) $selected ? ' checked="checked" ' : '');
|
||||
}
|
||||
|
||||
if ($forSelect)
|
||||
{
|
||||
$html .= "\n\t" . '<input type="radio" name="' . $name . '" id="' . $id . '" value="' . $optionValue . '" ' . $extra
|
||||
. $attribs . ' />';
|
||||
$html .= "\n\t" . '<label for="' . $id . '" id="' . $id . '-lbl">' . $labelText . '</label>';
|
||||
}
|
||||
else
|
||||
{
|
||||
$html .= "\n\t" . '<label for="' . $id . '" id="' . $id . '-lbl">';
|
||||
$html .= "\n\t\n\t" . '<input type="radio" name="' . $name . '" id="' . $id . '" value="' . $optionValue . '" ' . $extra
|
||||
. $attribs . ' />' . $labelText;
|
||||
$html .= "\n\t" . '</label>';
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$html .= "\n";
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates two radio buttons styled with FEF to appear as a YES/NO switch
|
||||
*
|
||||
* @param string $name Name of the field
|
||||
* @param string $selected Selected field
|
||||
* @param array $attribs Additional attributes to add to the switch
|
||||
*
|
||||
* @return string The HTML for the switch
|
||||
*/
|
||||
public static function booleanswitch($name, $selected, array $attribs = [])
|
||||
{
|
||||
if (empty($attribs))
|
||||
{
|
||||
$attribs = ['class' => 'akeeba-toggle'];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isset($attribs['class']))
|
||||
{
|
||||
$attribs['class'] .= ' akeeba-toggle';
|
||||
}
|
||||
else
|
||||
{
|
||||
$attribs['class'] = 'akeeba-toggle';
|
||||
}
|
||||
}
|
||||
|
||||
$temp = '';
|
||||
|
||||
foreach ($attribs as $key => $value)
|
||||
{
|
||||
$temp .= $key . ' = "' . $value . '"';
|
||||
}
|
||||
|
||||
$attribs = $temp;
|
||||
|
||||
$checked_1 = $selected ? '' : 'checked ';
|
||||
$checked_2 = $selected ? 'checked ' : '';
|
||||
|
||||
$html = '<div ' . $attribs . '>';
|
||||
$html .= '<input type="radio" class="radio-yes" name="' . $name . '" ' . $checked_2 . 'id="' . $name . '-2" value="1">';
|
||||
$html .= '<label for="' . $name . '-2" class="green">' . Text::_('JYES') . '</label>';
|
||||
$html .= '<input type="radio" class="radio-no" name="' . $name . '" ' . $checked_1 . 'id="' . $name . '-1" value="0">';
|
||||
$html .= '<label for="' . $name . '-1" class="red">' . Text::_('JNO') . '</label>';
|
||||
$html .= '</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user