node.admin.inc 6.4 KB

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