node.admin.inc 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. <?php
  2. /**
  3. * @file
  4. * Content administration and module settings user interface.
  5. */
  6. use Drupal\node\NodeInterface;
  7. /**
  8. * Updates all nodes in the passed-in array with the passed-in field values.
  9. *
  10. * IMPORTANT NOTE: This function is intended to work when called from a form
  11. * submission handler. Calling it outside of the form submission process may not
  12. * work correctly.
  13. *
  14. * @param array $nodes
  15. * Array of node nids or nodes to update.
  16. * @param array $updates
  17. * Array of key/value pairs with node field names and the value to update that
  18. * field to.
  19. * @param string $langcode
  20. * (optional) The language updates should be applied to. If none is specified
  21. * all available languages are processed.
  22. * @param bool $load
  23. * (optional) TRUE if $nodes contains an array of node IDs to be loaded, FALSE
  24. * if it contains fully loaded nodes. Defaults to FALSE.
  25. * @param bool $revisions
  26. * (optional) TRUE if $nodes contains an array of revision IDs instead of
  27. * node IDs. Defaults to FALSE; will be ignored if $load is FALSE.
  28. */
  29. function node_mass_update(array $nodes, array $updates, $langcode = NULL, $load = FALSE, $revisions = FALSE) {
  30. // We use batch processing to prevent timeout when updating a large number
  31. // of nodes.
  32. if (count($nodes) > 10) {
  33. $batch = [
  34. 'operations' => [
  35. ['_node_mass_update_batch_process', [$nodes, $updates, $langcode, $load, $revisions]],
  36. ],
  37. 'finished' => '_node_mass_update_batch_finished',
  38. 'title' => t('Processing'),
  39. // We use a single multi-pass operation, so the default
  40. // 'Remaining x of y operations' message will be confusing here.
  41. 'progress_message' => '',
  42. 'error_message' => t('The update has encountered an error.'),
  43. // The operations do not live in the .module file, so we need to
  44. // tell the batch engine which file to load before calling them.
  45. 'file' => drupal_get_path('module', 'node') . '/node.admin.inc',
  46. ];
  47. batch_set($batch);
  48. }
  49. else {
  50. $storage = \Drupal::entityTypeManager()->getStorage('node');
  51. if ($load && !$revisions) {
  52. $nodes = $storage->loadMultiple($nodes);
  53. }
  54. foreach ($nodes as $node) {
  55. if ($load && $revisions) {
  56. $node = $storage->loadRevision($node);
  57. }
  58. _node_mass_update_helper($node, $updates, $langcode);
  59. }
  60. \Drupal::messenger()->addStatus(t('The update has been performed.'));
  61. }
  62. }
  63. /**
  64. * Updates individual nodes when fewer than 10 are queued.
  65. *
  66. * @param \Drupal\node\NodeInterface $node
  67. * A node to update.
  68. * @param array $updates
  69. * Associative array of updates.
  70. * @param string $langcode
  71. * (optional) The language updates should be applied to. If none is specified
  72. * all available languages are processed.
  73. *
  74. * @return \Drupal\node\NodeInterface
  75. * An updated node object.
  76. *
  77. * @see node_mass_update()
  78. */
  79. function _node_mass_update_helper(NodeInterface $node, array $updates, $langcode = NULL) {
  80. $langcodes = isset($langcode) ? [$langcode] : array_keys($node->getTranslationLanguages());
  81. // For efficiency manually save the original node before applying any changes.
  82. $node->original = clone $node;
  83. foreach ($langcodes as $langcode) {
  84. foreach ($updates as $name => $value) {
  85. $node->getTranslation($langcode)->$name = $value;
  86. }
  87. }
  88. $node->save();
  89. return $node;
  90. }
  91. /**
  92. * Implements callback_batch_operation().
  93. *
  94. * Executes a batch operation for node_mass_update().
  95. *
  96. * @param array $nodes
  97. * An array of node IDs.
  98. * @param array $updates
  99. * Associative array of updates.
  100. * @param string $langcode
  101. * The language updates should be applied to. If none is specified all
  102. * available languages are processed.
  103. * @param bool $load
  104. * TRUE if $nodes contains an array of node IDs to be loaded, FALSE if it
  105. * contains fully loaded nodes.
  106. * @param bool $revisions
  107. * (optional) TRUE if $nodes contains an array of revision IDs instead of
  108. * node IDs. Defaults to FALSE; will be ignored if $load is FALSE.
  109. * @param array|\ArrayAccess $context
  110. * An array of contextual key/values.
  111. */
  112. function _node_mass_update_batch_process(array $nodes, array $updates, $langcode, $load, $revisions, &$context) {
  113. if (!isset($context['sandbox']['progress'])) {
  114. $context['sandbox']['progress'] = 0;
  115. $context['sandbox']['max'] = count($nodes);
  116. $context['sandbox']['nodes'] = $nodes;
  117. }
  118. // Process nodes by groups of 5.
  119. $storage = \Drupal::entityTypeManager()->getStorage('node');
  120. $count = min(5, count($context['sandbox']['nodes']));
  121. for ($i = 1; $i <= $count; $i++) {
  122. // For each nid, load the node, reset the values, and save it.
  123. $node = array_shift($context['sandbox']['nodes']);
  124. if ($load) {
  125. $node = $revisions ?
  126. $storage->loadRevision($node) : $storage->load($node);
  127. }
  128. $node = _node_mass_update_helper($node, $updates, $langcode);
  129. // Store result for post-processing in the finished callback.
  130. $context['results'][] = \Drupal::l($node->label(), $node->urlInfo());
  131. // Update our progress information.
  132. $context['sandbox']['progress']++;
  133. }
  134. // Inform the batch engine that we are not finished,
  135. // and provide an estimation of the completion level we reached.
  136. if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
  137. $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
  138. }
  139. }
  140. /**
  141. * Implements callback_batch_finished().
  142. *
  143. * Reports the 'finished' status of batch operation for node_mass_update().
  144. *
  145. * @param bool $success
  146. * A boolean indicating whether the batch mass update operation successfully
  147. * concluded.
  148. * @param string[] $results
  149. * An array of rendered links to nodes updated via the batch mode process.
  150. * @param array $operations
  151. * An array of function calls (not used in this function).
  152. *
  153. * @see _node_mass_update_batch_process()
  154. */
  155. function _node_mass_update_batch_finished($success, $results, $operations) {
  156. if ($success) {
  157. \Drupal::messenger()->addStatus(t('The update has been performed.'));
  158. }
  159. else {
  160. \Drupal::messenger()->addError(t('An error occurred and processing did not complete.'));
  161. $message = \Drupal::translation()->formatPlural(count($results), '1 item successfully processed:', '@count items successfully processed:');
  162. $item_list = [
  163. '#theme' => 'item_list',
  164. '#items' => $results,
  165. ];
  166. $message .= \Drupal::service('renderer')->render($item_list);
  167. \Drupal::messenger()->addStatus($message);
  168. }
  169. }