PageRenderTime 45ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/src/Backend/Modules/Mailmotor/Engine/CMHelper.php

http://github.com/forkcms/forkcms
PHP | 1092 lines | 599 code | 148 blank | 345 comment | 56 complexity | 30a2d9b2ebb341aafdaba042222daa5e MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, MIT, AGPL-3.0, LGPL-2.1, BSD-3-Clause
  1. <?php
  2. namespace Backend\Modules\Mailmotor\Engine;
  3. /*
  4. * This file is part of Fork CMS.
  5. *
  6. * For the full copyright and license information, please view the license
  7. * file that was distributed with this source code.
  8. */
  9. use Backend\Core\Engine\Exception as BackendException;
  10. use Backend\Core\Engine\Language as BL;
  11. use Backend\Core\Engine\Model as BackendModel;
  12. use Backend\Modules\Mailmotor\Engine\Model as BackendMailmotorModel;
  13. /**
  14. * In this file we store all generic functions that we will be using to communicate with CampaignMonitor
  15. */
  16. class CMHelper
  17. {
  18. /**
  19. * Checks if a valid CM account is set up
  20. *
  21. * @return bool
  22. */
  23. public static function checkAccount()
  24. {
  25. try {
  26. self::getCM();
  27. } catch (\Exception $e) {
  28. return false;
  29. }
  30. return true;
  31. }
  32. /**
  33. * Creates a new client
  34. *
  35. * @param string $companyName The client company name.
  36. * @param string $country This client’s country.
  37. * @param string $timezone Client timezone for tracking and reporting data.
  38. */
  39. public static function createClient(
  40. $companyName,
  41. $country = 'Belgium',
  42. $timezone = '(GMT+01:00) Brussels, Copenhagen, Madrid, Paris'
  43. ) {
  44. $clientId = self::getCM()->createClient($companyName, $country, $timezone);
  45. BackendModel::get('fork.settings')->set('Mailmotor', 'cm_client_id', $clientId);
  46. }
  47. /**
  48. * Creates a new custom field for a given group
  49. *
  50. * @param string $name The name of the custom field.
  51. * @param int $groupId The group ID you want to add the custom field for.
  52. *
  53. * @return bool
  54. */
  55. public static function createCustomField($name, $groupId)
  56. {
  57. $listId = self::getCampaignMonitorID('list', $groupId);
  58. if (!empty($listId)) {
  59. self::getCM()->createCustomField($name, 'text', null, $listId);
  60. return true;
  61. }
  62. return false;
  63. }
  64. /**
  65. * Deletes a custom field from a given group
  66. *
  67. * @param string $name The name of the custom field.
  68. * @param string $groupId The CampaignMonitor ID of the list you want to remove the custom field from.
  69. *
  70. * @return bool
  71. */
  72. public static function deleteCustomField($name, $groupId)
  73. {
  74. $listId = self::getCampaignMonitorID('list', $groupId);
  75. if (!empty($listId)) {
  76. self::getCM()->deleteCustomField($name, $listId);
  77. return true;
  78. }
  79. return false;
  80. }
  81. /**
  82. * Deletes one or more groups
  83. *
  84. * @param mixed $ids The ids to delete.
  85. *
  86. * @throws \CampaignMonitorException
  87. */
  88. public static function deleteGroups($ids)
  89. {
  90. $db = BackendModel::getContainer()->get('database');
  91. // if $ids is not an array, make one
  92. $ids = (!is_array($ids)) ? array($ids) : $ids;
  93. /*
  94. * I know this is messy, but since you can remove multiple groups at
  95. * the same time in the datagrid ànd remove the record in CM we need
  96. * to loop the ID's one by one
  97. */
  98. // loop the list
  99. foreach ($ids as $id) {
  100. // a list was deleted
  101. try {
  102. self::getCM()->deleteList(self::getCampaignMonitorID('list', $id));
  103. } catch (\CampaignMonitorException $e) {
  104. // if list doesn't exist anymore in CM, delete our list anyway
  105. if ($e->getMessage() != '400: Invalid ListID') {
  106. throw $e;
  107. }
  108. }
  109. BackendMailmotorModel::deleteGroups($id);
  110. $db->delete('mailmotor_campaignmonitor_ids', 'type = ? AND other_id = ?', array('list', $id));
  111. }
  112. }
  113. /**
  114. * Deletes one or more mailings
  115. *
  116. * @param mixed $ids The ids to delete.
  117. */
  118. public static function deleteMailings($ids)
  119. {
  120. // if $ids is not an array, make one
  121. $ids = (!is_array($ids)) ? array($ids) : $ids;
  122. /*
  123. * I know this is messy, but since you can remove multiple mailings at
  124. * the same time in the datagrid ànd remove the record in CM we need to
  125. * loop the ID's one by one
  126. */
  127. // loop the list
  128. foreach ($ids as $id) {
  129. /*
  130. Depending on when you call this method it may or may not trigger an exception due
  131. to emails no longer existing with CM. That's why this bit is in a try/catch.
  132. */
  133. try {
  134. self::getCM()->deleteCampaign(self::getCampaignMonitorID('campaign', $id));
  135. } catch (\Exception $e) {
  136. // ignore exception
  137. }
  138. BackendMailmotorModel::delete($id);
  139. }
  140. }
  141. /**
  142. * Returns all bounces
  143. *
  144. * @param int $id The id of the campaign.
  145. *
  146. * @return array
  147. */
  148. public static function getBounces($id)
  149. {
  150. $cmId = self::getCampaignMonitorID('campaign', $id);
  151. $bounces = self::getCM()->getCampaignBounces($cmId);
  152. $addresses = BackendMailmotorModel::getAddressesAsPairs();
  153. if (empty($bounces)) {
  154. return array();
  155. }
  156. // loop the bounces, check what bounces are still in our database
  157. foreach ($bounces as $key => $bounce) {
  158. // check if the bounced address is in the full list of addresses
  159. if (!in_array($bounce['email'], $addresses)) {
  160. unset($bounces[$key]);
  161. }
  162. }
  163. return (array) $bounces;
  164. }
  165. /**
  166. * Inserts a record into the mailmotor_campaignmonitor_ids table
  167. *
  168. * @param string $type The type to insert.
  169. * @param string $otherId The ID in our tables.
  170. *
  171. * @return string
  172. */
  173. public static function getCampaignMonitorID($type, $otherId)
  174. {
  175. return BackendModel::getContainer()->get('database')->getVar(
  176. 'SELECT cm_id
  177. FROM mailmotor_campaignmonitor_ids
  178. WHERE type = ? AND other_id = ?',
  179. array($type, $otherId)
  180. );
  181. }
  182. /**
  183. * Returns the CM IDs for a given list of group IDs
  184. *
  185. * @param array $groupIds The IDs of the groups.
  186. *
  187. * @return array
  188. */
  189. public static function getCampaignMonitorIDsForGroups(array $groupIds)
  190. {
  191. if (empty($groupIds)) {
  192. return array();
  193. }
  194. return (array) BackendModel::getContainer()->get('database')->getColumn(
  195. 'SELECT mci.cm_id
  196. FROM mailmotor_campaignmonitor_ids AS mci
  197. WHERE mci.type = ? AND mci.other_id IN (' . implode(',', $groupIds) . ')',
  198. array('list')
  199. );
  200. }
  201. /**
  202. * Returns the CM IDs for a given list of template IDs
  203. *
  204. * @param array $templateIds The ids of the templates.
  205. *
  206. * @return array
  207. */
  208. public static function getCampaignMonitorIDsForTemplates($templateIds)
  209. {
  210. $templates = (empty($templateIds)) ? array(BackendMailmotorModel::getDefaultTemplateID()) : $templateIds;
  211. return (array) BackendModel::getContainer()->get('database')->getColumn(
  212. 'SELECT mci.cm_id
  213. FROM mailmotor_campaignmonitor_ids AS mci
  214. WHERE mci.type = ? AND mci.other_id IN (' . implode(',', $templates) . ')',
  215. array('template')
  216. );
  217. }
  218. /**
  219. * Returns the client ID from the settings
  220. *
  221. * @return string
  222. */
  223. public static function getClientID()
  224. {
  225. return (string) BackendModel::get('fork.settings')->get('Mailmotor', 'cm_client_id');
  226. }
  227. /**
  228. * Returns the clients for use in a dropdown
  229. *
  230. * @return array
  231. */
  232. public static function getClientsAsPairs()
  233. {
  234. // get the base stack of clients
  235. $clients = self::getCM()->getClients();
  236. if (empty($clients)) {
  237. return array();
  238. }
  239. // reserve results stack
  240. $results = array();
  241. $results[0] = \SpoonFilter::ucfirst(BL::lbl('CreateNewClient', 'Mailmotor'));
  242. foreach ($clients as $client) {
  243. $results[$client['id']] = $client['name'];
  244. }
  245. return $results;
  246. }
  247. /**
  248. * Returns the CampaignMonitor object.
  249. *
  250. * @return \CampaignMonitor
  251. */
  252. public static function getCM()
  253. {
  254. // campaignmonitor reference exists
  255. if (!BackendModel::getContainer()->has('campaignmonitor')) {
  256. // check if the CampaignMonitor class exists
  257. if (!is_file(PATH_LIBRARY . '/external/campaignmonitor.php')) {
  258. // the class doesn't exist, so throw an exception
  259. throw new BackendException(BL::err('ClassDoesNotExist', 'Mailmotor'));
  260. }
  261. // require CampaignMonitor class
  262. require_once PATH_LIBRARY . '/external/campaignmonitor.php';
  263. // set login data
  264. $url = BackendModel::get('fork.settings')->get('Mailmotor', 'cm_url');
  265. $username = BackendModel::get('fork.settings')->get('Mailmotor', 'cm_username');
  266. $password = BackendModel::get('fork.settings')->get('Mailmotor', 'cm_password');
  267. // init CampaignMonitor object
  268. $cm = new \CampaignMonitor($url, $username, $password, 60, self::getClientId());
  269. // set CampaignMonitor object reference
  270. BackendModel::getContainer()->set('campaignmonitor', $cm);
  271. }
  272. return BackendModel::getContainer()->get('campaignmonitor');
  273. }
  274. /**
  275. * Returns the CampaignMonitor countries as pairs
  276. *
  277. * @return array
  278. */
  279. public static function getCountriesAsPairs()
  280. {
  281. $records = self::getCM()->getCountries();
  282. foreach ($records as &$record) {
  283. $records[$record] = $record;
  284. }
  285. return $records;
  286. }
  287. /**
  288. * Returns the custom fields by a given group ID
  289. *
  290. * @param int $groupId The id of the group.
  291. *
  292. * @return array
  293. */
  294. public static function getCustomFields($groupId)
  295. {
  296. $listId = self::getCampaignMonitorID('list', $groupId);
  297. $cmFields = self::getCM()->getCustomFields($listId);
  298. if (!empty($cmFields)) {
  299. $fields = BackendMailmotorModel::getCustomFields($groupId);
  300. foreach ($cmFields as $field) {
  301. if (!in_array($field['name'], $fields)) {
  302. $fields[] = $field['name'];
  303. }
  304. }
  305. BackendMailmotorModel::updateCustomFields($fields, $groupId);
  306. }
  307. return (array) $fields;
  308. }
  309. /**
  310. * Returns what addresses opened a certain mailing
  311. *
  312. * @param string $cmId The id of the mailing in CampaignMonitor.
  313. * @param bool $getColumn If this is true, it will return an array with just the email addresses
  314. * who opened the mailing.
  315. *
  316. * @return array
  317. */
  318. public static function getMailingOpens($cmId, $getColumn = false)
  319. {
  320. $records = self::getCM()->getCampaignOpens($cmId);
  321. if (empty($records)) {
  322. return false;
  323. }
  324. if (!$getColumn) {
  325. return (array) $records;
  326. }
  327. // new result stack
  328. $results = array();
  329. // loop the records, save emails to new result stack
  330. foreach ($records as $record) {
  331. $results[] = $record['email'];
  332. }
  333. return (array) $results;
  334. }
  335. /**
  336. * Get the custom field value for 'name'
  337. *
  338. * @param array $fields The custom fields array.
  339. *
  340. * @return string
  341. */
  342. private static function getNameFieldValue($fields)
  343. {
  344. $name = null;
  345. // set the name if it is present in the custom fields
  346. if (isset($fields['name'])) {
  347. $name = $fields['name'];
  348. } elseif (isset($fields['Name'])) {
  349. $name = $fields['Name'];
  350. } elseif (isset($fields[BL::lbl('Name')])) {
  351. $name = $fields[BL::lbl('Name')];
  352. }
  353. return $name;
  354. }
  355. /**
  356. * Returns the statistics for a given mailing
  357. *
  358. * @param int $id The id of the mailing.
  359. * @param bool $fetchClicks If the click-count should be included.
  360. * @param bool $fetchOpens If the open-count should be included.
  361. *
  362. * @return array
  363. */
  364. public static function getStatistics($id, $fetchClicks = false, $fetchOpens = false)
  365. {
  366. if (!BackendMailmotorModel::existsMailing($id)) {
  367. throw new \SpoonException('No mailing found for id ' . $id);
  368. }
  369. $cmId = self::getCampaignMonitorID('campaign', $id);
  370. if ($cmId) {
  371. $stats = self::getCM()->getCampaignSummary($cmId);
  372. if ($stats['recipients'] == 0) {
  373. return false;
  374. }
  375. $bounces = self::getBounces($id);
  376. // re-calculate base stats to match CM's
  377. $stats['bounces'] = count($bounces);
  378. $stats['recipients'] = ($stats['recipients']);
  379. $stats['recipients_total'] = ($stats['recipients']);
  380. $stats['unopens'] = $stats['recipients'] - $stats['unique_opens'] - $stats['bounces'];
  381. $stats['clicks_total'] = 0;
  382. // add percentages to these stats
  383. $stats['bounces_percentage'] = ($stats['recipients'] == 0) ? 0 : floor(
  384. ($stats['bounces'] / $stats['recipients_total']) * 100
  385. ) . '%';
  386. $stats['recipients_percentage'] = ($stats['recipients'] == 0) ? 0 : ceil(
  387. ($stats['recipients'] / $stats['recipients_total']) * 100
  388. ) . '%';
  389. $stats['unique_opens_percentage'] = ($stats['recipients'] == 0) ? 0 : ceil(
  390. ($stats['unique_opens'] / $stats['recipients']) * 100
  391. ) . '%';
  392. $stats['unopens_percentage'] = ($stats['recipients'] == 0) ? 0 : floor(
  393. ($stats['unopens'] / $stats['recipients']) * 100
  394. ) . '%';
  395. $stats['clicks_percentage'] = ($stats['recipients'] == 0) ? 0 : ceil(
  396. ($stats['clicks'] / $stats['recipients']) * 100
  397. ) . '%';
  398. // fetch clicks or not?
  399. if ($fetchClicks) {
  400. $subscriberClicks = self::getCM()->getCampaignClicks($cmId);
  401. // links have been clicked
  402. if (!empty($subscriberClicks)) {
  403. $stats['clicked_links'] = array();
  404. $stats['clicked_links_by'] = $subscriberClicks;
  405. // filter out the clicked links
  406. foreach ($subscriberClicks as $link => $clickers) {
  407. $clickerCount = count($clickers);
  408. $stats['clicked_links'][] = array('link' => $link, 'clicks' => $clickerCount);
  409. $stats['clicks_total'] += $clickerCount;
  410. }
  411. }
  412. }
  413. // fetch opened stats or not?
  414. if ($fetchOpens) {
  415. $stats['opens'] = self::getMailingOpens($cmId);
  416. }
  417. return $stats;
  418. }
  419. return false;
  420. }
  421. /**
  422. * Returns the statistics for a given e-mail address
  423. *
  424. * @param string $email The emailaddress to get the stats for.
  425. *
  426. * @return array
  427. */
  428. public static function getStatisticsByAddress($email)
  429. {
  430. // reserve statistics array
  431. $stats = array();
  432. $stats['recipients'] = 0;
  433. $stats['opens'] = 0;
  434. $stats['unique_opens'] = 0;
  435. $stats['clicks'] = 0;
  436. $stats['unsubscribes'] = 0;
  437. $stats['bounces'] = 0;
  438. $stats['recipients_total'] = 0;
  439. $stats['unopens'] = 0;
  440. // fetch all mailings in this campaign
  441. $mailings = BackendModel::getContainer()->get('database')->getRecords(
  442. BackendMailmotorModel::QRY_DATAGRID_BROWSE_SENT_FOR_CAMPAIGN,
  443. array('sent', $email)
  444. );
  445. if (empty($mailings)) {
  446. return array();
  447. }
  448. foreach ($mailings as $mailing) {
  449. $mailingStats = self::getStatistics($mailing['id']);
  450. // add all stats to the totals
  451. $stats['recipients'] += $mailingStats['recipients'];
  452. $stats['opens'] += $mailingStats['opens'];
  453. $stats['unique_opens'] += $mailingStats['unique_opens'];
  454. $stats['clicks'] += $mailingStats['clicks'];
  455. $stats['unsubscribes'] += $mailingStats['unsubscribes'];
  456. $stats['bounces'] += $mailingStats['bounces'];
  457. $stats['recipients_total'] += $mailingStats['recipients_total'];
  458. $stats['unopens'] += $mailingStats['unopens'];
  459. }
  460. // add percentages to these stats
  461. $stats['bounces_percentage'] = floor(($stats['bounces'] / $stats['recipients_total']) * 100) . '%';
  462. $stats['recipients_percentage'] = ceil(($stats['recipients'] / $stats['recipients_total']) * 100) . '%';
  463. $stats['unique_opens_percentage'] = ceil(($stats['unique_opens'] / $stats['recipients']) * 100) . '%';
  464. $stats['unopens_percentage'] = floor(($stats['unopens'] / $stats['recipients']) * 100) . '%';
  465. $stats['clicks_percentage'] = ceil(($stats['clicks'] / $stats['recipients']) * 100) . '%';
  466. return (array) $stats;
  467. }
  468. /**
  469. * Returns the statistics for all mailings in a given campaign
  470. *
  471. * @param int $id The id of the campaign.
  472. *
  473. * @return array
  474. */
  475. public static function getStatisticsByCampaignID($id)
  476. {
  477. // reserve statistics array
  478. $stats = array();
  479. $stats['recipients'] = 0;
  480. $stats['opens'] = 0;
  481. $stats['unique_opens'] = 0;
  482. $stats['clicks'] = 0;
  483. $stats['unsubscribes'] = 0;
  484. $stats['bounces'] = 0;
  485. $stats['recipients_total'] = 0;
  486. $stats['unopens'] = 0;
  487. // fetch all mailings in this campaign
  488. $mailings = BackendModel::getContainer()->get('database')->getRecords(
  489. BackendMailmotorModel::QRY_DATAGRID_BROWSE_SENT_FOR_CAMPAIGN,
  490. array('sent', $id)
  491. );
  492. if (empty($mailings)) {
  493. return array();
  494. }
  495. foreach ($mailings as $mailing) {
  496. // store the statistics in a separate array
  497. $mailingStats = self::getStatistics($mailing['id'], false);
  498. // add all stats to the totals
  499. $stats['recipients'] += $mailingStats['recipients'];
  500. $stats['opens'] += $mailingStats['opens'];
  501. $stats['unique_opens'] += $mailingStats['unique_opens'];
  502. $stats['clicks'] += $mailingStats['clicks'];
  503. $stats['unsubscribes'] += $mailingStats['unsubscribes'];
  504. $stats['bounces'] += $mailingStats['bounces'];
  505. $stats['recipients_total'] += $mailingStats['recipients_total'];
  506. $stats['unopens'] += $mailingStats['unopens'];
  507. }
  508. // add percentages to these stats
  509. $stats['bounces_percentage'] = floor(($stats['bounces'] / $stats['recipients_total']) * 100) . '%';
  510. $stats['recipients_percentage'] = ceil(($stats['recipients'] / $stats['recipients_total']) * 100) . '%';
  511. $stats['unique_opens_percentage'] = ceil(($stats['unique_opens'] / $stats['recipients']) * 100) . '%';
  512. $stats['unopens_percentage'] = floor(($stats['unopens'] / $stats['recipients']) * 100) . '%';
  513. $stats['clicks_percentage'] = ceil(($stats['clicks'] / $stats['recipients']) * 100) . '%';
  514. return (array) $stats;
  515. }
  516. /**
  517. * Returns all subscribers, regardless of the page limit CM gives us.
  518. *
  519. * @param string $listId The list ID to get the subscribers from.
  520. *
  521. * @return array
  522. */
  523. public static function getSubscribers($listId)
  524. {
  525. // get list statistics, so we can obtain the total subscribers for this list
  526. $listStats = self::getCM()->getListStatistics($listId);
  527. // pagecount is calculated by getting the total amount of subscribers
  528. // divided by 1k, which is the return limit for CM's getSubscribers()
  529. $pageCount = (int) ceil($listStats['total_subscribers'] / 1000);
  530. // reserve a result stack
  531. $results = array();
  532. // check if we have at least 1 page
  533. if ($listStats['total_subscribers'] !== 0) {
  534. // set the pagecount to 1 by default
  535. ++$pageCount;
  536. // loop the total amount of pages and fetch the subscribers accordingly
  537. for ($i = $pageCount; $i != 0; --$i) {
  538. $subscribers = self::getCM()->getSubscribers($listId, null, $i, 1000);
  539. $results = array_merge($results, $subscribers);
  540. }
  541. }
  542. return $results;
  543. }
  544. /**
  545. * Returns the CampaignMonitor countries as pairs
  546. *
  547. * @return array
  548. */
  549. public static function getTimezonesAsPairs()
  550. {
  551. $records = self::getCM()->getTimezones();
  552. foreach ($records as &$record) {
  553. $records[$record] = $record;
  554. }
  555. return $records;
  556. }
  557. /**
  558. * Inserts a record into the mailmotor_campaignmonitor_ids table
  559. *
  560. * @param string $type The type of the record.
  561. * @param string $id The id in CampaignMonitor.
  562. * @param string $otherId The id in our tables.
  563. *
  564. * @return string
  565. */
  566. public static function insertCampaignMonitorID($type, $id, $otherId)
  567. {
  568. $type = \SpoonFilter::getValue($type, array('campaign', 'list', 'template'), '');
  569. if ($type == '') {
  570. throw new \CampaignMonitorException('No valid CM ID type given (only campaign, list, template).');
  571. }
  572. BackendModel::getContainer()->get('database')->insert(
  573. 'mailmotor_campaignmonitor_ids',
  574. array('type' => $type, 'cm_id' => $id, 'other_id' => $otherId)
  575. );
  576. }
  577. /**
  578. * Creates a list in campaignmonitor and inserts the group record in the database. Returns the group ID
  579. *
  580. * @param array $item The group record to insert.
  581. *
  582. * @return int
  583. */
  584. public static function insertGroup(array $item)
  585. {
  586. // build unsubscribe link for this list
  587. $unsubscribeLink = SITE_URL .
  588. BackendModel::getURLForBlock(
  589. 'Mailmotor',
  590. 'unsubscribe',
  591. BL::getWorkingLanguage()
  592. );
  593. // predict the next insert ID for the mailmotor_groups table
  594. $groupId = BackendMailmotorModel::getMaximumIdForGroups() + 1;
  595. $cmId = self::getCM()->createList($item['name'], $unsubscribeLink . '/?group=' . $groupId . '&email=[email]');
  596. // a list was created
  597. if ($cmId) {
  598. // check if we have a default group set
  599. if ($item['is_default'] === 'Y' && $item['language'] != '0') {
  600. // set all defaults to N.
  601. BackendModel::getContainer()->get('database')->update(
  602. 'mailmotor_groups',
  603. array('is_default' => 'N', 'language' => null),
  604. 'language = ?',
  605. $item['language']
  606. );
  607. }
  608. $id = BackendMailmotorModel::insertGroup($item);
  609. self::insertCampaignMonitorID('list', $cmId, $id);
  610. return (int) $id;
  611. }
  612. }
  613. /**
  614. * Creates a campaign in campaignmonitor. Returns the campaign ID
  615. *
  616. * @param array $item The mailing record to insert.
  617. *
  618. * @return string|false
  619. */
  620. public static function insertMailing(array $item)
  621. {
  622. // create campaign in CM
  623. $cmId = self::getCM()->createCampaign(
  624. $item['name'],
  625. $item['subject'],
  626. $item['from_name'],
  627. $item['from_email'],
  628. $item['reply_to_email'],
  629. $item['content_html_url'],
  630. $item['content_plain_url'],
  631. $item['group_cm_ids']
  632. );
  633. // a campaign was created
  634. if ($cmId) {
  635. self::insertCampaignMonitorID('campaign', $cmId, $item['id']);
  636. return $cmId;
  637. }
  638. return false;
  639. }
  640. /**
  641. * Creates a campaign draft into campaignmonitor.
  642. *
  643. * @param array $item The mailing record to update a campaign draft.
  644. *
  645. * @return string The campaign ID of the newly created draft.
  646. */
  647. public static function insertMailingDraft(array $item)
  648. {
  649. // get the preview URLs, so CM knows where to get the HTML/plaintext content
  650. $item['content_html_url'] = BackendMailmotorModel::getMailingPreviewURL($item['id'], 'html', true);
  651. $item['content_plain_url'] = BackendMailmotorModel::getMailingPreviewURL($item['id'], 'plain', true);
  652. // get the CM IDs for all groups linked to the mailing record
  653. if (!isset($item['group_cm_ids'])) {
  654. $item['group_cm_ids'] = self::getCampaignMonitorIDsForGroups($item['groups']);
  655. }
  656. // create the campaign ID, and obtain the campaign CM ID
  657. // if we add a timestamp to the name, we won't get the duplicate campaign name errors.
  658. $campaignID = self::getCM()->createCampaign(
  659. $item['name'] . ' - ' . time(),
  660. $item['subject'],
  661. $item['from_name'],
  662. $item['from_email'],
  663. $item['reply_to_email'],
  664. $item['content_html_url'],
  665. $item['content_plain_url'],
  666. $item['group_cm_ids']
  667. );
  668. // if we received a valid CM ID, insert the CM ID in the database
  669. if (is_string($campaignID)) {
  670. self::insertCampaignMonitorID('campaign', $campaignID, $item['id']);
  671. }
  672. return $campaignID;
  673. }
  674. /**
  675. * Saves a draft mailing into campaignmonitor
  676. *
  677. * @param array $item The mailing record to create/update a campaign draft.
  678. *
  679. * @return string The newly created campaignmonitor ID
  680. */
  681. public static function saveMailingDraft(array $item)
  682. {
  683. // get the campaignmonitor ID for campaign
  684. $campaignID = self::getCampaignMonitorID('campaign', $item['id']);
  685. // either insert/update a draft, depends if we found a valid campaign ID or not
  686. if (!$campaignID) {
  687. return self::insertMailingDraft($item);
  688. } else {
  689. return self::updateMailingDraft($item);
  690. }
  691. }
  692. /**
  693. * Creates a campaign in campaignmonitor and sends it
  694. *
  695. * @param array $item The mailing record to insert.
  696. */
  697. public static function sendMailing($item)
  698. {
  699. // fetch the CM IDs for each group if this field is not set yet
  700. if (!isset($item['group_cm_ids'])) {
  701. $item['group_cm_ids'] = self::getCampaignMonitorIDsForGroups(
  702. $item['groups']
  703. );
  704. }
  705. // fetch the content URLs
  706. if (!isset($item['content_html_url'])) {
  707. $item['content_html_url'] = BackendMailmotorModel::getMailingPreviewURL(
  708. $item['id'],
  709. 'html',
  710. true
  711. );
  712. }
  713. if (!isset($item['content_plain_url'])) {
  714. $item['content_plain_url'] = BackendMailmotorModel::getMailingPreviewURL(
  715. $item['id'],
  716. 'plain',
  717. true
  718. );
  719. }
  720. // at this point $result should equal the CM ID, so let's attempt to send it
  721. self::getCM()->sendCampaign($item['cm_id'], $item['from_email'], $item['delivery_date']);
  722. }
  723. /**
  724. * Creates a campaign in campaignmonitor and sends it
  725. *
  726. * @param int $id The ID of the mailing
  727. * @param string $recipient The e-mail address to send a preview mailing to.
  728. */
  729. public static function sendPreviewMailing($id, $recipient)
  730. {
  731. $campaignID = self::getCampaignMonitorID('campaign', $id);
  732. self::getCM()->sendCampaignPreview($campaignID, $recipient);
  733. }
  734. /**
  735. * Subscribes an e-mail address and send him/her to CampaignMonitor
  736. *
  737. * @param string $email The emailaddress.
  738. * @param string $groupId The group wherein the emailaddress should be added.
  739. * @param array $customFields Any optional custom fields.
  740. *
  741. * @return bool
  742. */
  743. public static function subscribe($email, $groupId = null, $customFields = null)
  744. {
  745. $db = BackendModel::getContainer()->get('database');
  746. $cm = self::getCM();
  747. $groupId = !empty($groupId) ? $groupId : BackendMailmotorModel::getDefaultGroupID();
  748. $groupCMId = self::getCampaignMonitorID('list', $groupId);
  749. // see if the name is present in the custom fields
  750. $name = self::getNameFieldValue($customFields);
  751. // group ID found
  752. if (BackendMailmotorModel::existsGroup($groupId) &&
  753. $cm->subscribe($email, $name, $customFields, true, $groupCMId)
  754. ) {
  755. $subscriber['email'] = $email;
  756. $subscriber['source'] = 'CMS';
  757. $subscriber['created_on'] = BackendModel::getUTCDate('Y-m-d H:i:s');
  758. $db->execute(
  759. 'INSERT INTO mailmotor_addresses(email, source, created_on)
  760. VALUES (?, ?, ?)
  761. ON DUPLICATE KEY UPDATE source = ?, created_on = ?',
  762. array(
  763. $subscriber['email'],
  764. $subscriber['source'],
  765. $subscriber['created_on'],
  766. $subscriber['source'],
  767. $subscriber['created_on'],
  768. )
  769. );
  770. $subscriberGroup['email'] = $email;
  771. $subscriberGroup['group_id'] = $groupId;
  772. $subscriberGroup['status'] = 'subscribed';
  773. $subscriberGroup['subscribed_on'] = BackendModel::getUTCDate('Y-m-d H:i:s');
  774. // insert/update the user
  775. $db->execute(
  776. 'INSERT INTO mailmotor_addresses_groups(email, group_id, status, subscribed_on)
  777. VALUES (?, ?, ?, ?)
  778. ON DUPLICATE KEY UPDATE group_id = ?, status = ?, subscribed_on = ?',
  779. array(
  780. $subscriberGroup['email'],
  781. $subscriberGroup['group_id'],
  782. $subscriberGroup['status'],
  783. $subscriberGroup['subscribed_on'],
  784. $subscriberGroup['group_id'],
  785. $subscriberGroup['status'],
  786. $subscriberGroup['subscribed_on'],
  787. )
  788. );
  789. // update custom fields for this subscriber/group
  790. if (!empty($customFields)) {
  791. BackendMailmotorModel::updateCustomFields($customFields, $groupId, $email);
  792. }
  793. return true;
  794. }
  795. return false;
  796. }
  797. /**
  798. * Unsubscribes an e-mail address from CampaignMonitor and our database
  799. *
  800. * @param string $email The emailaddress to unsubscribe.
  801. * @param string $groupId The group wherefrom the emailaddress should be unsubscribed.
  802. *
  803. * @return bool
  804. */
  805. public static function unsubscribe($email, $groupId = null)
  806. {
  807. $groupId = !empty($groupId) ? $groupId : BackendMailmotorModel::getDefaultGroupID();
  808. $groupCMId = self::getCampaignMonitorID('list', $groupId);
  809. // group exists
  810. if (BackendMailmotorModel::existsGroup($groupId)) {
  811. self::getCM()->unsubscribe($email, $groupCMId);
  812. $subscriber = array();
  813. $subscriber['status'] = 'unsubscribed';
  814. $subscriber['unsubscribed_on'] = BackendModel::getUTCDate('Y-m-d H:i:s');
  815. BackendModel::getContainer()->get('database')->update(
  816. 'mailmotor_addresses_groups',
  817. $subscriber,
  818. 'email = ? AND group_id = ?',
  819. array($email, $groupId)
  820. );
  821. return true;
  822. }
  823. return false;
  824. }
  825. /**
  826. * Updates a client
  827. *
  828. * @param string $companyName The client company name.
  829. * @param string $country This client’s country.
  830. * @param string $timezone Client timezone for tracking and reporting data.
  831. */
  832. public static function updateClient(
  833. $companyName,
  834. $country = 'Belgium',
  835. $timezone = '(GMT+01:00) Brussels, Copenhagen, Madrid, Paris'
  836. ) {
  837. self::getCM()->updateClientBasics($companyName, $country, $timezone);
  838. }
  839. /**
  840. * Updates a list with campaignmonitor and in the database. Returns the affected rows
  841. *
  842. * @param array $item The new data.
  843. *
  844. * @return int
  845. */
  846. public static function updateGroup($item)
  847. {
  848. // build unsubscribe link for this list
  849. $unsubscribeLink = SITE_URL .
  850. BackendModel::getURLForBlock(
  851. 'Mailmotor',
  852. 'unsubscribe',
  853. BL::getWorkingLanguage()
  854. );
  855. // update the group with CM
  856. self::getCM()->updateList(
  857. $item['name'],
  858. $unsubscribeLink . '/?group=' . $item['id'] . '&email=[email]',
  859. null,
  860. null,
  861. self::getCampaignMonitorID('list', $item['id'])
  862. );
  863. // check if we have a default group set
  864. if ($item['is_default'] === 'Y' && $item['language'] != '0') {
  865. // set all defaults to N
  866. BackendModel::getContainer()->get('database')->update(
  867. 'mailmotor_groups',
  868. array('is_default' => 'N', 'language' => null),
  869. 'language = ?',
  870. array($item['language'])
  871. );
  872. }
  873. // update the group in our database
  874. return (int) BackendMailmotorModel::updateGroup($item);
  875. }
  876. /**
  877. * Updates a mailing
  878. *
  879. * @param array $item The mailing record to update.
  880. */
  881. public static function updateMailing(array $item)
  882. {
  883. $local = $item;
  884. self::deleteMailings($item['id']);
  885. // fetch the CM IDs for each group if this field is not set yet
  886. if (!isset($item['group_cm_ids'])) {
  887. $item['group_cm_ids'] = self::getCampaignMonitorIDsForGroups(
  888. $item['groups']
  889. );
  890. }
  891. // fetch the content URLs
  892. if (!isset($item['content_html_url'])) {
  893. $item['content_html_url'] = BackendMailmotorModel::getMailingPreviewURL(
  894. $item['id'],
  895. 'html',
  896. true
  897. );
  898. }
  899. if (!isset($item['content_plain_url'])) {
  900. $item['content_plain_url'] = BackendMailmotorModel::getMailingPreviewURL(
  901. $item['id'],
  902. 'plain',
  903. true
  904. );
  905. }
  906. // overwrite the name, because the previous one is taken -.-
  907. $item['name'] .= ' (#' . rand(0, 999) . ')';
  908. // re-insert the mailing in CM
  909. self::insertMailing($item);
  910. // unset vars we don't need, save vars we need later
  911. $groups = $local['groups'];
  912. unset($local['cm_id'], $local['groups'], $local['recipients'], $local['delivery_date']);
  913. // serialize full content mailing
  914. $local['data'] = serialize($local['data']);
  915. // re-insert the mailing in our database
  916. $id = BackendMailmotorModel::insertMailing($local);
  917. // reinsert the groups for this mailing
  918. BackendMailmotorModel::updateGroupsForMailing($id, $groups);
  919. }
  920. /**
  921. * "Updates" a mailing draft; it deletes and re-creates a draft mailing.
  922. * Campaignmonitor does not have an updateDraft method, so we have to do it this way in order
  923. * to be able to use their sendCampaignPreview method.
  924. *
  925. * @param array $item The mailing record to update a campaign draft.
  926. *
  927. * @return string|null Returns the newly made campaign ID, or false if the method failed.
  928. */
  929. public static function updateMailingDraft(array $item)
  930. {
  931. $db = BackendModel::getContainer()->get('database');
  932. $campaignID = self::getCampaignMonitorID('campaign', $item['id']);
  933. if (is_string($campaignID)) {
  934. // first we insert the new campaign draft and store the CM ID
  935. $newCampaignID = self::insertMailingDraft($item);
  936. // delete the old CM campaign
  937. self::getCM()->deleteCampaign($campaignID);
  938. // remove the old CM ID from the database
  939. $db->delete('mailmotor_campaignmonitor_ids', 'cm_id = ?', $campaignID);
  940. // return the CM ID for the newly created draft campaign
  941. return $newCampaignID;
  942. }
  943. }
  944. }