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