Version 1
[yaffs-website] / web / modules / contrib / devel / devel_generate / src / Plugin / DevelGenerate / ContentDevelGenerate.php
1 <?php
2
3 namespace Drupal\devel_generate\Plugin\DevelGenerate;
4
5 use Drupal\comment\CommentManagerInterface;
6 use Drupal\Component\Render\FormattableMarkup;
7 use Drupal\Core\Datetime\DateFormatterInterface;
8 use Drupal\Core\Entity\EntityStorageInterface;
9 use Drupal\Core\Extension\ModuleHandlerInterface;
10 use Drupal\Core\Form\FormStateInterface;
11 use Drupal\Core\Language\LanguageInterface;
12 use Drupal\Core\Language\LanguageManagerInterface;
13 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
14 use Drupal\Core\Routing\UrlGeneratorInterface;
15 use Drupal\devel_generate\DevelGenerateBase;
16 use Drupal\field\Entity\FieldConfig;
17 use Symfony\Component\DependencyInjection\ContainerInterface;
18
19 /**
20  * Provides a ContentDevelGenerate plugin.
21  *
22  * @DevelGenerate(
23  *   id = "content",
24  *   label = @Translation("content"),
25  *   description = @Translation("Generate a given number of content. Optionally delete current content."),
26  *   url = "content",
27  *   permission = "administer devel_generate",
28  *   settings = {
29  *     "num" = 50,
30  *     "kill" = FALSE,
31  *     "max_comments" = 0,
32  *     "title_length" = 4
33  *   }
34  * )
35  */
36 class ContentDevelGenerate extends DevelGenerateBase implements ContainerFactoryPluginInterface {
37
38   /**
39    * The node storage.
40    *
41    * @var \Drupal\Core\Entity\EntityStorageInterface
42    */
43   protected $nodeStorage;
44
45   /**
46    * The node type storage.
47    *
48    * @var \Drupal\Core\Entity\EntityStorageInterface
49    */
50   protected $nodeTypeStorage;
51
52   /**
53    * The module handler.
54    *
55    * @var \Drupal\Core\Extension\ModuleHandlerInterface
56    */
57   protected $moduleHandler;
58
59   /**
60    * The comment manager service.
61    *
62    * @var \Drupal\comment\CommentManagerInterface
63    */
64   protected $commentManager;
65
66   /**
67    * The language manager.
68    *
69    * @var \Drupal\Core\Language\LanguageManagerInterface
70    */
71   protected $languageManager;
72
73   /**
74    * The url generator service.
75    *
76    * @var \Drupal\Core\Routing\UrlGeneratorInterface
77    */
78   protected $urlGenerator;
79
80   /**
81    * The date formatter service.
82    *
83    * @var \Drupal\Core\Datetime\DateFormatterInterface
84    */
85   protected $dateFormatter;
86
87   /**
88    * @param array $configuration
89    *   A configuration array containing information about the plugin instance.
90    * @param string $plugin_id
91    *   The plugin ID for the plugin instance.
92    * @param array $plugin_definition
93    * @param \Drupal\Core\Entity\EntityStorageInterface $node_storage
94    *   The node storage.
95    * @param \Drupal\Core\Entity\EntityStorageInterface $node_type_storage
96    *   The node type storage.
97    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
98    *   The module handler.
99    * @param \Drupal\comment\CommentManagerInterface $comment_manager
100    *   The comment manager service.
101    * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
102    *   The language manager.
103    * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator
104    *   The url generator service.
105    * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
106    *   The date formatter service.
107    */
108   public function __construct(array $configuration, $plugin_id, array $plugin_definition, EntityStorageInterface $node_storage, EntityStorageInterface $node_type_storage, ModuleHandlerInterface $module_handler, CommentManagerInterface $comment_manager = NULL, LanguageManagerInterface $language_manager, UrlGeneratorInterface $url_generator, DateFormatterInterface $date_formatter) {
109     parent::__construct($configuration, $plugin_id, $plugin_definition);
110
111     $this->moduleHandler = $module_handler;
112     $this->nodeStorage = $node_storage;
113     $this->nodeTypeStorage = $node_type_storage;
114     $this->commentManager = $comment_manager;
115     $this->languageManager = $language_manager;
116     $this->urlGenerator = $url_generator;
117     $this->dateFormatter = $date_formatter;
118   }
119
120   /**
121    * {@inheritdoc}
122    */
123   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
124     $entity_manager = $container->get('entity.manager');
125     return new static(
126       $configuration, $plugin_id, $plugin_definition,
127       $entity_manager->getStorage('node'),
128       $entity_manager->getStorage('node_type'),
129       $container->get('module_handler'),
130       $container->has('comment.manager') ? $container->get('comment.manager') : NULL,
131       $container->get('language_manager'),
132       $container->get('url_generator'),
133       $container->get('date.formatter')
134     );
135   }
136
137   /**
138    * {@inheritdoc}
139    */
140   public function settingsForm(array $form, FormStateInterface $form_state) {
141     $types = $this->nodeTypeStorage->loadMultiple();
142
143     if (empty($types)) {
144       $create_url = $this->urlGenerator->generateFromRoute('node.type_add');
145       $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>', array(':create-type' => $create_url)), 'error', FALSE);
146       return;
147     }
148
149     $options = array();
150
151     foreach ($types as $type) {
152       $options[$type->id()] = array(
153         'type' => array('#markup' => $type->label()),
154       );
155       if ($this->commentManager) {
156         $comment_fields = $this->commentManager->getFields('node');
157         $map = array($this->t('Hidden'), $this->t('Closed'), $this->t('Open'));
158
159         $fields = array();
160         foreach ($comment_fields as $field_name => $info) {
161           // Find all comment fields for the bundle.
162           if (in_array($type->id(), $info['bundles'])) {
163             $instance = FieldConfig::loadByName('node', $type->id(), $field_name);
164             $default_value = $instance->getDefaultValueLiteral();
165             $default_mode = reset($default_value);
166             $fields[] = new FormattableMarkup('@field: @state', array(
167               '@field' => $instance->label(),
168               '@state' => $map[$default_mode['status']],
169             ));
170           }
171         }
172         // @todo Refactor display of comment fields.
173         if (!empty($fields)) {
174           $options[$type->id()]['comments'] = array(
175             'data' => array(
176               '#theme' => 'item_list',
177               '#items' => $fields,
178             ),
179           );
180         }
181         else {
182           $options[$type->id()]['comments'] = $this->t('No comment fields');
183         }
184       }
185     }
186
187     $header = array(
188       'type' => $this->t('Content type'),
189     );
190     if ($this->commentManager) {
191       $header['comments'] = array(
192         'data' => $this->t('Comments'),
193         'class' => array(RESPONSIVE_PRIORITY_MEDIUM),
194       );
195     }
196
197     $form['node_types'] = array(
198       '#type' => 'tableselect',
199       '#header' => $header,
200       '#options' => $options,
201     );
202
203     $form['kill'] = array(
204       '#type' => 'checkbox',
205       '#title' => $this->t('<strong>Delete all content</strong> in these content types before generating new content.'),
206       '#default_value' => $this->getSetting('kill'),
207     );
208     $form['num'] = array(
209       '#type' => 'number',
210       '#title' => $this->t('How many nodes would you like to generate?'),
211       '#default_value' => $this->getSetting('num'),
212       '#required' => TRUE,
213       '#min' => 0,
214     );
215
216     $options = array(1 => $this->t('Now'));
217     foreach (array(3600, 86400, 604800, 2592000, 31536000) as $interval) {
218       $options[$interval] = $this->dateFormatter->formatInterval($interval, 1) . ' ' . $this->t('ago');
219     }
220     $form['time_range'] = array(
221       '#type' => 'select',
222       '#title' => $this->t('How far back in time should the nodes be dated?'),
223       '#description' => $this->t('Node creation dates will be distributed randomly from the current time, back to the selected time.'),
224       '#options' => $options,
225       '#default_value' => 604800,
226     );
227
228     $form['max_comments'] = array(
229       '#type' => $this->moduleHandler->moduleExists('comment') ? 'number' : 'value',
230       '#title' => $this->t('Maximum number of comments per node.'),
231       '#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.'),
232       '#default_value' => $this->getSetting('max_comments'),
233       '#min' => 0,
234       '#access' => $this->moduleHandler->moduleExists('comment'),
235     );
236     $form['title_length'] = array(
237       '#type' => 'number',
238       '#title' => $this->t('Maximum number of words in titles'),
239       '#default_value' => $this->getSetting('title_length'),
240       '#required' => TRUE,
241       '#min' => 1,
242       '#max' => 255,
243     );
244     $form['add_alias'] = array(
245       '#type' => 'checkbox',
246       '#disabled' => !$this->moduleHandler->moduleExists('path'),
247       '#description' => $this->t('Requires path.module'),
248       '#title' => $this->t('Add an url alias for each node.'),
249       '#default_value' => FALSE,
250     );
251     $form['add_statistics'] = array(
252       '#type' => 'checkbox',
253       '#title' => $this->t('Add statistics for each node (node_counter table).'),
254       '#default_value' => TRUE,
255       '#access' => $this->moduleHandler->moduleExists('statistics'),
256     );
257
258     $options = array();
259     // We always need a language.
260     $languages = $this->languageManager->getLanguages(LanguageInterface::STATE_ALL);
261     foreach ($languages as $langcode => $language) {
262       $options[$langcode] = $language->getName();
263     }
264
265     $form['add_language'] = array(
266       '#type' => 'select',
267       '#title' => $this->t('Set language on nodes'),
268       '#multiple' => TRUE,
269       '#description' => $this->t('Requires locale.module'),
270       '#options' => $options,
271       '#default_value' => array(
272         $this->languageManager->getDefaultLanguage()->getId(),
273       ),
274     );
275
276     $form['#redirect'] = FALSE;
277
278     return $form;
279   }
280
281   /**
282    * {@inheritdoc}
283    */
284   function settingsFormValidate(array $form, FormStateInterface $form_state) {
285     if (!array_filter($form_state->getValue('node_types'))) {
286       $form_state->setErrorByName('node_types', $this->t('Please select at least one content type'));
287     }
288   }
289
290   /**
291    * {@inheritdoc}
292    */
293   protected function generateElements(array $values) {
294     if ($values['num'] <= 50 && $values['max_comments'] <= 10) {
295       $this->generateContent($values);
296     }
297     else {
298       $this->generateBatchContent($values);
299     }
300   }
301
302   /**
303    * Method responsible for creating content when
304    * the number of elements is less than 50.
305    */
306   private function generateContent($values) {
307     $values['node_types'] = array_filter($values['node_types']);
308     if (!empty($values['kill']) && $values['node_types']) {
309       $this->contentKill($values);
310     }
311
312     if (!empty($values['node_types'])) {
313       // Generate nodes.
314       $this->develGenerateContentPreNode($values);
315       $start = time();
316       for ($i = 1; $i <= $values['num']; $i++) {
317         $this->develGenerateContentAddNode($values);
318         if (function_exists('drush_log') && $i % drush_get_option('feedback', 1000) == 0) {
319           $now = time();
320           drush_log(dt('Completed @feedback nodes (@rate nodes/min)', array('@feedback' => drush_get_option('feedback', 1000), '@rate' => (drush_get_option('feedback', 1000) * 60) / ($now - $start))), 'ok');
321           $start = $now;
322         }
323       }
324     }
325     $this->setMessage($this->formatPlural($values['num'], '1 node created.', 'Finished creating @count nodes'));
326   }
327
328   /**
329    * Method responsible for creating content when
330    * the number of elements is greater than 50.
331    */
332   private function generateBatchContent($values) {
333     // Setup the batch operations and save the variables.
334     $operations[] = array('devel_generate_operation', array($this, 'batchContentPreNode', $values));
335
336     // Add the kill operation.
337     if ($values['kill']) {
338       $operations[] = array('devel_generate_operation', array($this, 'batchContentKill', $values));
339     }
340
341     // Add the operations to create the nodes.
342     for ($num = 0; $num < $values['num']; $num ++) {
343       $operations[] = array('devel_generate_operation', array($this, 'batchContentAddNode', $values));
344     }
345
346     // Start the batch.
347     $batch = array(
348       'title' => $this->t('Generating Content'),
349       'operations' => $operations,
350       'finished' => 'devel_generate_batch_finished',
351       'file' => drupal_get_path('module', 'devel_generate') . '/devel_generate.batch.inc',
352     );
353     batch_set($batch);
354   }
355
356   public function batchContentPreNode($vars, &$context) {
357     $context['results'] = $vars;
358     $context['results']['num'] = 0;
359     $this->develGenerateContentPreNode($context['results']);
360   }
361
362   public function batchContentAddNode($vars, &$context) {
363     $this->develGenerateContentAddNode($context['results']);
364     $context['results']['num']++;
365   }
366
367   public function batchContentKill($vars, &$context) {
368     $this->contentKill($context['results']);
369   }
370
371   /**
372    * {@inheritdoc}
373    */
374   public function validateDrushParams($args) {
375     $add_language = drush_get_option('languages');
376     if (!empty($add_language)) {
377       $add_language = explode(',', str_replace(' ', '', $add_language));
378       // Intersect with the enabled languages to make sure the language args
379       // passed are actually enabled.
380       $values['values']['add_language'] = array_intersect($add_language, array_keys($this->languageManager->getLanguages(LanguageInterface::STATE_ALL)));
381     }
382
383     $values['kill'] = drush_get_option('kill');
384     $values['title_length'] = 6;
385     $values['num'] = array_shift($args);
386     $values['max_comments'] = array_shift($args);
387     $all_types = array_keys(node_type_get_names());
388     $default_types = array_intersect(array('page', 'article'), $all_types);
389     $selected_types = _convert_csv_to_array(drush_get_option('types', $default_types));
390
391     // Validates the input format for content types option.
392     if (drush_get_option('types', $default_types) === TRUE) {
393       return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Wrong syntax or no content type selected. The correct syntax uses "=", eg.: --types=page,article'));
394     }
395
396     if (empty($selected_types)) {
397       return drush_set_error('DEVEL_GENERATE_NO_CONTENT_TYPES', dt('No content types available'));
398     }
399
400     $values['node_types'] = array_combine($selected_types, $selected_types);
401     $node_types = array_filter($values['node_types']);
402
403     if (!empty($values['kill']) && empty($node_types)) {
404       return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('Please provide content type (--types) in which you want to delete the content.'));
405     }
406
407     // Checks for any missing content types before generating nodes.
408     if (array_diff($node_types, $all_types)) {
409       return drush_set_error('DEVEL_GENERATE_INVALID_INPUT', dt('One or more content types have been entered that don\'t exist on this site'));
410     }
411
412     return $values;
413   }
414
415   /**
416    * Deletes all nodes of given node types.
417    *
418    * @param array $values
419    *   The input values from the settings form.
420    */
421   protected function contentKill($values) {
422     $nids = $this->nodeStorage->getQuery()
423       ->condition('type', $values['node_types'], 'IN')
424       ->execute();
425
426     if (!empty($nids)) {
427       $nodes = $this->nodeStorage->loadMultiple($nids);
428       $this->nodeStorage->delete($nodes);
429       $this->setMessage($this->t('Deleted %count nodes.', array('%count' => count($nids))));
430     }
431   }
432
433   /**
434    * Return the same array passed as parameter
435    * but with an array of uids for the key 'users'.
436    */
437   protected function develGenerateContentPreNode(&$results) {
438     // Get user id.
439     $users = $this->getUsers();
440     $users = array_merge($users, array('0'));
441     $results['users'] = $users;
442   }
443
444   /**
445    * Create one node. Used by both batch and non-batch code branches.
446    */
447   protected function develGenerateContentAddNode(&$results) {
448     if (!isset($results['time_range'])) {
449       $results['time_range'] = 0;
450     }
451     $users = $results['users'];
452
453     $node_type = array_rand(array_filter($results['node_types']));
454     $uid = $users[array_rand($users)];
455
456     $node = $this->nodeStorage->create(array(
457       'nid' => NULL,
458       'type' => $node_type,
459       'title' => $this->getRandom()->sentences(mt_rand(1, $results['title_length']), TRUE),
460       'uid' => $uid,
461       'revision' => mt_rand(0, 1),
462       'status' => TRUE,
463       'promote' => mt_rand(0, 1),
464       'created' => REQUEST_TIME - mt_rand(0, $results['time_range']),
465       'langcode' => $this->getLangcode($results),
466     ));
467
468     // A flag to let hook_node_insert() implementations know that this is a
469     // generated node.
470     $node->devel_generate = $results;
471
472     // Populate all fields with sample values.
473     $this->populateFields($node);
474
475     // See devel_generate_node_insert() for actions that happen before and after
476     // this save.
477     $node->save();
478   }
479
480   /**
481    * Determine language based on $results.
482    */
483   protected function getLangcode($results) {
484     if (isset($results['add_language'])) {
485       $langcodes = $results['add_language'];
486       $langcode = $langcodes[array_rand($langcodes)];
487     }
488     else {
489       $langcode = $this->languageManager->getDefaultLanguage()->getId();
490     }
491     return $langcode;
492   }
493
494   /**
495    * Retrive 50 uids from the database.
496    */
497   protected function getUsers() {
498     $users = array();
499     $result = db_query_range("SELECT uid FROM {users}", 0, 50);
500     foreach ($result as $record) {
501       $users[] = $record->uid;
502     }
503     return $users;
504   }
505
506 }