Skip to content

Commit

Permalink
Merge pull request #4653 from laboro/ticket/BAP-12666_1.12
Browse files Browse the repository at this point in the history
BAP-12666: Fix broken email - contact / user / customer associations
  • Loading branch information
Pavel Usachev authored Nov 30, 2016
2 parents 10a9fa5 + 2df324a commit b8f144c
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 8 deletions.
42 changes: 42 additions & 0 deletions src/Oro/Bundle/EmailBundle/Command/Manager/AssociationManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -18,6 +19,7 @@
class AssociationManager
{
const EMAIL_BUFFER_SIZE = 100;
const OWNER_IDS_BUFFER_SIZE = 100;

/** @var DoctrineHelper */
protected $doctrineHelper;
Expand Down Expand Up @@ -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
*
Expand Down
69 changes: 69 additions & 0 deletions src/Oro/Bundle/EmailBundle/Command/UpdateAssociationsCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

namespace Oro\Bundle\EmailBundle\Command;

use Doctrine\ORM\EntityManager;
use JMS\JobQueueBundle\Entity\Job;
use Oro\Bundle\EmailBundle\Command\Manager\AssociationManager;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class UpdateAssociationsCommand extends ContainerAwareCommand
{
const NAME = 'oro:email:update-associations';
const OPTION_FOREGROUND = 'foreground';

/**
* {@inheritdoc}
*/
protected function configure()
{
$this
->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('<info>Update of associations has been scheduled.</info>');
}

/**
* @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');
}
}
43 changes: 43 additions & 0 deletions src/Oro/Bundle/EmailBundle/Entity/Repository/EmailRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
*
Expand Down
31 changes: 23 additions & 8 deletions src/Oro/Bundle/EmailBundle/Provider/EmailOwnersProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,32 +102,47 @@ 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);
}

return null;
}

/**
* @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()
);
Expand Down

0 comments on commit b8f144c

Please sign in to comment.