src/Newsletter/ProviderHandler/Evalanche.php line 366

Open in your IDE?
  1. <?php
  2. namespace App\Newsletter\ProviderHandler;
  3. use App\Model\DataObject\Customer;
  4. use App\Newsletter\ProviderHandler\Evalanche\PoolExporter;
  5. use App\Newsletter\ProviderHandler\Evalanche\ProfileExporter;
  6. use CustomerManagementFrameworkBundle\ActivityManager\ActivityManagerInterface;
  7. use CustomerManagementFrameworkBundle\CustomerProvider\CustomerProviderInterface;
  8. use CustomerManagementFrameworkBundle\DataValidator\EmailValidator;
  9. use CustomerManagementFrameworkBundle\Model\Activity\MailchimpStatusChangeActivity;
  10. use CustomerManagementFrameworkBundle\Model\MailchimpAwareCustomerInterface;
  11. use CustomerManagementFrameworkBundle\Model\NewsletterAwareCustomerInterface;
  12. use CustomerManagementFrameworkBundle\Newsletter\ProviderHandler\NewsletterProviderHandlerInterface;
  13. use CustomerManagementFrameworkBundle\Newsletter\Queue\Item\DefaultNewsletterQueueItem;
  14. use CustomerManagementFrameworkBundle\Newsletter\Queue\Item\NewsletterQueueItemInterface;
  15. use CustomerManagementFrameworkBundle\Newsletter\Queue\NewsletterQueueInterface;
  16. use CustomerManagementFrameworkBundle\SegmentManager\SegmentManagerInterface;
  17. use CustomerManagementFrameworkBundle\Traits\LoggerAware;
  18. use Pimcore\File;
  19. use Pimcore\Log\ApplicationLogger;
  20. use Pimcore\Model\DataObject\CustomerSegmentGroup;
  21. use Pimcore\Model\DataObject\EvalancheAttributeValue;
  22. class Evalanche implements NewsletterProviderHandlerInterface
  23. {
  24.     use LoggerAware;
  25.     const PERMISSION_UNCONFIRMED '0';
  26.     const PERMISSION_OPT_IN '1';
  27.     const PERMISSION_DOUBLEOPT_IN '2';
  28.     const PERMISSION_OPT_OUT '3';
  29.     const STATUS_SUBSCRIBED 'subscribed';
  30.     const STATUS_UNSUBSCRIBED 'unsubscribed';
  31.     const STATUS_PENDING 'pending';
  32.     const ARTICLE_TEMPLATE_TYPE_EMAIL 33;
  33.     const ARTICLE_TEMPLATE_TYPE_TEXT 34;
  34.     const ARTICLE_TEMPLATE_TYPE_PDF 35;
  35.     const ARTICLE_TEMPLATE_TYPE_WEB_MOBILE 36;
  36.     const SALUTATION_FEMALE 1;
  37.     const SALUTATION_MALE 2;
  38.     const SALUTATION_FAMILY 3;
  39.     const SALUTATION_COMPANY 4;
  40.     const COUNTRYMAPPING = [
  41.         "-" => 0,
  42.         "DE" => 1,
  43.         "AT" => 2,
  44.         "AF" => 62,
  45.         "AL" => 63,
  46.         "DZ" => 64,
  47.         "AS" => 128,
  48.         "VI" => 129,
  49.         "Anderes" => 60,
  50.         "AD    " => 65,
  51.         "AO" => 66,
  52.         "AI" => 130,
  53.         "AQ" => 131,
  54.         "AG" => 132,
  55.         "AR" => 134,
  56.         "AM" => 67,
  57.         "AW" => 135,
  58.         "AU" => 115,
  59.         "AZ" => 68,
  60.         "BS" => 136,
  61.         "BH" => 69,
  62.         "BD" => 70,
  63.         "BB" => 137,
  64.         "BY" => 71,
  65.         "BE" => 15,
  66.         "BZ" => 138,
  67.         "BJ" => 72,
  68.         "BM" => 262,
  69.         "BT" => 139,
  70.         "BO" => 140,
  71.         "BA" => 29,
  72.         "BW" => 73,
  73.         "BV" => 142,
  74.         "BR" => 124,
  75.         "VG" => 143,
  76.         "IO" => 144,
  77.         "BN" => 145,
  78.         "BG" => 30,
  79.         "BF" => 74,
  80.         "BI" => 146,
  81.         "EA" => 147,
  82.         "CL" => 125,
  83.         "CN" => 32,
  84.         "CK" => 148,
  85.         "CR" => 149,
  86.         "CW" => 265,
  87.         "CI" => 150,
  88.         "CD" => 188,
  89.         "DM" => 151,
  90.         "DO" => 152,
  91.         "DJ" => 153,
  92.         "DK" => 16,
  93.         "EC" => 154,
  94.         "SV" => 155,
  95.         "ER" => 156,
  96.         "EE" => 34,
  97.         "FK" => 157,
  98.         "FJ" => 159,
  99.         "FI" => 17,
  100.         "FR" => 18,
  101.         "GF" => 160,
  102.         "PF" => 161,
  103.         "TF" => 162,
  104.         "FO" => 158,
  105.         "GA" => 163,
  106.         "GM" => 79,
  107.         "GE" => 80,
  108.         "GH" => 81,
  109.         "GI" => 164,
  110.         "GD" => 165,
  111.         "GR" => 19,
  112.         "GB" => 20,
  113.         "GL" => 166,
  114.         "GP" => 167,
  115.         "GU" => 168,
  116.         "GT" => 169,
  117.         "GG" => 170,
  118.         "GN" => 171,
  119.         "GW" => 172,
  120.         "GY" => 173,
  121.         "HT" => 174,
  122.         "HM" => 175,
  123.         "HN" => 176,
  124.         "HK" => 116,
  125.         "IN" => 82,
  126.         "ID" => 83,
  127.         "IM" => 177,
  128.         "IR" => 84,
  129.         "IQ" => 85,
  130.         "IE" => 21,
  131.         "IS" => 35,
  132.         "IL" => 36,
  133.         "IT" => 22,
  134.         "JM" => 178,
  135.         "JP" => 37,
  136.         "JE" => 179,
  137.         "JO" => 38,
  138.         "KY" => 180,
  139.         "KH" => 181,
  140.         "CM" => 75,
  141.         "CA" => 31,
  142.         "IC" => 182,
  143.         "CV" => 183,
  144.         "BQ" => 264,
  145.         "KZ" => 86,
  146.         "KE" => 87,
  147.         "KG" => 89,
  148.         "KI" => 184,
  149.         "CC" => 185,
  150.         "CO" => 186,
  151.         "KM" => 187,
  152.         "CG" => 77,
  153.         "XK    " => 263,
  154.         "HR" => 40,
  155.         "CU" => 189,
  156.         "KW" => 88,
  157.         "LA" => 190,
  158.         "LS" => 191,
  159.         "LV" => 41,
  160.         "LB" => 42,
  161.         "LR" => 192,
  162.         "LY" => 90,
  163.         "LI" => 91,
  164.         "LT" => 43,
  165.         "LU" => 23,
  166.         "MO" => 193,
  167.         "MG" => 194,
  168.         "MW" => 92,
  169.         "MY" => 121,
  170.         "MV" => 195,
  171.         "ML" => 196,
  172.         "MT" => 45,
  173.         "MA" => 95,
  174.         "MH" => 197,
  175.         "MQ" => 198,
  176.         "MR" => 199,
  177.         "MU" => 200,
  178.         "YT" => 201,
  179.         "MK" => 44,
  180.         "MX" => 122,
  181.         "FM" => 202,
  182.         "MD" => 93,
  183.         "MC" => 94,
  184.         "MN" => 203,
  185.         "ME" => 204,
  186.         "MS" => 205,
  187.         "MZ" => 96,
  188.         "MM" => 206,
  189.         "NA" => 207,
  190.         "NR" => 208,
  191.         "NP" => 97,
  192.         "NC" => 209,
  193.         "NZ" => 117,
  194.         "NI" => 210,
  195.         "NL" => 24,
  196.         "AN" => 211,
  197.         "NE    " => 98,
  198.         "NG" => 99,
  199.         "NU" => 212,
  200.         "KP" => 120,
  201.         "NF" => 214,
  202.         "NO" => 61,
  203.         "MP" => 213,
  204.         "OM" => 46,
  205.         "TL" => 215,
  206.         "PK" => 100,
  207.         "PW" => 216,
  208.         "PS" => 101,
  209.         "PA" => 217,
  210.         "PG" => 218,
  211.         "PY" => 219,
  212.         "PE" => 220,
  213.         "PH" => 221,
  214.         "PN" => 222,
  215.         "PL" => 47,
  216.         "PT    " => 25,
  217.         "PR" => 223,
  218.         "QA" => 102,
  219.         "RW" => 225,
  220.         "RO" => 48,
  221.         "RU" => 49,
  222.         "RE" => 224,
  223.         "BL" => 227,
  224.         "MF" => 228,
  225.         "SB" => 226,
  226.         "WS" => 229,
  227.         "SM" => 230,
  228.         "SA" => 50,
  229.         "SE" => 26,
  230.         "CH" => 3,
  231.         "SN" => 232,
  232.         "RS" => 39,
  233.         "SC" => 233,
  234.         "SL" => 234,
  235.         "SG" => 114,
  236.         "SX" => 267,
  237.         "Sk" => 51,
  238.         "SI" => 52,
  239.         "SO" => 235,
  240.         "ES" => 27,
  241.         "LK" => 103,
  242.         "SH" => 236,
  243.         "KN" => 237,
  244.         "LC" => 238,
  245.         "PM" => 239,
  246.         "VC" => 240,
  247.         "SD" => 104,
  248.         "SR" => 242,
  249.         "SJ" => 243,
  250.         "SZ" => 105,
  251.         "SY" => 106,
  252.         "ST" => 231,
  253.         "KR" => 118,
  254.         "ZA" => 126,
  255.         "GS" => 241,
  256.         "SS" => 266,
  257.         "TJ" => 244,
  258.         "TW" => 119,
  259.         "TZ" => 107,
  260.         "TH" => 245,
  261.         "TG" => 246,
  262.         "TK" => 247,
  263.         "TO" => 248,
  264.         "TT" => 249,
  265.         "TD" => 250,
  266.         "CZ" => 53,
  267.         "TN" => 55,
  268.         "TM" => 251,
  269.         "TC" => 252,
  270.         "TV    " => 253,
  271.         "TR    " => 54,
  272.         "US" => 57,
  273.         "UG" => 108,
  274.         "UA" => 109,
  275.         "HU" => 56,
  276.         "UM" => 254,
  277.         "UY" => 255,
  278.         "UZ" => 110,
  279.         "VU" => 256,
  280.         "VA" => 257,
  281.         "VE" => 258,
  282.         "AE" => 58,
  283.         "VN" => 123,
  284.         "WF" => 259,
  285.         "CX" => 260,
  286.         "EH" => 261,
  287.         "YE" => 111,
  288.         "ZM" => 112,
  289.         "CF" => 76,
  290.         "ZW" => 113,
  291.         "CY" => 59,
  292.         "EG" => 28,
  293.         "GQ" => 133,
  294.         "ET" => 78,
  295.         "AX" => 127,
  296.     ];
  297.     /**
  298.      * @var string
  299.      */
  300.     protected $shortcut$poolId$host$username$password;
  301.     /** @var \Scn\EvalancheSoapApiConnector\EvalancheConnectionInterface */
  302.     protected $connection;
  303.     /** @var ProfileExporter */
  304.     protected $profileExporter;
  305.     /** @var PoolExporter */
  306.     protected $poolExporter;
  307.     /**
  308.      * @var array
  309.      */
  310.     private $statusMapping;
  311.     /**
  312.      * @var array
  313.      */
  314.     private $reverseStatusMapping;
  315.     /**
  316.      * @var array
  317.      */
  318.     private $mergeFieldMapping;
  319.     /**
  320.      * @var array
  321.      */
  322.     private $fieldTransformers;
  323.     /**
  324.      * @var int
  325.      */
  326.     protected $batchThreshold 50;
  327.     /**
  328.      * @var SegmentManagerInterface
  329.      */
  330.     private $segmentManager;
  331.     /**
  332.      * @var array
  333.      */
  334.     private $confirmationMailingIDs;
  335.     /**
  336.      * Evalanche constructor.
  337.      * @param $shortcut
  338.      * @param $poolId
  339.      * @param $host
  340.      * @param $username
  341.      * @param $password
  342.      * @param array $confirmationMailingIDs
  343.      * @param array $statusMapping
  344.      * @param array $reverseStatusMapping
  345.      * @param array $mergeFieldMapping
  346.      * @param array $fieldTransformers
  347.      * @param SegmentManagerInterface $segmentManager
  348.      * @throws \Exception
  349.      */
  350.     public function __construct($shortcut$poolId$host$username$password, array $confirmationMailingIDs = [], array $statusMapping = [], array $reverseStatusMapping = [], array $mergeFieldMapping = [], array $fieldTransformers = [], SegmentManagerInterface $segmentManager)
  351.     {
  352.         if (!strlen($shortcut) || !File::getValidFilename($shortcut)) {
  353.             throw new \Exception('Please provide a valid newsletter provider handler shortcut.');
  354.         }
  355.         $this->shortcut $shortcut;
  356.         $this->poolId $poolId;
  357.         $this->host $host;
  358.         $this->username $username;
  359.         $this->password $password;
  360.         $this->statusMapping $statusMapping;
  361.         $this->reverseStatusMapping $reverseStatusMapping;
  362.         $this->mergeFieldMapping $mergeFieldMapping;
  363.         $this->fieldTransformers $fieldTransformers;
  364.         $this->segmentManager $segmentManager;
  365.         $this->confirmationMailingIDs $confirmationMailingIDs;
  366.     }
  367.     /**
  368.      * @return \Scn\EvalancheSoapApiConnector\EvalancheConnectionInterface
  369.      */
  370.     public function getConnection() {
  371.         if ($this->connection == '') {
  372.             $this->connection \Scn\EvalancheSoapApiConnector\EvalancheConnection::create(
  373.                 $this->host,
  374.                 $this->username,
  375.                 $this->password
  376.             );
  377.         }
  378.         return $this->connection;
  379.     }
  380.     /**
  381.      * @return string
  382.      */
  383.     public function getPoolId(): string
  384.     {
  385.         return $this->poolId;
  386.     }
  387.     protected function getProfileExporter() {
  388.         if ($this->profileExporter == '') {
  389.             $this->profileExporter = new ProfileExporter($this$this->mergeFieldMapping);
  390.         }
  391.         return $this->profileExporter;
  392.     }
  393.     protected function getPoolExporter() {
  394.         if ($this->poolExporter == '') {
  395.             $this->poolExporter = new PoolExporter($this);
  396.         }
  397.         return $this->poolExporter;
  398.     }
  399.     public function getShortcut()
  400.     {
  401.         return $this->shortcut;
  402.     }
  403.     public function processCustomerQueueItems(array $items$forceUpdate false)
  404.     {
  405.         $items $this->getUpdateNeededItems($items$forceUpdate);
  406.         list($emailChangedItems$regularItems) = $this->determineEmailChangedItems($items);
  407.         //Customers where the email address changed need to be handled by the single exporter as the batch exporter does not allow such operations.
  408.         if (count($emailChangedItems)) {
  409.             $this->getLogger()->info(
  410.                 sprintf(
  411.                     '[Evalanche][%s] process %s items where the email address changed...',
  412.                     $this->getShortcut(),
  413.                     count($emailChangedItems)
  414.                 )
  415.             );
  416.             foreach ($emailChangedItems as $item) {
  417.                 $this->customerExportSingle($item);
  418.             }
  419.         }
  420.         $itemCount count($regularItems);
  421.         if (!$itemCount) {
  422.             $this->getLogger()->info(
  423.                 sprintf(
  424.                     '[Evalanche][%s] 0 items to process...',
  425.                     $this->getShortcut()
  426.                 )
  427.             );
  428.         } elseif ($itemCount <= $this->batchThreshold) {
  429.             $this->getLogger()->info(
  430.                 sprintf(
  431.                     '[Evalanche][%s] Data count (%d) is below batch threshold (%d), sending one request per entry...',
  432.                     $this->getShortcut(),
  433.                     $itemCount,
  434.                     $this->batchThreshold
  435.                 )
  436.             );
  437.             foreach ($regularItems as $item) {
  438.                 $this->customerExportSingle($item);
  439.             }
  440.         } else {
  441.             $this->getLogger()->info(
  442.                 sprintf(
  443.                     '[Evalanche][%s] Sending data as batch request',
  444.                     $this->getShortcut()
  445.                 )
  446.             );
  447.             $this->customerExportBatch($regularItems);
  448.         }
  449.     }
  450.     public function updateSegmentGroups($forceUpdate false)
  451.     {
  452.         $groups $this->getExportableSegmentGroups();
  453.         $groupIds = [];
  454.         foreach ($groups as $group) {
  455.             $remoteGroupId $this->getPoolExporter()->exportGroup($group$this->poolIdfalse$forceUpdate);
  456.             $groupIds[] = $remoteGroupId;
  457.             $segments $this->segmentManager->getSegmentsFromSegmentGroup($group);
  458.             $this->getPoolExporter()->exportSegments($segments$this->poolId$remoteGroupId$forceUpdate);
  459.         }
  460. //        $this->getPoolExporter()->deleteNonExistingGroups($groupIds, $this->poolId); DO NOT DELETE NON EXISTING GROUPS AS WE CANNOT DIFFERENTIATE BETWEEN ATTRIBUTES CREATED BY US OR IN THE EVALANCHE SYSTEM
  461.     }
  462.     protected function getExportableSegmentGroups()
  463.     {
  464.         $fieldname 'exportNewsletterProvider' ucfirst($this->getShortcut());
  465.         $groups $this->segmentManager->getSegmentGroups();
  466.         $groups->addConditionParam($fieldname ' = 1');
  467.         return $groups;
  468.     }
  469.     public function subscribeCustomer(NewsletterAwareCustomerInterface $customer)
  470.     {
  471.         return $this->subscribeCustomerWithStatus($customerself::STATUS_SUBSCRIBED);
  472.     }
  473.     /**
  474.      * Directly Subscribes/exports a customer with given mailchimp status "subscribed" via the Mailchimp API.
  475.      *
  476.      * @param NewsletterAwareCustomerInterface $customer
  477.      * @return bool success
  478.      */
  479.     public function subscribeCustomerWithStatus(NewsletterAwareCustomerInterface $customerstring $status)
  480.     {
  481.         /**
  482.          * @var MailchimpAwareCustomerInterface $customer;
  483.          */
  484.         if (!$newsletterStatus $this->reverseMapNewsletterStatus($status)) {
  485.             $this->getLogger()->error(sprintf('subscribe failed: could not reverse map evalanche status %s'$status));
  486.             return false;
  487.         }
  488.         try {
  489.             $this->setNewsletterStatus($customer$newsletterStatus);
  490.             $item = new DefaultNewsletterQueueItem(
  491.                 $customer->getId(),
  492.                 $customer,
  493.                 $customer->getEmail(),
  494.                 NewsletterQueueInterface::OPERATION_UPDATE
  495.             );
  496.             $success $this->getProfileExporter()->update($customer$item$this);
  497.             if ($success) {
  498.                 $customer->saveWithOptions(
  499.                     $customer->getSaveManager()->getSaveOptions()
  500.                         ->disableNewsletterQueue()
  501.                         ->disableDuplicatesIndex()
  502.                         ->disableOnSaveSegmentBuilders()
  503.                 );
  504.             }
  505.         } catch (\Exception $e) {
  506.             $this->getLogger()->error('subscribe customer failed: '.$e->getMessage());
  507.             return false;
  508.         }
  509.         return $success;
  510.     }
  511.     /**
  512.      * Directly Subscribes/exports a customer with evalanche status "pending".
  513.      *
  514.      * @param NewsletterAwareCustomerInterface $customer
  515.      * @return bool success
  516.      */
  517.     public function subscribeCustomerPending(NewsletterAwareCustomerInterface $customer)
  518.     {
  519.         return $this->subscribeCustomerWithStatus($customerself::STATUS_PENDING);
  520.     }
  521.     public function unsubscribeCustomer(NewsletterAwareCustomerInterface $customer)
  522.     {
  523.         /**
  524.          * @var MailchimpAwareCustomerInterface $customer;
  525.          */
  526.         if (!$newsletterStatus $this->reverseMapNewsletterStatus(self::STATUS_UNSUBSCRIBED)) {
  527.             $this->getLogger()->error(sprintf('subscribe failed: could not reverse map evalanche status %s'self::STATUS_UNSUBSCRIBED));
  528.             return false;
  529.         }
  530.         try {
  531.             $this->setNewsletterStatus($customer$newsletterStatus);
  532.             $item = new DefaultNewsletterQueueItem(
  533.                 $customer->getId(),
  534.                 $customer,
  535.                 $customer->getEmail(),
  536.                 NewsletterQueueInterface::OPERATION_UPDATE
  537.             );
  538.             $success $this->getProfileExporter()->update($customer$item$this);
  539.             if ($success) {
  540.                 $customer->saveWithOptions(
  541.                     $customer->getSaveManager()->getSaveOptions()
  542.                         ->disableNewsletterQueue()
  543.                         ->disableDuplicatesIndex()
  544.                         ->disableOnSaveSegmentBuilders()
  545.                 );
  546.             }
  547.         } catch (\Exception $e) {
  548.             $this->getLogger()->error('unsubscribe customer failed: '.$e->getMessage());
  549.             return false;
  550.         }
  551.         return $success;
  552.     }
  553.     /**
  554.      * @param NewsletterQueueItemInterface[] $items
  555.      *
  556.      * @return array
  557.      */
  558.     protected function determineEmailChangedItems(array $items)
  559.     {
  560.         $emailChangedItems = [];
  561.         $regularItems = [];
  562.         foreach ($items as $item) {
  563.             if ($item->getOperation() != NewsletterQueueInterface::OPERATION_UPDATE) {
  564.                 $regularItems[] = $item;
  565.                 continue;
  566.             }
  567.             if (!$item->getCustomer()) {
  568.                 $regularItems[] = $item;
  569.                 continue;
  570.             }
  571.             if ($item->getCustomer()->getEmail() != $item->getEmail()) {
  572.                 $emailChangedItems[] = $item;
  573.                 continue;
  574.             }
  575.             $regularItems[] = $item;
  576.         }
  577.         return [$emailChangedItems$regularItems];
  578.     }
  579.     /**
  580.      * @param array $items
  581.      * @param bool $forceUpdate
  582.      * @return NewsletterQueueItemInterface[]
  583.      * @throws \Exception
  584.      */
  585.     protected function getUpdateNeededItems(array $items$forceUpdate false)
  586.     {
  587.         $updateNeededItems = [];
  588.         foreach ($items as $item) {
  589.             $emailValidator = new EmailValidator();
  590.             if ($item->getCustomer() && !$emailValidator->isValid($item->getCustomer()->getEmail()) && !$emailValidator->isValid(!$item->getEmail())) {
  591.                 $this->getLogger()->info(
  592.                     sprintf(
  593.                         '[Evalanche][CUSTOMER %s][%s] Export not needed as the customer has no valid email address.',
  594.                         $item->getCustomer() ? $item->getCustomer()->getId() : '',
  595.                         $this->getShortcut()
  596.                     )
  597.                 );
  598.                 $item->setSuccessfullyProcessed(true);
  599.             } elseif (!$item->getCustomer()) {
  600.                 $updateNeededItems[] = $item;
  601.             } elseif ($item->getOperation() == NewsletterQueueInterface::OPERATION_UPDATE) {
  602.                 if (!$item->getCustomer()->needsExportByNewsletterProviderHandler($this)) {
  603.                     /* Update item only if a mailchimp status is set in the customer.
  604.                        Otherwise the customer should not exist in the mailchimp list and therefore no deletion should be needed.
  605.                        Cleaned customers will be ignored as the email adress is invalid
  606.                     */
  607.                     $evalancheStatus $this->getEvalancheStatus($item->getCustomer());
  608.                     if ($evalancheStatus) {
  609.                         $updateNeededItems[] = $item;
  610.                     } else {
  611.                         $this->getLogger()->info(
  612.                             sprintf(
  613.                                 '[Evalanche][CUSTOMER %s][%s] Export not needed as the export data did not change (customer is not in export list).',
  614.                                 $item->getCustomer()->getId(),
  615.                                 $this->getShortcut()
  616.                             )
  617.                         );
  618.                         $item->setSuccessfullyProcessed(true);
  619.                     }
  620.                 } elseif ($forceUpdate || $this->getProfileExporter()->didExportDataChangeSinceLastExport($item->getCustomer(), $this->poolId$this->buildEntry($item->getCustomer()))) {
  621.                     $evalancheStatus $this->getEvalancheStatus($item->getCustomer());
  622.                     if (!$evalancheStatus) {
  623.                         $entry $this->buildEntry($item->getCustomer());
  624.                         $setStatus = isset($entry['status_if_new']) ?: $entry['status'];
  625.                         if ($setStatus == self::STATUS_UNSUBSCRIBED) {
  626.                             $this->getLogger()->info(
  627.                                 sprintf(
  628.                                     '[Evalanche][CUSTOMER %s][%s] Export not needed as the customer is unsubscribed and was not exported yet.',
  629.                                     $item->getCustomer()->getId(),
  630.                                     $this->getShortcut()
  631.                                 )
  632.                             );
  633.                             $item->setSuccessfullyProcessed(true);
  634.                         } else {
  635.                             $updateNeededItems[] = $item;
  636.                         }
  637.                     } else {
  638.                         $updateNeededItems[] = $item;
  639.                     }
  640.                 }
  641.             } else {
  642.                 $updateNeededItems[] = $item;
  643.             }
  644.         }
  645.         return $updateNeededItems;
  646.     }
  647.     public function setEvalancheStatus(NewsletterAwareCustomerInterface $customer$status)
  648.     {
  649.         $setter 'setEvalancheStatus' ucfirst($this->getShortcut());
  650.         if (!method_exists($customer$setter)) {
  651.             throw new \Exception(sprintf(
  652.                 'Customer needs to have a field %s in order to be able to hold the evalanche status for newsletter provider handler with shortcut %s',
  653.                 $setter,
  654.                 $this->getShortcut()
  655.             ));
  656.         }
  657.         $customer->$setter($status);
  658.     }
  659.     public function getEvalancheStatus(NewsletterAwareCustomerInterface $customer)
  660.     {
  661.         $getter 'getEvalancheStatus' ucfirst($this->getShortcut());
  662.         if (!method_exists($customer$getter)) {
  663.             throw new \Exception(sprintf(
  664.                 'Customer needs to have a field %s in order to be able to hold the evalanche status for newsletter provider handler with shortcut %s',
  665.                 $getter,
  666.                 $this->getShortcut()
  667.             ));
  668.         }
  669.         return $customer->$getter();
  670.     }
  671.     public function buildEntry(NewsletterAwareCustomerInterface $customer)
  672.     {
  673.         $mergeFieldsMapping count($this->mergeFieldMapping) ? $this->mergeFieldMapping : [
  674.             "firstname" => "FIRSTNAME",
  675.             "lastname" => "NAME"
  676.         ];
  677.         // build data array
  678.         $result = [
  679.             'EMAIL' => $customer->getEmail()
  680.         ];
  681.         foreach (array_keys($mergeFieldsMapping) as $field) {
  682.             $mapping $this->mapMergeField($field$customer);
  683.             $result[$mapping['field']] = $mapping['value'];
  684.         }
  685.         // add attribute values
  686.         $result $this->addAttributeValues($customer$result);
  687.         // add segment data
  688.         $result $this->addCustomerSegmentData($customer$result);
  689.         $result $this->addNewsletterStatusToEntry($customer$result);
  690. //        ApplicationLogger::getInstance()->info('[POOL ' . $this->getPoolId() . ']: building entry: ' . PHP_EOL . json_encode(['result' => $result, 'backtrace' => debug_backtrace()], JSON_PRETTY_PRINT), [
  691. //            'component' => 'Evalanche',
  692. //            'relatedObject' => $customer
  693. //        ]);
  694.         return $result;
  695.     }
  696.     public function getAttributesList() {
  697.         $mergeFieldsMapping sizeof($this->mergeFieldMapping) ? $this->mergeFieldMapping : [
  698.             "firstname" => "FIRSTNAME",
  699.             "lastname" => "NAME"
  700.         ];
  701.         return array_values($mergeFieldsMapping);
  702.     }
  703.     /**
  704.      * Maps Pimcore class field newsletterStatus to mailchimpNewsletterStatus
  705.      * @param NewsletterAwareCustomerInterface $customer
  706.      * @param array $entry
  707.      */
  708.     protected function addNewsletterStatusToEntry(NewsletterAwareCustomerInterface $customer, array $entry) {
  709.         $status $this->getNewsletterStatus($customer);
  710.         $evalancheStatus $this->getEvalancheStatus($customer);
  711.         if (!isset($this->statusMapping[$status])) {
  712.             $status self::STATUS_UNSUBSCRIBED;
  713.         } else {
  714.             $status $this->statusMapping[$status];
  715.         }
  716.         if (!$customer->needsExportByNewsletterProviderHandler($this)) {
  717.             $status null;
  718.         }
  719.         if ($status != $evalancheStatus) {
  720.             $entry['status'] = $status;
  721.         } else {
  722.             $entry['status_if_new'] = $status;
  723.         }
  724.         if ($status == self::STATUS_PENDING) {
  725.             $entry['PERMISSION'] = self::PERMISSION_UNCONFIRMED;
  726.             $entry['DELETED'] = 0;
  727.             $entry['UNSUBSCRIBE_DATE'] = 0;
  728.         } else if ($status == self::STATUS_SUBSCRIBED) {
  729.             $entry['PERMISSION'] = self::PERMISSION_DOUBLEOPT_IN;
  730.             $entry['DELETED'] = 0;
  731.             $entry['UNSUBSCRIBE_DATE'] = 0;
  732.         } else if ($status == self::STATUS_UNSUBSCRIBED) {
  733.             $entry['PERMISSION'] = self::PERMISSION_OPT_OUT;
  734.             $entry['DELETED'] = 1;
  735.             if ($status != $evalancheStatus) {
  736.                 $entry['UNSUBSCRIBE_DATE'] = time();
  737.             }
  738.         }
  739.         return $entry;
  740.     }
  741.     private function addCustomerSegmentData(NewsletterAwareCustomerInterface $customer, array $result)
  742.     {
  743.         $data = [];
  744.         foreach ($customer->getAllSegments() as $customerSegment) {
  745.             $remoteSegmentId $this->getPoolExporter()->getRemoteId($customerSegment$this->poolId);
  746.             $group $customerSegment->getGroup();
  747.             if(!$remoteSegmentId || !$group instanceof CustomerSegmentGroup) {
  748.                 continue;
  749.             }
  750.             $data[$group->getName()][] = $remoteSegmentId;
  751.         }
  752.         foreach ($this->getExportableSegmentGroups() as $segmentGroup) {
  753.             if (!empty($data[$segmentGroup->getName()])) {
  754.                 $result[PoolExporter::getValidAttributeName($segmentGroup->getName())] = implode('|'$data[$segmentGroup->getName()]);
  755.             } else {
  756.                 $result[PoolExporter::getValidAttributeName($segmentGroup->getName())] = '';
  757.             }
  758.         }
  759.         return $result;
  760.     }
  761.     private function addAttributeValues(NewsletterAwareCustomerInterface $customer, array $result) {
  762.         $attributeValuesPerAttribute = [];
  763.         if ($customer instanceof Customer) {
  764.             foreach($customer->getEvalancheAttributeValues() as $attributeValue) {
  765.                 if ($attributeValue->getPool() == $this->getPoolId()) {
  766.                     if (array_key_exists($attributeValue->getAttribute(), $attributeValuesPerAttribute)) {
  767.                         $attributeValuesPerAttribute[$attributeValue->getAttribute()][] = $attributeValue->getEvalancheId();
  768.                     } else {
  769.                         $attributeValuesPerAttribute[$attributeValue->getAttribute()] = [$attributeValue->getEvalancheId()];
  770.                     }
  771.                 }
  772.             }
  773.         }
  774.         foreach($this->getExportableAttributes() as $attribute) {
  775.             if (array_key_exists($attribute$attributeValuesPerAttribute) && !empty($attributeValuesPerAttribute[$attribute])) {
  776.                 $result[$attribute] = implode('|'$attributeValuesPerAttribute[$attribute]);
  777.             } else {
  778.                 $result[$attribute] = '';
  779.             }
  780.         }
  781.         return $result;
  782.     }
  783.     public function getExportableAttributes() {
  784.         $evalancheAtributeValues = new EvalancheAttributeValue\Listing();
  785.         $evalancheAtributeValues->addConditionParam('pool = :pool', ['pool' => $this->getPoolId()]);
  786.         $evalancheAtributeValues->setGroupBy('attribute');
  787.         $attributes = [];
  788.         foreach($evalancheAtributeValues as $attributeValue) {
  789.             if (!in_array($attributeValue->getAttribute(), $attributes)) {
  790.                 $attributes[] = $attributeValue->getAttribute();
  791.             }
  792.         }
  793.         return $attributes;
  794.     }
  795.     public function getSegmentGroupAttributes() {
  796.         $attributes = [];
  797.         foreach($this->getExportableSegmentGroups() as $segmentGroup) {
  798.             $attributes[] = PoolExporter::getValidAttributeName($segmentGroup->getName());
  799.         }
  800.         return $attributes;
  801.     }
  802.     /**
  803.      * @param $email
  804.      * @param int|false $customerId
  805.      * @return bool
  806.      */
  807.     public function doesOtherSubscribedCustomerWithEmailExist($email$customerId false)
  808.     {
  809.         if(!$email) {
  810.             return false;
  811.         }
  812.         $customerProvider $this->getCustomerProvider();
  813.         $list $customerProvider->getList();
  814.         $customerProvider->addActiveCondition($list);
  815.         if($customerId) {
  816.             $list->setCondition('trim(lower(email)) = ? and o_id != ?', [trim(strtolower($email)), $customerId]);
  817.         } else {
  818.             $list->setCondition('trim(lower(email)) = ?', [trim(strtolower($email))]);
  819.         }
  820.         foreach($list as $_customer) {
  821.             if(in_array($this->getEvalancheStatus($_customer), array(self::STATUS_PENDINGself::STATUS_SUBSCRIBED))) {
  822.                 return true;
  823.             }
  824.         }
  825.         return false;
  826.     }
  827.     protected function customerExportSingle(NewsletterQueueItemInterface $item)
  828.     {
  829.          $this->getProfileExporter()->singleExport($item$this);
  830.     }
  831.     /**
  832.      * @param NewsletterQueueItemInterface[] $items
  833.      */
  834.     protected function customerExportBatch(array $items)
  835.     {
  836.         $this->getProfileExporter()->batchExport($items$this);
  837.     }
  838.     /**
  839.      * @param $field
  840.      * @param NewsletterAwareCustomerInterface $customer
  841.      * @return array|false
  842.      */
  843.     public function mapMergeField($fieldNewsletterAwareCustomerInterface $customer)
  844.     {
  845.         $getter 'get' ucfirst($field);
  846.         $value $customer->$getter();
  847.         if (isset($this->mergeFieldMapping[$field])) {
  848.             $to $this->mergeFieldMapping[$field];
  849.             if ($to == 'COUNTRY') {
  850.                 $value self::getCountryNumberFromCountryCode($value);
  851.             }
  852.             if (isset($this->fieldTransformers[$field])) {
  853.                 $transformer $this->fieldTransformers[$field];
  854.                 $value $transformer->transformFromPimcoreToMailchimp($value);
  855.             }
  856.             $value is_null($value) ? '' $value;
  857.             return ['field' => $to'value' => $value];
  858.         }
  859.     }
  860.     /**
  861.      * @return array|false
  862.      */
  863.     public function reverseMapMergeField($field$value)
  864.     {
  865.         foreach ($this->mergeFieldMapping as $from => $to) {
  866.             if ($to == $field) {
  867.                 if ($from == 'COUNTRY') {
  868.                     $value self::getCountryCodeFromCountryNumber($value);
  869.                 }
  870.                 if (isset($this->fieldTransformers[$from])) {
  871.                     $transformer $this->fieldTransformers[$from];
  872.                     $value $transformer->transformFromMailchimpToPimcore($value);
  873.                 }
  874.                 return ['field' => $from'value' => $value];
  875.             }
  876.         }
  877.     }
  878.     /**
  879.      * @param string $pimcoreField
  880.      * @param mixed $pimcoreData
  881.      * @param mixed $evalancheImportData
  882.      */
  883.     public function didMergeFieldDataChange($pimcoreField$pimcoreData$evalancheImportData)
  884.     {
  885.         if (!isset($this->fieldTransformers[$pimcoreField])) {
  886.             return $pimcoreData != $evalancheImportData;
  887.         }
  888.         return $this->fieldTransformers[$pimcoreField]->didMergeFieldDataChange($pimcoreData$evalancheImportData);
  889.     }
  890.     /**
  891.      * Map evalanche status to pimcore object newsletterStatus
  892.      *
  893.      * @param $evalancheStatus
  894.      *
  895.      * @return mixed|null
  896.      */
  897.     public function reverseMapNewsletterStatus($evalancheStatus)
  898.     {
  899.         if (isset($this->reverseStatusMapping[$evalancheStatus])) {
  900.             return $this->reverseStatusMapping[$evalancheStatus];
  901.         }
  902.         return null;
  903.     }
  904.     public function getNewsletterStatus(NewsletterAwareCustomerInterface $customer)
  905.     {
  906.         $getter 'getNewsletterStatus' ucfirst($this->getShortcut());
  907.         if (!method_exists($customer$getter)) {
  908.             throw new \Exception(sprintf(
  909.                 'Customer needs to have a field %s in order to be able to hold the newsletter status for newsletter provider handler with shortcut %s',
  910.                 $getter,
  911.                 $this->getShortcut()
  912.             ));
  913.         }
  914.         return $customer->$getter();
  915.     }
  916.     public function setNewsletterStatus(NewsletterAwareCustomerInterface $customer$status)
  917.     {
  918.         $setter 'setNewsletterStatus' ucfirst($this->getShortcut());
  919.         if (!method_exists($customer$setter)) {
  920.             throw new \Exception(sprintf(
  921.                 'Customer needs to have a field %s in order to be able to hold the newsletter status for newsletter provider handler with shortcut %s',
  922.                 $setter,
  923.                 $this->getShortcut()
  924.             ));
  925.         }
  926.         $customer->$setter($status);
  927.     }
  928.     protected function trackStatusChangeActivity(NewsletterAwareCustomerInterface $customer$status)
  929.     {
  930.         $activity = new MailchimpStatusChangeActivity($customer$status, ['listId' => $this->getPoolId(), 'shortcut' => $this->getShortcut()]);
  931.         /**
  932.          * @var ActivityManagerInterface $activityManager
  933.          */
  934.         $activityManager \Pimcore::getContainer()->get('cmf.activity_manager');
  935.         $activityManager->trackActivity($activity);
  936.     }
  937.     public function updateEvalancheStatus(NewsletterAwareCustomerInterface $customer$status$saveCustomer true)
  938.     {
  939.         $getter 'getEvalancheStatus' ucfirst($this->getShortcut());
  940.         // status did not changed => no customer save needed
  941.         if ($customer->$getter() == $status) {
  942.             return;
  943.         }
  944.         $this->setEvalancheStatus($customer$status);
  945.         $this->trackStatusChangeActivity($customer$status);
  946.         if ($saveCustomer) {
  947.             /* The newsletter queue needs to be disabled to avoid endless loops.
  948.                Some other components are disabled for performance reasons as they are not needed here.
  949.                If somebody ever wants to build segments based on the evalanche status then they could be handled via the segment building queue.
  950.              */
  951.             $customer->saveWithOptions(
  952.                 $customer->getSaveManager()->getSaveOptions(true)
  953.                     ->disableNewsletterQueue()
  954.                     ->disableOnSaveSegmentBuilders()
  955.                     ->disableValidator()
  956.                     ->disableDuplicatesIndex()
  957.             );
  958.         }
  959.     }
  960.     /**
  961.      * @param null $language
  962.      * @return int|null
  963.      */
  964.     public function getConfirmationEmail($language null) {
  965.         if (!empty($this->confirmationMailingIDs)) {
  966.             return $this->confirmationMailingIDs[$language] ?: $this->confirmationMailingIDs['default'];
  967.         }
  968.         return null;
  969.     }
  970.     /**
  971.      * @param array $attributeNames
  972.      * @param int $timestampStart
  973.      * @param int $timestampEnd
  974.      * @return \Scn\EvalancheSoapStruct\Struct\Generic\HashMapInterface[]
  975.      * @throws \Scn\EvalancheSoapApiConnector\Exception\EmptyResultException
  976.      */
  977.     public function getModifiedProfiles(array $attributeNamesint $timestampStartint $timestampEnd) {
  978.         return $this->getConnection()->createProfileClient()->getModifiedProfiles($this->poolId$attributeNames$timestampStart$timestampEnd);
  979.     }
  980.     /**
  981.      * @param NewsletterAwareCustomerInterface $customer
  982.      * @return \Scn\EvalancheSoapStruct\Struct\Generic\HashMapInterface[]
  983.      * @throws \Scn\EvalancheSoapApiConnector\Exception\EmptyResultException
  984.      */
  985.     public function getProfileDataForCustomer(NewsletterAwareCustomerInterface $customer, array $attributesArray) {
  986.         return $this->getProfileExporter()->getEvalancheProfile($customer$attributesArray$this->poolId);
  987.     }
  988.     public function getAttributesFromPool() {
  989.         return $this->getConnection()->createPoolClient()->getAttributesByPool($this->poolId);
  990.     }
  991.     /**
  992.      * @param string|null $countryCode
  993.      * @return int
  994.      */
  995.     public static function getCountryNumberFromCountryCode(string $countryCode null) {
  996.         return array_key_exists($countryCodeself::COUNTRYMAPPING) ? self::COUNTRYMAPPING[$countryCode] : 0;
  997.     }
  998.     /**
  999.      * @param string|null $countryNumber
  1000.      * @return string|null
  1001.      */
  1002.     public static function getCountryCodeFromCountryNumber(string $countryNumber null) {
  1003.         foreach(self::COUNTRYMAPPING as $countryCode => $cNumber) {
  1004.             if ($cNumber == $countryNumber) {
  1005.                 return $countryCode == '-' null $countryCode;
  1006.             }
  1007.         }
  1008.         return null;
  1009.     }
  1010.     protected function getCustomerProvider(): CustomerProviderInterface
  1011.     {
  1012.         /**
  1013.          * @var CustomerProviderInterface $customerProvider
  1014.          */
  1015.         $customerProvider \Pimcore::getContainer()->get(CustomerProviderInterface::class);
  1016.         return $customerProvider;
  1017.     }
  1018. }