From e2369a8e09aadacfdb9ad5fa332fb90aa813fef1 Mon Sep 17 00:00:00 2001 From: AlexandrDmitriev Date: Thu, 16 Apr 2015 00:24:33 +0300 Subject: [PATCH 01/18] BAP-7898: ManyToMany Extend field updates incorrectly --- .../Tests/Unit/Tools/Fixtures/relations.txt | 30 ++++++- .../ExtendEntityGeneratorExtensionTest.php | 40 ++++++--- .../ExtendEntityGeneratorExtension.php | 90 ++++++++++++++++--- 3 files changed, 135 insertions(+), 25 deletions(-) diff --git a/src/Oro/Bundle/EntityExtendBundle/Tests/Unit/Tools/Fixtures/relations.txt b/src/Oro/Bundle/EntityExtendBundle/Tests/Unit/Tools/Fixtures/relations.txt index c98cc1ef9a3..c67af3affec 100644 --- a/src/Oro/Bundle/EntityExtendBundle/Tests/Unit/Tools/Fixtures/relations.txt +++ b/src/Oro/Bundle/EntityExtendBundle/Tests/Unit/Tools/Fixtures/relations.txt @@ -12,7 +12,26 @@ class Entity implements \Oro\Bundle\EntityExtendBundle\Entity\ExtendEntityInterf public function setRel1($value) { - $this->rel1 = $value; return $this; + if ((!$value instanceof \Traversable && !is_array($value) && !$value instanceof \ArrayAccess) || + !$this->rel1 instanceof \Doctrine\Common\Collections\Collection) { + $this->rel1 = $value; + return $this; + } + foreach ($this->rel1 as $item) { + $this->removeRel1($item); + } + foreach ($value as $item) { + $this->addRel1($item); + } + return $this; + } + + public function removeRel1($value) + { + if ($this->rel1 && $this->rel1->contains($value)) { + $this->rel1->removeElement($value); + $value->setTarget1(null); + } } public function getRel2() @@ -25,7 +44,16 @@ class Entity implements \Oro\Bundle\EntityExtendBundle\Entity\ExtendEntityInterf return $this->rel1; } + public function addRel1($value) + { + if (!$this->rel1->contains($value)) { + $this->rel1->add($value); + $value->setTarget1($this); + } + } + public function __construct() { + $this->rel1 = new \Doctrine\Common\Collections\ArrayCollection(); } } diff --git a/src/Oro/Bundle/EntityExtendBundle/Tests/Unit/Tools/GeneratorExtensions/ExtendEntityGeneratorExtensionTest.php b/src/Oro/Bundle/EntityExtendBundle/Tests/Unit/Tools/GeneratorExtensions/ExtendEntityGeneratorExtensionTest.php index 7174d726ba0..ba454708dbe 100644 --- a/src/Oro/Bundle/EntityExtendBundle/Tests/Unit/Tools/GeneratorExtensions/ExtendEntityGeneratorExtensionTest.php +++ b/src/Oro/Bundle/EntityExtendBundle/Tests/Unit/Tools/GeneratorExtensions/ExtendEntityGeneratorExtensionTest.php @@ -112,18 +112,6 @@ public function testProperties() $this->assertGeneration('properties.txt', $schema); } - public function testRelations() - { - $schema = [ - 'type' => 'Extend', - 'property' => [], - 'relation' => ['rel1' => 'rel1', 'rel_2' => 'rel_2'], - 'default' => [], - 'addremove' => [] - ]; - $this->assertGeneration('relations.txt', $schema); - } - public function testDefaults() { $schema = [ @@ -159,6 +147,24 @@ public function testCollections() $this->assertGeneration('collections.txt', $schema); } + public function testRelations() + { + $schema = [ + 'type' => 'Extend', + 'property' => [], + 'relation' => ['rel1' => 'rel1', 'rel_2' => 'rel_2'], + 'default' => [], + 'addremove' => [ + 'rel1' => [ + 'self' => 'rel1', + 'is_target_addremove' => false, + 'target' => 'target1', + ], + ] + ]; + $this->assertGeneration('relations.txt', $schema); + } + public function testCollectionsWithoutTarget() { $schema = [ @@ -195,6 +201,14 @@ protected function assertGeneration($expectedFile, $schema, $dump = false) } $expectedBody = file_get_contents(__DIR__ . '/../Fixtures/' . $expectedFile); - $this->assertEquals(trim($expectedBody), $classBody); + /** + * Support different line endings. + */ + $expectedBody = str_replace(PHP_EOL, "\n", $expectedBody); + $classBody = str_replace(PHP_EOL, "\n", $classBody); + $expectedBody = trim($expectedBody); + $classBody = trim($classBody); + + $this->assertEquals($expectedBody, $classBody); } } diff --git a/src/Oro/Bundle/EntityExtendBundle/Tools/GeneratorExtensions/ExtendEntityGeneratorExtension.php b/src/Oro/Bundle/EntityExtendBundle/Tools/GeneratorExtensions/ExtendEntityGeneratorExtension.php index 4b76e9f79d4..3c664263e91 100644 --- a/src/Oro/Bundle/EntityExtendBundle/Tools/GeneratorExtensions/ExtendEntityGeneratorExtension.php +++ b/src/Oro/Bundle/EntityExtendBundle/Tools/GeneratorExtensions/ExtendEntityGeneratorExtension.php @@ -90,7 +90,7 @@ protected function generateToStringMethod(array $schema, PhpClass $class) $toString = []; foreach ($schema['property'] as $fieldName => $config) { if ($schema['doctrine'][$schema['entity']]['fields'][$fieldName]['type'] == 'string') { - $toString[] = '$this->get' . ucfirst(Inflector::camelize($fieldName)) . '()'; + $toString[] = '$this->' . $this->generateGetMethodName($fieldName) . '()'; } } @@ -113,20 +113,50 @@ protected function generateProperties($propertyType, array $schema, PhpClass $cl ->setProperty(PhpProperty::create($fieldName)->setVisibility('protected')) ->setMethod( $this->generateClassMethod( - 'get' . ucfirst(Inflector::camelize($fieldName)), + $this->generateGetMethodName($fieldName), 'return $this->' . $fieldName . ';' ) ) ->setMethod( $this->generateClassMethod( - 'set' . ucfirst(Inflector::camelize($fieldName)), - '$this->' . $fieldName . ' = $value; return $this;', + $this->generateSetMethodName($fieldName), + $this->getSetterBody($fieldName, $schema), ['value'] ) ); } } + /** + * @param string $fieldName + * @param array $schema + * @return string + */ + protected function getSetterBody($fieldName, array $schema) + { + if (!isset($schema['addremove'][$fieldName])) { + return '$this->' . $fieldName . ' = $value; return $this;'; + } else { + $addMethodName = $this->generateAddMethodName($fieldName); + $removeMethodName = $this->generateRemoveMethodName($fieldName); + $body = <<$fieldName instanceof \Doctrine\Common\Collections\Collection) { + \$this->$fieldName = \$value; + return \$this; +} +foreach (\$this->$fieldName as \$item) { + \$this->$removeMethodName(\$item); +} +foreach (\$value as \$item) { + \$this->$addMethodName(\$item); +} +return \$this; +METHOD_BODY; + return $body; + } + } + /** * @param array $schema * @param PhpClass $class @@ -143,11 +173,13 @@ protected function generateCollectionMethods(array $schema, PhpClass $class) ' $this->' . $fieldName . '->removeElement($value);', ]; if (isset($config['target'])) { - $addMethodBody[] = ' $value->' . ($config['is_target_addremove'] ? 'add' : 'set') - . ucfirst(Inflector::camelize($config['target'])) . '($this);'; - $removeMethodBody[] = ' $value->' . ($config['is_target_addremove'] ? 'remove' : 'set') - . ucfirst(Inflector::camelize($config['target'])) - . '(' . ($config['is_target_addremove'] ? '$this' : 'null') . ');'; + if ($config['is_target_addremove']) { + $addMethodBody[] = " \$value->{$this->generateAddMethodName($config['target'])}(\$this);"; + $removeMethodBody[] = " \$value->{$this->generateRemoveMethodName($config['target'])}(\$this);"; + } else { + $addMethodBody[] = " \$value->{$this->generateSetMethodName($config['target'])}(\$this);"; + $removeMethodBody[] = " \$value->{$this->generateSetMethodName($config['target'])}(null);"; + } } $addMethodBody[] = '}'; $removeMethodBody[] = '}'; @@ -155,18 +187,54 @@ protected function generateCollectionMethods(array $schema, PhpClass $class) $class ->setMethod( $this->generateClassMethod( - 'add' . ucfirst(Inflector::camelize($config['self'])), + $this->generateAddMethodName($config['self']), implode("\n", $addMethodBody), ['value'] ) ) ->setMethod( $this->generateClassMethod( - 'remove' . ucfirst(Inflector::camelize($config['self'])), + $this->generateRemoveMethodName($config['self']), implode("\n", $removeMethodBody), ['value'] ) ); } } + + /** + * @param string $fieldName + * @return string + */ + protected function generateGetMethodName($fieldName) + { + return 'get' . ucfirst(Inflector::camelize($fieldName)); + } + + /** + * @param string $fieldName + * @return string + */ + protected function generateSetMethodName($fieldName) + { + return 'set' . ucfirst(Inflector::camelize($fieldName)); + } + + /** + * @param string $fieldName + * @return string + */ + protected function generateAddMethodName($fieldName) + { + return 'add' . ucfirst(Inflector::camelize($fieldName)); + } + + /** + * @param string $fieldName + * @return string + */ + protected function generateRemoveMethodName($fieldName) + { + return 'remove' . ucfirst(Inflector::camelize($fieldName)); + } } From f7f5cbe21675995ee6ed9ba14d06014ef94a14e4 Mon Sep 17 00:00:00 2001 From: krlove Date: Mon, 20 Apr 2015 20:41:15 +0300 Subject: [PATCH 02/18] CRM-3109: Impossible to build report by Call duration - Added Time field type --- .../Datagrid/DefaultColumnOptionsGuesser.php | 3 +++ .../Formatter/Property/AbstractProperty.php | 6 +++++ .../Formatter/Property/PropertyInterface.php | 1 + .../Extension/Totals/OrmTotalsExtension.php | 3 +++ .../ImportExport/DatagridDataConverter.php | 3 +++ .../Resources/config/requirejs.yml | 1 + .../public/js/datagrid/cell/time-cell.js | 22 +++++++++++++++++++ .../DefaultColumnOptionsGuesserTest.php | 1 + 8 files changed, 40 insertions(+) create mode 100644 src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/cell/time-cell.js diff --git a/src/Oro/Bundle/DataGridBundle/Datagrid/DefaultColumnOptionsGuesser.php b/src/Oro/Bundle/DataGridBundle/Datagrid/DefaultColumnOptionsGuesser.php index bf792202286..92c272b4601 100644 --- a/src/Oro/Bundle/DataGridBundle/Datagrid/DefaultColumnOptionsGuesser.php +++ b/src/Oro/Bundle/DataGridBundle/Datagrid/DefaultColumnOptionsGuesser.php @@ -31,6 +31,9 @@ public function guessFormatter($class, $property, $type) case 'datetime': $frontendType = Property::TYPE_DATETIME; break; + case 'time': + $frontendType = Property::TYPE_TIME; + break; case 'money': $frontendType = Property::TYPE_CURRENCY; break; diff --git a/src/Oro/Bundle/DataGridBundle/Extension/Formatter/Property/AbstractProperty.php b/src/Oro/Bundle/DataGridBundle/Extension/Formatter/Property/AbstractProperty.php index 9857ed5c569..ceb3a40a8ce 100644 --- a/src/Oro/Bundle/DataGridBundle/Extension/Formatter/Property/AbstractProperty.php +++ b/src/Oro/Bundle/DataGridBundle/Extension/Formatter/Property/AbstractProperty.php @@ -75,6 +75,12 @@ protected function convertValue($value) } $result = (string)$value; break; + case self::TYPE_TIME: + if ($value instanceof \DateTime) { + $value = $value->format('H:i:s'); + } + $result = (string)$value; + break; case self::TYPE_STRING: $result = (string)$value; break; diff --git a/src/Oro/Bundle/DataGridBundle/Extension/Formatter/Property/PropertyInterface.php b/src/Oro/Bundle/DataGridBundle/Extension/Formatter/Property/PropertyInterface.php index 9e78b4bcba1..5eea0dc6266 100644 --- a/src/Oro/Bundle/DataGridBundle/Extension/Formatter/Property/PropertyInterface.php +++ b/src/Oro/Bundle/DataGridBundle/Extension/Formatter/Property/PropertyInterface.php @@ -14,6 +14,7 @@ interface PropertyInterface { const TYPE_DATE = 'date'; const TYPE_DATETIME = 'datetime'; + const TYPE_TIME = 'time'; const TYPE_DECIMAL = 'decimal'; const TYPE_INTEGER = 'integer'; const TYPE_PERCENT = 'percent'; diff --git a/src/Oro/Bundle/DataGridBundle/Extension/Totals/OrmTotalsExtension.php b/src/Oro/Bundle/DataGridBundle/Extension/Totals/OrmTotalsExtension.php index dfdf1b9bae1..7056b8e0c2b 100644 --- a/src/Oro/Bundle/DataGridBundle/Extension/Totals/OrmTotalsExtension.php +++ b/src/Oro/Bundle/DataGridBundle/Extension/Totals/OrmTotalsExtension.php @@ -365,6 +365,9 @@ protected function applyFrontendFormatting($val = null, $formatter = null) case PropertyInterface::TYPE_DATETIME: $val = $this->dateTimeFormatter->format($val); break; + case PropertyInterface::TYPE_TIME: + $val = $this->dateTimeFormatter->formatTime($val); + break; case PropertyInterface::TYPE_DECIMAL: $val = $this->numberFormatter->formatDecimal($val); break; diff --git a/src/Oro/Bundle/DataGridBundle/ImportExport/DatagridDataConverter.php b/src/Oro/Bundle/DataGridBundle/ImportExport/DatagridDataConverter.php index fba4905306e..80e631aaa4a 100644 --- a/src/Oro/Bundle/DataGridBundle/ImportExport/DatagridDataConverter.php +++ b/src/Oro/Bundle/DataGridBundle/ImportExport/DatagridDataConverter.php @@ -118,6 +118,9 @@ protected function applyFrontendFormatting($val, $options) case PropertyInterface::TYPE_DATETIME: $val = $this->dateTimeFormatter->format($val); break; + case PropertyInterface::TYPE_TIME: + $val = $this->dateTimeFormatter->formatTime($val); + break; case PropertyInterface::TYPE_DECIMAL: $val = $this->numberFormatter->formatDecimal($val); break; diff --git a/src/Oro/Bundle/DataGridBundle/Resources/config/requirejs.yml b/src/Oro/Bundle/DataGridBundle/Resources/config/requirejs.yml index d844ca1f81a..085bc4db3be 100644 --- a/src/Oro/Bundle/DataGridBundle/Resources/config/requirejs.yml +++ b/src/Oro/Bundle/DataGridBundle/Resources/config/requirejs.yml @@ -41,6 +41,7 @@ config: 'oro/datagrid/cell/html-cell': 'bundles/orodatagrid/js/datagrid/cell/html-cell.js' 'oro/datagrid/cell/date-cell': 'bundles/orodatagrid/js/datagrid/cell/date-cell.js' 'oro/datagrid/cell/datetime-cell': 'bundles/orodatagrid/js/datagrid/cell/datetime-cell.js' + 'oro/datagrid/cell/time-cell': 'bundles/orodatagrid/js/datagrid/cell/time-cell.js' 'oro/datagrid/cell/number-cell': 'bundles/orodatagrid/js/datagrid/cell/number-cell.js' 'oro/datagrid/cell/select-cell': 'bundles/orodatagrid/js/datagrid/cell/select-cell.js' 'oro/datagrid/cell/select-row-cell': 'bundles/orodatagrid/js/datagrid/cell/select-row-cell.js' diff --git a/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/cell/time-cell.js b/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/cell/time-cell.js new file mode 100644 index 00000000000..26f1cb5b9ab --- /dev/null +++ b/src/Oro/Bundle/DataGridBundle/Resources/public/js/datagrid/cell/time-cell.js @@ -0,0 +1,22 @@ +/*global define*/ +define([ + './string-cell' +], function (StringCell) { + 'use strict'; + + var TimeCell; + + /** + * Time column cell + * + * @export oro/datagrid/cell/time-cell + * @class oro.datagrid.cell.TimeCell + * @extends oro.datagrid.cell.StringCell + */ + TimeCell = StringCell.extend({ + type: 'time', + className: 'time-cell' + }); + + return TimeCell; +}); diff --git a/src/Oro/Bundle/DataGridBundle/Tests/Unit/Datagrid/DefaultColumnOptionsGuesserTest.php b/src/Oro/Bundle/DataGridBundle/Tests/Unit/Datagrid/DefaultColumnOptionsGuesserTest.php index f2dba4b1a67..f16ba3ff227 100644 --- a/src/Oro/Bundle/DataGridBundle/Tests/Unit/Datagrid/DefaultColumnOptionsGuesserTest.php +++ b/src/Oro/Bundle/DataGridBundle/Tests/Unit/Datagrid/DefaultColumnOptionsGuesserTest.php @@ -37,6 +37,7 @@ public function guessFormatterProvider() ['boolean', ['frontend_type' => Property::TYPE_BOOLEAN]], ['date', ['frontend_type' => Property::TYPE_DATE]], ['datetime', ['frontend_type' => Property::TYPE_DATETIME]], + ['time', ['frontend_type' => Property::TYPE_TIME]], ['money', ['frontend_type' => Property::TYPE_CURRENCY]], ['percent', ['frontend_type' => Property::TYPE_PERCENT]], ['string', ['frontend_type' => Property::TYPE_STRING]], From c82f2981dc7dc118743e88f88b5d18bb81558682 Mon Sep 17 00:00:00 2001 From: OroCRM Deployer Date: Wed, 22 Apr 2015 19:37:14 +0300 Subject: [PATCH 03/18] BAP-8084 Add possibility to replace process definitions --- .../ProcessConfigurationProvider.php | 6 --- .../ProcessConfigurationProviderTest.php | 14 ++--- .../Resources/config/process.php | 54 +++++++++++++++++++ 3 files changed, 61 insertions(+), 13 deletions(-) create mode 100644 src/Oro/Bundle/WorkflowBundle/Tests/Unit/Configuration/Stub/DuplicateConfiguration/Resources/config/process.php diff --git a/src/Oro/Bundle/WorkflowBundle/Configuration/ProcessConfigurationProvider.php b/src/Oro/Bundle/WorkflowBundle/Configuration/ProcessConfigurationProvider.php index 96ba0b14d5f..fd588382e58 100644 --- a/src/Oro/Bundle/WorkflowBundle/Configuration/ProcessConfigurationProvider.php +++ b/src/Oro/Bundle/WorkflowBundle/Configuration/ProcessConfigurationProvider.php @@ -68,12 +68,6 @@ public function getProcessConfiguration( continue; } - if (isset($definitions[$definitionName])) { - throw new InvalidConfigurationException( - sprintf('Duplicated process definition name "%s" in %s', $definitionName, $realPathName) - ); - } - $definitions[$definitionName] = $definitionConfiguration; } diff --git a/src/Oro/Bundle/WorkflowBundle/Tests/Unit/Configuration/ProcessConfigurationProviderTest.php b/src/Oro/Bundle/WorkflowBundle/Tests/Unit/Configuration/ProcessConfigurationProviderTest.php index cb3fd5c64d4..ad13be00e64 100644 --- a/src/Oro/Bundle/WorkflowBundle/Tests/Unit/Configuration/ProcessConfigurationProviderTest.php +++ b/src/Oro/Bundle/WorkflowBundle/Tests/Unit/Configuration/ProcessConfigurationProviderTest.php @@ -34,8 +34,7 @@ protected function setUp() protected function tearDown() { - unset($this->definitionConfiguration); - unset($this->triggerConfiguration); + unset($this->definitionConfiguration, $this->triggerConfiguration); } /** @@ -52,18 +51,19 @@ public function testGetWorkflowDefinitionsIncorrectConfiguration() $configurationProvider->getProcessConfiguration(); } - /** - * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException - */ public function testGetWorkflowDefinitionsDuplicateConfiguration() { - $bundles = array(new CorrectConfigurationBundle(), new DuplicateConfigurationBundle()); + $bundles = [new CorrectConfigurationBundle(), new DuplicateConfigurationBundle()]; $configurationProvider = new ProcessConfigurationProvider( $bundles, $this->definitionConfiguration, $this->triggerConfiguration ); - $configurationProvider->getProcessConfiguration(); + + static::assertEquals( + $this->getExpectedProcessConfiguration('DuplicateConfiguration'), + $configurationProvider->getProcessConfiguration() + ); } public function testGetWorkflowDefinitions() diff --git a/src/Oro/Bundle/WorkflowBundle/Tests/Unit/Configuration/Stub/DuplicateConfiguration/Resources/config/process.php b/src/Oro/Bundle/WorkflowBundle/Tests/Unit/Configuration/Stub/DuplicateConfiguration/Resources/config/process.php new file mode 100644 index 00000000000..2340dc2bdc5 --- /dev/null +++ b/src/Oro/Bundle/WorkflowBundle/Tests/Unit/Configuration/Stub/DuplicateConfiguration/Resources/config/process.php @@ -0,0 +1,54 @@ + array( + 'test_definition' => array( + 'label' => 'Test Definition', + 'enabled' => true, + 'entity' => 'Oro\Bundle\UserBundle\Entity\User', + 'order' => 20, + 'exclude_definitions' => [], + 'actions_configuration' => array( + array('@assign_value' => array('$entity.field', 'value')) + ) + ), + 'another_definition' => array( + 'label' => 'Another definition', + 'entity' => 'My\Entity', + 'actions_configuration' => array(), + 'enabled' => true, + 'order' => 0, + 'exclude_definitions' => array() + ) + ), + ProcessConfigurationProvider::NODE_TRIGGERS => array( + 'test_definition' => array( + array( + 'event' => ProcessTrigger::EVENT_UPDATE, + 'field' => 'some_field', + 'priority' => 10, + 'queued' => true, + 'time_shift' => 123456 + ), + array( + 'event' => ProcessTrigger::EVENT_CREATE, + 'queued' => true, + 'time_shift' => 86700, + 'field' => null, + 'priority' => Job::PRIORITY_DEFAULT + ), + array( + 'event' => ProcessTrigger::EVENT_DELETE, + 'field' => null, + 'priority' => Job::PRIORITY_DEFAULT, + 'queued' => null, + 'time_shift' => null + ) + ) + ) +); From e0b27678219e42a66fa226cd8763b3a4ac712a42 Mon Sep 17 00:00:00 2001 From: Makar Sichevoy Date: Fri, 24 Apr 2015 12:37:06 +0300 Subject: [PATCH 04/18] CRM-3221: IMAP synchronize only incoming emails --- .../Bundle/ImapBundle/Mail/Storage/Folder.php | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/Oro/Bundle/ImapBundle/Mail/Storage/Folder.php b/src/Oro/Bundle/ImapBundle/Mail/Storage/Folder.php index 5950b120f8f..aea6431551f 100644 --- a/src/Oro/Bundle/ImapBundle/Mail/Storage/Folder.php +++ b/src/Oro/Bundle/ImapBundle/Mail/Storage/Folder.php @@ -24,6 +24,11 @@ class Folder extends BaseFolder self::FLAG_SPAM => FolderType::SPAM, ]; + /** @var array */ + protected static $possibleSentFolderNameMap = [ + 'SentBox', 'Sent' + ]; + /** @var string[] */ public $flags = null; @@ -122,15 +127,31 @@ public function deleteFlag($flag) */ public function guessFolderType() { - $this->type = 'other'; - + $this->type = FolderType::OTHER; foreach ($this->flagTypeMap as $flag => $type) { if ($this->hasFlag($flag)) { $this->type = $type; break; } } + // if sent box do not include flag for correct type guess + if ($this->type === FolderType::OTHER && $this->guessSentTypeByName()) { + $this->type = FolderType::SENT; + } return $this->type; } + + /** + * Try to guess sent folder by folder name + * + * @return bool + */ + public function guessSentTypeByName() + { + if (in_array($this->getGlobalName(), self::$possibleSentFolderNameMap, true)) { + return true; + } + return false; + } } From 0d68863e09dd916b4dca5e58f5cbb72b09238dd7 Mon Sep 17 00:00:00 2001 From: Valentine Velikiy Date: Fri, 24 Apr 2015 14:49:14 +0300 Subject: [PATCH 05/18] CRM-3221: IMAP synchronize only incoming emails. - Removed static attribute. --- src/Oro/Bundle/ImapBundle/Mail/Storage/Folder.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Oro/Bundle/ImapBundle/Mail/Storage/Folder.php b/src/Oro/Bundle/ImapBundle/Mail/Storage/Folder.php index aea6431551f..bf6c02e8b3f 100644 --- a/src/Oro/Bundle/ImapBundle/Mail/Storage/Folder.php +++ b/src/Oro/Bundle/ImapBundle/Mail/Storage/Folder.php @@ -25,7 +25,7 @@ class Folder extends BaseFolder ]; /** @var array */ - protected static $possibleSentFolderNameMap = [ + protected $possibleSentFolderNameMap = [ 'SentBox', 'Sent' ]; @@ -149,7 +149,7 @@ public function guessFolderType() */ public function guessSentTypeByName() { - if (in_array($this->getGlobalName(), self::$possibleSentFolderNameMap, true)) { + if (in_array($this->getGlobalName(), $this->possibleSentFolderNameMap, true)) { return true; } return false; From a0463de179a93a98b988a4cbb6c46a7aee849c0c Mon Sep 17 00:00:00 2001 From: Valentine Velikiy Date: Fri, 17 Apr 2015 19:29:46 +0300 Subject: [PATCH 06/18] CRM-2918: Field with one-to-many relation doesn't save its value --- .../Form/Type/FieldType.php | 2 +- .../MultipleEntitySubscriber.php | 36 ++++++++++++++++++- .../MultipleEntitySubscriberTest.php | 3 ++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/Oro/Bundle/EntityExtendBundle/Form/Type/FieldType.php b/src/Oro/Bundle/EntityExtendBundle/Form/Type/FieldType.php index b070d54ae71..d1f4d4d258b 100644 --- a/src/Oro/Bundle/EntityExtendBundle/Form/Type/FieldType.php +++ b/src/Oro/Bundle/EntityExtendBundle/Form/Type/FieldType.php @@ -275,7 +275,7 @@ protected function isAvailableRelation( if ($fieldId && $extendProvider->hasConfigById($fieldId) - && !$extendProvider->getConfigById($fieldId)->is('state', ExtendScope::STATE_DELETE) + && $extendProvider->getConfigById($fieldId)->is('state', ExtendScope::STATE_DELETE) ) { return false; } diff --git a/src/Oro/Bundle/FormBundle/Form/EventListener/MultipleEntitySubscriber.php b/src/Oro/Bundle/FormBundle/Form/EventListener/MultipleEntitySubscriber.php index 4ac551948ba..c10e4178e02 100644 --- a/src/Oro/Bundle/FormBundle/Form/EventListener/MultipleEntitySubscriber.php +++ b/src/Oro/Bundle/FormBundle/Form/EventListener/MultipleEntitySubscriber.php @@ -2,9 +2,10 @@ namespace Oro\Bundle\FormBundle\Form\EventListener; -use Doctrine\ORM\PersistentCollection; use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\ORM\PersistentCollection; +use Doctrine\ORM\Mapping\ClassMetadata; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; @@ -54,14 +55,47 @@ public function postSubmit(FormEvent $event) $added = $form->get('added')->getData(); $removed = $form->get('removed')->getData(); + $parentData = $form->getParent()->getData(); + /** @var Collection $collection */ $collection = $form->getData(); + + $mapping = []; + if ($collection instanceof PersistentCollection) { + $mapping = $collection->getMapping(); + } + foreach ($added as $relation) { + if ($mapping && $mapping['type'] === ClassMetadata::ONE_TO_MANY) { + $mappedBy = $mapping['mappedBy']; + $setter = $this->getSetterName($mappedBy); + $relation->$setter($parentData); + } $collection->add($relation); } foreach ($removed as $relation) { + if ($mapping && $mapping['type'] === ClassMetadata::ONE_TO_MANY) { + $mappedBy = $mapping['mappedBy']; + $setter = $this->getSetterName($mappedBy); + $relation->$setter(null); + } $collection->removeElement($relation); } } + + /** + * @param string $mappedBy + * @return string + */ + protected function getSetterName($mappedBy) + { + $parts = explode('_', $mappedBy); + $setter = 'set'; + foreach ($parts as $part) { + $setter .= ucfirst($part); + } + + return $setter; + } } diff --git a/src/Oro/Bundle/FormBundle/Tests/Unit/Form/EventListener/MultipleEntitySubscriberTest.php b/src/Oro/Bundle/FormBundle/Tests/Unit/Form/EventListener/MultipleEntitySubscriberTest.php index 3ddf6abd31c..c351ab318cc 100644 --- a/src/Oro/Bundle/FormBundle/Tests/Unit/Form/EventListener/MultipleEntitySubscriberTest.php +++ b/src/Oro/Bundle/FormBundle/Tests/Unit/Form/EventListener/MultipleEntitySubscriberTest.php @@ -104,6 +104,7 @@ public function testPostSubmit() $subscriber = new MultipleEntitySubscriber(); $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); + $parentForm = $this->getMock('Symfony\Component\Form\Test\FormInterface'); $event = new FormEvent($form, null); $existing = (object)['$existing' => true]; @@ -120,6 +121,8 @@ public function testPostSubmit() $map = [['added', $formAdded], ['removed', $formRemoved]]; $form->expects($this->any())->method('get')->willReturnMap($map); $form->expects($this->any())->method('getData')->willReturn($collection); + $parentForm->expects($this->any())->method('getData')->willReturn([]); + $form->expects($this->any())->method('getParent')->willReturn($parentForm); $subscriber->postSubmit($event); From 0a7969219e26fe62f4f684346157ec3db5e21fd5 Mon Sep 17 00:00:00 2001 From: Valentine Velikiy Date: Fri, 24 Apr 2015 12:39:36 +0300 Subject: [PATCH 07/18] CRM-2918: Field with one-to-many relation doesn't save its value. - Fixed one-to-many relation setter based on parent entity metadata. --- .../MultipleEntitySubscriber.php | 57 ++++++++++++++----- .../Form/Type/MultipleEntityType.php | 10 +++- .../FormBundle/Resources/config/form_type.yml | 1 + .../MultipleEntitySubscriberTest.php | 21 ++++++- .../Unit/Form/Type/MultipleEntityTypeTest.php | 8 ++- 5 files changed, 76 insertions(+), 21 deletions(-) diff --git a/src/Oro/Bundle/FormBundle/Form/EventListener/MultipleEntitySubscriber.php b/src/Oro/Bundle/FormBundle/Form/EventListener/MultipleEntitySubscriber.php index c10e4178e02..d906c84d103 100644 --- a/src/Oro/Bundle/FormBundle/Form/EventListener/MultipleEntitySubscriber.php +++ b/src/Oro/Bundle/FormBundle/Form/EventListener/MultipleEntitySubscriber.php @@ -4,6 +4,7 @@ use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\Common\Util\ClassUtils; use Doctrine\ORM\PersistentCollection; use Doctrine\ORM\Mapping\ClassMetadata; @@ -11,8 +12,21 @@ use Symfony\Component\Form\FormEvents; use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Oro\Bundle\EntityBundle\ORM\DoctrineHelper; + class MultipleEntitySubscriber implements EventSubscriberInterface { + /** @var DoctrineHelper */ + protected $doctrineHelper; + + /** + * @param DoctrineHelper $doctrineHelper + */ + public function __construct(DoctrineHelper $doctrineHelper) + { + $this->doctrineHelper = $doctrineHelper; + } + /** * {@inheritdoc} */ @@ -57,33 +71,46 @@ public function postSubmit(FormEvent $event) $parentData = $form->getParent()->getData(); + /** @var ClassMetadata $parentMetadata */ + $parentMetadata = $this->doctrineHelper->getEntityMetadata(ClassUtils::getClass($parentData)); + /** @var Collection $collection */ $collection = $form->getData(); - $mapping = []; - if ($collection instanceof PersistentCollection) { - $mapping = $collection->getMapping(); - } - foreach ($added as $relation) { - if ($mapping && $mapping['type'] === ClassMetadata::ONE_TO_MANY) { - $mappedBy = $mapping['mappedBy']; - $setter = $this->getSetterName($mappedBy); - $relation->$setter($parentData); - } + $this->processRelation($parentMetadata, $relation, $parentData); $collection->add($relation); } foreach ($removed as $relation) { - if ($mapping && $mapping['type'] === ClassMetadata::ONE_TO_MANY) { - $mappedBy = $mapping['mappedBy']; - $setter = $this->getSetterName($mappedBy); - $relation->$setter(null); - } + $this->processRelation($parentMetadata, $relation, null); $collection->removeElement($relation); } } + /** + * @param ClassMetadata $metadata + * @param object $relation + * @param mixed $value + */ + protected function processRelation($metadata, $relation, $value) + { + foreach ($metadata->getAssociationMappings() as $mapping) { + if (!is_array($mapping) || !isset($mapping['targetEntity'], $mapping['type'], $mapping['mappedBy'])) { + continue; + } + if ($mapping['targetEntity'] !== ClassUtils::getClass($relation)) { + continue; + } + if ($mapping['type'] !== ClassMetadata::ONE_TO_MANY) { + continue; + } + $mappedBy = $mapping['mappedBy']; + $setter = $this->getSetterName($mappedBy); + $relation->$setter($value); + } + } + /** * @param string $mappedBy * @return string diff --git a/src/Oro/Bundle/FormBundle/Form/Type/MultipleEntityType.php b/src/Oro/Bundle/FormBundle/Form/Type/MultipleEntityType.php index 9f094865495..8b859594d1a 100644 --- a/src/Oro/Bundle/FormBundle/Form/Type/MultipleEntityType.php +++ b/src/Oro/Bundle/FormBundle/Form/Type/MultipleEntityType.php @@ -8,19 +8,25 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolverInterface; +use Oro\Bundle\EntityBundle\ORM\DoctrineHelper; use Oro\Bundle\SecurityBundle\SecurityFacade; use Oro\Bundle\FormBundle\Form\EventListener\MultipleEntitySubscriber; class MultipleEntityType extends AbstractType { + /** @var DoctrineHelper */ + protected $doctrineHelper; + /** @var SecurityFacade */ protected $securityFacade; /** + * @param DoctrineHelper $doctrineHelper * @param SecurityFacade $securityFacade */ - public function __construct(SecurityFacade $securityFacade) + public function __construct(DoctrineHelper $doctrineHelper, SecurityFacade $securityFacade) { + $this->doctrineHelper = $doctrineHelper; $this->securityFacade = $securityFacade; } @@ -48,7 +54,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) ] ); - $builder->addEventSubscriber(new MultipleEntitySubscriber()); + $builder->addEventSubscriber(new MultipleEntitySubscriber($this->doctrineHelper)); } /** diff --git a/src/Oro/Bundle/FormBundle/Resources/config/form_type.yml b/src/Oro/Bundle/FormBundle/Resources/config/form_type.yml index cce6448ede3..6cc0a3150af 100644 --- a/src/Oro/Bundle/FormBundle/Resources/config/form_type.yml +++ b/src/Oro/Bundle/FormBundle/Resources/config/form_type.yml @@ -87,6 +87,7 @@ services: oro_form.type.multiple_entity: class: %oro_form.type.multiple_entity.class% arguments: + - @oro_entity.doctrine_helper - @oro_security.security_facade tags: - { name: form.type, alias: oro_multiple_entity } diff --git a/src/Oro/Bundle/FormBundle/Tests/Unit/Form/EventListener/MultipleEntitySubscriberTest.php b/src/Oro/Bundle/FormBundle/Tests/Unit/Form/EventListener/MultipleEntitySubscriberTest.php index c351ab318cc..69d4a10c47f 100644 --- a/src/Oro/Bundle/FormBundle/Tests/Unit/Form/EventListener/MultipleEntitySubscriberTest.php +++ b/src/Oro/Bundle/FormBundle/Tests/Unit/Form/EventListener/MultipleEntitySubscriberTest.php @@ -32,7 +32,10 @@ public function testSubscribedEvents() */ public function testPostSetData($data, $expectedAddedData, $expectedRemovedData) { - $subscriber = new MultipleEntitySubscriber(); + $doctrineHelper = $this->getMockBuilder('Oro\Bundle\EntityBundle\ORM\DoctrineHelper') + ->disableOriginalConstructor() + ->getMock(); + $subscriber = new MultipleEntitySubscriber($doctrineHelper); $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); $event = new FormEvent($form, null); @@ -101,7 +104,19 @@ public function postSetDataProvider() public function testPostSubmit() { - $subscriber = new MultipleEntitySubscriber(); + $metadata = $this->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadata') + ->disableOriginalConstructor() + ->getMock(); + $metadata->expects($this->any()) + ->method('getAssociationMappings') + ->willReturn([]); + $doctrineHelper = $this->getMockBuilder('Oro\Bundle\EntityBundle\ORM\DoctrineHelper') + ->disableOriginalConstructor() + ->getMock(); + $doctrineHelper->expects($this->once()) + ->method('getEntityMetadata') + ->willReturn($metadata); + $subscriber = new MultipleEntitySubscriber($doctrineHelper); $form = $this->getMock('Symfony\Component\Form\Test\FormInterface'); $parentForm = $this->getMock('Symfony\Component\Form\Test\FormInterface'); @@ -121,7 +136,7 @@ public function testPostSubmit() $map = [['added', $formAdded], ['removed', $formRemoved]]; $form->expects($this->any())->method('get')->willReturnMap($map); $form->expects($this->any())->method('getData')->willReturn($collection); - $parentForm->expects($this->any())->method('getData')->willReturn([]); + $parentForm->expects($this->any())->method('getData')->willReturn(new \stdClass()); $form->expects($this->any())->method('getParent')->willReturn($parentForm); $subscriber->postSubmit($event); diff --git a/src/Oro/Bundle/FormBundle/Tests/Unit/Form/Type/MultipleEntityTypeTest.php b/src/Oro/Bundle/FormBundle/Tests/Unit/Form/Type/MultipleEntityTypeTest.php index 19820ecca45..2011599d85d 100644 --- a/src/Oro/Bundle/FormBundle/Tests/Unit/Form/Type/MultipleEntityTypeTest.php +++ b/src/Oro/Bundle/FormBundle/Tests/Unit/Form/Type/MultipleEntityTypeTest.php @@ -15,6 +15,9 @@ class MultipleEntityTypeTest extends FormIntegrationTestCase const PERMISSION_ALLOW = 'test_permission_allow'; const PERMISSION_DISALLOW = 'test_permission_disallow'; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $doctrineHelper; + /** @var \PHPUnit_Framework_MockObject_MockObject */ protected $securityFacade; @@ -26,6 +29,9 @@ class MultipleEntityTypeTest extends FormIntegrationTestCase protected function setUp() { + $this->doctrineHelper = $this->getMockBuilder('Oro\Bundle\EntityBundle\ORM\DoctrineHelper') + ->disableOriginalConstructor() + ->getMock(); $this->securityFacade = $this->getMockBuilder('Oro\Bundle\SecurityBundle\SecurityFacade') ->disableOriginalConstructor()->getMock(); @@ -57,7 +63,7 @@ protected function tearDown() protected function getExtensions() { $types = [ - 'oro_multiple_entity' => new MultipleEntityType($this->securityFacade), + 'oro_multiple_entity' => new MultipleEntityType($this->doctrineHelper, $this->securityFacade), 'oro_entity_identifier' => new EntityIdentifierType($this->registry) ]; From 7e5d421f4759f4fb6ac44dce9c05598df3b9194b Mon Sep 17 00:00:00 2001 From: Ignat Shcheglovskyi Date: Sat, 25 Apr 2015 10:40:19 +0300 Subject: [PATCH 08/18] BAP-8093: Process exclude definition doesn't apply to existing process - fix import of exclude definitions --- src/Oro/Bundle/WorkflowBundle/Entity/ProcessDefinition.php | 3 ++- .../WorkflowBundle/Tests/Unit/Entity/ProcessDefinitionTest.php | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Oro/Bundle/WorkflowBundle/Entity/ProcessDefinition.php b/src/Oro/Bundle/WorkflowBundle/Entity/ProcessDefinition.php index 380009a9ee9..c0d540e11df 100644 --- a/src/Oro/Bundle/WorkflowBundle/Entity/ProcessDefinition.php +++ b/src/Oro/Bundle/WorkflowBundle/Entity/ProcessDefinition.php @@ -315,7 +315,8 @@ public function import(ProcessDefinition $definition) ->setLabel($definition->getLabel()) ->setRelatedEntity($definition->getRelatedEntity()) ->setExecutionOrder($definition->getExecutionOrder()) - ->setActionsConfiguration($definition->getActionsConfiguration()); + ->setActionsConfiguration($definition->getActionsConfiguration()) + ->setExcludeDefinitions($definition->getExcludeDefinitions()); return $this; } diff --git a/src/Oro/Bundle/WorkflowBundle/Tests/Unit/Entity/ProcessDefinitionTest.php b/src/Oro/Bundle/WorkflowBundle/Tests/Unit/Entity/ProcessDefinitionTest.php index 3709a0980a6..344bd4a5289 100644 --- a/src/Oro/Bundle/WorkflowBundle/Tests/Unit/Entity/ProcessDefinitionTest.php +++ b/src/Oro/Bundle/WorkflowBundle/Tests/Unit/Entity/ProcessDefinitionTest.php @@ -63,6 +63,7 @@ public function testImport() ->setEnabled(false) ->setRelatedEntity('My/Entity') ->setExecutionOrder(25) + ->setExcludeDefinitions(['foo']) ->setActionsConfiguration(array('key' => 'value')); $this->assertNotEquals($importedEntity->getName(), $this->entity->getName()); @@ -70,6 +71,7 @@ public function testImport() $this->assertNotEquals($importedEntity->getRelatedEntity(), $this->entity->getRelatedEntity()); $this->assertNotEquals($importedEntity->getExecutionOrder(), $this->entity->getExecutionOrder()); $this->assertNotEquals($importedEntity->getActionsConfiguration(), $this->entity->getActionsConfiguration()); + $this->assertNotEquals($importedEntity->getExcludeDefinitions(), $this->entity->getExcludeDefinitions()); $this->assertTrue($this->entity->isEnabled()); $this->entity->import($importedEntity); @@ -79,6 +81,7 @@ public function testImport() $this->assertEquals($importedEntity->getRelatedEntity(), $this->entity->getRelatedEntity()); $this->assertEquals($importedEntity->getExecutionOrder(), $this->entity->getExecutionOrder()); $this->assertEquals($importedEntity->getActionsConfiguration(), $this->entity->getActionsConfiguration()); + $this->assertEquals($importedEntity->getExcludeDefinitions(), $this->entity->getExcludeDefinitions()); $this->assertTrue($this->entity->isEnabled()); // enabled must not be changed } From 6f1b12a4323e65d1dbdb06af21272b35066b02e6 Mon Sep 17 00:00:00 2001 From: Sergey Zhuravel Date: Wed, 11 Feb 2015 23:12:12 +0200 Subject: [PATCH 09/18] CRM-2441: MailChimp integration failed with more than 50 members - track processed grids --- .../Grid/Extension/OrmDatasourceExtension.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Oro/Bundle/QueryDesignerBundle/Grid/Extension/OrmDatasourceExtension.php b/src/Oro/Bundle/QueryDesignerBundle/Grid/Extension/OrmDatasourceExtension.php index 8da34d1f6f8..97ebe065411 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Grid/Extension/OrmDatasourceExtension.php +++ b/src/Oro/Bundle/QueryDesignerBundle/Grid/Extension/OrmDatasourceExtension.php @@ -13,6 +13,13 @@ class OrmDatasourceExtension extends AbstractExtension { + const NAME_PATH = '[name]'; + + /** + * @var string[] + */ + protected $appliedFor; + /** @var RestrictionBuilderInterface */ protected $restrictionBuilder; @@ -38,10 +45,17 @@ public function isApplicable(DatagridConfiguration $config) */ public function visitDatasource(DatagridConfiguration $config, DatasourceInterface $datasource) { + $gridName = $config->offsetGetByPath(self::NAME_PATH); + + if (!empty($this->appliedFor[$gridName])) { + return; + } + /** @var QueryBuilder $qb */ $qb = $datasource->getQueryBuilder(); $ds = new GroupingOrmFilterDatasourceAdapter($qb); $filters = $config->offsetGetByPath('[source][query_config][filters]'); $this->restrictionBuilder->buildRestrictions($filters, $ds); + $this->appliedFor[$gridName] = true; } } From 50675aec51ae9d53503e607675ce33108c1796e0 Mon Sep 17 00:00:00 2001 From: Sergey Zhuravel Date: Wed, 11 Feb 2015 23:23:18 +0200 Subject: [PATCH 10/18] CRM-2441: MailChimp integration failed with more than 50 members - track processed grids --- .../Grid/Extension/OrmDatasourceExtension.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Oro/Bundle/QueryDesignerBundle/Grid/Extension/OrmDatasourceExtension.php b/src/Oro/Bundle/QueryDesignerBundle/Grid/Extension/OrmDatasourceExtension.php index 97ebe065411..a6b549f60f7 100644 --- a/src/Oro/Bundle/QueryDesignerBundle/Grid/Extension/OrmDatasourceExtension.php +++ b/src/Oro/Bundle/QueryDesignerBundle/Grid/Extension/OrmDatasourceExtension.php @@ -5,6 +5,7 @@ use Doctrine\ORM\QueryBuilder; use Oro\Bundle\DataGridBundle\Datagrid\Builder; +use Oro\Bundle\DataGridBundle\Datagrid\ParameterBag; use Oro\Bundle\DataGridBundle\Extension\AbstractExtension; use Oro\Bundle\DataGridBundle\Datasource\Orm\OrmDatasource; use Oro\Bundle\DataGridBundle\Datasource\DatasourceInterface; @@ -29,6 +30,7 @@ class OrmDatasourceExtension extends AbstractExtension public function __construct(RestrictionBuilderInterface $restrictionBuilder) { $this->restrictionBuilder = $restrictionBuilder; + $this->parameters = new ParameterBag(); } /** @@ -46,8 +48,9 @@ public function isApplicable(DatagridConfiguration $config) public function visitDatasource(DatagridConfiguration $config, DatasourceInterface $datasource) { $gridName = $config->offsetGetByPath(self::NAME_PATH); + $parametersKey = md5(json_encode($this->parameters->all())); - if (!empty($this->appliedFor[$gridName])) { + if (!empty($this->appliedFor[$gridName . $parametersKey])) { return; } @@ -56,6 +59,6 @@ public function visitDatasource(DatagridConfiguration $config, DatasourceInterfa $ds = new GroupingOrmFilterDatasourceAdapter($qb); $filters = $config->offsetGetByPath('[source][query_config][filters]'); $this->restrictionBuilder->buildRestrictions($filters, $ds); - $this->appliedFor[$gridName] = true; + $this->appliedFor[$gridName . $parametersKey] = true; } } From 65c0fa0ff10fa4cfc9aceba3e580d3e244c42bb8 Mon Sep 17 00:00:00 2001 From: Vova Soroka Date: Fri, 24 Apr 2015 15:42:04 +0300 Subject: [PATCH 11/18] BAP-7907: WSSE cache needs cleanup --- .../Cache/WsseNoncePhpFileCache.php | 190 ++++++++++++++++++ .../OroSecurityExtension.php | 63 +++++- 2 files changed, 245 insertions(+), 8 deletions(-) create mode 100644 src/Oro/Bundle/SecurityBundle/Cache/WsseNoncePhpFileCache.php diff --git a/src/Oro/Bundle/SecurityBundle/Cache/WsseNoncePhpFileCache.php b/src/Oro/Bundle/SecurityBundle/Cache/WsseNoncePhpFileCache.php new file mode 100644 index 00000000000..209d9180700 --- /dev/null +++ b/src/Oro/Bundle/SecurityBundle/Cache/WsseNoncePhpFileCache.php @@ -0,0 +1,190 @@ +nonceLifeTime = (int)$lifetime; + } + + /** + * {@inheritdoc} + */ + protected function doSave($id, $data, $lifeTime = 0) + { + $this->purge(); + parent::doSave($id, $data, $lifeTime); + } + + /** + * {@inheritdoc} + */ + protected function getFilename($id) + { + $namespace = $this->getNamespace(); + if ($namespace && strpos($id, $namespace) === 0) { + $id = substr($id, strlen($namespace)); + } + $id = $this->removeSpecialChars($id); + + return + $this->getDataDirectory() + . substr(hash('sha256', $id), 0, self::DIRECTORY_NAME_LENGTH) + . DIRECTORY_SEPARATOR + . $id + . $this->getExtension(); + } + + /** + * Gets the full path to the root directory where cached data are located. + * + * @return string + */ + protected function getDataDirectory() + { + $result = $this->getDirectory() . DIRECTORY_SEPARATOR; + + $namespace = $this->getNamespace(); + if ($namespace) { + $result .= $this->removeSpecialChars($namespace) . DIRECTORY_SEPARATOR; + } + + return $result; + } + + /** + * Removes special characters like \/:? and others from the given string + * + * @param string $str + * + * @return string + */ + protected function removeSpecialChars($str) + { + return preg_replace('@[\\\/:"*?<>|]+@', '', $str); + } + + /** + * Deletes expired nonces + */ + protected function purge() + { + $startTime = time(); + $directory = $this->getDataDirectory(); + $purgeStatusFileName = 'cache_purge_status' . $this->getExtension(); + $purgeStatusFilePath = $directory . $purgeStatusFileName; + + // load the purge status + $lastPurgeTime = @include $purgeStatusFilePath; + if (false === $lastPurgeTime) { + // initialize the purge status file + $lastPurgeTime = $startTime; + $this->writePurgeStatus($purgeStatusFilePath, $lastPurgeTime); + } + + if ($startTime - $lastPurgeTime < self::PURGE_INTERVAL) { + // exit because the purge interval is not elapsed yet + return; + } + + // delete expired nonces + $lastPurgeTime = $startTime; + if ($this->doPurge($directory, $startTime, $purgeStatusFileName)) { + $this->writePurgeStatus($purgeStatusFilePath, $lastPurgeTime); + } + } + + /** + * @param string $directory + * @param int $startTime + * @param string $purgeStatusFileName + * + * @return bool TRUE if all expired nonces have been purged; otherwise, FALSE + */ + protected function doPurge($directory, $startTime, $purgeStatusFileName) + { + $success = true; + $count = 0; + $fileIterator = $this->getExpiredFilesIterator($directory, $startTime - $this->nonceLifeTime); + /** @var \SplFileInfo $file */ + foreach ($fileIterator as $name => $file) { + if ($file->getFilename() === $purgeStatusFileName) { + continue; + } + + @unlink($name); + $count++; + if ($count % self::PURGE_BATCH_SIZE === 0 && time() - $startTime >= self::PURGE_MAX_TIME_FRAME) { + $success = false; + break; + } + } + + return $success; + } + + /** + * @param string $directory + * @param int $expirationTime + * + * @return \Iterator + */ + protected function getExpiredFilesIterator($directory, $expirationTime) + { + $fileExtension = $this->getExtension(); + $ignoreFilePrefix = substr(self::DOCTRINE_NAMESPACE_CACHEKEY, 0, -4); // remove ending "[%s]" + + return new \CallbackFilterIterator( + new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS), + \RecursiveIteratorIterator::LEAVES_ONLY + ), + function (\SplFileInfo $file) use ($fileExtension, $ignoreFilePrefix, $expirationTime) { + $fileName = $file->getFilename(); + + return + substr($fileName, -strlen($fileExtension)) === $fileExtension + && $file->getMTime() <= $expirationTime + && strpos($fileName, $ignoreFilePrefix) !== 0; + } + ); + } + + /** + * @param string $purgeStatusFilePath + * @param int $lastPurgeTime + * + * @return bool TRUE on success, FALSE if path cannot be created, if path is not writable or an any other error. + */ + protected function writePurgeStatus($purgeStatusFilePath, $lastPurgeTime) + { + return $this->writeFile( + $purgeStatusFilePath, + sprintf('load('services.yml'); } + /** + * {@inheritdoc} + */ + public function prepend(ContainerBuilder $container) + { + if ($container instanceof OroContainerBuilder) { + $this->setupWsseNonceCache($container); + } + } + /** * @return CumulativeConfigLoader */ @@ -56,4 +68,39 @@ public static function getAclAnnotationLoader() new AclAnnotationCumulativeResourceLoader(['Controller']) ); } + + /** + * Sets default implementation of the cache for WSSE nonces if a custom implementation is not specified + * + * @param OroContainerBuilder $container + */ + protected function setupWsseNonceCache(OroContainerBuilder $container) + { + $securityConfig = $container->getExtensionConfig('security'); + if (isset($securityConfig[0]['firewalls']['wsse_secured']) + && !isset($securityConfig[0]['firewalls']['wsse_secured']['wsse']['nonce_cache_service_id']) + ) { + $securityConfig[0]['firewalls']['wsse_secured']['wsse']['nonce_cache_service_id'] = + self::DEFAULT_WSSE_NONCE_CACHE_SERVICE_ID; + $container->setExtensionConfig('security', $securityConfig); + + if (!$container->hasDefinition(self::DEFAULT_WSSE_NONCE_CACHE_SERVICE_ID)) { + $wsseLifetime = 0; + if (isset($securityConfig[0]['firewalls']['wsse_secured']['wsse']['lifetime'])) { + $wsseLifetime = $securityConfig[0]['firewalls']['wsse_secured']['wsse']['lifetime']; + } + $cacheServiceDef = new Definition( + self::DEFAULT_WSSE_NONCE_CACHE_CLASS, + [self::DEFAULT_WSSE_NONCE_CACHE_PATH] + ); + if ($wsseLifetime) { + $cacheServiceDef->addMethodCall('setNonceLifeTime', [$wsseLifetime]); + } + $container->setDefinition( + self::DEFAULT_WSSE_NONCE_CACHE_SERVICE_ID, + $cacheServiceDef + ); + } + } + } } From 74dd7347cdca9186f4786dadf29ef80b189415f5 Mon Sep 17 00:00:00 2001 From: Vova Soroka Date: Fri, 24 Apr 2015 17:40:33 +0300 Subject: [PATCH 12/18] BAP-7907: WSSE cache needs cleanup Fix typo --- src/Oro/Bundle/SecurityBundle/Cache/WsseNoncePhpFileCache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Oro/Bundle/SecurityBundle/Cache/WsseNoncePhpFileCache.php b/src/Oro/Bundle/SecurityBundle/Cache/WsseNoncePhpFileCache.php index 209d9180700..615d27298e1 100644 --- a/src/Oro/Bundle/SecurityBundle/Cache/WsseNoncePhpFileCache.php +++ b/src/Oro/Bundle/SecurityBundle/Cache/WsseNoncePhpFileCache.php @@ -6,7 +6,7 @@ class WsseNoncePhpFileCache extends BasePhpFileCache { - /** Determines how ofter the purging of expired nonces is executed */ + /** Determines how often the purging of expired nonces is executed */ const PURGE_INTERVAL = 300; // 5 minutes /** Determines how long the purging of expired nonces can be executed */ From dcaf632f56676c8a80335a3890eeb12b64bc0787 Mon Sep 17 00:00:00 2001 From: Vova Soroka Date: Fri, 24 Apr 2015 17:41:21 +0300 Subject: [PATCH 13/18] BAP-7907: WSSE cache needs cleanup Fix typo --- src/Oro/Bundle/SecurityBundle/Cache/WsseNoncePhpFileCache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Oro/Bundle/SecurityBundle/Cache/WsseNoncePhpFileCache.php b/src/Oro/Bundle/SecurityBundle/Cache/WsseNoncePhpFileCache.php index 615d27298e1..da1abadffbd 100644 --- a/src/Oro/Bundle/SecurityBundle/Cache/WsseNoncePhpFileCache.php +++ b/src/Oro/Bundle/SecurityBundle/Cache/WsseNoncePhpFileCache.php @@ -15,7 +15,7 @@ class WsseNoncePhpFileCache extends BasePhpFileCache /** Determines how many files can be deleted at once */ const PURGE_BATCH_SIZE = 100; - /** Determines a length of directory names where cached data are located */ + /** Determines the length of directory names where cached data are located */ const DIRECTORY_NAME_LENGTH = 1; /** @var int */ From 90b70566f489b84108ba666f772cd7c6560e7fdc Mon Sep 17 00:00:00 2001 From: Makar Sichevoy Date: Tue, 28 Apr 2015 16:26:05 +0300 Subject: [PATCH 14/18] BAP-7799: Error on package installation --- composer.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index fe9a1a543f3..2af9f228650 100644 --- a/composer.json +++ b/composer.json @@ -13,6 +13,10 @@ { "type": "composer", "url": "http://packagist.orocrm.com" + }, + { + "type": "vcs", + "url": "https://github.com/orocrm/composer" } ], "require": { @@ -60,7 +64,7 @@ "guzzle/guzzle": "3.7.*", "lexik/maintenance-bundle": "v1.0.3", "sylius/flow-bundle": "0.6.*", - "composer/composer": "1.0.0-alpha8", + "composer/composer": "dev-master", "akeneo/batch-bundle": "~0.3", "luxifer/doctrine-functions": "1.2.1", "nesbot/Carbon": "1.8.*", From 5dd0830c0cc4e94dbf139a4f3ab8e901cdcf6c25 Mon Sep 17 00:00:00 2001 From: Makar Sichevoy Date: Tue, 28 Apr 2015 18:16:23 +0300 Subject: [PATCH 15/18] BAP-7799: Error on package installation - changed order of repositories --- composer.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 2af9f228650..07c25019b4f 100644 --- a/composer.json +++ b/composer.json @@ -10,13 +10,13 @@ } ], "repositories": [ - { - "type": "composer", - "url": "http://packagist.orocrm.com" - }, { "type": "vcs", "url": "https://github.com/orocrm/composer" + }, + { + "type": "composer", + "url": "http://packagist.orocrm.com" } ], "require": { From b2f158b4daed24b187716bf77a12b2db820edd08 Mon Sep 17 00:00:00 2001 From: Makar Sichevoy Date: Tue, 28 Apr 2015 18:40:30 +0300 Subject: [PATCH 16/18] BAP-7799: Error on package installation - add composer tag --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 07c25019b4f..a90e222dc39 100644 --- a/composer.json +++ b/composer.json @@ -64,7 +64,7 @@ "guzzle/guzzle": "3.7.*", "lexik/maintenance-bundle": "v1.0.3", "sylius/flow-bundle": "0.6.*", - "composer/composer": "dev-master", + "composer/composer": "1.0.0-alpha10-symfony23", "akeneo/batch-bundle": "~0.3", "luxifer/doctrine-functions": "1.2.1", "nesbot/Carbon": "1.8.*", From b7df11787859c4d91b2405dcb229531b2edb45f3 Mon Sep 17 00:00:00 2001 From: Makar Sichevoy Date: Tue, 28 Apr 2015 19:04:04 +0300 Subject: [PATCH 17/18] BAP-7799: Error on package installation - tag change --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index a90e222dc39..b4cc0c92f52 100644 --- a/composer.json +++ b/composer.json @@ -64,7 +64,7 @@ "guzzle/guzzle": "3.7.*", "lexik/maintenance-bundle": "v1.0.3", "sylius/flow-bundle": "0.6.*", - "composer/composer": "1.0.0-alpha10-symfony23", + "composer/composer": "1.0.0-p1", "akeneo/batch-bundle": "~0.3", "luxifer/doctrine-functions": "1.2.1", "nesbot/Carbon": "1.8.*", From 711b0bb571e91a6b467477e60c8e84cd6d8e144d Mon Sep 17 00:00:00 2001 From: Makar Sichevoy Date: Tue, 28 Apr 2015 19:34:02 +0300 Subject: [PATCH 18/18] BAP-7799: Error on package installation --- composer.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/composer.json b/composer.json index b4cc0c92f52..605284efd96 100644 --- a/composer.json +++ b/composer.json @@ -10,10 +10,6 @@ } ], "repositories": [ - { - "type": "vcs", - "url": "https://github.com/orocrm/composer" - }, { "type": "composer", "url": "http://packagist.orocrm.com"