<?php 
 
/* 
 * This file is part of Sulu. 
 * 
 * (c) Sulu GmbH 
 * 
 * This source file is subject to the MIT license that is bundled 
 * with this source code in the file LICENSE. 
 */ 
 
namespace Sulu\Bundle\PageBundle\Content\Structure; 
 
use PHPCR\NodeInterface; 
use Sulu\Bundle\SearchBundle\Search\Factory; 
use Sulu\Component\Content\Compat\PropertyInterface; 
use Sulu\Component\Content\Compat\StructureInterface; 
use Sulu\Component\Content\Compat\StructureManagerInterface; 
use Sulu\Component\Content\ContentTypeExportInterface; 
use Sulu\Component\Content\ContentTypeManagerInterface; 
use Sulu\Component\Content\Extension\AbstractExtension; 
use Sulu\Component\Content\Extension\ExportExtensionInterface; 
use Sulu\Component\Content\Mapper\Translation\TranslatedProperty; 
use Sulu\Component\Export\Manager\ExportManagerInterface; 
use Sulu\Component\Import\Manager\ImportManagerInterface; 
 
/** 
 * extends structure with seo content. 
 */ 
class ExcerptStructureExtension extends AbstractExtension implements ExportExtensionInterface 
{ 
    /** 
     * name of structure extension. 
     */ 
    public const EXCERPT_EXTENSION_NAME = 'excerpt'; 
 
    /** 
     * will be filled with data in constructor 
     * {@inheritdoc} 
     */ 
    protected $properties = []; 
 
    protected $name = self::EXCERPT_EXTENSION_NAME; 
 
    protected $additionalPrefix = self::EXCERPT_EXTENSION_NAME; 
 
    /** 
     * @var ContentTypeManagerInterface 
     */ 
    protected $contentTypeManager; 
 
    /** 
     * @var StructureManagerInterface 
     */ 
    protected $structureManager; 
 
    /** 
     * @var ExportManagerInterface 
     */ 
    protected $exportManager; 
 
    /** 
     * @var ImportManagerInterface 
     */ 
    protected $importManager; 
 
    /** 
     * @var string 
     */ 
    private $languageNamespace; 
 
    /** 
     * @var string 
     */ 
    private $languageCode; 
 
    /** 
     * @var Factory 
     */ 
    private $factory; 
 
    public function __construct( 
        StructureManagerInterface $structureManager, 
        ContentTypeManagerInterface $contentTypeManager, 
        ExportManagerInterface $exportManager, 
        ImportManagerInterface $importManager, 
        Factory $factory 
    ) { 
        $this->contentTypeManager = $contentTypeManager; 
        $this->structureManager = $structureManager; 
        $this->exportManager = $exportManager; 
        $this->importManager = $importManager; 
        $this->factory = $factory; 
    } 
 
    public function save(NodeInterface $node, $data, $webspaceKey, $languageCode) 
    { 
        $this->setLanguageCode($languageCode, 'i18n', null); 
 
        $excerptStructure = $this->getExcerptStructure($languageCode); 
 
        foreach ($excerptStructure->getProperties() as $property) { 
            $contentType = $this->contentTypeManager->get($property->getContentTypeName()); 
 
            if (\array_key_exists($property->getName(), $data)) { 
                $property->setValue($data[$property->getName()]); 
                $contentType->write( 
                    $node, 
                    new TranslatedProperty( 
                        $property, 
                        $languageCode, 
                        $this->languageNamespace, 
                        $this->additionalPrefix 
                    ), 
                    null, // userid 
                    $webspaceKey, 
                    $languageCode, 
                    null // segmentkey 
                ); 
            } 
        } 
    } 
 
    public function load(NodeInterface $node, $webspaceKey, $languageCode) 
    { 
        $excerptStructure = $this->getExcerptStructure($languageCode); 
 
        $data = []; 
        foreach ($excerptStructure->getProperties() as $property) { 
            $contentType = $this->contentTypeManager->get($property->getContentTypeName()); 
            $contentType->read( 
                $node, 
                new TranslatedProperty( 
                    $property, 
                    $languageCode, 
                    $this->languageNamespace, 
                    $this->additionalPrefix 
                ), 
                $webspaceKey, 
                $languageCode, 
                null // segmentkey 
            ); 
 
            $data[$property->getName()] = $property->getValue(); 
        } 
 
        return $data; 
    } 
 
    public function setLanguageCode($languageCode, $languageNamespace, $namespace) 
    { 
        // lazy load excerpt structure to avoid redeclaration of classes 
        // should be done before parent::setLanguageCode because it uses the $thi<->properties 
        // which will be set in initExcerptStructure 
        $this->initProperties($languageCode); 
 
        $this->languageCode = $languageCode; 
        $this->languageNamespace = $languageNamespace; 
 
        parent::setLanguageCode($languageCode, $languageNamespace, $namespace); 
    } 
 
    public function getContentData($container) 
    { 
        $container = new ExcerptValueContainer($container); 
 
        $data = []; 
        foreach ($this->getExcerptStructure()->getProperties() as $property) { 
            if ($container->__isset($property->getName())) { 
                $property->setValue($container->__get($property->getName())); 
                $contentType = $this->contentTypeManager->get($property->getContentTypeName()); 
                $data[$property->getName()] = $contentType->getContentData($property); 
            } 
        } 
 
        return $data; 
    } 
 
    public function getFieldMapping() 
    { 
        $mappings = parent::getFieldMapping(); 
 
        foreach ($this->getExcerptStructure()->getPropertiesByTagName('sulu.search.field') as $property) { 
            $tag = $property->getTag('sulu.search.field'); 
            $tagAttributes = $tag->getAttributes(); 
 
            $mappings['excerpt' . \ucfirst($property->getName())] = [ 
                'type' => isset($tagAttributes['type']) ? $tagAttributes['type'] : 'string', 
                'field' => $this->factory->createMetadataExpression( 
                    \sprintf('object.getExtensionsData()["excerpt"]["%s"]', $property->getName()) 
                ), 
            ]; 
        } 
 
        return $mappings; 
    } 
 
    /** 
     * Returns and caches excerpt-structure. 
     * 
     * @param string $locale 
     * 
     * @return StructureInterface 
     */ 
    private function getExcerptStructure($locale = null) 
    { 
        if (null === $locale) { 
            $locale = $this->languageCode; 
        } 
 
        $excerptStructure = $this->structureManager->getStructure(self::EXCERPT_EXTENSION_NAME); 
        $excerptStructure->setLanguageCode($locale); 
 
        return $excerptStructure; 
    } 
 
    /** 
     * Initiates structure and properties. 
     * 
     * @param string $locale 
     */ 
    private function initProperties($locale) 
    { 
        // Reset the properties before new initialization. 
        $this->properties = []; 
 
        /** @var PropertyInterface $property */ 
        foreach ($this->getExcerptStructure($locale)->getProperties() as $property) { 
            $this->properties[] = $property->getName(); 
        } 
    } 
 
    public function export($properties, $format = null) 
    { 
        $container = new ExcerptValueContainer($properties); 
 
        $data = []; 
        foreach ($this->getExcerptStructure()->getProperties() as $property) { 
            if ($container->__isset($property->getName())) { 
                $property->setValue($container->__get($property->getName())); 
                $contentType = $this->contentTypeManager->get($property->getContentTypeName()); 
                if ($this->exportManager->hasExport($property->getContentTypeName(), $format)) { 
                    $options = $this->exportManager->getOptions($property->getContentTypeName(), $format); 
 
                    $data[$property->getName()] = [ 
                        'name' => $property->getName(), 
                        'value' => $contentType->exportData($property->getValue()), 
                        'type' => $property->getContentTypeName(), 
                        'options' => $options, 
                    ]; 
                } 
            } 
        } 
 
        return $data; 
    } 
 
    public function getImportPropertyNames() 
    { 
        $propertyNames = []; 
 
        foreach ($this->getExcerptStructure()->getProperties() as $property) { 
            $propertyNames[] = $property->getName(); 
        } 
 
        return $propertyNames; 
    } 
 
    public function import(NodeInterface $node, $data, $webspaceKey, $languageCode, $format) 
    { 
        $this->setLanguageCode($languageCode, 'i18n', null); 
 
        foreach ($this->getExcerptStructure()->getProperties() as $property) { 
            $contentType = $this->contentTypeManager->get($property->getContentTypeName()); 
 
            if (isset($data[$property->getName()]) 
                && $this->importManager->hasImport($property->getContentTypeName(), $format) 
            ) { 
                /* @var ContentTypeExportInterface $contentType */ 
                $contentType->importData( 
                    $node, 
                    new TranslatedProperty( 
                        $property, 
                        $languageCode, 
                        $this->languageNamespace, 
                        $this->additionalPrefix 
                    ), 
                    $data[$property->getName()], 
                    null, // userid 
                    $webspaceKey, 
                    $languageCode, 
                    null // segmentkey 
                ); 
            } 
        } 
    } 
}