monster_menus-9.0.x-dev/src/Element/MMCatlist.php
src/Element/MMCatlist.php
<?php
/**
* @file
* Contains \Drupal\monster_menus\Element\MMCatlist.
*/
namespace Drupal\monster_menus\Element;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Attribute\FormElement;
use Drupal\Core\Render\Element\FormElementBase;
use Drupal\Core\Url;
use Drupal\monster_menus\Constants;
use Drupal\monster_menus\Plugin\MMTreeBrowserDisplay\Fallback;
use Drupal\monster_menus\Plugin\MMTreeBrowserDisplay\Groups;
use Drupal\monster_menus\Plugin\MMTreeBrowserDisplay\Nodes;
use Drupal\monster_menus\Plugin\MMTreeBrowserDisplay\Users;
/**
* Provides a graphical chooser for MM Tree entries.
*/
#[FormElement('mm_catlist')]
class MMCatlist extends FormElementBase {
public function getInfo() {
$class = static::class;
return [
'#input' => TRUE,
'#default_value' => [],
'#process' => [[$class, 'processGroup'],
[$class, 'process'],
[$class, 'processAjaxForm']],
'#pre_render' => [[$class, 'preRenderGroup'],
[$class, 'preRender']],
'#attached' => ['library' => ['monster_menus/mm']],
'#mm_list_min' => 0,
// min number of rows
'#mm_list_max' => 0,
// max number of rows
'#mm_list_popup_start' => '',
'#mm_list_popup_root' => 1,
// if 0, show the "[top]" entry
'#mm_list_enabled' => '',
// in category browser, attribute of cats user can open
'#mm_list_selectable' => Constants::MM_PERMS_APPLY,
// in category browser, attribute of cats user can choose
'#mm_list_buttons_underneath' => FALSE,
'#mm_list_readonly' => FALSE,
// let the user select rows, but not edit them
'#mm_list_no_info' => FALSE,
// don't show an item's info when clicked
'#mm_list_route' => 'monster_menus.browser_load',
// route to the popup tree browser
'#mm_list_submit_on_add' => FALSE,
// auto-submit the outer form upon first choice
'#mm_list_auto_choose' => FALSE,
// automatically click the "Choose" button upon load
'#mm_list_initial_focus' => '',
// form element to get focus by default
'#mm_list_hide_left_pane' => FALSE,
// should only be set when min==max==1
'#mm_list_mode' => Fallback::BROWSER_MODE_PAGE,
'#mm_list_other_name' => '',
'#mm_list_other_callback' => 'null',
'#mm_list_field_name' => '',
// set field_name and bundle_name when widget for a field
'#mm_list_bundle_name' => '',
'#mm_list_bundle_type' => 'node',
'#mm_list_info_func' => [$class, 'makeEntry'],
'#mm_list_min_wh' => '',
// a string in the format [width]x[height]
'#mm_list_file_types' => '',
// comma-separated list of types (image, video, etc.)
'#theme' => 'mm_catlist',
'#theme_wrappers' => ['form_element'],
];
}
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
}
public static function preRender($element) {
if (empty($element['#title'])) {
$element['#title'] = $element['#mm_list_max'] == 1 ? t('Page') : t('Pages');
}
static::preRenderMMList($element['#mm_list_mode'], $element,
$element['#mm_list_popup_root'] ?? '',
t('Path:'));
return $element;
}
/**
* Generate the form code to allow the user to add pages/groups to a node
*
* @param $mode
* The MM_BROWSER_X constant mode
* @param array &$elt
* An associative array containing the properties of the element. Properties
* used: #default_value, #description, #mm_list_autocomplete_name,
* #mm_list_route, #mm_list_buttons_underneath, #mm_list_enabled,
* #mm_list_max, #mm_list_min, #mm_list_no_info, #mm_list_other_callback,
* #mm_list_other_name, #mm_list_popup_root, #mm_list_readonly,
* #mm_list_selectable, #mm_list_info_func, #name, #parents, #required,
* #title, #type, #value.
* @param $start_mmtid
* The location in the tree at which to default the tree view
* @param $info_label
* The label appearing above the item information line
*/
static function preRenderMMList($mode, array &$elt, $start_mmtid, $info_label) {
if (isset($elt['#mm_list_instance'])) {
return;
}
$_mmlist_instance = &drupal_static('_mmlist_instance', $elt['#mm_list_instance_start'] ?? 0);
// Set a default AJAX event type if none is present.
if (isset($elt['#ajax']) && empty($elt['#ajax']['event'])) {
$elt['#ajax']['event'] = 'change';
}
if (!\Drupal::currentUser()->hasPermission('use tree browser')) {
$elt['#mm_list_readonly'] = TRUE;
}
$max = intval($elt['#mm_list_max']);
$min = intval($elt['#mm_list_min']);
$exactly1 = $max == 1 && $min == 1;
$flags = [];
$js_flags = [
'#mm_list_buttons_underneath' => 'action_under',
'#mm_list_submit_on_add' => 'submit_on_add',
'#mm_list_initial_focus' => 'initial_focus',
'#mm_list_hide_left_pane' => 'hide_left_pane',
];
foreach ($js_flags as $api_flag => $js_flag) {
if (!empty($elt[$api_flag])) {
$flags[$js_flag] = $elt[$api_flag];
}
}
$select_callback = 'catSelectCallback';
if (!empty($elt['#mm_list_readonly'])) {
$label_above_actions = $label_add_cat = $label_replace = $label_delete = '';
if ($elt['#mm_list_no_info']) {
$select_callback = 'null';
}
}
else {
$label_above_actions = $min == 1 && $max == 1 ? '' : t('Action:');
$label_add_cat = $max == 1 || $mode == Users::BROWSER_MODE_USER ? '' : t('Add...');
$label_replace = $mode == Users::BROWSER_MODE_USER ? '' : ($max == 1 ? t('Choose...') : t('Replace...'));
$label_delete = $exactly1 ? '' : ($min == 0 && $max == 1 ? t('Clear') : t('Delete'));
}
$label_above_info = !empty($elt['#mm_list_no_info']) ? '' : $info_label;
if ($mode == Users::BROWSER_MODE_USER) {
$popup_base = '';
}
else {
$start = "$start_mmtid-$mode-$_mmlist_instance-";
if ($elt['#type'] == 'mm_nodelist') {
$field_id = !empty($elt['#mm_list_field_name']) && !empty($elt['#mm_list_bundle_name']) ? $elt['#mm_list_field_name'] . ',' . $elt['#mm_list_bundle_name'] . ',' . $elt['#mm_list_bundle_type'] : '';
$start .= $elt['#mm_list_enabled'] . '-' . $elt['#mm_list_selectable'] . '--' . $field_id;
}
else {
$start .= ($elt['#mm_list_enabled'] ?? '') . '-' . $elt['#mm_list_selectable'] . '--';
}
$start .= '-' . $elt['#mm_list_file_types'] . '-' . $elt['#mm_list_min_wh'] . '/';
$popup_base = Url::fromRoute($elt['#mm_list_route'], [], ['query' => ['_path' => $start]])->toString();
mm_ui_modal_dialog('init', $elt);
}
$popup_URL = $elt['#mm_list_popup_start'];
if ($max == 1 && !$elt['#value']) {
if ($elt['#default_value']) {
$elt['#value'] = $elt['#default_value'];
}
else {
$msgs = [
Users::BROWSER_MODE_USER => t('(choose a user)'),
Groups::BROWSER_MODE_GROUP => t('(choose a group)'),
Nodes::BROWSER_MODE_NODE => t('(choose content)'),
Fallback::BROWSER_MODE_PAGE => t('(choose a location)'),
];
$elt['#value'] = ['' => $msgs[$mode] ?? $msgs[Fallback::BROWSER_MODE_PAGE]];
}
}
$adds = [];
if (!mm_ui_is_search()) {
$url = $info = '';
if (!empty($elt['#value']) && is_scalar($elt['#value'])) {
// The value is sometimes corrupted, so replace it with the default.
$elt['#value'] = $elt['#default_value'];
}
if (is_array($elt['#value'])) {
foreach ($elt['#value'] as $mmtid => $name) {
if (!$mmtid || empty($elt['#mm_list_info_func'])) {
$url = $exactly1 ? $popup_URL : '';
$info = '';
}
else {
$elt['#mm_list_info_func']($mmtid, $name, $url, $info, $popup_URL, $elt);
}
$adds[] = [$name, $mmtid, $url, $info];
}
}
else if (!empty($elt['#value'])) {
foreach (static::splitMMlist($elt['#value']) as $m) {
$name = $m[2];
if (!empty($elt['#mm_list_info_func'])) {
$elt['#mm_list_info_func']($m[1], $name, $url, $info, $popup_URL, $elt);
}
[$mmtid, $nid] = explode('/', $m[1]);
if ($mode != Users::BROWSER_MODE_USER && !$nid && !mm_content_user_can($mmtid, $elt['#mm_list_selectable'])) {
if ($exactly1) {
$name = t('(choose)');
$info = '';
}
else {
if ($url == $popup_URL) {
unset($popup_URL);
}
continue;
}
}
$adds[] = [$name, $mmtid, $url, $info];
}
}
}
else if ($exactly1) {
$adds[] = ['', '', '', ''];
}
if (empty($popup_URL)) {
$popup_URL = $start_mmtid;
}
$popup_label = t('Tree Browser');
if (isset($elt['#name'])) {
$name = $elt['#name'];
}
else {
$name = $elt['#parents'][0];
if (count($elt['#parents']) > 1) {
$name .= '[' . join('][', array_slice($elt['#parents'], 1)) . ']';
}
}
$other_name = $elt['#mm_list_other_name'] ?? '';
$other_callback = $elt['#mm_list_other_callback'] ?? NULL;
$class = static::addClass($elt);
$auto = '';
if (!empty($elt['#mm_list_autocomplete_name'])) {
if ($elt['#mm_list_autocomplete_name'] === TRUE) {
$auto = preg_replace('{\]$}', '-choose]', $name);
}
else {
$auto = $elt['#mm_list_autocomplete_name'];
}
}
$tag = $exactly1 || $elt['#mm_list_readonly'] && $elt['#mm_list_no_info'] ? 'span' : 'a';
$settings = [
'where' => NULL,
'listObjDivSelector' => "div[name=mm_list_obj$_mmlist_instance]",
'outerDivSelector' => "div[name=mm_list_obj$_mmlist_instance] + div[class=\"$class\"]",
'hiddenId' => $elt['#id'],
'hiddenName' => $name,
'add' => $adds,
'autoName' => $auto,
'parms' => [
'isSearch' => mm_ui_is_search(),
'minRows' => $min,
'maxRows' => $max,
'popupBase' => $popup_base,
'popupURL' => $popup_URL,
'popupLabel' => $popup_label,
'flags' => $flags,
'addCallback' => 'catAddCallback',
'replaceCallback' => 'catReplCallback',
'selectCallback' => $select_callback,
'dataCallback' => 'catDataCallback',
'labelAboveActions' => $label_above_actions,
'labelAddCat' => $label_add_cat,
'labelReplace' => $label_replace,
'labelDelete' => $label_delete,
'labelAboveInfo' => $label_above_info,
'updateOnChangeName' => $other_name,
'updateOnChangeCallback' => $other_callback,
'autoChoose' => !empty($elt['#mm_list_auto_choose']),
]
];
$elt['#attached']['drupalSettings']['MM']['mmListInit'][$_mmlist_instance] = $settings;
$elt += [
'#mm_list_instance' => $_mmlist_instance++,
'#mm_list_tag' => $tag,
'#mm_list_class' => $class,
];
}
// Helper function to pre-generate an entry in the list.
public static function makeEntry($mmtid, $name, &$url, &$info, &$popup_URL, &$elt) {
$parts = explode('/', $mmtid);
$mmtid = $parts[0] ?? NULL;
$node = $parts[1] ?? NULL;
$parents = mm_content_get_parents($mmtid);
array_shift($parents); // skip root
if ($node || isset($parts[2])) {
$parents[] = $mmtid;
}
$url = implode('/', $parents);
if ($mmtid) {
$url .= "/$mmtid";
}
$path = [];
foreach ($parents as $par) {
if (!($tree = mm_content_get($par))) {
break;
}
$path[] = mm_content_get_name($tree);
}
if (!$node) $path[] = $name;
$info = implode(' » ', $path);
if (isset($popup_URL)) {
$top = explode('/', $popup_URL, 2);
if (($found = strstr($url, "/$top[0]/")) !== FALSE) {
$url = substr($found, 1);
}
}
else {
$popup_URL = $url;
}
}
public static function addClass($elt) {
$list = $elt['#attributes']['class'] ?? [];
array_unshift($list, 'form-item');
return implode(' ', $list);
}
/**
* Split the result generated by setHiddenElt in mm.js.
*
* @param array $element
* The form element to process.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @return array
* The form element.
*/
public static function process(&$element, FormStateInterface $form_state) {
if (is_string($element['#value'])) {
$temp = $element['#value'];
$element['#value'] = [];
if (preg_match_all('#(\d+(?:/\d+)*)\{([^}]*)\}#', $temp, $matches, PREG_SET_ORDER)) {
foreach ($matches as $match) {
$element['#value'][$match[1]] = $match[2];
}
}
$form_state->setValueForElement($element, $element['#value']);
}
return $element;
}
private static function splitMMlist($str) {
if (is_array($str)) {
$matches = [];
foreach ($str as $key => $val) {
$matches[] = [NULL, $key, $val];
}
return $matches;
}
}
}
