monster_menus-9.0.x-dev/src/Form/ReorderNodesForm.php
src/Form/ReorderNodesForm.php
<?php
/**
* @file
* Contains \Drupal\monster_menus\Form\ReorderNodesForm.
*/
namespace Drupal\monster_menus\Form;
use Drupal;
use Drupal\Component\Utility\Html;
use Drupal\Core\Access\AccessResult;
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\Entity\MMTree;
use Drupal\node\Entity\NodeType;
use Symfony\Component\DependencyInjection\ContainerInterface;
class ReorderNodesForm extends FormBase {
private $regions;
/**
* Database Service Object.
*
* @var Connection
*/
protected $database;
/**
* Constructs an object.
*/
public function __construct(Connection $database) {
$this->database = $database;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static($container->get('database'));
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'mm_ui_node_reorder';
}
private function sort($a = NULL, $b = NULL) {
if (empty($a)) {
// Initialize.
$this->regions = array_keys(system_region_list(Drupal::theme()
->getActiveTheme()
->getName(), REGIONS_VISIBLE));
return;
}
if ($a->stuck != $b->stuck) {
return $b->stuck - $a->stuck;
}
if ($a->region != $b->region) {
return array_search($b->region, $this->regions) - array_search($a->region, $this->regions);
}
if ($a->weight != $b->weight) {
return $a->weight - $b->weight;
}
return $b->created - $a->created;
}
public function buildForm(array $form, FormStateInterface $form_state, MMTree $mm_tree = NULL) {
mm_static($form, 'mm_ui_node_reorder');
$mmtid = $mm_tree->id();
$where = '';
$hidden = mm_get_node_info(Constants::MM_NODE_INFO_NO_REORDER);
if ($hidden) {
$where = " AND n.type NOT IN('" . join("', '", $hidden) . "')";
}
$node_sql = mm_content_get_accessible_nodes_by_mmtid_query($mmtid, $count_sql, ', MAX(n.type) AS type, MAX(nfr.title) AS title, MAX(n.region) AS region, MAX(n.weight) AS weight', ' LEFT JOIN {node_field_revision} nfr ON nfr.vid = n.vid', $where);
$count = $this->database->query($count_sql)->fetchField();
$form['#title'] = $this->t('Reorder contents');
$form['#tree'] = TRUE;
if ($count) {
$form['mmtid'] = [
'#type' => 'value',
'#value' => $mmtid,
];
$form['actions'] = ['#type' => 'actions'];
if ($count > Constants::MM_UI_MAX_REORDER_ITEMS) {
$form['empty_menu'] = ['#markup' => $this->t('<p>There are too many pieces of content on this @thing to make reordering feasible.</p>', mm_ui_strings(FALSE))];
}
else {
$form['prefix'] = [
'#markup' => $this->t('<div id="help">To reorder a piece of content, grab the @sample and drag it to a new location. Be sure to <em>Save configuration</em> when done.</div>', ['@sample' => mm_ui_tabledrag_sample()]),
'#weight' => -1,
];
$form['#suffix'] = $this->t('<p></p><div id="help"><p>If a custom content order is being used, any new contents you add will appear at the top of the list. You may want to return here to change their location, afterward.</p><p>Use the <em>Remove custom ordering and save</em> button to revert to the default, reverse-chronological order. Any changes in region are unaffected by this button.</p></div>');
$theme = Drupal::theme()->getActiveTheme()->getName();
$form['edited_theme'] = [
'#type' => 'value',
'#value' => $theme,
];
$all_regions = system_region_list($theme, REGIONS_VISIBLE);
$form['regions'] = [
'#type' => 'value',
'#value' => $all_regions,
];
$q = $this->database->query($node_sql);
foreach ($q as $row) {
if (empty($row->region) || !isset($all_regions[$row->region])) {
$row->region = Constants::MM_UI_REGION_CONTENT;
}
$rows[] = $row;
}
$this->sort(); // Initialize.
uasort($rows, $this->sort(...));
$new_weight = 1;
foreach ($rows as $row) {
$id = 'nid:' . $row->nid;
$form['nodes'][$id] = [];
$form['nodes'][$id]['#attributes'] = ['class' => ['menu-enabled']];
$title = trim($row->title ?? '');
if (empty($title)) {
$title = $this->t('Title not provided');
}
$form['nodes'][$id]['title']['#markup'] = Link::fromTextAndUrl($title, Url::fromRoute('entity.node.canonical', ['node' => $row->nid]))
->toString();
$form['nodes'][$id]['type']['#markup'] = NodeType::load($row->type)->label();
$form['nodes'][$id]['weight'] = [
'#type' => 'weight',
'#delta' => $count,
'#default_value' => $new_weight++,
];
$allowed_regions = array_intersect_key($all_regions, array_flip(mm_content_get_allowed_regions_for_user(NULL, $row->type)));
// Make sure current region is allowed, even if it's normally not.
$allowed_regions[$row->region] = $all_regions[$row->region];
$form['nodes'][$id]['region'] = [
'#type' => 'select',
'#default_value' => $row->region,
'#title_display' => 'invisible',
'#title' => $this->t('Region for @title', ['@title' => $title]),
'#options' => $allowed_regions,
];
$form['nodes'][$id]['nid'] = [
'#type' => 'hidden',
'#value' => $row->nid,
'#wrapper_attributes' => ['class' => ['hidden']],
];
if ($row->stuck) {
$form['nodes'][$id]['stuck'] = [
'#type' => 'value',
'#value' => TRUE,
];
}
}
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Save configuration'),
];
$form['actions']['spacer'] = ['#markup' => ' '];
}
$form['actions']['reorder_normal'] = [
'#type' => 'submit',
'#value' => $this->t('Remove custom ordering and save'),
];
}
else {
$form['empty_menu'] = ['#markup' => $this->t('There is no content on this @thing to reorder.', mm_ui_strings(FALSE))];
}
$this->generateTable($form);
return $form;
}
public function submitForm(array &$form, FormStateInterface $form_state) {
$mmtid = $form_state->getValue('mmtid');
// Remove everything.
$this->database->delete('mm_node_reorder')
->condition('mmtid', $mmtid)
->execute();
if ($form_state->getTriggeringElement()['#id'] == 'edit-actions-reorder-normal') {
foreach ($form_state->getValue('nodes') as $section) {
foreach ($section as $entry) {
// make sure these are integers
if (is_array($entry) && isset($entry['nid']) && intval($entry['nid']) && $entry['region'] != Constants::MM_UI_REGION_CONTENT) {
$this->database->insert('mm_node_reorder')
->fields([
'mmtid' => $mmtid,
'nid' => intval($entry['nid']),
'weight' => 0,
'region' => $entry['region'],
])
->execute();
}
}
}
$this->messenger()->addStatus($this->t('Custom ordering has been removed.'));
}
else {
// Drupal's tableorder code produces weights where 0 is the bottom of the
// list, and they get smaller (negative) as they go up. If we were to save
// the order this way, it would mean that newly-added nodes would get put
// at the bottom of the list (0), even though they appear at the top when
// actually rendered. So, find the topmost item and make that 1.
$min_weight = 0;
foreach ($form_state->getValue('nodes') as $section) {
foreach ($section as $entry) {
// make sure these are integers
if (is_array($entry) && isset($entry['nid']) && intval($entry['nid'])) {
$min_weight = min($min_weight, $entry['weight']);
}
}
}
foreach ($form_state->getValue('nodes') as $section) {
foreach ($section as $entry) {
// make sure these are integers
if (is_array($entry) && isset($entry['nid']) && intval($entry['nid'])) {
$this->database->insert('mm_node_reorder')
->fields([
'mmtid' => $mmtid,
'nid' => intval($entry['nid']),
'weight' => $min_weight < 0 ? $entry['weight'] - $min_weight + 1 : $entry['weight'],
'region' => $entry['region'] == Constants::MM_UI_REGION_CONTENT ? NULL : $entry['region'],
])
->execute();
}
}
}
$this->messenger()->addStatus($this->t('The contents have been reordered.'));
}
// Make sure the page draws with the new order
mm_content_clear_caches();
mm_content_invalidate_mm_tree_cache($mmtid);
}
static public function access(MMTree $mm_tree) {
$mmtid = $mm_tree->id();
$perms = mm_content_user_can($mmtid);
return AccessResult::allowedIf($mmtid > 0 && !$perms[Constants::MM_PERMS_IS_RECYCLED] && $perms[Constants::MM_PERMS_WRITE] && $mmtid != mm_content_users_mmtid() && !mm_content_is_group($mmtid) && !mm_content_is_archive($mmtid) && !mm_content_is_archive($mmtid, TRUE));
}
/**
* Generate the node reorder table.
*
* @param &$form
* The form element
*/
private function generateTable(&$form) {
$rendered_rows = $region_targetable = [[], []];
if (isset($form['nodes'])) {
foreach (Element::children($form['nodes']) as $id) {
$element = &$form['nodes'][$id];
// Add special classes to be used for tabledrag.js.
$element['nid']['#attributes']['class'][] = 'menu-nid';
$region = $element['region']['#default_value'];
$element['region']['#attributes']['class'] = array('nodes-region-select', 'nodes-region-' . $region);
$element['weight']['#attributes']['class'] = array('nodes-weight', 'nodes-weight-' . $region);
$element['weight']['#attributes']['class'][] = isset($element['stuck']) ? 'sticky-weight' : 'unsticky-weight';
$row = array_intersect_key($element, ['title' => 1, 'type' => 1, 'region' => 1, 'weight' => 1, 'nid' => 1, '#attributes' => 1]);
$row['#attributes']['class'][] = 'draggable';
$sticky = isset($element['stuck']) ? 1 : 0;
$rendered_rows[$sticky][$region][$id] = $row;
foreach (array_keys($element['region']['#options']) as $region) {
$region_targetable[$sticky][$region] = TRUE;
}
}
}
$regions = $form['regions']['#value'] ?? [];
$output = [];
if ($rendered_rows[1]) {
$caption = $rendered_rows[0] ? $this->t('<h2>Sticky content</h2>') : '';
$output[] = $this->makeTable('mm-reorder1', $caption, $rendered_rows[1], $region_targetable[1], $regions);
}
if ($rendered_rows[0]) {
$caption = $rendered_rows[1] ? $this->t('<h2>Non-sticky content</h2>') : '';
$output[] = $this->makeTable('mm-reorder2', $caption, $rendered_rows[0], $region_targetable[0], $regions);
}
$form['nodes'] = $output;
}
private function header() {
return [
$this->t('Title'),
$this->t('Type'),
$this->t('Weight'),
$this->t('Region'),
];
}
private function makeTable($table_id, $caption, $rows, $region_targetable, $regions) {
$table = [
'#header' => $this->header(),
'#caption' => $caption,
'#type' => 'table',
'#attributes' => ['id' => $table_id],
'#tabledrag' => [],
];
foreach ($regions as $region => $desc) {
if (!empty($rows[$region]) || !empty($region_targetable[$region])) {
$table['#tabledrag'][] = [
'action' => 'match',
'relationship' => 'sibling',
'group' => 'nodes-region-select',
'subgroup' => 'nodes-region-' . $region,
];
$table['#tabledrag'][] = [
'action' => 'order',
'relationship' => 'sibling',
'group' => 'nodes-weight',
'subgroup' => 'nodes-weight-' . $region,
];
$table[] = [
'#attributes' => ['class' => ['region-title', "region-title-$region"], 'no_striping' => TRUE],
['#wrapper_attributes' => ['colspan' => 5], '#markup' => Html::escape($desc)],
];
$table[] = [
'#attributes' => ['class' => ['region-message', "region-$region-message", !empty($rows[$region]) ? 'region-populated' : 'region-empty'], 'no_striping' => TRUE],
['#wrapper_attributes' => ['colspan' => 5], '#markup' => $this->t('<em>There is no content in this region.</em>')],
];
if (!empty($rows[$region])) {
$table = array_merge($table, $rows[$region]);
}
}
}
return $table;
}
}
