Commit 4f978d98 authored by catch's avatar catch

Issue #2324121 by Berdir, slashrsm, alexpott: Fixed NodeType's settings array...

Issue #2324121 by Berdir, slashrsm, alexpott: Fixed NodeType's settings array was meant to be able to store information from mutliple modules.
parent c1dd1081
......@@ -2,11 +2,8 @@ type: book
name: 'Book page'
description: '<em>Books</em> have a built-in hierarchical navigation. Use for handbooks or tutorials.'
help: ''
settings:
node:
preview: 1
options:
revision: false
submitted: true
new_revision: false
display_submitted: true
preview_mode: 1
status: true
langcode: en
......@@ -2,11 +2,8 @@ type: forum
name: 'Forum topic'
description: 'A <em>forum topic</em> starts a new discussion thread within a forum.'
help: ''
settings:
node:
preview: 1
options:
revision: false
submitted: true
new_revision: false
display_submitted: true
preview_mode: 1
status: true
langcode: en
......@@ -14,7 +14,7 @@ menu_ui.settings:
type: boolean
label: 'Override parent selector'
menu.entity.node.*:
node_type.third_party.menu_ui:
type: mapping
label: 'Per-content type menu settings'
mapping:
......
......@@ -5,8 +5,6 @@
* Install, update and uninstall functions for the menu_ui module.
*/
use Drupal\Component\Uuid\Uuid;
/**
* Implements hook_install().
*/
......@@ -17,15 +15,6 @@ function menu_ui_install() {
// \Drupal\Core\Extension\ModuleHandler::install().
// @see https://drupal.org/node/2181151
\Drupal::service('router.builder')->rebuild();
if (\Drupal::moduleHandler()->moduleExists('node')) {
$node_types = array_keys(node_type_get_names());
foreach ($node_types as $type_id) {
\Drupal::config('menu.entity.node.' . $type_id)
->set('available_menus', array('main'))
->set('parent', 'main:0')
->save();
}
}
}
/**
......
......@@ -165,29 +165,6 @@ function menu_ui_node_update(EntityInterface $node) {
menu_ui_node_save($node);
}
/**
* Implements hook_ENTITY_TYPE_insert() for node_type entities.
*/
function menu_ui_node_type_insert(NodeTypeInterface $type) {
if ($type->isSyncing()) {
return;
}
\Drupal::config('menu.entity.node.' . $type->id())
->set('available_menus', array('main'))
->set('parent', 'main:')
->save();
}
/**
* Implements hook_ENTITY_TYPE_delete() for node_type entities.
*/
function menu_ui_node_type_delete(NodeTypeInterface $type) {
if ($type->isSyncing()) {
return;
}
\Drupal::config('menu.entity.node.' . $type->id())->delete();
}
/**
* Helper for hook_ENTITY_TYPE_insert() and hook_ENTITY_TYPE_update() for nodes.
*/
......@@ -251,13 +228,14 @@ function menu_ui_node_predelete(EntityInterface $node) {
function menu_ui_node_prepare_form(NodeInterface $node, $operation, FormStateInterface $form_state) {
if (empty($form_state['menu_link_definition'])) {
// Prepare the node for the edit form so that $node->menu always exists.
$node_type_config = \Drupal::config('menu.entity.node.' . $node->getType());
$menu_name = strtok($node_type_config->get('parent'), ':');
/** @var \Drupal\node\NodeTypeInterface $node_type */
$node_type = $node->type->entity;
$menu_name = strtok($node_type->getThirdPartySetting('menu_ui', 'parent', 'main:'), ':');
$definition = FALSE;
if ($node->id()) {
$id = FALSE;
// Give priority to the default menu
$type_menus = $node_type_config->get('available_menus');
$type_menus = $node_type->getThirdPartySetting('menu_ui', 'available_menus', array('main'));
if (in_array($menu_name, $type_menus)) {
$query = \Drupal::entityQuery('menu_link_content')
->condition('route_name', 'entity.node.canonical')
......@@ -323,11 +301,12 @@ function menu_ui_form_node_form_alter(&$form, FormStateInterface $form_state) {
// @todo This must be handled in a #process handler.
$node = $form_state->getFormObject()->getEntity();
$definition = $form_state['menu_link_definition'];
$type = $node->getType();
/** @var \Drupal\node\NodeTypeInterface $node_type */
$node_type = $node->type->entity;
/** @var \Drupal\Core\Menu\MenuParentFormSelectorInterface $menu_parent_selector */
$menu_parent_selector = \Drupal::service('menu.parent_form_selector');
$menu_names = menu_ui_get_menus();
$type_menus = \Drupal::config("menu.entity.node.$type")->get('available_menus');
$type_menus = $node_type->getThirdPartySetting('menu_ui', 'available_menus', array('main'));
$available_menus = array();
foreach ($type_menus as $menu) {
$available_menus[$menu] = $menu_names[$menu];
......@@ -336,7 +315,7 @@ function menu_ui_form_node_form_alter(&$form, FormStateInterface $form_state) {
$default = $definition['menu_name'] . ':' . $definition['parent'];
}
else {
$default = \Drupal::config('menu.entity.node.'.$type)->get('parent');
$default = $node_type->getThirdPartySetting('menu_ui', 'parent', 'main:');
}
$parent_element = $menu_parent_selector->parentSelectElement($default, $definition['id'], $available_menus);
// If no possible parent menu items were found, there is nothing to display.
......@@ -446,16 +425,8 @@ function menu_ui_form_node_type_form_alter(&$form, FormStateInterface $form_stat
/** @var \Drupal\Core\Menu\MenuParentFormSelectorInterface $menu_parent_selector */
$menu_parent_selector = \Drupal::service('menu.parent_form_selector');
$menu_options = menu_ui_get_menus();
/** @var \Drupal\node\NodeTypeInterface $type */
$type = $form_state->getFormObject()->getEntity();
if ($type->id()) {
$config_values = \Drupal::config('menu.entity.node.' . $type->id())->get();
}
else {
$config_values = array(
'available_menus' => array('main'),
'parent' => 'main:',
);
}
$form['menu'] = array(
'#type' => 'details',
'#title' => t('Menu settings'),
......@@ -467,7 +438,7 @@ function menu_ui_form_node_type_form_alter(&$form, FormStateInterface $form_stat
$form['menu']['menu_options'] = array(
'#type' => 'checkboxes',
'#title' => t('Available menus'),
'#default_value' => $config_values['available_menus'],
'#default_value' => $type->getThirdPartySetting('menu_ui', 'available_menus', array('main')),
'#options' => $menu_options,
'#description' => t('The menus available to place links in for this content type.'),
);
......@@ -480,14 +451,14 @@ function menu_ui_form_node_type_form_alter(&$form, FormStateInterface $form_stat
$form['menu']['menu_parent'] = array(
'#type' => 'select',
'#title' => t('Default parent item'),
'#default_value' => $config_values['parent'],
'#default_value' => $type->getThirdPartySetting('menu_ui', 'parent', 'main:'),
'#options' => $options,
'#description' => t('Choose the menu item to be the default parent for a new link in the content authoring form.'),
'#attributes' => array('class' => array('menu-title-select')),
);
$form['actions']['submit']['#validate'][] = 'menu_ui_form_node_type_form_validate';
$form['actions']['submit']['#submit'][] = 'menu_ui_form_node_type_form_submit';
$form['#entity_builders'][] = 'menu_ui_form_node_type_form_builder';
}
/**
......@@ -511,16 +482,13 @@ function menu_ui_form_node_type_form_validate(&$form, FormStateInterface $form_s
}
/**
* Submit handler for forms with menu options.
* Entity builder for the node type form with menu options.
*
* @see menu_ui_form_node_type_form_alter().
*/
function menu_ui_form_node_type_form_submit(&$form, FormStateInterface $form_state) {
$type = $form_state->getFormObject()->getEntity();
\Drupal::config('menu.entity.node.' . $type->id())
->set('available_menus', array_values(array_filter($form_state->getValue('menu_options'))))
->set('parent', $form_state->getValue('menu_parent'))
->save();
function menu_ui_form_node_type_form_builder($entity_type, NodeTypeInterface $type, &$form, FormStateInterface $form_state) {
$type->setThirdPartySetting('menu_ui', 'available_menus', array_values(array_filter($form_state->getValue('menu_options'))));
$type->setThirdPartySetting('menu_ui', 'parent', $form_state->getValue('menu_parent'));
}
/**
......
......@@ -14,8 +14,9 @@ process:
description: description
help: help
title_label: title_label
'settings/node/preview': 'constants/preview'
'settings/node/submitted': submitted
'preview_mode': 'constants/preview'
'display_submitted': display_submitted
'new_revision': 'options/revision'
'settings/node/options': options
create_body: has_body
create_body_label: body_label
......
......@@ -110,7 +110,7 @@ public function prepareRow(Row $row) {
}
$row->setSourceProperty('options', $options);
$submitted = isset($this->themeSettings['toggle_node_info_' . $type]) ? $this->themeSettings['toggle_node_info_' . $type] : FALSE;
$row->setSourceProperty('submitted', $submitted);
$row->setSourceProperty('display_submitted', $submitted);
return parent::prepareRow($row);
}
......
......@@ -52,16 +52,9 @@ public function testNodeType() {
// Test the test_page content type.
$node_type_page = entity_load('node_type', 'test_page');
$this->assertEqual($node_type_page->id(), 'test_page', 'Node type test_page loaded');
$expected = array(
'options' => array(
'revision' => FALSE,
),
'preview' => 1,
'submitted' => TRUE,
);
// @todo: Fix due to https://www.drupal.org/node/2283977
// $this->assertEqual($node_type_page->settings['node'], $expected, 'Node type test_page settings correct.');
$this->assertEqual($node_type_page->displaySubmitted(), TRUE);
$this->assertEqual($node_type_page->isNewRevision(), FALSE);
$this->assertEqual($node_type_page->getPreviewMode(), DRUPAL_OPTIONAL);
$this->assertEqual(array('test_page'), $migration->getIdMap()->lookupDestinationID(array('test_page')));
// Test we have a body field.
......@@ -71,15 +64,9 @@ public function testNodeType() {
// Test the test_story content type.
$node_type_story = entity_load('node_type', 'test_story');
$this->assertEqual($node_type_story->id(), 'test_story', 'Node type test_story loaded');
$expected = array(
'options' => array(
'revision' => FALSE,
),
'preview' => 1,
'submitted' => TRUE,
);
// @todo: Fix due to https://www.drupal.org/node/2283977
// $this->assertEqual($node_type_story->settings['node'], $expected, 'Node type test_story settings correct.');
$this->assertEqual($node_type_story->displaySubmitted(), TRUE);
$this->assertEqual($node_type_story->isNewRevision(), FALSE);
$this->assertEqual($node_type_story->getPreviewMode(), DRUPAL_OPTIONAL);
$this->assertEqual(array('test_story'), $migration->getIdMap()->lookupDestinationID(array('test_story')));
// Test we don't have a body field.
......@@ -89,16 +76,9 @@ public function testNodeType() {
// Test the test_event content type.
$node_type_event = entity_load('node_type', 'test_event');
$this->assertEqual($node_type_event->id(), 'test_event', 'Node type test_event loaded');
$expected = array(
'options' => array(
'revision' => TRUE,
),
'preview' => 1,
'submitted' => TRUE,
);
// @todo: Fix due to https://www.drupal.org/node/2283977
// $this->assertEqual($node_type_event->settings['node'], $expected, 'Node type test_event settings correct.');
$this->assertEqual($node_type_event->displaySubmitted(), TRUE);
$this->assertEqual($node_type_event->isNewRevision(), TRUE);
$this->assertEqual($node_type_event->getPreviewMode(), DRUPAL_OPTIONAL);
$this->assertEqual(array('test_event'), $migration->getIdMap()->lookupDestinationID(array('test_event')));
// Test we have a body field.
......
......@@ -27,39 +27,20 @@ node.type.*:
help:
type: text
label: 'Explanation or submission guidelines'
settings:
type: mapping
label: 'Settings'
mapping:
node:
type: node.settings.node
node.settings.node:
type: mapping
label: 'Content type settings'
mapping:
preview:
new_revision:
type: boolean
label: 'Whether a new revision should be created by default'
preview_mode:
type: integer
label: 'Preview before submitting'
options:
type: mapping
label: 'Publishing options'
mapping:
status:
type: boolean
label: 'Published'
promote:
type: boolean
label: 'Promoted to front page'
sticky:
type: boolean
label: 'Sticky at top of lists'
revision:
type: boolean
label: 'Create new revision'
submitted:
display_submitted:
type: boolean
label: 'Display setting for author and date Submitted by post information'
third_party_settings:
type: sequence
label: 'Third party settings'
sequence:
- type: node_type.third_party.[%key]
# Plugin \Drupal\node\Plugin\Search\NodeSearch
search.plugin.node_search:
......
......@@ -635,12 +635,10 @@ function template_preprocess_node(&$variables) {
}
// Display post information only on certain node types.
// Avoid loading the entire node type config entity here that may not exist.
$node_type_config = \Drupal::config('node.type.' . $node->bundle());
$node_type = $node->type->entity;
// Used by RDF to add attributes around the author and date submitted.
$variables['author_attributes'] = new Attribute();
// Display submitted by default.
$variables['display_submitted'] = $node_type_config->isNew() || $node_type_config->get('settings.node.submitted');
$variables['display_submitted'] = $node_type->displaySubmitted();
if ($variables['display_submitted']) {
if (theme_get_setting('features.node_user_picture')) {
// To change user picture settings (e.g. image style), edit the 'compact'
......
......@@ -7,8 +7,8 @@
namespace Drupal\node\Entity;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Config\Entity\ConfigEntityBundleBase;
use Drupal\Core\Config\Entity\ThirdPartySettingsTrait;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\node\NodeTypeInterface;
......@@ -41,6 +41,7 @@
* )
*/
class NodeType extends ConfigEntityBundleBase implements NodeTypeInterface {
use ThirdPartySettingsTrait;
/**
* The machine name of this node type.
......@@ -74,6 +75,27 @@ class NodeType extends ConfigEntityBundleBase implements NodeTypeInterface {
*/
public $help;
/**
* Default value of the 'Create new revision' checkbox of this node type.
*
* @var bool
*/
protected $new_revision = FALSE;
/**
* The preview mode.
*
* @var int
*/
protected $preview_mode = DRUPAL_OPTIONAL;
/**
* Display setting for author and date Submitted by post information.
*
* @var bool
*/
protected $display_submitted = TRUE;
/**
* Indicates whether a Body field should be created for this node type.
*
......@@ -97,37 +119,60 @@ class NodeType extends ConfigEntityBundleBase implements NodeTypeInterface {
protected $create_body_label = 'Body';
/**
* Module-specific settings for this node type, keyed by module name.
*
* @var array
*
* @todo Pluginify.
* {@inheritdoc}
*/
public $settings = array();
public function id() {
return $this->type;
}
/**
* {@inheritdoc}
*/
public function id() {
return $this->type;
public function isLocked() {
$locked = \Drupal::state()->get('node.type.locked');
return isset($locked[$this->id()]) ? $locked[$this->id()] : FALSE;
}
/**
* {@inheritdoc}
*/
public function getModuleSettings($module) {
if (isset($this->settings[$module]) && is_array($this->settings[$module])) {
return $this->settings[$module];
}
return array();
public function isNewRevision() {
return $this->new_revision;
}
/**
* {@inheritdoc}
*/
public function isLocked() {
$locked = \Drupal::state()->get('node.type.locked');
return isset($locked[$this->id()]) ? $locked[$this->id()] : FALSE;
public function setNewRevision($new_revision) {
$this->new_revision = $new_revision;
}
/**
* {@inheritdoc}
*/
public function displaySubmitted() {
return $this->display_submitted;
}
/**
* {@inheritdoc}
*/
public function setDisplaySubmitted($display_submtited) {
$this->display_submitted = $display_submtited;
}
/**
* {@inheritdoc}
*/
public function getPreviewMode() {
return $this->preview_mode;
}
/**
* {@inheritdoc}
*/
public function setPreviewMode($preview_mode) {
$this->preview_mode = $preview_mode;
}
/**
......@@ -173,23 +218,4 @@ public static function postDelete(EntityStorageInterface $storage, array $entiti
$storage->resetCache(array_keys($entities));
}
/**
* {@inheritdoc}
*/
public static function preCreate(EntityStorageInterface $storage, array &$values) {
parent::preCreate($storage, $values);
// Ensure default values are set.
if (!isset($values['settings']['node'])) {
$values['settings']['node'] = array();
}
$values['settings']['node'] = NestedArray::mergeDeep(array(
'options' => array(
'revision' => FALSE,
),
'preview' => DRUPAL_OPTIONAL,
'submitted' => TRUE,
), $values['settings']['node']);
}
}
......@@ -148,8 +148,7 @@ protected function checkFieldAccess($operation, FieldDefinitionInterface $field_
if ($account->hasPermission('administer nodes')) {
return TRUE;
}
$node_type_settings = $items->getEntity()->type->entity->getModuleSettings('node');
return !empty($node_type_settings['options']['revision']);
return $items->getEntity()->type->entity->isNewRevision();
}
return parent::checkFieldAccess($operation, $field_definition, $account, $items);
}
......
......@@ -21,13 +21,6 @@
*/
class NodeForm extends ContentEntityForm {
/**
* Default settings for this content/node type.
*
* @var array
*/
protected $settings;
/**
* The tempstore factory.
*
......@@ -64,9 +57,6 @@ public static function create(ContainerInterface $container) {
protected function prepareEntity() {
/** @var \Drupal\node\NodeInterface $node */
$node = $this->entity;
// Make node type settings easily accessible.
$type = $node->type->entity;
$this->settings = $type->getModuleSettings('node');
if (!$node->isNew()) {
// Remove the revision log message from the original node entity.
......@@ -154,7 +144,7 @@ public function form(array $form, FormStateInterface $form_state) {
$form['revision'] = array(
'#type' => 'checkbox',
'#title' => t('Create new revision'),
'#default_value' => !empty($this->settings['options']['revision']),
'#default_value' => $node->type->entity->isNewRevision(),
'#access' => $current_user->hasPermission('administer nodes'),
'#group' => 'revision_information',
);
......@@ -223,7 +213,7 @@ public function form(array $form, FormStateInterface $form_state) {
protected function actions(array $form, FormStateInterface $form_state) {
$element = parent::actions($form, $form_state);
$node = $this->entity;
$preview_mode = $this->settings['preview'];
$preview_mode = $node->type->entity->getPreviewMode();
$element['submit']['#access'] = $preview_mode != DRUPAL_REQUIRED || (!$form_state->getErrors() && isset($form_state['node_preview']));
......
......@@ -68,7 +68,6 @@ public function form(array $form, FormStateInterface $form_state) {
$node = $this->entityManager->getStorage('node')->create(array('type' => $type->id()));
}
$node_settings = $type->getModuleSettings('node');
$form['name'] = array(
'#title' => t('Name'),
'#type' => 'textfield',
......@@ -118,11 +117,10 @@ public function form(array $form, FormStateInterface $form_state) {
'#default_value' => $fields['title']->getLabel(),
'#required' => TRUE,
);
$form['submission']['preview'] = array(
$form['submission']['preview_mode'] = array(
'#type' => 'radios',
'#title' => t('Preview before submitting'),
'#parents' => array('settings', 'node', 'preview'),
'#default_value' => $node_settings['preview'],
'#default_value' => $type->getPreviewMode(),
'#options' => array(
DRUPAL_DISABLED => t('Disabled'),
DRUPAL_OPTIONAL => t('Optional'),
......@@ -144,7 +142,7 @@ public function form(array $form, FormStateInterface $form_state) {
'status' => $node->status->value,
'promote' => $node->promote->value,
'sticky' => $node->sticky->value,
'revision' => $type->settings['node']['options']['revision'],
'revision' => $type->isNewRevision(),
);
// Prepare workflow options to be used for 'checkboxes' form element.
$keys = array_keys(array_filter($workflow_options));
......@@ -182,11 +180,10 @@ public function form(array $form, FormStateInterface $form_state) {
'#title' => t('Display settings'),
'#group' => 'additional_settings',
);
$form['display']['submitted'] = array(
$form['display']['display_submitted'] = array(
'#type' => 'checkbox',
'#title' => t('Display author and date information.'),
'#parents' => array('settings', 'node', 'submitted'),
'#default_value' => $node_settings['submitted'],
'#default_value' => $type->displaySubmitted(),
'#description' => t('Author username and publish date will be displayed.'),
);
return $form;
......@@ -220,7 +217,7 @@ public function validate(array $form, FormStateInterface $form_state) {
*/
public function save(array $form, FormStateInterface $form_state) {
$type = $this->entity;
$type->settings['node']['options']['revision'] = $form_state['values']['options']['revision'];
$type->setNewRevision($form_state->getValue(array('options', 'revision')));
$type->type = trim($type->id());
$type->name = trim($type->name);
......
......@@ -8,30 +8,67 @@
namespace Drupal\node;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\Config\Entity\ThirdPartySettingsInterface;
/**
* Provides an interface defining a node type entity.
*/
interface NodeTypeInterface extends ConfigEntityInterface {
interface NodeTypeInterface extends ConfigEntityInterface, ThirdPartySettingsInterface {
/**
* Returns the configured node type settings of a given module, if any.
* Determines whether the node type is locked.
*
* @param string $module
* The name of the module whose settings to return.
* @return string|false
* The module name that locks the type or FALSE.
*/
public function isLocked();
/**
* Returns whether a new revision should be created by default.
*
* @return array
* An associative array containing the module's settings for the node type.
* Note that this can be empty, and default values do not necessarily exist.
* @return bool
* TRUE if a new revision should be created by default.
*/
public function getModuleSettings($module);
public function isNewRevision();
/**
* Determines whether the node type is locked.
* Set whether a new revision should be created by default.
*
* @return string|false
* The module name that locks the type or FALSE.
* @param bool $new_revision_
* TRUE if a new revision should be created by default.
*/
public function isLocked();
public function setNewRevision($new_revision);
/**
* Returns whether 'Submitted by' information should be shown.
*
* @return bool
* TRUE if the submitted by information should be shown.
*/
public function displaySubmitted();
/**
* Set whether 'Submitted by' information should be shown.
*
* @param bool $display_submitted
* TRUE if the submitted by information should be shown.
*/
public function setDisplaySubmitted($display_submtited);
/**
* Returns the preview mode.
*
* @return int
* DRUPAL_DISABLED, DRUPAL_OPTIONAL or DRUPAL_REQUIRED.
*/
public function getPreviewMode();
/**
* Sets the preview mode.
*
* @param int $preview_mode
* DRUPAL_DISABLED, DRUPAL_OPTIONAL or DRUPAL_REQUIRED.
*/
public function setPreviewMode($preview_mode);
}
......@@ -63,7 +63,7 @@ function testNodeCreation() {
// Change the node type setting to show submitted by information.
$node_type = entity_load('node_type', 'page');
$node_type->settings['node']['submitted'] = TRUE;
$node_type->setDisplaySubmitted(TRUE);
$node_type->save();
$this->drupalGet('node/' . $node->id());
......
......@@ -53,26 +53,14 @@ function testAccessToAdministrativeFields() {
// Create the page node type with revisions disabled.
$page = NodeType::create([
'type' => 'page',
'settings' => array(
'node' => array(
'options' => array(
'revision' => FALSE,
),
),
),
'new_revision' => FALSE,
]);
$page->save();