Skip to content

Commit

Permalink
Merge pull request #39 from sendsmaily/dev/1.5.0
Browse files Browse the repository at this point in the history
1.5.0 release
  • Loading branch information
tomkabel authored Nov 12, 2020
2 parents 09f7e7f + a50ad53 commit a13b817
Show file tree
Hide file tree
Showing 11 changed files with 281 additions and 108 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

### 1.5.0

- New feature to trigger opt-in if new customer joins with newsletter enabled. [issue #32](https://github.com/sendsmaily/smaily-prestashop-module/issues/32)
- Fix missing template variables. [issue #33](https://github.com/sendsmaily/smaily-prestashop-module/issues/33)
- Use PrestashopLogger for logs created by module. [issue #36](https://github.com/sendsmaily/smaily-prestashop-module/issues/36)

### 1.4.0

- Added RSS settings tab.
Expand Down
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,16 @@ All development for Smaily for Prestashop is [handled via GitHub](https://github
2. Insert your Smaily API authentication information and click **Validate** to get started.
3. Under **Customer Synchronization** tab select if you want to enable customer synchronization.
4. Select additional fields you want to synchronize (email is automatic), change cron token if you like your own.
5. Click **Save** to save customer synchronization settings.
6. Under **Abandoned Cart** tab select if you want to enable abandoned cart synchronization.
7. Select autoresponder for abandoned cart.
8. Select additional fields to send to abandoned cart template. Firstname, lastname and store-url are always added.
9. Add delay time when cart is considered abandoned. Minimum time 15 minutes. Change cron token if you like your own.
10. Click **Save** to save abandoned cart settings.
11. Cron is set up to synchronize contacts when CRON-url is visited. Use host Cpanel, PrestaShop Cron tasks manager or external cron service to automate process.
12. That's it, your PrestaShop store is now integrated with Smaily Plugin!
5. New customers who sign up with newsletter enabled can be added to Smaily by enabling trigger opt-in on customer signup.
6. An autoresponder can be selected for "opt-in on customer sign-up", this will only be triggered if the previous option is enabled.
7. Click **Save** to save customer synchronization settings.
8. Under **Abandoned Cart** tab select if you want to enable abandoned cart synchronization.
9. Select autoresponder for abandoned cart.
10. Select additional fields to send to abandoned cart template. Firstname, lastname and store-url are always added.
11. Add delay time when cart is considered abandoned. Minimum time 15 minutes. Change cron token if you like your own.
12. Click **Save** to save abandoned cart settings.
13. Cron is set up to synchronize contacts when CRON-url is visited. Use host Cpanel, PrestaShop Cron tasks manager or external cron service to automate process.
14. That's it, your PrestaShop store is now integrated with Smaily Plugin!

## Using Newsletter Subscription form

Expand Down
3 changes: 2 additions & 1 deletion USERGUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ Registered customers, who have opted for the newsletter will be added to Smaily'
1. Enable automatic customer synchronization feature under **Customer synchronization** section.
2. There is an option to import **additional fields** available from the store into Smaily to personalize newsletter emails.
3. The synchronization can be automated using the cron URL. Recommended synchronization interval is once a day or less.

4. New customers who sign up with newsletter enabled can be added to Smaily, by enabling trigger opt-in on customer signup.
5. An autoresponder can be selected for new customers who sign up with newsletter, the autoresponder won't work if the setting is disabled.
![Customer synchronization section](assets/CustomerSync.png)

## Abandoned cart emails
Expand Down
Binary file modified assets/CustomerSync.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 8 additions & 3 deletions controllers/front/SmailyCartCron.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ private function abandonedCart()
}

// Settings.
$autoresponder = unserialize(Configuration::get('SMAILY_CART_AUTORESPONDER'));
$autoresponder_id = pSQL($autoresponder['id']);
$autoresponder_id = pSQL(Configuration::get('SMAILY_CART_AUTORESPONDER'));
$delay = pSQL(Configuration::get('SMAILY_ABANDONED_CART_TIME'));
$sync_fields = unserialize(Configuration::get('SMAILY_CART_SYNCRONIZE_ADDITIONAL'));

Expand Down Expand Up @@ -157,7 +156,13 @@ private function abandonedCart()
$response['result']['code'] === 101) {
$this->updateSentStatus($id_customer, $id_cart);
} else {
$this->module->logTofile('smaily-cart.txt', Tools::jsonEncode($response));
$this->module->logErrorWithFormatting(
"Failed sending out abandoned cart email for email: %s, cart_id: %s. Smaily response code: %s, message: %s.",
$abandoned_cart['email'],
$abandoned_cart['id_cart'],
$response['result']['code'],
$response['result']['message']
);
}
}
echo('Abandoned carts emails sent!');
Expand Down
14 changes: 11 additions & 3 deletions controllers/front/SmailyCustomerCron.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public function init()
echo('Access denied!');
die(1);
}

if ((int) Configuration::get('SMAILY_ENABLE_CRON') !== 1) {
echo('User synchronization disabled!');
die(1);
Expand All @@ -65,14 +65,14 @@ private function syncContacts()
{
$unsubscribers_synchronized = $this->removeUnsubscribers(self::UNSUBSCRIBERS_BATCH_LIMIT);
if (!$unsubscribers_synchronized) {
$this->module->logTofile('smaily-cron.txt', 'Customer sync failed - unsubscribers are not removed');
$this->module->logMessageWithSeverity("Customer sync failed - unsubscribers are not removed", 1);
return false;
}

// Don't sync customers if failed to remove unsubscribers.
$subscribers_synchronized = $this->sendSubscribersToSmaily(self::SUBSCRIBERS_BATCH_LIMIT);
if (!$subscribers_synchronized) {
$this->module->logTofile('smaily-cron.txt', 'Customer sync failed - faild to send subscribers to Smaily');
$this->module->logMessageWithSeverity("Customer sync failed - failed to send subscribers to Smaily", 1);
return false;
}

Expand Down Expand Up @@ -126,6 +126,7 @@ private function removeUnsubscribers($limit = 1000)

// Stop if error.
if (!isset($unsubscribers['success'])) {
$this->module->logErrorWithFormatting("Failed fetching unsubscribers.");
return false;
}
// Stop if no more subscribers.
Expand All @@ -147,6 +148,7 @@ function ($item) {
$query_result = Db::getInstance()->execute($query);
// Stop if query fails.
if ($query_result === false) {
$this->module->logErrorWithFormatting("Failed removing subscribed status for unsubscribers.");
return false;
}

Expand Down Expand Up @@ -178,6 +180,7 @@ public function sendSubscribersToSmaily($limit)
$customers = Db::getInstance()->executeS($sql);
// Stop if query fails.
if ($customers === false) {
$this->module->logErrorWithFormatting("Failed retrieving newsletter subscribers from DB.");
return false;
}
// Stop if no more qustomers.
Expand All @@ -195,6 +198,11 @@ public function sendSubscribersToSmaily($limit)
$response = $this->module->callApi('contact', $update_data, 'POST');
// Stop if not successful update.
if (isset($response['result']['code']) && $response['result']['code'] !== 101) {
$this->module->logErrorWithFormatting(
"Failed sending subscribers to Smaily. Smaily response code: %s, message: %s",
$response['result']['code'],
$response['result']['message']
);
return false;
}

Expand Down
114 changes: 87 additions & 27 deletions smailyforprestashop.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function __construct()
{
$this->name = 'smailyforprestashop';
$this->tab = 'advertising_marketing';
$this->version = '1.4.0';
$this->version = '1.5.0';
$this->author = 'Smaily';
$this->need_instance = 0;
$this->ps_versions_compliancy = array(
Expand Down Expand Up @@ -78,12 +78,16 @@ public function install()
!Configuration::updateValue('SMAILY_RSS_LIMIT', '50') ||
!Configuration::updateValue('SMAILY_RSS_SORT_BY', 'date_upd') ||
!Configuration::updateValue('SMAILY_RSS_SORT_ORDER', 'desc') ||
!Configuration::updateValue('SMAILY_OPTIN_ENABLED', 0) ||
!Configuration::updateValue('SMAILY_OPTIN_AUTORESPONDER', '') ||
// Add tab to sidebar
!$this->installTab('AdminAdmin', 'AdminSmailyforprestashopAjax', 'Smaily for PrestaShop') ||
// Add Newsletter subscription form.
!$this->registerHook('footerBefore') ||
!$this->registerHook('leftColumn') ||
!$this->registerHook('rightColumn')
!$this->registerHook('rightColumn') ||
// User has option to trigger opt-in when customer joins store & newsletter through sign-up.
!$this->registerHook('actionCustomerAccountAdd')
) {
return false;
}
Expand Down Expand Up @@ -132,6 +136,8 @@ public function uninstall()
!Configuration::deleteByName('SMAILY_RSS_LIMIT') ||
!Configuration::deleteByName('SMAILY_RSS_SORT_BY') ||
!Configuration::deleteByName('SMAILY_RSS_SORT_ORDER') ||
!Configuration::deleteByName('SMAILY_OPTIN_ENABLED') ||
!Configuration::deleteByName('SMAILY_OPTIN_AUTORESPONDER') ||
// Remove sideTab of smaily module.
!$this->uninstallTab('AdminSmailyforprestashopAjax')
) {
Expand Down Expand Up @@ -164,9 +170,10 @@ public function getContent()
) {
// Disable customer sync.
Configuration::updateValue('SMAILY_ENABLE_CRON', 0);
// Disable abandoned cart cron and remove autoresponder.
// Disable abandoned cart cron and remove all autoresponders.
Configuration::updateValue('SMAILY_ENABLE_ABANDONED_CART', 0);
Configuration::updateValue('SMAILY_CART_AUTORESPONDER', '');
Configuration::updateValue('SMAILY_OPTIN_AUTORESPONDER', '');
// Return success message.
$output .= $this->displayConfirmation($this->l('Credentials removed!'));
} else {
Expand Down Expand Up @@ -194,6 +201,8 @@ public function getContent()
$escaped_sync_additional[] = pSQL($value);
}
}
$optin_enabled = pSQL(Tools::getValue('SMAILY_OPTIN_ENABLED'));
$customer_join_autoresponder = pSQL(Tools::getValue('SMAILY_OPTIN_AUTORESPONDER'));
// Check if subdomain is saved to db to verify that credentials are validated.
if (empty(Configuration::get('SMAILY_SUBDOMAIN'))) {
// Display error message.
Expand All @@ -203,6 +212,8 @@ public function getContent()
Configuration::updateValue('SMAILY_ENABLE_CRON', $enable_cron);
Configuration::updateValue('SMAILY_CUSTOMER_CRON_TOKEN', $customer_cron_token);
Configuration::updateValue('SMAILY_SYNCRONIZE_ADDITIONAL', serialize($escaped_sync_additional));
Configuration::updateValue('SMAILY_OPTIN_ENABLED', $optin_enabled);
Configuration::updateValue('SMAILY_OPTIN_AUTORESPONDER', $customer_join_autoresponder);
// Display success message.
$output .= $this->displayConfirmation($this->l('Settings updated'));
}
Expand All @@ -222,17 +233,7 @@ public function getContent()
$cart_cron_token = uniqid();
}
// Abandoned cart Autoresponder
$cart_autoresponder = pSQL((Tools::getValue('SMAILY_CART_AUTORESPONDER')));
$cart_autoresponder = str_replace('\"', '"', $cart_autoresponder);
// Get autoresponder array from json string.
$cart_autoresponder = Tools::jsonDecode($cart_autoresponder);
// Clean autoresponder for inserting to database.
$escaped_cart_autoresponder = array();
if (!empty($cart_autoresponder)) {
foreach ($cart_autoresponder as $key => $value) {
$escaped_cart_autoresponder[ pSQL($key)] = pSQL($value);
}
}
$cart_autoresponder = pSQL(Tools::getValue('SMAILY_CART_AUTORESPONDER'));
// Syncronize additional for abandoned cart template.
$cart_syncronize_additional = Tools::getValue('SMAILY_CART_SYNCRONIZE_ADDITIONAL');
$cart_escaped_sync_additional = array();
Expand All @@ -250,7 +251,7 @@ public function getContent()
$output .= $this->displayError($this->l('Select autoresponder for abandoned cart.'));
} else {
Configuration::updateValue('SMAILY_ENABLE_ABANDONED_CART', $enable_abandoned_cart);
Configuration::updateValue('SMAILY_CART_AUTORESPONDER', serialize($escaped_cart_autoresponder));
Configuration::updateValue('SMAILY_CART_AUTORESPONDER', $cart_autoresponder);
Configuration::updateValue('SMAILY_ABANDONED_CART_TIME', $abandoned_cart_time);
Configuration::updateValue('SMAILY_CART_CRON_TOKEN', $cart_cron_token);
Configuration::updateValue(
Expand Down Expand Up @@ -308,10 +309,10 @@ public function getContent()
} else {
$cart_cron_token = uniqid();
}
// Get customer join autoresponder values for template.
$optin_autoresponder = pSQL(Configuration::get('SMAILY_OPTIN_AUTORESPONDER'));
// Get abandoned cart autoresponder values for template.
$cart_autoresponder_for_template = pSQL((Configuration::get('SMAILY_CART_AUTORESPONDER')));
$cart_autoresponder_for_template = str_replace('\"', '"', $cart_autoresponder_for_template);
$cart_autoresponder_for_template = unserialize($cart_autoresponder_for_template);
$cart_autoresponder = pSQL(Configuration::get('SMAILY_CART_AUTORESPONDER'));

$categories = Category::getNestedCategories(null, Context::getContext()->language->id);

Expand All @@ -323,7 +324,7 @@ public function getContent()
'smaily_subdomain' => pSQL(Configuration::get('SMAILY_SUBDOMAIN')),
'smaily_username' => pSQL(Configuration::get('SMAILY_USERNAME')),
'smaily_password' => pSQL(Configuration::get('SMAILY_PASSWORD')),
'smaily_cart_autoresponder' => $cart_autoresponder_for_template,
'smaily_cart_autoresponder' => $cart_autoresponder,
'smaily_abandoned_cart_time' => pSQL(Configuration::get('SMAILY_ABANDONED_CART_TIME')),
'smaily_syncronize_additional' => $sync_array,
'smaily_cart_syncronize_additional' => $cart_sync_array,
Expand All @@ -334,6 +335,8 @@ public function getContent()
'SmailyCustomerCron',
array('token' => $customer_cron_token)
),
'smaily_customer_cron_token' => $customer_cron_token,
'smaily_cart_cron_token' => $cart_cron_token,
'smaily_cart_cron_url' => Context::getContext()->link->getModuleLink(
'smailyforprestashop',
'SmailyCartCron',
Expand All @@ -344,6 +347,8 @@ public function getContent()
'smaily_rss_limit' => pSQL(Configuration::get('SMAILY_RSS_LIMIT')),
'smaily_rss_sort_by' => pSQL(Configuration::get('SMAILY_RSS_SORT_BY')),
'smaily_rss_sort_order' => pSQL(Configuration::get('SMAILY_RSS_SORT_ORDER')),
'smaily_optin_autoresponder' => $optin_autoresponder,
'smaily_optin_enabled' => pSQL(Configuration::get('SMAILY_OPTIN_ENABLED')),
)
);
// Display settings form.
Expand Down Expand Up @@ -443,17 +448,72 @@ public function hookDisplayBackOfficeHeader()
}

/**
* Log API response to text-file.
* Trigger Smaily Opt-in if customer joins with newsletter subscription.
*
* @param string $filename Name of the file created.
* @param string $msg Text response from api.
* @return void
* @param array $params Array of parameters being passed to the hook function.
* @return bool Success of the operation.
*/
public function logToFile($filename, $response)
public function hookActionCustomerAccountAdd($params)
{
$logger = new FileLogger(1);
$logger->setFilename(_PS_MODULE_DIR_. $this->name ."/" . $filename);
$logger->logInfo('Response from API - ' . $response);
if (empty($params['newCustomer'])) {
return false;
}
$email = $params['newCustomer']->email;
if (!Validate::isEmail($email)) {
return false;
}
$is_newsletter_checked = $params['newCustomer']->newsletter === "1";
$is_subscription_optin_enabled = Configuration::get('SMAILY_OPTIN_ENABLED') === "1";
if (!$is_newsletter_checked || !$is_subscription_optin_enabled) {
return false;
}

$autoresponder = Configuration::get('SMAILY_OPTIN_AUTORESPONDER');
$autoresponder_id = empty($autoresponder) ? '' : (int) $autoresponder;
$query = array(
'autoresponder' => $autoresponder_id,
'addresses' => [['email' => $email]]
);
$response = $this->callApi('autoresponder', $query, 'POST');
$opt_in_successful = array_key_exists('success', $response) && isset($response['result']['code']) && $response['result']['code'] === 101;
if (array_key_exists('success', $response) &&
isset($response['result']['code']) &&
$response['result']['code'] === 101) {
return true; // All good.
} else {
// Supply query values and save log of unsuccesful operation.
$this->logErrorWithFormatting(
"Failed to opt-in new customer with email: %s using autoresponder ID: %s. Smaily response code: %s, message: %s.",
$query['addresses'][0]['email'],
$query['autoresponder'],
$response['result']['code'],
$response['result']['message']
);
return false;
}
}

/**
* Add error (severity 3) to Prestashop log with formatted arguments.
*
* @param string $message
* @return void
*/
public function logErrorWithFormatting() {
$args = func_get_args();
$message = call_user_func_array('sprintf', $args);
PrestaShopLogger::addLog("[SMAILY] " . $message, 3);
}

/**
* Add information to Prestashop log.
*
* @param string $message
* @param int $severity (1 is informative, 3 error)
* @return void
*/
public function logMessageWithSeverity($message, $severity) {
PrestaShopLogger::addLog("[SMAILY] " . $message, $severity);
}

/**
Expand Down
Loading

0 comments on commit a13b817

Please sign in to comment.