vendor/sulu/sulu/src/Sulu/Component/Webspace/Loader/XmlFileLoader10.php line 266

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of Sulu.
  4.  *
  5.  * (c) Sulu GmbH
  6.  *
  7.  * This source file is subject to the MIT license that is bundled
  8.  * with this source code in the file LICENSE.
  9.  */
  10. namespace Sulu\Component\Webspace\Loader;
  11. use Sulu\Component\Localization\Localization;
  12. use Sulu\Component\Webspace\CustomUrl;
  13. use Sulu\Component\Webspace\Environment;
  14. use Sulu\Component\Webspace\Exception\InvalidWebspaceException;
  15. use Sulu\Component\Webspace\Loader\Exception\ExpectedDefaultTemplatesNotFound;
  16. use Sulu\Component\Webspace\Loader\Exception\InvalidAmountOfDefaultErrorTemplateException;
  17. use Sulu\Component\Webspace\Loader\Exception\InvalidCustomUrlException;
  18. use Sulu\Component\Webspace\Loader\Exception\InvalidDefaultErrorTemplateException;
  19. use Sulu\Component\Webspace\Loader\Exception\InvalidDefaultLocalizationException;
  20. use Sulu\Component\Webspace\Loader\Exception\InvalidErrorTemplateException;
  21. use Sulu\Component\Webspace\Loader\Exception\InvalidPortalDefaultLocalizationException;
  22. use Sulu\Component\Webspace\Loader\Exception\InvalidUrlDefinitionException;
  23. use Sulu\Component\Webspace\Loader\Exception\InvalidWebspaceDefaultLocalizationException;
  24. use Sulu\Component\Webspace\Loader\Exception\InvalidWebspaceDefaultSegmentException;
  25. use Sulu\Component\Webspace\Loader\Exception\PortalDefaultLocalizationNotFoundException;
  26. use Sulu\Component\Webspace\Loader\Exception\WebspaceDefaultSegmentNotFoundException;
  27. use Sulu\Component\Webspace\Navigation;
  28. use Sulu\Component\Webspace\NavigationContext;
  29. use Sulu\Component\Webspace\Portal;
  30. use Sulu\Component\Webspace\Security;
  31. use Sulu\Component\Webspace\Segment;
  32. use Sulu\Component\Webspace\Url;
  33. use Sulu\Component\Webspace\Webspace;
  34. use Symfony\Component\Config\Util\XmlUtils;
  35. /**
  36.  * This file loader is responsible for webspace configuration files in the xml format using the 1.0 version of the
  37.  * webspace schema definition.
  38.  *
  39.  * @deprecated
  40.  */
  41. class XmlFileLoader10 extends BaseXmlFileLoader
  42. {
  43.     public const SCHEMA_LOCATION '/schema/webspace/webspace-1.0.xsd';
  44.     public const SCHEMA_URI 'http://schemas.sulu.io/webspace/webspace-1.0.xsd';
  45.     /**
  46.      * @var \DOMXPath
  47.      */
  48.     protected $xpath;
  49.     /**
  50.      * The webspace which is created by this file loader.
  51.      *
  52.      * @var Webspace
  53.      */
  54.     protected $webspace;
  55.     /**
  56.      * Loads a webspace from a xml file.
  57.      *
  58.      * @param mixed $resource The resource
  59.      * @param string $type The resource type
  60.      *
  61.      * @return Webspace The webspace object for the given resource
  62.      */
  63.     public function load($resource$type null)
  64.     {
  65.         $path $this->getLocator()->locate($resource);
  66.         // load data in path
  67.         return $this->parseXml($path);
  68.     }
  69.     /**
  70.      * Returns true if this class supports the given resource.
  71.      *
  72.      * @param mixed $resource A resource
  73.      * @param string $type The resource type
  74.      *
  75.      * @return bool true if this class supports the given resource, false otherwise
  76.      */
  77.     public function supports($resource$type null)
  78.     {
  79.         return parent::supports($resource$type);
  80.     }
  81.     /**
  82.      * Parses the entire file and returns a webspace object.
  83.      *
  84.      * @param string $file
  85.      *
  86.      * @return Webspace
  87.      */
  88.     protected function parseXml($file)
  89.     {
  90.         $this->xpath = new \DOMXPath($this->tryLoad($file));
  91.         $this->xpath->registerNamespace('x''http://schemas.sulu.io/webspace/webspace');
  92.         // set simple webspace properties
  93.         $this->webspace = new Webspace();
  94.         $this->webspace->setName($this->xpath->query('/x:webspace/x:name')->item(0)->nodeValue);
  95.         $this->webspace->setKey($this->xpath->query('/x:webspace/x:key')->item(0)->nodeValue);
  96.         $this->webspace->setTheme($this->generateTheme());
  97.         $this->webspace->setNavigation($this->generateNavigation());
  98.         $this->webspace->setResourceLocatorStrategy('tree_leaf_edit');
  99.         $this->generateTemplates($this->webspace);
  100.         $this->generateDefaultTemplates($this->webspace);
  101.         // set security
  102.         $this->generateSecurity();
  103.         // set localizations on webspaces
  104.         $this->generateWebspaceLocalizations();
  105.         // set segments on webspaces
  106.         $this->generateSegments();
  107.         // set portals on webspaces
  108.         $this->generatePortals();
  109.         // validate the webspace, and throw exceptions if not valid
  110.         $this->validate();
  111.         return $this->webspace;
  112.     }
  113.     /**
  114.      * Returns xml-doc when one scheme matches.
  115.      *
  116.      * @param string $file
  117.      *
  118.      * @return \DOMDocument
  119.      *
  120.      * @throws InvalidWebspaceException
  121.      */
  122.     protected function tryLoad($file)
  123.     {
  124.         try {
  125.             return XmlUtils::loadFile($file__DIR__ . static::SCHEMA_LOCATION);
  126.         } catch (\InvalidArgumentException $e) {
  127.             throw new InvalidWebspaceException(
  128.                 \sprintf(
  129.                     'Could not parse webspace XML file "%s"',
  130.                     $file
  131.                 ),
  132.                 null,
  133.                 $e
  134.             );
  135.         }
  136.     }
  137.     /**
  138.      * Validate result.
  139.      */
  140.     protected function validate()
  141.     {
  142.         $this->validateWebspaceDefaultLocalization();
  143.         $this->validateDefaultPortalLocalization();
  144.         $this->validateWebspaceDefaultSegment();
  145.     }
  146.     /**
  147.      * Sets the default localization for the given portal.
  148.      *
  149.      * @param Portal $portal
  150.      *
  151.      * @return bool True when successful, otherwise false
  152.      */
  153.     protected function loadPortalLocalizationDefaultFromWebspace($portal)
  154.     {
  155.         $webspaceDefaultLocalization $this->webspace->getDefaultLocalization();
  156.         foreach ($portal->getLocalizations() as $localization) {
  157.             if ($webspaceDefaultLocalization
  158.                 && $webspaceDefaultLocalization->getLocale() == $localization->getLocale()
  159.             ) {
  160.                 $localization->setDefault(true);
  161.                 $portal->setDefaultLocalization($localization);
  162.                 return true;
  163.             }
  164.         }
  165.         return false;
  166.     }
  167.     /**
  168.      * Generates all localizations for the given portal.
  169.      */
  170.     protected function generatePortalLocalizations(\DOMNode $portalNodePortal $portal)
  171.     {
  172.         if ($this->xpath->query('x:localizations'$portalNode)->length 0) {
  173.             // set localizations from portal, if they are set
  174.             $localizationNodes $this->xpath->query('x:localizations/x:localization'$portalNode);
  175.             $this->generateLocalizationsFromNodeList($localizationNodes$portal);
  176.         } else {
  177.             // if the portal has no localizations fallback to the localizations from the webspace
  178.             $localizationNodes $this->xpath->query('/x:webspace/x:localizations//x:localization');
  179.             $this->generateLocalizationsFromNodeList($localizationNodes$portaltrue);
  180.         }
  181.     }
  182.     /**
  183.      * Generates the localizations for the given portal from the given DOMNodeList.
  184.      *
  185.      * @param bool $flat
  186.      */
  187.     protected function generateLocalizationsFromNodeList(\DOMNodeList $localizationNodesPortal $portal$flat false)
  188.     {
  189.         foreach ($localizationNodes as $localizationNode) {
  190.             $localization $this->generateLocalizationFromNode($localizationNode$flat);
  191.             $portal->addLocalization($localization);
  192.         }
  193.     }
  194.     /**
  195.      * Generates a localization from the given node.
  196.      *
  197.      * @param bool $flat
  198.      * @param ?\DOMElement $parent
  199.      *
  200.      * @return Localization
  201.      */
  202.     protected function generateLocalizationFromNode(\DOMElement $localizationNode$flat false$parent null)
  203.     {
  204.         $localization = new Localization();
  205.         $localization->setLanguage($localizationNode->attributes->getNamedItem('language')->nodeValue);
  206.         // set parent if given
  207.         if ($parent) {
  208.             $localization->setParent($parent);
  209.         }
  210.         // set optional nodes
  211.         $countryNode $localizationNode->attributes->getNamedItem('country');
  212.         if ($countryNode) {
  213.             $localization->setCountry($countryNode->nodeValue);
  214.         }
  215.         $shadowNode $localizationNode->attributes->getNamedItem('shadow');
  216.         if ($shadowNode) {
  217.             $localization->setShadow($shadowNode->nodeValue);
  218.         }
  219.         $defaultNode $localizationNode->attributes->getNamedItem('default');
  220.         if ($defaultNode) {
  221.             $localization->setDefault('true' == $defaultNode->nodeValue);
  222.         } else {
  223.             $localization->setDefault(false);
  224.         }
  225.         $xDefaultNode $localizationNode->attributes->getNamedItem('x-default');
  226.         if ($xDefaultNode) {
  227.             // @deprecated
  228.             @\trigger_error('Set x-default="true" attribute on the `<localization>` tag in webspace is deprecated use default="true" instead.'\E_USER_DEPRECATED);
  229.             $localization->setXDefault('true' == $xDefaultNode->nodeValue);
  230.         } else {
  231.             $localization->setXDefault(false);
  232.         }
  233.         // set child nodes
  234.         if (!$flat) {
  235.             foreach ($this->xpath->query('x:localization'$localizationNode) as $childNode) {
  236.                 $localization->addChild($this->generateLocalizationFromNode($childNode$flat$localization));
  237.             }
  238.         }
  239.         return $localization;
  240.     }
  241.     /**
  242.      * Generates and sets the security object from the XML document.
  243.      */
  244.     protected function generateSecurity()
  245.     {
  246.         $securityNodeList $this->xpath->query('/x:webspace/x:security');
  247.         if ($securityNodeList->length 0) {
  248.             $securityNode $securityNodeList->item(0);
  249.             $securitySystemNode $this->xpath->query('x:system'$securityNode);
  250.             $permissionCheckAttribute $securityNode->attributes->getNamedItem('permission-check');
  251.             $security = new Security();
  252.             $security->setSystem($securitySystemNode->item(0)->nodeValue);
  253.             $security->setPermissionCheck($permissionCheckAttribute 'true' === $permissionCheckAttribute->nodeValue false);
  254.             $this->webspace->setSecurity($security);
  255.         }
  256.     }
  257.     /**
  258.      * Generates the localization for the webspace from the XML document.
  259.      */
  260.     protected function generateWebspaceLocalizations()
  261.     {
  262.         foreach ($this->xpath->query('/x:webspace/x:localizations/x:localization') as $localizationNode) {
  263.             $localization $this->generateLocalizationFromNode($localizationNode);
  264.             $this->webspace->addLocalization($localization);
  265.         }
  266.     }
  267.     /**
  268.      * Generates the available segments for the webspace from the XML document.
  269.      */
  270.     protected function generateSegments()
  271.     {
  272.         foreach ($this->xpath->query('/x:webspace/x:segments/x:segment') as $segmentNode) {
  273.             /** @var \DOMNode $segmentNode */
  274.             $segment = new Segment();
  275.             $segment->setKey($segmentNode->attributes->getNamedItem('key')->nodeValue);
  276.             $segment->setMetadata($this->loadMeta('x:meta/x:*'$segmentNode));
  277.             $defaultNode $segmentNode->attributes->getNamedItem('default');
  278.             if ($defaultNode) {
  279.                 $segment->setDefault('true' == $defaultNode->nodeValue);
  280.             } else {
  281.                 $segment->setDefault(false);
  282.             }
  283.             $this->webspace->addSegment($segment);
  284.         }
  285.     }
  286.     /**
  287.      * Generate all the portals for the webspace.
  288.      */
  289.     protected function generatePortals()
  290.     {
  291.         foreach ($this->xpath->query('/x:webspace/x:portals/x:portal') as $portalNode) {
  292.             /** @var \DOMNode $portalNode */
  293.             $portal = new Portal();
  294.             $portal->setName($this->xpath->query('x:name'$portalNode)->item(0)->nodeValue);
  295.             $portal->setKey($this->xpath->query('x:key'$portalNode)->item(0)->nodeValue);
  296.             // set localization on portal
  297.             $this->generatePortalLocalizations($portalNode$portal);
  298.             $this->webspace->addPortal($portal);
  299.             $portal->setWebspace($this->webspace);
  300.             // set environments
  301.             $this->generateEnvironments($portalNode$portal);
  302.         }
  303.     }
  304.     /**
  305.      * Generates the theme for the webspace.
  306.      *
  307.      * @return string
  308.      */
  309.     protected function generateTheme()
  310.     {
  311.         $nodes $this->xpath->query('/x:webspace/x:theme/x:key');
  312.         if ($nodes->length 0) {
  313.             return $nodes->item(0)->nodeValue;
  314.         }
  315.         $nodes $this->xpath->query('/x:webspace/x:theme');
  316.         if (=== $nodes->length) {
  317.             return;
  318.         }
  319.         return $nodes->item(0)->nodeValue;
  320.     }
  321.     /**
  322.      * Generates the available template types for the given webspace.
  323.      *
  324.      * @return Webspace
  325.      *
  326.      * @throws InvalidAmountOfDefaultErrorTemplateException
  327.      * @throws InvalidDefaultErrorTemplateException
  328.      * @throws InvalidErrorTemplateException
  329.      */
  330.     protected function generateTemplates(Webspace $webspace)
  331.     {
  332.         $defaultErrorTemplates 0;
  333.         foreach ($this->xpath->query('/x:webspace/x:theme/x:error-templates/x:error-template') as $errorTemplateNode) {
  334.             /* @var \DOMNode $errorTemplateNode */
  335.             $template $errorTemplateNode->nodeValue;
  336.             if (null !== ($codeNode $errorTemplateNode->attributes->getNamedItem('code'))) {
  337.                 $webspace->addTemplate('error-' $codeNode->nodeValue$template);
  338.             } elseif (null !== ($defaultNode $errorTemplateNode->attributes->getNamedItem('default'))) {
  339.                 $default 'true' === $defaultNode->nodeValue;
  340.                 if (!$default) {
  341.                     throw new InvalidDefaultErrorTemplateException($template$this->webspace->getKey());
  342.                 }
  343.                 ++$defaultErrorTemplates;
  344.                 $webspace->addTemplate('error'$template);
  345.             } else {
  346.                 throw new InvalidErrorTemplateException($template$this->webspace->getKey());
  347.             }
  348.         }
  349.         // only one or none default error-template is legal
  350.         if ($defaultErrorTemplates 1) {
  351.             throw new InvalidAmountOfDefaultErrorTemplateException($this->webspace->getKey());
  352.         }
  353.         return $webspace;
  354.     }
  355.     /**
  356.      * Generates the default templates for the webspace.
  357.      *
  358.      * @return Webspace
  359.      *
  360.      * @throws ExpectedDefaultTemplatesNotFound
  361.      */
  362.     protected function generateDefaultTemplates(Webspace $webspace)
  363.     {
  364.         $expected = ['page''home'];
  365.         foreach ($this->xpath->query('/x:webspace/x:theme/x:default-templates/x:default-template') as $node) {
  366.             /* @var \DOMNode $node */
  367.             $template $node->nodeValue;
  368.             $type $node->attributes->getNamedItem('type')->nodeValue;
  369.             $webspace->addDefaultTemplate($type$template);
  370.             if ('homepage' === $type) {
  371.                 $webspace->addDefaultTemplate('home'$template);
  372.             }
  373.         }
  374.         $found \array_keys($webspace->getDefaultTemplates());
  375.         foreach ($expected as $item) {
  376.             if (!\in_array($item$found)) {
  377.                 throw new ExpectedDefaultTemplatesNotFound($this->webspace->getKey(), $expected$found);
  378.             }
  379.         }
  380.         return $webspace;
  381.     }
  382.     /**
  383.      * Generates the availabel navigation contexts for the webspace.
  384.      *
  385.      * @return Navigation
  386.      */
  387.     protected function generateNavigation()
  388.     {
  389.         $contexts = [];
  390.         foreach ($this->xpath->query('/x:webspace/x:navigation/x:contexts/x:context') as $contextNode) {
  391.             /* @var \DOMNode $contextNode */
  392.             $contexts[] = new NavigationContext(
  393.                 $contextNode->attributes->getNamedItem('key')->nodeValue,
  394.                 $this->loadMeta('x:meta/x:*'$contextNode)
  395.             );
  396.         }
  397.         return new Navigation($contexts);
  398.     }
  399.     /**
  400.      * Loads the meta information like a translatable title from the webspace.
  401.      *
  402.      * @param string $path
  403.      *
  404.      * @return mixed[]
  405.      */
  406.     protected function loadMeta($path\DOMNode $context null)
  407.     {
  408.         $result = [];
  409.         /** @var \DOMElement $node */
  410.         foreach ($this->xpath->query($path$context) as $node) {
  411.             $attribute $node->tagName;
  412.             $lang $this->xpath->query('@lang'$node)->item(0)->nodeValue;
  413.             if (!isset($result[$node->tagName])) {
  414.                 $result[$attribute] = [];
  415.             }
  416.             $result[$attribute][$lang] = $node->textContent;
  417.         }
  418.         return $result;
  419.     }
  420.     /**
  421.      * Generates the definitions for the available environments for this webspace.
  422.      */
  423.     protected function generateEnvironments(\DOMNode $portalNodePortal $portal)
  424.     {
  425.         foreach ($this->xpath->query('x:environments/x:environment'$portalNode) as $environmentNode) {
  426.             /** @var \DOMNode $environmentNode */
  427.             $environment = new Environment();
  428.             $environment->setType($environmentNode->attributes->getNamedItem('type')->nodeValue);
  429.             $this->generateUrls($environmentNode$environment);
  430.             $this->generateCustomUrls($environmentNode$environment);
  431.             $portal->addEnvironment($environment);
  432.         }
  433.     }
  434.     /**
  435.      * Generates the URLs for the given environment.
  436.      *
  437.      * @throws Exception\InvalidUrlDefinitionException
  438.      */
  439.     protected function generateUrls(\DOMNode $environmentNodeEnvironment $environment)
  440.     {
  441.         foreach ($this->xpath->query('x:urls/x:url'$environmentNode) as $urlNode) {
  442.             // check if the url is valid, and throw an exception otherwise
  443.             if (!$this->checkUrlNode($urlNode)) {
  444.                 throw new InvalidUrlDefinitionException($this->webspace$urlNode->nodeValue);
  445.             }
  446.             /** @var \DOMNode $urlNode */
  447.             $url = new Url();
  448.             $url->setUrl(\rtrim($urlNode->nodeValue'/'));
  449.             // set optional nodes
  450.             $url->setLanguage($this->getOptionalNodeAttribute($urlNode'language'));
  451.             $url->setCountry($this->getOptionalNodeAttribute($urlNode'country'));
  452.             $url->setSegment($this->getOptionalNodeAttribute($urlNode'segment'));
  453.             $url->setRedirect($this->getOptionalNodeAttribute($urlNode'redirect'));
  454.             $url->setMain($this->getOptionalNodeAttribute($urlNode'main'false));
  455.             $environment->addUrl($url);
  456.         }
  457.     }
  458.     /**
  459.      * Generates the custom URLs from the XML document.
  460.      *
  461.      * A custom URL must contain at lease one *, which will be used as a placeholder.
  462.      *
  463.      * @throws InvalidCustomUrlException
  464.      */
  465.     protected function generateCustomUrls(\DOMNode $environmentNodeEnvironment $environment)
  466.     {
  467.         foreach ($this->xpath->query('x:custom-urls/x:custom-url'$environmentNode) as $urlNode) {
  468.             /** @var \DOMNode $urlNode */
  469.             $url = new CustomUrl();
  470.             $url->setUrl(\rtrim($urlNode->nodeValue'/'));
  471.             if (false === \strpos($url->getUrl(), '*')) {
  472.                 throw new InvalidCustomUrlException($this->webspace$url->getUrl());
  473.             }
  474.             $environment->addCustomUrl($url);
  475.         }
  476.     }
  477.     /**
  478.      * Returns an optional value from the given node. The default value will be used if the node does not exist.
  479.      *
  480.      * @param string $name
  481.      */
  482.     protected function getOptionalNodeAttribute(\DOMNode $node$name$default null)
  483.     {
  484.         $attribute $node->attributes->getNamedItem($name);
  485.         if ($attribute) {
  486.             return $attribute->nodeValue;
  487.         }
  488.         return $default;
  489.     }
  490.     /**
  491.      * Checks if the urlNode is valid for this webspace.
  492.      *
  493.      * @return bool
  494.      */
  495.     protected function checkUrlNode(\DOMNode $urlNode)
  496.     {
  497.         $hasLocalization = (null != $urlNode->attributes->getNamedItem('localization'))
  498.             || (false !== \strpos($urlNode->nodeValue'{localization}'));
  499.         $hasLanguage = (null != $urlNode->attributes->getNamedItem('language'))
  500.             || (false !== \strpos($urlNode->nodeValue'{language}'))
  501.             || $hasLocalization;
  502.         $hasRedirect = (null != $urlNode->attributes->getNamedItem('redirect'));
  503.         return $hasLanguage || $hasRedirect;
  504.     }
  505.     /**
  506.      * Validate default webspace localization.
  507.      *
  508.      * @throws Exception\InvalidWebspaceDefaultLocalizationException
  509.      */
  510.     protected function validateWebspaceDefaultLocalization()
  511.     {
  512.         try {
  513.             $this->validateDefaultLocalization($this->webspace->getLocalizations());
  514.         } catch (InvalidDefaultLocalizationException $ex) {
  515.             throw new InvalidWebspaceDefaultLocalizationException($this->webspace$ex);
  516.         }
  517.     }
  518.     /**
  519.      * Validate portal localization.
  520.      *
  521.      * @throws Exception\PortalDefaultLocalizationNotFoundException
  522.      * @throws Exception\InvalidPortalDefaultLocalizationException
  523.      */
  524.     protected function validateDefaultPortalLocalization()
  525.     {
  526.         // check all portal localizations
  527.         foreach ($this->webspace->getPortals() as $portal) {
  528.             try {
  529.                 if (!$this->validateDefaultLocalization($portal->getLocalizations())) {
  530.                     // try to load the webspace localizations before throwing an exception
  531.                     if (!$this->loadPortalLocalizationDefaultFromWebspace($portal)) {
  532.                         throw new PortalDefaultLocalizationNotFoundException($this->webspace$portal);
  533.                     }
  534.                 }
  535.             } catch (InvalidDefaultLocalizationException $ex) {
  536.                 throw new InvalidPortalDefaultLocalizationException($this->webspace$portal$ex);
  537.             }
  538.         }
  539.     }
  540.     /**
  541.      * Validate webspace default segment.
  542.      *
  543.      * @throws Exception\WebspaceDefaultSegmentNotFoundException
  544.      * @throws Exception\InvalidWebspaceDefaultSegmentException
  545.      */
  546.     protected function validateWebspaceDefaultSegment()
  547.     {
  548.         // check if there are duplicate defaults in the webspaces segments
  549.         $segments $this->webspace->getSegments();
  550.         if ($segments) {
  551.             $webspaceDefaultSegmentFound false;
  552.             foreach ($segments as $webspaceSegment) {
  553.                 if ($webspaceSegment->isDefault()) {
  554.                     // throw an exception, if a new default segment is found, although there already is one
  555.                     if ($webspaceDefaultSegmentFound) {
  556.                         throw new InvalidWebspaceDefaultSegmentException($this->webspace);
  557.                     }
  558.                     $webspaceDefaultSegmentFound true;
  559.                 }
  560.             }
  561.             if (!$webspaceDefaultSegmentFound) {
  562.                 throw new WebspaceDefaultSegmentNotFoundException($this->webspace);
  563.             }
  564.         }
  565.     }
  566.     /**
  567.      * Returns true if there is one default localization.
  568.      *
  569.      * @param Localization[] $localizations
  570.      *
  571.      * @return bool
  572.      *
  573.      * @throws Exception\InvalidDefaultLocalizationException
  574.      */
  575.     protected function validateDefaultLocalization($localizations)
  576.     {
  577.         $result false;
  578.         foreach ($localizations as $localization) {
  579.             if ($localization->isDefault()) {
  580.                 if ($result) {
  581.                     throw new InvalidDefaultLocalizationException();
  582.                 }
  583.                 $result true;
  584.             }
  585.         }
  586.         return $result;
  587.     }
  588. }