cms_content_sync-3.0.x-dev/src/Plugin/cms_content_sync/field_handler/DefaultLinkHandler.php

src/Plugin/cms_content_sync/field_handler/DefaultLinkHandler.php
<?php

namespace Drupal\cms_content_sync\Plugin\cms_content_sync\field_handler;

use Drupal\cms_content_sync\Plugin\FieldHandlerBase;
use Drupal\cms_content_sync\PullIntent;
use Drupal\cms_content_sync\PushIntent;
use Drupal\cms_content_sync\SyncIntent;
use Drupal\Core\Entity\TranslatableInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\StreamWrapper\PublicStream;
use Drupal\Core\Url;
use Drupal\menu_link_content\Entity\MenuLinkContent;
use EdgeBox\SyncCore\Interfaces\Configuration\IDefineEntityType;
use EdgeBox\SyncCore\Interfaces\Configuration\IDefineObjectProperty;

/**
 * Providing a minimalistic implementation for any field type.
 *
 * @FieldHandler(
 *   id = "cms_content_sync_default_link_handler",
 *   label = @Translation("Default Link"),
 *   weight = 90
 * )
 */
class DefaultLinkHandler extends FieldHandlerBase {

  /**
   * {@inheritdoc}
   */
  public static function supports($entity_type, $bundle, $field_name, FieldDefinitionInterface $field) {
    $allowed = ['link'];

    return FALSE !== in_array($field->getType(), $allowed);
  }

  /**
   * {@inheritdoc}
   */
  public function getHandlerSettings($current_values, $type = 'both') {
    $options = [];

    if ('pull' !== $type) {
      $options['export_as_absolute_url'] = [
        '#type' => 'checkbox',
        '#title' => 'Push as absolute URL',
        '#default_value' => $current_values['export_as_absolute_url'] ?? FALSE,
      ];
    }

    return array_merge(parent::getHandlerSettings($current_values, $type), $options);
  }

  /**
   * {@inheritdoc}
   */
  public function pull(PullIntent $intent) {
    $action = $intent->getAction();
    /**
     * @var \Drupal\Core\Entity\FieldableEntityInterface $entity
     */
    $entity = $intent->getEntity();

    // Deletion doesn't require any action on field basis for static data.
    if (SyncIntent::ACTION_DELETE == $action) {
      return FALSE;
    }

    if ($intent->shouldMergeChanges()) {
      return FALSE;
    }

    $data = $intent->getProperty($this->fieldName);

    $status = $intent->getEntityStatus();
    $language = $entity instanceof TranslatableInterface ? $entity->language()->getId() : 'und';
    $status->resetMissingReferences($language, $this->fieldName);

    if (empty($data)) {
      $entity->set($this->fieldName, NULL);
    }
    else {
      $result = [];

      $base_path = '/' . PublicStream::basePath() . '/';

      foreach ($data as &$link_element) {
        if (empty($link_element['uri'])) {
          try {
            $reference = $intent->loadEmbeddedEntity($link_element);
          }
          catch (\Exception $e) {
            $reference = NULL;
          }
          $reference_data = $intent->getEmbeddedEntityData($link_element);
          $reference_meta = $intent->loadReference($link_element);
          if ($reference) {
            if (empty($reference_data['file_uri'])) {
              if (($reference_data['uri_format'] ?? NULL) === 'relative_entity') {
                $uri = 'internal:/' . $reference->getEntityTypeId() . '/' . $reference->id();
              }
              else {
                $uri = 'entity:' . $reference->getEntityTypeId() . '/' . $reference->id();
              }
              $result[] = [
                'uri' => $uri,
                'title' => $reference_data['title'],
                'options' => $reference_data['options'] ?? [],
              ];
            }
            else {
              $url = $base_path . substr($reference_data['file_uri'], 9);
              $result[] = [
                'uri' => 'internal:' . $url . (empty($reference_data['uri_anchor']) ? '' : '#' . $reference_data['uri_anchor']),
                'title' => $reference_data['title'],
                'options' => $reference_data['options'] ?? [],
              ];
            }
          }
          elseif ($reference_meta->getType() && $reference_meta->getBundle()) {
            // Menu items are created before the node as they are embedded
            // entities. For the link to work however the node must already
            // exist which won't work. So instead we're creating a temporary
            // uri that uses the entity UUID instead of it's ID. Once the node
            // is pulled it will look for this link and replace it with the
            // now available entity reference by ID.
            if ($entity instanceof MenuLinkContent && 'link' == $this->fieldName) {
              $result[] = [
                'uri' => 'internal:/',
                'title' => $reference_data['title'],
                'options' => $reference_data['options'] ?? [],
              ];
            }
            else {
              // Shortcut: If it's just one value and a normal entity_reference field, the MissingDependencyManager will
              // directly update the field value of the entity and save it. Otherwise it will request a full pull of the
              // entity. So this saves some performance for simple references.
              if ('entity_reference' === $this->fieldDefinition->getType() && !$this->fieldDefinition->getFieldStorageDefinition()->isMultiple()) {
                $intent->saveUnresolvedDependency($link_element, $this->fieldName);
              }
              else {
                $intent->saveUnresolvedDependency($link_element);
              }

              $status->addMissingReference($language, $this->fieldName, $link_element);
            }
          }
        }
        else {
          $result[] = [
            'uri' => $link_element['uri'],
            'title' => $link_element['title'],
            'options' => $link_element['options'],
          ];
        }
      }

      $entity->set($this->fieldName, $result);
    }

    return TRUE;
  }

  /**
   * {@inheritdoc}
   */
  public function push(PushIntent $intent) {
    $action = $intent->getAction();
    /**
     * @var \Drupal\Core\Entity\FieldableEntityInterface $entity
     */
    $entity = $intent->getEntity();

    // Deletion doesn't require any action on field basis for static data.
    if (SyncIntent::ACTION_DELETE == $action) {
      return FALSE;
    }

    $data = $entity->get($this->fieldName)->getValue();

    $absolute = !empty($this->settings['handler_settings']['export_as_absolute_url']);

    $result = [];

    $base_path = '/' . PublicStream::basePath();

    foreach ($data as $key => $value) {
      if (empty($data[$key]['uri'])) {
        continue;
      }

      $uri = &$data[$key]['uri'];
      // Find the linked entity and replace it's id with the UUID
      // References have following pattern: entity:entity_type/entity_id.
      preg_match('/^entity:(.*)\/(\d*)$/', $uri, $found);
      $meta_data = [];
      $link_entity = NULL;
      if (empty($found)) {
        if (substr($uri, 0, strlen($base_path) + 10) === ('internal:' . $base_path . '/')) {
          // PDF files can have a #page=... anchor attached that we want to keep.
          $parts = explode('#', substr($uri, 10 + strlen($base_path)));
          $path = $parts[0];
          $anchor = count($parts) > 1 ? $parts[1] : '';
          $file_uri = 'public://' . urldecode($path);
          $files = \Drupal::entityTypeManager()
            ->getStorage('file')
            ->loadByProperties(['uri' => $file_uri]);
          if (count($files)) {
            $link_entity = reset($files);
            $meta_data['file_uri'] = $file_uri;
            if ($anchor) {
              $meta_data['uri_anchor'] = $anchor;
            }
          }
          $meta_data['uri_format'] = 'relative_file';
        }
        elseif (preg_match('@^internal:/(node)/([0-9]+)(#.*)?$@', $uri, $internal_route_found)) {
          $link_entity = \Drupal::entityTypeManager()->getStorage($internal_route_found[1])->load($internal_route_found[2]);
          $meta_data['uri_format'] = 'relative_entity';
          if (!empty($internal_route_found[3]) && strlen($internal_route_found[3]) > 1) {
            $meta_data['uri_anchor'] = substr($internal_route_found[3], 1);
          }
        }
      }
      else {
        $meta_data['uri_format'] = 'entity_reference';
      }
      if ((empty($link_entity) && empty($found)) || $absolute) {
        if ($absolute) {
          $uri = Url::fromUri($uri, ['absolute' => TRUE])->toString();
        }
        $result[] = [
          'uri' => $uri,
          'title' => $value['title'] ?? NULL,
          'options' => $value['options'],
        ];
      }
      else {
        if (empty($link_entity)) {
          $link_entity_type = $found[1];
          $link_entity_id = $found[2];
          $entity_manager = \Drupal::entityTypeManager();
          $link_entity = $entity_manager->getStorage($link_entity_type)
            ->load($link_entity_id);

          if (empty($link_entity)) {
            continue;
          }
        }

        $details = array_merge([
          'title' => $value['title'],
          'options' => $value['options'],
        ], $meta_data);

        if ($intent->getFlow()->getController()->canPushEntity($link_entity, PushIntent::PUSH_AS_DEPENDENCY)) {
          $reference = $intent->embed($link_entity, $details);
        }
        else {
          try {
            $view_url = $link_entity->toUrl('canonical', [
              'absolute' => TRUE,
              // Workaround for PathProcessorAlias::processOutbound to explicitly ignore us
              // as we always want the pure, unaliased e.g. /node/:id path because
              // we don't use the URL for end-users but for editors and it has to
              // be reliable (aliases can be removed or change).
              'alias' => TRUE,
            ] + ($entity instanceof TranslatableInterface ? ['language' => $entity->language()] : []))->toString();
          }
          catch (\Exception $e) {
            $view_url = Url::fromUri($uri, ['absolute' => TRUE])->toString();
          }

          $reference = $intent->addReference($link_entity, $details, $view_url);
        }

        $reference['entity'] = $reference;

        $result[] = $reference;
      }
    }

    $intent->setProperty($this->fieldName, $result);

    return TRUE;
  }

  /**
   * {@inheritDoc}
   */
  public function definePropertyAtType(IDefineEntityType $type_definition) {
    $property = parent::definePropertyAtType($type_definition);

    if (!$property || !($property instanceof IDefineObjectProperty)) {
      return $property;
    }

    $property->addReferenceProperty('entity', 'Entity', FALSE, FALSE, 'entity_reference');

    return $property;
  }

}

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

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