getExistingImapFolders()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 6

Duplication

Lines 12
Ratio 100 %
Metric Value
dl 12
loc 12
rs 9.4285
cc 1
eloc 6
nc 1
nop 1
1
<?php
2
3
namespace Oro\Bundle\ImapBundle\Sync;
4
5
use Doctrine\Common\Collections\ArrayCollection;
6
use Doctrine\ORM\EntityManager;
7
use Doctrine\ORM\Query;
8
9
use Oro\Bundle\BatchBundle\ORM\Query\BufferedQueryResultIterator;
10
use Oro\Bundle\EmailBundle\Entity\Mailbox;
11
use Oro\Bundle\EmailBundle\Model\FolderType;
12
use Oro\Bundle\EmailBundle\Builder\EmailEntityBuilder;
13
use Oro\Bundle\EmailBundle\Entity\Email as EmailEntity;
14
use Oro\Bundle\EmailBundle\Entity\EmailFolder;
15
use Oro\Bundle\EmailBundle\Entity\EmailOrigin;
16
use Oro\Bundle\EmailBundle\Entity\EmailUser;
17
use Oro\Bundle\EmailBundle\Sync\AbstractEmailSynchronizationProcessor;
18
use Oro\Bundle\EmailBundle\Sync\KnownEmailAddressCheckerInterface;
19
use Oro\Bundle\ImapBundle\Entity\ImapEmail;
20
use Oro\Bundle\ImapBundle\Entity\ImapEmailFolder;
21
use Oro\Bundle\ImapBundle\Entity\Repository\ImapEmailFolderRepository;
22
use Oro\Bundle\ImapBundle\Entity\Repository\ImapEmailRepository;
23
use Oro\Bundle\ImapBundle\Mail\Storage\Exception\UnsupportException;
24
use Oro\Bundle\ImapBundle\Mail\Storage\Exception\UnselectableFolderException;
25
use Oro\Bundle\ImapBundle\Mail\Storage\Folder;
26
use Oro\Bundle\ImapBundle\Manager\ImapEmailIterator;
27
use Oro\Bundle\ImapBundle\Manager\ImapEmailManager;
28
use Oro\Bundle\ImapBundle\Manager\DTO\Email;
29
30
/**
31
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
32
 */
33
class ImapEmailSynchronizationProcessor extends AbstractEmailSynchronizationProcessor
34
{
35
    /** Determines how many emails can be loaded from IMAP server at once */
36
    const READ_BATCH_SIZE = 100;
37
38
    /** Determines how often "Processed X of N emails" hint should be added to a log */
39
    const READ_HINT_COUNT = 500;
40
41
    /** Determines how often the clearing of outdated folders routine should be executed */
42
    const CLEANUP_EVERY_N_RUN = 100;
43
44
    /** @var ImapEmailManager */
45
    protected $manager;
46
47
    /**
48
     * Constructor
49
     *
50
     * @param EntityManager                     $em
51
     * @param EmailEntityBuilder                $emailEntityBuilder
52
     * @param KnownEmailAddressCheckerInterface $knownEmailAddressChecker
53
     * @param ImapEmailManager                  $manager
54
     */
55
    public function __construct(
56
        EntityManager $em,
57
        EmailEntityBuilder $emailEntityBuilder,
58
        KnownEmailAddressCheckerInterface $knownEmailAddressChecker,
59
        ImapEmailManager $manager
60
    ) {
61
        parent::__construct($em, $emailEntityBuilder, $knownEmailAddressChecker);
62
        $this->manager = $manager;
63
    }
64
65
    /**
66
     * {@inheritdoc}
67
     */
68
    public function process(EmailOrigin $origin, $syncStartTime)
69
    {
70
        // make sure that the entity builder is empty
71
        $this->emailEntityBuilder->clear();
72
73
        $this->initEnv($origin);
74
75
        // iterate through all folders enabled for sync and do a synchronization of emails for each one
76
        $imapFolders = $this->getSyncEnabledImapFolders($origin);
77
        foreach ($imapFolders as $imapFolder) {
78
            $folder = $imapFolder->getFolder();
79
80
            // ask an email server to select the current folder
81
            $folderName = $folder->getFullName();
82
            try {
83
                $this->manager->selectFolder($folderName);
84
                $this->logger->info(sprintf('The folder "%s" is selected.', $folderName));
85
86
                // register the current folder in the entity builder
87
                $this->emailEntityBuilder->setFolder($folder);
88
89
                // sync emails using this search query
90
                $lastSynchronizedAt = $this->syncEmails($origin, $imapFolder);
91
                $folder->setSynchronizedAt($lastSynchronizedAt > $syncStartTime ? $lastSynchronizedAt : $syncStartTime);
92
93
                $startDate = $folder->getSynchronizedAt();
94
                $checkStartDate = clone $startDate;
95
                $checkStartDate->modify('-6 month');
96
97
                // set seen flags from previously synchronized emails
98
                $this->checkFlags($imapFolder, $checkStartDate);
99
100
                $this->em->flush($folder);
101
            } catch (UnselectableFolderException $e) {
102
                $this->logger->info(sprintf('The folder "%s" cannot be selected and was skipped.', $folderName));
103
            }
104
105
            $this->cleanUp(true, $imapFolder->getFolder());
106
        }
107
108
        $this->removeRemotelyRemovedEmails($origin);
109
110
        // run removing of empty outdated folders every N synchronizations
111
        if ($origin->getSyncCount() > 0 && $origin->getSyncCount() % self::CLEANUP_EVERY_N_RUN == 0) {
112
            $this->cleanupOutdatedFolders($origin);
113
        }
114
    }
115
116
    /**
117
     * @param EmailOrigin $origin
118
     */
119
    protected function removeRemotelyRemovedEmails(EmailOrigin $origin)
120
    {
121
        $imapFolders = $this->getSyncEnabledImapFolders($origin);
122
        foreach ($imapFolders as $imapFolder) {
123
            $folder = $imapFolder->getFolder();
124
            $folderName = $folder->getFullName();
125
            try {
126
                $this->manager->selectFolder($folderName);
127
128
                $this->em->transactional(function () use ($imapFolder, $folder) {
129
                    $existingUids = $this->manager->getEmailUIDs();
130
131
                    $staleImapEmailsQb = $this->em->getRepository('OroImapBundle:ImapEmail')->createQueryBuilder('ie');
132
                    $staleImapEmailsQb
133
                        ->andWhere($staleImapEmailsQb->expr()->eq('ie.imapFolder', ':imap_folder'))
134
                        ->setParameter('imap_folder', $imapFolder);
135
136
                    if ($existingUids) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $existingUids of type array 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...
137
                        $staleImapEmailsQb
138
                            ->andWhere($staleImapEmailsQb->expr()->notIn('ie.uid', ':uids'))
139
                            ->setParameter('uids', $existingUids);
140
                    }
141
142
                    $staleImapEmails = (new BufferedQueryResultIterator($staleImapEmailsQb))
143
                        ->setPageCallback(function () {
144
                            $this->em->flush();
145
                            $this->em->clear();
146
                        });
147
148
                    /* @var $staleImapEmails ImapEmail[] */
149
                    foreach ($staleImapEmails as $imapEmail) {
150
                        $email = $imapEmail->getEmail();
151
                        $email->getEmailUsers()->forAll(function ($key, EmailUser $emailUser) use ($folder) {
152
                            $emailUser->removeFolder($folder);
153
                            if (!$emailUser->getFolders()->count()) {
154
                                $this->em->remove($emailUser);
155
                            }
156
                        });
157
                        $this->em->remove($imapEmail);
158
                    }
159
                });
160
            } catch (UnselectableFolderException $e) {
161
                $this->logger->info(
162
                    sprintf('The folder "%s" cannot be selected for remove email and was skipped.', $folderName)
163
                );
164
            }
165
        }
166
    }
167
168
    /**
169
     * @param ImapEmailFolder $imapFolder
170
     * @param \DateTime $startDate
171
     */
172
    protected function checkFlags(ImapEmailFolder $imapFolder, $startDate)
173
    {
174
        try {
175
            $uids = $this->manager->getUnseenEmailUIDs($startDate);
176
177
            $emailImapRepository = $this->em->getRepository('OroImapBundle:ImapEmail');
178
            $emailUserRepository = $this->em->getRepository('OroEmailBundle:EmailUser');
179
180
            $ids = $emailImapRepository->getEmailUserIdsByUIDs($uids, $imapFolder->getFolder(), $startDate);
181
            $invertedIds = $emailUserRepository->getInvertedIdsFromFolder($ids, $imapFolder->getFolder(), $startDate);
182
183
            $emailUserRepository->setEmailUsersSeen($ids, false);
184
            $emailUserRepository->setEmailUsersSeen($invertedIds, true);
185
        } catch (UnsupportException $e) {
186
            $this->logger->info(sprintf('Seen update unsupported - "%s"', $imapFolder->getFolder()->getOrigin()));
187
        }
188
    }
189
190
    /**
191
     * Deletes all empty outdated folders
192
     *
193
     * @param EmailOrigin $origin
194
     */
195
    protected function cleanupOutdatedFolders(EmailOrigin $origin)
196
    {
197
        $this->logger->info('Removing empty outdated folders ...');
198
199
        /** @var ImapEmailFolderRepository $repo */
200
        $repo        = $this->em->getRepository('OroImapBundle:ImapEmailFolder');
201
        $imapFolders = $repo->getEmptyOutdatedFoldersByOrigin($origin);
202
        $folders     = new ArrayCollection();
203
204
        foreach ($imapFolders as $imapFolder) {
205
            $this->logger->info(sprintf('Remove "%s" folder.', $imapFolder->getFolder()->getFullName()));
206
207
            if (!$folders->contains($imapFolder->getFolder())) {
208
                $folders->add($imapFolder->getFolder());
209
            }
210
211
            $this->em->remove($imapFolder);
212
        }
213
214
        foreach ($folders as $folder) {
215
            $this->em->remove($folder);
216
        }
217
218
        if (count($imapFolders) > 0) {
219
            $this->em->flush();
220
            $this->logger->info(sprintf('Removed %d folder(s).', count($imapFolders)));
221
        }
222
    }
223
224
    /**
225
     * Gets the list of IMAP folders already stored in a database
226
     * The outdated folders are ignored
227
     *
228
     * @param EmailOrigin $origin
229
     *
230
     * @return ImapEmailFolder[]
231
     */
232 View Code Duplication
    protected function getExistingImapFolders(EmailOrigin $origin)
233
    {
234
        $this->logger->info('Loading existing folders ...');
235
236
        /** @var ImapEmailFolderRepository $repo */
237
        $repo        = $this->em->getRepository('OroImapBundle:ImapEmailFolder');
238
        $imapFolders = $repo->getFoldersByOrigin($origin);
239
240
        $this->logger->info(sprintf('Loaded %d folder(s).', count($imapFolders)));
241
242
        return $imapFolders;
243
    }
244
245
    /**
246
     * Gets the list of IMAP folders enabled for sync
247
     * The outdated folders are ignored
248
     *
249
     * @param EmailOrigin $origin
250
     *
251
     * @return ImapEmailFolder[]
252
     */
253 View Code Duplication
    protected function getSyncEnabledImapFolders(EmailOrigin $origin)
254
    {
255
        $this->logger->info('Get folders enabled for sync...');
256
257
        /** @var ImapEmailFolderRepository $repo */
258
        $repo        = $this->em->getRepository('OroImapBundle:ImapEmailFolder');
259
        $imapFolders = $repo->getFoldersByOrigin($origin, false, EmailFolder::SYNC_ENABLED_TRUE);
260
261
        $this->logger->info(sprintf('Got %d folder(s).', count($imapFolders)));
262
263
        return $imapFolders;
264
    }
265
266
    /**
267
     * Gets all folders from IMAP server
268
     *
269
     * @return Folder[]
270
     */
271
    protected function getFolders()
272
    {
273
        $this->logger->info('Retrieving folders from an email server ...');
274
275
        $srcFolders = $this->manager->getFolders(null, true);
276
277
        $folders = [];
278
        foreach ($srcFolders as $srcFolder) {
279
            if (!$srcFolder->isSelectable()) {
280
                continue;
281
            }
282
            if ($srcFolder->hasFlag([Folder::FLAG_DRAFTS, Folder::FLAG_SPAM, Folder::FLAG_TRASH, Folder::FLAG_ALL])) {
283
                continue;
284
            }
285
286
            $folders[] = $srcFolder;
287
        }
288
289
        $this->logger->info(sprintf('Retrieved %d folder(s).', count($folders)));
290
291
        return $folders;
292
    }
293
294
    /**
295
     * Gets UIDVALIDITY of the given folder
296
     *
297
     * @param Folder $folder
298
     *
299
     * @return int
300
     */
301
    protected function getUidValidity(Folder $folder)
302
    {
303
        $this->manager->selectFolder($folder->getGlobalName());
304
305
        return $this->manager->getUidValidity();
306
    }
307
308
    /**
309
     * Performs synchronization of emails retrieved by the given search query in the given folder
310
     *
311
     * @param EmailOrigin $origin
312
     * @param ImapEmailFolder $imapFolder
313
     *
314
     * @return \DateTime The max sent date
315
     */
316
    protected function syncEmails(EmailOrigin $origin, ImapEmailFolder $imapFolder)
317
    {
318
        $folder             = $imapFolder->getFolder();
319
        $lastSynchronizedAt = $folder->getSynchronizedAt();
320
        $emails = $this->getEmailIterator($origin, $imapFolder, $folder);
321
        $count = $processed = $invalid = $totalInvalid = 0;
322
        $emails->setIterationOrder(true);
323
        $emails->setBatchSize(self::READ_BATCH_SIZE);
324
        $emails->setConvertErrorCallback(
325
            function (\Exception $e) use (&$invalid) {
326
                $invalid++;
327
                $this->logger->error(
328
                    sprintf('Error occurred while trying to process email: %s', $e->getMessage()),
329
                    ['exception' => $e]
330
                );
331
            }
332
        );
333
334
        $this->logger->info(sprintf('Found %d email(s).', $emails->count()));
335
336
        $batch = [];
337
        /** @var Email $email */
338
        foreach ($emails as $email) {
339
            $processed++;
340
            if ($processed % self::READ_HINT_COUNT === 0) {
341
                $this->logger->info(
342
                    sprintf(
343
                        'Processed %d of %d emails.%s',
344
                        $processed,
345
                        $emails->count(),
346
                        $invalid === 0 ? '' : sprintf(' Detected %d invalid email(s).', $invalid)
347
                    )
348
                );
349
                $totalInvalid += $invalid;
350
                $invalid = 0;
351
            }
352
353
            if ($email->getSentAt() > $lastSynchronizedAt) {
354
                $lastSynchronizedAt = $email->getSentAt();
355
            }
356
357
            $count++;
358
            $batch[] = $email;
359
            if ($count === self::DB_BATCH_SIZE) {
360
                $this->saveEmails(
361
                    $batch,
362
                    $imapFolder
363
                );
364
                $count = 0;
365
                $batch = [];
366
            }
367
        }
368
        if ($count > 0) {
369
            $this->saveEmails(
370
                $batch,
371
                $imapFolder
372
            );
373
        }
374
375
        $totalInvalid += $invalid;
376
        if ($totalInvalid > 0) {
377
            $this->logger->warning(
378
                sprintf('Detected %d invalid email(s) in "%s" folder.', $totalInvalid, $folder->getFullName())
379
            );
380
        }
381
382
        return $lastSynchronizedAt;
383
    }
384
385
    /**
386
     * Saves emails into the database
387
     *
388
     * @param Email[]         $emails
389
     * @param ImapEmailFolder $imapFolder
390
     */
391
    protected function saveEmails(array $emails, ImapEmailFolder $imapFolder)
392
    {
393
        $this->emailEntityBuilder->removeEmails();
394
395
        $folder        = $imapFolder->getFolder();
396
        $existingUids  = $this->getExistingUids($folder, $emails);
397
        $messageIds         = $this->getMessageIds($emails);
398
        $existingImapEmails = $this->getExistingImapEmails($folder->getOrigin(), $messageIds);
399
        $existingEmailUsers = $this->getExistingEmailUsers($folder, $messageIds);
400
        /** @var ImapEmail[] $newImapEmails */
401
        $newImapEmails = [];
402
        foreach ($emails as $email) {
403
            if (!$this->checkOnOldEmailForMailbox($folder, $email, $folder->getOrigin()->getMailbox())) {
404
                continue;
405
            }
406
            if (!$this->checkOnExistsSavedEmail($email, $existingUids)) {
407
                continue;
408
            }
409
410
            /** @var ImapEmail[] $relatedExistingImapEmails */
411
            $relatedExistingImapEmails = array_filter(
412
                $existingImapEmails,
413
                function (ImapEmail $imapEmail) use ($email) {
414
                    return $imapEmail->getEmail()->getMessageId() === $email->getMessageId();
415
                }
416
            );
417
418
            try {
419
                if (!isset($existingEmailUsers[$email->getMessageId()])) {
420
                    $emailUser = $this->addEmailUser(
421
                        $email,
422
                        $folder,
423
                        $email->hasFlag("\\Seen"),
424
                        $this->currentUser,
425
                        $this->currentOrganization
426
                    );
427
                } else {
428
                    $emailUser = $existingEmailUsers[$email->getMessageId()];
429
                    if (!$emailUser->getFolders()->contains($folder)) {
430
                        $emailUser->addFolder($folder);
431
                    }
432
                }
433
                $imapEmail = $this->createImapEmail($email->getId()->getUid(), $emailUser->getEmail(), $imapFolder);
434
                $newImapEmails[] = $imapEmail;
435
                $this->em->persist($imapEmail);
436
                $this->logger->notice(
437
                    sprintf(
438
                        'The "%s" (UID: %d) email was persisted.',
439
                        $email->getSubject(),
440
                        $email->getId()->getUid()
441
                    )
442
                );
443
            } catch (\Exception $e) {
444
                $this->logger->warning(
445
                    sprintf(
446
                        'Failed to persist "%s" (UID: %d) email. Error: %s',
447
                        $email->getSubject(),
448
                        $email->getId()->getUid(),
449
                        $e->getMessage()
450
                    )
451
                );
452
            }
453
454
            $this->removeEmailFromOutdatedFolders($relatedExistingImapEmails);
455
        }
456
457
        $this->emailEntityBuilder->getBatch()->persist($this->em);
458
459
        // update references if needed
460
        $changes = $this->emailEntityBuilder->getBatch()->getChanges();
461
        foreach ($newImapEmails as $imapEmail) {
462
            foreach ($changes as $change) {
463
                if ($change['old'] instanceof EmailEntity && $imapEmail->getEmail() === $change['old']) {
464
                    $imapEmail->setEmail($change['new']);
465
                }
466
            }
467
        }
468
        $this->em->flush();
469
470
        $this->cleanUp();
471
    }
472
473
    /**
474
     * Check allowing to save email by date
475
     *
476
     * @param EmailFolder $folder
477
     * @param Email $email
478
     * @param Mailbox $mailbox
479
     *
480
     * @return bool
481
     */
482
    protected function checkOnOldEmailForMailbox(EmailFolder $folder, Email $email, $mailbox)
483
    {
484
        /**
485
         * @description Will select max of those dates because emails in folder `sent` could have no received date
486
         *              or same date.
487
         */
488
        $dateForCheck = max($email->getReceivedAt(), $email->getSentAt());
489
490
        if ($mailbox && $folder->getSyncStartDate() > $dateForCheck) {
491
            $this->logger->info(
492
                sprintf(
493
                    'Skip "%s" (UID: %d) email, because it was sent earlier than the start synchronization is set',
494
                    $email->getSubject(),
495
                    $email->getId()->getUid()
496
                )
497
            );
498
499
            return false;
500
        }
501
502
        return true;
503
    }
504
505
    /**
506
     * Check allowing to save email by uid
507
     *
508
     * @param Email $email
509
     * @param array $existingUids
510
     *
511
     * @return bool
512
     */
513
    protected function checkOnExistsSavedEmail(Email $email, array $existingUids)
514
    {
515
        if (in_array($email->getId()->getUid(), $existingUids)) {
516
            $this->logger->info(
517
                sprintf(
518
                    'Skip "%s" (UID: %d) email, because it is already synchronised.',
519
                    $email->getSubject(),
520
                    $email->getId()->getUid()
521
                )
522
            );
523
            return false;
524
        }
525
526
        return true;
527
    }
528
529
    /**
530
     * Removes email from all outdated folders
531
     *
532
     * @param ImapEmail[] $imapEmails The list of all related IMAP emails
533
     */
534
    protected function removeEmailFromOutdatedFolders(array $imapEmails)
535
    {
536
        /** @var ImapEmail[] $outdatedImapEmails */
537
        $outdatedImapEmails = array_filter(
538
            $imapEmails,
539
            function (ImapEmail $imapEmail) {
540
                return $imapEmail->getImapFolder()->getFolder()->isOutdated();
541
            }
542
        );
543
        foreach ($outdatedImapEmails as $imapEmail) {
544
            $this->removeImapEmailReference($imapEmail);
545
        }
546
    }
547
548
    /**
549
     * Removes an email from a folder linked to the given IMAP email object
550
     *
551
     * @param ImapEmail $imapEmail
552
     */
553
    protected function removeImapEmailReference(ImapEmail $imapEmail)
554
    {
555
        $this->logger->info(
556
            sprintf(
557
                'Remove "%s" (UID: %d) email from "%s".',
558
                $imapEmail->getEmail()->getSubject(),
559
                $imapEmail->getUid(),
560
                $imapEmail->getImapFolder()->getFolder()->getFullName()
561
            )
562
        );
563
564
        $emailUser = $imapEmail->getEmail()->getEmailUserByFolder($imapEmail->getImapFolder()->getFolder());
565
        if ($emailUser != null) {
566
            $imapEmail->getEmail()->getEmailUsers()->removeElement($emailUser);
567
        }
568
        $this->em->remove($imapEmail);
569
    }
570
571
    /**
572
     * Gets the list of UIDs of emails already exist in a database
573
     *
574
     * @param EmailFolder $folder
575
     * @param Email[]     $emails
576
     *
577
     * @return int[] array if UIDs
578
     */
579
    protected function getExistingUids(EmailFolder $folder, array $emails)
580
    {
581
        if (empty($emails)) {
582
            return [];
583
        }
584
585
        $uids = array_map(
586
            function ($el) {
587
                /** @var Email $el */
588
                return $el->getId()->getUid();
589
            },
590
            $emails
591
        );
592
593
        /** @var ImapEmailRepository $repo */
594
        $repo = $this->em->getRepository('OroImapBundle:ImapEmail');
595
596
        return $repo->getExistingUids($folder, $uids);
597
    }
598
599
    /**
600
     * Gets the list of IMAP emails by Message-ID
601
     *
602
     * @param EmailOrigin $origin
603
     * @param string[]    $messageIds
604
     *
605
     * @return ImapEmail[]
606
     */
607
    protected function getExistingImapEmails(EmailOrigin $origin, array $messageIds)
608
    {
609
        if (empty($messageIds)) {
610
            return [];
611
        }
612
        /** @var ImapEmailRepository $repo */
613
        $repo = $this->em->getRepository('OroImapBundle:ImapEmail');
614
615
        return $repo->getEmailsByMessageIds($origin, $messageIds);
616
    }
617
618
    /**
619
     * Gets the list of Message-IDs for emails
620
     *
621
     * @param Email[] $emails
622
     *
623
     * @return string[]
624
     */
625
    protected function getMessageIds(array $emails)
626
    {
627
        $result = [];
628
        foreach ($emails as $email) {
629
            $result[] = $email->getMessageId();
630
        }
631
632
        return $result;
633
    }
634
635
    /**
636
     * Creates new ImapEmail object
637
     *
638
     * @param int             $uid
639
     * @param EmailEntity     $email
640
     * @param ImapEmailFolder $imapFolder
641
     *
642
     * @return ImapEmail
643
     */
644
    protected function createImapEmail($uid, EmailEntity $email, ImapEmailFolder $imapFolder)
645
    {
646
        $imapEmail = new ImapEmail();
647
        $imapEmail
648
            ->setUid($uid)
649
            ->setEmail($email)
650
            ->setImapFolder($imapFolder);
651
652
        return $imapEmail;
653
    }
654
655
    /**
656
     * Get email ids and create iterator
657
     *
658
     * @param EmailOrigin $origin
659
     * @param ImapEmailFolder $imapFolder
660
     * @param EmailFolder $folder
661
     *
662
     * @return ImapEmailIterator
663
     */
664
    protected function getEmailIterator(
665
        EmailOrigin $origin,
666
        ImapEmailFolder $imapFolder,
667
        EmailFolder $folder
668
    ) {
669
        $lastUid = $this->em->getRepository('OroImapBundle:ImapEmail')->findLastUidByFolder($imapFolder);
670
        if (!$lastUid && $origin->getMailbox() && $folder->getSyncStartDate()) {
671
            $emails = $this->initialMailboxSync($folder);
672
        } else {
673
            $this->logger->info(sprintf('Previous max email UID "%s"', $lastUid));
674
            $emails = $this->manager->getEmailsUidBased($lastUid);
675
        }
676
677
        return $emails;
678
    }
679
680
    /**
681
     * @param ImapEmail|null $existingImapEmail
682
     * @param bool $isMultiFolder
683
     * @param Email $email
684
     *
685
     * @return bool
686
     */
687
    protected function isMovableToOtherFolder($existingImapEmail, $isMultiFolder, $email)
688
    {
689
        return !$isMultiFolder
690
            && $existingImapEmail
691
            && $email->getId()->getUid() === $existingImapEmail->getUid();
692
    }
693
694
    /**
695
     * First system mailbox sync from sync start date
696
     *
697
     * @param EmailFolder $folder
698
     *
699
     * @return ImapEmailIterator
700
     */
701
    protected function initialMailboxSync(EmailFolder $folder)
702
    {
703
        // build search query for emails sync
704
        $sqb = $this->manager->getSearchQueryBuilder();
705
        if ($folder->getType() === FolderType::SENT) {
706
            $sqb->sent($folder->getSyncStartDate());
0 ignored issues
show
Documentation introduced by
$folder->getSyncStartDate() is of type object<DateTime>, but the function expects a string|null.

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...
707
        } else {
708
            $sqb->received($folder->getSyncStartDate());
0 ignored issues
show
Documentation introduced by
$folder->getSyncStartDate() is of type object<DateTime>, but the function expects a string|null.

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...
709
        }
710
        $searchQuery = $sqb->get();
711
        $this->logger->info(sprintf('Loading emails from "%s" folder ...', $folder->getFullName()));
712
        $this->logger->info(sprintf('Query: "%s".', $searchQuery->convertToSearchString()));
713
        $emails = $this->manager->getEmails($searchQuery);
714
715
        return $emails;
716
    }
717
}
718