primo commit
This commit is contained in:
923
libraries/fof40/Html/FEFHelper/BrowseView.php
Normal file
923
libraries/fof40/Html/FEFHelper/BrowseView.php
Normal file
@ -0,0 +1,923 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FOF
|
||||
* @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
|
||||
* @license GNU General Public License version 3, or later
|
||||
*/
|
||||
|
||||
namespace FOF40\Html\FEFHelper;
|
||||
|
||||
defined('_JEXEC') || die;
|
||||
|
||||
use FOF40\Container\Container;
|
||||
use FOF40\Html\SelectOptions;
|
||||
use FOF40\Model\DataModel;
|
||||
use FOF40\Utils\ArrayHelper;
|
||||
use FOF40\View\DataView\DataViewInterface;
|
||||
use FOF40\View\View;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Language\Text;
|
||||
|
||||
/**
|
||||
* 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(string $fieldName): string
|
||||
{
|
||||
$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(string $fieldName): string
|
||||
{
|
||||
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 string
|
||||
*/
|
||||
public static function sortgrid(string $field, ?string $langKey = null): string
|
||||
{
|
||||
/** @var DataViewInterface $view */
|
||||
$view = self::getViewFromBacktrace();
|
||||
|
||||
if (is_null($langKey))
|
||||
{
|
||||
$langKey = self::fieldLabelKey($field);
|
||||
}
|
||||
|
||||
return HTMLHelper::_('FEFHelp.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(string $localField, string $modelTitleField = 'title', ?string $modelName = null,
|
||||
?string $placeholder = null, array $params = []): string
|
||||
{
|
||||
/** @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 Text 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(string $localField, ?string $searchField = null, ?string $placeholder = null,
|
||||
array $attributes = []): string
|
||||
{
|
||||
/** @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['placeholder'] = !isset($attributes['placeholder']) ? $view->escape(Text::_($placeholder)) : $attributes['placeholder'];
|
||||
$attributes['title'] = $attributes['title'] ?? $attributes['placeholder'];
|
||||
$attributes['value'] = $view->escape($model->getState($localField));
|
||||
|
||||
if (!isset($attributes['onchange']))
|
||||
{
|
||||
$attributes['class'] = trim(($attributes['class'] ?? '') . ' akeebaCommonEventsOnChangeSubmit');
|
||||
$attributes['data-akeebasubmittarget'] = $attributes['data-akeebasubmittarget'] ?? 'adminForm';
|
||||
}
|
||||
|
||||
// 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 HTMLHelper 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(string $localField, array $options, ?string $placeholder = null,
|
||||
array $params = []): string
|
||||
{
|
||||
/** @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(string $localField, ?string $placeholder = null, array $params = []): string
|
||||
{
|
||||
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(string $localField, ?string $placeholder = null, array $params = []): string
|
||||
{
|
||||
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 mixed $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 HTMLHelper 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(string $name, string $modelName, $currentValue, array $params = [],
|
||||
array $modelState = [], array $options = []): string
|
||||
{
|
||||
$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 HTMLHelper 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(string $value, ?string $modelName = null, array $params = [],
|
||||
array $modelState = [], array $options = []): ?string
|
||||
{
|
||||
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 HTMLHelper options
|
||||
*
|
||||
* @param mixed $selected The currently selected value
|
||||
* @param array $data The HTMLHelper 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, array $data, string $optKey = 'value', string $optText = 'text', bool $selectFirst = true): ?string
|
||||
{
|
||||
$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 Text::_(). 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 string $name
|
||||
* @param array $options
|
||||
* @param mixed $currentValue
|
||||
* @param array $params
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function genericSelect(string $name, array $options, $currentValue, array $params = []): string
|
||||
{
|
||||
$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'];
|
||||
|
||||
$classes = $params['class'] ?? '';
|
||||
$classes = is_array($classes) ? implode(' ', $classes) : $classes;
|
||||
|
||||
// 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';
|
||||
$classes .= ' akeebaCommonEventsOnChangeSubmit';
|
||||
$params['data-akeebasubmittarget'] = $formName;
|
||||
}
|
||||
|
||||
// Construct SELECT element's attributes
|
||||
$attr = [
|
||||
'class' => trim($classes) ?: null,
|
||||
'size' => ($params['size'] ?? null) ?: null,
|
||||
'multiple' => ($params['multiple'] ?? null) ?: null,
|
||||
'required' => ($params['required'] ?? false) ?: null,
|
||||
'aria-required' => ($params['required'] ?? false) ? 'true' : null,
|
||||
'autofocus' => ($params['autofocus'] ?? false) ?: null,
|
||||
'disabled' => (($params['disabled'] ?? false) || ($params['readonly'] ?? false)) ?: null,
|
||||
'onchange' => $params['onchange'] ?? null,
|
||||
];
|
||||
|
||||
$attr = array_filter($attr, function ($x) {
|
||||
return !is_null($x);
|
||||
});
|
||||
|
||||
// We merge the constructed SELECT element's attributes with the 'list.attr' array, if it was provided
|
||||
$params['list.attr'] = array_merge($attr, (($params['list.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::_('FEFHelp.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::_('FEFHelp.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) === 0)
|
||||
{
|
||||
$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::_('FEFHelp.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(string $text, DataModel $item): string
|
||||
{
|
||||
$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(): View
|
||||
{
|
||||
// In case we are on a brain-dead 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get HTMLHelper 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 Text? Default: true source_format Set
|
||||
* to "optionsobject" if you're returning an array of HTMLHelper options. Ignored otherwise.
|
||||
*
|
||||
* @param array $attribs
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
private static function getOptionsFromSource(array $attribs = []): array
|
||||
{
|
||||
$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
|
||||
// ...and so does the option
|
||||
if (class_exists($source_class, true) && 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::_('FEFHelp.select.option', $key, $value, 'value', 'text');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reset($options);
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get HTMLHelper 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 Text? 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 HTMLHelper select options you want to add in front of the model's returned
|
||||
* values
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
private static function getOptionsFromModel(string $modelName, array $params = [], array $modelState = [],
|
||||
array $options = []): array
|
||||
{
|
||||
// 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::_('FEFHelp.select.option', null, Text::_($params['none']));
|
||||
|
||||
if ($params['none_as_zero'])
|
||||
{
|
||||
$options[] = HTMLHelper::_('FEFHelp.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);
|
||||
|
||||
foreach ($items as $item)
|
||||
{
|
||||
$value = $item->{$params['value_field']};
|
||||
|
||||
if ($params['translate'])
|
||||
{
|
||||
$value = Text::_($value);
|
||||
}
|
||||
|
||||
$options[] = HTMLHelper::_('FEFHelp.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(): Container
|
||||
{
|
||||
// In case we are on a brain-dead 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 !== 0)
|
||||
{
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
867
libraries/fof40/Html/FEFHelper/browse.php
Normal file
867
libraries/fof40/Html/FEFHelper/browse.php
Normal file
@ -0,0 +1,867 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FOF
|
||||
* @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
|
||||
* @license GNU General Public License version 3, or later
|
||||
*/
|
||||
|
||||
defined('_JEXEC') || die;
|
||||
|
||||
use FOF40\Html\FEFHelper\BrowseView;
|
||||
use FOF40\Model\DataModel;
|
||||
use FOF40\Utils\ArrayHelper;
|
||||
use FOF40\View\DataView\DataViewInterface;
|
||||
use FOF40\View\DataView\Raw as DataViewRaw;
|
||||
use Joomla\CMS\Factory as JoomlaFactory;
|
||||
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 HTMLHelper::_('FEFHelp.browse.methodName', $parameter1, $parameter2, ...)
|
||||
*
|
||||
* @noinspection PhpIllegalPsrClassPathInspection
|
||||
*/
|
||||
abstract class FEFHelpBrowse
|
||||
{
|
||||
/**
|
||||
* 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(int $i, string $task, $prefix = '', string $active_title = '',
|
||||
string $inactive_title = '', bool $tip = false,
|
||||
string $active_class = '', string $inactive_class = '',
|
||||
bool $enabled = true, bool $translate = true, string $checkbox = 'cb'): string
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
if ($enabled)
|
||||
{
|
||||
$btnColor = 'grey';
|
||||
|
||||
if (substr($active_class, 0, 2) == '--')
|
||||
{
|
||||
[$btnColor, $active_class] = explode(' ', $active_class, 2);
|
||||
$btnColor = ltrim($btnColor, '-');
|
||||
}
|
||||
|
||||
$html = [];
|
||||
$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>';
|
||||
|
||||
return implode($html);
|
||||
}
|
||||
|
||||
$btnColor = 'grey';
|
||||
|
||||
if (substr($inactive_class, 0, 2) == '--')
|
||||
{
|
||||
[$btnColor, $inactive_class] = explode(' ', $inactive_class, 2);
|
||||
$btnColor = ltrim($btnColor, '-');
|
||||
}
|
||||
|
||||
$html = [];
|
||||
$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(array $states, int $value, int $i, $prefix = '', bool $enabled = true,
|
||||
bool $translate = true, string $checkbox = 'cb'): string
|
||||
{
|
||||
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(int $value, int $i, $prefix = '', bool $enabled = true, string $checkbox = 'cb',
|
||||
?string $publish_up = null, ?string $publish_down = null): string
|
||||
{
|
||||
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 = JoomlaFactory::getDbo()->getNullDate();
|
||||
$nowDate = JoomlaFactory::getDate()->toUnix();
|
||||
|
||||
$tz = JoomlaFactory::getUser()->getTimezone();
|
||||
|
||||
$publish_up = (!empty($publish_up) && ($publish_up != $nullDate)) ? JoomlaFactory::getDate($publish_up, 'UTC')->setTimeZone($tz) : false;
|
||||
$publish_down = (!empty($publish_down) && ($publish_down != $nullDate)) ? JoomlaFactory::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 (array_keys($states) as $key)
|
||||
{
|
||||
// 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(int $value, int $i, $prefix = '', bool $enabled = true, string $checkbox = 'cb'): string
|
||||
{
|
||||
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(int $i, string $editorName, string $time, $prefix = '', bool $enabled = false,
|
||||
string $checkbox = 'cb'): string
|
||||
{
|
||||
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(string $orderingField, ?string $order, string $class = 'input-sm',
|
||||
string $icon = 'akion-android-more-vertical',
|
||||
string $inactiveIcon = 'akion-android-more-vertical',
|
||||
DataViewInterface $view = null): string
|
||||
{
|
||||
$order = $order ?? 'asc';
|
||||
|
||||
/** @var \FOF40\View\DataView\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)
|
||||
{
|
||||
$html .= '<input type="text" name="order[]" style="display: none" 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(string $orderingField = 'ordering', string $icon = 'akion-stats-bars'): string
|
||||
{
|
||||
$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(int $i, string $task = 'orderup', $prefix = '', string $text = 'JLIB_HTML_MOVE_UP',
|
||||
bool $enabled = true, string $checkbox = 'cb'): string
|
||||
{
|
||||
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(int $i, string $task = 'orderdown', string $prefix = '',
|
||||
string $text = 'JLIB_HTML_MOVE_DOWN', bool $enabled = true,
|
||||
string $checkbox = 'cb'): string
|
||||
{
|
||||
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(string $title, string $order, ?string $direction = 'asc', string $selected = '',
|
||||
?string $task = null, string $new_direction = 'asc', string $tip = '',
|
||||
?string $form = null): string
|
||||
{
|
||||
HTMLHelper::_('behavior.core');
|
||||
HTMLHelper::_('bootstrap.popover');
|
||||
|
||||
$direction = strtolower($direction ?? 'asc');
|
||||
$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>';
|
||||
}
|
||||
|
||||
return $html . '</a>';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(string $name = 'checkall-toggle', string $tip = 'JGLOBAL_CHECK_ALL',
|
||||
string $action = 'Joomla.checkAll(this)'): string
|
||||
{
|
||||
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 mixed $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, empty if checked out.
|
||||
*
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function id(int $rowNum, $recId, bool $checkedOut = false, string $name = 'cid',
|
||||
string $stub = 'cb'): string
|
||||
{
|
||||
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(string $orderBy, bool $return = false): ?string
|
||||
{
|
||||
$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
|
||||
{
|
||||
JoomlaFactory::getApplication()->getDocument()->addScriptDeclaration($js);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
// If we have no application, well, not having table sorting JS is the least of your worries...
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
?string $sortBy = null, ?string $order_Dir = null): string
|
||||
{
|
||||
if (is_null($view))
|
||||
{
|
||||
$view = BrowseView::getViewFromBacktrace();
|
||||
}
|
||||
|
||||
$showBrowsePagination = $view->showBrowsePagination ?? true;
|
||||
$showBrowseOrdering = $view->showBrowseOrdering ?? true;
|
||||
$showBrowseOrderBy = $view->showBrowseOrderBy ?? true;
|
||||
|
||||
if (!$showBrowsePagination && !$showBrowseOrdering && !$showBrowseOrderBy)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
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');
|
||||
$orderingDecr = Text::_('JFIELD_ORDERING_DESC');
|
||||
$sortByLabel = Text::_('JGLOBAL_SORT_BY');
|
||||
|
||||
// Order direction dropdown
|
||||
$directionSelect = HTMLHelper::_('FEFHelp.select.genericlist', [
|
||||
'' => $orderingDecr,
|
||||
'asc' => Text::_('JGLOBAL_ORDER_ASCENDING'),
|
||||
'desc' => Text::_('JGLOBAL_ORDER_DESCENDING'),
|
||||
], 'directionTable', [
|
||||
'id' => 'directionTable',
|
||||
'list.select' => $order_Dir,
|
||||
'list.attr' => [
|
||||
'class' => 'input-medium custom-select akeebaCommonEventsOnChangeOrderTable',
|
||||
],
|
||||
]);
|
||||
|
||||
// Sort by field dropdown
|
||||
|
||||
$sortTable = HTMLHelper::_('FEFHelp.select.genericlist', array_merge([
|
||||
'' => Text::_('JGLOBAL_SORT_BY'),
|
||||
], $sortFields), 'sortTable', [
|
||||
'id' => 'sortTable',
|
||||
'list.select' => $sortBy,
|
||||
'list.attr' => [
|
||||
'class' => 'input-medium custom-select akeebaCommonEventsOnChangeOrderTable',
|
||||
],
|
||||
]);
|
||||
|
||||
$html = '';
|
||||
|
||||
if ($showBrowsePagination)
|
||||
{
|
||||
$html .= <<< HTML
|
||||
<div class="akeeba-filter-element akeeba-form-group">
|
||||
<label for="limit" class="element-invisible">
|
||||
$limitLabel
|
||||
</label>
|
||||
{$pagination->getLimitBox()}
|
||||
</div>
|
||||
|
||||
HTML;
|
||||
}
|
||||
|
||||
if ($showBrowseOrdering)
|
||||
{
|
||||
$html .= <<< HTML
|
||||
<div class="akeeba-filter-element akeeba-form-group">
|
||||
<label for="directionTable" class="element-invisible">
|
||||
$orderingDecr
|
||||
</label>
|
||||
$directionSelect
|
||||
</div>
|
||||
|
||||
HTML;
|
||||
}
|
||||
|
||||
if ($showBrowseOrderBy)
|
||||
{
|
||||
$html .= <<< HTML
|
||||
<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): array
|
||||
{
|
||||
$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 (array_keys($model->getFields()) as $field)
|
||||
{
|
||||
$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/fof40/Html/FEFHelper/edit.php
Normal file
60
libraries/fof40/Html/FEFHelper/edit.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FOF
|
||||
* @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
|
||||
* @license GNU General Public License version 3, or later
|
||||
*/
|
||||
|
||||
defined('_JEXEC') || die;
|
||||
|
||||
use Joomla\CMS\Editor\Editor;
|
||||
use Joomla\CMS\Factory as JoomlaFactory;
|
||||
|
||||
/**
|
||||
* Custom JHtml (HTMLHelper) class. Offers edit (form) view controls compatible with Akeeba Frontend
|
||||
* Framework (FEF).
|
||||
*
|
||||
* Call these methods as HTMLHelper::_('FEFHelp.edit.methodName', $parameter1, $parameter2, ...)
|
||||
*/
|
||||
abstract class FEFHelpEdit
|
||||
{
|
||||
public static function editor(string $fieldName, ?string $value, array $params = []): string
|
||||
{
|
||||
$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 = JoomlaFactory::getConfig()->get('editor');
|
||||
$user = JoomlaFactory::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);
|
||||
}
|
||||
}
|
||||
882
libraries/fof40/Html/FEFHelper/select.php
Normal file
882
libraries/fof40/Html/FEFHelper/select.php
Normal file
@ -0,0 +1,882 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FOF
|
||||
* @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
|
||||
* @license GNU General Public License version 3, or later
|
||||
*/
|
||||
|
||||
defined('_JEXEC') || die;
|
||||
|
||||
use FOF40\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 HTMLHelper::_('FEFHelp.select.methodName', $parameter1, $parameter2, ...)
|
||||
*
|
||||
* @noinspection PhpIllegalPsrClassPathInspection
|
||||
*/
|
||||
abstract class FEFHelpSelect
|
||||
{
|
||||
/**
|
||||
* 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 mixed $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
|
||||
*
|
||||
* @see JFormFieldRadio
|
||||
*/
|
||||
public static function booleanlist($name, $attribs = [], $selected = null, $yes = 'JYES', $no = 'JNO', $id = false)
|
||||
{
|
||||
$options = [
|
||||
\Joomla\CMS\HTML\HTMLHelper::_('FEFHelp.select.option', '0', \Joomla\CMS\Language\Text::_($no)),
|
||||
\Joomla\CMS\HTML\HTMLHelper::_('FEFHelp.select.option', '1', \Joomla\CMS\Language\Text::_($yes)),
|
||||
];
|
||||
$attribs = array_merge(['forSelect' => 1], $attribs);
|
||||
|
||||
return \Joomla\CMS\HTML\HTMLHelper::_('FEFHelp.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 HTMLHelper::$formatOptions}.
|
||||
* Selection options, see {@see HTMLHelper::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.
|
||||
*
|
||||
*/
|
||||
public static function genericlist(array $data, string $name, ?array $attribs = null, string $optKey = 'value',
|
||||
string $optText = 'text', $selected = null, $idtag = false,
|
||||
bool $translate = false): string
|
||||
{
|
||||
// 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']++);
|
||||
|
||||
return $baseIndent . '<select' . ($id !== '' ? ' id="' . $id . '"' : '') . ' name="' . $name . '"' . $attribs . '>' . $options['format.eol']
|
||||
. static::options($data, $options) . $baseIndent . '</select>' . $options['format.eol'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 HTMLHelper::$formatOptions}.
|
||||
* Selection options. See {@see HTMLHelper::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
|
||||
* Text::_().
|
||||
*
|
||||
* @return string HTML for the select list
|
||||
*
|
||||
* @throws RuntimeException If a group has contents that cannot be processed.
|
||||
*/
|
||||
public static function groupedlist(array $data, string $name, array $options = []): string
|
||||
{
|
||||
// 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'];
|
||||
}
|
||||
}
|
||||
|
||||
return $html . ($baseIndent . '</select>' . $options['format.eol']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(int $start, int $end, int $inc, string $name, ?array $attribs = null,
|
||||
$selected = null, string $format = ''): string
|
||||
{
|
||||
// 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::_('FEFHelp.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(?string $value, string $text = '', $optKey = 'value', string $optText = 'text',
|
||||
bool $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 HTMLHelper::$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
|
||||
* Text::_(). 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(array $arr, $optKey = 'value', string $optText = 'text',
|
||||
?string $selected = null, bool $translate = false): string
|
||||
{
|
||||
$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 mixed $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 ? \Joomla\CMS\Language\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>';
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return $html . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates two radio buttons styled with FEF to appear as a YES/NO switch
|
||||
*
|
||||
* @param string $name Name of the field
|
||||
* @param mixed $selected Selected field
|
||||
* @param array $attribs Additional attributes to add to the switch
|
||||
*
|
||||
* @return string The HTML for the switch
|
||||
*/
|
||||
public static function booleanswitch(string $name, $selected, array $attribs = []): string
|
||||
{
|
||||
if (empty($attribs))
|
||||
{
|
||||
$attribs = ['class' => 'akeeba-toggle'];
|
||||
}
|
||||
elseif (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;
|
||||
}
|
||||
}
|
||||
56
libraries/fof40/Html/Fields/fancyradio.php
Normal file
56
libraries/fof40/Html/Fields/fancyradio.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FOF
|
||||
* @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
|
||||
* @license GNU General Public License version 3, or later
|
||||
*/
|
||||
|
||||
defined('_JEXEC') || die();
|
||||
|
||||
use Joomla\CMS\Form\FormHelper;
|
||||
|
||||
// Prevent PHP fatal errors if this somehow gets accidentally loaded multiple times
|
||||
if (class_exists('JFormFieldFancyradio'))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Load the base form field class
|
||||
FormHelper::loadFieldClass('radio');
|
||||
|
||||
/**
|
||||
* Yes/No switcher, compatible with Joomla 3 and 4
|
||||
*
|
||||
* ## How to use
|
||||
*
|
||||
* 1. Create a folder in your project for custom Joomla form fields, e.g. components/com_example/fields
|
||||
* 2. Create a new file called `fancyradio.php` with the content
|
||||
* ```php
|
||||
* defined('_JEXEC') || die();
|
||||
* require_once JPATH_LIBRARIES . '/fof40/Html/Fields/fancyradio.php';
|
||||
* ```
|
||||
*
|
||||
* @package Joomla\CMS\Form\Field
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @noinspection PhpUnused
|
||||
* @noinspection PhpIllegalPsrClassPathInspection
|
||||
*/
|
||||
class JFormFieldFancyradio extends JFormFieldRadio
|
||||
{
|
||||
public function __construct($form = null)
|
||||
{
|
||||
if (version_compare(JVERSION, '3.999.999', 'gt'))
|
||||
{
|
||||
// Joomla 4.0 and later.
|
||||
$this->layout = 'joomla.form.field.radio.switcher';
|
||||
}
|
||||
else
|
||||
{
|
||||
// Joomla 3.x. Yes, 3.10 does have the layout but I am playing it safe.
|
||||
$this->layout = 'joomla.form.field.radio';
|
||||
}
|
||||
|
||||
parent::__construct($form);
|
||||
}
|
||||
}
|
||||
466
libraries/fof40/Html/SelectOptions.php
Normal file
466
libraries/fof40/Html/SelectOptions.php
Normal file
@ -0,0 +1,466 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FOF
|
||||
* @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
|
||||
* @license GNU General Public License version 3, or later
|
||||
*/
|
||||
|
||||
namespace FOF40\Html;
|
||||
|
||||
defined('_JEXEC') || die;
|
||||
|
||||
use Joomla\CMS\Cache\Cache;
|
||||
use Joomla\CMS\Factory as JoomlaFactory;
|
||||
use Joomla\CMS\Helper\UserGroupsHelper;
|
||||
use Joomla\CMS\HTML\HTMLHelper;
|
||||
use Joomla\CMS\Language\LanguageHelper;
|
||||
use Joomla\CMS\Language\Text;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* Returns arrays of HTMLHelper select options for Joomla-specific information such as access levels.
|
||||
*
|
||||
* @method static array access() access(array $params)
|
||||
* @method static array usergroups() usergroups(array $params)
|
||||
* @method static array cachehandlers() cachehandlers(array $params)
|
||||
* @method static array components() components(array $params)
|
||||
* @method static array languages() languages(array $params)
|
||||
* @method static array published() published(array $params)
|
||||
*/
|
||||
class SelectOptions
|
||||
{
|
||||
private static $cache = [];
|
||||
|
||||
/**
|
||||
* Magic method to handle static calls
|
||||
*
|
||||
* @param string $name The name of the static method being called
|
||||
* @param array $arguments Optional arguments, if they are supported by the options type.
|
||||
*
|
||||
* @return mixed
|
||||
* @since 3.3.0
|
||||
*/
|
||||
public static function __callStatic(string $name, array $arguments = [])
|
||||
{
|
||||
return self::getOptions($name, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of Joomla options of the type you specify. Supported types
|
||||
* - access View access levels
|
||||
* - usergroups User groups
|
||||
* - cachehandlers Cache handlers
|
||||
* - components Installed components accessible by the current user
|
||||
* - languages Site or administrator languages
|
||||
* - published Published status
|
||||
*
|
||||
* Global params:
|
||||
* - cache Should I returned cached data? Default: true.
|
||||
*
|
||||
* See the private static methods of this class for more information on params.
|
||||
*
|
||||
* @param string $type The options type to get
|
||||
* @param array $params Optional arguments, if they are supported by the options type.
|
||||
*
|
||||
* @return stdClass[]
|
||||
* @since 3.3.0
|
||||
* @api
|
||||
*/
|
||||
public static function getOptions(string $type, array $params = []): array
|
||||
{
|
||||
if ((substr($type, 0, 1) == '_') || !method_exists(__CLASS__, '_api_' . $type))
|
||||
{
|
||||
throw new \InvalidArgumentException(__CLASS__ . "does not support option type '$type'.");
|
||||
}
|
||||
|
||||
$useCache = true;
|
||||
|
||||
if (isset($params['cache']))
|
||||
{
|
||||
$useCache = isset($params['cache']);
|
||||
unset($params['cache']);
|
||||
}
|
||||
|
||||
$cacheKey = sha1($type . '--' . print_r($params, true));
|
||||
$fetchNew = !$useCache || ($useCache && !isset(self::$cache[$cacheKey]));
|
||||
$ret = [];
|
||||
|
||||
if ($fetchNew)
|
||||
{
|
||||
$ret = forward_static_call_array([__CLASS__, '_api_' . $type], [$params]);
|
||||
}
|
||||
|
||||
if (!$useCache)
|
||||
{
|
||||
return $ret;
|
||||
}
|
||||
|
||||
if ($fetchNew)
|
||||
{
|
||||
self::$cache[$cacheKey] = $ret;
|
||||
}
|
||||
|
||||
return self::$cache[$cacheKey];
|
||||
}
|
||||
|
||||
/**
|
||||
* Joomla! Access Levels (previously: view access levels)
|
||||
*
|
||||
* Available params:
|
||||
* - allLevels: Show an option for all levels (default: false)
|
||||
*
|
||||
* @param array $params Parameters
|
||||
*
|
||||
* @return stdClass[]
|
||||
* @since 3.3.0
|
||||
* @internal
|
||||
*
|
||||
* @see \Joomla\CMS\HTML\Helpers\Access::level()
|
||||
*
|
||||
* @noinspection PhpUnusedPrivateMethodInspection
|
||||
*/
|
||||
private static function _api_access(array $params = []): array
|
||||
{
|
||||
$db = JoomlaFactory::getDbo();
|
||||
$query = $db->getQuery(true)
|
||||
->select($db->quoteName('a.id', 'value') . ', ' . $db->quoteName('a.title', 'text'))
|
||||
->from($db->quoteName('#__viewlevels', 'a'))
|
||||
->group($db->quoteName(['a.id', 'a.title', 'a.ordering']))
|
||||
->order($db->quoteName('a.ordering') . ' ASC')
|
||||
->order($db->quoteName('title') . ' ASC');
|
||||
|
||||
// Get the options.
|
||||
$db->setQuery($query);
|
||||
$options = $db->loadObjectList() ?? [];
|
||||
|
||||
if (isset($params['allLevels']) && $params['allLevels'])
|
||||
{
|
||||
array_unshift($options, HTMLHelper::_('select.option', '', Text::_('JOPTION_ACCESS_SHOW_ALL_LEVELS')));
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Joomla! User Groups
|
||||
*
|
||||
* Available params:
|
||||
* - allGroups: Show an option for all groups (default: false)
|
||||
*
|
||||
* @param array $params Parameters
|
||||
*
|
||||
* @return stdClass[]
|
||||
* @since 3.3.0
|
||||
* @internal
|
||||
*
|
||||
* @see \Joomla\CMS\HTML\Helpers\Access::usergroup()
|
||||
*
|
||||
* @noinspection PhpUnusedPrivateMethodInspection
|
||||
*/
|
||||
private static function _api_usergroups(array $params = []): array
|
||||
{
|
||||
$options = array_values(UserGroupsHelper::getInstance()->getAll());
|
||||
|
||||
for ($i = 0, $n = count($options); $i < $n; $i++)
|
||||
{
|
||||
$options[$i]->value = $options[$i]->id;
|
||||
$options[$i]->text = str_repeat('- ', $options[$i]->level) . $options[$i]->title;
|
||||
}
|
||||
|
||||
// If all usergroups is allowed, push it into the array.
|
||||
if (isset($params['allGroups']) && $params['allGroups'])
|
||||
{
|
||||
array_unshift($options, HTMLHelper::_('select.option', '', Text::_('JOPTION_ACCESS_SHOW_ALL_GROUPS')));
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Joomla cache handlers
|
||||
*
|
||||
* @param array $params Ignored
|
||||
*
|
||||
* @return stdClass[]
|
||||
* @since 3.3.0
|
||||
* @internal
|
||||
*
|
||||
* @noinspection PhpUnusedPrivateMethodInspection
|
||||
*/
|
||||
private static function _api_cachehandlers(array $params = []): array
|
||||
{
|
||||
$options = [];
|
||||
|
||||
// Convert to name => name array.
|
||||
foreach (Cache::getStores() as $store)
|
||||
{
|
||||
$options[] = HTMLHelper::_('select.option', $store, Text::_('JLIB_FORM_VALUE_CACHE_' . $store), 'value', 'text');
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all installed components and also translates them.
|
||||
*
|
||||
* Available params:
|
||||
* - client_ids Array of Joomla application client IDs
|
||||
*
|
||||
* @param array $params
|
||||
*
|
||||
* @return stdClass[]
|
||||
* @since 3.3.0
|
||||
* @internal
|
||||
*
|
||||
* @noinspection PhpUnusedPrivateMethodInspection
|
||||
*/
|
||||
private static function _api_components(array $params = []): array
|
||||
{
|
||||
$db = JoomlaFactory::getDbo();
|
||||
|
||||
// Check for client_ids override
|
||||
$client_ids = $params['client_ids'] ?? [0, 1];
|
||||
|
||||
if (is_string($client_ids))
|
||||
{
|
||||
$client_ids = explode(',', $client_ids);
|
||||
}
|
||||
|
||||
// Calculate client_ids where clause
|
||||
$client_ids = array_map(function ($client_id) use ($db) {
|
||||
return $db->q((int) trim($client_id));
|
||||
}, $client_ids);
|
||||
|
||||
$query = $db->getQuery(true)
|
||||
->select(
|
||||
[
|
||||
$db->qn('name'),
|
||||
$db->qn('element'),
|
||||
$db->qn('client_id'),
|
||||
$db->qn('manifest_cache'),
|
||||
]
|
||||
)
|
||||
->from($db->qn('#__extensions'))
|
||||
->where($db->qn('type') . ' = ' . $db->q('component'))
|
||||
->where($db->qn('client_id') . ' IN (' . implode(',', $client_ids) . ')');
|
||||
$components = $db->setQuery($query)->loadObjectList('element');
|
||||
|
||||
// Convert to array of objects, so we can use sortObjects()
|
||||
// Also translate component names with Text::_()
|
||||
$aComponents = [];
|
||||
$user = JoomlaFactory::getUser();
|
||||
|
||||
foreach ($components as $component)
|
||||
{
|
||||
// Don't show components in the list where the user doesn't have access for
|
||||
// TODO: perhaps add an option for this
|
||||
if (!$user->authorise('core.manage', $component->element))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
$aComponents[$component->element] = (object) [
|
||||
'value' => $component->element,
|
||||
'text' => self::_translateComponentName($component),
|
||||
];
|
||||
}
|
||||
|
||||
// Reorder the components array, because the alphabetical
|
||||
// ordering changed due to the Text::_() translation
|
||||
uasort(
|
||||
$aComponents,
|
||||
function ($a, $b) {
|
||||
return strcasecmp($a->text, $b->text);
|
||||
}
|
||||
);
|
||||
|
||||
return $aComponents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to get the field options.
|
||||
*
|
||||
* Available params:
|
||||
* - client 'site' (default) or 'administrator'
|
||||
* - none Text to show for "all languages" option, use empty string to remove it
|
||||
*
|
||||
* @param array $params
|
||||
*
|
||||
* @return object[] Languages for the specified client
|
||||
* @since 3.3.0
|
||||
* @internal
|
||||
*
|
||||
* @noinspection PhpUnusedPrivateMethodInspection
|
||||
*/
|
||||
private static function _api_languages(array $params): array
|
||||
{
|
||||
$client = $params['client'] ?? 'site';
|
||||
|
||||
if (!in_array($client, ['site', 'administrator']))
|
||||
{
|
||||
$client = 'site';
|
||||
}
|
||||
|
||||
// Make sure the languages are sorted base on locale instead of random sorting
|
||||
$options = LanguageHelper::createLanguageList(null, constant('JPATH_' . strtoupper($client)), true, true);
|
||||
|
||||
if (count($options) > 1)
|
||||
{
|
||||
usort(
|
||||
$options,
|
||||
function ($a, $b) {
|
||||
return strcmp($a['value'], $b['value']);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$none = $params['none'] ?? '*';
|
||||
|
||||
if (!empty($none))
|
||||
{
|
||||
array_unshift($options, HTMLHelper::_('select.option', '*', Text::_($none)));
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for a Published field
|
||||
*
|
||||
* Params:
|
||||
* - none Placeholder for no selection (empty key). Default: null.
|
||||
* - published Show "Published"? Default: true
|
||||
* - unpublished Show "Unpublished"? Default: true
|
||||
* - archived Show "Archived"? Default: false
|
||||
* - trash Show "Trashed"? Default: false
|
||||
* - all Show "All" option? This is different than none, the key is '*'. Default: false
|
||||
*
|
||||
* @param array $params
|
||||
*
|
||||
* @return array
|
||||
* @since 3.3.0
|
||||
* @internal
|
||||
*
|
||||
* @noinspection PhpUnusedPrivateMethodInspection
|
||||
*/
|
||||
private static function _api_published(array $params = []): array
|
||||
{
|
||||
$config = array_merge([
|
||||
'none' => '',
|
||||
'published' => true,
|
||||
'unpublished' => true,
|
||||
'archived' => false,
|
||||
'trash' => false,
|
||||
'all' => false,
|
||||
], $params);
|
||||
|
||||
$options = [];
|
||||
|
||||
if (!empty($config['none']))
|
||||
{
|
||||
$options[] = HTMLHelper::_('select.option', '', Text::_($config['none']));
|
||||
}
|
||||
|
||||
if ($config['published'])
|
||||
{
|
||||
$options[] = HTMLHelper::_('select.option', '1', Text::_('JPUBLISHED'));
|
||||
}
|
||||
|
||||
if ($config['unpublished'])
|
||||
{
|
||||
$options[] = HTMLHelper::_('select.option', '0', Text::_('JUNPUBLISHED'));
|
||||
}
|
||||
|
||||
if ($config['archived'])
|
||||
{
|
||||
$options[] = HTMLHelper::_('select.option', '2', Text::_('JARCHIVED'));
|
||||
}
|
||||
|
||||
if ($config['trash'])
|
||||
{
|
||||
$options[] = HTMLHelper::_('select.option', '-2', Text::_('JTRASHED'));
|
||||
}
|
||||
|
||||
if ($config['all'])
|
||||
{
|
||||
$options[] = HTMLHelper::_('select.option', '*', Text::_('JALL'));
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for a Published field
|
||||
*
|
||||
* Params:
|
||||
* - none Placeholder for no selection (empty key). Default: null.
|
||||
*
|
||||
* @param array $params
|
||||
*
|
||||
* @return array
|
||||
* @since 3.3.0
|
||||
* @internal
|
||||
*
|
||||
* @noinspection PhpUnusedPrivateMethodInspection
|
||||
*/
|
||||
private static function _api_boolean(array $params = []): array
|
||||
{
|
||||
$config = array_merge([
|
||||
'none' => '',
|
||||
], $params);
|
||||
|
||||
$options = [];
|
||||
|
||||
if (!empty($config['none']))
|
||||
{
|
||||
$options[] = HTMLHelper::_('select.option', '', Text::_($config['none']));
|
||||
}
|
||||
|
||||
$options[] = HTMLHelper::_('select.option', '1', Text::_('JYES'));
|
||||
$options[] = HTMLHelper::_('select.option', '0', Text::_('JNO'));
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Translate a component name
|
||||
*
|
||||
* @param object $item The component object
|
||||
*
|
||||
* @return string $text The translated name of the extension
|
||||
* @since 3.3.0
|
||||
* @internal
|
||||
*
|
||||
* @see /administrator/com_installer/models/extension.php
|
||||
*/
|
||||
private static function _translateComponentName(object $item): string
|
||||
{
|
||||
// Map the manifest cache to $item. This is needed to get the name from the
|
||||
// manifest_cache and NOT from the name column, else some Text::_() translations fails.
|
||||
$mData = json_decode($item->manifest_cache);
|
||||
|
||||
if ($mData)
|
||||
{
|
||||
foreach ($mData as $key => $value)
|
||||
{
|
||||
if ($key == 'type')
|
||||
{
|
||||
// Ignore the type field
|
||||
continue;
|
||||
}
|
||||
|
||||
$item->$key = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$lang = JoomlaFactory::getLanguage();
|
||||
$source = JPATH_ADMINISTRATOR . '/components/' . $item->element;
|
||||
$lang->load("$item->element.sys", JPATH_ADMINISTRATOR, null, false, false)
|
||||
|| $lang->load("$item->element.sys", $source, null, false, false)
|
||||
|| $lang->load("$item->element.sys", JPATH_ADMINISTRATOR, $lang->getDefault(), false, false)
|
||||
|| $lang->load("$item->element.sys", $source, $lang->getDefault(), false, false);
|
||||
|
||||
return Text::_($item->name);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user