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);
}
}
