Skip to content

Commit

Permalink
Merge pull request #7 from sendsmaily/develop/changes-workflows
Browse files Browse the repository at this point in the history
Develop/changes workflows
  • Loading branch information
sinukaarel authored Apr 26, 2019
2 parents 21ff4db + 33a09cd commit 250cc0b
Show file tree
Hide file tree
Showing 12 changed files with 1,103 additions and 795 deletions.
51 changes: 39 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,25 @@ All development for Smaily for Prestashop is [handled via GitHub](https://github

## Usage

1. Go to Modules -> Modules & Services -> Installed Modules -> Smaily for Prestashop and click configure
2. Insert your Smaily API authentication information to get started.
3. Select if you want to use Cron for contact synchronization between PrestaShop and Smaily
4. Next, click **validate** to check credentials and reveive Autoresponder information.
5. Select your autoresponder and additional fields you want to synchronize (email is automatic)
6. Enter Cron token to make running Cron more secure
7. Click Save Changes
8. Cron is set up to synchronize contacts when CRON-url is visited. To make running cron more secure you can enter
unique token that is added to url. Use host Cpanel, PrestaShop Cron tasks manager or external cron service to automate process.
9. To use Newsletter Subscription form for direct subscription handling transplant Smaily module in Design ->
Positions -> Transplant module section. Select Smaily for Prestashop module and displayFooterBefore hook.
10. That's it, your PrestaShop store is now integrated with Smaily Plugin!
1. Go to Modules -> Module Manager -> Smaily for Prestashop and click Configure
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!

## Using Newsletter Subscription form

1. Navigate to Design -> Positions -> Transplant a Module section.
2. Select **Smaily for Prestashop** module in Module field.
3. Select hook where you would like to transplant newsletter form. You can chose FooterBefore / LeftColumn / RightColumn.
4. New form is displayed when you have validated your credentials in Smaily for Opencart module settings.

## Frequently Asked Questions

Expand Down Expand Up @@ -92,6 +99,26 @@ Product category: {{ product_category_[1-10] }}.

## Changelog

### 1.2.0

New feature:

- Changes due to Smaily workflows automation
- Subdomain field parsed when full url entered
- Separate url-s to run customer and cart cron.
- Settings page updated for better user-friendliness
- Cron tokens auto generated and url-example now dynamic
- Optimized newsletter subscribe form to use in left/right column of your webpage
- Estonian language translations
- You can now remove validated credentials from admin page
- Subscribe newsletter form sends user language

Bugfix:

- Abandoned cart didn't erase email fields that were previously sent
- Customer cron did't get new state of unsubscribed customers before synchronizing with Smaily
- Rss-feed did't show discount and price correctly with taxes.

### 1.1.0 - 2019

- New Feature. Added Abandoned cart support.
Expand Down
77 changes: 68 additions & 9 deletions controllers/admin/AdminSmailyforprestashopAjaxController.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,71 @@ public function ajaxProcessSmailyValidate()
$response = array('error' => $this->l('Please enter password!'));
die(Tools::jsonEncode($response));
}

$subdomain = Tools::getValue('subdomain');
// Normalize subdomain.
// First, try to parse as full URL. If that fails, try to parse as subdomain.sendsmaily.net, and
// if all else fails, then clean up subdomain and pass as is.
if (filter_var($subdomain, FILTER_VALIDATE_URL)) {
$url = parse_url($subdomain);
$parts = explode('.', $url['host']);
$subdomain = count($parts) >= 3 ? $parts[0] : '';
} elseif (preg_match('/^[^\.]+\.sendsmaily\.net$/', $subdomain)) {
$parts = explode('.', $subdomain);
$subdomain = $parts[0];
}
$subdomain = preg_replace('/[^a-zA-Z0-9]+/', '', $subdomain);

// Clean user entered subdomain.
$subdomain = pSQL(Tools::getValue('subdomain'));
$subdomain = trim(Tools::stripslashes($subdomain));
$subdomain = pSQL($subdomain);
// Clean user entered username
$username = pSQL(Tools::getValue('username'));
$username = trim(Tools::stripslashes($username));
// Clean user entered password.
$password = pSQL(Tools::getValue('password'));
$password = trim(Tools::stripslashes($password));
// Make API call to Smaily to get autoresponders list.
$response = $this->callApi('autoresponder', $subdomain, $username, $password);
$response = $this->callApi(
'workflows',
$subdomain,
$username,
$password,
['trigger_type' => 'form_submitted']
);
// Failsafe for empty response.
if (!$response) {
$response = array('error' => $this->l('Invalid login details!'));
die(Tools::jsonEncode($response));
}
// Add credentials to DB if successfully validated.
if (array_key_exists('success', $response)) {
Configuration::updateValue('SMAILY_SUBDOMAIN', $subdomain);
Configuration::updateValue('SMAILY_USERNAME', $username);
Configuration::updateValue('SMAILY_PASSWORD', $password);
}
die(Tools::jsonEncode($response));
}
}

public function ajaxProcessGetAutoresponders()
{
$response = [];
// Validate token and if request is ajax call.
if (Tools::getValue('ajax') &&
Tools::getValue('token') === Tools::getAdminTokenLite('AdminSmailyforprestashopAjax')
) {
// Get credentials from db.
$subdomain = pSQL(Configuration::get('SMAILY_SUBDOMAIN'));
$username = pSQL(Configuration::get('SMAILY_USERNAME'));
$password = pSQL(Configuration::get('SMAILY_PASSWORD'));
// Make API call to Smaily to get autoresponders list.
$response = $this->callApi(
'workflows',
$subdomain,
$username,
$password,
['trigger_type' => 'form_submitted']
);
die(Tools::jsonEncode($response));
}
}
Expand Down Expand Up @@ -120,14 +170,23 @@ public function callApi($endpoint, $subdomain, $username, $password, $data = arr
$result = json_decode(curl_exec($ch), true);

$http_status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ((int) $http_status === 401) {
return $result = array('error' => $this->l('Check credentials, unauthorized!'));
}

if (curl_errno($ch)) {
if (!curl_errno($ch)) {
switch ((int) $http_status) {
case 200:
return array('success' => true, 'autoresponders' => $result);
break;
case 401:
return $result = array('error' => $this->l('Check credentials, unauthorized!'));
break;
case 404:
return $result = array('error' => $this->l('Check subdomain, unauthorized!'));
break;
default:
return $result = array('error' => $this->l('Something went wrong with request to Smaily!'));
}
} else {
return $result = array("error" => $this->l(curl_error($ch)));
}
curl_close($ch);
return array('success' => true, 'autoresponders' => $result);
}
}
163 changes: 163 additions & 0 deletions controllers/front/SmailyCartCron.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<?php
/**
* 2018 Smaily
*
* NOTICE OF LICENSE
*
* Smaily for PrestaShop is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Smaily for PrestaShop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Smaily for PrestaShop. If not, see <http://www.gnu.org/licenses/>.
*
* @author Smaily <[email protected]>
* @copyright 2018 Smaily
* @license GPL3
*/

class SmailyforprestashopSmailyCartCronModuleFrontController extends ModuleFrontController
{

public function init()
{
parent::init();
header('Content-Type: text/plain');
if (Tools::getValue('token') == Configuration::get('SMAILY_CART_CRON_TOKEN')) {
$this->abandonedCart();
die();
} else {
die($this->l('Access denied! '));
}
}

/**
* Send abandoned cart emails to customers.
*
* @return void
*/
private function abandonedCart()
{
if (Configuration::get('SMAILY_ENABLE_ABANDONED_CART') === "1") {
// Settings
$autoresponder = unserialize(Configuration::get('SMAILY_CART_AUTORESPONDER'));
$autoresponder_id = pSQL($autoresponder['id']);
$delay = pSQL(Configuration::get('SMAILY_ABANDONED_CART_TIME'));
// Values to sync array
$sync_fields = unserialize(Configuration::get('SMAILY_CART_SYNCRONIZE_ADDITIONAL'));

// Select all carts where cart_id is not on orders table and exclude if excists in smaily_cart table.
// Gather customer data also.
$sql = 'SELECT c.id_cart,
c.id_customer,
c.date_upd,
cu.firstname,
cu.lastname,
cu.email
FROM ' . _DB_PREFIX_ . 'cart c
LEFT JOIN ' . _DB_PREFIX_ . 'orders o
ON (o.id_cart = c.id_cart)
RIGHT JOIN ' . _DB_PREFIX_ . 'customer cu
ON (cu.id_customer = c.id_customer)
LEFT OUTER JOIN ' . _DB_PREFIX_ . 'smaily_cart sc
ON (c.id_cart = sc.id_cart)
WHERE sc.id_cart IS NULL
AND DATE_SUB(CURDATE(),INTERVAL 10 DAY) <= c.date_add
AND o.id_order IS NULL';

$sql .= Shop::addSqlRestriction(Shop::SHARE_CUSTOMER, 'c');
$sql .= ' GROUP BY cu.id_customer';

$abandoned_carts = Db::getInstance()->executeS($sql);
foreach ($abandoned_carts as $abandoned_cart) {
// If time has passed form last cart update
$cart_updated_time = strtotime($abandoned_cart['date_upd']);
$reminder_time = strtotime('+' . $delay . ' minutes', $cart_updated_time);
$current_time = strtotime(date('Y-m-d H:i') . ':00');

// Check if delay passed to send cart.
$id_customer = (int) $abandoned_cart['id_customer'];
$id_cart = (int) $abandoned_cart['id_cart'];

if ($current_time >= $reminder_time) {
$cart = new Cart($abandoned_cart['id_cart']);
$products = $cart->getProducts();

// Dont continue if no products in cart.
if (empty($products)) {
continue;
}

$adresses = array(
'email' => $abandoned_cart['email'],
'firstname' => $abandoned_cart['firstname'],
'lastname' => $abandoned_cart['lastname'],
'store_url' => _PS_BASE_URL_.__PS_BASE_URI__,
);
// Populate abandoned cart with empty values for legacy api.
$fields_available = array(
'name',
'description_short',
'price',
'category',
'quantity'
);
foreach ($fields_available as $field) {
for ($i=1; $i<=10; $i++) {
$adresses['product_' . $field . '_' . $i] = '';
}
}
// Collect products of abandoned cart.
$count = 1;
foreach ($products as $product) {
if ($count <= 10) {
foreach ($sync_fields as $sync_field) {
$adresses['product_' . $sync_field .'_' . $count] = strip_tags($product[$sync_field]);
}
}
$count++;
}
// Smaily api query.
$query = array(
'autoresponder' => $autoresponder_id,
'addresses' => array($adresses)
);
// Send cart data to smaily api.
$response = $this->module->callApi('autoresponder', $query, 'POST');
// If email sent successfully update sent status in database.
if (array_key_exists('success', $response) &&
isset($response['result']['code']) &&
$response['result']['code'] === 101) {
$this->updateSentStatus($id_customer, $id_cart);
} else {
$this->module->logTofile('smaily-cart.txt', Tools::jsonEncode($response));
}

}
}
echo($this->l('Abandoned carts emails sent!'));
} else {
echo($this->l('Abandoned cart disabled!'));
}
}

/**
* Updates Sent email status in smaily cart table.
*
* @param integer $id_customer Customer ID
* @param integer $id_cart Cart ID
* @return void
*/
private function updateSentStatus(int $id_customer, int $id_cart)
{
$sql = 'INSERT INTO ' . _DB_PREFIX_ . 'smaily_cart (id_customer, id_cart, date_sent)
VALUES (' . $id_customer . ', ' . $id_cart . ', CURRENT_TIMESTAMP)';
Db::getInstance()->execute($sql);
}
}
Loading

0 comments on commit 250cc0b

Please sign in to comment.