Update Composer, update everything

This commit is contained in:
Oliver Davies 2018-11-23 12:29:20 +00:00
parent ea3e94409f
commit dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions

View file

@ -6,5 +6,5 @@ dependencies:
id: node_delete_action
label: 'Delete content'
type: node
plugin: node_delete_action
plugin: entity:delete_action:node
configuration: { }

View file

@ -6,5 +6,5 @@ dependencies:
id: node_publish_action
label: 'Publish content'
type: node
plugin: node_publish_action
plugin: entity:publish_action:node
configuration: { }

View file

@ -6,5 +6,5 @@ dependencies:
id: node_save_action
label: 'Save content'
type: node
plugin: node_save_action
plugin: entity:save_action:node
configuration: { }

View file

@ -6,5 +6,5 @@ dependencies:
id: node_unpublish_action
label: 'Unpublish content'
type: node
plugin: node_unpublish_action
plugin: entity:unpublish_action:node
configuration: { }

View file

@ -28,7 +28,7 @@ display:
empty:
area_text_custom:
admin_label: ''
content: 'No front page content has been created yet.'
content: 'No front page content has been created yet.<br/>Follow the <a target="_blank" href="https://www.drupal.org/docs/user_guide/en/index.html">User Guide</a> to start building your site.'
empty: true
field: area_text_custom
group_type: group

View file

@ -62,18 +62,26 @@ action.configuration.node_promote_action:
type: action_configuration_default
label: 'Promote selected content from front page configuration'
# @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
# @see https://www.drupal.org/node/2919303
action.configuration.node_publish_action:
type: action_configuration_default
label: 'Publish selected content configuration'
# @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
# @see https://www.drupal.org/node/2919303
action.configuration.node_unpublish_action:
type: action_configuration_default
label: 'Unpublish selected content configuration'
# @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
# @see https://www.drupal.org/node/2919303
action.configuration.node_save_action:
type: action_configuration_default
label: 'Save content configuration'
# @deprecated in Drupal 8.6.x, to be removed before Drupal 9.0.0.
# @see https://www.drupal.org/node/2934349
action.configuration.node_delete_action:
type: action_configuration_default
label: 'Delete content configuration'

View file

@ -0,0 +1,81 @@
/**
* @file
* Javascript for the node content editing form.
*/
(function($, Drupal) {
/**
* Behaviors for setting summaries on content type form.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches summary behaviors on content type edit forms.
*/
Drupal.behaviors.contentTypes = {
attach(context) {
const $context = $(context);
// Provide the vertical tab summaries.
$context.find('#edit-submission').drupalSetSummary(context => {
const vals = [];
vals.push(
Drupal.checkPlain(
$(context)
.find('#edit-title-label')
.val(),
) || Drupal.t('Requires a title'),
);
return vals.join(', ');
});
$context.find('#edit-workflow').drupalSetSummary(context => {
const vals = [];
$(context)
.find('input[name^="options"]:checked')
.next('label')
.each(function() {
vals.push(Drupal.checkPlain($(this).text()));
});
if (
!$(context)
.find('#edit-options-status')
.is(':checked')
) {
vals.unshift(Drupal.t('Not published'));
}
return vals.join(', ');
});
$('#edit-language', context).drupalSetSummary(context => {
const vals = [];
vals.push(
$(
'.js-form-item-language-configuration-langcode select option:selected',
context,
).text(),
);
$('input:checked', context)
.next('label')
.each(function() {
vals.push(Drupal.checkPlain($(this).text()));
});
return vals.join(', ');
});
$context.find('#edit-display').drupalSetSummary(context => {
const vals = [];
const $editContext = $(context);
$editContext
.find('input:checked')
.next('label')
.each(function() {
vals.push(Drupal.checkPlain($(this).text()));
});
if (!$editContext.find('#edit-display-submitted').is(':checked')) {
vals.unshift(Drupal.t("Don't display post information"));
}
return vals.join(', ');
});
},
};
})(jQuery, Drupal);

View file

@ -1,24 +1,15 @@
/**
* @file
* Javascript for the node content editing form.
*/
* DO NOT EDIT THIS FILE.
* See the following change record for more information,
* https://www.drupal.org/node/2815083
* @preserve
**/
(function ($, Drupal) {
'use strict';
/**
* Behaviors for setting summaries on content type form.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches summary behaviors on content type edit forms.
*/
Drupal.behaviors.contentTypes = {
attach: function (context) {
attach: function attach(context) {
var $context = $(context);
// Provide the vertical tab summaries.
$context.find('#edit-submission').drupalSetSummary(function (context) {
var vals = [];
vals.push(Drupal.checkPlain($(context).find('#edit-title-label').val()) || Drupal.t('Requires a title'));
@ -26,7 +17,7 @@
});
$context.find('#edit-workflow').drupalSetSummary(function (context) {
var vals = [];
$(context).find('input[name^="options"]:checked').parent().each(function () {
$(context).find('input[name^="options"]:checked').next('label').each(function () {
vals.push(Drupal.checkPlain($(this).text()));
});
if (!$(context).find('#edit-options-status').is(':checked')) {
@ -58,5 +49,4 @@
});
}
};
})(jQuery, Drupal);
})(jQuery, Drupal);

View file

@ -9,7 +9,7 @@
/* Narrow screens */
.layout-region {
box-sizing: border-box;
box-sizing: border-box;
}
/* Wide screens */
@ -51,7 +51,7 @@
.layout-region-node-secondary .form-number,
.layout-region-node-secondary .form-color,
.layout-region-node-secondary textarea {
box-sizing: border-box;
box-sizing: border-box;
width: 100%;
max-width: 100%;
}

View file

@ -1,55 +0,0 @@
id: d6_node_translation
label: Node translations
migration_tags:
- Drupal 6
deriver: Drupal\node\Plugin\migrate\D6NodeDeriver
source:
plugin: d6_node
translations: true
process:
# If you are using this file to build a custom migration consider removing
# the nid field to allow incremental migrations.
nid: tnid
type: type
langcode:
plugin: default_value
source: language
default_value: "und"
title: title
uid: node_uid
status: status
created: created
changed: changed
promote: promote
sticky: sticky
'body/format':
plugin: migration_lookup
migration: d6_filter_format
source: format
'body/value': body
'body/summary': teaser
revision_uid: revision_uid
revision_log: log
revision_timestamp: timestamp
content_translation_source: source_langcode
# unmapped d6 fields.
# translate
# moderate
# comment
destination:
plugin: entity:node
translations: true
migration_dependencies:
required:
- d6_user
- d6_node_type
- d6_node_settings
- d6_filter_format
- language
optional:
- d6_field_instance_widget_settings
- d6_field_formatter_settings
- d6_upload_field_instance
provider: migrate_drupal

View file

@ -1,42 +0,0 @@
id: d7_node_translation
label: Node translations
migration_tags:
- Drupal 7
- translation
deriver: Drupal\node\Plugin\migrate\D7NodeDeriver
source:
plugin: d7_node
translations: true
process:
# If you are using this file to build a custom migration consider removing
# the nid field to allow incremental migrations.
nid: tnid
type: type
langcode:
plugin: default_value
source: language
default_value: "und"
title: title
uid: node_uid
status: status
created: created
changed: changed
promote: promote
sticky: sticky
revision_uid: revision_uid
revision_log: log
revision_timestamp: timestamp
content_translation_source: source_langcode
destination:
plugin: entity:node
translations: true
content_translation_update_definitions:
- node
migration_dependencies:
required:
- d7_user
- d7_node_type
- language
optional:
- d7_field_instance
provider: migrate_drupal

View file

@ -1,7 +1,9 @@
id: d6_node
label: Nodes
audit: true
migration_tags:
- Drupal 6
- Content
deriver: Drupal\node\Plugin\migrate\D6NodeDeriver
source:
plugin: d6_node

View file

@ -1,7 +1,9 @@
id: d6_node_revision
label: Node revisions
audit: true
migration_tags:
- Drupal 6
- Content
deriver: Drupal\node\Plugin\migrate\D6NodeDeriver
source:
plugin: d6_node_revision

View file

@ -2,6 +2,7 @@ id: d6_node_setting_promote
label: Node type 'promote' setting
migration_tags:
- Drupal 6
- Configuration
source:
plugin: d6_node_type
constants:

View file

@ -2,6 +2,7 @@ id: d6_node_setting_status
label: Node type 'status' setting
migration_tags:
- Drupal 6
- Configuration
source:
plugin: d6_node_type
constants:

View file

@ -2,6 +2,7 @@ id: d6_node_setting_sticky
label: Node type 'sticky' setting
migration_tags:
- Drupal 6
- Configuration
source:
plugin: d6_node_type
constants:

View file

@ -2,10 +2,12 @@ id: d6_node_settings
label: Node configuration
migration_tags:
- Drupal 6
- Configuration
source:
plugin: variable
variables:
- node_admin_theme
source_module: node
process:
use_admin_theme: node_admin_theme
destination:

View file

@ -2,6 +2,7 @@ id: d6_node_type
label: Node type configuration
migration_tags:
- Drupal 6
- Configuration
source:
plugin: d6_node_type
constants:

View file

@ -2,6 +2,7 @@ id: d6_view_modes
label: View modes
migration_tags:
- Drupal 6
- Configuration
source:
plugin: d6_view_mode
constants:

View file

@ -1,7 +1,9 @@
id: d7_node
label: Nodes
audit: true
migration_tags:
- Drupal 7
- Content
deriver: Drupal\node\Plugin\migrate\D7NodeDeriver
source:
plugin: d7_node
@ -35,3 +37,4 @@ migration_dependencies:
- d7_node_type
optional:
- d7_field_instance
- d7_comment_field_instance

View file

@ -1,7 +1,9 @@
id: d7_node_revision
label: Node revisions
audit: true
migration_tags:
- Drupal 7
- Content
deriver: Drupal\node\Plugin\migrate\D7NodeDeriver
source:
plugin: d7_node_revision

View file

@ -2,10 +2,12 @@ id: d7_node_settings
label: Node configuration
migration_tags:
- Drupal 7
- Configuration
source:
plugin: variable
variables:
- node_admin_theme
source_module: node
process:
use_admin_theme: node_admin_theme
destination:

View file

@ -2,6 +2,7 @@ id: d7_node_title_label
label: Node title label
migration_tags:
- Drupal 7
- Configuration
source:
plugin: d7_node_type
constants:

View file

@ -2,6 +2,7 @@ id: d7_node_type
label: Node type configuration
migration_tags:
- Drupal 7
- Configuration
source:
plugin: d7_node_type
constants:

View file

@ -35,7 +35,7 @@ function node_mass_update(array $nodes, array $updates, $langcode = NULL, $load
if (count($nodes) > 10) {
$batch = [
'operations' => [
['_node_mass_update_batch_process', [$nodes, $updates, $langcode, $load, $revisions]]
['_node_mass_update_batch_process', [$nodes, $updates, $langcode, $load, $revisions]],
],
'finished' => '_node_mass_update_batch_finished',
'title' => t('Processing'),
@ -60,7 +60,7 @@ function node_mass_update(array $nodes, array $updates, $langcode = NULL, $load
}
_node_mass_update_helper($node, $updates, $langcode);
}
drupal_set_message(t('The update has been performed.'));
\Drupal::messenger()->addStatus(t('The update has been performed.'));
}
}
@ -164,16 +164,16 @@ function _node_mass_update_batch_process(array $nodes, array $updates, $langcode
*/
function _node_mass_update_batch_finished($success, $results, $operations) {
if ($success) {
drupal_set_message(t('The update has been performed.'));
\Drupal::messenger()->addStatus(t('The update has been performed.'));
}
else {
drupal_set_message(t('An error occurred and processing did not complete.'), 'error');
\Drupal::messenger()->addError(t('An error occurred and processing did not complete.'));
$message = \Drupal::translation()->formatPlural(count($results), '1 item successfully processed:', '@count items successfully processed:');
$item_list = [
'#theme' => 'item_list',
'#items' => $results,
];
$message .= drupal_render($item_list);
drupal_set_message($message);
$message .= \Drupal::service('renderer')->render($item_list);
\Drupal::messenger()->addStatus($message);
}
}

View file

@ -169,7 +169,7 @@ function hook_node_access_records(\Drupal\node\NodeInterface $node) {
'grant_view' => 1,
'grant_update' => 0,
'grant_delete' => 0,
'langcode' => 'ca'
'langcode' => 'ca',
];
}
// For the example_author array, the GID is equivalent to a UID, which
@ -183,7 +183,7 @@ function hook_node_access_records(\Drupal\node\NodeInterface $node) {
'grant_view' => 1,
'grant_update' => 1,
'grant_delete' => 1,
'langcode' => 'ca'
'langcode' => 'ca',
];
}
@ -297,11 +297,17 @@ function hook_node_grants_alter(&$grants, \Drupal\Core\Session\AccountInterface
* permission may always view and edit content through the administrative
* interface.
*
* Note that not all modules will want to influence access on all node types. If
* your module does not want to explicitly allow or forbid access, return an
* AccessResultInterface object with neither isAllowed() nor isForbidden()
* equaling TRUE. Blindly returning an object with isForbidden() equaling TRUE
* will break other node access modules.
* The access to a node can be influenced in several ways:
* - To explicitly allow access, return an AccessResultInterface object with
* isAllowed() returning TRUE. Other modules can override this access by
* returning TRUE for isForbidden().
* - To explicitly forbid access, return an AccessResultInterface object with
* isForbidden() returning TRUE. Access will be forbidden even if your module
* (or another module) also returns TRUE for isNeutral() or isAllowed().
* - To neither allow nor explicitly forbid access, return an
* AccessResultInterface object with isNeutral() returning TRUE.
* - If your module does not return an AccessResultInterface object, neutral
* access will be assumed.
*
* Also note that this function isn't called for node listings (e.g., RSS feeds,
* the default home page at path 'node', a recent content block, etc.) See
@ -320,7 +326,7 @@ function hook_node_grants_alter(&$grants, \Drupal\Core\Session\AccountInterface
* The user object to perform the access check operation on.
*
* @return \Drupal\Core\Access\AccessResultInterface
* The access result.
* The access result.
*
* @ingroup node_access
*/

View file

@ -0,0 +1,56 @@
/**
* @file
* Defines Javascript behaviors for the node module.
*/
(function($, Drupal, drupalSettings) {
/**
* Behaviors for tabs in the node edit form.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches summary behavior for tabs in the node edit form.
*/
Drupal.behaviors.nodeDetailsSummaries = {
attach(context) {
const $context = $(context);
$context.find('.node-form-author').drupalSetSummary(context => {
const $authorContext = $(context);
const name = $authorContext.find('.field--name-uid input').val();
const date = $authorContext.find('.field--name-created input').val();
if (name && date) {
return Drupal.t('By @name on @date', {
'@name': name,
'@date': date,
});
}
if (name) {
return Drupal.t('By @name', { '@name': name });
}
if (date) {
return Drupal.t('Authored on @date', { '@date': date });
}
});
$context.find('.node-form-options').drupalSetSummary(context => {
const $optionsContext = $(context);
const vals = [];
if ($optionsContext.find('input').is(':checked')) {
$optionsContext
.find('input:checked')
.next('label')
.each(function() {
vals.push(Drupal.checkPlain($.trim($(this).text())));
});
return vals.join(', ');
}
return Drupal.t('Not promoted');
});
},
};
})(jQuery, Drupal, drupalSettings);

View file

@ -6,4 +6,4 @@ version: VERSION
core: 8.x
configure: entity.node_type.collection
dependencies:
- text
- drupal:text

View file

@ -74,7 +74,7 @@ function node_schema() {
'default' => 0,
],
'realm' => [
'description' => 'The realm in which the user must possess the grant ID. Each node access node can define one or more realms.',
'description' => 'The realm in which the user must possess the grant ID. Modules can define one or more realms by implementing hook_node_grants().',
'type' => 'varchar_ascii',
'length' => 255,
'not null' => TRUE,
@ -246,3 +246,24 @@ function node_update_8301() {
$entity_type->set('entity_keys', $keys);
$definition_update_manager->updateEntityType($entity_type);
}
/**
* Fix realm column description on the node_access table.
*/
function node_update_8400() {
$schema = drupal_get_module_schema('node', 'node_access');
$schema['fields']['realm']['description'] = 'The realm in which the user must possess the grant ID. Modules can define one or more realms by implementing hook_node_grants().';
Database::getConnection()->schema()->changeField('node_access', 'realm', 'realm', $schema['fields']['realm']);
}
/**
* Run a node access rebuild, if required.
*/
function node_update_8401() {
// Get the list of node access modules.
$modules = \Drupal::moduleHandler()->getImplementations('node_grants');
// If multilingual usage, then rebuild node access.
if (count($modules) > 0 && \Drupal::languageManager()->isMultilingual()) {
node_access_needs_rebuild(TRUE);
}
}

View file

@ -1,22 +1,13 @@
/**
* @file
* Defines Javascript behaviors for the node module.
*/
* DO NOT EDIT THIS FILE.
* See the following change record for more information,
* https://www.drupal.org/node/2815083
* @preserve
**/
(function ($, Drupal, drupalSettings) {
'use strict';
/**
* Behaviors for tabs in the node edit form.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches summary behavior for tabs in the node edit form.
*/
Drupal.behaviors.nodeDetailsSummaries = {
attach: function (context) {
attach: function attach(context) {
var $context = $(context);
$context.find('.node-form-author').drupalSetSummary(function (context) {
@ -25,13 +16,16 @@
var date = $authorContext.find('.field--name-created input').val();
if (name && date) {
return Drupal.t('By @name on @date', {'@name': name, '@date': date});
return Drupal.t('By @name on @date', {
'@name': name,
'@date': date
});
}
else if (name) {
return Drupal.t('By @name', {'@name': name});
if (name) {
return Drupal.t('By @name', { '@name': name });
}
else if (date) {
return Drupal.t('Authored on @date', {'@date': date});
if (date) {
return Drupal.t('Authored on @date', { '@date': date });
}
});
@ -45,11 +39,9 @@
});
return vals.join(', ');
}
else {
return Drupal.t('Not promoted');
}
return Drupal.t('Not promoted');
});
}
};
})(jQuery, Drupal, drupalSettings);
})(jQuery, Drupal, drupalSettings);

View file

@ -20,6 +20,7 @@ drupal.node.preview:
- core/jquery
- core/jquery.once
- core/drupal
- core/drupal.dialog
- core/drupal.form
drupal.content_types:

View file

@ -28,12 +28,15 @@ use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\node\NodeInterface;
use Drupal\node\NodeTypeInterface;
use Drupal\user\UserInterface;
/**
* Denotes that the node is not published.
*
* @deprecated Scheduled for removal in Drupal 9.0.x.
* Use \Drupal\node\NodeInterface::NOT_PUBLISHED instead.
*
* @see https://www.drupal.org/node/2316145
*/
const NODE_NOT_PUBLISHED = 0;
@ -42,6 +45,8 @@ const NODE_NOT_PUBLISHED = 0;
*
* @deprecated Scheduled for removal in Drupal 9.0.x.
* Use \Drupal\node\NodeInterface::PUBLISHED instead.
*
* @see https://www.drupal.org/node/2316145
*/
const NODE_PUBLISHED = 1;
@ -50,6 +55,8 @@ const NODE_PUBLISHED = 1;
*
* @deprecated Scheduled for removal in Drupal 9.0.x.
* Use \Drupal\node\NodeInterface::NOT_PROMOTED instead.
*
* @see https://www.drupal.org/node/2316145
*/
const NODE_NOT_PROMOTED = 0;
@ -58,6 +65,8 @@ const NODE_NOT_PROMOTED = 0;
*
* @deprecated Scheduled for removal in Drupal 9.0.x.
* Use \Drupal\node\NodeInterface::PROMOTED instead.
*
* @see https://www.drupal.org/node/2316145
*/
const NODE_PROMOTED = 1;
@ -66,6 +75,8 @@ const NODE_PROMOTED = 1;
*
* @deprecated Scheduled for removal in Drupal 9.0.x.
* Use \Drupal\node\NodeInterface::NOT_STICKY instead.
*
* @see https://www.drupal.org/node/2316145
*/
const NODE_NOT_STICKY = 0;
@ -74,6 +85,8 @@ const NODE_NOT_STICKY = 0;
*
* @deprecated Scheduled for removal in Drupal 9.0.x.
* Use \Drupal\node\NodeInterface::STICKY instead.
*
* @see https://www.drupal.org/node/2316145
*/
const NODE_STICKY = 1;
@ -92,7 +105,7 @@ function node_help($route_name, RouteMatchInterface $route_match) {
else {
$message = t('The content access permissions need to be rebuilt. <a href=":node_access_rebuild">Rebuild permissions</a>.', [':node_access_rebuild' => \Drupal::url('node.configure_rebuild_confirm')]);
}
drupal_set_message($message, 'error');
\Drupal::messenger()->addError($message);
}
switch ($route_name) {
@ -121,7 +134,7 @@ function node_help($route_name, RouteMatchInterface $route_match) {
case 'entity.entity_form_display.node.default':
case 'entity.entity_form_display.node.form_mode':
$type = $route_match->getParameter('node_type');
return '<p>' . t('Content items can be edited using different form modes. Here, you can define which fields are shown and hidden when %type content is edited in each form mode, and define how the field form widgets are displayed in each form mode.', ['%type' => $type->label()]) . '</p>' ;
return '<p>' . t('Content items can be edited using different form modes. Here, you can define which fields are shown and hidden when %type content is edited in each form mode, and define how the field form widgets are displayed in each form mode.', ['%type' => $type->label()]) . '</p>';
case 'entity.entity_view_display.node.default':
case 'entity.entity_view_display.node.view_mode':
@ -535,9 +548,12 @@ function template_preprocess_node_add_list(&$variables) {
* Implements hook_preprocess_HOOK() for HTML document templates.
*/
function node_preprocess_html(&$variables) {
// If on an individual node page, add the node type to body classes.
if (($node = \Drupal::routeMatch()->getParameter('node')) && $node instanceof NodeInterface) {
$variables['node_type'] = $node->getType();
// If on an individual node page or node preview page, add the node type to
// the body classes.
if (($node = \Drupal::routeMatch()->getParameter('node')) || ($node = \Drupal::routeMatch()->getParameter('node_preview'))) {
if ($node instanceof NodeInterface) {
$variables['node_type'] = $node->getType();
}
}
}
@ -593,9 +609,9 @@ function template_preprocess_node(&$variables) {
$variables['node'] = $variables['elements']['#node'];
/** @var \Drupal\node\NodeInterface $node */
$node = $variables['node'];
$variables['date'] = drupal_render($variables['elements']['created']);
$variables['date'] = \Drupal::service('renderer')->render($variables['elements']['created']);
unset($variables['elements']['created']);
$variables['author_name'] = drupal_render($variables['elements']['uid']);
$variables['author_name'] = \Drupal::service('renderer')->render($variables['elements']['uid']);
unset($variables['elements']['uid']);
$variables['url'] = $node->url('canonical', [
@ -698,7 +714,7 @@ function node_ranking() {
/**
* Implements hook_user_cancel().
*/
function node_user_cancel($edit, $account, $method) {
function node_user_cancel($edit, UserInterface $account, $method) {
switch ($method) {
case 'user_cancel_block_unpublish':
// Unpublish nodes (current revisions).
@ -794,7 +810,7 @@ function node_get_recent($number = 10) {
* the global content language of the current request.
*
* @return array
* An array as expected by drupal_render().
* An array as expected by \Drupal\Core\Render\RendererInterface::render().
*/
function node_view(NodeInterface $node, $view_mode = 'full', $langcode = NULL) {
return entity_view($node, $view_mode, $langcode);
@ -812,7 +828,8 @@ function node_view(NodeInterface $node, $view_mode = 'full', $langcode = NULL) {
* content language of the current request.
*
* @return array
* An array in the format expected by drupal_render().
* An array in the format expected by
* \Drupal\Core\Render\RendererInterface::render().
*/
function node_view_multiple($nodes, $view_mode = 'teaser', $langcode = NULL) {
return entity_view_multiple($nodes, $view_mode, $langcode);
@ -828,7 +845,7 @@ function node_page_top(array &$page) {
$page['page_top']['node_preview'] = [
'#type' => 'container',
'#attributes' => [
'class' => ['node-preview-container', 'container-inline']
'class' => ['node-preview-container', 'container-inline'],
],
];
@ -1022,7 +1039,6 @@ function node_access_view_all_nodes($account = NULL) {
return $access[$account->id()];
}
/**
* Implements hook_query_TAG_alter().
*
@ -1099,7 +1115,7 @@ function node_query_node_access_alter(AlterableInterface $query) {
// context.
$request = \Drupal::requestStack()->getCurrentRequest();
$renderer = \Drupal::service('renderer');
if ($request->isMethodSafe() && $renderer->hasRenderContext()) {
if ($request->isMethodCacheable() && $renderer->hasRenderContext()) {
$build = ['#cache' => ['contexts' => ['user.node_grants:' . $op]]];
$renderer->render($build);
}
@ -1173,7 +1189,7 @@ function node_access_rebuild($batch_mode = FALSE) {
'operations' => [
['_node_access_rebuild_batch_operation', []],
],
'finished' => '_node_access_rebuild_batch_finished'
'finished' => '_node_access_rebuild_batch_finished',
];
batch_set($batch);
}
@ -1209,7 +1225,7 @@ function node_access_rebuild($batch_mode = FALSE) {
}
if (!isset($batch)) {
drupal_set_message(t('Content permissions have been rebuilt.'));
\Drupal::messenger()->addStatus(t('Content permissions have been rebuilt.'));
node_access_needs_rebuild(FALSE);
}
}
@ -1282,11 +1298,11 @@ function _node_access_rebuild_batch_operation(&$context) {
*/
function _node_access_rebuild_batch_finished($success, $results, $operations) {
if ($success) {
drupal_set_message(t('The content access permissions have been rebuilt.'));
\Drupal::messenger()->addStatus(t('The content access permissions have been rebuilt.'));
node_access_needs_rebuild(FALSE);
}
else {
drupal_set_message(t('The content access permissions have not been properly rebuilt.'), 'error');
\Drupal::messenger()->addError(t('The content access permissions have not been properly rebuilt.'));
}
}

View file

@ -12,18 +12,17 @@ administer nodes:
restrict access: true
access content overview:
title: 'Access the Content overview page'
access content:
title: 'View published content'
view own unpublished content:
title: 'View own unpublished content'
view all revisions:
title: 'View all revisions'
description: 'To view a revision, you also need permission to view the content item.'
revert all revisions:
title: 'Revert all revisions'
description: 'Role requires permission <em>view revisions</em> and <em>edit rights</em> for nodes in question or <em>administer nodes</em>.'
description: 'To revert a revision, you also need permission to edit the content item.'
delete all revisions:
title: 'Delete all revisions'
description: 'Role requires permission to <em>view revisions</em> and <em>delete rights</em> for nodes in question or <em>administer nodes</em>.'
description: 'To delete a revision, you also need permission to delete the content item.'
permission_callbacks:
- \Drupal\node\NodePermissions::nodeTypePermissions

View file

@ -0,0 +1,29 @@
<?php
/**
* @file
* Post update functions for Node.
*/
use Drupal\Core\Entity\Entity\EntityFormDisplay;
/**
* Load all form displays for nodes, add status with these settings, save.
*/
function node_post_update_configure_status_field_widget() {
$query = \Drupal::entityQuery('entity_form_display')->condition('targetEntityType', 'node');
$ids = $query->execute();
$form_displays = EntityFormDisplay::loadMultiple($ids);
// Assign status settings for each 'node' target entity types with 'default'
// form mode.
foreach ($form_displays as $id => $form_display) {
/** @var \Drupal\Core\Entity\Display\EntityDisplayInterface $form_display */
$form_display->setComponent('status', [
'type' => 'boolean_checkbox',
'settings' => [
'display_label' => TRUE,
],
])->save();
}
}

View file

@ -0,0 +1,112 @@
/**
* @file
* Preview behaviors.
*/
(function($, Drupal) {
/**
* Disables all non-relevant links in node previews.
*
* Destroys links (except local fragment identifiers such as href="#frag") in
* node previews to prevent users from leaving the page.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches confirmation prompt for clicking links in node preview mode.
* @prop {Drupal~behaviorDetach} detach
* Detaches confirmation prompt for clicking links in node preview mode.
*/
Drupal.behaviors.nodePreviewDestroyLinks = {
attach(context) {
function clickPreviewModal(event) {
// Only confirm leaving previews when left-clicking and user is not
// pressing the ALT, CTRL, META (Command key on the Macintosh keyboard)
// or SHIFT key.
if (
event.button === 0 &&
!event.altKey &&
!event.ctrlKey &&
!event.metaKey &&
!event.shiftKey
) {
event.preventDefault();
const $previewDialog = $(
`<div>${Drupal.theme('nodePreviewModal')}</div>`,
).appendTo('body');
Drupal.dialog($previewDialog, {
title: Drupal.t('Leave preview?'),
buttons: [
{
text: Drupal.t('Cancel'),
click() {
$(this).dialog('close');
},
},
{
text: Drupal.t('Leave preview'),
click() {
window.top.location.href = event.target.href;
},
},
],
}).showModal();
}
}
const $preview = $(context).once('node-preview');
if ($(context).find('.node-preview-container').length) {
$preview.on(
'click.preview',
'a:not([href^="#"], .node-preview-container a)',
clickPreviewModal,
);
}
},
detach(context, settings, trigger) {
if (trigger === 'unload') {
const $preview = $(context)
.find('.content')
.removeOnce('node-preview');
if ($preview.length) {
$preview.off('click.preview');
}
}
},
};
/**
* Switch view mode.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches automatic submit on `formUpdated.preview` events.
*/
Drupal.behaviors.nodePreviewSwitchViewMode = {
attach(context) {
const $autosubmit = $(context)
.find('[data-drupal-autosubmit]')
.once('autosubmit');
if ($autosubmit.length) {
$autosubmit.on('formUpdated.preview', function() {
$(this.form).trigger('submit');
});
}
},
};
/**
* Theme function for node preview modal.
*
* @return {string}
* Markup for the node preview modal.
*/
Drupal.theme.nodePreviewModal = function() {
return `<p>${Drupal.t(
'Leaving the preview will cause unsaved changes to be lost. Are you sure you want to leave the preview?',
)}</p><small class="description">${Drupal.t(
'CTRL+Left click will prevent this dialog from showing and proceed to the clicked link.',
)}</small>`;
};
})(jQuery, Drupal);

View file

@ -1,60 +1,40 @@
/**
* @file
* Preview behaviors.
*/
* DO NOT EDIT THIS FILE.
* See the following change record for more information,
* https://www.drupal.org/node/2815083
* @preserve
**/
(function ($, Drupal) {
'use strict';
/**
* Disables all non-relevant links in node previews.
*
* Destroys links (except local fragment identifiers such as href="#frag") in
* node previews to prevent users from leaving the page.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches confirmation prompt for clicking links in node preview mode.
* @prop {Drupal~behaviorDetach} detach
* Detaches confirmation prompt for clicking links in node preview mode.
*/
Drupal.behaviors.nodePreviewDestroyLinks = {
attach: function (context) {
attach: function attach(context) {
function clickPreviewModal(event) {
// Only confirm leaving previews when left-clicking and user is not
// pressing the ALT, CTRL, META (Command key on the Macintosh keyboard)
// or SHIFT key.
if (event.button === 0 && !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey) {
event.preventDefault();
var $previewDialog = $('<div>' + Drupal.theme('nodePreviewModal') + '</div>').appendTo('body');
Drupal.dialog($previewDialog, {
title: Drupal.t('Leave preview?'),
buttons: [
{
text: Drupal.t('Cancel'),
click: function () {
$(this).dialog('close');
}
}, {
text: Drupal.t('Leave preview'),
click: function () {
window.top.location.href = event.target.href;
}
buttons: [{
text: Drupal.t('Cancel'),
click: function click() {
$(this).dialog('close');
}
]
}, {
text: Drupal.t('Leave preview'),
click: function click() {
window.top.location.href = event.target.href;
}
}]
}).showModal();
}
}
var $preview = $(context).find('.content').once('node-preview');
var $preview = $(context).once('node-preview');
if ($(context).find('.node-preview-container').length) {
$preview.on('click.preview', 'a:not([href^=#], #edit-backlink, #toolbar-administration a)', clickPreviewModal);
$preview.on('click.preview', 'a:not([href^="#"], .node-preview-container a)', clickPreviewModal);
}
},
detach: function (context, settings, trigger) {
detach: function detach(context, settings, trigger) {
if (trigger === 'unload') {
var $preview = $(context).find('.content').removeOnce('node-preview');
if ($preview.length) {
@ -64,16 +44,8 @@
}
};
/**
* Switch view mode.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches automatic submit on `formUpdated.preview` events.
*/
Drupal.behaviors.nodePreviewSwitchViewMode = {
attach: function (context) {
attach: function attach(context) {
var $autosubmit = $(context).find('[data-drupal-autosubmit]').once('autosubmit');
if ($autosubmit.length) {
$autosubmit.on('formUpdated.preview', function () {
@ -83,17 +55,7 @@
}
};
/**
* Theme function for node preview modal.
*
* @return {string}
* Markup for the node preview modal.
*/
Drupal.theme.nodePreviewModal = function () {
return '<p>' +
Drupal.t('Leaving the preview will cause unsaved changes to be lost. Are you sure you want to leave the preview?') +
'</p><small class="description">' +
Drupal.t('CTRL+Left click will prevent this dialog from showing and proceed to the clicked link.') + '</small>';
return '<p>' + Drupal.t('Leaving the preview will cause unsaved changes to be lost. Are you sure you want to leave the preview?') + '</p><small class="description">' + Drupal.t('CTRL+Left click will prevent this dialog from showing and proceed to the clicked link.') + '</small>';
};
})(jQuery, Drupal);
})(jQuery, Drupal);

View file

@ -2,8 +2,17 @@ node.multiple_delete_confirm:
path: '/admin/content/node/delete'
defaults:
_form: '\Drupal\node\Form\DeleteMultiple'
entity_type_id: 'node'
requirements:
_permission: 'administer nodes'
_entity_delete_multiple_access: 'node'
entity.node.delete_multiple_form:
path: '/admin/content/node/delete'
defaults:
_form: '\Drupal\node\Form\DeleteMultiple'
entity_type_id: 'node'
requirements:
_entity_delete_multiple_access: 'node'
node.add_page:
path: '/node/add'

View file

@ -30,7 +30,7 @@ services:
- { name: event_subscriber }
node_preview:
class: Drupal\node\ParamConverter\NodePreviewConverter
arguments: ['@user.private_tempstore']
arguments: ['@tempstore.private']
tags:
- { name: paramconverter }
lazy: true

View file

@ -4,8 +4,9 @@ namespace Drupal\node\ContextProvider;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Plugin\Context\Context;
use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Core\Plugin\Context\ContextProviderInterface;
use Drupal\Core\Plugin\Context\EntityContext;
use Drupal\Core\Plugin\Context\EntityContextDefinition;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\node\Entity\Node;
use Drupal\Core\StringTranslation\StringTranslationTrait;
@ -39,7 +40,7 @@ class NodeRouteContext implements ContextProviderInterface {
*/
public function getRuntimeContexts(array $unqualified_context_ids) {
$result = [];
$context_definition = new ContextDefinition('entity:node', NULL, FALSE);
$context_definition = EntityContextDefinition::create('node')->setRequired(FALSE);
$value = NULL;
if (($route_object = $this->routeMatch->getRouteObject()) && ($route_contexts = $route_object->getOption('parameters')) && isset($route_contexts['node'])) {
if ($node = $this->routeMatch->getParameter('node')) {
@ -65,7 +66,7 @@ class NodeRouteContext implements ContextProviderInterface {
* {@inheritdoc}
*/
public function getAvailableContexts() {
$context = new Context(new ContextDefinition('entity:node', $this->t('Node from URL')));
$context = EntityContext::fromEntityTypeId('node', $this->t('Node from URL'));
return ['node' => $context];
}

View file

@ -122,7 +122,7 @@ class NodeController extends ControllerBase implements ContainerInjectionInterfa
* The node revision ID.
*
* @return array
* An array suitable for drupal_render().
* An array suitable for \Drupal\Core\Render\RendererInterface::render().
*/
public function revisionShow($node_revision) {
$node = $this->entityManager()->getStorage('node')->loadRevision($node_revision);
@ -154,7 +154,7 @@ class NodeController extends ControllerBase implements ContainerInjectionInterfa
* A node object.
*
* @return array
* An array as expected by drupal_render().
* An array as expected by \Drupal\Core\Render\RendererInterface::render().
*/
public function revisionOverview(NodeInterface $node) {
$account = $this->currentUser();
@ -173,6 +173,7 @@ class NodeController extends ControllerBase implements ContainerInjectionInterfa
$rows = [];
$default_revision = $node->getRevisionId();
$current_revision_displayed = FALSE;
foreach ($this->getRevisionIds($node, $node_storage) as $vid) {
/** @var \Drupal\node\NodeInterface $revision */
@ -187,11 +188,18 @@ class NodeController extends ControllerBase implements ContainerInjectionInterfa
// Use revision link to link to revisions that are not active.
$date = $this->dateFormatter->format($revision->revision_timestamp->value, 'short');
if ($vid != $node->getRevisionId()) {
// We treat also the latest translation-affecting revision as current
// revision, if it was the default revision, as its values for the
// current language will be the same of the current default revision in
// this case.
$is_current_revision = $vid == $default_revision || (!$current_revision_displayed && $revision->wasDefaultRevision());
if (!$is_current_revision) {
$link = $this->l($date, new Url('entity.node.revision', ['node' => $node->id(), 'node_revision' => $vid]));
}
else {
$link = $node->link($date);
$current_revision_displayed = TRUE;
}
$row = [];
@ -210,7 +218,7 @@ class NodeController extends ControllerBase implements ContainerInjectionInterfa
$this->renderer->addCacheableDependency($column['data'], $username);
$row[] = $column;
if ($vid == $default_revision) {
if ($is_current_revision) {
$row[] = [
'data' => [
'#prefix' => '<em>',

View file

@ -2,9 +2,7 @@
namespace Drupal\node\Entity;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\Core\Entity\EntityPublishedTrait;
use Drupal\Core\Entity\EditorialContentEntityBase;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
@ -35,7 +33,8 @@ use Drupal\user\UserInterface;
* "form" = {
* "default" = "Drupal\node\NodeForm",
* "delete" = "Drupal\node\Form\NodeDeleteForm",
* "edit" = "Drupal\node\NodeForm"
* "edit" = "Drupal\node\NodeForm",
* "delete-multiple-confirm" = "Drupal\node\Form\DeleteMultiple"
* },
* "route_provider" = {
* "html" = "Drupal\node\Entity\NodeRouteProvider",
@ -61,6 +60,11 @@ use Drupal\user\UserInterface;
* "published" = "status",
* "uid" = "uid",
* },
* revision_metadata_keys = {
* "revision_user" = "revision_uid",
* "revision_created" = "revision_timestamp",
* "revision_log_message" = "revision_log"
* },
* bundle_entity_type = "node_type",
* field_ui_base_route = "entity.node_type.edit_form",
* common_reference_target = TRUE,
@ -68,16 +72,15 @@ use Drupal\user\UserInterface;
* links = {
* "canonical" = "/node/{node}",
* "delete-form" = "/node/{node}/delete",
* "delete-multiple-form" = "/admin/content/node/delete",
* "edit-form" = "/node/{node}/edit",
* "version-history" = "/node/{node}/revisions",
* "revision" = "/node/{node}/revisions/{node_revision}/view",
* "create" = "/node",
* }
* )
*/
class Node extends ContentEntityBase implements NodeInterface {
use EntityChangedTrait;
use EntityPublishedTrait;
class Node extends EditorialContentEntityBase implements NodeInterface {
/**
* Whether the node is being previewed or not.
@ -209,7 +212,6 @@ class Node extends ContentEntityBase implements NodeInterface {
return $this->get('created')->value;
}
/**
* {@inheritdoc}
*/
@ -278,21 +280,6 @@ class Node extends ContentEntityBase implements NodeInterface {
return $this;
}
/**
* {@inheritdoc}
*/
public function getRevisionCreationTime() {
return $this->get('revision_timestamp')->value;
}
/**
* {@inheritdoc}
*/
public function setRevisionCreationTime($timestamp) {
$this->set('revision_timestamp', $timestamp);
return $this;
}
/**
* {@inheritdoc}
*/
@ -300,13 +287,6 @@ class Node extends ContentEntityBase implements NodeInterface {
return $this->getRevisionUser();
}
/**
* {@inheritdoc}
*/
public function getRevisionUser() {
return $this->get('revision_uid')->entity;
}
/**
* {@inheritdoc}
*/
@ -315,50 +295,11 @@ class Node extends ContentEntityBase implements NodeInterface {
return $this;
}
/**
* {@inheritdoc}
*/
public function setRevisionUser(UserInterface $user) {
$this->set('revision_uid', $user);
return $this;
}
/**
* {@inheritdoc}
*/
public function getRevisionUserId() {
return $this->get('revision_uid')->entity->id();
}
/**
* {@inheritdoc}
*/
public function setRevisionUserId($user_id) {
$this->set('revision_uid', $user_id);
return $this;
}
/**
* {@inheritdoc}
*/
public function getRevisionLogMessage() {
return $this->get('revision_log')->value;
}
/**
* {@inheritdoc}
*/
public function setRevisionLogMessage($revision_log_message) {
$this->set('revision_log', $revision_log_message);
return $this;
}
/**
* {@inheritdoc}
*/
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields = parent::baseFieldDefinitions($entity_type);
$fields += static::publishedBaseFieldDefinitions($entity_type);
$fields['title'] = BaseFieldDefinition::create('string')
->setLabel(t('Title'))
@ -400,6 +341,16 @@ class Node extends ContentEntityBase implements NodeInterface {
])
->setDisplayConfigurable('form', TRUE);
$fields['status']
->setDisplayOptions('form', [
'type' => 'boolean_checkbox',
'settings' => [
'display_label' => TRUE,
],
'weight' => 120,
])
->setDisplayConfigurable('form', TRUE);
$fields['created'] = BaseFieldDefinition::create('created')
->setLabel(t('Authored on'))
->setDescription(t('The time that the node was created.'))
@ -450,39 +401,6 @@ class Node extends ContentEntityBase implements NodeInterface {
])
->setDisplayConfigurable('form', TRUE);
$fields['revision_timestamp'] = BaseFieldDefinition::create('created')
->setLabel(t('Revision timestamp'))
->setDescription(t('The time that the current revision was created.'))
->setQueryable(FALSE)
->setRevisionable(TRUE);
$fields['revision_uid'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('Revision user ID'))
->setDescription(t('The user ID of the author of the current revision.'))
->setSetting('target_type', 'user')
->setQueryable(FALSE)
->setRevisionable(TRUE);
$fields['revision_log'] = BaseFieldDefinition::create('string_long')
->setLabel(t('Revision log message'))
->setDescription(t('Briefly describe the changes you have made.'))
->setRevisionable(TRUE)
->setDefaultValue('')
->setDisplayOptions('form', [
'type' => 'string_textarea',
'weight' => 25,
'settings' => [
'rows' => 4,
],
]);
$fields['revision_translation_affected'] = BaseFieldDefinition::create('boolean')
->setLabel(t('Revision translation affected'))
->setDescription(t('Indicates if the last edit of a translation belongs to current revision.'))
->setReadOnly(TRUE)
->setRevisionable(TRUE)
->setTranslatable(TRUE);
return $fields;
}

View file

@ -15,7 +15,7 @@ class NodeRouteProvider implements EntityRouteProviderInterface {
/**
* {@inheritdoc}
*/
public function getRoutes( EntityTypeInterface $entity_type) {
public function getRoutes(EntityTypeInterface $entity_type) {
$route_collection = new RouteCollection();
$route = (new Route('/node/{node}'))
->addDefaults([

View file

@ -12,6 +12,13 @@ use Drupal\node\NodeTypeInterface;
* @ConfigEntityType(
* id = "node_type",
* label = @Translation("Content type"),
* label_collection = @Translation("Content types"),
* label_singular = @Translation("content type"),
* label_plural = @Translation("content types"),
* label_count = @PluralTranslation(
* singular = "@count content type",
* plural = "@count content types",
* ),
* handlers = {
* "access" = "Drupal\node\NodeTypeAccessControlHandler",
* "form" = {
@ -179,7 +186,7 @@ class NodeType extends ConfigEntityBundleBase implements NodeTypeInterface {
if ($update && $this->getOriginalId() != $this->id()) {
$update_count = node_type_update_nodes($this->getOriginalId(), $this->id());
if ($update_count) {
drupal_set_message(\Drupal::translation()->formatPlural($update_count,
\Drupal::messenger()->addStatus(\Drupal::translation()->formatPlural($update_count,
'Changed the content type of 1 post from %old-type to %type.',
'Changed the content type of @count posts from %old-type to %type.',
[

View file

@ -0,0 +1,128 @@
<?php
namespace Drupal\node\EventSubscriber;
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\ParamConverter\ParamNotConvertedException;
use Drupal\Core\Routing\UrlGeneratorInterface;
use Drupal\Core\State\StateInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Redirect node translations that have been consolidated by migration.
*
* If we migrated node translations from Drupal 6 or 7, these nodes are now
* combined with their source language node. Since there still might be
* references to the URLs of these now consolidated nodes, this service catches
* the 404s and try to redirect them to the right node in the right language.
*
* The mapping of the old nids to the new ones is made by the
* NodeTranslationMigrateSubscriber class during the migration and is stored
* in the "node_translation_redirect" key/value collection.
*
* @see \Drupal\node\NodeServiceProvider
* @see \Drupal\node\EventSubscriber\NodeTranslationMigrateSubscriber
*/
class NodeTranslationExceptionSubscriber implements EventSubscriberInterface {
/**
* The key value factory.
*
* @var \Drupal\Core\KeyValueStore\KeyValueFactoryInterface
*/
protected $keyValue;
/**
* The language manager.
*
* @var \Drupal\Core\Language\LanguageManagerInterface
*/
protected $languageManager;
/**
* The URL generator.
*
* @var \Drupal\Core\Routing\UrlGeneratorInterface
*/
protected $urlGenerator;
/**
* The state service.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* Constructs the NodeTranslationExceptionSubscriber.
*
* @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value
* The key value factory.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
* @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator
* The URL generator.
* @param \Drupal\Core\State\StateInterface $state
* The state service.
*/
public function __construct(KeyValueFactoryInterface $key_value, LanguageManagerInterface $language_manager, UrlGeneratorInterface $url_generator, StateInterface $state) {
$this->keyValue = $key_value;
$this->languageManager = $language_manager;
$this->urlGenerator = $url_generator;
$this->state = $state;
}
/**
* Redirects not found node translations using the key value collection.
*
* @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
* The exception event.
*/
public function onException(GetResponseForExceptionEvent $event) {
$exception = $event->getException();
// If this is not a 404, we don't need to check for a redirection.
if (!($exception instanceof NotFoundHttpException)) {
return;
}
$previous_exception = $exception->getPrevious();
if ($previous_exception instanceof ParamNotConvertedException) {
$route_name = $previous_exception->getRouteName();
$parameters = $previous_exception->getRawParameters();
if ($route_name === 'entity.node.canonical' && isset($parameters['node'])) {
// If the node_translation_redirect state is not set, we don't need to check
// for a redirection.
if (!$this->state->get('node_translation_redirect')) {
return;
}
$old_nid = $parameters['node'];
$collection = $this->keyValue->get('node_translation_redirect');
if ($old_nid && $value = $collection->get($old_nid)) {
list($nid, $langcode) = $value;
$language = $this->languageManager->getLanguage($langcode);
$url = $this->urlGenerator->generateFromRoute('entity.node.canonical', ['node' => $nid], ['language' => $language]);
$response = new RedirectResponse($url, 301);
$event->setResponse($response);
}
}
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events = [];
$events[KernelEvents::EXCEPTION] = ['onException'];
return $events;
}
}

View file

@ -0,0 +1,113 @@
<?php
namespace Drupal\node\EventSubscriber;
use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
use Drupal\Core\State\StateInterface;
use Drupal\migrate\Event\EventBase;
use Drupal\migrate\Event\MigrateEvents;
use Drupal\migrate\Event\MigrateImportEvent;
use Drupal\migrate\Event\MigratePostRowSaveEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Creates a key value collection for migrated node translation redirections.
*
* If we are migrating node translations from Drupal 6 or 7, these nodes will be
* combined with their source node. Since there still might be references to the
* URLs of these now consolidated nodes, this service saves the mapping between
* the old nids to the new ones to be able to redirect them to the right node in
* the right language.
*
* The mapping is stored in the "node_translation_redirect" key/value collection
* and the redirection is made by the NodeTranslationExceptionSubscriber class.
*
* @see \Drupal\node\NodeServiceProvider
* @see \Drupal\node\EventSubscriber\NodeTranslationExceptionSubscriber
*/
class NodeTranslationMigrateSubscriber implements EventSubscriberInterface {
/**
* The key value factory.
*
* @var \Drupal\Core\KeyValueStore\KeyValueFactoryInterface
*/
protected $keyValue;
/**
* The state service.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* Constructs the NodeTranslationMigrateSubscriber.
*
* @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value
* The key value factory.
* @param \Drupal\Core\State\StateInterface $state
* The state service.
*/
public function __construct(KeyValueFactoryInterface $key_value, StateInterface $state) {
$this->keyValue = $key_value;
$this->state = $state;
}
/**
* Helper method to check if we are migrating translated nodes.
*
* @param \Drupal\migrate\Event\EventBase $event
* The migrate event.
*
* @return bool
* True if we are migrating translated nodes, false otherwise.
*/
protected function isNodeTranslationsMigration(EventBase $event) {
$migration = $event->getMigration();
$source_configuration = $migration->getSourceConfiguration();
$destination_configuration = $migration->getDestinationConfiguration();
return !empty($source_configuration['translations']) && $destination_configuration['plugin'] === 'entity:node';
}
/**
* Maps the old nid to the new one in the key value collection.
*
* @param \Drupal\migrate\Event\MigratePostRowSaveEvent $event
* The migrate post row save event.
*/
public function onPostRowSave(MigratePostRowSaveEvent $event) {
if ($this->isNodeTranslationsMigration($event)) {
$row = $event->getRow();
$source = $row->getSource();
$destination = $row->getDestination();
$collection = $this->keyValue->get('node_translation_redirect');
$collection->set($source['nid'], [$destination['nid'], $destination['langcode']]);
}
}
/**
* Set the node_translation_redirect state to enable the redirections.
*
* @param \Drupal\migrate\Event\MigrateImportEvent $event
* The migrate import event.
*/
public function onPostImport(MigrateImportEvent $event) {
if ($this->isNodeTranslationsMigration($event)) {
$this->state->set('node_translation_redirect', TRUE);
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events = [];
$events[MigrateEvents::POST_ROW_SAVE] = ['onPostRowSave'];
$events[MigrateEvents::POST_IMPORT] = ['onPostImport'];
return $events;
}
}

View file

@ -2,76 +2,15 @@
namespace Drupal\node\Form;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Entity\Form\DeleteMultipleForm as EntityDeleteMultipleForm;
use Drupal\Core\Url;
use Drupal\user\PrivateTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
/**
* Provides a node deletion confirmation form.
*
* @internal
*/
class DeleteMultiple extends ConfirmFormBase {
/**
* The array of nodes to delete.
*
* @var string[][]
*/
protected $nodeInfo = [];
/**
* The tempstore factory.
*
* @var \Drupal\user\PrivateTempStoreFactory
*/
protected $tempStoreFactory;
/**
* The node storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $manager;
/**
* Constructs a DeleteMultiple form object.
*
* @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
* The tempstore factory.
* @param \Drupal\Core\Entity\EntityManagerInterface $manager
* The entity manager.
*/
public function __construct(PrivateTempStoreFactory $temp_store_factory, EntityManagerInterface $manager) {
$this->tempStoreFactory = $temp_store_factory;
$this->storage = $manager->getStorage('node');
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('user.private_tempstore'),
$container->get('entity.manager')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'node_multiple_delete_confirm';
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->formatPlural(count($this->nodeInfo), 'Are you sure you want to delete this item?', 'Are you sure you want to delete these items?');
}
class DeleteMultiple extends EntityDeleteMultipleForm {
/**
* {@inheritdoc}
@ -83,117 +22,15 @@ class DeleteMultiple extends ConfirmFormBase {
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return t('Delete');
protected function getDeletedMessage($count) {
return $this->formatPlural($count, 'Deleted @count content item.', 'Deleted @count content items.');
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$this->nodeInfo = $this->tempStoreFactory->get('node_multiple_delete_confirm')->get(\Drupal::currentUser()->id());
if (empty($this->nodeInfo)) {
return new RedirectResponse($this->getCancelUrl()->setAbsolute()->toString());
}
/** @var \Drupal\node\NodeInterface[] $nodes */
$nodes = $this->storage->loadMultiple(array_keys($this->nodeInfo));
$items = [];
foreach ($this->nodeInfo as $id => $langcodes) {
foreach ($langcodes as $langcode) {
$node = $nodes[$id]->getTranslation($langcode);
$key = $id . ':' . $langcode;
$default_key = $id . ':' . $node->getUntranslated()->language()->getId();
// If we have a translated entity we build a nested list of translations
// that will be deleted.
$languages = $node->getTranslationLanguages();
if (count($languages) > 1 && $node->isDefaultTranslation()) {
$names = [];
foreach ($languages as $translation_langcode => $language) {
$names[] = $language->getName();
unset($items[$id . ':' . $translation_langcode]);
}
$items[$default_key] = [
'label' => [
'#markup' => $this->t('@label (Original translation) - <em>The following content translations will be deleted:</em>', ['@label' => $node->label()]),
],
'deleted_translations' => [
'#theme' => 'item_list',
'#items' => $names,
],
];
}
elseif (!isset($items[$default_key])) {
$items[$key] = $node->label();
}
}
}
$form['nodes'] = [
'#theme' => 'item_list',
'#items' => $items,
];
$form = parent::buildForm($form, $form_state);
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
if ($form_state->getValue('confirm') && !empty($this->nodeInfo)) {
$total_count = 0;
$delete_nodes = [];
/** @var \Drupal\Core\Entity\ContentEntityInterface[][] $delete_translations */
$delete_translations = [];
/** @var \Drupal\node\NodeInterface[] $nodes */
$nodes = $this->storage->loadMultiple(array_keys($this->nodeInfo));
foreach ($this->nodeInfo as $id => $langcodes) {
foreach ($langcodes as $langcode) {
$node = $nodes[$id]->getTranslation($langcode);
if ($node->isDefaultTranslation()) {
$delete_nodes[$id] = $node;
unset($delete_translations[$id]);
$total_count += count($node->getTranslationLanguages());
}
elseif (!isset($delete_nodes[$id])) {
$delete_translations[$id][] = $node;
}
}
}
if ($delete_nodes) {
$this->storage->delete($delete_nodes);
$this->logger('content')->notice('Deleted @count posts.', ['@count' => count($delete_nodes)]);
}
if ($delete_translations) {
$count = 0;
foreach ($delete_translations as $id => $translations) {
$node = $nodes[$id]->getUntranslated();
foreach ($translations as $translation) {
$node->removeTranslation($translation->language()->getId());
}
$node->save();
$count += count($translations);
}
if ($count) {
$total_count += $count;
$this->logger('content')->notice('Deleted @count content translations.', ['@count' => $count]);
}
}
if ($total_count) {
drupal_set_message($this->formatPlural($total_count, 'Deleted 1 post.', 'Deleted @count posts.'));
}
$this->tempStoreFactory->get('node_multiple_delete_confirm')->delete(\Drupal::currentUser()->id());
}
$form_state->setRedirect('system.admin_content');
protected function getInaccessibleMessage($count) {
return $this->formatPlural($count, "@count content item has not been deleted because you do not have the necessary permissions.", "@count content items have not been deleted because you do not have the necessary permissions.");
}
}

View file

@ -6,6 +6,8 @@ use Drupal\Core\Entity\ContentEntityDeleteForm;
/**
* Provides a form for deleting a node.
*
* @internal
*/
class NodeDeleteForm extends ContentEntityDeleteForm {

View file

@ -12,6 +12,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Contains a form for switching the view mode of a node during preview.
*
* @internal
*/
class NodePreviewForm extends FormBase {
@ -105,7 +107,7 @@ class NodePreviewForm extends FormBase {
'#default_value' => $view_mode,
'#attributes' => [
'data-drupal-autosubmit' => TRUE,
]
],
];
$form['submit'] = [

View file

@ -11,6 +11,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a form for reverting a node revision.
*
* @internal
*/
class NodeRevisionDeleteForm extends ConfirmFormBase {
@ -116,7 +118,12 @@ class NodeRevisionDeleteForm extends ConfirmFormBase {
$this->logger('content')->notice('@type: deleted %title revision %revision.', ['@type' => $this->revision->bundle(), '%title' => $this->revision->label(), '%revision' => $this->revision->getRevisionId()]);
$node_type = $this->nodeTypeStorage->load($this->revision->bundle())->label();
drupal_set_message(t('Revision from %revision-date of @type %title has been deleted.', ['%revision-date' => format_date($this->revision->getRevisionCreationTime()), '@type' => $node_type, '%title' => $this->revision->label()]));
$this->messenger()
->addStatus($this->t('Revision from %revision-date of @type %title has been deleted.', [
'%revision-date' => format_date($this->revision->getRevisionCreationTime()),
'@type' => $node_type,
'%title' => $this->revision->label(),
]));
$form_state->setRedirect(
'entity.node.canonical',
['node' => $this->revision->id()]

View file

@ -2,6 +2,7 @@
namespace Drupal\node\Form;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Form\ConfirmFormBase;
@ -12,6 +13,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a form for reverting a node revision.
*
* @internal
*/
class NodeRevisionRevertForm extends ConfirmFormBase {
@ -25,7 +28,7 @@ class NodeRevisionRevertForm extends ConfirmFormBase {
/**
* The node storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
* @var \Drupal\node\NodeStorageInterface
*/
protected $nodeStorage;
@ -50,11 +53,13 @@ class NodeRevisionRevertForm extends ConfirmFormBase {
* The node storage.
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
* The date formatter service.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time service.
*/
public function __construct(EntityStorageInterface $node_storage, DateFormatterInterface $date_formatter) {
public function __construct(EntityStorageInterface $node_storage, DateFormatterInterface $date_formatter, TimeInterface $time) {
$this->nodeStorage = $node_storage;
$this->dateFormatter = $date_formatter;
$this->time = \Drupal::service('datetime.time');
$this->time = $time;
}
/**
@ -63,7 +68,8 @@ class NodeRevisionRevertForm extends ConfirmFormBase {
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.manager')->getStorage('node'),
$container->get('date.formatter')
$container->get('date.formatter'),
$container->get('datetime.time')
);
}
@ -122,12 +128,18 @@ class NodeRevisionRevertForm extends ConfirmFormBase {
$this->revision = $this->prepareRevertedRevision($this->revision, $form_state);
$this->revision->revision_log = t('Copy of the revision from %date.', ['%date' => $this->dateFormatter->format($original_revision_timestamp)]);
$this->revision->setRevisionUserId($this->currentUser()->id());
$this->revision->setRevisionCreationTime($this->time->getRequestTime());
$this->revision->setChangedTime($this->time->getRequestTime());
$this->revision->save();
$this->logger('content')->notice('@type: reverted %title revision %revision.', ['@type' => $this->revision->bundle(), '%title' => $this->revision->label(), '%revision' => $this->revision->getRevisionId()]);
drupal_set_message(t('@type %title has been reverted to the revision from %revision-date.', ['@type' => node_get_type_label($this->revision), '%title' => $this->revision->label(), '%revision-date' => $this->dateFormatter->format($original_revision_timestamp)]));
$this->messenger()
->addStatus($this->t('@type %title has been reverted to the revision from %revision-date.', [
'@type' => node_get_type_label($this->revision),
'%title' => $this->revision->label(),
'%revision-date' => $this->dateFormatter->format($original_revision_timestamp),
]));
$form_state->setRedirect(
'entity.node.version_history',
['node' => $this->revision->id()]

View file

@ -2,6 +2,7 @@
namespace Drupal\node\Form;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Form\FormStateInterface;
@ -11,6 +12,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a form for reverting a node revision for a single translation.
*
* @internal
*/
class NodeRevisionRevertTranslationForm extends NodeRevisionRevertForm {
@ -37,9 +40,11 @@ class NodeRevisionRevertTranslationForm extends NodeRevisionRevertForm {
* The date formatter service.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time service.
*/
public function __construct(EntityStorageInterface $node_storage, DateFormatterInterface $date_formatter, LanguageManagerInterface $language_manager) {
parent::__construct($node_storage, $date_formatter);
public function __construct(EntityStorageInterface $node_storage, DateFormatterInterface $date_formatter, LanguageManagerInterface $language_manager, TimeInterface $time) {
parent::__construct($node_storage, $date_formatter, $time);
$this->languageManager = $language_manager;
}
@ -50,7 +55,8 @@ class NodeRevisionRevertTranslationForm extends NodeRevisionRevertForm {
return new static(
$container->get('entity.manager')->getStorage('node'),
$container->get('date.formatter'),
$container->get('language_manager')
$container->get('language_manager'),
$container->get('datetime.time')
);
}
@ -82,10 +88,15 @@ class NodeRevisionRevertTranslationForm extends NodeRevisionRevertForm {
$this->langcode = $langcode;
$form = parent::buildForm($form, $form_state, $node_revision);
// Unless untranslatable fields are configured to affect only the default
// translation, we need to ask the user whether they should be included in
// the revert process.
$default_translation_affected = $this->revision->isDefaultTranslationAffectedOnly();
$form['revert_untranslated_fields'] = [
'#type' => 'checkbox',
'#title' => $this->t('Revert content shared among translations'),
'#default_value' => FALSE,
'#default_value' => $default_translation_affected && $this->revision->getTranslation($this->langcode)->isDefaultTranslation(),
'#access' => !$default_translation_affected,
];
return $form;
@ -95,24 +106,9 @@ class NodeRevisionRevertTranslationForm extends NodeRevisionRevertForm {
* {@inheritdoc}
*/
protected function prepareRevertedRevision(NodeInterface $revision, FormStateInterface $form_state) {
$revert_untranslated_fields = $form_state->getValue('revert_untranslated_fields');
/** @var \Drupal\node\NodeInterface $default_revision */
$latest_revision = $this->nodeStorage->load($revision->id());
$latest_revision_translation = $latest_revision->getTranslation($this->langcode);
$revision_translation = $revision->getTranslation($this->langcode);
foreach ($latest_revision_translation->getFieldDefinitions() as $field_name => $definition) {
if ($definition->isTranslatable() || $revert_untranslated_fields) {
$latest_revision_translation->set($field_name, $revision_translation->get($field_name)->getValue());
}
}
$latest_revision_translation->setNewRevision();
$latest_revision_translation->isDefaultRevision(TRUE);
return $latest_revision_translation;
$revert_untranslated_fields = (bool) $form_state->getValue('revert_untranslated_fields');
$translation = $revision->getTranslation($this->langcode);
return $this->nodeStorage->createRevision($translation, TRUE, $revert_untranslated_fields);
}
}

View file

@ -7,6 +7,8 @@ use Drupal\Core\Form\FormStateInterface;
/**
* Provides a form for content type deletion.
*
* @internal
*/
class NodeTypeDeleteConfirm extends EntityDeleteForm {

View file

@ -6,6 +6,11 @@ use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
/**
* Form for rebuilding permissions.
*
* @internal
*/
class RebuildPermissionsForm extends ConfirmFormBase {
/**

View file

@ -50,7 +50,6 @@ class NodeAccessControlHandler extends EntityAccessControlHandler implements Nod
);
}
/**
* {@inheritdoc}
*/

View file

@ -4,39 +4,52 @@ namespace Drupal\node;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Entity\ContentEntityForm;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\user\PrivateTempStoreFactory;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Form handler for the node edit forms.
*
* @internal
*/
class NodeForm extends ContentEntityForm {
/**
* The tempstore factory.
*
* @var \Drupal\user\PrivateTempStoreFactory
* @var \Drupal\Core\TempStore\PrivateTempStoreFactory
*/
protected $tempStoreFactory;
/**
* The Current User object.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* Constructs a NodeForm object.
*
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
* @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
* The entity repository.
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
* The factory for the temp store object.
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
* The entity type bundle service.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time service.
* @param \Drupal\Core\Session\AccountInterface $current_user
* The current user.
*/
public function __construct(EntityManagerInterface $entity_manager, PrivateTempStoreFactory $temp_store_factory, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, TimeInterface $time = NULL) {
parent::__construct($entity_manager, $entity_type_bundle_info, $time);
public function __construct(EntityRepositoryInterface $entity_repository, PrivateTempStoreFactory $temp_store_factory, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, TimeInterface $time = NULL, AccountInterface $current_user) {
parent::__construct($entity_repository, $entity_type_bundle_info, $time);
$this->tempStoreFactory = $temp_store_factory;
$this->currentUser = $current_user;
}
/**
@ -44,10 +57,11 @@ class NodeForm extends ContentEntityForm {
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.manager'),
$container->get('user.private_tempstore'),
$container->get('entity.repository'),
$container->get('tempstore.private'),
$container->get('entity_type.bundle.info'),
$container->get('datetime.time')
$container->get('datetime.time'),
$container->get('current_user')
);
}
@ -86,7 +100,10 @@ class NodeForm extends ContentEntityForm {
$node = $this->entity;
if ($this->operation == 'edit') {
$form['#title'] = $this->t('<em>Edit @type</em> @title', ['@type' => node_get_type_label($node), '@title' => $node->label()]);
$form['#title'] = $this->t('<em>Edit @type</em> @title', [
'@type' => node_get_type_label($node),
'@title' => $node->label(),
]);
}
// Changed must be sent to the client, for later overwrite error checking.
@ -99,6 +116,36 @@ class NodeForm extends ContentEntityForm {
$form['advanced']['#attributes']['class'][] = 'entity-meta';
$form['meta'] = [
'#type' => 'details',
'#group' => 'advanced',
'#weight' => -10,
'#title' => $this->t('Status'),
'#attributes' => ['class' => ['entity-meta__header']],
'#tree' => TRUE,
'#access' => $this->currentUser->hasPermission('administer nodes'),
];
$form['meta']['published'] = [
'#type' => 'item',
'#markup' => $node->isPublished() ? $this->t('Published') : $this->t('Not published'),
'#access' => !$node->isNew(),
'#wrapper_attributes' => ['class' => ['entity-meta__title']],
];
$form['meta']['changed'] = [
'#type' => 'item',
'#title' => $this->t('Last saved'),
'#markup' => !$node->isNew() ? format_date($node->getChangedTime(), 'short') : $this->t('Not saved yet'),
'#wrapper_attributes' => ['class' => ['entity-meta__last-saved']],
];
$form['meta']['author'] = [
'#type' => 'item',
'#title' => $this->t('Author'),
'#markup' => $node->getOwner()->getUsername(),
'#wrapper_attributes' => ['class' => ['entity-meta__author']],
];
$form['status']['#group'] = 'footer';
// Node author information for administrators.
$form['author'] = [
'#type' => 'details',
@ -147,8 +194,6 @@ class NodeForm extends ContentEntityForm {
$form['#attached']['library'][] = 'node/form';
$form['#entity_builders']['update_status'] = '::updateStatus';
return $form;
}
@ -165,11 +210,14 @@ class NodeForm extends ContentEntityForm {
* The current state of the form.
*
* @see \Drupal\node\NodeForm::form()
*
* @deprecated in Drupal 8.4.x, will be removed before Drupal 9.0.0.
* The "Publish" button was removed.
*/
public function updateStatus($entity_type_id, NodeInterface $node, array $form, FormStateInterface $form_state) {
$element = $form_state->getTriggeringElement();
if (isset($element['#published_status'])) {
$node->setPublished($element['#published_status']);
$element['#published_status'] ? $node->setPublished() : $node->setUnpublished();
}
}
@ -183,59 +231,6 @@ class NodeForm extends ContentEntityForm {
$element['submit']['#access'] = $preview_mode != DRUPAL_REQUIRED || $form_state->get('has_been_previewed');
// If saving is an option, privileged users get dedicated form submit
// buttons to adjust the publishing status while saving in one go.
// @todo This adjustment makes it close to impossible for contributed
// modules to integrate with "the Save operation" of this form. Modules
// need a way to plug themselves into 1) the ::submit() step, and
// 2) the ::save() step, both decoupled from the pressed form button.
if ($element['submit']['#access'] && \Drupal::currentUser()->hasPermission('administer nodes')) {
// isNew | prev status » default & publish label & unpublish label
// 1 | 1 » publish & Save and publish & Save as unpublished
// 1 | 0 » unpublish & Save and publish & Save as unpublished
// 0 | 1 » publish & Save and keep published & Save and unpublish
// 0 | 0 » unpublish & Save and keep unpublished & Save and publish
// Add a "Publish" button.
$element['publish'] = $element['submit'];
// If the "Publish" button is clicked, we want to update the status to "published".
$element['publish']['#published_status'] = TRUE;
$element['publish']['#dropbutton'] = 'save';
if ($node->isNew()) {
$element['publish']['#value'] = t('Save and publish');
}
else {
$element['publish']['#value'] = $node->isPublished() ? t('Save and keep published') : t('Save and publish');
}
$element['publish']['#weight'] = 0;
// Add a "Unpublish" button.
$element['unpublish'] = $element['submit'];
// If the "Unpublish" button is clicked, we want to update the status to "unpublished".
$element['unpublish']['#published_status'] = FALSE;
$element['unpublish']['#dropbutton'] = 'save';
if ($node->isNew()) {
$element['unpublish']['#value'] = t('Save as unpublished');
}
else {
$element['unpublish']['#value'] = !$node->isPublished() ? t('Save and keep unpublished') : t('Save and unpublish');
}
$element['unpublish']['#weight'] = 10;
// If already published, the 'publish' button is primary.
if ($node->isPublished()) {
unset($element['unpublish']['#button_type']);
}
// Otherwise, the 'unpublish' button is primary and should come first.
else {
unset($element['publish']['#button_type']);
$element['unpublish']['#weight'] = -10;
}
// Remove the "Save" button.
$element['submit']['#access'] = FALSE;
}
$element['preview'] = [
'#type' => 'submit',
'#access' => $preview_mode != DRUPAL_DISABLED && ($node->access('create') || $node->access('update')),
@ -290,11 +285,11 @@ class NodeForm extends ContentEntityForm {
if ($insert) {
$this->logger('content')->notice('@type: added %title.', $context);
drupal_set_message(t('@type %title has been created.', $t_args));
$this->messenger()->addStatus($this->t('@type %title has been created.', $t_args));
}
else {
$this->logger('content')->notice('@type: updated %title.', $context);
drupal_set_message(t('@type %title has been updated.', $t_args));
$this->messenger()->addStatus($this->t('@type %title has been updated.', $t_args));
}
if ($node->id()) {
@ -317,7 +312,7 @@ class NodeForm extends ContentEntityForm {
else {
// In the unlikely case something went wrong on save, the node will be
// rebuilt and node form redisplayed the same way as in preview.
drupal_set_message(t('The post could not be saved.'), 'error');
$this->messenger()->addError($this->t('The post could not be saved.'));
$form_state->setRebuild();
}
}

View file

@ -138,7 +138,7 @@ class NodeGrantDatabaseStorage implements NodeGrantDatabaseStorageInterface {
$grants = static::buildGrantsQueryCondition(node_access_grants('view', $account));
if (count($grants) > 0 ) {
if (count($grants) > 0) {
$query->condition($grants);
}
return $query->execute()->fetchField();
@ -211,6 +211,7 @@ class NodeGrantDatabaseStorage implements NodeGrantDatabaseStorageInterface {
$query = $this->database->insert('node_access')->fields(['nid', 'langcode', 'fallback', 'realm', 'gid', 'grant_view', 'grant_update', 'grant_delete']);
// If we have defined a granted langcode, use it. But if not, add a grant
// for every language this node is translated to.
$fallback_langcode = $node->getUntranslated()->language()->getId();
foreach ($grants as $grant) {
if ($realm && $realm != $grant['realm']) {
continue;
@ -227,7 +228,7 @@ class NodeGrantDatabaseStorage implements NodeGrantDatabaseStorageInterface {
$grant['nid'] = $node->id();
$grant['langcode'] = $grant_langcode;
// The record with the original langcode is used as the fallback.
if ($grant['langcode'] == $node->language()->getId()) {
if ($grant['langcode'] == $fallback_langcode) {
$grant['fallback'] = 1;
}
else {

View file

@ -31,11 +31,11 @@ interface NodeGrantDatabaseStorageInterface {
* @param array $tables
* A list of tables that need to be part of the alter.
* @param string $op
* The operation to be performed on the node. Possible values are:
* - "view"
* - "update"
* - "delete"
* - "create"
* The operation to be performed on the node. Possible values are:
* - "view"
* - "update"
* - "delete"
* - "create"
* @param \Drupal\Core\Session\AccountInterface $account
* A user object representing the user for whom the operation is to be
* performed.

View file

@ -25,13 +25,6 @@ class NodeListBuilder extends EntityListBuilder {
*/
protected $dateFormatter;
/**
* The redirect destination service.
*
* @var \Drupal\Core\Routing\RedirectDestinationInterface
*/
protected $redirectDestination;
/**
* Constructs a new NodeListBuilder object.
*
@ -110,7 +103,7 @@ class NodeListBuilder extends EntityListBuilder {
$row['title']['data'] = [
'#type' => 'link',
'#title' => $entity->label(),
'#suffix' => ' ' . drupal_render($mark),
'#suffix' => ' ' . \Drupal::service('renderer')->render($mark),
'#url' => $uri,
];
$row['type'] = node_get_type_label($entity);
@ -128,17 +121,4 @@ class NodeListBuilder extends EntityListBuilder {
return $row + parent::buildRow($entity);
}
/**
* {@inheritdoc}
*/
protected function getDefaultOperations(EntityInterface $entity) {
$operations = parent::getDefaultOperations($entity);
$destination = $this->redirectDestination->getAsArray();
foreach ($operations as $key => $operation) {
$operations[$key]['query'] = $destination;
}
return $operations;
}
}

View file

@ -62,14 +62,15 @@ class NodePermissions {
],
"view $type_id revisions" => [
'title' => $this->t('%type_name: View revisions', $type_params),
'description' => t('To view a revision, you also need permission to view the content item.'),
],
"revert $type_id revisions" => [
'title' => $this->t('%type_name: Revert revisions', $type_params),
'description' => t('Role requires permission <em>view revisions</em> and <em>edit rights</em> for nodes in question, or <em>administer nodes</em>.'),
'description' => t('To revert a revision, you also need permission to edit the content item.'),
],
"delete $type_id revisions" => [
'title' => $this->t('%type_name: Delete revisions', $type_params),
'description' => $this->t('Role requires permission to <em>view revisions</em> and <em>delete rights</em> for nodes in question, or <em>administer nodes</em>.'),
'description' => $this->t('To delete a revision, you also need permission to delete the content item.'),
],
];
}

View file

@ -0,0 +1,42 @@
<?php
namespace Drupal\node;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceProviderInterface;
use Drupal\node\EventSubscriber\NodeTranslationExceptionSubscriber;
use Drupal\node\EventSubscriber\NodeTranslationMigrateSubscriber;
use Symfony\Component\DependencyInjection\Reference;
/**
* Registers services in the container.
*/
class NodeServiceProvider implements ServiceProviderInterface {
/**
* {@inheritdoc}
*/
public function register(ContainerBuilder $container) {
// Register the node.node_translation_migrate service in the container if
// the migrate and language modules are enabled.
$modules = $container->getParameter('container.modules');
if (isset($modules['migrate']) && isset($modules['language'])) {
$container->register('node.node_translation_migrate', NodeTranslationMigrateSubscriber::class)
->addTag('event_subscriber')
->addArgument(new Reference('keyvalue'))
->addArgument(new Reference('state'));
}
// Register the node.node_translation_exception service in the container if
// the language module is enabled.
if (isset($modules['language'])) {
$container->register('node.node_translation_exception', NodeTranslationExceptionSubscriber::class)
->addTag('event_subscriber')
->addArgument(new Reference('keyvalue'))
->addArgument(new Reference('language_manager'))
->addArgument(new Reference('url_generator'))
->addArgument(new Reference('state'));
}
}
}

View file

@ -19,7 +19,7 @@ class NodeStorage extends SqlContentEntityStorage implements NodeStorageInterfac
*/
public function revisionIds(NodeInterface $node) {
return $this->database->query(
'SELECT vid FROM {node_revision} WHERE nid=:nid ORDER BY vid',
'SELECT vid FROM {' . $this->getRevisionTable() . '} WHERE nid=:nid ORDER BY vid',
[':nid' => $node->id()]
)->fetchCol();
}
@ -29,7 +29,7 @@ class NodeStorage extends SqlContentEntityStorage implements NodeStorageInterfac
*/
public function userRevisionIds(AccountInterface $account) {
return $this->database->query(
'SELECT vid FROM {node_field_revision} WHERE uid = :uid ORDER BY vid',
'SELECT vid FROM {' . $this->getRevisionDataTable() . '} WHERE uid = :uid ORDER BY vid',
[':uid' => $account->id()]
)->fetchCol();
}
@ -38,14 +38,14 @@ class NodeStorage extends SqlContentEntityStorage implements NodeStorageInterfac
* {@inheritdoc}
*/
public function countDefaultLanguageRevisions(NodeInterface $node) {
return $this->database->query('SELECT COUNT(*) FROM {node_field_revision} WHERE nid = :nid AND default_langcode = 1', [':nid' => $node->id()])->fetchField();
return $this->database->query('SELECT COUNT(*) FROM {' . $this->getRevisionDataTable() . '} WHERE nid = :nid AND default_langcode = 1', [':nid' => $node->id()])->fetchField();
}
/**
* {@inheritdoc}
*/
public function updateType($old_type, $new_type) {
return $this->database->update('node')
return $this->database->update($this->getBaseTable())
->fields(['type' => $new_type])
->condition('type', $old_type)
->execute();
@ -55,7 +55,7 @@ class NodeStorage extends SqlContentEntityStorage implements NodeStorageInterfac
* {@inheritdoc}
*/
public function clearRevisionsLanguage(LanguageInterface $language) {
return $this->database->update('node_revision')
return $this->database->update($this->getRevisionTable())
->fields(['langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED])
->condition('langcode', $language->getId())
->execute();

View file

@ -17,10 +17,12 @@ class NodeStorageSchema extends SqlContentEntityStorageSchema {
protected function getEntitySchema(ContentEntityTypeInterface $entity_type, $reset = FALSE) {
$schema = parent::getEntitySchema($entity_type, $reset);
$schema['node_field_data']['indexes'] += [
'node__frontpage' => ['promote', 'status', 'sticky', 'created'],
'node__title_type' => ['title', ['type', 4]],
];
if ($data_table = $this->storage->getDataTable()) {
$schema[$data_table]['indexes'] += [
'node__frontpage' => ['promote', 'status', 'sticky', 'created'],
'node__title_type' => ['title', ['type', 4]],
];
}
return $schema;
}

View file

@ -39,10 +39,8 @@ class NodeTranslationHandler extends ContentTranslationHandler {
}
}
if (isset($status_translatable)) {
foreach (['publish', 'unpublish', 'submit'] as $button) {
if (isset($form['actions'][$button])) {
$form['actions'][$button]['#value'] .= ' ' . ($status_translatable ? t('(this translation)') : t('(all translations)'));
}
if (isset($form['actions']['submit'])) {
$form['actions']['submit']['#value'] .= ' ' . ($status_translatable ? t('(this translation)') : t('(all translations)'));
}
}
}

View file

@ -11,6 +11,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Form handler for node type forms.
*
* @internal
*/
class NodeTypeForm extends BundleEntityFormBase {
@ -122,7 +124,7 @@ class NodeTypeForm extends BundleEntityFormBase {
DRUPAL_REQUIRED => t('Required'),
],
];
$form['submission']['help'] = [
$form['submission']['help'] = [
'#type' => 'textarea',
'#title' => t('Explanation or submission guidelines'),
'#default_value' => $type->getHelp(),
@ -223,11 +225,11 @@ class NodeTypeForm extends BundleEntityFormBase {
$t_args = ['%name' => $type->label()];
if ($status == SAVED_UPDATED) {
drupal_set_message(t('The content type %name has been updated.', $t_args));
$this->messenger()->addStatus($this->t('The content type %name has been updated.', $t_args));
}
elseif ($status == SAVED_NEW) {
node_add_body_field($type);
drupal_set_message(t('The content type %name has been added.', $t_args));
$this->messenger()->addStatus($this->t('The content type %name has been added.', $t_args));
$context = array_merge($t_args, ['link' => $type->link($this->t('View'), 'collection')]);
$this->logger('node')->notice('Added content type %name.', $context);
}

View file

@ -56,7 +56,7 @@ class NodeTypeListBuilder extends ConfigEntityListBuilder {
public function render() {
$build = parent::render();
$build['table']['#empty'] = $this->t('No content types available. <a href=":link">Add content type</a>.', [
':link' => Url::fromRoute('node.type_add')->toString()
':link' => Url::fromRoute('node.type_add')->toString(),
]);
return $build;
}

View file

@ -2,10 +2,8 @@
namespace Drupal\node;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityViewBuilder;
use Drupal\node\Entity\Node;
/**
* View builder handler for nodes.
@ -29,12 +27,15 @@ class NodeViewBuilder extends EntityViewBuilder {
if ($display->getComponent('links')) {
$build[$id]['links'] = [
'#lazy_builder' => [get_called_class() . '::renderLinks', [
$entity->id(),
$view_mode,
$entity->language()->getId(),
!empty($entity->in_preview),
]],
'#lazy_builder' => [
get_called_class() . '::renderLinks', [
$entity->id(),
$view_mode,
$entity->language()->getId(),
!empty($entity->in_preview),
$entity->isDefaultRevision() ? NULL : $entity->getLoadedRevisionId(),
],
],
];
}
@ -45,7 +46,7 @@ class NodeViewBuilder extends EntityViewBuilder {
'#title' => t('Language'),
'#markup' => $entity->language()->getName(),
'#prefix' => '<div id="field-language-display">',
'#suffix' => '</div>'
'#suffix' => '</div>',
];
}
}
@ -76,11 +77,14 @@ class NodeViewBuilder extends EntityViewBuilder {
* The language in which the node entity is being viewed.
* @param bool $is_in_preview
* Whether the node is currently being previewed.
* @param $revision_id
* (optional) The identifier of the node revision to be loaded. If none
* is provided, the default revision will be loaded.
*
* @return array
* A renderable array representing the node links.
*/
public static function renderLinks($node_entity_id, $view_mode, $langcode, $is_in_preview) {
public static function renderLinks($node_entity_id, $view_mode, $langcode, $is_in_preview, $revision_id = NULL) {
$links = [
'#theme' => 'links__node',
'#pre_render' => ['drupal_pre_render_links'],
@ -88,7 +92,10 @@ class NodeViewBuilder extends EntityViewBuilder {
];
if (!$is_in_preview) {
$entity = Node::load($node_entity_id)->getTranslation($langcode);
$storage = \Drupal::entityTypeManager()->getStorage('node');
/** @var \Drupal\node\NodeInterface $revision */
$revision = !isset($revision_id) ? $storage->load($node_entity_id) : $storage->loadRevision($revision_id);
$entity = $revision->getTranslation($langcode);
$links['node'] = static::buildLinks($entity, $view_mode);
// Allow other modules to alter the node links.
@ -139,29 +146,4 @@ class NodeViewBuilder extends EntityViewBuilder {
];
}
/**
* {@inheritdoc}
*/
protected function alterBuild(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
/** @var \Drupal\node\NodeInterface $entity */
parent::alterBuild($build, $entity, $display, $view_mode);
if ($entity->id()) {
if ($entity->isDefaultRevision()) {
$build['#contextual_links']['node'] = [
'route_parameters' => ['node' => $entity->id()],
'metadata' => ['changed' => $entity->getChangedTime()],
];
}
else {
$build['#contextual_links']['node_revision'] = [
'route_parameters' => [
'node' => $entity->id(),
'node_revision' => $entity->getRevisionId(),
],
'metadata' => ['changed' => $entity->getChangedTime()],
];
}
}
}
}

View file

@ -58,14 +58,6 @@ class NodeViewsData extends EntityViewsData {
$data['node_field_data']['sticky']['filter']['type'] = 'yes-no';
$data['node_field_data']['sticky']['sort']['help'] = $this->t('Whether or not the content is sticky. To list sticky content first, set this to descending.');
$data['node']['path'] = [
'field' => [
'title' => $this->t('Path'),
'help' => $this->t('The aliased path to this content.'),
'id' => 'node_path',
],
];
$data['node']['node_bulk_form'] = [
'title' => $this->t('Node operations bulk form'),
'help' => $this->t('Add a form element that lets you run operations on multiple nodes.'),
@ -288,7 +280,7 @@ class NodeViewsData extends EntityViewsData {
// Define the base group of this table. Fields that don't have a group defined
// will go into this field by default.
$data['node_access']['table']['group'] = $this->t('Content access');
$data['node_access']['table']['group'] = $this->t('Content access');
// For other base tables, explain how we join.
$data['node_access']['table']['join'] = [
@ -330,7 +322,7 @@ class NodeViewsData extends EntityViewsData {
'field' => 'sid',
'table' => 'search_index',
'extra' => "node_search_index.type = 'node_search' AND node_search_index.langcode = node_field_data.langcode",
]
],
];
$data['node_search_total']['table']['join'] = [

View file

@ -2,7 +2,7 @@
namespace Drupal\node\ParamConverter;
use Drupal\user\PrivateTempStoreFactory;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Symfony\Component\Routing\Route;
use Drupal\Core\ParamConverter\ParamConverterInterface;
@ -14,14 +14,14 @@ class NodePreviewConverter implements ParamConverterInterface {
/**
* Stores the tempstore factory.
*
* @var \Drupal\user\PrivateTempStoreFactory
* @var \Drupal\Core\TempStore\PrivateTempStoreFactory
*/
protected $tempStoreFactory;
/**
* Constructs a new NodePreviewConverter.
*
* @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $temp_store_factory
* The factory for the temp store object.
*/
public function __construct(PrivateTempStoreFactory $temp_store_factory) {

View file

@ -2,98 +2,33 @@
namespace Drupal\node\Plugin\Action;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Action\Plugin\Action\DeleteAction;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\PrivateTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
/**
* Redirects to a node deletion form.
*
* @deprecated in Drupal 8.6.x, to be removed before Drupal 9.0.0.
* Use \Drupal\Core\Action\Plugin\Action\DeleteAction instead.
*
* @see \Drupal\Core\Action\Plugin\Action\DeleteAction
* @see https://www.drupal.org/node/2934349
*
* @Action(
* id = "node_delete_action",
* label = @Translation("Delete content"),
* type = "node",
* confirm_form_route_name = "node.multiple_delete_confirm"
* label = @Translation("Delete content")
* )
*/
class DeleteNode extends ActionBase implements ContainerFactoryPluginInterface {
/**
* The tempstore object.
*
* @var \Drupal\user\SharedTempStore
*/
protected $tempStore;
/**
* The current user.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* Constructs a new DeleteNode object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin ID for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
* The tempstore factory.
* @param AccountInterface $current_user
* Current user.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, PrivateTempStoreFactory $temp_store_factory, AccountInterface $current_user) {
$this->currentUser = $current_user;
$this->tempStore = $temp_store_factory->get('node_multiple_delete_confirm');
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
class DeleteNode extends DeleteAction {
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('user.private_tempstore'),
$container->get('current_user')
);
}
/**
* {@inheritdoc}
*/
public function executeMultiple(array $entities) {
$info = [];
/** @var \Drupal\node\NodeInterface $node */
foreach ($entities as $node) {
$langcode = $node->language()->getId();
$info[$node->id()][$langcode] = $langcode;
}
$this->tempStore->set($this->currentUser->id(), $info);
}
/**
* {@inheritdoc}
*/
public function execute($object = NULL) {
$this->executeMultiple([$object]);
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
/** @var \Drupal\node\NodeInterface $object */
return $object->access('delete', $account, $return_as_object);
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, PrivateTempStoreFactory $temp_store_factory, AccountInterface $current_user) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager, $temp_store_factory, $current_user);
@trigger_error(__NAMESPACE__ . '\DeleteNode is deprecated in Drupal 8.6.x, will be removed before Drupal 9.0.0. Use \Drupal\Core\Action\Plugin\Action\DeleteAction instead. See https://www.drupal.org/node/2934349.', E_USER_DEPRECATED);
}
}

View file

@ -2,8 +2,8 @@
namespace Drupal\node\Plugin\Action;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Field\FieldUpdateActionBase;
use Drupal\node\NodeInterface;
/**
* Demotes a node.
@ -14,25 +14,13 @@ use Drupal\Core\Session\AccountInterface;
* type = "node"
* )
*/
class DemoteNode extends ActionBase {
class DemoteNode extends FieldUpdateActionBase {
/**
* {@inheritdoc}
*/
public function execute($entity = NULL) {
$entity->setPromoted(FALSE);
$entity->save();
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
/** @var \Drupal\node\NodeInterface $object */
$result = $object->access('update', $account, TRUE)
->andIf($object->promote->access('edit', $account, TRUE));
return $return_as_object ? $result : $result->isAllowed();
protected function getFieldsToUpdate() {
return ['promote' => NodeInterface::NOT_PROMOTED];
}
}

View file

@ -2,8 +2,8 @@
namespace Drupal\node\Plugin\Action;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Field\FieldUpdateActionBase;
use Drupal\node\NodeInterface;
/**
* Promotes a node.
@ -14,24 +14,13 @@ use Drupal\Core\Session\AccountInterface;
* type = "node"
* )
*/
class PromoteNode extends ActionBase {
class PromoteNode extends FieldUpdateActionBase {
/**
* {@inheritdoc}
*/
public function execute($entity = NULL) {
$entity->setPromoted(TRUE);
$entity->save();
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
/** @var \Drupal\node\NodeInterface $object */
$access = $object->access('update', $account, TRUE)
->andif($object->promote->access('edit', $account, TRUE));
return $return_as_object ? $access : $access->isAllowed();
protected function getFieldsToUpdate() {
return ['promote' => NodeInterface::PROMOTED];
}
}

View file

@ -2,36 +2,32 @@
namespace Drupal\node\Plugin\Action;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Action\Plugin\Action\PublishAction;
use Drupal\Core\Entity\EntityTypeManagerInterface;
/**
* Publishes a node.
*
* @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
* Use \Drupal\Core\Action\Plugin\Action\PublishAction instead.
*
* @see \Drupal\Core\Action\Plugin\Action\PublishAction
* @see https://www.drupal.org/node/2919303
*
* @Action(
* id = "node_publish_action",
* label = @Translation("Publish selected content"),
* type = "node"
* )
*/
class PublishNode extends ActionBase {
class PublishNode extends PublishAction {
/**
* {@inheritdoc}
*/
public function execute($entity = NULL) {
$entity->setPublished()->save();
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
/** @var \Drupal\node\NodeInterface $object */
$result = $object->access('update', $account, TRUE)
->andIf($object->status->access('edit', $account, TRUE));
return $return_as_object ? $result : $result->isAllowed();
public function __construct($configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager);
@trigger_error(__NAMESPACE__ . '\PublishNode is deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Use \Drupal\Core\Action\Plugin\Action\PublishAction instead. See https://www.drupal.org/node/2919303.', E_USER_DEPRECATED);
}
}

View file

@ -2,36 +2,33 @@
namespace Drupal\node\Plugin\Action;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Action\Plugin\Action\SaveAction;
use Drupal\Core\Entity\EntityTypeManagerInterface;
/**
* Provides an action that can save any entity.
*
* @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
* Use \Drupal\Core\Action\Plugin\Action\SaveAction instead.
*
* @see \Drupal\Core\Action\Plugin\Action\SaveAction
* @see https://www.drupal.org/node/2919303
*
* @Action(
* id = "node_save_action",
* label = @Translation("Save content"),
* type = "node"
* )
*/
class SaveNode extends ActionBase {
class SaveNode extends SaveAction {
/**
* {@inheritdoc}
*/
public function execute($entity = NULL) {
// We need to change at least one value, otherwise the changed timestamp
// will not be updated.
$entity->changed = 0;
$entity->save();
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
/** @var \Drupal\node\NodeInterface $object */
return $object->access('update', $account, $return_as_object);
public function __construct($configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, TimeInterface $time) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager, $time);
@trigger_error(__NAMESPACE__ . '\SaveNode is deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Use \Drupal\Core\Action\Plugin\Action\SaveAction instead. See https://www.drupal.org/node/2919303.', E_USER_DEPRECATED);
}
}

View file

@ -2,8 +2,8 @@
namespace Drupal\node\Plugin\Action;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Field\FieldUpdateActionBase;
use Drupal\node\NodeInterface;
/**
* Makes a node sticky.
@ -14,23 +14,13 @@ use Drupal\Core\Session\AccountInterface;
* type = "node"
* )
*/
class StickyNode extends ActionBase {
class StickyNode extends FieldUpdateActionBase {
/**
* {@inheritdoc}
*/
public function execute($entity = NULL) {
$entity->setSticky(TRUE)->save();
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
/** @var \Drupal\node\NodeInterface $object */
$access = $object->access('update', $account, TRUE)
->andif($object->sticky->access('edit', $account, TRUE));
return $return_as_object ? $access : $access->isAllowed();
protected function getFieldsToUpdate() {
return ['sticky' => NodeInterface::STICKY];
}
}

View file

@ -24,8 +24,8 @@ class UnpublishByKeywordNode extends ConfigurableActionBase {
public function execute($node = NULL) {
foreach ($this->configuration['keywords'] as $keyword) {
$elements = node_view(clone $node);
if (strpos(drupal_render($elements), $keyword) !== FALSE || strpos($node->label(), $keyword) !== FALSE) {
$node->setPublished(FALSE);
if (strpos(\Drupal::service('renderer')->render($elements), $keyword) !== FALSE || strpos($node->label(), $keyword) !== FALSE) {
$node->setUnpublished();
$node->save();
break;
}

View file

@ -2,36 +2,32 @@
namespace Drupal\node\Plugin\Action;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Action\Plugin\Action\UnpublishAction;
use Drupal\Core\Entity\EntityTypeManagerInterface;
/**
* Unpublishes a node.
*
* @deprecated in Drupal 8.5.x, to be removed before Drupal 9.0.0.
* Use \Drupal\Core\Action\Plugin\Action\UnpublishAction instead.
*
* @see \Drupal\Core\Action\Plugin\Action\UnpublishAction
* @see https://www.drupal.org/node/2919303
*
* @Action(
* id = "node_unpublish_action",
* label = @Translation("Unpublish selected content"),
* type = "node"
* )
*/
class UnpublishNode extends ActionBase {
class UnpublishNode extends UnpublishAction {
/**
* {@inheritdoc}
*/
public function execute($entity = NULL) {
$entity->setUnpublished()->save();
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
/** @var \Drupal\node\NodeInterface $object */
$access = $object->access('update', $account, TRUE)
->andIf($object->status->access('edit', $account, TRUE));
return $return_as_object ? $access : $access->isAllowed();
public function __construct($configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager);
@trigger_error(__NAMESPACE__ . '\UnpublishNode is deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0. Use \Drupal\Core\Action\Plugin\Action\UnpublishAction instead. See https://www.drupal.org/node/2919303.', E_USER_DEPRECATED);
}
}

View file

@ -2,8 +2,8 @@
namespace Drupal\node\Plugin\Action;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Field\FieldUpdateActionBase;
use Drupal\node\NodeInterface;
/**
* Makes a node not sticky.
@ -14,24 +14,13 @@ use Drupal\Core\Session\AccountInterface;
* type = "node"
* )
*/
class UnstickyNode extends ActionBase {
class UnstickyNode extends FieldUpdateActionBase {
/**
* {@inheritdoc}
*/
public function execute($entity = NULL) {
$entity->setSticky(FALSE)->save();
}
/**
* {@inheritdoc}
*/
public function access($object, AccountInterface $account = NULL, $return_as_object = FALSE) {
/** @var \Drupal\node\NodeInterface $object */
$access = $object->access('update', $account, TRUE)
->andIf($object->sticky->access('edit', $account, TRUE));
return $return_as_object ? $access : $access->isAllowed();
protected function getFieldsToUpdate() {
return ['sticky' => NodeInterface::NOT_STICKY];
}
}

View file

@ -3,7 +3,6 @@
namespace Drupal\node\Plugin\EntityReferenceSelection;
use Drupal\Core\Entity\Plugin\EntityReferenceSelection\DefaultSelection;
use Drupal\Core\Form\FormStateInterface;
use Drupal\node\NodeInterface;
/**
@ -19,15 +18,6 @@ use Drupal\node\NodeInterface;
*/
class NodeSelection extends DefaultSelection {
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form = parent::buildConfigurationForm($form, $form_state);
$form['target_bundles']['#title'] = $this->t('Content types');
return $form;
}
/**
* {@inheritdoc}
*/
@ -52,7 +42,7 @@ class NodeSelection extends DefaultSelection {
// In order to create a referenceable node, it needs to published.
/** @var \Drupal\node\NodeInterface $node */
$node->setPublished(TRUE);
$node->setPublished();
return $node;
}

View file

@ -13,6 +13,7 @@ use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Access\AccessibleInterface;
use Drupal\Core\Database\Query\Condition;
@ -114,10 +115,17 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
*/
const ADVANCED_FORM = 'advanced-form';
/**
* The messenger.
*
* @var \Drupal\Core\Messenger\MessengerInterface
*/
protected $messenger;
/**
* {@inheritdoc}
*/
static public function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
@ -128,6 +136,7 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
$container->get('config.factory')->get('search.settings'),
$container->get('language_manager'),
$container->get('renderer'),
$container->get('messenger'),
$container->get('current_user')
);
}
@ -153,16 +162,19 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
* The language manager.
* @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer.
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
* The messenger.
* @param \Drupal\Core\Session\AccountInterface $account
* The $account object to use for checking for access to advanced search.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, Connection $database, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, Config $search_settings, LanguageManagerInterface $language_manager, RendererInterface $renderer, AccountInterface $account = NULL) {
public function __construct(array $configuration, $plugin_id, $plugin_definition, Connection $database, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, Config $search_settings, LanguageManagerInterface $language_manager, RendererInterface $renderer, MessengerInterface $messenger, AccountInterface $account = NULL) {
$this->database = $database;
$this->entityManager = $entity_manager;
$this->moduleHandler = $module_handler;
$this->searchSettings = $search_settings;
$this->languageManager = $language_manager;
$this->renderer = $renderer;
$this->messenger = $messenger;
$this->account = $account;
parent::__construct($configuration, $plugin_id, $plugin_definition);
@ -289,15 +301,15 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
$status = $query->getStatus();
if ($status & SearchQuery::EXPRESSIONS_IGNORED) {
drupal_set_message($this->t('Your search used too many AND/OR expressions. Only the first @count terms were included in this search.', ['@count' => $this->searchSettings->get('and_or_limit')]), 'warning');
$this->messenger->addWarning($this->t('Your search used too many AND/OR expressions. Only the first @count terms were included in this search.', ['@count' => $this->searchSettings->get('and_or_limit')]));
}
if ($status & SearchQuery::LOWER_CASE_OR) {
drupal_set_message($this->t('Search for either of the two terms with uppercase <strong>OR</strong>. For example, <strong>cats OR dogs</strong>.'), 'warning');
$this->messenger->addWarning($this->t('Search for either of the two terms with uppercase <strong>OR</strong>. For example, <strong>cats OR dogs</strong>.'));
}
if ($status & SearchQuery::NO_POSITIVE_KEYWORDS) {
drupal_set_message($this->formatPlural($this->searchSettings->get('index.minimum_word_size'), 'You must include at least one keyword to match in the content, and punctuation is ignored.', 'You must include at least one keyword to match in the content. Keywords must be at least @count characters, and punctuation is ignored.'), 'warning');
$this->messenger->addWarning($this->formatPlural($this->searchSettings->get('index.minimum_word_size'), 'You must include at least one keyword to match in the content, and punctuation is ignored.', 'You must include at least one keyword to match in the content. Keywords must be at least @count characters, and punctuation is ignored.'));
}
return $find;
@ -475,7 +487,7 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
'#prefix' => '<h1>',
'#plain_text' => $node->label(),
'#suffix' => '</h1>',
'#weight' => -1000
'#weight' => -1000,
];
$text = $this->renderer->renderPlain($build);
@ -781,7 +793,7 @@ class NodeSearch extends ConfigurableSearchPluginBase implements AccessibleInter
'#open' => TRUE,
];
$form['content_ranking']['info'] = [
'#markup' => '<p><em>' . $this->t('Influence is a numeric multiplier used in ordering search results. A higher number means the corresponding factor has more influence on search results; zero means the factor is ignored. Changing these numbers does not require the search index to be rebuilt. Changes take effect immediately.') . '</em></p>'
'#markup' => '<p><em>' . $this->t('Influence is a numeric multiplier used in ordering search results. A higher number means the corresponding factor has more influence on search results; zero means the factor is ignored. Changing these numbers does not require the search index to be rebuilt. Changes take effect immediately.') . '</em></p>',
];
// Prepare table.
$header = [$this->t('Factor'), $this->t('Influence')];

View file

@ -165,7 +165,7 @@ class D6NodeDeriver extends DeriverBase implements ContainerDeriverInterface {
$this->fieldPluginCache[$field_type] = $this->fieldPluginManager->createInstance($plugin_id, ['core' => 6], $migration);
}
$this->fieldPluginCache[$field_type]
->processFieldValues($migration, $field_name, $info);
->defineValueProcessPipeline($migration, $field_name, $info);
}
catch (PluginNotFoundException $ex) {
try {

View file

@ -0,0 +1,21 @@
<?php
namespace Drupal\node\Plugin\migrate;
use Drupal\migrate\Plugin\Migration;
use Drupal\migrate_drupal\Plugin\MigrationWithFollowUpInterface;
/**
* Migration plugin for the Drupal 6 node translations.
*/
class D6NodeTranslation extends Migration implements MigrationWithFollowUpInterface {
/**
* {@inheritdoc}
*/
public function generateFollowUpMigrations() {
$this->migrationPluginManager->clearCachedDefinitions();
return $this->migrationPluginManager->createInstances('d6_entity_reference_translation');
}
}

View file

@ -141,6 +141,16 @@ class D7NodeDeriver extends DeriverBase implements ContainerDeriverInterface {
$values['source']['node_type'] = $node_type;
$values['destination']['default_bundle'] = $node_type;
// Comment status must be mapped to correct comment type.
// Comment type migration creates a separate comment type for each
// node type except for Forum which uses 'comment_forum'.
$comment_type = 'comment_node_' . $node_type;
if ($node_type == 'forum') {
$comment_type = 'comment_forum';
}
$nested_key = $comment_type . '/0/status';
$values['process'][$nested_key] = 'comment';
// If this migration is based on the d7_node_revision migration or
// is for translations of nodes, it should explicitly depend on the
// corresponding d7_node variant.
@ -158,7 +168,7 @@ class D7NodeDeriver extends DeriverBase implements ContainerDeriverInterface {
$this->fieldPluginCache[$field_type] = $this->fieldPluginManager->createInstance($plugin_id, ['core' => 7], $migration);
}
$this->fieldPluginCache[$field_type]
->processFieldValues($migration, $field_name, $info);
->defineValueProcessPipeline($migration, $field_name, $info);
}
catch (PluginNotFoundException $ex) {
try {

View file

@ -0,0 +1,21 @@
<?php
namespace Drupal\node\Plugin\migrate;
use Drupal\migrate\Plugin\Migration;
use Drupal\migrate_drupal\Plugin\MigrationWithFollowUpInterface;
/**
* Migration plugin for the Drupal 7 node translations.
*/
class D7NodeTranslation extends Migration implements MigrationWithFollowUpInterface {
/**
* {@inheritdoc}
*/
public function generateFollowUpMigrations() {
$this->migrationPluginManager->clearCachedDefinitions();
return $this->migrationPluginManager->createInstances('d7_entity_reference_translation');
}
}

View file

@ -15,7 +15,9 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
* Drupal 6 node source from database.
*
* @MigrateSource(
* id = "d6_node"
* id = "d6_node",
* source_module = "node"
*
* )
*/
class Node extends DrupalSqlBase {
@ -176,24 +178,24 @@ class Node extends DrupalSqlBase {
}
/**
* Gets CCK field values for a node.
* Gets field values for a node.
*
* @param \Drupal\migrate\Row $node
* The node.
*
* @return array
* CCK field values, keyed by field name.
* Field values, keyed by field name.
*/
protected function getFieldValues(Row $node) {
$values = [];
foreach ($this->getFieldInfo($node->getSourceProperty('type')) as $field => $info) {
$values[$field] = $this->getCckData($info, $node);
$values[$field] = $this->getFieldData($info, $node);
}
return $values;
}
/**
* Gets CCK field and instance definitions from the database.
* Gets field and instance definitions from the database.
*
* @param string $node_type
* The node type for which to get field info.
@ -205,14 +207,14 @@ class Node extends DrupalSqlBase {
if (!isset($this->fieldInfo)) {
$this->fieldInfo = [];
// Query the database directly for all CCK field info.
// Query the database directly for all field info.
$query = $this->select('content_node_field_instance', 'cnfi');
$query->join('content_node_field', 'cnf', 'cnf.field_name = cnfi.field_name');
$query->fields('cnfi');
$query->fields('cnf');
foreach ($query->execute() as $field) {
$this->fieldInfo[ $field['type_name'] ][ $field['field_name'] ] = $field;
$this->fieldInfo[$field['type_name']][$field['field_name']] = $field;
}
foreach ($this->fieldInfo as $type => $fields) {
@ -230,7 +232,7 @@ class Node extends DrupalSqlBase {
}
/**
* Retrieves raw CCK field data for a node.
* Retrieves raw field data for a node.
*
* @param array $field
* A field and instance definition from getFieldInfo().
@ -240,7 +242,7 @@ class Node extends DrupalSqlBase {
* @return array
* The field values, keyed by delta.
*/
protected function getCckData(array $field, Row $node) {
protected function getFieldData(array $field, Row $node) {
$field_table = 'content_' . $field['field_name'];
$node_table = 'content_type_' . $node->getSourceProperty('type');
@ -276,10 +278,9 @@ class Node extends DrupalSqlBase {
return $query
// This call to isNotNull() is a kludge which relies on the convention
// that CCK field schemas usually define their most important
// column first. A better way would be to allow cckfield plugins to
// alter the query directly before it's run, but this will do for
// the time being.
// that field schemas usually define their most important column first.
// A better way would be to allow field plugins to alter the query
// directly before it's run, but this will do for the time being.
->isNotNull($field['field_name'] . '_' . $columns[0])
->condition('nid', $node->getSourceProperty('nid'))
->condition('vid', $node->getSourceProperty('vid'))
@ -291,6 +292,24 @@ class Node extends DrupalSqlBase {
}
}
/**
* Retrieves raw field data for a node.
*
* @deprecated in Drupal 8.2.x, to be removed in Drupal 9.0.x. Use
* getFieldData() instead.
*
* @param array $field
* A field and instance definition from getFieldInfo().
* @param \Drupal\migrate\Row $node
* The node.
*
* @return array
* The field values, keyed by delta.
*/
protected function getCckData(array $field, Row $node) {
return $this->getFieldData($field, $node);
}
/**
* {@inheritdoc}
*/

View file

@ -1,13 +1,15 @@
<?php
namespace Drupal\node\Plugin\migrate\source\d6;
use Drupal\Core\Database\Query\SelectInterface;
/**
* Drupal 6 node revision source from database.
*
* @MigrateSource(
* id = "d6_node_revision"
* id = "d6_node_revision",
* source_module = "node"
* )
*/
class NodeRevision extends Node {

View file

@ -9,7 +9,8 @@ use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
* Drupal 6 Node types source from database.
*
* @MigrateSource(
* id = "d6_node_type"
* id = "d6_node_type",
* source_module = "node"
* )
*/
class NodeType extends DrupalSqlBase {
@ -62,7 +63,7 @@ class NodeType extends DrupalSqlBase {
* {@inheritdoc}
*/
public function fields() {
return [
$fields = [
'type' => $this->t('Machine name of the node type.'),
'name' => $this->t('Human name of the node type.'),
'module' => $this->t('The module providing the node type.'),
@ -78,6 +79,28 @@ class NodeType extends DrupalSqlBase {
'orig_type' => $this->t('The original type.'),
'teaser_length' => $this->t('Teaser length'),
];
if ($this->moduleExists('comment')) {
$fields += $this->getCommentFields();
}
return $fields;
}
/**
* Returns the fields containing comment settings for each node type.
*
* @return string[]
* An associative array of field descriptions, keyed by field.
*/
protected function getCommentFields() {
return [
'comment' => $this->t('Default comment setting'),
'comment_default_mode' => $this->t('Default display mode'),
'comment_default_per_page' => $this->t('Default comments per page'),
'comment_anonymous' => $this->t('Anonymous commenting'),
'comment_subject_field' => $this->t('Comment subject field'),
'comment_preview' => $this->t('Preview comment'),
'comment_form_location' => $this->t('Location of comment submission form'),
];
}
/**
@ -111,6 +134,13 @@ class NodeType extends DrupalSqlBase {
$row->setSourceProperty('available_menus', [$default_node_menu]);
$row->setSourceProperty('parent', $default_node_menu . ':');
}
if ($this->moduleExists('comment')) {
foreach (array_keys($this->getCommentFields()) as $field) {
$row->setSourceProperty($field, $this->variableGet($field . '_' . $type, NULL));
}
}
return parent::prepareRow($row);
}

View file

@ -7,7 +7,7 @@ namespace Drupal\node\Plugin\migrate\source\d6;
*
* @MigrateSource(
* id = "d6_view_mode",
* source_provider = "content"
* source_module = "content"
* )
*/
class ViewMode extends ViewModeBase {

View file

@ -12,7 +12,7 @@ abstract class ViewModeBase extends DrupalSqlBase {
/**
* {@inheritdoc}
*/
public function count() {
public function count($refresh = FALSE) {
return count($this->initializeIterator());
}

View file

@ -17,7 +17,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
*
* @MigrateSource(
* id = "d7_node",
* source_provider = "node"
* source_module = "node"
* )
*/
class Node extends FieldableEntity {
@ -104,17 +104,40 @@ class Node extends FieldableEntity {
* {@inheritdoc}
*/
public function prepareRow(Row $row) {
$nid = $row->getSourceProperty('nid');
$vid = $row->getSourceProperty('vid');
$type = $row->getSourceProperty('type');
// If this entity was translated using Entity Translation, we need to get
// its source language to get the field values in the right language.
// The translations will be migrated by the d7_node_entity_translation
// migration.
$entity_translatable = $this->isEntityTranslatable('node') && (int) $this->variableGet('language_content_type_' . $type, 0) === 4;
$source_language = $this->getEntityTranslationSourceLanguage('node', $nid);
$language = $entity_translatable && $source_language ? $source_language : $row->getSourceProperty('language');
// Get Field API field values.
foreach (array_keys($this->getFields('node', $row->getSourceProperty('type'))) as $field) {
$nid = $row->getSourceProperty('nid');
$vid = $row->getSourceProperty('vid');
$row->setSourceProperty($field, $this->getFieldValues('node', $field, $nid, $vid));
foreach ($this->getFields('node', $type) as $field_name => $field) {
// Ensure we're using the right language if the entity and the field are
// translatable.
$field_language = $entity_translatable && $field['translatable'] ? $language : NULL;
$row->setSourceProperty($field_name, $this->getFieldValues('node', $field_name, $nid, $vid, $field_language));
}
// Make sure we always have a translation set.
if ($row->getSourceProperty('tnid') == 0) {
$row->setSourceProperty('tnid', $row->getSourceProperty('nid'));
}
// If the node title was replaced by a real field using the Drupal 7 Title
// module, use the field value instead of the node title.
if ($this->moduleExists('title')) {
$title_field = $row->getSourceProperty('title_field');
if (isset($title_field[0]['value'])) {
$row->setSourceProperty('title', $title_field[0]['value']);
}
}
return parent::prepareRow($row);
}

View file

@ -0,0 +1,118 @@
<?php
namespace Drupal\node\Plugin\migrate\source\d7;
use Drupal\migrate\Row;
use Drupal\migrate_drupal\Plugin\migrate\source\d7\FieldableEntity;
/**
* Provides Drupal 7 node entity translations source plugin.
*
* @MigrateSource(
* id = "d7_node_entity_translation",
* source_module = "entity_translation"
* )
*/
class NodeEntityTranslation extends FieldableEntity {
/**
* {@inheritdoc}
*/
public function query() {
$query = $this->select('entity_translation', 'et')
->fields('et')
->fields('n', [
'title',
'type',
'promote',
'sticky',
])
->fields('nr', [
'log',
'timestamp',
])
->condition('et.entity_type', 'node')
->condition('et.source', '', '<>');
$query->addField('nr', 'uid', 'revision_uid');
$query->innerJoin('node', 'n', 'n.nid = et.entity_id');
$query->innerJoin('node_revision', 'nr', 'nr.vid = et.revision_id');
if (isset($this->configuration['node_type'])) {
$query->condition('n.type', $this->configuration['node_type']);
}
return $query;
}
/**
* {@inheritdoc}
*/
public function prepareRow(Row $row) {
$nid = $row->getSourceProperty('entity_id');
$vid = $row->getSourceProperty('revision_id');
$type = $row->getSourceProperty('type');
$language = $row->getSourceProperty('language');
// Get Field API field values.
foreach ($this->getFields('node', $type) as $field_name => $field) {
// Ensure we're using the right language if the entity is translatable.
$field_language = $field['translatable'] ? $language : NULL;
$row->setSourceProperty($field_name, $this->getFieldValues('node', $field_name, $nid, $vid, $field_language));
}
// If the node title was replaced by a real field using the Drupal 7 Title
// module, use the field value instead of the node title.
if ($this->moduleExists('title')) {
$title_field = $row->getSourceProperty('title_field');
if (isset($title_field[0]['value'])) {
$row->setSourceProperty('title', $title_field[0]['value']);
}
}
return parent::prepareRow($row);
}
/**
* {@inheritdoc}
*/
public function fields() {
return [
'entity_type' => $this->t('The entity type this translation relates to'),
'entity_id' => $this->t('The entity ID this translation relates to'),
'revision_id' => $this->t('The entity revision ID this translation relates to'),
'language' => $this->t('The target language for this translation.'),
'source' => $this->t('The source language from which this translation was created.'),
'uid' => $this->t('The author of this translation.'),
'status' => $this->t('Boolean indicating whether the translation is published (visible to non-administrators).'),
'translate' => $this->t('A boolean indicating whether this translation needs to be updated.'),
'created' => $this->t('The Unix timestamp when the translation was created.'),
'changed' => $this->t('The Unix timestamp when the translation was most recently saved.'),
'title' => $this->t('Node title'),
'type' => $this->t('Node type'),
'promote' => $this->t('Promoted to front page'),
'sticky' => $this->t('Sticky at top of lists'),
'log' => $this->t('Revision log'),
'timestamp' => $this->t('The timestamp the latest revision of this node was created.'),
'revision_uid' => $this->t('Revision authored by (uid)'),
];
}
/**
* {@inheritdoc}
*/
public function getIds() {
return [
'entity_id' => [
'type' => 'integer',
'alias' => 'et',
],
'language' => [
'type' => 'string',
'alias' => 'et',
],
];
}
}

View file

@ -7,7 +7,7 @@ namespace Drupal\node\Plugin\migrate\source\d7;
*
* @MigrateSource(
* id = "d7_node_revision",
* source_provider = "node"
* source_module = "node"
* )
*/
class NodeRevision extends Node {

View file

@ -10,7 +10,7 @@ use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
*
* @MigrateSource(
* id = "d7_node_type",
* source_provider = "node"
* source_module = "node"
* )
*/
class NodeType extends DrupalSqlBase {
@ -54,6 +54,28 @@ class NodeType extends DrupalSqlBase {
'orig_type' => $this->t('The original type.'),
'teaser_length' => $this->t('Teaser length'),
];
if ($this->moduleExists('comment')) {
$fields += $this->getCommentFields();
}
return $fields;
}
/**
* Returns the fields containing comment settings for each node type.
*
* @return string[]
* An associative array of field descriptions, keyed by field.
*/
protected function getCommentFields() {
return [
'comment' => $this->t('Default comment setting'),
'comment_default_mode' => $this->t('Default display mode'),
'comment_default_per_page' => $this->t('Default comments per page'),
'comment_anonymous' => $this->t('Anonymous commenting'),
'comment_subject_field' => $this->t('Comment subject field'),
'comment_preview' => $this->t('Preview comment'),
'comment_form_location' => $this->t('Location of comment submission form'),
];
}
/**
@ -107,6 +129,13 @@ class NodeType extends DrupalSqlBase {
if ($parent = $this->variableGet('menu_parent_' . $type, NULL)) {
$row->setSourceProperty('parent', $parent . ':');
}
if ($this->moduleExists('comment')) {
foreach (array_keys($this->getCommentFields()) as $field) {
$row->setSourceProperty($field, $this->variableGet($field . '_' . $type, NULL));
}
}
return parent::prepareRow($row);
}

View file

@ -29,7 +29,7 @@ class Nid extends NumericArgument {
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param NodeStorageInterface $node_storage
* @param \Drupal\node\NodeStorageInterface $node_storage
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, NodeStorageInterface $node_storage) {
parent::__construct($configuration, $plugin_id, $plugin_definition);

View file

@ -29,7 +29,7 @@ class Type extends StringArgument {
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
* @param \Drupal\Core\Entity\EntityStorageInterface $node_type_storage
* The entity storage class.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityStorageInterface $node_type_storage) {

View file

@ -29,7 +29,7 @@ class Vid extends NumericArgument {
protected $nodeStorage;
/**
* Constructs a Drupal\Component\Plugin\PluginBase object.
* Constructs a \Drupal\node\Plugin\views\argument\Vid object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.

View file

@ -2,7 +2,7 @@
namespace Drupal\node\Plugin\views\field;
use Drupal\system\Plugin\views\field\BulkForm;
use Drupal\views\Plugin\views\field\BulkForm;
/**
* Defines a node operations bulk form element.

View file

@ -2,6 +2,8 @@
namespace Drupal\node\Plugin\views\field;
@trigger_error('Drupal\node\Plugin\views\field\Path is deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. Use @ViewsField("entity_link") with \'output_url_as_text\' set.', E_USER_DEPRECATED);
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\views\field\FieldPluginBase;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
@ -14,6 +16,9 @@ use Drupal\views\ViewExecutable;
* @ingroup views_field_handlers
*
* @ViewsField("node_path")
*
* @deprecated in Drupal 8.5.x, will be removed before Drupal 9.0.0.
* Use @ViewsField("entity_link") with 'output_url_as_text' set.
*/
class Path extends FieldPluginBase {

View file

@ -2,6 +2,7 @@
namespace Drupal\node\Plugin\views\filter;
use Drupal\Core\Database\Query\Condition;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\views\filter\FilterPluginBase;
@ -14,8 +15,10 @@ use Drupal\views\Plugin\views\filter\FilterPluginBase;
*/
class Access extends FilterPluginBase {
public function adminSummary() { }
protected function operatorForm(&$form, FormStateInterface $form_state) { }
public function adminSummary() {}
protected function operatorForm(&$form, FormStateInterface $form_state) {}
public function canExpose() {
return FALSE;
}
@ -27,10 +30,10 @@ class Access extends FilterPluginBase {
$account = $this->view->getUser();
if (!$account->hasPermission('bypass node access')) {
$table = $this->ensureMyTable();
$grants = db_or();
$grants = new Condition('OR');
foreach (node_access_grants('view', $account) as $realm => $gids) {
foreach ($gids as $gid) {
$grants->condition(db_and()
$grants->condition((new Condition('AND'))
->condition($table . '.gid', $gid)
->condition($table . '.realm', $realm)
);

View file

@ -14,11 +14,13 @@ use Drupal\views\Plugin\views\filter\FilterPluginBase;
*/
class Status extends FilterPluginBase {
public function adminSummary() { }
public function adminSummary() {}
protected function operatorForm(&$form, FormStateInterface $form_state) { }
protected function operatorForm(&$form, FormStateInterface $form_state) {}
public function canExpose() { return FALSE; }
public function canExpose() {
return FALSE;
}
public function query() {
$table = $this->ensureMyTable();

View file

@ -22,23 +22,11 @@ class Node extends WizardPluginBase {
/**
* Set the created column.
*
* @var string
*/
protected $createdColumn = 'node_field_data-created';
/**
* Set default values for the filters.
*/
protected $filters = [
'status' => [
'value' => TRUE,
'table' => 'node_field_data',
'field' => 'status',
'plugin_id' => 'boolean',
'entity_type' => 'node',
'entity_field' => 'status',
]
];
/**
* Overrides Drupal\views\Plugin\views\wizard\WizardPluginBase::getAvailableSorts().
*
@ -49,7 +37,7 @@ class Node extends WizardPluginBase {
public function getAvailableSorts() {
// You can't execute functions in properties, so override the method
return [
'node_field_data-title:ASC' => $this->t('Title')
'node_field_data-title:ASC' => $this->t('Title'),
];
}

Some files were not shown because too many files have changed in this diff Show more