diff --git a/src/Oro/Bundle/ActivityListBundle/Resources/public/js/app/views/activity-view.js b/src/Oro/Bundle/ActivityListBundle/Resources/public/js/app/views/activity-view.js index 04443c681aa..def1cba7e7c 100644 --- a/src/Oro/Bundle/ActivityListBundle/Resources/public/js/app/views/activity-view.js +++ b/src/Oro/Bundle/ActivityListBundle/Resources/public/js/app/views/activity-view.js @@ -147,6 +147,7 @@ define(function(require) { }, _onContentChange: function() { + this.disposePageComponents(); this.$(this.options.infoBlock).html(this.model.get('contentHTML')); this.initLayout().done(_.bind(function() { // if the activity has an EmailTreadView -- handle comment count change in own way diff --git a/src/Oro/Bundle/DashboardBundle/Helper/DateHelper.php b/src/Oro/Bundle/DashboardBundle/Helper/DateHelper.php index 4568d5af52d..eefe679abf4 100644 --- a/src/Oro/Bundle/DashboardBundle/Helper/DateHelper.php +++ b/src/Oro/Bundle/DashboardBundle/Helper/DateHelper.php @@ -11,6 +11,9 @@ use Oro\Bundle\FilterBundle\Form\Type\Filter\AbstractDateFilterType; use Oro\Bundle\LocaleBundle\Model\LocaleSettings; +/** + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + */ class DateHelper { const YEAR_TYPE_DAYS = 1460; @@ -169,48 +172,78 @@ public function getDatePeriod(\DateTime $start, \DateTime $end) * @param \DateTime $start * @param \DateTime $end * @param QueryBuilder $qb - * @param $entityField + * @param string $entityField + * @param bool $useCurrentTimeZone */ - public function addDatePartsSelect(\DateTime $start, \DateTime $end, QueryBuilder $qb, $entityField) - { + public function addDatePartsSelect( + \DateTime $start, + \DateTime $end, + QueryBuilder $qb, + $entityField, + $useCurrentTimeZone = true + ) { switch ($this->getFormatStrings($start, $end)['viewType']) { case 'year': - $qb->addSelect(sprintf('%s as yearCreated', $this->getEnforcedTimezoneFunction('YEAR', $entityField))); + $qb->addSelect(sprintf( + '%s as yearCreated', + $this->getEnforcedTimezoneFunction('YEAR', $entityField, $useCurrentTimeZone) + )); $qb->addGroupBy('yearCreated'); break; case 'month': - $qb->addSelect(sprintf('%s as yearCreated', $this->getEnforcedTimezoneFunction('YEAR', $entityField))); + $qb->addSelect(sprintf( + '%s as yearCreated', + $this->getEnforcedTimezoneFunction('YEAR', $entityField, $useCurrentTimeZone) + )); $qb->addSelect( sprintf( '%s as monthCreated', - $this->getEnforcedTimezoneFunction('MONTH', $entityField) + $this->getEnforcedTimezoneFunction('MONTH', $entityField, $useCurrentTimeZone) ) ); $qb->addGroupBy('yearCreated'); $qb->addGroupBy('monthCreated'); break; case 'date': - $qb->addSelect(sprintf("%s as yearCreated", $this->getEnforcedTimezoneFunction('YEAR', $entityField))); - $qb->addSelect(sprintf('%s as weekCreated', $this->getEnforcedTimezoneFunction('WEEK', $entityField))); + $qb->addSelect(sprintf( + "%s as yearCreated", + $this->getEnforcedTimezoneFunction('YEAR', $entityField, $useCurrentTimeZone) + )); + $qb->addSelect(sprintf( + '%s as weekCreated', + $this->getEnforcedTimezoneFunction('WEEK', $entityField, $useCurrentTimeZone) + )); $qb->addGroupBy('yearCreated'); $qb->addGroupBy('weekCreated'); break; case 'day': - $qb->addSelect(sprintf("%s as yearCreated", $this->getEnforcedTimezoneFunction('YEAR', $entityField))); + $qb->addSelect(sprintf( + "%s as yearCreated", + $this->getEnforcedTimezoneFunction('YEAR', $entityField, $useCurrentTimeZone) + )); $qb->addSelect( sprintf( "%s as monthCreated", - $this->getEnforcedTimezoneFunction('MONTH', $entityField) + $this->getEnforcedTimezoneFunction('MONTH', $entityField, $useCurrentTimeZone) ) ); - $qb->addSelect(sprintf("%s as dayCreated", $this->getEnforcedTimezoneFunction('DAY', $entityField))); + $qb->addSelect(sprintf( + "%s as dayCreated", + $this->getEnforcedTimezoneFunction('DAY', $entityField, $useCurrentTimeZone) + )); $qb->addGroupBy('yearCreated'); $qb->addGroupBy('monthCreated'); $qb->addGroupBy('dayCreated'); break; case 'time': - $qb->addSelect(sprintf('%s as dateCreated', $this->getEnforcedTimezoneFunction('DATE', $entityField))); - $qb->addSelect(sprintf('%s as hourCreated', $this->getEnforcedTimezoneFunction('HOUR', $entityField))); + $qb->addSelect(sprintf( + '%s as dateCreated', + $this->getEnforcedTimezoneFunction('DATE', $entityField, $useCurrentTimeZone) + )); + $qb->addSelect(sprintf( + '%s as hourCreated', + $this->getEnforcedTimezoneFunction('HOUR', $entityField, $useCurrentTimeZone) + )); $qb->addGroupBy('dateCreated'); $qb->addGroupBy('hourCreated'); break; @@ -233,7 +266,6 @@ public function getKey(\DateTime $start, \DateTime $end, $row) break; case 'year': return $row['yearCreated']; - break; case 'day': $time = strtotime(sprintf('%s-%s-%s', $row['yearCreated'], $row['monthCreated'], $row['dayCreated'])); break; @@ -241,9 +273,8 @@ public function getKey(\DateTime $start, \DateTime $end, $row) $week = $row['weekCreated'] < 10 ? '0' . $row['weekCreated'] : $row['weekCreated']; return $row['yearCreated'] . '-' . $week; - break; case 'time': - return $row['dateCreated'] . '-' . $row['hourCreated']; + return $row['dateCreated'] . '-' . str_pad($row['hourCreated'], 2, '0', STR_PAD_LEFT); } return date($config['valueStringFormat'], $time); @@ -402,12 +433,13 @@ protected function getFormattedLabel($config, \DateTime $date, $increment) * * @param string $functionName * @param string $fieldName + * @param bool $useCurrentTimeZone * * @return string */ - protected function getEnforcedTimezoneFunction($functionName, $fieldName) + protected function getEnforcedTimezoneFunction($functionName, $fieldName, $useCurrentTimeZone = true) { - if ('UTC' !== $this->localeSettings->getTimeZone()) { + if ($useCurrentTimeZone && 'UTC' !== $this->localeSettings->getTimeZone()) { $fieldName = sprintf("CONVERT_TZ(%s, '+00:00', '%s')", $fieldName, $this->getTimeZoneOffset()); } $result = sprintf('%s(%s)', $functionName, $fieldName); diff --git a/src/Oro/Bundle/DashboardBundle/Tests/Unit/Helper/DateHelperTest.php b/src/Oro/Bundle/DashboardBundle/Tests/Unit/Helper/DateHelperTest.php index ff1023b4de8..ba078ccc5b0 100644 --- a/src/Oro/Bundle/DashboardBundle/Tests/Unit/Helper/DateHelperTest.php +++ b/src/Oro/Bundle/DashboardBundle/Tests/Unit/Helper/DateHelperTest.php @@ -427,4 +427,117 @@ public function getPreviousDateTimeIntervalDataProvider() ] ]; } + + /** + * @dataProvider getFormatStringsProvider + */ + public function testGetFormatStrings(\DateTime $start, \DateTime $end, array $expectedValue) + { + $this->assertEquals($expectedValue, $this->helper->getFormatStrings($start, $end)); + } + + public function getFormatStringsProvider() + { + return [ + 'year' => [ + new \DateTime('2010-01-01', new \DateTimeZone('UTC')), + new \DateTime('2015-01-01', new \DateTimeZone('UTC')), + [ + 'intervalString' => 'P1Y', + 'valueStringFormat' => 'Y', + 'viewType' => 'year', + ] + ], + 'month' => [ + new \DateTime('2010-01-01', new \DateTimeZone('UTC')), + new \DateTime('2010-05-01', new \DateTimeZone('UTC')), + [ + 'intervalString' => 'P1M', + 'valueStringFormat' => 'Y-m', + 'viewType' => 'month', + ] + ], + 'date' => [ + new \DateTime('2010-01-01', new \DateTimeZone('UTC')), + new \DateTime('2010-03-15', new \DateTimeZone('UTC')), + [ + 'intervalString' => 'P1W', + 'valueStringFormat' => 'Y-W', + 'viewType' => 'date', + ] + ], + 'day' => [ + new \DateTime('2010-01-01', new \DateTimeZone('UTC')), + new \DateTime('2010-01-15', new \DateTimeZone('UTC')), + [ + 'intervalString' => 'P1D', + 'valueStringFormat' => 'Y-m-d', + 'viewType' => 'day', + ] + ], + 'time' => [ + new \DateTime('2010-01-01', new \DateTimeZone('UTC')), + new \DateTime('2010-01-02', new \DateTimeZone('UTC')), + [ + 'intervalString' => 'PT1H', + 'valueStringFormat' => 'Y-m-d-H', + 'viewType' => 'time', + ] + ], + ]; + } + + /** + * @dataProvider getKeyGeneratesKeysFromGetDatePeriod + */ + public function testGetKeyGeneratesKeysFromGetDatePeriod( + \DateTime $start, + \DateTime $end, + array $row, + $expectedViewType + ) { + $formatStrings = $this->helper->getFormatStrings($start, $end); + $this->assertEquals($expectedViewType, $formatStrings['viewType']); + + $this->assertArrayHasKey( + $this->helper->getKey($start, $end, $row), + $this->helper->getDatePeriod($start, $end) + ); + } + + public function getKeyGeneratesKeysFromGetDatePeriod() + { + return [ + 'year' => [ + new \DateTime('2010-01-01', new \DateTimeZone('UTC')), + new \DateTime('2015-01-01', new \DateTimeZone('UTC')), + ['yearCreated' => '2010'], + 'year', + ], + 'month' => [ + new \DateTime('2010-01-01', new \DateTimeZone('UTC')), + new \DateTime('2010-05-01', new \DateTimeZone('UTC')), + ['yearCreated' => '2010', 'monthCreated' => '4'], + 'month', + ], + 'date' => [ + new \DateTime('2010-01-01', new \DateTimeZone('UTC')), + new \DateTime('2010-03-15', new \DateTimeZone('UTC')), + ['yearCreated' => '2010', 'weekCreated' => '1'], + 'date', + ], + 'time with hour having 1 digit' => [ + new \DateTime('2010-01-01', new \DateTimeZone('UTC')), + new \DateTime('2010-01-02', new \DateTimeZone('UTC')), + ['dateCreated' => '2010-01-01', 'hourCreated' => '5'], + 'time', + ], + 'time with hour having 2 digits' => [ + new \DateTime('2010-01-01', new \DateTimeZone('UTC')), + new \DateTime('2010-01-02', new \DateTimeZone('UTC')), + ['dateCreated' => '2010-01-01', 'hourCreated' => '11'], + 'time', + ], + ]; + } } diff --git a/src/Oro/Bundle/EmailBundle/Command/Manager/AssociationManager.php b/src/Oro/Bundle/EmailBundle/Command/Manager/AssociationManager.php index 73cc6b326ab..9be91cce9ce 100644 --- a/src/Oro/Bundle/EmailBundle/Command/Manager/AssociationManager.php +++ b/src/Oro/Bundle/EmailBundle/Command/Manager/AssociationManager.php @@ -9,6 +9,7 @@ use Oro\Bundle\BatchBundle\ORM\Query\BufferedQueryResultIterator; use Oro\Bundle\EmailBundle\Command\AddAssociationCommand; +use Oro\Bundle\EmailBundle\Command\UpdateEmailOwnerAssociationsCommand; use Oro\Bundle\EmailBundle\Entity\Email; use Oro\Bundle\EmailBundle\Entity\Manager\EmailManager; use Oro\Bundle\EntityBundle\ORM\DoctrineHelper; @@ -18,6 +19,7 @@ class AssociationManager { const EMAIL_BUFFER_SIZE = 100; + const OWNER_IDS_BUFFER_SIZE = 100; /** @var DoctrineHelper */ protected $doctrineHelper; @@ -70,6 +72,46 @@ public function processAddAssociation($ids, $targetClass, $targetId) return $countNewAssociations; } + /** + * Makes sure that all email owners have assigned their emails + */ + public function processUpdateAllEmailOwners() + { + $jobManager = $this->doctrineHelper->getEntityManagerForClass(Job::class); + + $emailOwnerClasseNames = $this->emailOwnersProvider->getSupportedEmailOwnerClassNames(); + foreach ($emailOwnerClasseNames as $emailOwnerClassName) { + $ownerColumnName = $this->emailOwnersProvider->getOwnerColumnName($emailOwnerClassName); + if (!$ownerColumnName) { + continue; + } + + $ownerIdsQb = $this->doctrineHelper + ->getEntityRepository(Email::class) + ->getOwnerIdsWithEmailsQb( + $emailOwnerClassName, + $this->doctrineHelper->getSingleEntityIdentifierFieldName($emailOwnerClassName), + $ownerColumnName + ); + + $ownerIds = (new BufferedQueryResultIterator($ownerIdsQb)) + ->setBufferSize(self::OWNER_IDS_BUFFER_SIZE) + ->setPageLoadedCallback(function (array $rows) use ($emailOwnerClassName, $jobManager) { + $job = new Job( + UpdateEmailOwnerAssociationsCommand::COMMAND_NAME, + array_merge([$emailOwnerClassName], array_map('current', $rows)) + ); + $jobManager->persist($job); + $jobManager->flush(); + $jobManager->clear(); + }); + + // iterate through ownerIds to call pageLoadedCallback + foreach ($ownerIds as $ownerId) { + } + } + } + /** * Process of command oro:email:update-email-owner-associations * diff --git a/src/Oro/Bundle/EmailBundle/Command/UpdateAssociationsCommand.php b/src/Oro/Bundle/EmailBundle/Command/UpdateAssociationsCommand.php new file mode 100644 index 00000000000..39e0bc320a5 --- /dev/null +++ b/src/Oro/Bundle/EmailBundle/Command/UpdateAssociationsCommand.php @@ -0,0 +1,69 @@ +setName('oro:email:update-associations') + ->setDescription('Update associations to emails') + ->addOption( + static::OPTION_FOREGROUND, + 'f', + InputOption::VALUE_NONE, + 'Schedule jobs in backround and exits immediately' + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + if ($input->getOption(static::OPTION_FOREGROUND)) { + $this + ->getCommandAssociationManager() + ->processUpdateAllEmailOwners(); + } else { + $job = new Job(static::NAME, ['--foreground']); + + $em = $this->getJobManager(); + $em->persist($job); + $em->flush(); + } + + $output->writeln('Update of associations has been scheduled.'); + } + + /** + * @return EntityManager + */ + public function getJobManager() + { + return $this->getContainer()->get('doctrine')->getManagerForClass(Job::class); + } + + /** + * @return AssociationManager + */ + protected function getCommandAssociationManager() + { + return $this->getContainer()->get('oro_email.command.association_manager'); + } +} diff --git a/src/Oro/Bundle/EmailBundle/Datagrid/OriginFolderFilterProvider.php b/src/Oro/Bundle/EmailBundle/Datagrid/OriginFolderFilterProvider.php index 4d2ae13c4ef..f105b28f444 100644 --- a/src/Oro/Bundle/EmailBundle/Datagrid/OriginFolderFilterProvider.php +++ b/src/Oro/Bundle/EmailBundle/Datagrid/OriginFolderFilterProvider.php @@ -128,7 +128,9 @@ protected function prepareMailboxOrigins($results, $extended) { $systemMailboxes = $this->getMailboxes(); foreach ($systemMailboxes as $mailbox) { - $origin = $mailbox->getOrigin(); + if (!$origin = $mailbox->getOrigin()) { + continue; + } $folders = $origin->getFolders(); $mailbox = $mailbox->getLabel(); $folders = $this->filterFolders($folders->toArray()); diff --git a/src/Oro/Bundle/EmailBundle/Entity/Manager/MailboxManager.php b/src/Oro/Bundle/EmailBundle/Entity/Manager/MailboxManager.php index 2af3b346721..c65a7388fd5 100644 --- a/src/Oro/Bundle/EmailBundle/Entity/Manager/MailboxManager.php +++ b/src/Oro/Bundle/EmailBundle/Entity/Manager/MailboxManager.php @@ -5,9 +5,9 @@ use Doctrine\Bundle\DoctrineBundle\Registry; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\QueryBuilder; - use Oro\Bundle\EmailBundle\Entity\EmailOrigin; use Oro\Bundle\EmailBundle\Entity\Mailbox; +use Oro\Bundle\EmailBundle\Model\FolderType; use Oro\Bundle\OrganizationBundle\Entity\Organization; use Oro\Bundle\UserBundle\Entity\User; @@ -60,7 +60,7 @@ public function findAvailableMailboxes($user, Organization $organization = null) } /** - * Returns a list of mailbox emails available to user logged under organization. + * Returns a list of mailbox emails available to user logged under organization from which he can send emails. * * @param User|integer $user User or user id * @param Organization|null $organization @@ -71,6 +71,10 @@ public function findAvailableMailboxEmails($user, Organization $organization = n { $result = $this->createAvailableMailboxesQuery($user, $organization) ->select('mb.email AS email') + ->join('mb.origin', '_o') + ->join('_o.folders', '_f') + ->andWhere('_f.type = :sentFolderType') + ->setParameter('sentFolderType', FolderType::SENT) ->getQuery() ->getResult(); diff --git a/src/Oro/Bundle/EmailBundle/Entity/Repository/EmailRepository.php b/src/Oro/Bundle/EmailBundle/Entity/Repository/EmailRepository.php index 4e954f50f0d..1ce7712dcb5 100644 --- a/src/Oro/Bundle/EmailBundle/Entity/Repository/EmailRepository.php +++ b/src/Oro/Bundle/EmailBundle/Entity/Repository/EmailRepository.php @@ -7,6 +7,7 @@ use Doctrine\ORM\QueryBuilder; use Oro\Bundle\EmailBundle\Entity\Email; +use Oro\Bundle\EntityBundle\ORM\DoctrineHelper; use Oro\Bundle\OrganizationBundle\Entity\Organization; use Oro\Bundle\UserBundle\Entity\User; use Oro\Component\PhpUtils\ArrayUtil; @@ -164,6 +165,48 @@ function (QueryBuilder $qb) { ); } + /** + * Returns QueryBuilder which returns ids or all owner records which have at least one email + * + * @param string $ownerClassName + * @param string $ownerIdentifierName + * @param string $ownerColumnName + * + * @return QueryBuilder + */ + public function getOwnerIdsWithEmailsQb($ownerClassName, $ownerIdentifierName, $ownerColumnName) + { + $qb = $this->_em->createQueryBuilder(); + + return $qb + ->select(sprintf('owner.%s', $ownerIdentifierName)) + ->from($ownerClassName, 'owner') + ->where($qb->expr()->orX( + // has incoming email + $qb->expr()->exists( + $this + ->createQueryBuilder('e') + ->select('e.id') + ->join('e.recipients', 'r') + ->join('r.emailAddress', 'ea') + ->andWhere(sprintf('ea.%s = owner.%s', $ownerColumnName, $ownerIdentifierName)) + ->andWhere('ea.hasOwner = :hasOwner') + ->getDQL() + ), + // has outgoing email + $qb->expr()->exists( + $this + ->createQueryBuilder('e2') + ->select('e2.id') + ->join('e2.fromEmailAddress', 'ea2') + ->andWhere(sprintf('ea2.%s = owner.%s', $ownerColumnName, $ownerIdentifierName)) + ->andWhere('ea2.hasOwner = :hasOwner') + ->getDQL() + ) + )) + ->setParameter('hasOwner', true); + } + /** * Has email entities by owner entity * diff --git a/src/Oro/Bundle/EmailBundle/Form/Type/MailboxType.php b/src/Oro/Bundle/EmailBundle/Form/Type/MailboxType.php index 55fc62aecc0..d0f93b790e0 100644 --- a/src/Oro/Bundle/EmailBundle/Form/Type/MailboxType.php +++ b/src/Oro/Bundle/EmailBundle/Form/Type/MailboxType.php @@ -79,9 +79,9 @@ public function buildForm(FormBuilderInterface $builder, array $options) ]); if ($this->userConfigManager->get('oro_imap.enable_google_imap')) { - $builder->add('imapAccountType', 'oro_imap_choice_account_type'); + $builder->add('imapAccountType', 'oro_imap_choice_account_type', ['error_bubbling' => false]); } else { - $builder->add('origin', 'oro_imap_configuration'); + $builder->add('origin', 'oro_imap_configuration', ['error_bubbling' => false]); } $builder->add('processType', 'choice', [ diff --git a/src/Oro/Bundle/EmailBundle/Provider/EmailOwnersProvider.php b/src/Oro/Bundle/EmailBundle/Provider/EmailOwnersProvider.php index 75f48b17f2d..3983a8fc719 100644 --- a/src/Oro/Bundle/EmailBundle/Provider/EmailOwnersProvider.php +++ b/src/Oro/Bundle/EmailBundle/Provider/EmailOwnersProvider.php @@ -102,13 +102,28 @@ public function supportOwnerProvider($entity) } /** - * @param object $entity + * @return string[] + */ + public function getSupportedEmailOwnerClassNames() + { + $providers = $this->emailOwnerStorage->getProviders(); + + $classes = []; + foreach ($providers as $provider) { + $classes[] = $provider->getEmailOwnerClass(); + } + + return $classes; + } + + /** + * @param object|string $entityOrClass * * @return string|null */ - protected function getOwnerColumnName($entity) + public function getOwnerColumnName($entityOrClass) { - if ($provider = $this->findEmailOwnerProvider($entity)) { + if ($provider = $this->findEmailOwnerProvider($entityOrClass)) { return $this->emailOwnerStorage->getEmailOwnerFieldName($provider); } @@ -116,18 +131,18 @@ protected function getOwnerColumnName($entity) } /** - * @param object $entity + * @param object|string $entityOrClass * * @return EmailOwnerProviderInterface|null */ - protected function findEmailOwnerProvider($entity) + protected function findEmailOwnerProvider($entityOrClass) { - $entityClass = ClassUtils::getClass($entity); + $entityClass = is_object($entityOrClass) ? ClassUtils::getClass($entityOrClass) : $entityOrClass; return ArrayUtil::find( - function (EmailOwnerProviderInterface $provider) use ($entity, $entityClass) { + function (EmailOwnerProviderInterface $provider) use ($entityOrClass, $entityClass) { return $provider->getEmailOwnerClass() === $entityClass - && $this->activityListChainProvider->isSupportedTargetEntity($entity); + && $this->activityListChainProvider->isSupportedTargetEntity($entityOrClass); }, $this->emailOwnerStorage->getProviders() ); diff --git a/src/Oro/Bundle/EmailBundle/Resources/config/services.yml b/src/Oro/Bundle/EmailBundle/Resources/config/services.yml index 1c70075f0c9..1e6d4e3d3d1 100644 --- a/src/Oro/Bundle/EmailBundle/Resources/config/services.yml +++ b/src/Oro/Bundle/EmailBundle/Resources/config/services.yml @@ -473,6 +473,13 @@ services: tags: - { name: validator.constraint_validator, alias: oro_email.email_recipients_validator } + oro_email.validator.mailbox_origin: + class: Oro\Bundle\EmailBundle\Validator\MailboxOriginValidator + arguments: + - @translator + tags: + - { name: validator.constraint_validator, alias: oro_email.validator.mailbox_origin } + oro_email.datagrid_query_factory: class: %oro_email.datagrid_query_factory.class% arguments: diff --git a/src/Oro/Bundle/EmailBundle/Resources/config/validation.yml b/src/Oro/Bundle/EmailBundle/Resources/config/validation.yml index c76ded3206f..e10920f2780 100644 --- a/src/Oro/Bundle/EmailBundle/Resources/config/validation.yml +++ b/src/Oro/Bundle/EmailBundle/Resources/config/validation.yml @@ -33,3 +33,8 @@ Oro\Bundle\EmailBundle\Form\Model\Email: contexts: - Valid: traverse: false + +Oro\Bundle\EmailBundle\Entity\Mailbox: + properties: + origin: + - Oro\Bundle\EmailBundle\Validator\Constraints\MailboxOrigin: ~ diff --git a/src/Oro/Bundle/EmailBundle/Resources/public/js/app/views/email-body-view.js b/src/Oro/Bundle/EmailBundle/Resources/public/js/app/views/email-body-view.js index 32b833d68b1..68b255c65b6 100644 --- a/src/Oro/Bundle/EmailBundle/Resources/public/js/app/views/email-body-view.js +++ b/src/Oro/Bundle/EmailBundle/Resources/public/js/app/views/email-body-view.js @@ -34,6 +34,7 @@ define(function(require) { _.extend(this, _.pick(options, ['bodyContent', 'styles'])); this.$frame = this.$el; this.$frame.on('emailShown', _.bind(this._updateHeight, this)); + this.$frame.on('load', this.reattachBody.bind(this)); this.setElement(this.$el.contents().find('html')); EmailBodyView.__super__.initialize.apply(this, arguments); }, diff --git a/src/Oro/Bundle/EmailBundle/Resources/views/Form/fields.html.twig b/src/Oro/Bundle/EmailBundle/Resources/views/Form/fields.html.twig index aa85468e666..64bc3fa0de2 100644 --- a/src/Oro/Bundle/EmailBundle/Resources/views/Form/fields.html.twig +++ b/src/Oro/Bundle/EmailBundle/Resources/views/Form/fields.html.twig @@ -296,12 +296,15 @@ {% endblock %} {% block oro_email_mailbox_widget %} - {% if form.children.imapAccountType is defined %} - {% set imapAccountType = form_widget(form.children.imapAccountType) %} - {% endif %} - {% if form.children.origin is defined %} - {% set imapAccountType = form_widget(form.children.origin) %} - {% endif %} + {% set imapAccountType %} + {% if form.children.origin is defined %} + {{ form_widget(form.children.origin) }} + {{ form_errors(form.children.origin) }} + {% elseif form.children.imapAccountType is defined %} + {{ form_widget(form.children.imapAccountType) }} + {{ form_errors(form.children.imapAccountType) }} + {% endif %} + {% endset %} {% set process = form_row(form.children.processType) ~ form_widget(form.children.processSettings) %} diff --git a/src/Oro/Bundle/EmailBundle/Validator/Constraints/MailboxOrigin.php b/src/Oro/Bundle/EmailBundle/Validator/Constraints/MailboxOrigin.php new file mode 100644 index 00000000000..9c5e1fbcc8a --- /dev/null +++ b/src/Oro/Bundle/EmailBundle/Validator/Constraints/MailboxOrigin.php @@ -0,0 +1,20 @@ +translator = $translator; + } + + /** + * {@inheritdoc} + */ + public function validate($value, Constraint $constraint) + { + if (!$value instanceof EmailOrigin) { + return; + } + + if ($value->getFolder(FolderType::SENT)) { + return; + } + + $this->context->addViolation( + $constraint->message, + [ + '%button%' => $this->translator->trans('oro.imap.configuration.connect_and_retrieve_folders'), + ] + ); + } +} diff --git a/src/Oro/Bundle/FormBundle/Autocomplete/SearchHandler.php b/src/Oro/Bundle/FormBundle/Autocomplete/SearchHandler.php index fe4b57053e8..6097a06956a 100644 --- a/src/Oro/Bundle/FormBundle/Autocomplete/SearchHandler.php +++ b/src/Oro/Bundle/FormBundle/Autocomplete/SearchHandler.php @@ -7,7 +7,6 @@ use Doctrine\ORM\EntityRepository; use Doctrine\ORM\QueryBuilder; -use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; use Symfony\Component\PropertyAccess\PropertyAccess; use Symfony\Component\PropertyAccess\PropertyAccessor; @@ -298,11 +297,11 @@ function ($key) { $propertyPath = implode('', $keys); } - try { - return $this->propertyAccessor->getValue($item, $propertyPath); - } catch (NoSuchPropertyException $e) { + if (!$this->propertyAccessor->isReadable($item, $propertyPath)) { return null; } + + return $this->propertyAccessor->getValue($item, $propertyPath); } /** diff --git a/src/Oro/Bundle/FormBundle/Tests/Unit/Autocomplete/SearchHandlerTest.php b/src/Oro/Bundle/FormBundle/Tests/Unit/Autocomplete/SearchHandlerTest.php index eef191f94ac..1fadf836052 100644 --- a/src/Oro/Bundle/FormBundle/Tests/Unit/Autocomplete/SearchHandlerTest.php +++ b/src/Oro/Bundle/FormBundle/Tests/Unit/Autocomplete/SearchHandlerTest.php @@ -470,4 +470,67 @@ public function createMockEntity(array $data) return $result; } + + /** + * @dataProvider convertItemProvider + */ + public function testConvertItem($item, $expectedItem) + { + $this->assertEquals($expectedItem, $this->searchHandler->convertItem($item)); + } + + public function convertItemProvider() + { + return [ + 'missing properties' => [ + $this->createStdClass([]), + [ + 'id' => null, + 'name' => null, + 'email' => null, + 'property.path' => null, + ], + ], + 'null properties' => [ + $this->createStdClass([ + 'property' => null, + 'name' => null, + 'email' => null, + ]), + [ + 'id' => null, + 'name' => null, + 'email' => null, + 'property.path' => null, + ], + ], + 'properties with values' => [ + $this->createStdClass([ + 'id' => 1, + 'name' => 'nval', + 'email' => 'eval', + 'property' => [ + 'path' => 'ppval' + ], + ]), + [ + 'id' => 1, + 'name' => 'nval', + 'email' => 'eval', + 'property.path' => 'ppval', + ], + ], + ]; + } + + /** + * @param \stdClass $stdClass + * @param array $properties + * + * @return \stdClass + */ + protected function createStdClass(array $properties) + { + return json_decode(json_encode($properties)); + } } diff --git a/src/Oro/Bundle/ImapBundle/Manager/DTO/Email.php b/src/Oro/Bundle/ImapBundle/Manager/DTO/Email.php index 474495af248..760a1a7f0a9 100644 --- a/src/Oro/Bundle/ImapBundle/Manager/DTO/Email.php +++ b/src/Oro/Bundle/ImapBundle/Manager/DTO/Email.php @@ -103,11 +103,12 @@ public function getAttachments() if ($this->attachments === null) { $this->attachments = array(); - if ($this->getBody()->getContent() === self::EMAIL_EMPTY_BODY_CONTENT) { + $attachments = $this->message->getAttachments(); + if (!$attachments && $this->getBody()->getContent() === self::EMAIL_EMPTY_BODY_CONTENT) { $attachment = $this->message->getMessageAsAttachment(); - $attachments = $attachment === null ? [] : [$attachment]; - } else { - $attachments = $this->message->getAttachments(); + if ($attachment) { + $attachments[] = $attachment; + } } foreach ($attachments as $a) { diff --git a/src/Oro/Bundle/ImportExportBundle/Strategy/Import/ConfigurableAddOrReplaceStrategy.php b/src/Oro/Bundle/ImportExportBundle/Strategy/Import/ConfigurableAddOrReplaceStrategy.php index 2ac466a95bd..d3bd24a96ca 100644 --- a/src/Oro/Bundle/ImportExportBundle/Strategy/Import/ConfigurableAddOrReplaceStrategy.php +++ b/src/Oro/Bundle/ImportExportBundle/Strategy/Import/ConfigurableAddOrReplaceStrategy.php @@ -357,7 +357,10 @@ protected function combineIdentityValues($entity, $entityClass, array $searchCon if (null !== $value) { if ('' !== $value) { if (is_object($value)) { - $notEmptyValues[$fieldName] = $this->databaseHelper->getIdentifier($value); + $identifier = $this->databaseHelper->getIdentifier($value); + if ($identifier !== null) { + $notEmptyValues[$fieldName] = $identifier; + } } else { $notEmptyValues[$fieldName] = $value; } diff --git a/src/Oro/Bundle/UIBundle/Resources/public/js/app/models/load-more-collection.js b/src/Oro/Bundle/UIBundle/Resources/public/js/app/models/load-more-collection.js index 895a2dd291e..2d38ed85778 100644 --- a/src/Oro/Bundle/UIBundle/Resources/public/js/app/models/load-more-collection.js +++ b/src/Oro/Bundle/UIBundle/Resources/public/js/app/models/load-more-collection.js @@ -43,7 +43,9 @@ define(function(require) { * @inheritDoc */ parse: function(response) { - this._state.set('totalItemsQuantity', response.count || 0); + if (!this.disposed) { + this._state.set('totalItemsQuantity', response.count || 0); + } return LoadMoreCollection.__super__.parse.apply(this, arguments); }, diff --git a/src/Oro/Bundle/UIBundle/Resources/translations/messages.en.yml b/src/Oro/Bundle/UIBundle/Resources/translations/messages.en.yml index 63cd84aada4..8aa7565dfe9 100644 --- a/src/Oro/Bundle/UIBundle/Resources/translations/messages.en.yml +++ b/src/Oro/Bundle/UIBundle/Resources/translations/messages.en.yml @@ -37,6 +37,7 @@ oro: created_at: Created At delete: Delete delete_confirm: Are you sure you want to delete this %entity_label%? + delete_confirm_cascade: Are you sure you want to delete this %entity_label% with other entities related to it? delete_entity: Delete %entityName% delete_message: %entity_label% deleted edit: Edit