EmailActivityListProvider::__construct()   B
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 25
Code Lines 23

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 25
rs 8.8571
cc 1
eloc 23
nc 1
nop 11

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace Oro\Bundle\EmailBundle\Provider;
4
5
use Doctrine\Common\Util\ClassUtils;
6
use Doctrine\ORM\QueryBuilder;
7
8
use Symfony\Bundle\FrameworkBundle\Routing\Router;
9
use Symfony\Component\Routing\Exception\RouteNotFoundException;
10
use Symfony\Component\PropertyAccess\PropertyAccess;
11
use Symfony\Component\Security\Core\SecurityContextInterface;
12
13
use Oro\Bundle\ActivityBundle\Tools\ActivityAssociationHelper;
14
use Oro\Bundle\ActivityListBundle\Entity\ActivityList;
15
use Oro\Bundle\ActivityListBundle\Entity\ActivityOwner;
16
use Oro\Bundle\ActivityListBundle\Model\ActivityListDateProviderInterface;
17
use Oro\Bundle\ActivityListBundle\Model\ActivityListGroupProviderInterface;
18
use Oro\Bundle\ActivityListBundle\Model\ActivityListProviderInterface;
19
use Oro\Bundle\CommentBundle\Model\CommentProviderInterface;
20
use Oro\Bundle\CommentBundle\Tools\CommentAssociationHelper;
21
use Oro\Bundle\EmailBundle\Entity\Email;
22
use Oro\Bundle\EmailBundle\Entity\EmailOwnerInterface;
23
use Oro\Bundle\EmailBundle\Entity\EmailUser;
24
use Oro\Bundle\EmailBundle\Entity\Provider\EmailThreadProvider;
25
use Oro\Bundle\EntityBundle\ORM\DoctrineHelper;
26
use Oro\Bundle\EntityBundle\Provider\EntityNameResolver;
27
use Oro\Bundle\EntityConfigBundle\Config\ConfigManager;
28
use Oro\Bundle\EntityConfigBundle\DependencyInjection\Utils\ServiceLink;
29
use Oro\Bundle\SecurityBundle\Authentication\Token\OrganizationContextTokenInterface;
30
use Oro\Bundle\UIBundle\Tools\HtmlTagHelper;
31
32
/**
33
 * For the Email activity in the case when EmailAddress does not have owner(User|Organization),
34
 * we are trying to extract Organization from the current logged user.
35
 *
36
 * @todo Should be refactored in the BAP-8520
37
 * @see EmailActivityListProvider::isApplicable
38
 * @see EmailActivityListProvider::getOrganization
39
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
40
 */
41
class EmailActivityListProvider implements
42
    ActivityListProviderInterface,
43
    ActivityListDateProviderInterface,
44
    ActivityListGroupProviderInterface,
45
    CommentProviderInterface
46
{
47
    const ACTIVITY_CLASS = 'Oro\Bundle\EmailBundle\Entity\Email';
48
    const ACL_CLASS = 'Oro\Bundle\EmailBundle\Entity\EmailUser';
49
50
    /** @var DoctrineHelper */
51
    protected $doctrineHelper;
52
53
    /** @var ServiceLink */
54
    protected $doctrineRegistryLink;
55
56
    /** @var EntityNameResolver */
57
    protected $entityNameResolver;
58
59
    /** @var Router */
60
    protected $router;
61
62
    /** @var ConfigManager */
63
    protected $configManager;
64
65
    /** @var EmailThreadProvider */
66
    protected $emailThreadProvider;
67
68
    /** @var HtmlTagHelper */
69
    protected $htmlTagHelper;
70
71
    /** @var ServiceLink */
72
    protected $securityContextLink;
73
74
    /** @var ServiceLink */
75
    protected $securityFacadeLink;
76
77
    /** @var ServiceLink */
78
    protected $mailboxProcessStorageLink;
79
80
    /** @var ActivityAssociationHelper */
81
    protected $activityAssociationHelper;
82
83
    /** @var CommentAssociationHelper */
84
    protected $commentAssociationHelper;
85
86
    /**
87
     * @param DoctrineHelper            $doctrineHelper
88
     * @param ServiceLink               $doctrineRegistryLink
89
     * @param EntityNameResolver        $entityNameResolver
90
     * @param Router                    $router
91
     * @param ConfigManager             $configManager
92
     * @param EmailThreadProvider       $emailThreadProvider
93
     * @param HtmlTagHelper             $htmlTagHelper
94
     * @param ServiceLink               $securityFacadeLink
95
     * @param ServiceLink               $mailboxProcessStorageLink
96
     * @param ActivityAssociationHelper $activityAssociationHelper
97
     * @param CommentAssociationHelper  $commentAssociationHelper
98
     *
99
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
100
     */
101
    public function __construct(
102
        DoctrineHelper $doctrineHelper,
103
        ServiceLink $doctrineRegistryLink,
104
        EntityNameResolver $entityNameResolver,
105
        Router $router,
106
        ConfigManager $configManager,
107
        EmailThreadProvider $emailThreadProvider,
108
        HtmlTagHelper $htmlTagHelper,
109
        ServiceLink $securityFacadeLink,
110
        ServiceLink $mailboxProcessStorageLink,
111
        ActivityAssociationHelper $activityAssociationHelper,
112
        CommentAssociationHelper $commentAssociationHelper
113
    ) {
114
        $this->doctrineHelper            = $doctrineHelper;
115
        $this->doctrineRegistryLink      = $doctrineRegistryLink;
116
        $this->entityNameResolver        = $entityNameResolver;
117
        $this->router                    = $router;
118
        $this->configManager             = $configManager;
119
        $this->emailThreadProvider       = $emailThreadProvider;
120
        $this->htmlTagHelper             = $htmlTagHelper;
121
        $this->securityFacadeLink        = $securityFacadeLink;
122
        $this->mailboxProcessStorageLink = $mailboxProcessStorageLink;
123
        $this->activityAssociationHelper = $activityAssociationHelper;
124
        $this->commentAssociationHelper  = $commentAssociationHelper;
125
    }
126
127
    /**
128
     * @param ServiceLink $securityContextLink
129
     */
130
    public function setSecurityContextLink(ServiceLink $securityContextLink)
131
    {
132
        $this->securityContextLink = $securityContextLink;
133
    }
134
135
    /**
136
     * {@inheritdoc}
137
     */
138
    public function isApplicableTarget($entityClass, $accessible = true)
139
    {
140
        return $this->activityAssociationHelper->isActivityAssociationEnabled(
141
            $entityClass,
142
            self::ACTIVITY_CLASS,
143
            $accessible
144
        );
145
    }
146
147
    /**
148
     * {@inheritdoc}
149
     */
150
    public function getRoutes()
151
    {
152
        return [
153
            'itemView'  => 'oro_email_view',
154
            'groupView' => 'oro_email_view_group',
155
        ];
156
    }
157
158
    /**
159
     * {@inheritdoc}
160
     */
161
    public function getActivityClass()
162
    {
163
        return self::ACTIVITY_CLASS;
164
    }
165
166
    /**
167
     * {@inheritdoc}
168
     */
169
    public function getAclClass()
170
    {
171
        return self::ACL_CLASS;
172
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177
    public function getSubject($entity)
178
    {
179
        /** @var $entity Email */
180
        return $entity->getSubject();
181
    }
182
183
    /**
184
     * {@inheritdoc}
185
     */
186
    public function getDescription($entity)
187
    {
188
        /** @var $entity Email */
189
        if ($entity->getEmailBody()) {
190
            $body = $entity->getEmailBody()->getBodyContent();
191
            $content = $this->htmlTagHelper->purify($body);
192
            $content = $this->htmlTagHelper->stripTags($content);
193
            $content = $this->htmlTagHelper->shorten($content);
194
195
            return $content;
196
        }
197
198
        return null;
199
    }
200
201
    /**
202
     * {@inheritdoc}
203
     */
204
    public function getOwner($entity)
205
    {
206
        return null;
207
    }
208
209
    /**
210
     * {@inheritdoc}
211
     */
212
    public function getCreatedAt($entity)
213
    {
214
        /** @var $entity Email */
215
        return $entity->getSentAt();
216
    }
217
218
    /**
219
     * {@inheritdoc}
220
     */
221
    public function getUpdatedAt($entity)
222
    {
223
        /** @var $entity Email */
224
        return $entity->getSentAt();
225
    }
226
227
    /**
228
     * {@inheritdoc}
229
     */
230
    public function isHead($entity)
231
    {
232
        /** @var $entity Email */
233
        return $entity->isHead();
234
    }
235
236
    /**
237
     * {@inheritdoc}
238
     */
239
    public function getOrganization($activityEntity)
240
    {
241
        /**
242
         * @var $activityEntity Email
243
         * @var $emailAddressOwner EmailOwnerInterface
244
         */
245
        $emailAddressOwner = $activityEntity->getFromEmailAddress()->getOwner();
246
        if ($emailAddressOwner && $emailAddressOwner->getOrganization()) {
247
            return $emailAddressOwner->getOrganization();
248
        }
249
250
        /** @var SecurityContextInterface $securityContext */
251
        $securityContext = $this->securityContextLink->getService();
252
        $token           = $securityContext->getToken();
253
        if ($token instanceof OrganizationContextTokenInterface) {
254
            return $token->getOrganizationContext();
255
        }
256
257
        $processes = $this->mailboxProcessStorageLink->getService()->getProcesses();
258
        foreach ($processes as $process) {
259
            $settingsClass = $process->getSettingsEntityFQCN();
260
261
            $mailboxes = $this->doctrineRegistryLink->getService()->getRepository('OroEmailBundle:Mailbox')
262
                ->findBySettingsClassAndEmail($settingsClass, $activityEntity);
263
264
            foreach ($mailboxes as $mailbox) {
265
                return $mailbox->getOrganization();
266
            }
267
        }
268
269
        return null;
270
    }
271
272
    /**
273
     * {@inheritdoc}
274
     */
275
    public function getData(ActivityList $activityListEntity)
276
    {
277
        /** @var Email $email */
278
        $email = $headEmail = $this->doctrineRegistryLink->getService()
279
            ->getRepository($activityListEntity->getRelatedActivityClass())
280
            ->find($activityListEntity->getRelatedActivityId());
281
        if ($email->isHead() && $email->getThread()) {
282
            $headEmail = $this->emailThreadProvider->getHeadEmail(
283
                $this->doctrineHelper->getEntityManager($activityListEntity->getRelatedActivityClass()),
0 ignored issues
show
Documentation introduced by
$this->doctrineHelper->g...RelatedActivityClass()) is of type object<Doctrine\Common\P...nce\ObjectManager>|null, but the function expects a object<Doctrine\ORM\EntityManager>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
284
                $email
285
            );
286
        }
287
288
        $data = [
289
            'ownerName'     => $email->getFromName(),
290
            'ownerLink'     => null,
291
            'entityId'      => $email->getId(),
292
            'headOwnerName' => $headEmail->getFromName(),
293
            'headSubject'   => $headEmail->getSubject(),
294
            'headSentAt'    => $headEmail->getSentAt()->format('c'),
295
            'isHead'        => $email->isHead() && $email->getThread(),
296
            'treadId'       => $email->getThread() ? $email->getThread()->getId() : null
297
        ];
298
        $data = $this->setReplaedEmailId($email, $data);
299
300
        if ($email->getFromEmailAddress()->getHasOwner()) {
301
            $owner = $email->getFromEmailAddress()->getOwner();
302
            $data['headOwnerName'] = $data['ownerName'] = $this->entityNameResolver->getName($owner);
303
            $data = $this->setOwnerLink($owner, $data);
304
        }
305
306
        return $data;
307
    }
308
309
    /**
310
     * {@inheritdoc}
311
     */
312
    public function getTemplate()
313
    {
314
        return 'OroEmailBundle:Email:js/activityItemTemplate.js.twig';
315
    }
316
317
    /**
318
     * {@inheritdoc}
319
     */
320
    public function getGroupedTemplate()
321
    {
322
        return 'OroEmailBundle:Email:js/groupedActivityItemTemplate.js.twig';
323
    }
324
325
    /**
326
     * {@inheritdoc}
327
     */
328
    public function getActivityId($entity)
329
    {
330
        if ($this->doctrineHelper->getEntityClass($entity) === self::ACL_CLASS) {
331
            $entity = $entity->getEmail();
332
        }
333
        return $this->doctrineHelper->getSingleEntityIdentifier($entity);
334
    }
335
336
    /**
337
     * {@inheritdoc}
338
     */
339
    public function isApplicable($entity)
340
    {
341
        return $this->doctrineHelper->getEntityClass($entity) == self::ACTIVITY_CLASS;
342
    }
343
344
    /**
345
     * {@inheritdoc}
346
     */
347
    public function getTargetEntities($entity)
348
    {
349
        return $entity->getActivityTargetEntities();
350
    }
351
352
    /**
353
     * {@inheritdoc}
354
     */
355
    public function isCommentsEnabled($entityClass)
356
    {
357
        return $this->commentAssociationHelper->isCommentAssociationEnabled($entityClass);
358
    }
359
360
    /**
361
     * {@inheritdoc}
362
     */
363
    public function getGroupedEntities($email)
364
    {
365
        /** @var QueryBuilder $queryBuilder */
366
        $queryBuilder = $this->doctrineRegistryLink->getService()
367
            ->getRepository('OroActivityListBundle:ActivityList')->createQueryBuilder('a');
368
369
        $queryBuilder->innerJoin(
370
            'OroEmailBundle:Email',
371
            'e',
372
            'INNER',
373
            'a.relatedActivityId = e.id and a.relatedActivityClass = :class'
374
        )
375
            ->setParameter('class', self::ACTIVITY_CLASS)
376
            ->andWhere('e.thread = :thread')
377
            ->setParameter('thread', $email->getThread());
378
379
        return $queryBuilder->getQuery()->getResult();
380
    }
381
382
    /**
383
     * {@inheritdoc}
384
     */
385
    public function getActivityOwners($entity, ActivityList $activityList)
386
    {
387
        $entity = $this->getEmailEntity($entity);
388
        $filter = ['email' => $entity];
389
        $targetEntities = $this->getTargetEntities($entity);
390
        $organizations = [$this->getOrganization($entity)];
391
        $propertyAccessor = PropertyAccess::createPropertyAccessor();
392
        foreach ($targetEntities as $target) {
393
            try {
394
                $organizations[] = $propertyAccessor->getValue($target, 'organization');
395
            } catch (\Exception $e) {
396
                // skipp target
397
            }
398
        }
399
        if (count($organizations) > 0) {
400
            $filter['organization'] = $organizations;
401
        }
402
403
        $activityArray = [];
404
        /** @var EmailUser[] $owners */
405
        $owners = $this->doctrineRegistryLink->getService()
406
            ->getRepository('OroEmailBundle:EmailUser')
407
            ->findBy($filter);
408
409
        if ($owners) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $owners of type Oro\Bundle\EmailBundle\Entity\EmailUser[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
410
            foreach ($owners as $owner) {
411
                if (($owner->getMailboxOwner() && $owner->getOrganization()) ||
412
                    (!$owner->getMailboxOwner() && $owner->getOrganization() && $owner->getOwner() )) {
413
                    $activityOwner = new ActivityOwner();
414
                    $activityOwner->setActivity($activityList);
415
                    $activityOwner->setOrganization($owner->getOrganization());
0 ignored issues
show
Documentation introduced by
$owner->getOrganization() is of type object<Oro\Bundle\Organi...\OrganizationInterface>, but the function expects a null|object<Oro\Bundle\O...le\Entity\Organization>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
416
                    $user = $owner->getOwner();
417
                    if (!$owner->getOwner() && $owner->getMailboxOwner()) {
418
                        $settings =  $owner->getMailboxOwner()->getProcessSettings();
419
                        if ($settings) {
420
                            $user = $settings->getOwner();
421
                        }
422
                    }
423
                    $activityOwner->setUser($user);
424
                    $activityArray[] = $activityOwner;
425
                }
426
            }
427
        }
428
429
        return $activityArray;
430
    }
431
432
    /**
433
     * @param $entity
434
     * @return mixed
435
     */
436
    protected function getEmailEntity($entity)
437
    {
438
        if (ClassUtils::getClass($entity) === self::ACL_CLASS) {
439
            $entity = $entity->getEmail();
440
        }
441
442
        return $entity;
443
    }
444
445
    /**
446
     * @param Email $email
447
     * @param $data
448
     *
449
     * @return mixed
450
     */
451
    protected function setReplaedEmailId($email, $data)
452
    {
453
        if ($email->getThread()) {
454
            $emails = $email->getThread()->getEmails();
455
            // if there are just two email - add replayedEmailId to use on client side
456
            if (count($emails) === 2) {
457
                $data['replayedEmailId'] = $emails[0]->getId();
458
            }
459
        }
460
461
        return $data;
462
    }
463
464
    /**
465
     * @param EmailOwnerInterface $owner
466
     * @param $data
467
     *
468
     * @return mixed
469
     */
470
    protected function setOwnerLink($owner, $data)
471
    {
472
        $route = $this->configManager->getEntityMetadata(ClassUtils::getClass($owner))
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Metadata\ClassMetadata as the method getRoute() does only exist in the following sub-classes of Metadata\ClassMetadata: Oro\Bundle\EntityConfigB...Metadata\EntityMetadata. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
473
            ->getRoute('view');
474
        $securityFacade = $this->securityFacadeLink->getService();
475
        if (null !== $route && $securityFacade->isGranted('VIEW', $owner)) {
476
            $id = $this->doctrineHelper->getSingleEntityIdentifier($owner);
477
            try {
478
                $data['ownerLink'] = $this->router->generate($route, ['id' => $id]);
479
            } catch (RouteNotFoundException $e) {
480
                // Do not set owner link if route is not found.
481
            }
482
        }
483
484
        return $data;
485
    }
486
}
487