layoutcomponents-8.x-1.14-beta2/modules/lc_commands/src/Commands/LcCommands.php

modules/lc_commands/src/Commands/LcCommands.php
<?php

namespace Drupal\lc_commands\Commands;

use Drush\Commands\DrushCommands;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\block_content\Entity\BlockContent;
use Symfony\Component\Serializer\SerializerInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\layout_builder\Section;
use Drupal\layout_builder\SectionComponent;
use Drupal\layoutcomponents\Entity\LcEntityViewDisplay;
use Drupal\Core\Field\FieldConfigBase;
use Drupal\field\Entity\FieldConfig;
use Drupal\layout_builder\InlineBlockUsage;

/**
 * LC commands.
 */
class LcCommands extends DrushCommands {

  /**
   * The folder path.
   *
   * @var string
   */
  protected $folder = DRUPAL_ROOT;

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The serializer.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $serializer;

  /**
   * The Config factory object.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  protected $configFactory;

  /**
   * The Inline block usage.
   *
   * @var \Drupal\layout_builder\InlineBlockUsage
   */
  protected $inlineBlockUsage;

  /**
   * LcCommands constructor.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity manager interface object.
   * @param \Symfony\Component\Serializer\SerializerInterface $serializer
   *   The serializer interface object.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   The config factory interface object.
   * @param \Drupal\layout_builder\InlineBlockUsage $inline_block_usage
   *   The inline block usage manager.
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager, SerializerInterface $serializer, ConfigFactoryInterface $config_factory, InlineBlockUsage $inline_block_usage) {
    $this->entityTypeManager = $entity_type_manager;
    $this->serializer = $serializer;
    $this->configFactory = $config_factory;
    $this->folder .= $this->configFactory->getEditable('layoutcomponents.general')->get('folder') . '/';
    $this->inlineBlockUsage = $inline_block_usage;
  }

  /**
   * Delete all content_blocks.
   *
   * @command lc:delete
   * @aliases lcd
   */
  public function delete() {
    $block_storage = $this->entityTypeManager->getStorage('block_content');
    $blocks = $block_storage->loadMultiple();

    /** @var \Drupal\block_content\Entity\BlockContent $block */
    foreach ($blocks as $i => $block) {
      // Provide the las version of block.
      $revision = $block_storage->getLatestRevisionId($block->id());
      $block_revision = $block_storage->loadRevision($revision);

      // Filter by block that they are using by layout.
      if ($block_revision instanceof BlockContent) {
        if (!$this->isLcBlock($block_revision, 'delete')) {
          unset($blocks[$i]);
          continue;
        }
      }

      $this->output->writeln('Removing: ' . $block->uuid());
      $block->delete();
    }

    $this->output->writeln(count($blocks) . ' blocks have been deleted');
  }

  /**
   * Export the content_blocks.
   *
   * @command lc:export
   * @aliases lce
   */
  public function export() {
    $blocks = $this->entityTypeManager->getStorage('block_content')->loadMultiple();

    if (!$this->prepareFolder()) {
      return FALSE;
    }

    // Clear the directory.
    $this->clearDirectory();

    /** @var \Drupal\block_content\Entity\BlockContent $block */
    foreach ($blocks as $i => $block) {
      // Ensure that is the last revision.
      $block_revision = $this->getLastRevisionBlock($block->id());

      // Filter by block that they are using by layout.
      if (!$this->isLcBlock($block_revision)) {
        unset($blocks[$i]);
        continue;
      }
      $this->exportBlock($block_revision);
    }

    return TRUE;
  }

  /**
   * Import the content_blocks.
   *
   * @command lc:import
   * @aliases lci
   */
  public function import() {
    // Remove the current blocks.
    $this->delete();

    // Check the directory.
    $files = $this->readDirectory();
    if (empty($files)) {
      $this->output->writeln('The directory is empty');
      return FALSE;
    }

    foreach ($files as $file) {
      $this->output->writeln('Importing: ' . $file);

      /** @var \Drupal\block_content\Entity\BlockContent $n_block */
      $n_block = $this->readFile($file);

      $uuid = $n_block['uuid'][0];

      // Normalize Layout builder field if exists.
      $this->normalizeSections($n_block);

      /** @var \Drupal\block_content\Entity\BlockContent $d_block */
      $d_block = $this->getBlock($uuid);

      // Check for references.
      $references = $n_block['_embedded'];
      if (isset($references)) {
        foreach ($references as $link => $reference) {
          // Get the dependencies already createds.
          $dependencies = $this->getDependencie($n_block, $link);

          // Register the dependencies, the dependencies as media won't
          // be exported.
          /** @var \Drupal\block_content\Entity\BlockContent $dependencie */
          if (!empty($dependencies)) {
            foreach ($dependencies as $i => $dependencie) {
              $n_block[$this->getEmbedded($link)][$i]['target_id'] = $dependencie->id();
              $n_block[$this->getEmbedded($link)][$i]['target_revision_id'] = $dependencie->getRevisionId();
            }
          }
        }
      }

      // Create or update the block.
      if (!empty($d_block)) {
        $d_block->delete();
      }
      $this->createBlock($n_block);
    }

    return TRUE;
  }

  /**
   * Export a block.
   *
   * @param \Drupal\block_content\Entity\BlockContent $block
   *   The block.
   *
   * @return bool
   *   If the block has been expoterd.
   */
  public function exportBlock(BlockContent $block) {
    $item = $this->prepareFile($block->toArray());
    if (!$this->writeFile($this->folder . $block->uuid() . '.json', $item)) {
      return FALSE;
    }
    $this->output->writeln('Exporting: ' . $block->uuid());
    return TRUE;
  }

  /**
   * Export the sub-blocks of a block.
   *
   * @param \Drupal\block_content\Entity\BlockContent $block
   *   The block.
   */
  public function exportSubBlocks(BlockContent $block) {
    if ($block->hasField('layout_builder__layout')) {
      $layout = $block->get('layout_builder__layout')->getValue();
      if (!empty($layout)) {
        foreach ($layout as $item) {
          /** @var \Drupal\layout_builder\Section $section */
          $section = $item['section'];
          $components = $section->getComponents();
          if (!empty($components)) {
            /** @var \Drupal\layout_builder\SectionComponent $component */
            foreach ($components as $component) {
              /** @var \Drupal\block_content\Entity\BlockContent $sub_block */
              $sub_block = $this->getLastRevisionBlock($component->toArray()['configuration']['block_revision_id']);
              if ($sub_block instanceof BlockContent) {
                $this->exportBlock($sub_block);
              }
            }
          }
        }
      }
    }
    else {
      // Find the sub field blocks and import them.
      foreach ($block->getFieldDefinitions() as $field_name => $definition) {
        if ($definition instanceof FieldConfig && $definition->getType() == 'entity_reference_revisions') {
          $this->exportBlock($this->getLastRevisionBlock($block->get($field_name)->getValue()[0]['target_id']));
        }
      }
    }
  }

  /**
   * Create the block and store the rest of translates.
   *
   * @param array $n_block
   *   Array block.
   */
  public function createBlock(array $n_block) {
    // Get the langcodes.
    $langcodes = $n_block['langcode'];
    // Store the default block.
    BlockContent::create($n_block)->save();
    // Get the new block.
    $block = $this->getBlock($n_block['uuid'][0]['value']);
    // If the block contains more than 1 language.
    if (count($langcodes) > 1) {
      // Store each language.
      foreach ($langcodes as $langcode) {
        // Filter by non-default language.
        if ($block->language()->getId() !== $langcode['lang']) {
          $block_translate = [];
          // Find the fields.
          foreach ($block->getFieldDefinitions() as $definition) {
            if ($definition instanceof FieldConfigBase) {
              $field_name = $definition->get('field_name');
              // Find the value by language.
              $field_translate = $n_block[$field_name];
              if (empty($field_translate)) {
                continue;
              }
              foreach ($field_translate as $i => $value) {
                if ($value['lang'] == $langcode['lang']) {
                  $block_translate[$field_name] = $n_block[$field_name][$i];
                }
              }
            }
          }
          // Store the new translation.
          $block->addTranslation($langcode['lang'], $block_translate)->save();
        }
      }
    }
  }

  /**
   * Check if the block is a LC block and is used from a display.
   *
   * @param \Drupal\block_content\Entity\BlockContent $block
   *   The block.
   * @param string $action
   *   The type of action.
   *
   * @return bool
   *   If is a LC block.
   */
  public function isLcBlock(BlockContent $block, $action = 'export') {
    /** @var \Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplayStorage $storage */
    $storage = $this->entityTypeManager->getStorage('entity_view_display');
    $displays = $storage->loadMultiple();

    foreach ($displays as $name => $display) {
      // Filter by LcEntityViewDisplay entity.
      if ($display instanceof LcEntityViewDisplay) {
        foreach ($display->getSections() as $section) {
          $components = $section->getComponents();
          if (!empty($components)) {
            foreach ($components as $name => $component) {
              $configuration = $component->get('configuration');
              // Compare the label and block revision id.
              if (!empty($configuration['label'])) {
                if ($configuration['label'] == $block->get('info')->getString() && $configuration['block_revision_id'] == $block->getRevisionId()) {
                  // Entity reference revisions validation.
                  // Find the entity_reference_revisions fields.
                  $definitions = $block->getFieldDefinitions();
                  foreach ($definitions as $definition) {
                    if ($definition->getType() == 'entity_reference_revisions') {
                      /** @var \Drupal\entity_reference_revisions\EntityReferenceRevisionsFieldItemList $list */
                      $list = $block->get($definition->getName());
                      for ($i = 0; $i < $list->count(); $i++) {
                        /** @var \Drupal\block_content\Entity\BlockContent $tab_item */
                        $tab_item = $this->getLastRevisionBlock($list->get($i)->getValue()['target_id']);
                        if (!isset($tab_item)) {
                          continue;
                        }

                        if ($action == 'delete') {
                          $tab_item->delete();
                          $this->writeln('Deleting: ' . $tab_item->uuid());
                        }
                        else {
                          // Export the entity_reference_revisions block.
                          $this->exportBlock($tab_item);

                          // Export the sub entity_reference_revisions blocks.
                          $this->exportSubBlocks($tab_item);
                        }
                      }
                    }
                  }
                  return TRUE;
                }
              }
            }
          }
        }
      }
    }
    // Block not found in any display.
    return FALSE;
  }

  /**
   * Get the dependencie or create if not exists.
   *
   * @param \Drupal\block_content\Entity\BlockContent $n_block
   *   The block.
   * @param string $reference
   *   The new reference.
   *
   * @return array
   *   The array of dependencies.
   */
  public function getDependencie(BlockContent &$n_block, string $reference) {
    $dependencies = [];
    // Get the embed reference.
    $embed = $this->getEmbedded($reference);
    if (!empty($embed) && strpos($embed, 'field') !== FALSE) {
      foreach ($n_block['_embedded'][$reference] as $i => $dependencie) {
        $n_uuid = $n_block['_embedded'][$reference][$i]['uuid'][0]['value'];
        // Check if the file xists.
        if (!file_exists($this->folder . $n_uuid . '.json')) {
          return NULL;
        }
        // Get the reference block.
        $new_block = $this->readFile($n_uuid . '.json');
        $uuid = $new_block['uuid'][0];

        // Get the current block.
        $block = $this->getBlock($uuid);

        if (empty($block)) {
          // Normalize Layout builder field if exists.
          $this->normalizeSections($new_block);

          // Create if not exists.
          BlockContent::create($new_block)->save();
          $dependencies[$i] = $this->getBlock($uuid);
        }
        else {
          // Update the block with the new data.
          $dependencies[$i] = $this->updateDependencie($block, $n_uuid);
        }
      }
    }
    return $dependencies;
  }

  /**
   * Get the decoded file content.
   *
   * @param string $file
   *   The file.
   *
   * @return string
   *   The content.
   */
  public function readFile($file) {
    return $this->serializer->decode($this->parseFile($file), 'hal_json');
  }

  /**
   * Get the current block.
   *
   * @param string $uuid
   *   The uuid.
   *
   * @return \Drupal\block_content\Entity\BlockContent
   *   The new block.
   */
  public function getBlock($uuid) {
    $block = $this->entityTypeManager
      ->getStorage('block_content')
      ->loadByProperties(['uuid' => $uuid]);

    /** @var \Drupal\block_content\Entity\BlockContent $block */
    $block = reset($block);

    return $block;
  }

  /**
   * Get the block by last revision.
   *
   * @param string $id
   *   The block id.
   *
   * @return \Drupal\block_content\Entity\BlockContent
   *   The full block.
   */
  public function getLastRevisionBlock($id) {
    /** @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage $storage */
    $storage = $this->entityTypeManager->getStorage('block_content');
    $revision = $storage->getLatestRevisionId($id);
    return $storage->loadRevision($revision);
  }

  /**
   * Update the dependencie of the block.
   *
   * @param \Drupal\block_content\Entity\BlockContent $block
   *   The block.
   * @param string $uuid
   *   The uuid.
   *
   * @return \Drupal\block_content\Entity\BlockContent
   *   The new block.
   */
  public function updateDependencie(BlockContent $block, string $uuid) {
    $new_block = $this->readFile($uuid . '.json');
    return $this->updateBlock($block, $new_block);
  }

  /**
   * Get the uuid of the file.
   *
   * @param string $link
   *   The uuid file.
   *
   * @return string
   *   The string.
   */
  public function getEmbedded($link) {
    $parts = explode('/', $link);
    return $parts[count($parts) - 1];
  }

  /**
   * Update each block with the new data.
   *
   * @return \Drupal\block_content\Entity\BlockContent
   *   The new block.
   */
  public function updateBlock($block, $n_block) {
    foreach ($block->getFields() as $name => $field) {
      $value = $n_block[$name];
      $block->set($name, $value);
    }

    $block->save();
    return $block;
  }

  /**
   * Check the folder.
   *
   * @return bool
   *   If exists and is writable.
   */
  public function prepareFolder() {
    if (!is_dir($this->folder)) {
      $this->output->writeln('The folder not exists');
      return FALSE;
    }

    if (!is_writable($this->folder)) {
      $this->output->writeln('The folder is not writabble');
      return FALSE;
    }

    return TRUE;
  }

  /**
   * Serialize the file content.
   *
   * @return string
   *   The file content serialized.
   */
  public function prepareFile($item) {
    // Normalize the Layout builder field.
    if (array_key_exists('layout_builder__layout', $item)) {
      $new = $item;
      $new['layout_builder__layout'] = $this->prepareSections($item['layout_builder__layout']);
      $item = $new;
    }
    return $this->serializer->serialize($item, 'hal_json', ['json_encode_options' => 128]);
  }

  /**
   * Prepare the sections to import them.
   *
   * @param array $sections
   *   The array of sections.
   *
   * @return array
   *   The array converted.
   */
  public function prepareSections(array $sections) {
    $json_sections = [];
    foreach ($sections as $key => $section) {
      $json_sections[$key]['section'] = $this->prepareFile($section['section']->toArray());
    }
    return $json_sections;
  }

  /**
   * Normalize the sections for layout builder fields.
   *
   * @param array $block
   *   The block as array.
   */
  public function normalizeSections(array &$block) {
    if (array_key_exists('layout_builder__layout', $block)) {
      // Normalize Layout builder value.
      foreach ($block['layout_builder__layout'] as $key => $section) {
        $data = $this->serializer->decode($section['section'], 'hal_json');
        $components = [];
        // Register the components.
        foreach ($data['components'] as $uuid => $component) {
          $components[$uuid] = new SectionComponent($component['uuid'], $component['region'], $component['configuration'], $component['additional']);
        }
        // Generate the section.
        $layout_builder[$key] = new Section($data['layout_id'], $data['layout_settings'], $components, []);
      }
      $block['layout_builder__layout'] = $layout_builder;
    }
  }

  /**
   * Write the file.
   *
   * @return bool
   *   If the file has been written correctly.
   */
  public function writeFile($file, $item) {
    if (!file_put_contents($file, $item)) {
      $this->output->writeln('An error encoured writing the file');
      return FALSE;
    }
    return TRUE;
  }

  /**
   * Get the file content.
   *
   * @return string|false
   *   The function returns the read data or false on failure.
   */
  protected function parseFile($file) {
    return file_get_contents($this->folder . $file);
  }

  /**
   * Read the files of directory.
   *
   * @return array
   *   The array with the files.
   */
  public function readDirectory() {
    return array_diff(scandir($this->folder), ['..', '.']);
  }

  /**
   * Remove the files of directory.
   */
  public function clearDirectory() {
    $files = $this->readDirectory();
    foreach ($files as $file) {
      unlink($this->folder . $file);
    }
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc