<commit_before><?php
/**
 * Copyright (C) 2014 Ready Business System
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
namespace Rbs\Catalog\Documents;

use Change\Documents\Events\Event;
use Change\Http\Rest\V1\Link;
use Change\Stdlib\String;

/**
 * @name \Rbs\Catalog\Documents\Product
 */
class Product extends \Compilation\Rbs\Catalog\Documents\Product
{
	/**
	 * @return \Rbs\Media\Documents\Image|null
	 */
	public function getFirstVisual()
	{
		$visuals = $this->getVisuals();
		return $visuals->count() ? $visuals->offsetGet(0) : null;
	}

	public function onDefaultUpdateRestResult(Event $event)
	{
		parent::onDefaultUpdateRestResult($event);
		$restResult = $event->getParam('restResult');

		/** @var $document Product */
		$document = $event->getDocument();
		if ($restResult instanceof \Change\Http\Rest\V1\Resources\DocumentResult)
		{

			$documentResult = $restResult;
			$um = $documentResult->getUrlManager();
			$selfLinks = $documentResult->getRelLink('self');
			$selfLink = array_shift($selfLinks);
			if ($selfLink instanceof Link)
			{
				$pathParts = explode('/', $selfLink->getPathInfo());
				array_pop($pathParts);
				$baseUrl = implode('/', $pathParts);
				$this->addLinkOnResult($documentResult, $um, $baseUrl);

				$image = $document->getFirstVisual();
				if ($image)
				{
					$documentResult->addLink(array('href' => $image->getPublicURL(512, 512), 'rel' => 'adminthumbnail'));
				}

				if ($document->getVariantGroup())
				{
					/* @var $commerceServices \Rbs\Commerce\CommerceServices */
					$commerceServices = $event->getServices('commerceServices');
					$catalogManager = $commerceServices->getCatalogManager();
					$documentResult->setProperty('variantInfo', $catalogManager->getVariantInfo($document));
				}
			}
		}
		elseif ($restResult instanceof \Change\Http\Rest\V1\Resources\DocumentLink)
		{
			$image = $document->getFirstVisual();
			if ($image)
			{
				$restResult->setProperty('adminthumbnail', $image->getPublicURL(512, 512));
			}
			if ($document->getVariantGroup())
			{
				$restResult->setProperty('variantGroup', array('id' => $document->getVariantGroup()->getId(),
					'rootProductId' => $document->getVariantGroup()->getRootProductId()));
			}
			$restResult->setProperty('variant', $document->getVariant());
		}
	}

	/**
	 * @param \Change\Http\Rest\V1\Resources\DocumentResult $documentResult
	 * @param \Change\Http\UrlManager $urlManager
	 * @param string $baseUrl
	 */
	protected function addLinkOnResult($documentResult, $urlManager, $baseUrl)
	{
		$documentResult->addLink(new Link($urlManager, $baseUrl . '/ProductListItems/', 'productListItems'));
		$documentResult->addLink(new Link($urlManager, $baseUrl . '/Prices/', 'prices'));
	}

	protected $ignoredPropertiesForRestEvents = array('model', 'declinationGroup', 'declination');

	/**
	 * @param \Zend\EventManager\EventManagerInterface $eventManager
	 */
	protected function attachEvents($eventManager)
	{
		parent::attachEvents($eventManager);
		$eventManager->attach(Event::EVENT_CREATED, array($this, 'onDefaultCreated'), 5);
		$eventManager->attach(Event::EVENT_CREATE, array($this, 'onDefaultCreate'), 10);
		$eventManager->attach(Event::EVENT_UPDATE, array($this, 'onDefaultUpdate'), 10);
		$eventManager->attach(Event::EVENT_UPDATED, array($this, 'onDefaultUpdated'), 10);
	}

	/**
	 * @param Event $event
	 */
	public function onDefaultCreate(Event $event)
	{
		/** @var $product Product */
		$product = $event->getDocument();
		$cs = $event->getServices('commerceServices');

		if ($cs instanceof \Rbs\Commerce\CommerceServices)
		{
			$attributeValues = $product->getCurrentLocalization()->getAttributeValues();
			$normalizedAttributeValues = $cs->getAttributeManager()->normalizeRestAttributeValues($attributeValues, $product->getAttribute());
			$product->getCurrentLocalization()->setAttributeValues($normalizedAttributeValues);
		}

		if ($product->getNewSkuOnCreation())
		{
			/* @var $sku \Rbs\Stock\Documents\Sku */
			$sku = $product->getDocumentManager()->getNewDocumentInstanceByModelName('Rbs_Stock_Sku');
			$sku->setCode($product->buildSkuCodeFromLabel($cs));
			$sku->save();
			$product->setSku($sku);
		}
	}

	/**
	 * @param Event $event
	 */
	public function onDefaultCreated(Event $event)
	{
		$product = $event->getDocument();
		if ($product instanceof Product)
		{
			// Section product list synchronization.
			if ($product->getPublicationSections()->count())
			{
				$product->synchronizeSectionDocumentLists();
			}

			$cs = $event->getServices('commerceServices');
			if ($cs instanceof \Rbs\Commerce\CommerceServices)
			{
				$normalizedAttributeValues = $product->getCurrentLocalization()->getAttributeValues();
				$cs->getAttributeManager()->setAttributeValues($product, $normalizedAttributeValues);
			}
		}
	}

	/**
	 * @param \Rbs\Commerce\CommerceServices $commerceServices
	 * @return string
	 */
	protected function buildSkuCodeFromLabel(\Rbs\Commerce\CommerceServices $commerceServices = null)
	{
		$retry = 0;
		$baseCode = String::subString(preg_replace('/[^a-zA-Z0-9]+/', '-',
			String::stripAccents(String::toUpper($this->getLabel()))), 0, 80);
		$skuCode = $baseCode;
		if ($commerceServices)
		{
			$sku = $commerceServices->getStockManager()->getSkuByCode($skuCode);
			while ($sku && $retry++ < 100)
			{
				$skuCode = String::subString($baseCode, 0, 73) . '-' . String::toUpper(String::random(6, false));
				$sku = $commerceServices->getStockManager()->getSkuByCode($skuCode);
			}
		}
		return $skuCode;
	}

	/**
	 * @param Event $event
	 */
	public function onDefaultUpdate(Event $event)
	{
		/** @var $product Product */
		$product = $event->getDocument();
		$modifiedPropertyNames = $product->getModifiedPropertyNames();
		if (in_array('attributeValues', $modifiedPropertyNames) || in_array('attribute', $modifiedPropertyNames))
		{
			$cs = $event->getServices('commerceServices');
			if ($cs instanceof \Rbs\Commerce\CommerceServices)
			{
				$attributeValues = $product->getCurrentLocalization()->getAttributeValues();
				$normalizedAttributeValues = $cs->getAttributeManager()->normalizeRestAttributeValues($attributeValues, $product->getAttribute());
				$product->getCurrentLocalization()->setAttributeValues($normalizedAttributeValues);
				$cs->getAttributeManager()->setAttributeValues($product, $normalizedAttributeValues);
			}
		}

		// Section product list synchronization.
		if (in_array('publicationSections', $modifiedPropertyNames))
		{
			$product->synchronizeSectionDocumentLists();
		}

		if (in_array('variantGroup', $modifiedPropertyNames))
		{
			$variantGroup = $product->getVariantGroup();
			if ($variantGroup && $variantGroup->getRootProduct() === $this)
			{
				$product->setSku(null);
			}
		}
	}

	protected function synchronizeSectionDocumentLists()
	{
		$dm = $this->getDocumentManager();

		if ($this->getPublicationSectionsCount())
		{
			$dqb1 = $dm->getNewQuery('Rbs_Catalog_SectionProductList');
			$pb1 = $dqb1->getPredicateBuilder();
			$dqb1->andPredicates($pb1->in('synchronizedSection', $this->getPublicationSectionsIds()));
			$requiredListIds = $dqb1->getDocuments()->ids();
		}
		else
		{
			$requiredListIds = array();
		}

		$dqb2 = $dm->getNewQuery('Rbs_Catalog_SectionProductList');
		$d2qb2 = $dqb2->getModelBuilder('Rbs_Catalog_ProductListItem', 'productList');
		$pb2 = $d2qb2->getPredicateBuilder();
		$d2qb2->andPredicates($pb2->eq('product', $this));
		$existingListIds = $dqb2->getDocuments()->ids();

		// Item creation.
		$listIds = array_diff($requiredListIds, $existingListIds);
		foreach ($listIds as $listId)
		{
			/* @var $list \Rbs\Catalog\Documents\SectionProductList */
			$list = $dm->getDocumentInstance($listId);

			/* @var $item \Rbs\Catalog\Documents\ProductListItem */
			$item = $dm->getNewDocumentInstanceByModelName('Rbs_Catalog_ProductListItem');
			$item->setProductList($list);
			$item->setProduct($this);
			$item->save();
		}

		// Item deletion.
		$listIds = array_diff($existingListIds, $requiredListIds);
		if (count($listIds))
		{
			$dqb3 = $dm->getNewQuery('Rbs_Catalog_ProductListItem');
			$pb3 = $dqb3->getPredicateBuilder();
			$dqb3->andPredicates($pb3->in('productList', $listIds), $pb3->eq('product', $this));
			foreach ($dqb3->getDocuments() as $item)
			{
				$item->delete();
			}
		}
	}

	/**
	 * @var boolean
	 */
	protected $newSkuOnCreation = true;

	/**
	 * @return boolean
	 */
	public function getNewSkuOnCreation()
	{
		return $this->newSkuOnCreation;
	}

	/**
	 * @param boolean $newSkuOnCreation
	 * @return $this
	 */
	public function setNewSkuOnCreation($newSkuOnCreation)
	{
		$this->newSkuOnCreation = $newSkuOnCreation;
		return $this;
	}

	/**
	 * @param string $crossSellingType
	 * @return \Rbs\Catalog\Documents\CrossSellingProductList|null
	 */
	public function getCrossSellingListByType($crossSellingType)
	{
		$query = $this->getDocumentManager()->getNewQuery('Rbs_Catalog_CrossSellingProductList');
		$query->andPredicates($query->eq('product', $this->getId()), $query->eq('crossSellingType', $crossSellingType));
		return $query->getFirstDocument();
	}

	/**
	 * @return Boolean
	 */
	public function hasVariants()
	{
		return !($this->getVariant()) && $this->getVariantGroup();
	}

	/**
	 * @return null|Product
	 */
	protected function getRelatedRootProduct()
	{
		if ($this->getVariant() && $this->getVariantGroupId())
		{
			$variantGroup = $this->getVariantGroup();
			if ($variantGroup)
			{
				return $variantGroup->getRootProduct();
			}
		}
		return null;
	}

	/**
	 * @return \Change\Documents\DocumentArrayProperty|\Rbs\Website\Documents\Section[]
	 */
	public function getPublicationSections()
	{
		$publicationSections = parent::getPublicationSections();
		if ($publicationSections instanceof \Change\Documents\DocumentArrayProperty && !$publicationSections->count())
		{
			$rootProduct = $this->getRelatedRootProduct();
			if ($rootProduct instanceof Product)
			{
				$publicationSections->setDefaultIds($rootProduct->getPublicationSections()->getIds());
			}
		}
		return $publicationSections;
	}

	/**
	 * @param Event $event
	 */
	public function onDefaultUpdated(Event $event)
	{
		if ($this !== $event->getDocument())
		{
			return;
		}

		$modifiedPropertyNames = $event->getParam('modifiedPropertyNames');
		if (is_array($modifiedPropertyNames) && in_array('publicationSections', $modifiedPropertyNames))
		{
			if ($this->getVariantGroupId() && !$this->getVariant())
			{
				$query = $this->getDocumentManager()->getNewQuery($this->getDocumentModel());
				$query->andPredicates($query->eq('variantGroup', $this->getVariantGroupId()),
					$query->neq('id', $this->getId()));
<commit_msg>Fix publicationSection override in product document<commit_after><?php
/**
 * Copyright (C) 2014 Ready Business System
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
namespace Rbs\Catalog\Documents;

use Change\Documents\Events\Event;
use Change\Http\Rest\V1\Link;
use Change\Stdlib\String;

/**
 * @name \Rbs\Catalog\Documents\Product
 */
class Product extends \Compilation\Rbs\Catalog\Documents\Product
{
	/**
	 * @return \Rbs\Media\Documents\Image|null
	 */
	public function getFirstVisual()
	{
		$visuals = $this->getVisuals();
		return $visuals->count() ? $visuals->offsetGet(0) : null;
	}

	public function onDefaultUpdateRestResult(Event $event)
	{
		parent::onDefaultUpdateRestResult($event);
		$restResult = $event->getParam('restResult');

		/** @var $document Product */
		$document = $event->getDocument();
		if ($restResult instanceof \Change\Http\Rest\V1\Resources\DocumentResult)
		{

			$documentResult = $restResult;
			$um = $documentResult->getUrlManager();
			$selfLinks = $documentResult->getRelLink('self');
			$selfLink = array_shift($selfLinks);
			if ($selfLink instanceof Link)
			{
				$pathParts = explode('/', $selfLink->getPathInfo());
				array_pop($pathParts);
				$baseUrl = implode('/', $pathParts);
				$this->addLinkOnResult($documentResult, $um, $baseUrl);

				$image = $document->getFirstVisual();
				if ($image)
				{
					$documentResult->addLink(array('href' => $image->getPublicURL(512, 512), 'rel' => 'adminthumbnail'));
				}

				if ($document->getVariantGroup())
				{
					/* @var $commerceServices \Rbs\Commerce\CommerceServices */
					$commerceServices = $event->getServices('commerceServices');
					$catalogManager = $commerceServices->getCatalogManager();
					$documentResult->setProperty('variantInfo', $catalogManager->getVariantInfo($document));
				}
			}
		}
		elseif ($restResult instanceof \Change\Http\Rest\V1\Resources\DocumentLink)
		{
			$image = $document->getFirstVisual();
			if ($image)
			{
				$restResult->setProperty('adminthumbnail', $image->getPublicURL(512, 512));
			}
			if ($document->getVariantGroup())
			{
				$restResult->setProperty('variantGroup', array('id' => $document->getVariantGroup()->getId(),
					'rootProductId' => $document->getVariantGroup()->getRootProductId()));
			}
			$restResult->setProperty('variant', $document->getVariant());
		}
	}

	/**
	 * @param \Change\Http\Rest\V1\Resources\DocumentResult $documentResult
	 * @param \Change\Http\UrlManager $urlManager
	 * @param string $baseUrl
	 */
	protected function addLinkOnResult($documentResult, $urlManager, $baseUrl)
	{
		$documentResult->addLink(new Link($urlManager, $baseUrl . '/ProductListItems/', 'productListItems'));
		$documentResult->addLink(new Link($urlManager, $baseUrl . '/Prices/', 'prices'));
	}

	protected $ignoredPropertiesForRestEvents = array('model', 'declinationGroup', 'declination');

	/**
	 * @param \Zend\EventManager\EventManagerInterface $eventManager
	 */
	protected function attachEvents($eventManager)
	{
		parent::attachEvents($eventManager);
		$eventManager->attach(Event::EVENT_CREATED, array($this, 'onDefaultCreated'), 5);
		$eventManager->attach(Event::EVENT_CREATE, array($this, 'onDefaultCreate'), 10);
		$eventManager->attach(Event::EVENT_UPDATE, array($this, 'onDefaultUpdate'), 10);
		$eventManager->attach(Event::EVENT_UPDATED, array($this, 'onDefaultUpdated'), 10);
	}

	/**
	 * @param Event $event
	 */
	public function onDefaultCreate(Event $event)
	{
		/** @var $product Product */
		$product = $event->getDocument();
		$cs = $event->getServices('commerceServices');

		if ($cs instanceof \Rbs\Commerce\CommerceServices)
		{
			$attributeValues = $product->getCurrentLocalization()->getAttributeValues();
			$normalizedAttributeValues = $cs->getAttributeManager()->normalizeRestAttributeValues($attributeValues, $product->getAttribute());
			$product->getCurrentLocalization()->setAttributeValues($normalizedAttributeValues);
		}

		if ($product->getNewSkuOnCreation())
		{
			/* @var $sku \Rbs\Stock\Documents\Sku */
			$sku = $product->getDocumentManager()->getNewDocumentInstanceByModelName('Rbs_Stock_Sku');
			$sku->setCode($product->buildSkuCodeFromLabel($cs));
			$sku->save();
			$product->setSku($sku);
		}
	}

	/**
	 * @param Event $event
	 */
	public function onDefaultCreated(Event $event)
	{
		$product = $event->getDocument();
		if ($product instanceof Product)
		{
			// Section product list synchronization.
			if ($product->getPublicationSectionsCount())
			{
				$product->synchronizeSectionDocumentLists();
			}

			$cs = $event->getServices('commerceServices');
			if ($cs instanceof \Rbs\Commerce\CommerceServices)
			{
				$normalizedAttributeValues = $product->getCurrentLocalization()->getAttributeValues();
				$cs->getAttributeManager()->setAttributeValues($product, $normalizedAttributeValues);
			}
		}
	}

	/**
	 * @param \Rbs\Commerce\CommerceServices $commerceServices
	 * @return string
	 */
	protected function buildSkuCodeFromLabel(\Rbs\Commerce\CommerceServices $commerceServices = null)
	{
		$retry = 0;
		$baseCode = String::subString(preg_replace('/[^a-zA-Z0-9]+/', '-',
			String::stripAccents(String::toUpper($this->getLabel()))), 0, 80);
		$skuCode = $baseCode;
		if ($commerceServices)
		{
			$sku = $commerceServices->getStockManager()->getSkuByCode($skuCode);
			while ($sku && $retry++ < 100)
			{
				$skuCode = String::subString($baseCode, 0, 73) . '-' . String::toUpper(String::random(6, false));
				$sku = $commerceServices->getStockManager()->getSkuByCode($skuCode);
			}
		}
		return $skuCode;
	}

	/**
	 * @param Event $event
	 */
	public function onDefaultUpdate(Event $event)
	{
		/** @var $product Product */
		$product = $event->getDocument();
		$modifiedPropertyNames = $product->getModifiedPropertyNames();
		if (in_array('attributeValues', $modifiedPropertyNames) || in_array('attribute', $modifiedPropertyNames))
		{
			$cs = $event->getServices('commerceServices');
			if ($cs instanceof \Rbs\Commerce\CommerceServices)
			{
				$attributeValues = $product->getCurrentLocalization()->getAttributeValues();
				$normalizedAttributeValues = $cs->getAttributeManager()->normalizeRestAttributeValues($attributeValues, $product->getAttribute());
				$product->getCurrentLocalization()->setAttributeValues($normalizedAttributeValues);
				$cs->getAttributeManager()->setAttributeValues($product, $normalizedAttributeValues);
			}
		}

		// Section product list synchronization.
		if (in_array('publicationSections', $modifiedPropertyNames))
		{
			$product->synchronizeSectionDocumentLists();
		}

		if (in_array('variantGroup', $modifiedPropertyNames))
		{
			$variantGroup = $product->getVariantGroup();
			if ($variantGroup && $variantGroup->getRootProduct() === $this)
			{
				$product->setSku(null);
			}
		}
	}

	protected function synchronizeSectionDocumentLists()
	{
		$dm = $this->getDocumentManager();

		if ($this->getPublicationSectionsCount())
		{
			$dqb1 = $dm->getNewQuery('Rbs_Catalog_SectionProductList');
			$pb1 = $dqb1->getPredicateBuilder();
			$dqb1->andPredicates($pb1->in('synchronizedSection', $this->getPublicationSectionsIds()));
			$requiredListIds = $dqb1->getDocuments()->ids();
		}
		else
		{
			$requiredListIds = array();
		}

		$dqb2 = $dm->getNewQuery('Rbs_Catalog_SectionProductList');
		$d2qb2 = $dqb2->getModelBuilder('Rbs_Catalog_ProductListItem', 'productList');
		$pb2 = $d2qb2->getPredicateBuilder();
		$d2qb2->andPredicates($pb2->eq('product', $this));
		$existingListIds = $dqb2->getDocuments()->ids();

		// Item creation.
		$listIds = array_diff($requiredListIds, $existingListIds);
		foreach ($listIds as $listId)
		{
			/* @var $list \Rbs\Catalog\Documents\SectionProductList */
			$list = $dm->getDocumentInstance($listId);

			/* @var $item \Rbs\Catalog\Documents\ProductListItem */
			$item = $dm->getNewDocumentInstanceByModelName('Rbs_Catalog_ProductListItem');
			$item->setProductList($list);
			$item->setProduct($this);
			$item->save();
		}

		// Item deletion.
		$listIds = array_diff($existingListIds, $requiredListIds);
		if (count($listIds))
		{
			$dqb3 = $dm->getNewQuery('Rbs_Catalog_ProductListItem');
			$pb3 = $dqb3->getPredicateBuilder();
			$dqb3->andPredicates($pb3->in('productList', $listIds), $pb3->eq('product', $this));
			foreach ($dqb3->getDocuments() as $item)
			{
				$item->delete();
			}
		}
	}

	/**
	 * @var boolean
	 */
	protected $newSkuOnCreation = true;

	/**
	 * @return boolean
	 */
	public function getNewSkuOnCreation()
	{
		return $this->newSkuOnCreation;
	}

	/**
	 * @param boolean $newSkuOnCreation
	 * @return $this
	 */
	public function setNewSkuOnCreation($newSkuOnCreation)
	{
		$this->newSkuOnCreation = $newSkuOnCreation;
		return $this;
	}

	/**
	 * @param string $crossSellingType
	 * @return \Rbs\Catalog\Documents\CrossSellingProductList|null
	 */
	public function getCrossSellingListByType($crossSellingType)
	{
		$query = $this->getDocumentManager()->getNewQuery('Rbs_Catalog_CrossSellingProductList');
		$query->andPredicates($query->eq('product', $this->getId()), $query->eq('crossSellingType', $crossSellingType));
		return $query->getFirstDocument();
	}

	/**
	 * @return Boolean
	 */
	public function hasVariants()
	{
		return !($this->getVariant()) && $this->getVariantGroup();
	}

	/**
	 * @return null|Product
	 */
	protected function getRelatedRootProduct()
	{
		if ($this->getVariant() && $this->getVariantGroupId())
		{
			$variantGroup = $this->getVariantGroup();
			if ($variantGroup)
			{
				return $variantGroup->getRootProduct();
			}
		}
		return null;
	}

	/**
	 * @return \Change\Documents\DocumentArrayProperty|\Rbs\Website\Documents\Section[]
	 */
	public function getPublicationSections()
	{
		$publicationSections = parent::getPublicationSections();
		if ($publicationSections instanceof \Change\Documents\DocumentArrayProperty && !$publicationSections->count()
			&& !$publicationSections->isModified())
		{
			$rootProduct = $this->getRelatedRootProduct();
			if ($rootProduct instanceof Product)
			{
				$publicationSections->setDefaultIds($rootProduct->getPublicationSections()->getIds());
			}
		}
		return $publicationSections;
	}

	/**
	 * @param Event $event
	 */
	public function onDefaultUpdated(Event $event)
	{
		if ($this !== $event->getDocument())
		{
			return;
		}

		$modifiedPropertyNames = $event->getParam('modifiedPropertyNames');
		if (is_array($modifiedPropertyNames) && in_array('publicationSections', $modifiedPropertyNames))
		{
			if ($this->getVariantGroupId() && !$this->getVariant())
			{
				$query = $this->getDocumentManager()->getNewQuery($this->getDocumentModel());
				$query->andPredicates($query->eq('variantGroup', $this->getVariantGroupId()),
					$query->neq('id', $this->getId()));

{'fraction_non_alphanumeric': 0.1668971876440756, 'fraction_numerical': 0.00636237897648686, 'mean_word_length': 4.883102793599132, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 291, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5388830', 'n_tokens_mistral': 7717, 'n_tokens_neox': 6790, 'n_words': 1066}