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

src/Form/SearchReplaceForm.php
<?php

namespace Drupal\monster_menus\Form;

use Drupal\Core\Asset\AttachedAssets;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Render\RendererInterface;
use Drupal\monster_menus\AlterSearchReplace;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormState;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\UserSession;
use Drupal\filter\Render\FilteredMarkup;
use Drupal\monster_menus\Constants;
use Drupal\monster_menus\MMSearchAction\MMSearchActionBase;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\field\Entity\FieldStorageConfig;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;

class SearchReplaceForm extends FormBase {

  final public const MMSR_REGEXP = '/
    ([$|=]) \{
      ( (
        (?> (?: \{[^{}]*?\} | [^$|={}]++ | [$|=][^{] )+ ) |
        (?R)
      )* )
    \}/xs';

  /**
   * @var \Drupal\monster_menus\MMSearchAction\MMSearchActionManager
   */
  protected $pluginManager;

  /**
   * @var \Drupal\Core\Form\FormBuilderInterface
   */
  protected $formBuilder;

  /**
   * @var \Drupal\Core\Render\RendererInterface
   */
  protected $renderer;

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

  /**
   * @var \Symfony\Component\HttpFoundation\Request
   */
  protected $currentRequest;

  /**
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * @var \Drupal\Core\Cache\CacheBackendInterface
   */
  protected $cache;

  public function __construct(DefaultPluginManager $manager, FormBuilderInterface $form_builder, RendererInterface $renderer, Request $current_request, Connection $database, EntityTypeManagerInterface $entity_type_manager, CacheBackendInterface $cache) {
    $this->pluginManager = $manager;
    $this->formBuilder = $form_builder;
    $this->renderer = $renderer;
    $this->currentRequest = $current_request;
    $this->database = $database;
    $this->entityTypeManager = $entity_type_manager;
    $this->cache = $cache;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('plugin.manager.mm_search_action'),
      $container->get('form_builder'),
      $container->get('renderer'),
      $container->get('request_stack')->getCurrentRequest(),
      $container->get('database'),
      $container->get('entity_type.manager'),
      $container->get('cache.default')
    );
  }

  public static function getForm($mmtid) {
    $data = (object) [];
    if ($temp_mmtid = \Drupal::request()->query->getInt('mmtid', 0)) {
      $mmtid = $temp_mmtid;
    }
    // In case of error, don't save session as wrong user.
    $accountSwitcher = \Drupal::service('account_switcher');
    $accountSwitcher->switchTo(new UserSession(['uid' => 1]));
    $output = \Drupal::formBuilder()->getForm(self::class, $mmtid, $data);
    // Re-enable session saving.
    $accountSwitcher->switchBack();
    return $output;
  }

  private function walkPage($item, $key, $data) {
    static $last_fieldset;

    if (is_array($item) && $key != '#groups') {
      if (isset($item['#type']) && ($item['#type'] == 'fieldset' || $item['#type'] == 'details')) {
        $last_fieldset = (string) $item['#title'];
      }
      elseif (isset($item['#type']) && $item['#type'] == 'textarea') {
        $this->disableWysiwyg($item);
      }
      if (isset($item['#mm-search'])) {
        if (is_array($item['#mm-search'])) {
          $i = 0;
          foreach ($item['#mm-search'] as $k => $v) {
            $this_key = "$key-$i";
            if (isset($last_fieldset)) {
              $data->form['search-page-wheres']['#options'][$last_fieldset][$this_key] = $k;
            }
            else {
              $data->form['search-page-wheres']['#options'][$this_key] = $k;
            }
            $this->getFormOpts($data, $item, $this_key, $v);
            $i++;
          }
        }
        else {
          if (isset($last_fieldset)) {
            $data->form['search-page-wheres']['#options'][$last_fieldset][$key] = $item['#mm-search'];
          }
          else {
            $data->form['search-page-wheres']['#options'][$key] = $item['#mm-search'];
          }

          if (isset($item['#mm-search-opt-check'])) {
            $this->getFormOpts($data, $item, $key, $item['#mm-search-opt-check'], FALSE, '#mm-search-opt-check');
          }
          if (isset($item['#mm-search-opt-optgroup'])) {
            $this->getFormOpts($data, $item, $key, $item['#mm-search-opt-optgroup'], FALSE, '#mm-search-opt-optgroup');
          }
          if (isset($item['#mm-search-opt-list'])) {
            $this->getFormOpts($data, $item, $key, $item['#mm-search-opt-list'], FALSE, '#mm-search-opt-list');
          }
          if (isset($item['#mm-search-opt'])) {
            $this->getFormOpts($data, $item, $key, $item['#mm-search-opt']);
          }
        }
      }
      elseif (is_array($item) && !isset($item['#mm-search-processed']) && (!isset($item['#type']) || $item['#type'] != 'vertical_tabs')) {
        $item['#mm-search-processed'] = TRUE;
        array_walk($item, $this->walkPage(...), $data);
      }
    }
  }

  private function walkGroup($item, $key, $data) {
    static $last_fieldset;

    if (is_array($item) && $key != '#groups') {
      if (isset($item['#type']) && ($item['#type'] == 'fieldset' || $item['#type'] == 'details')) {
        $last_fieldset = (string) $item['#title'];
      }
      elseif (isset($item['#type']) && $item['#type'] == 'textarea') {
        $this->disableWysiwyg($item);
      }
      if (isset($item['#mm-search'])) {
        if (is_array($item['#mm-search'])) {
          $i = 0;
          foreach ($item['#mm-search'] as $k => $v) {
            $this_key = "$key-$i";
            if (isset($last_fieldset)) {
              $data->form['search-group-wheres']['#options'][$last_fieldset][$this_key] = $k;
            }
            else {
              $data->form['search-group-wheres']['#options'][$this_key] = $k;
            }
            $this->getFormOpts($data, $item, $this_key, $v, TRUE);
            $i++;
          }
        }
        else {
          if (isset($last_fieldset)) {
            $data->form['search-group-wheres']['#options'][$last_fieldset][$key] = $item['#mm-search'];
          }
          else {
            $data->form['search-group-wheres']['#options'][$key] = $item['#mm-search'];
          }

          if (isset($item['#mm-search-opt-check'])) {
            $this->getFormOpts($data, $item, $key, $item['#mm-search-opt-check'], TRUE, '#mm-search-opt-check');
          }
          if (isset($item['#mm-search-opt-optgroup'])) {
            $this->getFormOpts($data, $item, $key, $item['#mm-search-opt-optgroup'], TRUE, '#mm-search-opt-optgroup');
          }
          if (isset($item['#mm-search-opt-list'])) {
            $this->getFormOpts($data, $item, $key, $item['#mm-search-opt-list'], TRUE, '#mm-search-opt-list');
          }
          if (isset($item['#mm-search-opt'])) {
            $this->getFormOpts($data, $item, $key, $item['#mm-search-opt'], TRUE);
          }
        }
      }
      elseif (isset($item['#type']) && $item['#type'] == 'mm_userlist') {
        if (is_array($data->form["search-$key-choose"])) {
          $data->form["search-$key-choose"][$key] = $item;
        }
      }
      elseif (is_array($item) && !isset($item['#mm-search-processed']) && (!isset($item['#type']) || $item['#type'] != 'vertical_tabs')) {
        $item['#mm-search-processed'] = TRUE;
        array_walk($item, $this->walkGroup(...), $data);
      }
    }
  }

  private function walkNode($item, $key, $data) {
    if (is_array($item) && $key != '#groups') {
      $item['#weight'] = 0;
      $item['#required'] = FALSE;
      $item['#description'] = $item['#mm-search-description'] ?? NULL;
      $mm_search_opt = NULL;
      if (isset($item['#mm-search-key'])) {
        $key = $item['#mm-search-key'];
      }
      if (isset($item['#type'])) {
        if ($item['#type'] == 'container') {
          // Flatten containers
          if (isset($item['#language']) && isset($item[$item['#language']])) {
            $item = $item[$item['#language']];
          }
        }

        if (isset($item['#type'])) {
          switch ($item['#type']) {
            /** @noinspection PhpMissingBreakStatementInspection */
            case 'textarea':
              $this->disableWysiwyg($item);
              // no break

            case 'textfield':
            case 'datetime':
              if (isset($item['#field_name'])) {
                $field_name = $item['#field_name'];
                $col_name = $item['#columns'][0];
                $info = FieldStorageConfig::loadByName($item['#field_type'], $field_name);
                if ($info && isset($info['storage']['details']['sql'])) {
                  if (isset($info['foreign_keys'][$col_name])) {
                    $data_table = $info['foreign_keys'][$col_name]['table'];
                    $data_field = $info['foreign_keys'][$col_name]['columns'][$col_name];
                  }
                  else {
                    return;
                  }

                  $mm_search = $this->t('the field "@title"', ['@title' => $item['#title']]);
                  $mm_search_opt = [
                    'contains the value' => '= ${qval}',
                    'does not contain the value' => 'IS NULL OR {' . $data_table . '}.name <> ${qval}',
                  ];
                  $field_table = mm_ui_mmlist_key0($info['storage']['details']['sql'][EntityStorageInterface::FIELD_LOAD_CURRENT]);
                  $field_name = $info['storage']['details']['sql'][EntityStorageInterface::FIELD_LOAD_CURRENT][$field_table][$col_name];
                  AlterSearchReplace::$nodeQueries[$key] = [
                    [
                      $field_table => '{' . $field_table . '}.entity_id = {node}.nid',
                      $data_table => '{' . $data_table . '}.' . $data_field . ' = {' . $field_table . '}.' . $field_name,
                    ],
                    [
                      $key => '{' . $data_table . '}.name ={return $query_segments[' . $key . '][intval(${search-' . $key . '-0})]}',
                    ],
                  ];
                  unset($item['#description']);
                }
              }
              else {
                $mm_search = mb_strtolower($item['#title']);
                $mm_search_opt = AlterSearchReplace::$queryDefaults['s'];
              }

              break;
          }
        }
      }

      if (isset($item['#mm-search'])) {
        $mm_search = $item['#mm-search'];
      }
      if (isset($item['#mm-search-opt'])) {
        $mm_search_opt = $item['#mm-search-opt'];
      }

      if ($key === 'users_w-choose' || $key === 'users_w') {
        $data->form['search-groups_w'][$key] = $item;
      }
      elseif (isset($mm_search)) {
        if (is_array($mm_search)) {
          $i = 0;
          foreach ($mm_search as $k => $v) {
            $this_key = "$key-$i";
            $data->form['search-node-wheres']['#options'][$this_key] = $k;
            $this->getFormOpts($data, $item, $this_key, $v);
            $i++;
          }
        }
        else {
          $data->form['search-node-wheres']['#options'][$key] = $mm_search;
          $this->getFormOpts($data, $item, $key, $mm_search_opt);
        }
      }
      elseif (isset($item['#type']) && $item['#type'] == 'mm_userlist') {
        if (is_array($data->form["search-$key-choose"])) {
          $data->form["search-$key-choose"][$key] = $item;
        }
      }
      elseif (is_array($item) && !isset($item['#mm-search-processed']) && (!isset($item['#type']) || $item['#type'] != 'vertical_tabs')) {
        $item['#mm-search-processed'] = TRUE;
        array_walk($item, $this->walkNode(...), $data);
      }
    }
  }

  private function disableWysiwyg(&$item) {
    $item['#rows'] = 3;
    $item['#wysiwyg'] = FALSE;
  }

  private function getFormOpts(&$data, $item, $key, $opt, $is_group = FALSE, $type = 'select') {
    $segs = $is_group ? 'grp_segs' : 'segs';
    $do_form = !isset($data->form["search-$key"]);

    if ($do_form) {
      $data->form["search-$key"] = [
        '#prefix' => "<div id=\"search-$key\" class=\"hidden\">",
        '#suffix' => '</div>',
      ];
    }
    $used_subpanels_outer = FALSE;
    if (isset($opt[0]) && is_array($opt[0]) && $type != '#mm-search-opt-optgroup' && $type != '#mm-search-opt-list') {
      $i = 0;
      $weight = -10;
      foreach ($opt as $title => $o) {
        $k = "$key-$i";
        $options = array_keys($o);
        $sp_options = [];
        $used_subpanels = FALSE;
        foreach ($options as $o2) {
          if (preg_match('/^\[(.*?)\]$/', $o2, $matches)) {
            $subpanel = $matches[1];
            $sp_options[$subpanel] = $item[$subpanel]['#title'] ?? '';
            $item[$subpanel]['#prefix'] = '<div class="subpanel" name="' . "search-$k-$subpanel" . '">';
            $item[$subpanel]['#suffix'] = '</div>';
            if ($do_form) {
              $data->form["search-$key"]["search-$k-$subpanel"] = $item[$subpanel];
            }
            unset($item[$subpanel]);
            $used_subpanels = $used_subpanels_outer = TRUE;
          }
        }

        if ($do_form) {
          $data->form["search-$key"]["search-$k"] = [
            '#type' => 'select',
            '#title' => is_numeric($title) ? NULL : $title,
            '#options' => $used_subpanels ? $sp_options : $options,
            '#weight' => $item['#mm-search-weight'] ?? $weight++,
            '#attributes' => ($item['#mm-search-attr'] ?? ($used_subpanels ? ['class' => ['subpanel-select']] : NULL)),
          ];
        }
        $data->query[$segs][$k] = array_values($o);
        $i++;
      }
      if (isset($item['#mm-search-query'])) {
        $data->query['queries'][$key][0] = $item['#mm-search-joins'] ?? '';
        $data->query['queries'][$key][1] = $item['#mm-search-query'];
      }
    }
    elseif (is_array($opt)) {
      // arbitrary list of other form elements and/or selects
      if ($type == '#mm-search-opt-list') {
        $weight = -10;
        foreach ($opt as $k => $v) {
          if (isset($v['#type'])) {
            if ($do_form) {
              $data->form["search-$key"]["search-$key-$k"] = $v;
            }
          }
          else {
            if ($do_form) {
              $data->form["search-$key"]["search-$key-$k"] = [
                '#type' => 'select',
                '#title' => is_numeric($k) ? NULL : $k,
                '#options' => array_keys($v),
                '#weight' => $item['#mm-search-weight'] ?? $weight++,
                '#attributes' => ($item['#mm-search-attr'] ?? $item['#attributes']),
              ];
            }

            foreach ($v as $k2 => $v2) {
              if (isset(AlterSearchReplace::$queryDefaults[$v2][$k2])) {
                $data->query[$segs]["$key-$k"][] = AlterSearchReplace::$queryDefaults[$v2][$k2];
              }
              else {
                $data->query[$segs]["$key-$k"][] = $v2;
              }
            }
          }
        }
      }
      else {
        // categorized select list (<optgroup>)
        if ($type == '#mm-search-opt-optgroup') {
          $keys = [];
          foreach ($opt as $cat => $v) {
            foreach ($v as $k2 => $v2) {
              foreach ($v2 as $k3 => $v3) {
                $keys[$cat][$k2] = $k3;
                if (isset(AlterSearchReplace::$queryDefaults[$v3][$k2])) {
                  $data->query[$segs][$key][] = AlterSearchReplace::$queryDefaults[$v3][$k2];
                }
                else {
                  $data->query[$segs][$key][] = $v3;
                }
              }
            }
          }
        }
        else {
          $keys = array_keys($opt);
          foreach ($opt as $k => $v) {
            if (isset(AlterSearchReplace::$queryDefaults[$v][$k])) {
              $data->query[$segs][$key][] = AlterSearchReplace::$queryDefaults[$v][$k];
            }
            else {
              $data->query[$segs][$key][] = $v;
            }
          }
        }

        if ($do_form) {
          $data->form["search-$key"]["search-$key-0"] = [
            '#title' => $type == '#mm-search-opt-check' ? $keys[1] : NULL,
            '#type' => $type == '#mm-search-opt-check' ? 'checkbox' : 'select',
            '#options' => $keys,
            '#weight' => $item['#mm-search-weight'] ?? -1,
            '#attributes' => ($item['#mm-search-attr'] ?? ($item['#attributes'] ?? [])),
            '#value' => NULL,
          ];
        }
      }
      if (isset($item['#mm-search-query'])) {
        unset($item['#description']);
        $data->query['queries'][$key][0] = $item['#mm-search-joins'] ?? '';
        $data->query['queries'][$key][1] = $item['#mm-search-query'];
      }
    }
    else {
      if (isset($item['#mm-search-query'])) {
        $data->query['queries'][$key][0] = $item['#mm-search-joins'];
        $data->query['queries'][$key][1][$key] = $item['#mm-search-query'][$key];
      }
      return;
    }

    unset($item['#title']);
    if (!$used_subpanels_outer && $type != '#mm-search-opt-check') {
      $data->form["search-$key"][$key] = $item;
    }
  }

  private function getResultQuery($data, $query_info, $results, &$result_query, &$header, &$search_type) {
    $_mmsr_query_segments = &drupal_static('_mmsr_query_segments');

    $row = 0;
    $visited = ['search-logic' => TRUE];
    $args = [];
    foreach (explode('&', $data) as $arg) {
      [$key, $val] = explode('=', $arg, 2);
      // Multiple SELECTs have '[]' in the name.
      $key = preg_replace('/\[\]$/', '', $key);
      $val = urldecode($val);
      if (!isset($visited[$key]) || !$visited[$key]) {
        $visited[$key] = TRUE;
      }
      else {
        $row++;
        $visited = ['search-logic' => TRUE];
      }
      $args[$row][$key] = $val;
    }
    $joins = [];
    $wheres = $logic = $result_groupby = $sort_order = $count_query = '';
    $wlist2 = [];
    $cat_key = 'search-page-cat';
    $depth = -1;
    foreach ($args as $row) {
      $wlist = $vars = [];
      $qlist = [];
      foreach ($row as $key => $val) {
        if ($key == 'search-type') {
          $_mmsr_query_segments = $_SESSION['mmsr-query']['segs'];
          $search_type = [
            MMSearchActionBase::SEARCH_TYPE_NODES,
            MMSearchActionBase::SEARCH_TYPE_PAGES,
            MMSearchActionBase::SEARCH_TYPE_NODES_ON_PAGES,
            MMSearchActionBase::SEARCH_TYPE_GROUPS,
          ][$val];
          switch ($val) {
            // contents
            case 0:
              $cat_key = '';
              // no break

              // contents on pages
            case 2:
              $count_query = 'SELECT COUNT(DISTINCT {node}.nid) FROM {node} INNER JOIN {node_field_data} ON {node_field_data}.vid = {node}.vid';
              if ($results) {
                $joins['node_revision'] = '{node_revision}.vid = {node}.vid';
                $header = [
                  ['data' => $this->t('Title'), 'field' => '{node_field_data}.title'],
                  ['data' => $this->t('Type'), 'field' => '{node_field_data}.type'],
                  ['data' => $this->t('Modified'), 'field' => '{node_field_data}.changed', 'sort' => 'DESC'],
                  ['data' => $this->t('Created'), 'field' => '{node_field_data}.created'],
                ];
                if (mm_module_exists('amherstprofile')) {
                  $joins['eduprofile'] = '{eduprofile}.uid = {node_field_data}.uid';
                  $header[] = ['data' => $this->t('Owner'), 'field' => '{eduprofile}.lastname'];
                  $result_query = 'SELECT MAX({node_field_data}.title) AS title, {node_field_data}.nid, MAX({node_field_data}.type) AS type, MAX({node_field_data}.changed) AS changed, MAX({node_field_data}.created) AS created, MAX({eduprofile}.pref_fml) AS pref_fml, MAX({eduprofile}.pref_lfm) as pref_lfm, MAX({eduprofile}.lastname) AS lastname, MAX({eduprofile}.firstname) AS firstname, MAX({eduprofile}.username) AS name, MAX({eduprofile}.middlename) AS middlename, MAX({eduprofile}.hover) AS hover, MAX({node_field_data}.uid) AS uid FROM {node} INNER JOIN {node_field_data} ON {node_field_data}.vid = {node}.vid';
                }
                else {
                  $joins['users_field_data'] = '{users_field_data}.uid = {node_field_data}.uid';
                  $header[] = ['data' => $this->t('Owner'), 'field' => '{users_field_data}.name'];
                  $result_query = 'SELECT MAX({node_field_data}.title) AS title, {node_field_data}.nid, MAX({node_field_data}.type) AS type, MAX({node_field_data}.changed) AS changed, MAX({node_field_data}.created) AS created, MAX({node_field_data}.uid) AS uid, MAX({users_field_data}.name) AS name FROM {node} INNER JOIN {node_field_data} ON {node_field_data}.vid = {node}.vid';
                }
                $result_groupby = ' GROUP BY {node_field_data}.nid ';
              }
              $joins['mm_node2tree'] = '{mm_node2tree}.nid = {node_field_data}.nid';
              $joins['mm_tree'] = '{mm_tree}.mmtid = {mm_node2tree}.mmtid';
              break;

            // groups
            case 3:
              $cat_key = 'search-group-cat';
              $_mmsr_query_segments = $_SESSION['mmsr-query']['grp_segs'];
              // no break

              // pages
            case 1:
              $count_query = 'SELECT COUNT(DISTINCT {mm_tree}.mmtid) FROM {mm_tree}';
              if ($results) {
                $header = [
                  ['data' => $this->t('Page'), 'field' => '{mm_tree}.name'],
                ];
                if (mm_module_exists('amherstprofile')) {
                  $joins['eduprofile'] = '{eduprofile}.uid = {mm_tree}.uid';
                  $result_query = 'SELECT {mm_tree}.name AS pgname, {mm_tree}.mmtid, {eduprofile}.pref_fml, {eduprofile}.pref_lfm, {eduprofile}.lastname, {eduprofile}.firstname, {eduprofile}.username AS name, {eduprofile}.middlename, {eduprofile}.hover, {mm_tree}.uid FROM {mm_tree}';
                  $header[] = ['data' => $this->t('Owner'), 'field' => '{eduprofile}.lastname'];
                }
                else {
                  $joins['users_field_data'] = '{users_field_data}.uid = {mm_tree}.uid';
                  $result_query = 'SELECT MAX({mm_tree}.name) AS pgname, {mm_tree}.mmtid, MAX({users_field_data}.name) AS name, MAX({mm_tree}.uid) AS uid FROM {mm_tree}';
                  $header[] = ['data' => $this->t('Owner'), 'field' => '{users_field_data}.name'];
                }
                $result_groupby = ' GROUP BY {mm_tree}.mmtid ';
              }
              break;
          } // switch
        }
        elseif ($key == 'search-node-type' && $val) {
          $w = $this->parse('{node_field_data}.type=${qval}', 'node', $val);
          if ($w != '') {
            $wlist2[] = $w;
          }
        }
        elseif ($key == $cat_key) {
          if ($v = intval($val)) {
            if ($depth) {
              $joins['mm_tree_parents'] = '{mm_tree_parents}.mmtid = {mm_tree}.mmtid';
              if ($depth == -1) {
                $w = $this->parse('({mm_tree}.mmtid = ${ival} OR {mm_tree_parents}.parent = ${ival})', 'mm_node2tree', $v);
              }
              elseif ($depth == 1) {
                $w = $this->parse('({mm_tree}.mmtid = ${ival} OR {mm_tree}.parent = ${ival})', 'mm_node2tree', $v);
              }
              else {
                $w = $this->parse('({mm_tree}.mmtid = ${ival} OR {mm_tree_parents}.parent = ${ival} AND (SELECT depth FROM {mm_tree_parents} WHERE mmtid = {mm_tree}.mmtid AND parent = {mm_tree}.parent) - {mm_tree_parents}.depth < ' . $depth . ')', 'mm_node2tree', $v);
              }
            }
            else {
              $w = $this->parse('{mm_tree}.mmtid = ${ival}', 'mm_node2tree', $v);
            }

            if ($w != '') {
              $wlist2[] = $w;
            }
            $_SESSION['mmsr-mmtid'] = $v;
          }
        }
        elseif ($key == 'search-logic') {
          if ($wheres) {
            $logic = $val == 'and' ? ' AND ' : ' OR ';
          }
        }
        elseif ($key == 'search-page-wheres' || $key == 'search-group-wheres' || $key == 'search-node-wheres') {
          if (isset($query_info[$val])) {
            $qlist[] = &$query_info[$val];
          }
        }
        elseif ($key == 'search-page-depth' || $key == 'search-group-depth') {
          $depth = $val;
        }
        else {
          $vars[$key] = $val;
        }
      } // foreach

      foreach ($qlist as $q) {
        if (isset($q[0]) && is_array($q[0])) {
          foreach ($q[0] as $table => $join_seg) {
            if (!isset($joins[$table])) {
              $joins[$table] = $this->parse($join_seg, $table, '', $vars);
            }
          }
        }

        $w = '';
        foreach ($q[1] as $varname => $seg) {
          if (isset($vars[$varname])) {
            $w .= $this->parse($seg, '', $vars[$varname], $vars);
          }
          elseif (isset($vars[$varname . '[date]'])) {
            $w .= $this->parse($seg, '', $vars[$varname . '[date]'], $vars);
          }
          elseif (!str_contains($seg, '$')) {
            $w .= $this->parse($seg, '', '', $vars);
          }
        }

        if ($w != '') {
          $wlist[] = "($w)";
        }
      }

      if ($wlist) {
        $wheres .= $logic . join(' AND ', $wlist);
      }
    }   // foreach

    if ($wlist2) {
      if ($wheres) {
        $wheres = '(' . $wheres . ') AND ';
      }
      $wheres .= join(' AND ', $wlist2);
    }

    $query_joins = '';
    foreach ($joins as $table => $on) {
      $query_joins .= ' LEFT JOIN {' . $table . '} ON ' . $on;
    }

    if ($wheres) {
      $query_joins .= ' WHERE ' . $wheres;
    }
    $result_query .= $query_joins;
    if ($results) {
      $result_query .= $result_groupby;
      foreach ($header as $h) {
        $get = $this->currentRequest->query;
        if ($get->getAlnum('order', FALSE) === $h['data']->render()) {
          $sort_field = $h['field'];
          $sort = $get->getAlpha('sort');
          $sort_order = $sort == 'desc' || $sort == 'asc' ? strtoupper($sort) : ($h['sort'] ?? '');
          break;
        }
        elseif (!isset($sort_field)) {
          $sort_field = $h['field'];
          $sort_order = $h['sort'] ?? '';
        }
      }

      if (isset($sort_field)) {
        $result_query .= " ORDER BY MAX($sort_field) $sort_order";
      }
    }
    return $count_query . $query_joins;
  }

  private function setVariables($val) {
    $_mmsr_vars = &drupal_static('_mmsr_vars');

    $_mmsr_vars['val'] = $val;
    $_mmsr_vars['ival'] = intval($val);
    $_mmsr_vars['qval'] = $this->database->quote($val);
  }

  private function parse($seg, $table, $val, $vars2 = NULL) {
    $_mmsr_vars = &drupal_static('_mmsr_vars');

    $_mmsr_vars = !empty($table) ? ['table' => '{' . $table . '}'] : [];
    $this->setVariables($val);
    if (is_array($vars2)) {
      $_mmsr_vars = array_merge($_mmsr_vars, $vars2);
    }

    return trim(preg_replace_callback(static::MMSR_REGEXP, $this->regexp(...), $seg));
  }

  private function regexp($matches) {
    $_mmsr_vars = &drupal_static('_mmsr_vars');
    $_mmsr_query_segments = &drupal_static('_mmsr_query_segments');

    // debug_add_dump($matches,$_mmsr_vars);
    /** @noinspection PhpUnusedLocalVariableInspection */
    // set for use within pseudocode
    $query_segments = $_mmsr_query_segments;
    // ={something}
    if ($matches[1] == '=') {
      $e = preg_replace_callback(static::MMSR_REGEXP, $this->regexp(...), $matches[2]) . ';';
      return preg_replace_callback(static::MMSR_REGEXP, $this->regexp(...), eval($e));
    }

    // |{something}
    if ($matches[1] == '|') {
      $e = [];
      foreach (explode(',', $old = $_mmsr_vars['val']) as $v) {
        $this->setVariables($v);
        $e[] = preg_replace_callback(static::MMSR_REGEXP, $this->regexp(...), $matches[2]);
      }
      $this->setVariables($old);
      return preg_replace_callback(static::MMSR_REGEXP, $this->regexp(...), join(', ', $e));
    }

    // ${'something'}
    if ($matches[3][0] == "'") {
      return $this->database->quote($_mmsr_vars[substr($matches[3], 1, -1)]);
    }

    // ${something}
    return $_mmsr_vars[$matches[3]];
  }

  private function searchDate($item_id, $field) {
    $_mmsr_vars = &drupal_static('_mmsr_vars');
    $_mmsr_query_segments = &drupal_static('_mmsr_query_segments');

    $date = $_mmsr_vars[$item_id . '[date]'] . ' ' . $_mmsr_vars[$item_id . '[time]'];
    $dv = preg_match('/\d/', $date) ? @date_create($date) : '';
    return $field . $_mmsr_query_segments[$item_id][intval($_mmsr_vars["search-$item_id-0"])] . ($dv ? date_format($dv, 'U') : 0);
  }

  private function getSearchDepthList($thing) {
    return [
      0 => $this->t('only this @thing', ['@thing' => $thing]),
      -1 => $this->t('this @thing and all children', ['@thing' => $thing]),
      1 => $this->t('this @thing and 1 level of children', ['@thing' => $thing]),
      2 => $this->t('this @thing and 2 levels of children', ['@thing' => $thing]),
      3 => $this->t('this @thing and 3 levels of children', ['@thing' => $thing]),
      4 => $this->t('this @thing and 4 levels of children', ['@thing' => $thing]),
      5 => $this->t('this @thing and 5 levels of children', ['@thing' => $thing]),
    ];
  }

  public function getFormId() {
    return 'mm_search_form';
  }

  public function buildForm(array $form, FormStateInterface $form_state, $mmtid = NULL, $data = NULL) {
    drupal_static('mm_building_search_form', 1);
    $item = (object) ['mmtid' => $mmtid, 'flags' => []];
    $form = $this->formBuilder->getForm(EditContentForm::class, $item, $mmtid, FALSE, TRUE, TRUE);

    /** @var \stdClass $data */
    $data->form['search-type'] = [
      '#type' => 'select',
      '#title' => $this->t('Find all'),
      '#default_value' => 1,
      '#options' => [$this->t('contents'), $this->t('pages'), $this->t('contents on pages'), $this->t('groups')],
    ];
    $tree = mm_content_get($item->mmtid);
    $tree->name = mm_content_get_name($tree);
    $data->form['search-group-catlist'] = [
      '#prefix' => '<div id="search-group-catlist">',
      '#suffix' => '</div>',
    ];
    $data->form['search-group-catlist']['search-group-cat'] = [
      '#type' => 'mm_grouplist',
      '#mm_list_min' => 1,
      '#mm_list_max' => 1,
      '#mm_list_selectable' => '',
      '#title' => $this->t('starting at'),
      '#default_value' => [$tree->mmtid => $tree->name],
      '#description' => $this->t('Search down the tree starting at this location.'),
    ];
    $data->form['search-group-catlist']['search-group-depth'] = [
      '#type' => 'select',
      '#title' => $this->t('limited to'),
      '#options' => $this->getSearchDepthList($this->t('group')),
      '#default_value' => -1,
    ];
    $data->form['search-page-catlist'] = [
      '#prefix' => '<div id="search-page-catlist">',
      '#suffix' => '</div>',
    ];
    $data->form['search-page-catlist']['search-page-cat'] = [
      '#type' => 'mm_catlist',
      '#mm_list_min' => 1,
      '#mm_list_max' => 1,
      '#mm_list_selectable' => '',
      '#mm_list_no_info' => TRUE,
      '#title' => $this->t('starting at'),
      '#default_value' => [$tree->mmtid => $tree->name],
      '#description' => $this->t('Search down the tree starting at this location.'),
    ];
    $data->form['search-page-catlist']['search-page-depth'] = [
      '#type' => 'select',
      '#title' => $this->t('limited to'),
      '#options' => $this->getSearchDepthList($this->t('page')),
      '#default_value' => -1,
    ];
    $data->form['search-logic'] = [
      '#type' => 'select',
      '#default_value' => 'and',
      '#id' => 'search-logic',
      '#attributes' => ['style' => 'display: none'],
      '#options' => ['and' => $this->t('and'), 'or' => $this->t('or')],
    ];
    $data->form['search-page-wheres'] = [
      '#type' => 'select',
      '#default_value' => '',
      '#id' => 'search-page-wheres',
      '#attributes' => ['style' => 'display: none'],
      '#options' => ['' => '(choose a property)'],
    ];
    $data->form['search-group-wheres'] = [
      '#type' => 'select',
      '#default_value' => '',
      '#id' => 'search-group-wheres',
      '#attributes' => ['style' => 'display: none'],
      '#options' => ['' => '(choose a property)'],
    ];

    $node_types = ['' => $this->t('(any type)')];
    /** @var \Drupal\node\Entity\NodeType $type */
    foreach (NodeType::loadMultiple() as $type) {
      if (mm_node_access_create($type->id())) {
        $node_types[$type->id()] = $type->label();
      }
    }
    natcasesort($node_types);
    $data->form['search-node-type'] = [
      '#type' => 'select',
      '#id' => 'search-node-type',
      '#options' => $node_types,
    ];

    AlterSearchReplace::alterMM($form, FALSE);
    array_walk($form, $this->walkPage(...), $data);

    $form = $this->formBuilder->getForm(EditContentForm::class, $item, $mmtid, TRUE, TRUE, TRUE);
    AlterSearchReplace::alterMM($form, TRUE);
    array_walk($form, $this->walkGroup(...), $data);

    $data->form['search-node-wheres'] = [
      '#type' => 'select',
      '#id' => 'search-node-wheres',
      '#options' => ['' => $this->t('(choose a property)')],
    ];
    $data->form['data'] = [
      '#type' => 'hidden',
    ];
    $data->form['actions'] = [
      '#type' => 'actions',
      'reset' => [
        '#type' => 'submit',
        '#value' => $this->t('Reset'),
      ],
    ];

    $data->form['do_actions'] = [
      '#type' => 'details',
      '#title' => $this->t('Perform the action'),
      '#open' => TRUE,
      '#id' => 'search-actions',
      '#attributes' => ['class' => ['hidden']],
      'action_type' => [
        '#type' => 'select',
        '#options' => [],
      ],
      'action_config' => [
        '#type' => 'container',
        '#id' => 'action-config',
      ],
    ];

    // Set up a dummy node. Use the story type if available, otherwise use the
    // first defined type.
    $node_type = NodeType::load($type_name = 'story');
    if (!$node_type) {
      $list = array_keys(NodeType::loadMultiple());
      $type_name = $list[0];
    }
    $node = Node::create([
      'type' => $type_name,
      'uid' => 1,
      'name' => '',
      'language' => '',
    ]);
    $form_id = $type_name . '_node_form';
    $info = $form_state->getBuildInfo();
    array_unshift($info['args'], $node);

    $temp_form_state_add = [
      'is_mm_search' => TRUE,
      'build_info' => $info,
    ];
    /** @var \Drupal\Core\Entity\EntityFormInterface $form */
    $form = $this->entityTypeManager->getFormObject($node->getEntityTypeId(), 'default');
    $form->setEntity($node);
    $temp_form_state = (new FormState())->setFormState($temp_form_state_add);
    $form = $this->formBuilder->buildForm($form, $temp_form_state);
    $this->formBuilder->prepareForm($form_id, $form, $temp_form_state);
    $form['mm_appearance']['changed'] = $form['mm_appearance']['author']['date'] ?? '';
    $this->disableWysiwyg($form['body_field']['body']);
    AlterSearchReplace::alterNode($form);
    // debug_add_dump($form);
    array_walk($form, $this->walkNode(...), $data);

    $mmlist_name = $item->mmtid . '{' . $tree->name . '}';
    if (mm_content_is_group($item->mmtid)) {
      $node_page = mm_home_mmtid() . '{' . mm_content_get_name(mm_home_mmtid()) . '}';
      $group_cat = $mmlist_name;
    }
    else {
      $node_page = $mmlist_name;
      $group_cat = mm_content_groups_mmtid() . '{' . mm_content_expand_name(Constants::MM_ENTRY_NAME_GROUPS) . '}';
    }

    $reset = $startup = [
      'search-type' => 0,
      'search-page-cat' => $node_page,
      'search-group-cat' => $group_cat,
      'search-node-type' => '',
      'mmsr-cont-row' => [['search-node-wheres' => '']],
    ];
    if (isset($_SESSION['mmsr-data'])) {
      $row = -1;
      $startup = [];
      foreach (explode('&', $_SESSION['mmsr-data']) as $arg) {
        [$key, $val] = explode('=', $arg, 2);
        $val = urldecode($val);
        if ($key == 'search-node-wheres') {
          $row_type = 'mmsr-cont-row';
        }
        elseif ($key == 'search-page-wheres') {
          $row_type = 'mmsr-page-row';
        }
        elseif ($key == 'search-group-wheres') {
          $row_type = 'mmsr-group-row';
        }

        if ($key == 'search-logic') {
          if ($row < 0) {
            continue;
          }
          else {
            $row++;
          }
        }
        elseif ($row < 0 && ($key == 'search-node-wheres' || $key == 'search-page-wheres' || $key == 'search-group-wheres')) {
          $row++;
        }

        if ($row >= 0 && $key != 'search-page-cat' && $key != 'search-group-cat' && $key != 'search-node-cat') {
          $startup[$row_type][$row][$key] = $val;
        }
        else {
          $startup[$key] = $val;
        }
      }
      $startup['search-page-cat'] = $reset['search-page-cat'];
      $startup['search-group-cat'] = $reset['search-group-cat'];
      $startup = array_merge($reset, $startup);
    }

    mm_add_js_setting($data->form, 'MMSR', [
      'get_path' => base_path() . 'mmsr-get',
      'startup' => $startup,
      'reset' => $reset,
      'fixups' => [],
    ]);

    $data->query['queries'] += AlterSearchReplace::$nodeQueries;
    $_SESSION['mmsr-query'] = $data->query;
    if (Constants::MMSR_debug) {
      mm_add_page_footer(['#markup' => FilteredMarkup::create('<span style="white-space: pre;">' . htmlspecialchars(print_r($data, TRUE)) . '</span>')]);
    }
    mm_add_library($data->form, 'mm_search_replace');
    return $data->form;
  }

  public function submitForm(array &$form, FormStateInterface $form_state) {
  }

  public function getSearchResultCount() {
    $data = $this->currentRequest->get('data', '');
    $debug = $count_query = '';
    if (Constants::MMSR_debug) {
      $debug = '<p>' . htmlspecialchars($data) . '</p>';
    }

    $count = 0;
    $have_error = FALSE;
    $cid = 'mmsr:' . md5($data) . ':' . $this->currentUser()->id();
    if (!$this->currentRequest->get('recalc', FALSE) && ($_SESSION['mmsr-data'] ?? '') == $data && ($cache = $this->cache->get($cid))) {
      [$result, $count, $count_query, $result_query, $header, $search_type] = $cache->data;
    }
    else if (!isset($_SESSION['mmsr-query'])) {
      $result = $this->t('Some data is missing from your request. Please refresh this page and try again.');
      $have_error = TRUE;
    }
    else {
      try {
        $count_query = $this->getResultQuery($_SESSION['mmsr-data'] = $data, $_SESSION['mmsr-query']['queries'], TRUE, $result_query, $header, $search_type);
        $result = $this->database->query($count_query)->fetchField();
        if (isset($result)) {
          $count = $result;
          $result = $this->getStringTranslation()->formatPlural($result, '@count match', '@count matches');
        }
      }
      catch (\Exception $e) {
        $result = $this->t('An error occurred. See the <em>Query</em> section for details.');
        $debug .= '<p>' . $e->getMessage() . '</p>';
        $have_error = TRUE;
      }
    }

    if (!$have_error) {
      $this->cache->set($cid, [$result, $count, $count_query, $result_query, $header, $search_type], time() + 60);
    }

    $debug .= '<p>' . htmlspecialchars($this->database->prefixTables($count_query)) . '</p>';

    $response = [
      'result' => $result,
      'query' => $debug,
      'actions' => [],
      'action_selected' => '',
      'form' => '',
    ];
    if ($count) {
      /** @var \Drupal\monster_menus\MMSearchAction\MMSearchActionBase[] $plugins */
      $plugins = $definitions = [];
      $config = [
        'result_count' => $count,
        'start_mmtid' => $_SESSION['mmsr-mmtid'] ?? NULL,
        'search_type' => $search_type,
        'result_query' => $result_query,
        'header' => $header,
      ];
      $actions = [];
      foreach ($this->pluginManager->getAvailableActions($config) as $plugin_id => $data) {
        $actions[$plugin_id] = $data['label'];
        $plugins[$plugin_id] = $data['plugin'];
        $definitions[$plugin_id] = $data['definition'];
      }
      $test_selected = $this->currentRequest->get('action_type', $_SESSION['mmsr-action']['id'] ?? '');
      if (isset($actions[$test_selected])) {
        $selected = $test_selected;
      }
      elseif (isset($actions['mm_search_action_display_table'])) {
        $selected = 'mm_search_action_display_table';
      }
      if ($_SESSION['mmsr-action']['id'] = $selected) {
        if ($plugins[$selected]->access()) {
          $form = $this->formBuilder->getForm($plugins[$selected]);
          $response['form'] = $this->renderer->renderRoot($form);
          $def = $definitions[$selected];
          if (!empty($def['useDrupalSettings']) || !empty($def['useJS'])) {
            $attachments = BubbleableMetadata::createFromRenderArray($form);
            $assets = (new AttachedAssets())
              ->setLibraries($attachments->getAttachments()['library'] ?? [])
              ->setSettings($attachments->getAttachments()['drupalSettings'] ?? []);
            $response['drupalSettings'] = !empty($def['useDrupalSettings']) ? $assets->getSettings() : [];
            $response['js'] = !empty($def['useJS']) ? $assets->getLibraries() : [];
          }
          if (!empty($def['jsInit'])) {
            $response['jsInit'] = $def['jsInit'];
          }
        }
      }
      $response['action_selected'] = $selected;
      $response['actions'] = $actions;
    }
    return mm_json_response($response);
  }

}

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

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