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
- class \Drupal\Component\Plugin\PluginBase implements \Drupal\Component\Plugin\PluginInspectionInterface, \Drupal\Component\Plugin\DerivativeInspectionInterface
- class \Drupal\Core\Plugin\PluginBase extends \Drupal\Component\Plugin\PluginBase uses \Drupal\Core\StringTranslation\StringTranslationTrait, \Drupal\Core\DependencyInjection\DependencySerializationTrait, \Drupal\Core\Messenger\MessengerTrait
- class \Drupal\devel_generate\DevelGenerateBase extends \Drupal\Core\Plugin\PluginBase implements \Drupal\devel_generate\DevelGenerateBaseInterface
- class \Drupal\devel_generate\Plugin\DevelGenerate\BlockContentDevelGenerate extends \Drupal\devel_generate\DevelGenerateBase implements \Drupal\Core\Plugin\ContainerFactoryPluginInterface
- class \Drupal\devel_generate\DevelGenerateBase extends \Drupal\Core\Plugin\PluginBase implements \Drupal\devel_generate\DevelGenerateBaseInterface
- class \Drupal\Core\Plugin\PluginBase extends \Drupal\Component\Plugin\PluginBase uses \Drupal\Core\StringTranslation\StringTranslationTrait, \Drupal\Core\DependencyInjection\DependencySerializationTrait, \Drupal\Core\Messenger\MessengerTrait
Expanded class hierarchy of BlockContentDevelGenerate
File
-
devel_generate/
src/ Plugin/ DevelGenerate/ BlockContentDevelGenerate.php, line 34
Namespace
Drupal\devel_generate\Plugin\DevelGenerateView 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 | |
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. | ||
DevelGenerateBaseInterface::__construct | public | function | |||
PluginInspectionInterface::getPluginDefinition | public | function | Gets the definition of the plugin implementation. | 6 | |
PluginInspectionInterface::getPluginId | public | function | Gets the plugin ID of the plugin instance. | 2 |