monster_menus-9.0.x-dev/src/Form/EditContentForm.php

src/Form/EditContentForm.php
<?php

namespace Drupal\monster_menus\Form;

use Drupal\Core\Database\Connection;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Link;
use Drupal\Core\Render\Element;
use Drupal\Core\Url;
use Drupal\monster_menus\Constants;
use Drupal\monster_menus\Controller\DefaultController;
use Drupal\monster_menus\PermissionsSolver;
use Drupal\node\Entity\NodeType;
use Drupal\user\Entity\Role;
use Drupal\user\Entity\User;
use Symfony\Component\DependencyInjection\ContainerInterface;

class EditContentForm extends FormBase {

  /**
   * Database Service Object.
   *
   * @var \Drupal\Core\Database\Connection
   */
  protected $database;

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'mm_ui_content_edit';
  }

  /**
   * Constructs an object.
   */
  public function __construct(Connection $database) {
    $this->database = $database;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static($container->get('database'));
  }

  public function buildForm(array $form, FormStateInterface $form_state, $item = NULL, $mmtid = NULL, $is_group = FALSE, $is_new = FALSE, $is_search = FALSE) {
    $x = mm_ui_strings($is_group);
    $all_menus = $this->currentUser()->hasPermission('administer all menus');
    $ilist = array_combine([1, 2, 3, 4, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 75, 100], [1, 2, 3, 4, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 75, 100]);

    $form['path'] = [
      '#type' => 'value',
      '#value' => $mmtid,
    ];

    $owner = $form_state->isSubmitted() ? $form_state->getValue('owner') : ($item->uid ?? '');
    $is_site_root = $mmtid == 1 || !$is_new && (!empty($item->parent) && $item->parent == 1 && !$item->perms[Constants::MM_PERMS_IS_USER]);

    if (!$is_new && $this->currentUser()->hasPermission('see create/modify times')) {
      // this code correctly handles legacy tree nodes without creation dates/users
      if (!empty($item->ctime) && !is_null($item->cuid)) {
        $x['@ctime'] = mm_format_date($item->ctime, 'medium');
        $x['@cuser'] = mm_ui_uid2name($item->cuid, TRUE);
      }

      if (!empty($item->mtime) && !is_null($item->muid)) {
        $x['@mtime'] = mm_format_date($item->mtime, 'medium');
        $x['@muser'] = mm_ui_uid2name($item->muid, TRUE);
      }

      if (isset($x['@ctime'])) {
        $msg = $this->t('This @thing was created by @cuser on @ctime.', $x);
      }

      if (isset($x['@mtime']) && (!isset($x['@ctime']) || $x['@mtime'] != $x['@ctime'])) {
        if (!empty($msg)) {
          $msg .= ' ' . $this->t('It was last modified by @muser on @mtime.', $x);
        }
        else {
          $msg = $this->t('This @thing was last modified by @muser on @mtime.', $x);
        }
      }

      if (!empty($msg)) {
        $form['moddate'] = [
          '#markup' => $msg,
        ];
      }
    }

    if ($is_group) {
      $form['is_group'] = [
        '#type' => 'value',
        '#value' => TRUE,
      ];
    }

    if ($is_new) {
      $form['is_new'] = [
        '#type' => 'value',
        '#value' => TRUE,
      ];
    }
    else {
      $form['weight'] = [
        '#type' => 'value',
        '#value' => $item->weight,
      ];
    }

    $flags_not_admin = [];
    foreach (monster_menus_mm_tree_flags() as $flag => $val) {
      $flags_not_admin[$flag] = isset($item->flags[$flag]) && !$all_menus;
    }

    _mm_ui_form_array_merge($form, 'settings_perms', [
      '#type' => 'details',
      '#open' => TRUE,
      '#title' => $this->t('Permissions'),
    ]);

    if ($flags_not_admin['limit_write']) {
      $form['settings_perms']['message'] = ['#type' => 'item', '#input' => FALSE, '#markup' => $this->t('<p>You are not allowed to modify the first column of the permissions.</p>')];
    }

    if (DefaultController::menuAccessSolverByMMTID($mmtid)) {
      PermissionsSolver::getSolverForm($form['settings_perms'], $mmtid);
    }

    $types = static::getPermsLabels(isset($item->flags['limit_write']), $is_group, $x);
    if ($is_group) {
      unset($types[Constants::MM_PERMS_APPLY]);
    }

    $default_modes = [];
    if (!$form_state->hasValue('group_r_everyone') && isset($item->default_mode)) {
      $default_modes = explode(',', $item->default_mode);
    }

    $users = $groups = [];

    if (!$is_search) {
      // individual users
      if ($form_state->hasValue('all_values_user')) {
        $form_state['post']['path'] = $mmtid;
        [$groups, $users] = _mm_ui_form_parse_perms($form_state, NULL, FALSE);
      }
      else {
        $gids = [];
        $select = $this->database->select('mm_tree', 't');
        $select->join('mm_tree_access', 'a', 'a.mmtid = t.mmtid');
        $select->fields('a', ['gid'])
          ->condition('a.gid', 0, '<')
          ->condition('a.mmtid', $item->mmtid);
        $result = $select->execute();
        foreach ($result as $r) {
          $gids[] = $r->gid;
        }

        if ($gids) {
          $users_in_groups = mm_content_get_users_in_group($gids, NULL, FALSE, 0);
          if (!is_null($users_in_groups)) {
            foreach ($users_in_groups as $uid => $usr) {
              if (is_numeric($uid) && $uid >= 0) {
                $select = $this->database->select('mm_group', 'g');
                $select->join('mm_tree_access', 'a', 'a.gid = g.gid');
                $select->addExpression('GROUP_CONCAT(a.mode)', 'modes');
                $select->condition('a.gid', 0, '<')
                  ->condition('g.gid', $gids, 'IN')
                  ->condition('g.uid', $uid, 'IN');
                $r = $select->execute()->fetchObject();
                if ($r) {
                  $users[$uid]['modes'] = explode(',', $r->modes);
                  $users[$uid]['name'] = $usr;
                }
              }
            }
          }
        }

        $temp_perms = mm_content_get_perms($item->mmtid, FALSE, TRUE, TRUE);
        $allowed = [];
        foreach ([Constants::MM_PERMS_WRITE, Constants::MM_PERMS_SUB, Constants::MM_PERMS_APPLY, Constants::MM_PERMS_READ] as $mode) {
          foreach (array_keys($temp_perms[$mode]['groups']) as $gid) {
            if (!isset($allowed[$gid])) {
              $allowed[$gid] = !$is_new || mm_content_user_can($gid, Constants::MM_PERMS_APPLY);
              if ($allowed) {
                $members = mm_content_get_users_in_group($gid, '<br />', FALSE, 20, TRUE, $form);
                if ($members == '') {
                  $members = $this->t('(none)');
                }
                $groups[$gid]['name'] = mm_content_get_name($gid);
                $groups[$gid]['members'] = $members;
              }
            }
            $groups[$gid]['modes'][] = $mode;
          }
        }
      }
    }

    static::permissionsForm($form['settings_perms'], $types, $default_modes, $groups, $users, $owner, $is_search, $flags_not_admin['limit_write'], $x);

    if (!$is_search) {
      $node_prop_desc = '';
      if ($this->currentUser()->hasPermission('propagate page perms')) {
        $form['settings_perms']['propagate'] = [
          '#type' => 'checkbox',
          '#title' => $this->t('Copy these permissions to all @subthings of this @thing', $x),
          '#default_value' => FALSE,
          '#description' => $this->t('If this option is checked, the permissions will be copied to all @subthings of this one that you have permission to change.', $x),
        ];
        $node_prop_desc = ' ' . $this->t('If the option above is also checked, permissions will be copied to the content on all @subthings.', $x);
      }

      $form['settings_perms']['node_propagate'] = [
        '#type' => 'checkbox',
        '#title' => $this->t('Copy these permissions to all content on this @thing', $x),
        '#access' => !$is_group && $this->currentUser()->hasPermission('propagate node perms'),
        '#default_value' => FALSE,
        '#description' => $this->t('If this option is checked, the permissions will be copied to all pieces of content on this @thing that you have permission to change.', $x) . $node_prop_desc,
      ];
    }   // !$is_search

    $form['settings_perms']['hover'] = [
      '#type' => 'value',
      '#value' => $is_new ? '' : $item->hover,
    ];

    $form['additional_settings'] = [
      '#type' => 'vertical_tabs',
    ];

    // Initialize summaries.
    _mm_ui_add_summary_js($form);
    if (isset($item->flags['limit_name']) && !$all_menus) {
      $form['settings_general']['name'] = [
        '#type' => 'value',
        '#value' => $item->name,
        '#group' => 'additional_settings',
      ];
    }
    else {
      $form['settings_general'] = [
        '#type' => 'details',
        '#title' => $this->t('General settings'),
        '#description' => $this->t('General settings for this @thing', $x),
        '#group' => 'additional_settings',
      ];
      _mm_ui_add_summary_js($form['settings_general'], 'settings_general');
      $form['settings_general']['name'] = [
        '#type' => 'textfield',
        '#title' => $is_group ? $this->t('Group name') : ($is_site_root ? $this->t('Site name') : $this->t('Page name')),
        '#default_value' => $item->name ?? '',
        '#required' => TRUE,
        '#size' => 40,
        '#maxlength' => 128,
        '#description' => $is_group ? '' : $this->t('The name that appears in menus.'),
      ];
    }

    if ($is_group) {
      if ($is_new ? $mmtid == 1 : $item->parent == 1) {
        $form['settings_general']['alias'] = [
          '#type' => 'value',
          '#value' => $item->alias,
          '#group' => 'additional_settings',
        ];
      }

      $form['members'] = [
        '#type' => 'details',
        '#title' => $this->t('Group members'),
        '#open' => TRUE,
      ];

      if (!$is_new && $this->currentUser()->hasPermission('administer permissions')) {
        $roles = [];
        foreach (Role::loadMultiple() as $rid => $role) {
          if (isset($role->mm_gid) && $role->mm_gid == $item->mmtid) {
            $roles[] = [
              'r_name' => $role->label(),
              'r_link' => Url::fromRoute('entity.user_role.edit_form', ['user_role' => $rid])->toString(),
              'p_link' => Url::fromRoute('entity.user_role.edit_permissions_form', ['user_role' => $rid])->toString(),
            ];
          }
        }

        if ($roles) {
          $strings = ['@plur' => count($roles) == 1 ? $this->t('the role') : $this->t('these roles:') . ' '];
          $message = 'This group\'s members will be added to @plur ';
          foreach ($roles as $index => $role) {
            if ($index > 0) {
              $message .= ', ';
            }
            $message .= '<a href=":r_link' . $index . '">:r_name' . $index . '</a> (<a href=":p_link' . $index . '">permissions</a>)';
            $strings[':r_link' . $index] = $role['r_link'];
            $strings[':r_name' . $index] = $role['r_name'];
            $strings[':p_link' . $index] = $role['p_link'];
          }
          $form['members']['warning'] = [
            '#type' => 'item',
            '#input' => FALSE,
            '#markup' => $this->t($message, $strings),
          ];
        }
      }

      if (mm_content_is_vgroup($item->mmtid)) {
        if ($is_new) {
          $data = [
            'field' => $this->t('ColumnName'),
            'qfrom' => $this->t('FROM TableName WHERE Condition'),
          ];
        }
        else {
          $select = $this->database->select('mm_group', 'g');
          $select->join('mm_vgroup_query', 'v', 'g.vgid = v.vgid');
          $select->fields('v')
            ->condition('g.gid', $item->mmtid);
          $data = $select->execute()->fetchAssoc();
          if ($data) {
            $form['vgid'] = [
              '#type' => 'value',
              '#value' => $data['vgid'],
            ];

            $msgs = [
              Constants::MM_VGROUP_DIRTY_NEXT_CRON => $this->t('This group will be regenerated during the next cron run.'),
              Constants::MM_VGROUP_DIRTY_FAILED => $this->t('This group has been marked as potentially corrupt, and must be examined before it will be regenerated.'),
              Constants::MM_VGROUP_DIRTY_REDO => $this->t('This group was previously marked as potentially corrupt, but will be regenerated during the next cron run.'),
            ];
            if (isset($msgs[$data['dirty']])) {
              if ($data['dirty'] == Constants::MM_VGROUP_DIRTY_FAILED) {
                $this->messenger()->addError($msgs[$data['dirty']]);
              }
              else {
                $this->messenger()->addWarning($msgs[$data['dirty']]);
              }
            }
          }
        }

        $current_mode = isset($data['field']) && $data['field'] == Constants::MM_VQUERY_PHP ? 'php' : 'sql';
        $form['members']['qmode'] = [
          '#type' => 'select',
          '#title' => $this->t('Mode'),
          '#options' => ['sql' => $this->t('SQL query'), 'php' => $this->t('PHP function')],
          '#default_value' => $current_mode,
        ];
        $form['members']['sql'] = [
          '#type' => 'container',
          '#states' => ['visible' => ['#edit-qmode' => ['value' => 'sql']]],
        ];
        $form['members']['sql']['desc'] = [
          '#type' => 'item',
          '#description' => $this->t('Enter two portions of a SQL statement that returns the user IDs (uids) of the users in the group:<br />' .
          '<code>SELECT <span style="color:green;text-decoration:underline">ColumnName</span> AS uid <span style="color:green;text-decoration:underline">FROM TableName WHERE Condition</span></code>'),
        ];
        $form['members']['sql']['qfield'] = [
          '#type' => 'textfield',
          '#title' => $this->t('Column to select'),
          '#default_value' => $current_mode == 'sql' && isset($data['field']) ? $data['field'] : '',
          '#size' => 40,
          '#maxlength' => 40,
          '#description' => $this->t('The name of the database column (or a constant value) to SELECT; if blank, the group will not contain any users'),
        ];
        $form['members']['sql']['qfrom'] = [
          '#type' => 'textarea',
          '#title' => $this->t('FROM clause'),
          '#default_value' => $data['qfrom'] ?? '',
          '#rows' => 4,
          '#wysiwyg' => FALSE,
          '#description' => $this->t('The FROM portion of the SELECT statement; can be blank'),
        ];
        if (\Drupal::moduleHandler()->moduleExists('token')) {
          $form['members']['sql']['tokens'] = [
            '#type' => 'details',
            '#title' => $this->t('Tokens'),
            [
              '#theme' => 'token_tree_link',
              '#token_types' => ['mm_tree'],
              '#global_types' => FALSE,
            ],
          ];
        }
        $form['members']['php'] = [
          '#type' => 'container',
          '#states' => ['visible' => ['#edit-qmode' => ['value' => 'php']]],
        ];
        $form['members']['php']['qfrom_php'] = [
          '#type' => 'textfield',
          '#title' => $this->t('PHP function name'),
          '#default_value' => $current_mode == 'php' ? ($data['qfrom'] ?? '') : '',
          '#size' => 40,
          '#maxlength' => 40,
          '#description' => $this->t('The name of the PHP function to call. It must return an array of UIDs.'),
        ];
      }
      // normal group
      else {
        $token = \Drupal::csrfToken()->get(Constants::MM_LARGE_GROUP_TOKEN . $mmtid . ($is_new ? 'new' : ''));
        $form['mm_form_token'] = ['#value' => $token];
        mm_static($form, 'group_table', $mmtid, $token, 'members');
        _mm_ui_userlist_setup($is_search ? [] : NULL, $form['members'], 'members', $this->t('Members'), FALSE, $this->t('Choose the members of this group.'), '', !$is_search);
        if (!$is_search && isset($form['members']['members-add'])) {
          $form['members']['members-add'][] = ['#markup' => '&nbsp;&nbsp;&nbsp;'];
          $form['members']['members-add'][] = Link::createFromRoute($this->t('Download as CSV'), 'monster_menus.export', ['mm_tree' => $mmtid], ['attributes' => ['title' => $this->t('Download a CSV file containing the members of this group')]])->toRenderable();
        }
        $form['members']['upload_group'] = [
          '#type' => 'details',
          '#prefix' => '<div class="clearfix"></div>',
          '#title' => $this->t('Upload group members'),
          '#open' => FALSE,
          '#weight' => 999,
        ];
        $form['members']['upload_group']['upload_file'] = [
          '#name' => 'files[upload_file]',
          '#type' => 'file',
          '#title' => $this->t('CSV file of usernames'),
          '#description' => $this->t('Upload a file containing one username per line. The contents of this file will replace the entire user list, regardless of any changes made above.'),
        ];
        $form['members']['upload_group']['actions'] = [
          '#type' => 'actions',
          'upload_action' => [
            '#type' => 'submit',
            '#value' => $this->t('Upload'),
          ],
        ];
      }
    }
    // !$is_group
    elseif (isset($item->flags['limit_alias']) && !$all_menus || ($is_new ? $mmtid == 1 : $item->parent == 1)) {
      $form['settings_general']['alias'] = [
        '#type' => 'value',
        '#value' => $item->alias,
        '#group' => 'additional_settings',
      ];
    }
    else {
      $form['settings_general']['alias'] = [
        '#type' => 'machine_name',
        '#title' => $this->t('URL name'),
        '#machine_name' => [
          'exists' => 'mm_ui_machine_name_exists',
          'source' => ['settings_general', 'name'],
          'label' => $this->t('URL name'),
          'replace_pattern' => '[^-.\w]+',
          'replace' => '-',
          'error' => $this->t('The URL name must contain only letters, numerals, hyphens, periods and underscores.'),
          'standalone' => TRUE,
        ],
        '#default_value' => $item->alias ?? '',
        '#required' => (!isset($item->is_user_home) || !$item->is_user_home) && !$all_menus,
        '#size' => 20,
        '#maxlength' => 128,
        '#description' => $this->t('The name that will be used in the Web address of the page. ' .
          'Make this a shortened version of the Page name, using only lowercase letters, ' .
          'numerals, hyphens, periods and underscores.'),
        '#group' => 'additional_settings',
      ];
      if (!$is_search && !$is_new) {
        $prevent_mode = mm_get_setting('prevent_showpage_removal');
        if ($prevent_mode != Constants::MM_PREVENT_SHOWPAGE_REMOVAL_NONE && ($showpage_mmtid = mm_content_test_showpage_mmtid($item->mmtid))) {
          if ($prevent_mode == Constants::MM_PREVENT_SHOWPAGE_REMOVAL_WARN || $this->currentUser()->hasPermission('administer all menus')) {
            $form['settings_general']['alias']['#description'] = $showpage_mmtid == $item->mmtid ?
              $this->t('<strong>Warning:</strong> This page contains dynamic content which may no longer appear if its URL name is changed.') :
              $this->t('<strong>Warning:</strong> The sub-page <a href=":link">@title</a> contains dynamic content which may no longer appear if this page\'s URL name is changed.', ['@title' => mm_content_get_name($showpage_mmtid), ':link' => mm_content_get_mmtid_url($showpage_mmtid)->toString()]);
          }
          else {
            $form['settings_general']['alias'] = [
              '#type' => 'value',
              '#value' => $item->alias,
              '#group' => 'additional_settings',
            ];
            $form['settings_general']['message'] = [
              '#type' => 'item',
              '#input' => FALSE,
              '#description' => $showpage_mmtid == $item->mmtid ?
              $this->t('This page\'s URL name cannot be changed because it contains dynamic content that depends on the name.') :
              $this->t('This page\'s URL name cannot be changed because the sub-page <a href=":link">@title</a> contains dynamic content that depends on the name.', ['@title' => mm_content_get_name($showpage_mmtid), ':link' => mm_content_get_mmtid_url($showpage_mmtid)->toString()]),
              '#group' => 'additional_settings',
            ];
          }
        }
      }
    }

    if (isset($form['settings_general']['alias'])) {
      $form['settings_general']['alias_name'] = [
        '#type' => 'hidden',
        '#attributes' => ['class' => ['mm-alias-name']],
        '#value' => $item->alias ?? '',
        '#group' => 'additional_settings',
      ];
    }

    if ($all_menus) {
      $form['flags'] = [
        '#type' => 'details',
        '#title' => $this->t('Flags'),
        '#description' => $this->t('Attributes used in special queries; only administrators can edit this list'),
        '#group' => 'additional_settings',
      ];
      _mm_ui_add_summary_js($form['flags'], 'flags');

      $predefined = mm_ui_flags_info();
      foreach ($predefined as $module => $list) {
        if (count($predefined) > 1 || $is_search) {
          $form['flags'][$module] = [
            '#type' => 'details',
            '#title' => $module,
            '#open' => FALSE,
          ];
          $form['flags'][$module]['title'] = [
            '#markup' => "<h2>{$module}</h2>",
          ];
        }
        ksort($list);
        $weight = 1;
        foreach ($list as $flag => $elem) {
          if (!isset($elem['#title'])) {
            $elem['#title'] = $flag;
          }
          $elem['#weight'] = $elem['#type'] == 'checkbox' ? $weight : $weight + 100;
          if ($elem['#type'] == 'textfield') {
            $elem['#maxlength'] = 255;
          }
          $elem['#default_value'] = $elem['#type'] == 'checkbox' ? isset($item->flags[$flag]) : ($item->flags[$flag] ?? '');
          $elem['#prefix'] = "<div class='container-inline'>";
          $elem['#suffix'] = '&nbsp;&nbsp;&nbsp;' .
            $this->renderTooltip([
              '#text' => $this->t('help'),
              '#title' => $elem['#title'],
              '#tip' => $elem['#description'],
            ]) . '</div>';

          $elem['#attributes'] = ['class' => ['flag-checkbox']];
          unset($elem['#description']);
          $form['flags'][$module]["flag_$flag"] = $elem;

          unset($item->flags[$flag]);
          $weight++;
        }
      }

      $free_flags = [];
      foreach ($item->flags as $flag => $data) {
        if (!empty($data)) {
          $free_flags[] = "$flag=$data";
        }
        else {
          $free_flags[] = $flag;
        }
      }

      $form['flags']['free_flags'] = [
        '#type' => 'textarea',
        '#title' => $this->t('Others'),
        '#default_value' => join("\n", $free_flags),
        '#wysiwyg' => FALSE,
        '#weight' => 200,
        '#rows' => max(count($free_flags) + 1, 2),
        '#description' => $this->t('A free-form list of attributes, one per line. Can either be <code>name</code> or <code>name=value</code>.'),
      ];
    }

    if (!$is_group) {
      $form['menu'] = [
        '#type' => 'details',
        '#title' => $this->t('Menu and layout'),
        '#group' => 'additional_settings',
      ];
      _mm_ui_add_summary_js($form['menu'], 'menu');

      $menu_vals = [
        'bid' => Constants::MM_MENU_UNSET,
        'max_depth' => -1,
        'max_parents' => -1,
        'allow_reorder' => -1,
      ];
      if (!$is_new) {
        $result = $this->database->select('mm_tree_block', 'b')
          ->fields('b', ['bid', 'max_depth', 'max_parents'])
          ->condition('b.mmtid', $item->mmtid)
          ->range(0, 1)
          ->execute();
        if ($r = $result->fetchAssoc()) {
          $menu_vals = $r;
        }
        $allow_reorder = mm_content_resolve_cascaded_setting('allow_reorder', $item->mmtid, $reorder_at, $reorder_parent);
        if ($reorder_at == $item->mmtid) {
          $menu_vals['allow_reorder'] = $allow_reorder;
        }
        elseif (!isset($reorder_at) && ($item->mmtid == 1 || $item->mmtid == mm_home_mmtid())) {
          $menu_vals['allow_reorder'] = 0;
        }
        else {
          $menu_vals['allow_reorder'] = -1;
        }
      }

      if (!$flags_not_admin['limit_hidden']) {
        $form['menu']['hide_menu'] = [
          '#type' => 'checkbox',
          '#title' => $this->t('Don\'t show this @thing in the menu', $x),
          '#description' => $this->t('If checked, this page will not be listed in the navigation menu but can still be accessed directly using its URL address.'),
          '#default_value' => $item->hidden ?? FALSE,
        ];
      }
      else {
        $form['menu']['hide_menu'] = ['#type' => 'value', '#value' => $item->hidden];
      }

      if ($flags_not_admin['limit_location'] || $is_site_root) {
        $form['menu']['menu_start'] = [
          '#type' => 'value',
          '#value' => $is_site_root ? Constants::MM_MENU_BID : $menu_vals['bid'],
        ];
      }
      else {
        $blocks = [Constants::MM_MENU_UNSET => $this->t('Standard page')];
        $help = [Constants::MM_MENU_UNSET => $this->t('Creates a new page and the menu item that points to the page. The menu item will be indented and listed alphabetically under the parent menu item in the left navigation menu.')];
        foreach (mm_content_get_blocks(TRUE) as $bid => $b) {
          $blocks[$bid] = $b->toArray()['settings']['label'];
          $help[$bid] = $b->toArray()['settings']['help'];
        }

        $form['menu']['menu_start'] = [
          '#type' => $is_search ? 'select' : 'mm_help_radios',
          '#title' => $this->t('Location on screen'),
          '#default_value' => isset($blocks[$menu_vals['bid']]) ? $menu_vals['bid'] : mm_ui_mmlist_key0($blocks),
          '#attributes' => ['class' => ['settings-menu-start']],
          '#options' => $blocks,
          '#help' => $help,
        ];
      }

      $form['menu']['max_depth'] = [
        '#type' => 'select',
        '#title' => $this->t('Max. number of child levels to display'),
        '#default_value' => $menu_vals['max_depth'],
        '#attributes' => ['class' => ['settings-max-depth']],
        '#options' => $this->levels(),
      ];
      if ($all_menus) {
        $form['menu']['max_parents'] = [
          '#type' => 'select',
          '#title' => $this->t('Max. number of parent levels to display'),
          '#default_value' => $menu_vals['max_parents'],
          '#attributes' => ['class' => ['settings-max-parents']],
          '#options' => $this->levels(),
          '#description' => $this->t('As the user gets deeper down in the menu tree, higher-level entries will be removed. This keeps a deeply-nested menu from getting too indented. This setting is inherited by any @subthings.', $x),
        ];
        $form['menu']['allow_reorder'] = [
          '#type' => 'select',
          '#title' => $this->t('Allow the menu and its children to be reordered'),
          '#default_value' => $menu_vals['allow_reorder'],
          '#options' => [-1 => $this->t('(inherit from parent page)'), 1 => $this->t('Yes'), 0 => $this->t('No')],
          '#description' => $this->t('Administrators always have the ability to reorder menus.'),
        ];
      }
      else {
        $form['menu']['max_parents'] = [
          '#type' => 'value',
          '#value' => $menu_vals['max_parents'],
        ];
      }

      if ($this->currentUser()->hasPermission('show/hide post information')) {
        $form['defaults']['node_info'] = [
          '#type' => 'select',
          '#title' => $this->t('Default attribution style'),
          '#default_value' => $item->node_info ?? '',
          '#options' => _mm_ui_node_info_values($form),
          '#description' => $this->t('Unless disabled by an administrator, this value will be the default for all new content added to this page. You can change this behavior in the <em>Appearance</em> settings of the content.'),
        ];
      }
      elseif (!$is_search) {
        $form['defaults']['node_info'] = [
          '#type' => 'value',
          '#value' => $item->node_info,
        ];
      }

      mm_add_js_setting($form, 'comment_enabled', mm_module_exists('comment'));

      if (!$is_group && mm_module_exists('comment')) {
        if (mm_get_setting('comments.finegrain_readability')) {
          $comments_readable = $is_new ? '' : mm_content_get_cascaded_settings($item->mmtid, 'comments_readable');
          $form['defaults']['comments_readable'] = [
            '#type' => 'select',
            '#title' => $this->t('Who can read comments by default'),
            '#default_value' => $comments_readable,
            '#options' => _mm_ui_comment_read_setting_values($this->t('(inherit from parent page)')),
            '#description' => $this->t('This value will be the default for all new content added to this page. You can change this behavior in the <em>Comment settings</em> of the content.'),
          ];
        }
        $form['defaults']['comment'] = [
          '#type' => 'select',
          '#title' => $this->t('Who can add comments by default'),
          '#access' => $this->currentUser()->hasPermission('enable/disable comments') || $this->currentUser()->hasPermission('administer comments'),
          '#default_value' => $item->comment ?? '',
          '#options' => _mm_ui_comment_write_setting_values(),
          '#description' => $this->t('Unless disabled by an administrator, this value will be the default for all new content added to this page. You can change this behavior in the <em>Comment settings</em> of the content.'),
        ];
      }

      if (Element::getVisibleChildren($form['defaults'])) {
        $form['defaults'] = array_merge($form['defaults'], [
          '#type' => 'details',
          '#title' => $this->t('Defaults'),
          '#group' => 'additional_settings',
        ]);
        _mm_ui_add_summary_js($form['defaults'], 'defaults');
      }

      $form['appearance'] = [
        '#type' => 'details',
        '#title' => $this->t('Appearance'),
        '#group' => 'additional_settings',
      ];
      $theme[''] = $this->t('(use parent\'s theme)');
      $all_themes = [];
      $allowed_themes = mm_content_resolve_cascaded_setting('allowed_themes', $item->mmtid, $theme_at, $theme_parent, $is_new);
      $desc_add = '';
      foreach (\Drupal::service('theme_handler')->listInfo() as $id => $t) {
        if ($t->status) {
          $name = $t->info['name'];
          if ($is_search || !count($allowed_themes) || in_array($name, $allowed_themes)) {
            $theme[$id] = $name;
          }
          elseif ($all_menus) {
            $theme[$id] = $name . ' *';
            $desc_add = ' ' . $this->t('Themes in the list ending with * are only available to administrators, based on the current settings.');
          }

          $all_themes[$id] = $name;
        }
      }
      natcasesort($theme);

      if (count($theme) > 2) {
        $parent_mmtids = $is_new ? mm_content_get_parents_with_self($item->mmtid) : mm_content_get_parents($item->mmtid);
        $parents = mm_content_get($parent_mmtids, [], 0, TRUE);
        foreach (array_reverse($parents) as $parent) {
          if (!empty($parent->theme) && isset($all_themes[$parent->theme])) {
            $desc_add = ' ' . $this->t('If no theme is chosen here, the theme %themename from <a href=":link">@title</a> will be used.',
                ['%themename' => $parent->theme, '@title' => mm_content_get_name($parent), ':link' => mm_content_get_mmtid_url($parent->mmtid)->toString()]) . $desc_add;
            break;
          }
        }

        if (!$desc_add) {
          $desc_add = ' ' . $this->t('If no theme is chosen here, the default theme %themename will be used.',
              ['%themename' => \Drupal::config('system.theme')->get('default')]) . $desc_add;
        }

        $form['appearance']['theme'] = [
          '#type' => 'select',
          '#title' => $this->t('Theme for this @thing and its children', $x),
          '#default_value' => $item->theme ?? '',
          '#options' => $theme,
          '#description' => $this->t('If chosen, the theme will be applied to this @thing and its @subthings, unless overridden at a lower level.', $x) . $desc_add,
        ];
      }
      else {
        $form['appearance']['theme'] = [
          '#type' => 'value',
          '#value' => $item->theme,
        ];
      }

      if ($all_menus) {
        $desc = $this->t('This option is only available to administrators.');
        if (!$theme_parent) {
          $desc .= ' ' . $this->t('No parents of this @thing have theme limits, so if you deselect all themes here, non-admin users will be able to choose any theme in the list.', $x);
        }
        else {
          $desc .= ' ' . $this->t('To inherit the settings of the parent, <a href=":link">@title</a>, deselect all themes here.',
              ['@title' => mm_content_get_name($theme_parent), ':link' => mm_content_get_mmtid_url($theme_parent)->toString()]);
        }
        natcasesort($all_themes);
        $form['appearance']['allowed_themes'] = [
          '#type' => 'select',
          '#title' => $this->t('Allowed themes for this @thing and its children', $x),
          '#multiple' => TRUE,
          '#size' => 5,
          '#default_value' => !$is_new && $theme_at == $item->mmtid ? $allowed_themes : [],
          '#options' => $all_themes,
          '#description' => $desc,
        ];
      }

      _mm_ui_add_summary_js($form['appearance'], 'appearance');
      $form['appearance']['previews'] = [
        '#type' => 'checkbox',
        '#title' => $this->t('Show only the summaries ("teasers") of all contents'),
        '#default_value' => $item->previews ?? '',
      ];

      if (mm_get_setting('pages.enable_rss')) {
        $form['appearance']['rss'] = [
          '#type' => 'checkbox',
          '#title' => $this->t('Enable the RSS feed for this page'),
          '#default_value' => !empty($item->rss) ? $item->rss : '',
        ];
      }
      else {
        $form['appearance']['rss'] = [
          '#type' => 'value',
          '#value' => $item->rss ?? '',
        ];
      }

      $nodes_per_page = $is_new ? '' : mm_content_get_cascaded_settings($item->mmtid, 'nodes_per_page');
      if (is_null($nodes_per_page) && !$is_new && $item->mmtid == mm_home_mmtid()) {
        $nodes_per_page = Constants::MM_DEFAULT_NODES_PER_PAGE;
      }

      $form['appearance']['nodes_per_page'] = [
        '#type' => 'select',
        '#title' => $this->t('Pieces of content to display at one time'),
        '#default_value' => $nodes_per_page,
        '#options' => ['' => $this->t('(inherit from parent page)')] + $ilist + [
          0 => $this->t('(display all content immediately)'),
          -2 => $this->t('(display all content as needed)'),
        ],
        '#description' => $this->t('If more than this number of pieces of content is present, pagination controls will be displayed.'),
      ];
      $form['appearance']['hide_menu_tabs'] = [
        '#type' => 'select',
        '#title' => t('Hide the Contents/Settings/etc. tabs from non-admin users'),
        '#default_value' => $is_new ? -1 : mm_content_get_cascaded_settings($item->mmtid, 'hide_menu_tabs'),
        '#options' => [-1 => t('(inherit from parent page)'), 1 => t('Hide'), 0 => t('Show')],
        '#access' => $all_menus,
      ];

      if ($all_menus) {
        $form['settings_node_types'] = [
          '#type' => 'details',
          '#title' => $this->t('Allowed content types'),
          '#group' => 'additional_settings',
        ];
        $form['settings_node_types']['title'] = [
          '#markup' => '<h3>' . $this->t('Allowed content types for this @thing and its children', $x) . '</h3>',
        ];
        _mm_ui_add_summary_js($form['settings_node_types'], 'settings_node_types');

        $all_types = [];
        $allowed_node_types = mm_content_resolve_cascaded_setting('allowed_node_types', $item->mmtid, $types_at, $types_parent, $is_new);
        $types_inherit = $is_new || $types_at != $item->mmtid || count($allowed_node_types) == 0;
        /** @var \Drupal\node\Entity\NodeType $t */
        foreach (NodeType::loadMultiple() as $t) {
          $all_types[$t->id()] = $t->label();
        }
        natcasesort($all_types);

        $desc = $this->t('This option is only available to administrators.');
        if (!$types_parent) {
          $cb_desc = $this->t('<span style="color:red">No parents of this @thing have node types defined. Unless you uncheck this option and select something in the Node Types list, only administrators will be able to add content.</span>', $x);
          $form['settings_node_types']['#collapsed'] = FALSE;
        }
        else {
          $cb_desc = $this->t('If checked, the settings of the parent, @link, will be inherited',
            ['@link' => Link::fromTextAndUrl(mm_content_get_name($types_parent), mm_content_get_mmtid_url($types_parent))->toString()]);
          $desc .= ' ' . $this->t('If you deselect all node types here and do not check the Inherit option, only admin users will be able to add new content.');
        }

        $form['settings_node_types']['allowed_node_types_inherit'] = [
          '#type' => 'checkbox',
          '#title' => $this->t('Inherit from parent @thing', $x),
          '#default_value' => $types_inherit,
          '#description' => $cb_desc,
          '#attributes' => ['class' => ['settings-node-types']],
        ];
        $form['settings_node_types']['allowed_node_types'] = [
          '#type' => 'select',
          '#title' => $this->t('Node types'),
          '#multiple' => TRUE,
          '#size' => 10,
          '#default_value' => $types_at == $item->mmtid ? $allowed_node_types : [],
          '#options' => $all_types,
          '#states' => [
            'invisible' => ["#edit-allowed-node-types-inherit" => ['checked' => TRUE]],
          ],
        ];
        $form['settings_node_types']['help'] = [
          '#markup' => '<div class="description">' . $desc . '</div>',
        ];
      }

      $form['settings_archive'] = [
        '#type' => 'details',
        '#title' => $this->t('Archive'),
        '#group' => 'additional_settings',
        '#access' => $this->currentUser()->hasPermission('create archives'),
      ];
      _mm_ui_add_summary_js($form['settings_archive'], 'settings_archive');
      $item->frequency = 'month';
      $item->main_nodes = 10;
      $item->archive_mmtid = [];
      if (!$is_new) {
        $tree = mm_content_get($mmtid, Constants::MM_GET_ARCHIVE);
        if (isset($tree->archive_mmtid)) {
          $item = (object) array_merge((array) $item, (array) $tree);
          $item->archive_mmtid = [$item->archive_mmtid => mm_content_get_name($item->archive_mmtid)];
        }
      }

      if (isset($tree, $tree->archive_mmtid) && $tree->archive_mmtid == $mmtid) {
        $x['@title'] = mm_content_get_name($tree->main_mmtid);
        $x['@link'] = Link::createFromRoute(mm_content_get_name($tree->main_mmtid), 'monster_menus.handle_page_settings', ['mm_tree' => $tree->main_mmtid])->toString();
        $form['settings_archive']['#description'] = $this->t('This @thing is an archive for @link. To change the archival settings, visit that @thing.', $x);
      }
      else {
        $form['settings_archive']['#description'] = $this->t('Contents past a certain age will be automatically moved to a secondary page, where they are organized by date.');
        $have_archive = count($item->archive_mmtid);
        $form['settings_archive']['archive'] = [
          '#type' => 'checkbox',
          '#title' => $this->t('Use an archive'),
          '#default_value' => $have_archive,
        ];
        $form['settings_archive']['inner'] = [
          '#type' => 'container',
          '#prefix' => '<div class="settings-archive">',
          '#suffix' => '</div>',
          '#states' => [
            'visible' => ["#edit-archive" => ['checked' => TRUE]],
          ],
        ];
        $form['settings_archive']['inner']['frequency'] = [
          '#type' => 'select',
          '#title' => $this->t('Frequency'),
          '#options' => [
            'day' => $this->t('daily'),
            'week' => $this->t('weekly'),
            'month' => $this->t('monthly'),
            'year' => $this->t('yearly'),
          ],
          '#default_value' => $item->frequency,
        ];
        $form['settings_archive']['inner']['main_nodes'] = [
          '#type' => 'select',
          '#title' => $this->t('Pieces of content to show on the main page'),
          '#default_value' => $item->main_nodes,
          '#description' => $this->t('At least this many posts will be shown, even if they have been archived.'),
          '#options' => $ilist,
        ];
        $path_mmtids = mm_content_get_parents_with_self($item->mmtid);
        $form['settings_archive']['inner']['archive_mmtid'] = [
          '#type' => 'mm_catlist',
          '#mm_list_selectable' => Constants::MM_PERMS_WRITE,
          '#mm_list_popup_start' => implode('/', $path_mmtids),
          '#mm_list_route' => 'monster_menus.browser_load',
          '#title' => $this->t('Location of archive:'),
          '#description' => $this->t('Choose the page to contain the archived contents. It must already exist.'),
          '#mm_list_min' => 1,
          '#mm_list_max' => 1,
          '#default_value' => $item->archive_mmtid,
          '#states' => [
            'required' => ["#edit-archive" => ['checked' => TRUE]],
          ],
        ];
      }   // not archive destination
    }   // !$is_group

    $form['actions'] = ['#type' => 'actions'];
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $is_new ? ($mmtid == 1 ? $this->t('Create site', $x) : $this->t('Create @subthing', $x)) : $this->t('Save settings'),
      '#button_type' => 'primary',
    ];
    mm_add_library($form, 'modal_dialog');

    if (\Drupal::request()->request->all() && $form_state->getErrors()) {
      $this->messenger()->addError($this->t('The settings have not been saved because of the errors.'));
    }

    return $form;
  }

  public static function getPermsLabels($limited, $is_group, $x) {
    return [
      Constants::MM_PERMS_WRITE => $limited ?
        [
          t('Change @thing settings', $x),
          'If checked, @class can change this @thingpos settings.',
        ] :
        [
          t('Delete/&#8203;change settings', $x),
          'If checked, @class can delete this @thing or change its settings.',
        ],
      Constants::MM_PERMS_SUB => [
        t('Append @subthings', $x),
        'If checked, @class can append @subthings to this @thing.',
      ],
      Constants::MM_PERMS_APPLY => [
        t('Add content', $x),
        'If checked, @class can add content to this @thing.',
      ],
      Constants::MM_PERMS_READ => $is_group ?
        [
          t('See group members'),
          'If checked, @class can see the members of this group.',
        ] :
        [
          t('Read', $x),
          'If checked, @class can read this @thing.',
        ],
    ];
  }

  public function validateForm(array &$form, FormStateInterface $form_state) {
    $form_vals =& $form_state->getValues();
    if ($form_state->getTriggeringElement()['#id'] == 'edit-upload-action') {
      $validators = ['file_validate_extensions' => ['csv']];
      $file = file_save_upload('upload_file', $validators);
      if (!$file) {
        $form_state->setErrorByName('upload_file', $this->t('A file must be uploaded if you want to replace group membership using a file.'));
      }
      else {
        $form_vals['upload_file'] = $file;
      }
    }
    $is_new = isset($form_vals['is_new']);

    $mmtid = $form_vals['path'];
    if (!is_numeric($mmtid)) {
      $form_state->setErrorByName('', $this->t('Bad data'));
      return;
    }

    $x = mm_ui_strings($is_group = mm_content_is_group($mmtid));

    if (!mm_content_user_can($mmtid, $is_new ? Constants::MM_PERMS_SUB : Constants::MM_PERMS_WRITE)) {
      $message = $is_new ?
        'You do not have permission to add @subthings to this @thing' :
        'You do not have permission to modify this @thing';
      \Drupal::logger('mm')->warning($message, $x);
      $form_state->setErrorByName('', $this->t($message, $x));
      return;
    }

    $test_mmtid = !$is_new ? mm_content_get_parent($mmtid) : $mmtid;

    if (!_mm_ui_validate_entry($mmtid, $test_mmtid, $form_state, $form_vals, $is_new)) {
      return;
    }

    if ($is_group) {
      if (mm_content_is_vgroup($mmtid)) {
        if ($form_vals['qmode'] == 'sql') {
          if ($qfield = trim($form_vals['qfield'])) {
            $q = 'SELECT ' . $qfield . ' AS uid';
            if (trim($form_vals['qfrom']) != '') {
              $q .= ' ' . trim($form_vals['qfrom']);
            }
            try {
              $this->database->query($q);
            }
            catch (\Exception $e) {
              $form_state->setErrorByName('members', $this->t('There was an error testing the query.<br /><strong>Query:</strong> @query<br /><strong>Error:</strong> @error', ['@query' => $q, '@error' => $e->getMessage()]));
            }
          }
        }
        elseif ($qfield = trim($form_vals['qfrom_php'])) {
          try {
            if (!is_array(call_user_func($qfield))) {
              $form_state->setErrorByName('members', $this->t('The function @func did not return an array.', ['@func' => $qfield]));
            }
          }
          catch (\Exception | \Throwable $e) {
            $form_state->setErrorByName('members', $this->t('There was an error testing the function @func<br /><strong>Error:</strong> @error', ['@func' => $qfield, '@error' => $e->getMessage()]));
          }
        }
      }
      elseif (isset($form_vals['members'])) {
        _mm_ui_verify_userlist($form_state, $form_vals['members'], 'members');
      }
    }

    if ($this->currentUser()->hasPermission('administer all menus')) {
      _mm_ui_verify_userlist($form_state, $form_vals['owner'], 'owner');
    }

    [$form_vals['all_values_group'], $form_vals['all_values_user'], $form_vals['default_modes']] = _mm_ui_form_parse_perms($form_state, NULL, TRUE);

    if (!empty($form_vals['archive']) && $this->currentUser()->hasPermission('create archives')) {
      $archive_mmtid = mm_ui_mmlist_key0($form_vals['archive_mmtid']);
      // We're using a "sometimes_required", so this test is necessary
      if (!isset($archive_mmtid)) {
        $form_state->setErrorByName('archive_mmtid', $this->t('The archive location is required.'));
      }
      elseif (!mm_content_user_can($archive_mmtid, Constants::MM_PERMS_WRITE)) {
        $form_state->setErrorByName('archive_mmtid', $this->t('You do not have permission to modify the @thing you chose for the archive.', $x));
      }
      else {
        $tree = mm_content_get($archive_mmtid, Constants::MM_GET_ARCHIVE);
        if (isset($tree->archive_mmtid)) {
          if ($tree->archive_mmtid == $archive_mmtid && ($is_new || $tree->main_mmtid != $mmtid)) {
            $form_state->setErrorByName('archive_mmtid', $this->t('The @thing you chose for the archive is already the archive for another @thing.', $x));
          }
          elseif ($tree->main_mmtid == $archive_mmtid) {
            $form_state->setErrorByName('archive_mmtid', $this->t('The @thing you chose for the archive already contains an archive. Please choose a different @thing.', $x));
          }
        }
        $content = mm_content_get_nids_by_mmtid($archive_mmtid, 1);
        if (count($content)) {
          $form_state->setErrorByName('archive_mmtid', $this->t('The @thing you chose for the archive already has contents. Before it can become an archive, you must remove the contents.', $x));
        }
      }
    }

    if (isset($form_vals['menu_start']) && $form_vals['menu_start'] != Constants::MM_MENU_UNSET) {
      $blocks = mm_content_get_blocks(TRUE);
      $bid = $form_vals['menu_start'];
      if (is_null($test_mmtid) || $bid != Constants::MM_MENU_UNSET && !isset($blocks[$bid])) {
        $form_state->setErrorByName('menu_start', $this->t('The <em>Location on screen</em> you chose is not valid.', $x));
      }
      elseif ($blocks[$bid]->toArray()['settings']['show_node_contents'] && $form['menu']['menu_start']['#default_value'] != $bid && !mm_content_user_can($test_mmtid, Constants::MM_PERMS_WRITE)) {
        $form_state->setErrorByName('menu_start', $this->t('The <em>Location on screen</em> you chose would change the appearance of this @thing\'s parent. You do not have edit/delete permission for the parent @thing.', $x));
      }
    }
  }

  public function submitForm(array &$form, FormStateInterface $form_state) {
    $form_vals =& $form_state->getValues();
    $mmtid = $form_vals['path'];
    $x = mm_ui_strings($is_group = mm_content_is_group($mmtid));
    $is_new = isset($form_vals['is_new']);
    $alias = empty($form_vals['alias']) || $mmtid == mm_home_mmtid() && !$is_new ? '' : trim($form_vals['alias']);
    $name = trim($form_vals['name']);

    if ($is_group && ($is_new || $mmtid != mm_content_groups_mmtid())) {
      // Currently, groups always have MM_PERMS_APPLY for everyone. Change this if
      // you want to only allow some users to apply a group to something's
      // permissions.
      $form_vals['all_values_group'][Constants::MM_PERMS_APPLY] = $form_vals['all_values_user'][Constants::MM_PERMS_APPLY] = [];
      if (!in_array(Constants::MM_PERMS_APPLY, $form_vals['default_modes'])) {
        $form_vals['default_modes'][] = Constants::MM_PERMS_APPLY;
      }
    }
    $perms = [];
    foreach ([Constants::MM_PERMS_WRITE, Constants::MM_PERMS_SUB, Constants::MM_PERMS_APPLY, Constants::MM_PERMS_READ] as $m) {
      $perms[$m]['groups'] = $form_vals['all_values_group'][$m] ?? [];
      $perms[$m]['users']  = $form_vals['all_values_user'][$m] ?? [];
    }

    $flags = [];
    // Get the current cascaded settings.
    $cascaded = $is_new ? [] : mm_content_get_cascaded_settings($mmtid);
    $owner = $this->currentUser()->id();
    if ($this->currentUser()->hasPermission('administer all menus')) {
      $owner = $form_vals['owner'];

      $predefined = \Drupal::moduleHandler()->invokeAll('mm_tree_flags');
      foreach ($predefined as $flag => $elem) {
        $flag_name = "flag_$flag";
        if (!empty($form_vals[$flag_name])) {
          $flags[$flag] = $elem['#type'] == 'checkbox' ? '' : trim($form_vals[$flag_name]);
        }
      }

      if (!empty($form_vals['free_flags'])) {
        foreach (explode("\n", $form_vals['free_flags']) as $f) {
          $f = trim($f);
          if (!empty($f)) {
            if (preg_match('/^\s*(.*?)\s*=\s*(.*?)\s*$/', $f, $data)) {
              $flags[$data[1]] = $data[2];
            }
            else {
              $flags[trim($f)] = '';
            }
          }
        }
      }

      $cascaded['allowed_node_types'] = [];
      if (empty($form_vals['allowed_node_types_inherit'])) {
        // intentionally empty
        $cascaded['allowed_node_types'] = isset($form_vals['allowed_node_types']) && count($form_vals['allowed_node_types']) ? $form_vals['allowed_node_types'] : [''];
      }
    }
    elseif (!$is_new) {
      // read old flags for item
      if ($tree = mm_content_get($mmtid, Constants::MM_GET_FLAGS)) {
        _mm_ui_is_user_home($tree);
        $flags = $tree->flags;
      }
    }

    // Merge cascaded settings not already handled above
    foreach (mm_content_get_cascaded_settings() as $setting_name => $desc) {
      if ($setting_name != 'allowed_node_types') {
        if (!isset($desc['user_access']) || $this->currentUser()->hasPermission($desc['user_access'])) {
          if (isset($form_vals[$setting_name])) {
            $cascaded[$setting_name] = $form_vals[$setting_name];
          }
        }
      }
    }

    $md = $form_vals['max_depth'] ?? -1;
    $mp = $form_vals['max_parents'] ?? -1;
    if (isset($form_vals['menu_start'])) {
      $ms = $form_vals['menu_start'];
      if (empty($ms) && ($md != -1 || $mp != -1)) {
        $ms = Constants::MM_MENU_UNSET;
      }
    }
    else {
      $ms = '';
      $md = $mp = -1;
    }

    $params = [
      'alias'                => $alias,
      'archive_mmtid'        => 0,
      'cascaded'             => $cascaded,
      'comment'              => $form_vals['comment'] ?? '',
      'default_mode'         => implode(',', $form_vals['default_modes']),
      'flags'                => $flags,
      'hidden'               => !empty($form_vals['hide_menu']),
      'hover'                => $form_vals['hover'],
      'max_depth'            => $md,
      'max_parents'          => $mp,
      'menu_start'           => $ms,
      'name'                 => $name,
      'node_info'            => $form_vals['node_info'] ?? NULL,
      'perms'                => $perms,
      'previews'             => !empty($form_vals['previews']),
      'propagate_node_perms' => $form_vals['node_propagate'],
      'recurs_perms'         => !empty($form_vals['propagate']),
      'rss'                  => $form_vals['rss'] ?? '',
      'theme'                => $form_vals['theme'] ?? '',
      'uid'                  => $owner,
      'weight'               => isset($form_vals['weight']) && empty($form_vals['hide_menu']) ? $form_vals['weight'] : 0,
    ];
    if ($is_group) {
      if ($form_state->getTriggeringElement()['#id'] == 'edit-upload-action') {
        $filepath = $form_vals['upload_file'][0]->getFileUri();
        $handle = @fopen($filepath, 'r');
        $members = [];
        $success = 0;
        $fail = 0;
        if ($handle) {
          while ($row = fgetcsv($handle, 1000, ',')) {
            $username = trim($row[0]);
            if ($username !== '') {
              $uid = $this->database->query('SELECT uid FROM {users_field_data} WHERE name = :username', [':username' => $username])->fetchField();
              if (!empty($uid)) {
                $members[$uid] = 1;
                $success++;
              }
              else {
                $fail++;
              }
            }
          }
          if ($success) {
            $this->messenger()->addStatus($this->t('Successfully imported @success user(s).', ['@success' => $success]));
          }
          if ($fail) {
            $this->messenger()->addStatus($this->t('Failed to import @fail user(s).', ['@fail' => $fail]));
          }
          $params['members'] = array_keys($members);
        }
        else {
          $this->messenger()->addStatus($this->t('There was an internal problem uploading users. Please try again.'));
        }
      }
      else {
        if (isset($form_vals['members'])) {
          $params['members'] = array_keys($form_vals['members']);
        }
        $params['large_group_form_token'] = isset($form_vals['members-use-large-group']) && $form_vals['members-use-large-group'] == 'yes' ? $form['mm_form_token']['#value'] : '';
        if (isset($form_vals['qmode']) && $form_vals['qmode'] == 'php') {
          $params['qfield'] = Constants::MM_VQUERY_PHP;
          $params['qfrom'] = trim($form_vals['qfrom_php']);
        }
        else {
          $params['qfield'] = $qfield = isset($form_vals['qfield']) ? trim($form_vals['qfield']) : '';
          $params['qfrom'] = isset($form_vals['qfrom']) ? trim($form_vals['qfrom']) : '';
        }
      }
    }
    // !$is_group
    elseif (!empty($form_vals['archive'])) {
      $archive_mmtid = mm_ui_mmlist_key0($form_vals['archive_mmtid']);
      if ($archive_mmtid) {
        $params['archive_mmtid'] = $archive_mmtid;
        $params['frequency'] = $form_vals['frequency'];
        $params['main_nodes'] = $form_vals['main_nodes'];
      }
    }

    mm_module_invoke_all_array('mm_content_edit_submit_alter', [$is_new, $mmtid, &$params, $form_state]);

    $mmtid = mm_content_insert_or_update($is_new, $mmtid, $params);
    if (empty($mmtid)) {
      return;
    }

    if ($is_new) {
      if (!$is_group && in_array(Constants::MM_PERMS_READ, $form_vals['default_modes'])) {
        $x[':link'] = Url::fromRoute('monster_menus.handle_page_settings', ['mm_tree' => $mmtid])->toString();
        $this->messenger()->addStatus($this->t('<div id="public-warning">The @subthing was successfully created. Note that it is publicly viewable. To make adjustments to who can read it, <a href=":link">click here to change the settings</a>.</div>', $x));
      }
      else {
        $this->messenger()->addStatus($this->t('The @subthing was successfully created.', $x));
      }

      if ($form_vals['path'] == 1 && DefaultController::accessAllAdmin()) {
        $form_state->setRedirect('monster_menus.mm_admin_list_sites');
      }
      else {
        mm_set_form_redirect_to_mmtid($form_state, $mmtid);
      }
    }
    else {
      if (!$is_group && in_array(Constants::MM_PERMS_READ, $form_vals['default_modes'])) {
        $x[':link'] = Url::fromRoute('monster_menus.handle_page_settings', ['mm_tree' => $mmtid])->toString();
        $this->messenger()->addStatus($this->t('<div id="public-warning">The settings for this @thing have been saved. Note that it is publicly viewable. To make adjustments to who can read it, <a href=":link">click here to change the settings</a>.</div>', $x));
      }
      else {
        $this->messenger()->addStatus($this->t('The settings for this @thing have been saved.', $x));
      }
    }
  }

  public static function permissionsForm(&$form, $types, $default_modes, $groups, $users, $owner = NULL, $is_search = FALSE, $limit_write = FALSE, $x = [], $instance_suffix = '') {
    $x['@class'] = t('everyone');
    $all_menus = \Drupal::currentUser()->hasPermission('administer all menus');
    $checks = [];
    foreach (array_keys($types) as $type) {
      $checks[] = $type == Constants::MM_PERMS_WRITE && !$all_menus ? NULL : count($default_modes) && in_array($type, $default_modes);
      $checks[] = FALSE;
    }
    $form['table']['everyone'] = [
      '#type' => 'value',
      '#value' => [
        'title' => t('Everyone'),
        'types' => $types,
        'headings' => TRUE,
      ],
    ];
    $form['table']['everyone'][] = _mm_ui_perms_table_row('group', "everyone$instance_suffix", t('All users'), '', NULL, $types, $x, $checks);

    if (!$is_search) {
      $form['table']['indiv'] = [
        '#type' => 'value',
        '#value' => [
          'title' => t('Individuals'),
          'types' => $types,
          'action' => mm_ui_add_user_subform($form, 'settings-perms-indiv-add', t('add'), t('User(s) to add to permissions'), t('Add users to permissions'), 'Drupal.MMSettingsPermsAddUsers'),
        ],
      ];
      $x['@class'] = t('this user');
      if (is_numeric($owner)) {
        [$name, $msg, $owner_readonly] = mm_ui_owner_desc($form, $x, $owner, $is_search);
        $form['table']['indiv'][] = _mm_ui_perms_table_row(
          'user',
          'owner',
          t('<span class="settings-perms-owner-prefix">Owner: </span><span class="settings-perms-owner-name">@name</span>', ['@name' => $name]),
          $msg,
          $owner_readonly ? NULL : mm_ui_add_user_subform($form, 'settings-perms-indiv-owner', t('change'), t('Owner'), t('Change the owner'), 'Drupal.MMSettingsPermsOwner', $owner, $name),
          $types,
          $x,
          [TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE]
        );
      }

      // defaults for new row when no users are listed
      $checks = [TRUE, FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, FALSE];

      $delete_link = mm_ui_js_link_no_href(['title' => t('Remove this user'), 'onclick' => 'return Drupal.MMSettingsPermsDelete(this)'], t('delete'));
      $all_values_user = '';
      foreach ($users as $uid => $data) {
        $checks = [];
        foreach (array_keys($types) as $type) {
          if ($checked = in_array($type, $data['modes'])) {
            $all_values_user .= $type . $uid;
          }
          $checks[] = $checked;
          $checks[] = $type == Constants::MM_PERMS_WRITE && $limit_write;
        }
        $name = [
          ['#type' => 'item', '#input' => FALSE, '#markup' => $data['name']],
        ];
        $form['table']['indiv'][] = _mm_ui_perms_table_row('user', $uid, $name, '', $checks[0] && $limit_write ? '' : $delete_link, $types, $x, $checks);
      }

      // Empty row to be used when adding new users
      if ($limit_write) {
        $checks[0] = FALSE;
        $checks[1] = TRUE;
      }
      $form['table']['indiv'][] = _mm_ui_perms_table_row('user', 'new', '', '', $delete_link, $types, $x, $checks);
      if (!empty($owner) && empty($owner_readonly)) {
        $form['owner'] = ['#type' => 'hidden', '#default_value' => $owner];
      }

      $form["all_values_user$instance_suffix"] = [
        '#type' => 'hidden',
      // default value, in case JS is disabled
        '#default_value' => $all_values_user,
        '#attributes' => ['class' => ['mm-permissions-all-values-user']],
      ];
      if ($limit_write) {
        // Tell the JS code that it needs to act differently
        $form['limit_write_not_admin'] = ['#type' => 'hidden'];
      }

      $form['table']['groups'] = [
        '#type' => 'value',
        '#value' => [
          'title' => t('Groups'),
          'types' => $types,
          'action' => mm_ui_settings_perms_add_group_link($form),
        ],
      ];

      $delete_link = mm_ui_js_link_no_href(['title' => t('Remove this group'), 'onclick' => 'return Drupal.MMSettingsPermsDelete(this)'], t('delete'));
      $x['@class'] = t('the users in this group');
      $elem = [
        [
          '#type' => 'details',
          '#open' => FALSE,
          [
            '#type' => 'item',
            '#input' => FALSE,
          ],
        ],
      ];
      $all_values_group = '';
      foreach ($groups as $gid => $data) {
        if (empty($gid)) {
          continue;
        }
        $checks = [];
        foreach (array_keys($types) as $type) {
          if ($checked = in_array($type, $data['modes'])) {
            $all_values_group .= $type . $gid;
          }
          $checks[] = $checked;
          $checks[] = $type == Constants::MM_PERMS_WRITE && $limit_write;
        }
        $elem[0]['#title'] = $data['name'];
        $group_details = mm_content_get($gid);
        if ($group_details && !empty($group_details->uid) && ($group_user = User::load($group_details->uid))) {
          $owner_display = ['#theme' => 'username', '#account' => $group_user];
          $owner_display = \Drupal::service('renderer')->render($owner_display);
        }
        else {
          $owner_display = t('not available');
        }
        $group_display = Link::fromTextAndUrl($gid, mm_content_get_mmtid_url($gid))->toString();
        $info = mm_get_setting(mm_content_is_vgroup($gid) ? 'vgroup.group_info_message' : 'group.group_info_message');
        $info = t($info, ['@gid' => $group_display, '@owner' => $owner_display]);
        $group_info_message = '<div id="mmgroupinfo-' . $gid . '" class="hidden"><p>' . $info . '</p></div>';
        $group_info_link = Link::fromTextAndUrl(t('Group information'), Url::fromRoute('<current>', [], [
          'fragment' => "mmgroupinfo-" . $gid,
          'external' => TRUE,
          'attributes' => [
            'id' => mm_ui_modal_dialog([], $elem[0][0]),
            'title' => t('Information about this group'),
          ],
        ]))->toString();
        $edit_link = mm_content_user_can($gid, Constants::MM_PERMS_WRITE) ? Link::fromTextAndUrl(t('Edit this group'), Url::fromRoute('monster_menus.handle_page_settings', ['mm_tree' => $gid]))->toString() . ' | ' : '';
        $elem[0][0]['#markup'] = $group_info_message . '<div class="form-item">' . $edit_link . $group_info_link . '<br />' . $data['members'] . '</div>';
        $form['table']['groups'][] = _mm_ui_perms_table_row('group', $gid, $elem, '', $checks[0] && $limit_write ? '' : $delete_link, $types, $x, $checks);
      }

      // Empty row to be used when adding new groups
      if ($limit_write) {
        $checks[0] = FALSE;
        $checks[1] = TRUE;
      }
      else {
        $checks[0] = $checks[1] = FALSE;
      }
      $elem[0]['#title'] = ' ';
      $elem[0][0] = ['#markup' => '<div class="mm-permissions-group-new form-item"></div>'];
      $form['table']['groups'][] = _mm_ui_perms_table_row('group', 'new', $elem, '', $delete_link, $types, $x, $checks);

      $form["all_values_group$instance_suffix"] = [
        '#type' => 'hidden',
        // default value, in case JS is disabled
        '#default_value' => $all_values_group,
        '#attributes' => ['class' => ['mm-permissions-all-values-group']],
      ];
    }
    mm_ui_permissions($form['table']);
  }

  private function renderTooltip($desc) {
    $desc['#theme'] = 'tooltip';
    return \Drupal::service('renderer')->render($desc);
  }

  private function levels() {
    $list[-1] = t('(show all levels)');
    $list[0] = t('(show only the currently selected level)');
    for ($i = 1; $i <= 10; $i++) {
      $list[$i] = $i;
    }

    return $list;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc