PageRenderTime 137ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/web/modules/contrib/devel/devel_generate/src/Plugin/DevelGenerate/ContentDevelGenerate.php

https://gitlab.com/andecode/theme-spark
PHP | 729 lines | 441 code | 76 blank | 212 comment | 49 complexity | e5b0c86ad910b1895d1f651cb7f37269 MD5 | raw file
  1. <?php
  2. namespace Drupal\devel_generate\Plugin\DevelGenerate;
  3. use Drupal\comment\CommentManagerInterface;
  4. use Drupal\Component\Datetime\Time;
  5. use Drupal\Component\Render\FormattableMarkup;
  6. use Drupal\content_translation\ContentTranslationManagerInterface;
  7. use Drupal\Core\Database\Connection;
  8. use Drupal\Core\Datetime\DateFormatterInterface;
  9. use Drupal\Core\Entity\EntityStorageInterface;
  10. use Drupal\Core\Extension\ModuleHandlerInterface;
  11. use Drupal\Core\Form\FormStateInterface;
  12. use Drupal\Core\Language\LanguageInterface;
  13. use Drupal\Core\Language\LanguageManagerInterface;
  14. use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
  15. use Drupal\Core\Routing\UrlGeneratorInterface;
  16. use Drupal\devel_generate\DevelGenerateBase;
  17. use Drupal\field\Entity\FieldConfig;
  18. use Drupal\node\NodeInterface;
  19. use Drupal\path_alias\PathAliasStorage;
  20. use Drupal\user\UserStorageInterface;
  21. use Drush\Utils\StringUtils;
  22. use Symfony\Component\DependencyInjection\ContainerInterface;
  23. /**
  24. * Provides a ContentDevelGenerate plugin.
  25. *
  26. * @DevelGenerate(
  27. * id = "content",
  28. * label = @Translation("content"),
  29. * description = @Translation("Generate a given number of content. Optionally delete current content."),
  30. * url = "content",
  31. * permission = "administer devel_generate",
  32. * settings = {
  33. * "num" = 50,
  34. * "kill" = FALSE,
  35. * "max_comments" = 0,
  36. * "title_length" = 4,
  37. * "add_type_label" = FALSE
  38. * }
  39. * )
  40. */
  41. class ContentDevelGenerate extends DevelGenerateBase implements ContainerFactoryPluginInterface {
  42. /**
  43. * The node storage.
  44. *
  45. * @var \Drupal\Core\Entity\EntityStorageInterface
  46. */
  47. protected $nodeStorage;
  48. /**
  49. * The node type storage.
  50. *
  51. * @var \Drupal\Core\Entity\EntityStorageInterface
  52. */
  53. protected $nodeTypeStorage;
  54. /**
  55. * The user storage.
  56. *
  57. * @var \Drupal\user\UserStorageInterface
  58. */
  59. protected $userStorage;
  60. /**
  61. * The module handler.
  62. *
  63. * @var \Drupal\Core\Extension\ModuleHandlerInterface
  64. */
  65. protected $moduleHandler;
  66. /**
  67. * The comment manager service.
  68. *
  69. * @var \Drupal\comment\CommentManagerInterface
  70. */
  71. protected $commentManager;
  72. /**
  73. * The language manager.
  74. *
  75. * @var \Drupal\Core\Language\LanguageManagerInterface
  76. */
  77. protected $languageManager;
  78. /**
  79. * The content translation manager.
  80. *
  81. * @var \Drupal\content_translation\ContentTranslationManagerInterface
  82. */
  83. protected $contentTranslationManager;
  84. /**
  85. * The url generator service.
  86. *
  87. * @var \Drupal\Core\Routing\UrlGeneratorInterface
  88. */
  89. protected $urlGenerator;
  90. /**
  91. * The alias storage.
  92. *
  93. * @var \Drupal\path_alias\PathAliasStorage
  94. */
  95. protected $aliasStorage;
  96. /**
  97. * The date formatter service.
  98. *
  99. * @var \Drupal\Core\Datetime\DateFormatterInterface
  100. */
  101. protected $dateFormatter;
  102. /**
  103. * The Drush batch flag.
  104. *
  105. * @var bool
  106. */
  107. protected $drushBatch;
  108. /**
  109. * Provides system time.
  110. *
  111. * @var \Drupal\Core\Datetime\Time
  112. */
  113. protected $time;
  114. /**
  115. * Database connection.
  116. *
  117. * @var \Drupal\Core\Database\Connection
  118. */
  119. protected $database;
  120. /**
  121. * The construct.
  122. *
  123. * @param array $configuration
  124. * A configuration array containing information about the plugin instance.
  125. * @param string $plugin_id
  126. * The plugin ID for the plugin instance.
  127. * @param array $plugin_definition
  128. * The plugin implementation definition.
  129. * @param \Drupal\Core\Entity\EntityStorageInterface $node_storage
  130. * The node storage.
  131. * @param \Drupal\Core\Entity\EntityStorageInterface $node_type_storage
  132. * The node type storage.
  133. * @param \Drupal\user\UserStorageInterface $user_storage
  134. * The user storage.
  135. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
  136. * The module handler.
  137. * @param \Drupal\comment\CommentManagerInterface $comment_manager
  138. * The comment manager service.
  139. * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
  140. * The language manager.
  141. * @param \Drupal\content_translation\ContentTranslationManagerInterface $content_translation_manager
  142. * The content translation manager service.
  143. * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator
  144. * The url generator service.
  145. * @param \Drupal\path_alias\PathAliasStorage $alias_storage
  146. * The alias storage.
  147. * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
  148. * The date formatter service.
  149. * @param \Drupal\Core\Datetime\Time $time
  150. * Provides system time.
  151. * @param \Drupal\Core\Database\Connection $database
  152. * Database connection.
  153. */
  154. public function __construct(array $configuration, $plugin_id, array $plugin_definition, EntityStorageInterface $node_storage, EntityStorageInterface $node_type_storage, UserStorageInterface $user_storage, ModuleHandlerInterface $module_handler, CommentManagerInterface $comment_manager = NULL, LanguageManagerInterface $language_manager, ContentTranslationManagerInterface $content_translation_manager = NULL, UrlGeneratorInterface $url_generator, PathAliasStorage $alias_storage, DateFormatterInterface $date_formatter, Time $time, Connection $database) {
  155. parent::__construct($configuration, $plugin_id, $plugin_definition);
  156. $this->moduleHandler = $module_handler;
  157. $this->nodeStorage = $node_storage;
  158. $this->nodeTypeStorage = $node_type_storage;
  159. $this->userStorage = $user_storage;
  160. $this->commentManager = $comment_manager;
  161. $this->languageManager = $language_manager;
  162. $this->contentTranslationManager = $content_translation_manager;
  163. $this->urlGenerator = $url_generator;
  164. $this->aliasStorage = $alias_storage;
  165. $this->dateFormatter = $date_formatter;
  166. $this->time = $time;
  167. $this->database = $database;
  168. }
  169. /**
  170. * {@inheritdoc}
  171. */
  172. public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
  173. $entity_type_manager = $container->get('entity_type.manager');
  174. return new static(
  175. $configuration, $plugin_id, $plugin_definition,
  176. $entity_type_manager->getStorage('node'),
  177. $entity_type_manager->getStorage('node_type'),
  178. $entity_type_manager->getStorage('user'),
  179. $container->get('module_handler'),
  180. $container->has('comment.manager') ? $container->get('comment.manager') : NULL,
  181. $container->get('language_manager'),
  182. $container->has('content_translation.manager') ? $container->get('content_translation.manager') : NULL,
  183. $container->get('url_generator'),
  184. $entity_type_manager->getStorage('path_alias'),
  185. $container->get('date.formatter'),
  186. $container->get('datetime.time'),
  187. $container->get('database')
  188. );
  189. }
  190. /**
  191. * {@inheritdoc}
  192. */
  193. public function settingsForm(array $form, FormStateInterface $form_state) {
  194. $types = $this->nodeTypeStorage->loadMultiple();
  195. if (empty($types)) {
  196. $create_url = $this->urlGenerator->generateFromRoute('node.type_add');
  197. $this->setMessage($this->t('You do not have any content types that can be generated. <a href=":create-type">Go create a new content type</a>', [':create-type' => $create_url]), 'error', FALSE);
  198. return;
  199. }
  200. $options = [];
  201. foreach ($types as $type) {
  202. $options[$type->id()] = [
  203. 'type' => ['#markup' => $type->label()],
  204. ];
  205. if ($this->commentManager) {
  206. $comment_fields = $this->commentManager->getFields('node');
  207. $map = [$this->t('Hidden'), $this->t('Closed'), $this->t('Open')];
  208. $fields = [];
  209. foreach ($comment_fields as $field_name => $info) {
  210. // Find all comment fields for the bundle.
  211. if (in_array($type->id(), $info['bundles'])) {
  212. $instance = FieldConfig::loadByName('node', $type->id(), $field_name);
  213. $default_value = $instance->getDefaultValueLiteral();
  214. $default_mode = reset($default_value);
  215. $fields[] = new FormattableMarkup('@field: @state', [
  216. '@field' => $instance->label(),
  217. '@state' => $map[$default_mode['status']],
  218. ]);
  219. }
  220. }
  221. // @todo Refactor display of comment fields.
  222. if (!empty($fields)) {
  223. $options[$type->id()]['comments'] = [
  224. 'data' => [
  225. '#theme' => 'item_list',
  226. '#items' => $fields,
  227. ],
  228. ];
  229. }
  230. else {
  231. $options[$type->id()]['comments'] = $this->t('No comment fields');
  232. }
  233. }
  234. }
  235. $header = [
  236. 'type' => $this->t('Content type'),
  237. ];
  238. if ($this->commentManager) {
  239. $header['comments'] = [
  240. 'data' => $this->t('Comments'),
  241. 'class' => [RESPONSIVE_PRIORITY_MEDIUM],
  242. ];
  243. }
  244. $form['node_types'] = [
  245. '#type' => 'tableselect',
  246. '#header' => $header,
  247. '#options' => $options,
  248. ];
  249. $form['kill'] = [
  250. '#type' => 'checkbox',
  251. '#title' => $this->t('<strong>Delete all content</strong> in these content types before generating new content.'),
  252. '#default_value' => $this->getSetting('kill'),
  253. ];
  254. $form['num'] = [
  255. '#type' => 'number',
  256. '#title' => $this->t('How many nodes would you like to generate?'),
  257. '#default_value' => $this->getSetting('num'),
  258. '#required' => TRUE,
  259. '#min' => 0,
  260. ];
  261. $options = [1 => $this->t('Now')];
  262. foreach ([3600, 86400, 604800, 2592000, 31536000] as $interval) {
  263. $options[$interval] = $this->dateFormatter->formatInterval($interval, 1) . ' ' . $this->t('ago');
  264. }
  265. $form['time_range'] = [
  266. '#type' => 'select',
  267. '#title' => $this->t('How far back in time should the nodes be dated?'),
  268. '#description' => $this->t('Node creation dates will be distributed randomly from the current time, back to the selected time.'),
  269. '#options' => $options,
  270. '#default_value' => 604800,
  271. ];
  272. $form['max_comments'] = [
  273. '#type' => $this->moduleHandler->moduleExists('comment') ? 'number' : 'value',
  274. '#title' => $this->t('Maximum number of comments per node.'),
  275. '#description' => $this->t('You must also enable comments for the content types you are generating. Note that some nodes will randomly receive zero comments. Some will receive the max.'),
  276. '#default_value' => $this->getSetting('max_comments'),
  277. '#min' => 0,
  278. '#access' => $this->moduleHandler->moduleExists('comment'),
  279. ];
  280. $form['title_length'] = [
  281. '#type' => 'number',
  282. '#title' => $this->t('Maximum number of words in titles'),
  283. '#default_value' => $this->getSetting('title_length'),
  284. '#required' => TRUE,
  285. '#min' => 1,
  286. '#max' => 255,
  287. ];
  288. $form['add_type_label'] = [
  289. '#type' => 'checkbox',
  290. '#title' => $this->t('Prefix the title with the content type label.'),
  291. '#description' => $this->t('This will not count against the maximum number of title words specified above.'),
  292. '#default_value' => $this->getSetting('add_type_label'),
  293. ];
  294. $form['add_alias'] = [
  295. '#type' => 'checkbox',
  296. '#disabled' => !$this->moduleHandler->moduleExists('path'),
  297. '#description' => $this->t('Requires path.module'),
  298. '#title' => $this->t('Add an url alias for each node.'),
  299. '#default_value' => FALSE,
  300. ];
  301. $form['add_statistics'] = [
  302. '#type' => 'checkbox',
  303. '#title' => $this->t('Add statistics for each node (node_counter table).'),
  304. '#default_value' => TRUE,
  305. '#access' => $this->moduleHandler->moduleExists('statistics'),
  306. ];
  307. // Add the language and translation options.
  308. $form += $this->getLanguageForm('nodes');
  309. // Add the user selection checkboxes.
  310. $author_header = [
  311. 'id' => $this->t('User ID'),
  312. 'user' => $this->t('Name'),
  313. 'role' => $this->t('Role(s)'),
  314. ];
  315. $author_rows = [];
  316. /** @var \Drupal\user\UserInterface $user */
  317. foreach ($this->userStorage->loadMultiple() as $user) {
  318. $author_rows[$user->id()] = [
  319. 'id' => ['#markup' => $user->id()],
  320. 'user' => ['#markup' => $user->getAccountName()],
  321. 'role' => ['#markup' => implode(", ", $user->getRoles())],
  322. ];
  323. }
  324. $form['authors-wrap'] = [
  325. '#type' => 'details',
  326. '#title' => $this->t('Users'),
  327. '#open' => FALSE,
  328. '#description' => $this->t('Select users for randomly assigning as authors of the generated content. Leave all unchecked to use a random selection of up to 50 users.'),
  329. ];
  330. $form['authors-wrap']['authors'] = [
  331. '#type' => 'tableselect',
  332. '#header' => $author_header,
  333. '#options' => $author_rows,
  334. ];
  335. $form['#redirect'] = FALSE;
  336. return $form;
  337. }
  338. /**
  339. * {@inheritdoc}
  340. */
  341. public function settingsFormValidate(array $form, FormStateInterface $form_state) {
  342. if (!array_filter($form_state->getValue('node_types'))) {
  343. $form_state->setErrorByName('node_types', $this->t('Please select at least one content type'));
  344. }
  345. }
  346. /**
  347. * {@inheritdoc}
  348. */
  349. protected function generateElements(array $values) {
  350. if ($this->isBatch($values['num'], $values['max_comments'])) {
  351. $this->generateBatchContent($values);
  352. }
  353. else {
  354. $this->generateContent($values);
  355. }
  356. }
  357. /**
  358. * Generate content when not in batch mode.
  359. *
  360. * This method is used when the number of elements is under 50.
  361. */
  362. private function generateContent($values) {
  363. $values['node_types'] = array_filter($values['node_types']);
  364. if (!empty($values['kill']) && $values['node_types']) {
  365. $this->contentKill($values);
  366. }
  367. if (!empty($values['node_types'])) {
  368. // Generate nodes.
  369. $this->develGenerateContentPreNode($values);
  370. $start = time();
  371. $values['num_translations'] = 0;
  372. for ($i = 1; $i <= $values['num']; $i++) {
  373. $this->develGenerateContentAddNode($values);
  374. if (isset($values['feedback']) && $i % $values['feedback'] == 0) {
  375. $now = time();
  376. $options = [
  377. '@feedback' => $values['feedback'],
  378. '@rate' => ($values['feedback'] * 60) / ($now - $start),
  379. ];
  380. $this->messenger()->addStatus(dt('Completed @feedback nodes (@rate nodes/min)', $options));
  381. $start = $now;
  382. }
  383. }
  384. }
  385. $this->setMessage($this->formatPlural($values['num'], 'Created 1 node', 'Created @count nodes'));
  386. if ($values['num_translations'] > 0) {
  387. $this->setMessage($this->formatPlural($values['num_translations'], 'Created 1 node translation', 'Created @count node translations'));
  388. }
  389. }
  390. /**
  391. * Generate content in batch mode.
  392. *
  393. * This method is used when the number of elements is 50 or more.
  394. */
  395. private function generateBatchContent($values) {
  396. // Remove unselected node types.
  397. $values['node_types'] = array_filter($values['node_types']);
  398. // If it is drushBatch then this operation is already run in the
  399. // self::validateDrushParams().
  400. if (!$this->drushBatch) {
  401. // Setup the batch operations and save the variables.
  402. $operations[] = ['devel_generate_operation',
  403. [$this, 'batchContentPreNode', $values],
  404. ];
  405. }
  406. // Add the kill operation.
  407. if ($values['kill']) {
  408. $operations[] = ['devel_generate_operation',
  409. [$this, 'batchContentKill', $values],
  410. ];
  411. }
  412. // Add the operations to create the nodes.
  413. for ($num = 0; $num < $values['num']; $num++) {
  414. $operations[] = ['devel_generate_operation',
  415. [$this, 'batchContentAddNode', $values],
  416. ];
  417. }
  418. // Set the batch.
  419. $batch = [
  420. 'title' => $this->t('Generating Content'),
  421. 'operations' => $operations,
  422. 'finished' => 'devel_generate_batch_finished',
  423. 'file' => drupal_get_path('module', 'devel_generate') . '/devel_generate.batch.inc',
  424. ];
  425. batch_set($batch);
  426. if ($this->drushBatch) {
  427. drush_backend_batch_process();
  428. }
  429. }
  430. /**
  431. * Batch wrapper for calling ContentPreNode.
  432. */
  433. public function batchContentPreNode($vars, &$context) {
  434. $context['results'] = $vars;
  435. $context['results']['num'] = 0;
  436. $context['results']['num_translations'] = 0;
  437. $this->develGenerateContentPreNode($context['results']);
  438. }
  439. /**
  440. * Batch wrapper for calling ContentAddNode.
  441. */
  442. public function batchContentAddNode($vars, &$context) {
  443. if ($this->drushBatch) {
  444. $this->develGenerateContentAddNode($vars);
  445. }
  446. else {
  447. $this->develGenerateContentAddNode($context['results']);
  448. }
  449. $context['results']['num']++;
  450. if (!empty($vars['num_translations'])) {
  451. $context['results']['num_translations'] += $vars['num_translations'];
  452. }
  453. }
  454. /**
  455. * Batch wrapper for calling ContentKill.
  456. */
  457. public function batchContentKill($vars, &$context) {
  458. if ($this->drushBatch) {
  459. $this->contentKill($vars);
  460. }
  461. else {
  462. $this->contentKill($context['results']);
  463. }
  464. }
  465. /**
  466. * {@inheritdoc}
  467. */
  468. public function validateDrushParams(array $args, array $options = []) {
  469. $add_language = StringUtils::csvToArray($options['languages']);
  470. // Intersect with the enabled languages to make sure the language args
  471. // passed are actually enabled.
  472. $valid_languages = array_keys($this->languageManager->getLanguages(LanguageInterface::STATE_ALL));
  473. $values['add_language'] = array_intersect($add_language, $valid_languages);
  474. $translate_language = StringUtils::csvToArray($options['translations']);
  475. $values['translate_language'] = array_intersect($translate_language, $valid_languages);
  476. $values['add_type_label'] = $options['add-type-label'];
  477. $values['kill'] = $options['kill'];
  478. $values['feedback'] = $options['feedback'];
  479. $values['title_length'] = 6;
  480. $values['num'] = array_shift($args);
  481. $values['max_comments'] = array_shift($args);
  482. $all_types = array_keys(node_type_get_names());
  483. $default_types = array_intersect(['page', 'article'], $all_types);
  484. $selected_types = StringUtils::csvToArray($options['bundles'] ?: $default_types);
  485. if (empty($selected_types)) {
  486. throw new \Exception(dt('No content types available'));
  487. }
  488. $values['node_types'] = array_combine($selected_types, $selected_types);
  489. $node_types = array_filter($values['node_types']);
  490. if (!empty($values['kill']) && empty($node_types)) {
  491. throw new \Exception(dt('To delete content, please provide the content types (--bundles)'));
  492. }
  493. // Checks for any missing content types before generating nodes.
  494. if (array_diff($node_types, $all_types)) {
  495. throw new \Exception(dt('One or more content types have been entered that don\'t exist on this site'));
  496. }
  497. $values['authors'] = is_null($options['authors']) ? [] : explode(',',
  498. $options['authors']);
  499. if ($this->isBatch($values['num'], $values['max_comments'])) {
  500. $this->drushBatch = TRUE;
  501. $this->develGenerateContentPreNode($values);
  502. }
  503. return $values;
  504. }
  505. /**
  506. * Determines if the content should be generated in batch mode.
  507. */
  508. protected function isBatch($content_count, $comment_count) {
  509. return $content_count >= 50 || $comment_count >= 10;
  510. }
  511. /**
  512. * Deletes all nodes of given node types.
  513. *
  514. * @param array $values
  515. * The input values from the settings form.
  516. */
  517. protected function contentKill(array $values) {
  518. $nids = $this->nodeStorage->getQuery()
  519. ->condition('type', $values['node_types'], 'IN')
  520. ->execute();
  521. if (!empty($nids)) {
  522. $nodes = $this->nodeStorage->loadMultiple($nids);
  523. $this->nodeStorage->delete($nodes);
  524. $this->setMessage($this->t('Deleted %count nodes.', ['%count' => count($nids)]));
  525. }
  526. }
  527. /**
  528. * Preprocesses $results before adding content.
  529. *
  530. * @param array $results
  531. * Results information.
  532. */
  533. protected function develGenerateContentPreNode(array &$results) {
  534. $authors = $results['authors'];
  535. // Remove non-selected users. !== 0 will leave the Anonymous user in if it
  536. // was selected on the form or entered in the drush parameters.
  537. $authors = array_filter($authors, function ($k) {
  538. return $k !== 0;
  539. });
  540. // If no users are specified then get a random set up to a maximum of 50.
  541. // There is no direct way randomise the selection using entity queries, so
  542. // we use a database query instead.
  543. if (empty($authors)) {
  544. $query = $this->database->select('users', 'u')
  545. ->fields('u', ['uid'])
  546. ->range(0, 50)
  547. ->orderRandom();
  548. $authors = $query->execute()->fetchCol();
  549. }
  550. $results['users'] = $authors;
  551. }
  552. /**
  553. * Create one node. Used by both batch and non-batch code branches.
  554. *
  555. * @param array $results
  556. * Results information.
  557. */
  558. protected function develGenerateContentAddNode(array &$results) {
  559. if (!isset($results['time_range'])) {
  560. $results['time_range'] = 0;
  561. }
  562. $users = $results['users'];
  563. $node_type = array_rand($results['node_types']);
  564. $uid = $users[array_rand($users)];
  565. // Add the content type label if required.
  566. $title_prefix = $results['add_type_label'] ? $this->nodeTypeStorage->load($node_type)->label() . ' - ' : '';
  567. $values = [
  568. 'nid' => NULL,
  569. 'type' => $node_type,
  570. 'title' => $title_prefix . $this->getRandom()->sentences(mt_rand(1, $results['title_length']), TRUE),
  571. 'uid' => $uid,
  572. 'revision' => mt_rand(0, 1),
  573. 'moderation_state' => 'published',
  574. 'status' => TRUE,
  575. 'promote' => mt_rand(0, 1),
  576. 'created' => $this->time->getRequestTime() - mt_rand(0, $results['time_range']),
  577. ];
  578. if (isset($results['add_language'])) {
  579. $values['langcode'] = $this->getLangcode($results['add_language']);
  580. }
  581. $node = $this->nodeStorage->create($values);
  582. // A flag to let hook_node_insert() implementations know that this is a
  583. // generated node.
  584. $node->devel_generate = $results;
  585. // Populate all fields with sample values.
  586. $this->populateFields($node);
  587. // See devel_generate_entity_insert() for actions that happen before and
  588. // after this save.
  589. $node->save();
  590. // Add url alias if required.
  591. if (!empty($results['add_alias'])) {
  592. $path_alias = $this->aliasStorage->create([
  593. 'path' => '/node/' . $node->id(),
  594. 'alias' => '/node-' . $node->id() . '-' . $node->bundle(),
  595. 'langcode' => $values['langcode'] ?? LanguageInterface::LANGCODE_NOT_SPECIFIED,
  596. ]);
  597. $path_alias->save();
  598. }
  599. // Add translations.
  600. if (isset($results['translate_language']) && !empty($results['translate_language'])) {
  601. $this->develGenerateContentAddNodeTranslation($results, $node);
  602. }
  603. }
  604. /**
  605. * Create translation for the given node.
  606. *
  607. * @param array $results
  608. * Results array.
  609. * @param \Drupal\node\NodeInterface $node
  610. * Node to add translations to.
  611. *
  612. * @throws \Drupal\Core\Entity\EntityStorageException
  613. */
  614. protected function develGenerateContentAddNodeTranslation(array &$results, NodeInterface $node) {
  615. if (is_null($this->contentTranslationManager)) {
  616. return;
  617. }
  618. if (!$this->contentTranslationManager->isEnabled('node', $node->getType())) {
  619. return;
  620. }
  621. if ($node->langcode == LanguageInterface::LANGCODE_NOT_SPECIFIED || $node->langcode == LanguageInterface::LANGCODE_NOT_APPLICABLE) {
  622. return;
  623. }
  624. if (!isset($results['num_translations'])) {
  625. $results['num_translations'] = 0;
  626. }
  627. // Translate node to each target language.
  628. $skip_languages = [
  629. LanguageInterface::LANGCODE_NOT_SPECIFIED,
  630. LanguageInterface::LANGCODE_NOT_APPLICABLE,
  631. $node->langcode->value,
  632. ];
  633. foreach ($results['translate_language'] as $langcode) {
  634. if (in_array($langcode, $skip_languages)) {
  635. continue;
  636. }
  637. $translation_node = $node->addTranslation($langcode);
  638. $translation_node->devel_generate = $results;
  639. $translation_node->setTitle($node->getTitle() . ' (' . $langcode . ')');
  640. $this->populateFields($translation_node);
  641. $translation_node->save();
  642. if ($translation_node->id() > 0 && !empty($results['add_alias'])) {
  643. $path_alias = $this->aliasStorage->create([
  644. 'path' => '/node/' . $translation_node->id(),
  645. 'alias' => '/node-' . $translation_node->id() . '-' . $translation_node->bundle() . '-' . $langcode,
  646. 'langcode' => $langcode,
  647. ]);
  648. $path_alias->save();
  649. }
  650. $results['num_translations']++;
  651. }
  652. }
  653. }