3 namespace Drupal\devel_generate\Plugin\DevelGenerate;
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;
20 * Provides a ContentDevelGenerate plugin.
24 * label = @Translation("content"),
25 * description = @Translation("Generate a given number of content. Optionally delete current content."),
27 * permission = "administer devel_generate",
36 class ContentDevelGenerate extends DevelGenerateBase implements ContainerFactoryPluginInterface {
41 * @var \Drupal\Core\Entity\EntityStorageInterface
43 protected $nodeStorage;
46 * The node type storage.
48 * @var \Drupal\Core\Entity\EntityStorageInterface
50 protected $nodeTypeStorage;
55 * @var \Drupal\Core\Extension\ModuleHandlerInterface
57 protected $moduleHandler;
60 * The comment manager service.
62 * @var \Drupal\comment\CommentManagerInterface
64 protected $commentManager;
67 * The language manager.
69 * @var \Drupal\Core\Language\LanguageManagerInterface
71 protected $languageManager;
74 * The url generator service.
76 * @var \Drupal\Core\Routing\UrlGeneratorInterface
78 protected $urlGenerator;
81 * The date formatter service.
83 * @var \Drupal\Core\Datetime\DateFormatterInterface
85 protected $dateFormatter;
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
95 * @param \Drupal\Core\Entity\EntityStorageInterface $node_type_storage
96 * The node type storage.
97 * @param \Drupal\Core\Extension\ModuleHandlerInterface $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.
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);
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;
123 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
124 $entity_manager = $container->get('entity.manager');
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')
140 public function settingsForm(array $form, FormStateInterface $form_state) {
141 $types = $this->nodeTypeStorage->loadMultiple();
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);
151 foreach ($types as $type) {
152 $options[$type->id()] = array(
153 'type' => array('#markup' => $type->label()),
155 if ($this->commentManager) {
156 $comment_fields = $this->commentManager->getFields('node');
157 $map = array($this->t('Hidden'), $this->t('Closed'), $this->t('Open'));
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']],
172 // @todo Refactor display of comment fields.
173 if (!empty($fields)) {
174 $options[$type->id()]['comments'] = array(
176 '#theme' => 'item_list',
182 $options[$type->id()]['comments'] = $this->t('No comment fields');
188 'type' => $this->t('Content type'),
190 if ($this->commentManager) {
191 $header['comments'] = array(
192 'data' => $this->t('Comments'),
193 'class' => array(RESPONSIVE_PRIORITY_MEDIUM),
197 $form['node_types'] = array(
198 '#type' => 'tableselect',
199 '#header' => $header,
200 '#options' => $options,
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'),
208 $form['num'] = array(
210 '#title' => $this->t('How many nodes would you like to generate?'),
211 '#default_value' => $this->getSetting('num'),
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');
220 $form['time_range'] = array(
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,
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'),
234 '#access' => $this->moduleHandler->moduleExists('comment'),
236 $form['title_length'] = array(
238 '#title' => $this->t('Maximum number of words in titles'),
239 '#default_value' => $this->getSetting('title_length'),
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,
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'),
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();
265 $form['add_language'] = array(
267 '#title' => $this->t('Set language on nodes'),
269 '#description' => $this->t('Requires locale.module'),
270 '#options' => $options,
271 '#default_value' => array(
272 $this->languageManager->getDefaultLanguage()->getId(),
276 $form['#redirect'] = FALSE;
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'));
293 protected function generateElements(array $values) {
294 if ($values['num'] <= 50 && $values['max_comments'] <= 10) {
295 $this->generateContent($values);
298 $this->generateBatchContent($values);
303 * Method responsible for creating content when
304 * the number of elements is less than 50.
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);
312 if (!empty($values['node_types'])) {
314 $this->develGenerateContentPreNode($values);
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) {
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');
325 $this->setMessage($this->formatPlural($values['num'], '1 node created.', 'Finished creating @count nodes'));
329 * Method responsible for creating content when
330 * the number of elements is greater than 50.
332 private function generateBatchContent($values) {
333 // Setup the batch operations and save the variables.
334 $operations[] = array('devel_generate_operation', array($this, 'batchContentPreNode', $values));
336 // Add the kill operation.
337 if ($values['kill']) {
338 $operations[] = array('devel_generate_operation', array($this, 'batchContentKill', $values));
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));
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',
356 public function batchContentPreNode($vars, &$context) {
357 $context['results'] = $vars;
358 $context['results']['num'] = 0;
359 $this->develGenerateContentPreNode($context['results']);
362 public function batchContentAddNode($vars, &$context) {
363 $this->develGenerateContentAddNode($context['results']);
364 $context['results']['num']++;
367 public function batchContentKill($vars, &$context) {
368 $this->contentKill($context['results']);
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)));
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));
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'));
396 if (empty($selected_types)) {
397 return drush_set_error('DEVEL_GENERATE_NO_CONTENT_TYPES', dt('No content types available'));
400 $values['node_types'] = array_combine($selected_types, $selected_types);
401 $node_types = array_filter($values['node_types']);
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.'));
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'));
416 * Deletes all nodes of given node types.
418 * @param array $values
419 * The input values from the settings form.
421 protected function contentKill($values) {
422 $nids = $this->nodeStorage->getQuery()
423 ->condition('type', $values['node_types'], 'IN')
427 $nodes = $this->nodeStorage->loadMultiple($nids);
428 $this->nodeStorage->delete($nodes);
429 $this->setMessage($this->t('Deleted %count nodes.', array('%count' => count($nids))));
434 * Return the same array passed as parameter
435 * but with an array of uids for the key 'users'.
437 protected function develGenerateContentPreNode(&$results) {
439 $users = $this->getUsers();
440 $users = array_merge($users, array('0'));
441 $results['users'] = $users;
445 * Create one node. Used by both batch and non-batch code branches.
447 protected function develGenerateContentAddNode(&$results) {
448 if (!isset($results['time_range'])) {
449 $results['time_range'] = 0;
451 $users = $results['users'];
453 $node_type = array_rand(array_filter($results['node_types']));
454 $uid = $users[array_rand($users)];
456 $node = $this->nodeStorage->create(array(
458 'type' => $node_type,
459 'title' => $this->getRandom()->sentences(mt_rand(1, $results['title_length']), TRUE),
461 'revision' => mt_rand(0, 1),
463 'promote' => mt_rand(0, 1),
464 'created' => REQUEST_TIME - mt_rand(0, $results['time_range']),
465 'langcode' => $this->getLangcode($results),
468 // A flag to let hook_node_insert() implementations know that this is a
470 $node->devel_generate = $results;
472 // Populate all fields with sample values.
473 $this->populateFields($node);
475 // See devel_generate_node_insert() for actions that happen before and after
481 * Determine language based on $results.
483 protected function getLangcode($results) {
484 if (isset($results['add_language'])) {
485 $langcodes = $results['add_language'];
486 $langcode = $langcodes[array_rand($langcodes)];
489 $langcode = $this->languageManager->getDefaultLanguage()->getId();
495 * Retrive 50 uids from the database.
497 protected function getUsers() {
499 $result = db_query_range("SELECT uid FROM {users}", 0, 50);
500 foreach ($result as $record) {
501 $users[] = $record->uid;