class BlockContentDevelGenerate

Provides a BlockContentDevelGenerate plugin.

Plugin annotation


@DevelGenerate(
  id = "block_content",
  label = @Translation("Block Content"),
  description = @Translation("Generate a given number of Block content blocks. Optionally delete current blocks."),
  url = "block-content",
  permission = "administer devel_generate",
  settings = {
    "num" = 50,
    "kill" = FALSE,
    "title_length" = 4,
    "add_type_label" = FALSE,
    "reusable" = TRUE
  },
)

Hierarchy

Expanded class hierarchy of BlockContentDevelGenerate

File

devel_generate/src/Plugin/DevelGenerate/BlockContentDevelGenerate.php, line 34

Namespace

Drupal\devel_generate\Plugin\DevelGenerate
View source
class BlockContentDevelGenerate extends DevelGenerateBase implements ContainerFactoryPluginInterface {
  
  /**
   * The block content storage.
   */
  protected EntityStorageInterface $blockContentStorage;
  
  /**
   * The block content type storage.
   */
  protected EntityStorageInterface $blockContentTypeStorage;
  
  /**
   * The extension path resolver service.
   */
  protected ExtensionPathResolver $extensionPathResolver;
  
  /**
   * The entity type bundle info service.
   */
  protected EntityTypeBundleInfoInterface $entityTypeBundleInfo;
  
  /**
   * The content translation manager.
   */
  protected ?ContentTranslationManagerInterface $contentTranslationManager;
  
  /**
   * The Drush batch flag.
   */
  protected bool $drushBatch = FALSE;
  
  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) : static {
    $entity_type_manager = $container->get('entity_type.manager');
    // @phpstan-ignore ternary.alwaysTrue (False positive)
    $content_translation_manager = $container->has('content_translation.manager') ? $container->get('content_translation.manager') : NULL;
    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
    $instance->blockContentStorage = $entity_type_manager->getStorage('block_content');
    $instance->blockContentTypeStorage = $entity_type_manager->getStorage('block_content_type');
    $instance->extensionPathResolver = $container->get('extension.path.resolver');
    $instance->entityTypeBundleInfo = $container->get('entity_type.bundle.info');
    $instance->contentTranslationManager = $content_translation_manager;
    return $instance;
  }
  
  /**
   * {@inheritdoc}
   */
  public function settingsForm(array $form, FormStateInterface $form_state) : array {
    /** @var \Drupal\block_content\BlockContentTypeInterface[] $blockTypes */
    $blockTypes = $this->blockContentTypeStorage
      ->loadMultiple();
    $options = [];
    foreach ($blockTypes as $type) {
      $options[$type->id()] = [
        'type' => [
          'label' => $type->label(),
          'description' => $type->getDescription(),
        ],
      ];
    }
    $header = [
      'type' => $this->t('Block Content type'),
      'description' => $this->t('Description'),
    ];
    $form['block_types'] = [
      '#type' => 'tableselect',
      '#header' => $header,
      '#options' => $options,
    ];
    $form['kill'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('<strong>Delete all content</strong> in these block types before generating new content.'),
      '#default_value' => $this->getSetting('kill'),
    ];
    $form['num'] = [
      '#type' => 'number',
      '#title' => $this->t('How many blocks would you like to generate?'),
      '#default_value' => $this->getSetting('num'),
      '#required' => TRUE,
      '#min' => 0,
    ];
    $form['title_length'] = [
      '#type' => 'number',
      '#title' => $this->t('Maximum number of words in block descriptions'),
      '#default_value' => $this->getSetting('title_length'),
      '#required' => TRUE,
      '#min' => 1,
      '#max' => 255,
    ];
    $form['skip_fields'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Fields to leave empty'),
      '#description' => $this->t('Enter the field names as a comma-separated list. These will be skipped and have a default value in the generated content.'),
      '#default_value' => NULL,
    ];
    $form['base_fields'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Base fields to populate'),
      '#description' => $this->t('Enter the field names as a comma-separated list. These will be populated.'),
      '#default_value' => NULL,
    ];
    $form['reusable'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Reusable blocks'),
      '#description' => $this->t('This will mark the blocks to be created as reusable.'),
      '#default_value' => $this->getSetting('reusable'),
    ];
    $form['add_type_label'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Prefix the title with the block type label.'),
      '#description' => $this->t('This will not count against the maximum number of title words specified above.'),
      '#default_value' => $this->getSetting('add_type_label'),
    ];
    // Add the language and translation options.
    $form += $this->getLanguageForm('blocks');
    return $form;
  }
  
  /**
   * {@inheritdoc}
   */
  public function settingsFormValidate(array $form, FormStateInterface $form_state) : void {
    if (array_filter($form_state->getValue('block_types')) === []) {
      $form_state->setErrorByName('block_types', $this->t('Please select at least one block type'));
    }
    $skip_fields = is_null($form_state->getValue('skip_fields')) ? [] : self::csvToArray($form_state->getValue('skip_fields'));
    $base_fields = is_null($form_state->getValue('base_fields')) ? [] : self::csvToArray($form_state->getValue('base_fields'));
    $form_state->setValue('skip_fields', $skip_fields);
    $form_state->setValue('base_fields', $base_fields);
  }
  
  /**
   * {@inheritdoc}
   */
  public function validateDrushParams(array $args, array $options = []) : array {
    $add_language = self::csvToArray($options['languages']);
    // Intersect with the enabled languages to make sure the language args
    // passed are actually enabled.
    $valid_languages = array_keys($this->languageManager
      ->getLanguages(LanguageInterface::STATE_ALL));
    $values['add_language'] = array_intersect($add_language, $valid_languages);
    $translate_language = self::csvToArray($options['translations']);
    $values['translate_language'] = array_intersect($translate_language, $valid_languages);
    $values['add_type_label'] = $options['add-type-label'];
    $values['kill'] = $options['kill'];
    $values['feedback'] = $options['feedback'];
    $values['skip_fields'] = is_null($options['skip-fields']) ? [] : self::csvToArray($options['skip-fields']);
    $values['base_fields'] = is_null($options['base-fields']) ? [] : self::csvToArray($options['base-fields']);
    $values['title_length'] = 6;
    $values['num'] = array_shift($args);
    $values['max_comments'] = array_shift($args);
    $all_types = array_keys($this->blockContentGetBundles());
    $selected_types = self::csvToArray($options['block_types']);
    if ($selected_types === []) {
      throw new \Exception(dt('No Block content types available'));
    }
    $values['block_types'] = array_combine($selected_types, $selected_types);
    $block_types = array_filter($values['block_types']);
    if (!empty($values['kill']) && $block_types === []) {
      throw new \Exception(dt('To delete content, please provide the Block content types (--bundles)'));
    }
    // Checks for any missing block content types before generating blocks.
    if (array_diff($block_types, $all_types) !== []) {
      throw new \Exception(dt('One or more block content types have been entered that don\'t exist on this site'));
    }
    if ($this->isBatch($values['num'])) {
      $this->drushBatch = TRUE;
    }
    return $values;
  }
  
  /**
   * {@inheritdoc}
   */
  protected function generateElements(array $values) : void {
    if ($this->isBatch($values['num'])) {
      $this->generateBatchContent($values);
    }
    else {
      $this->generateContent($values);
    }
  }
  
  /**
   * Generate content in batch mode.
   *
   * This method is used when the number of elements is 50 or more.
   */
  private function generateBatchContent(array $values) : void {
    $operations = [];
    // Remove unselected block content types.
    $values['block_types'] = array_filter($values['block_types']);
    // If it is drushBatch then this operation is already run in the
    // self::validateDrushParams().
    // Add the kill operation.
    if ($values['kill']) {
      $operations[] = [
        'devel_generate_operation',
        [
          $this,
          'batchContentKill',
          $values,
        ],
      ];
    }
    // Add the operations to create the blocks.
    for ($num = 0; $num < $values['num']; ++$num) {
      $operations[] = [
        'devel_generate_operation',
        [
          $this,
          'batchContentAddBlock',
          $values,
        ],
      ];
    }
    // Set the batch.
    $batch = [
      'title' => $this->t('Generating Content'),
      'operations' => $operations,
      'finished' => 'devel_generate_batch_finished',
      'file' => $this->extensionPathResolver
        ->getPath('module', 'devel_generate') . '/devel_generate.batch.inc',
    ];
    batch_set($batch);
    if ($this->drushBatch) {
      drush_backend_batch_process();
    }
  }
  
  /**
   * Batch wrapper for calling ContentAddBlock.
   */
  public function batchContentAddBlock(array $vars, array &$context) : void {
    if (!isset($context['results']['num'])) {
      $context['results']['num'] = 0;
    }
    if ($this->drushBatch) {
      ++$context['results']['num'];
      $this->develGenerateContentAddBlock($vars);
    }
    else {
      $context['results'] = $vars;
      $this->develGenerateContentAddBlock($context['results']);
    }
    if (!empty($vars['num_translations'])) {
      $context['results']['num_translations'] += $vars['num_translations'];
    }
  }
  
  /**
   * Batch wrapper for calling ContentKill.
   */
  public function batchContentKill(array $vars, array &$context) : void {
    if ($this->drushBatch) {
      $this->contentKill($vars);
    }
    else {
      $context['results'] = $vars;
      $this->contentKill($context['results']);
    }
  }
  
  /**
   * Generate content when not in batch mode.
   *
   * This method is used when the number of elements is under 50.
   */
  private function generateContent(array $values) : void {
    $values['block_types'] = array_filter($values['block_types']);
    if (!empty($values['kill']) && $values['block_types']) {
      $this->contentKill($values);
    }
    if (isset($values['block_types']) && $values['block_types'] !== []) {
      $start = time();
      $values['num_translations'] = 0;
      for ($i = 1; $i <= $values['num']; ++$i) {
        $this->develGenerateContentAddBlock($values);
        if (isset($values['feedback']) && $i % $values['feedback'] == 0) {
          $now = time();
          $options = [
            '@feedback' => $values['feedback'],
            '@rate' => $values['feedback'] * 60 / ($now - $start),
          ];
          $this->messenger
            ->addStatus(dt('Completed @feedback blocks (@rate blocks/min)', $options));
          $start = $now;
        }
      }
    }
    $this->setMessage($this->formatPlural($values['num'], 'Created 1 block', 'Created @count blocks'));
    if ($values['num_translations'] > 0) {
      $this->setMessage($this->formatPlural($values['num_translations'], 'Created 1 block translation', 'Created @count block translations'));
    }
  }
  
  /**
   * Create one block. Used by both batch and non-batch code branches.
   *
   * @param array $results
   *   Results information.
   */
  protected function develGenerateContentAddBlock(array &$results) : void {
    if (!isset($results['time_range'])) {
      $results['time_range'] = 0;
    }
    $block_type = array_rand($results['block_types']);
    // Add the block type label if required.
    $title_prefix = $results['add_type_label'] ? $this->blockContentTypeStorage
      ->load($block_type)
      ->label() . ' - ' : '';
    $values = [
      'info' => $title_prefix . $this->getRandom()
        ->sentences(mt_rand(1, $results['title_length']), TRUE),
      'type' => $block_type,
      // A flag to let hook_block_content_insert() implementations know that this is a generated block.
'devel_generate' => $results,
    ];
    if (isset($results['add_language'])) {
      $values['langcode'] = $this->getLangcode($results['add_language']);
    }
    if (isset($results['reusable'])) {
      $values['reusable'] = (int) $results['reusable'];
    }
    /** @var \Drupal\block_content\BlockContentInterface $block */
    $block = $this->blockContentStorage
      ->create($values);
    // Populate non-skipped fields with sample values.
    $this->populateFields($block, $results['skip_fields'], $results['base_fields']);
    // Remove the fields which are intended to have no value.
    foreach ($results['skip_fields'] as $field) {
      unset($block->{$field});
    }
    $block->save();
    // Add translations.
    $this->develGenerateContentAddBlockTranslation($results, $block);
  }
  
  /**
   * Create translation for the given block.
   *
   * @param array $results
   *   Results array.
   * @param \Drupal\block_content\BlockContentInterface $block
   *   Block to add translations to.
   *
   * @throws \Drupal\Core\Entity\EntityStorageException
   */
  protected function develGenerateContentAddBlockTranslation(array &$results, BlockContentInterface $block) : void {
    if (empty($results['translate_language'])) {
      return;
    }
    if (is_null($this->contentTranslationManager)) {
      return;
    }
    if (!$this->contentTranslationManager
      ->isEnabled('block_content', $block->bundle())) {
      return;
    }
    if ($block->get('langcode')
      ->getLangcode() === LanguageInterface::LANGCODE_NOT_SPECIFIED || $block->get('langcode')
      ->getLangcode() === LanguageInterface::LANGCODE_NOT_APPLICABLE) {
      return;
    }
    if (!isset($results['num_translations'])) {
      $results['num_translations'] = 0;
    }
    // Translate the block to each target language.
    $skip_languages = [
      LanguageInterface::LANGCODE_NOT_SPECIFIED,
      LanguageInterface::LANGCODE_NOT_APPLICABLE,
      $block->get('langcode')
        ->getLangcode(),
    ];
    foreach ($results['translate_language'] as $langcode) {
      if (in_array($langcode, $skip_languages)) {
        continue;
      }
      $translation_block = $block->addTranslation($langcode);
      $translation_block->setInfo($block->label() . ' (' . $langcode . ')');
      $this->populateFields($translation_block);
      $translation_block->save();
      ++$results['num_translations'];
    }
  }
  
  /**
   * Deletes all blocks of given block content types.
   *
   * @param array $values
   *   The input values from the settings form.
   */
  protected function contentKill(array $values) : void {
    $bids = $this->blockContentStorage
      ->getQuery()
      ->condition('type', $values['block_types'], 'IN')
      ->accessCheck(FALSE)
      ->execute();
    if (!empty($bids)) {
      $blocks = $this->blockContentStorage
        ->loadMultiple($bids);
      $this->blockContentStorage
        ->delete($blocks);
      $this->setMessage($this->t('Deleted %count blocks.', [
        '%count' => count($bids),
      ]));
    }
  }
  
  /**
   * Determines if the content should be generated in batch mode.
   */
  protected function isBatch($content_count) : bool {
    return $content_count >= 50;
  }
  
  /**
   * Returns a list of available block content type names.
   *
   * This list can include types that are queued for addition or deletion.
   *
   * @return string[]
   *   An array of block content type labels,
   *   keyed by the block content type name.
   */
  public function blockContentGetBundles() : array {
    return array_map(static fn($bundle_info) => $bundle_info['label'], $this->entityTypeBundleInfo
      ->getBundleInfo('block_content'));
  }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title Overrides
BlockContentDevelGenerate::$blockContentStorage protected property The block content storage.
BlockContentDevelGenerate::$blockContentTypeStorage protected property The block content type storage.
BlockContentDevelGenerate::$contentTranslationManager protected property The content translation manager.
BlockContentDevelGenerate::$drushBatch protected property The Drush batch flag.
BlockContentDevelGenerate::$entityTypeBundleInfo protected property The entity type bundle info service.
BlockContentDevelGenerate::$extensionPathResolver protected property The extension path resolver service.
BlockContentDevelGenerate::batchContentAddBlock public function Batch wrapper for calling ContentAddBlock.
BlockContentDevelGenerate::batchContentKill public function Batch wrapper for calling ContentKill.
BlockContentDevelGenerate::blockContentGetBundles public function Returns a list of available block content type names.
BlockContentDevelGenerate::contentKill protected function Deletes all blocks of given block content types.
BlockContentDevelGenerate::create public static function Instantiates a new instance of this class. Overrides DevelGenerateBase::create
BlockContentDevelGenerate::develGenerateContentAddBlock protected function Create one block. Used by both batch and non-batch code branches.
BlockContentDevelGenerate::develGenerateContentAddBlockTranslation protected function Create translation for the given block.
BlockContentDevelGenerate::generateBatchContent private function Generate content in batch mode.
BlockContentDevelGenerate::generateContent private function Generate content when not in batch mode.
BlockContentDevelGenerate::generateElements protected function Business logic relating with each DevelGenerate plugin. Overrides DevelGenerateBase::generateElements
BlockContentDevelGenerate::isBatch protected function Determines if the content should be generated in batch mode.
BlockContentDevelGenerate::settingsForm public function Returns the form for the plugin. Overrides DevelGenerateBase::settingsForm
BlockContentDevelGenerate::settingsFormValidate public function Form validation handler. Overrides DevelGenerateBase::settingsFormValidate
BlockContentDevelGenerate::validateDrushParams public function Responsible for validating Drush params. Overrides DevelGenerateBaseInterface::validateDrushParams
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 2
DependencySerializationTrait::__wakeup public function #[\ReturnTypeWillChange] 2
DevelGenerateBase::$entityFieldManager protected property The entity field manager.
DevelGenerateBase::$entityTypeManager protected property The entity type manager service.
DevelGenerateBase::$languageManager protected property The language manager. 1
DevelGenerateBase::$moduleHandler protected property The module handler. 1
DevelGenerateBase::$random protected property The random data generator.
DevelGenerateBase::$settings protected property The plugin settings.
DevelGenerateBase::csvToArray public static function Convert a csv string into an array of items.
DevelGenerateBase::generate public function Execute the instructions in common for all DevelGenerate plugin. Overrides DevelGenerateBaseInterface::generate
DevelGenerateBase::getDefaultSettings public function Returns the default settings for the plugin. Overrides DevelGenerateBaseInterface::getDefaultSettings
DevelGenerateBase::getLangcode protected function Return a language code. 1
DevelGenerateBase::getLanguageForm protected function Creates the language and translation section of the form.
DevelGenerateBase::getRandom protected function Returns the random data generator.
DevelGenerateBase::getSetting public function Returns the array of settings, including defaults for missing settings. Overrides DevelGenerateBaseInterface::getSetting
DevelGenerateBase::getSettings public function Returns the current settings for the plugin. Overrides DevelGenerateBaseInterface::getSettings
DevelGenerateBase::handleDrushParams public function
DevelGenerateBase::isNumber public static function Check if a given param is a number.
DevelGenerateBase::populateFields public function Populate the fields on a given entity with sample values.
DevelGenerateBase::randomSentenceOfLength protected function Generates a random sentence of specific length.
DevelGenerateBase::setMessage protected function Set a message for either drush or the web interface.
MessengerTrait::$messenger protected property The messenger. 25
MessengerTrait::messenger public function Gets the messenger. 25
MessengerTrait::setMessenger public function Sets the messenger.
PluginBase::$configuration protected property Configuration information passed into the plugin. 1
PluginBase::$pluginDefinition protected property The plugin implementation definition. 1
PluginBase::$pluginId protected property The plugin ID.
PluginBase::DERIVATIVE_SEPARATOR constant A string which is used to separate base plugin IDs from the derivative ID.
PluginBase::getBaseId public function Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface::getBaseId
PluginBase::getDerivativeId public function Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface::getDerivativeId
PluginBase::getPluginDefinition public function Gets the definition of the plugin implementation. Overrides PluginInspectionInterface::getPluginDefinition 2
PluginBase::getPluginId public function Gets the plugin ID of the plugin instance. Overrides PluginInspectionInterface::getPluginId
PluginBase::isConfigurable public function Determines if the plugin is configurable.
PluginBase::__construct public function Constructs a \Drupal\Component\Plugin\PluginBase object. 87
StringTranslationTrait::$stringTranslation protected property The string translation service. 3
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.