Suppression du module PhpList
This commit is contained in:
@@ -1,426 +0,0 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* Copyright (c) Franck Allimant, CQFDev */
|
||||
/* email : thelia@cqfdev.fr */
|
||||
/* web : http://www.cqfdev.fr */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace PhpList\Api;
|
||||
|
||||
/**
|
||||
* Created by Franck Allimant, CQFDev <franck@cqfdev.fr>
|
||||
* Date: 18/07/2016 19:58
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* example PHP client class to access the phpList Rest API.
|
||||
* License: MIT, https://opensource.org/licenses/MIT
|
||||
*
|
||||
* To use this class, you need the restapi plugin for phpList, https://resources.phplist.com/plugin/restapi
|
||||
* Set the parameters below to match your system:
|
||||
*
|
||||
* - url : URL of your phpList installation
|
||||
* - loginName : admin login
|
||||
* - password : matching password
|
||||
* - remoteProcessingSecret : (optional) the secret as defined in your phpList settings
|
||||
*
|
||||
* v 1.01 Nov 26, 2015 added optional secret on instantiation
|
||||
* v 1 * Michiel Dethmers, phpList Ltd, November 18, 2015
|
||||
* Initial implementation of basic API calls
|
||||
*/
|
||||
class PhpListRESTApiClient
|
||||
{
|
||||
/*
|
||||
* URL of the API to connect to including the path
|
||||
* generally something like.
|
||||
*
|
||||
* https://website.com/lists/admin/?pi=restapi&page=call
|
||||
*/
|
||||
private $url;
|
||||
/*
|
||||
* login name for the phpList installation.
|
||||
*/
|
||||
private $loginName;
|
||||
|
||||
/*
|
||||
* password to login.
|
||||
*/
|
||||
private $password;
|
||||
|
||||
/*
|
||||
* the path where we can write our cookiejar.
|
||||
*/
|
||||
public $tmpPath;
|
||||
|
||||
/*
|
||||
* optionally the remote processing secret of the phpList installation
|
||||
* this will increase the security of the API calls.
|
||||
*/
|
||||
private $remoteProcessingSecret;
|
||||
|
||||
/**
|
||||
* construct, provide the Credentials for the API location.
|
||||
*
|
||||
* @param string $url URL of the API
|
||||
* @param string $loginName name to login with
|
||||
* @param string $password password for the account
|
||||
* @param string $secret
|
||||
*/
|
||||
public function __construct($url, $loginName, $password, $secret = '')
|
||||
{
|
||||
$this->tmpPath = sys_get_temp_dir();
|
||||
|
||||
$this->url = $url;
|
||||
$this->loginName = $loginName;
|
||||
$this->password = $password;
|
||||
$this->remoteProcessingSecret = $secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a call to the API using cURL.
|
||||
*
|
||||
* @param string $command The command to run
|
||||
* @param array $post_params Array for parameters for the API call
|
||||
* @param bool $decode json_decode the result (defaults to true)
|
||||
*
|
||||
* @return string result of the CURL execution
|
||||
*/
|
||||
private function callApi($command, $post_params, $decode = true)
|
||||
{
|
||||
$post_params['cmd'] = $command;
|
||||
|
||||
// optionally add the secret to a call, if provided
|
||||
if (!empty($this->remoteProcessingSecret)) {
|
||||
$post_params['secret'] = $this->remoteProcessingSecret;
|
||||
}
|
||||
$post_params = http_build_query($post_params);
|
||||
$c = curl_init();
|
||||
curl_setopt($c, CURLOPT_URL, $this->url);
|
||||
curl_setopt($c, CURLOPT_HEADER, 0);
|
||||
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($c, CURLOPT_POST, 1);
|
||||
curl_setopt($c, CURLOPT_POSTFIELDS, $post_params);
|
||||
curl_setopt($c, CURLOPT_COOKIEFILE, $this->tmpPath.'/phpList_RESTAPI_cookiejar.txt');
|
||||
curl_setopt($c, CURLOPT_COOKIEJAR, $this->tmpPath.'/phpList_RESTAPI_cookiejar.txt');
|
||||
curl_setopt($c, CURLOPT_HTTPHEADER, array('Connection: Keep-Alive', 'Keep-Alive: 60'));
|
||||
|
||||
// Execute the call
|
||||
$result = curl_exec($c);
|
||||
|
||||
// Check if decoding of result is required
|
||||
if ($decode === true) {
|
||||
$result = json_decode($result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a real login to test login api call.
|
||||
*
|
||||
* @param none
|
||||
*
|
||||
* @return bool true if user exists and login successful
|
||||
*/
|
||||
public function login()
|
||||
{
|
||||
// Set the username and pwd to login with
|
||||
$post_params = array(
|
||||
'login' => $this->loginName,
|
||||
'password' => $this->password,
|
||||
);
|
||||
|
||||
// Execute the login with the credentials as params
|
||||
$result = $this->callApi('login', $post_params);
|
||||
return $result->status == 'success';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a list.
|
||||
*
|
||||
* @param string $listName Name of the list
|
||||
* @param string $listDescription Description of the list
|
||||
*
|
||||
* @return int ListId of the list created
|
||||
*/
|
||||
public function listAdd($listName, $listDescription)
|
||||
{
|
||||
// Create minimal params for api call
|
||||
$post_params = array(
|
||||
'name' => $listName,
|
||||
'description' => $listDescription,
|
||||
'listorder' => '0',
|
||||
'active' => '1',
|
||||
);
|
||||
|
||||
// Execute the api call
|
||||
$result = $this->callApi('listAdd', $post_params);
|
||||
|
||||
// get the ID of the list we just created
|
||||
$listId = $result->data->id;
|
||||
|
||||
return $listId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all lists.
|
||||
*
|
||||
* @return array|false All lists
|
||||
*/
|
||||
public function listsGet()
|
||||
{
|
||||
// Create minimal params for api call
|
||||
$post_params = array(
|
||||
);
|
||||
|
||||
// Execute the api call
|
||||
$result = $this->callApi('listsGet', $post_params);
|
||||
|
||||
// Return all list as array
|
||||
return $result !== null ? $result->data : false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a subscriber by email address.
|
||||
*
|
||||
* @param string $emailAddress Email address to search
|
||||
*
|
||||
* @return int $subscriberID if found false if not found
|
||||
*/
|
||||
public function subscriberFindByEmail($emailAddress)
|
||||
{
|
||||
$params = array(
|
||||
'email' => $emailAddress,
|
||||
);
|
||||
$result = $this->callApi('subscriberGetByEmail', $params);
|
||||
|
||||
if (!empty($result->data->id)) {
|
||||
return $result->data->id;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a subscriber.
|
||||
*
|
||||
* This is the main method to use to add a subscriber. It will add the subscriber as
|
||||
* a non-confirmed subscriber in phpList and it will send the Request-for-confirmation
|
||||
* email as set up in phpList.
|
||||
*
|
||||
* The lists parameter will set the lists the subscriber will be added to. This has
|
||||
* to be comma-separated list-IDs, eg "1,2,3,4".
|
||||
*
|
||||
* @param string $emailAddress email address of the subscriber to add
|
||||
* @param string $lists comma-separated list of IDs of the lists to add the subscriber to
|
||||
*
|
||||
* @return int $subscriberId if added, or false if failed
|
||||
*/
|
||||
public function subscribe($emailAddress, $lists)
|
||||
{
|
||||
// Set the user details as parameters
|
||||
$post_params = array(
|
||||
'email' => $emailAddress,
|
||||
'foreignkey' => '',
|
||||
'htmlemail' => 1,
|
||||
'subscribepage' => 0,
|
||||
'lists' => $lists,
|
||||
);
|
||||
|
||||
// Execute the api call
|
||||
$result = $this->callApi('subscribe', $post_params);
|
||||
|
||||
if (!empty($result->data->id)) {
|
||||
$subscriberId = $result->data->id;
|
||||
|
||||
return $subscriberId;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch subscriber by ID.
|
||||
*
|
||||
* @param int $subscriberID ID of the subscriber
|
||||
*
|
||||
* @return the subscriber
|
||||
*/
|
||||
public function subscriberGet($subscriberId)
|
||||
{
|
||||
$post_params = array(
|
||||
'id' => $subscriberId,
|
||||
);
|
||||
|
||||
// Execute the api call
|
||||
$result = $this->callApi('subscriberGet', $post_params);
|
||||
if (!empty($result->data->id)) {
|
||||
$fetchedSubscriberId = $result->data->id;
|
||||
$this->assertEquals($fetchedSubscriberId, $subscriberId);
|
||||
|
||||
return $result->data;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a subscriber by Foreign Key.
|
||||
*
|
||||
* Note the difference with subscriberFindByEmail which only returns the SubscriberID
|
||||
* Both API calls return the subscriber
|
||||
*
|
||||
* @param string $foreignKey Foreign Key to search
|
||||
*
|
||||
* @return subscriber object if found false if not found
|
||||
*/
|
||||
public function subscriberGetByForeignkey($foreignKey)
|
||||
{
|
||||
$post_params = array(
|
||||
'foreignkey' => $foreignKey,
|
||||
);
|
||||
|
||||
$result = $this->callApi('subscriberGetByForeignkey', $post_params);
|
||||
|
||||
if (!empty($result->data->id)) {
|
||||
return $result->data;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total number of subscribers.
|
||||
*
|
||||
* @param none
|
||||
*
|
||||
* @return int total number of subscribers in the system
|
||||
*/
|
||||
public function subscriberCount()
|
||||
{
|
||||
$post_params = array(
|
||||
);
|
||||
|
||||
$result = $this->callApi('subscribersCount', $post_params);
|
||||
|
||||
return $result !== null ? $result->data->total : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a subscriber to an existing list.
|
||||
*
|
||||
* @param int $listId ID of the list
|
||||
* @param int $subscriberId ID of the subscriber
|
||||
*
|
||||
* @return the lists this subscriber is member of
|
||||
*/
|
||||
public function listSubscriberAdd($listId, $subscriberId)
|
||||
{
|
||||
// Set list and subscriber vars
|
||||
$post_params = array(
|
||||
'list_id' => $listId,
|
||||
'subscriber_id' => $subscriberId,
|
||||
);
|
||||
|
||||
$result = $this->callApi('listSubscriberAdd', $post_params);
|
||||
|
||||
return $result !== null ? $result->data : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a subscriber
|
||||
*
|
||||
* @param int $listId ID of the list
|
||||
* @param int $subscriberId ID of the subscriber
|
||||
*
|
||||
* @return the lists this subscriber is member of
|
||||
*/
|
||||
/**
|
||||
* Add a subscriber
|
||||
*
|
||||
* @param $email
|
||||
* @param $confirmed
|
||||
* @param $password
|
||||
* @return mixed
|
||||
*/
|
||||
public function subscriberAdd($email, $confirmed, $password)
|
||||
{
|
||||
// Set subscriber vars
|
||||
$post_params = array(
|
||||
'email' => $email,
|
||||
'confirmed' => $confirmed ? 1 : 0,
|
||||
'password' => $password,
|
||||
'disabled' => 0,
|
||||
'htmlemail' => 1,
|
||||
'subscribepage' => 0,
|
||||
'foreignkey' => '',
|
||||
);
|
||||
|
||||
if (null !== $result = $this->callApi('subscriberAdd', $post_params)) {
|
||||
return $result->data;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the lists a subscriber is member of.
|
||||
*
|
||||
* @param int $subscriberId ID of the subscriber
|
||||
*
|
||||
* @return the lists this subcriber is member of
|
||||
*/
|
||||
public function listsSubscriber($subscriberId)
|
||||
{
|
||||
$post_params = array(
|
||||
'subscriber_id' => $subscriberId,
|
||||
);
|
||||
|
||||
$result = $this->callApi('listsSubscriber', $post_params);
|
||||
|
||||
return $result !== null ? $result->data : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subscribers to a given list
|
||||
*
|
||||
* @param int $listId the list id
|
||||
* @return a lust of subscribers
|
||||
*/
|
||||
public function listSubscribers($listId)
|
||||
{
|
||||
$post_params = array(
|
||||
'list_id' => $listId,
|
||||
);
|
||||
|
||||
$result = $this->callApi('listSubscribers', $post_params);
|
||||
|
||||
return $result !== null ? $result->data : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a Subscriber from a list.
|
||||
*
|
||||
* @param int $listId ID of the list to remove
|
||||
* @param int $subscriberId ID of the subscriber
|
||||
*
|
||||
* @return the lists this subcriber is member of
|
||||
*/
|
||||
public function listSubscriberDelete($listId, $subscriberId)
|
||||
{
|
||||
// Set list and subscriber vars
|
||||
$post_params = array(
|
||||
'list_id' => $listId,
|
||||
'subscriber_id' => $subscriberId,
|
||||
);
|
||||
|
||||
$result = $this->callApi('listSubscriberDelete', $post_params);
|
||||
|
||||
return $result !== null ? $result->data : false;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* Copyright (c) Franck Allimant, CQFDev */
|
||||
/* email : thelia@cqfdev.fr */
|
||||
/* web : http://www.cqfdev.fr */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
/**
|
||||
* Created by Franck Allimant, CQFDev <franck@cqfdev.fr>
|
||||
* Date: 21/07/2016 09:14
|
||||
*/
|
||||
|
||||
namespace PhpList\Command;
|
||||
|
||||
use PhpList\PhpList;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Thelia\Command\ContainerAwareCommand;
|
||||
|
||||
class Sync extends ContainerAwareCommand
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName("phplist:sync")
|
||||
->setDescription("Synchronize local newsletter subscribers with phpList subscribers")
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$output->writeln("<info>Synchronizing...</info>");
|
||||
|
||||
$this->getDispatcher()->dispatch(PhpList::RESYNC_EVENT);
|
||||
|
||||
$output->writeln("<info>Synchronization done...</info>");
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<config xmlns="http://thelia.net/schema/dic/config"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://thelia.net/schema/dic/config http://thelia.net/schema/dic/config/thelia-1.0.xsd">
|
||||
|
||||
<commands>
|
||||
<command class="PhpList\Command\Sync"/>
|
||||
</commands>
|
||||
|
||||
<forms>
|
||||
<form name="phplist.configuration.form" class="PhpList\Form\ConfigurationForm" />
|
||||
</forms>
|
||||
|
||||
<services>
|
||||
<service id="phplist.event_listener" class="PhpList\EventListeners\EventManager">
|
||||
<tag name="kernel.event_subscriber" />
|
||||
</service>
|
||||
</services>
|
||||
|
||||
<hooks>
|
||||
<hook id="phplist.product_edit" class="PhpList\Hook\HookManager" scope="request">
|
||||
<tag name="hook.event_listener" event="module.configuration" type="back" method="onModuleConfigure" />
|
||||
</hook>
|
||||
</hooks>
|
||||
|
||||
</config>
|
||||
@@ -1,28 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module xmlns="http://thelia.net/schema/dic/module"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://thelia.net/schema/dic/module http://thelia.net/schema/dic/module/module-2_2.xsd">
|
||||
<fullnamespace>PhpList\PhpList</fullnamespace>
|
||||
<descriptive locale="en_US">
|
||||
<title>PhpList interface module</title>
|
||||
</descriptive>
|
||||
<descriptive locale="fr_FR">
|
||||
<title>Module d'interface avec PhpList</title>
|
||||
</descriptive>
|
||||
<languages>
|
||||
<language>en_US</language>
|
||||
<language>fr_FR</language>
|
||||
</languages>
|
||||
<version>1.0.0</version>
|
||||
<authors>
|
||||
<author>
|
||||
<name>Franck Allimant</name>
|
||||
<company>CQFDev</company>
|
||||
<email>thelia@cqfdev.fr</email>
|
||||
<website>www.cqfdev.fr</website>
|
||||
</author>
|
||||
</authors>
|
||||
<type>classic</type>
|
||||
<thelia>2.3.0</thelia>
|
||||
<stability>other</stability>
|
||||
</module>
|
||||
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<routes xmlns="http://symfony.com/schema/routing"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
|
||||
|
||||
<route id="phplist.configure" path="/admin/module/phplist/configure" methods="post">
|
||||
<default key="_controller">PhpList\Controller\ConfigurationController::configure</default>
|
||||
</route>
|
||||
|
||||
<route id="phplist.sync" path="/admin/module/phplist/sync">
|
||||
<default key="_controller">PhpList\Controller\ConfigurationController::sync</default>
|
||||
<default key="not-logged">1</default>
|
||||
</route>
|
||||
</routes>
|
||||
@@ -1,110 +0,0 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* Copyright (c) Franck Allimant, CQFDev */
|
||||
/* email : thelia@cqfdev.fr */
|
||||
/* web : http://www.cqfdev.fr */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
/**
|
||||
* Created by Franck Allimant, CQFDev <franck@cqfdev.fr>
|
||||
* Date: 20/07/2016 15:18
|
||||
*/
|
||||
|
||||
namespace PhpList\Controller;
|
||||
|
||||
use PhpList\PhpList;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Thelia\Controller\Admin\BaseAdminController;
|
||||
use Thelia\Core\Security\AccessManager;
|
||||
use Thelia\Core\Security\Resource\AdminResources;
|
||||
use Thelia\Form\Exception\FormValidationException;
|
||||
use Thelia\Log\Tlog;
|
||||
use Thelia\Tools\URL;
|
||||
|
||||
class ConfigurationController extends BaseAdminController
|
||||
{
|
||||
public function configure()
|
||||
{
|
||||
if (null !== $response = $this->checkAuth(AdminResources::MODULE, 'phpList', AccessManager::UPDATE)) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Create the Form from the request
|
||||
$configurationForm = $this->getTheliaFormFactory()->createForm('phplist.configuration.form');
|
||||
|
||||
try {
|
||||
// Check the form against constraints violations
|
||||
$form = $this->validateForm($configurationForm, "POST");
|
||||
|
||||
// Get the form field values
|
||||
$data = $form->getData();
|
||||
|
||||
foreach ($data as $name => $value) {
|
||||
if (is_array($value)) {
|
||||
$value = implode(';', $value);
|
||||
}
|
||||
|
||||
PhpList::setConfigValue($name, $value);
|
||||
}
|
||||
|
||||
// Log configuration modification
|
||||
$this->adminLogAppend(
|
||||
"phplist.configuration.message",
|
||||
AccessManager::UPDATE,
|
||||
sprintf("PhpList configuration updated")
|
||||
);
|
||||
|
||||
// Redirect to the success URL,
|
||||
if ($this->getRequest()->get('save_mode') == 'stay') {
|
||||
// If we have to stay on the same page, redisplay the configuration page/
|
||||
$route = '/admin/module/PhpList';
|
||||
} else {
|
||||
// If we have to close the page, go back to the module back-office page.
|
||||
$route = '/admin/modules';
|
||||
}
|
||||
|
||||
return new RedirectResponse(URL::getInstance()->absoluteUrl($route));
|
||||
|
||||
} catch (FormValidationException $ex) {
|
||||
// Form cannot be validated. Create the error message using
|
||||
// the BaseAdminController helper method.
|
||||
$error_msg = $this->createStandardFormValidationErrorMessage($ex);
|
||||
|
||||
Tlog::getInstance()->error($ex->getTraceAsString());
|
||||
}
|
||||
catch (\Exception $ex) {
|
||||
// Any other error
|
||||
$error_msg = $ex->getMessage();
|
||||
|
||||
Tlog::getInstance()->error($ex->getTraceAsString());
|
||||
}
|
||||
|
||||
// At this point, the form has errors
|
||||
$this->setupFormErrorContext(
|
||||
$this->getTranslator()->trans("PhpList configuration", [], PhpList::DOMAIN_NAME),
|
||||
$error_msg,
|
||||
$configurationForm,
|
||||
$ex
|
||||
);
|
||||
|
||||
|
||||
return new RedirectResponse(URL::getInstance()->absoluteUrl('/admin/module/PhpList'));
|
||||
}
|
||||
|
||||
public function sync()
|
||||
{
|
||||
$this->getDispatcher()->dispatch(PhpList::RESYNC_EVENT);
|
||||
|
||||
return new RedirectResponse(URL::getInstance()->absoluteUrl('/admin/module/PhpList'));
|
||||
}
|
||||
|
||||
public function bulkAdd()
|
||||
{
|
||||
$this->getDispatcher()->dispatch(PhpList::BULK_ADD);
|
||||
|
||||
return new RedirectResponse(URL::getInstance()->absoluteUrl('/admin/module/PhpList'));
|
||||
}
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* Copyright (c) Franck Allimant, CQFDev */
|
||||
/* email : thelia@cqfdev.fr */
|
||||
/* web : http://www.cqfdev.fr */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
/**
|
||||
* Created by Franck Allimant, CQFDev <franck@cqfdev.fr>
|
||||
* Date: 18/07/2016 20:02
|
||||
*/
|
||||
|
||||
namespace PhpList\EventListeners;
|
||||
|
||||
use PhpList\Api\PhpListRESTApiClient;
|
||||
use PhpList\PhpList;
|
||||
use Propel\Runtime\ActiveQuery\Criteria;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Thelia\Core\Event\Newsletter\NewsletterEvent;
|
||||
use Thelia\Core\Event\TheliaEvents;
|
||||
use Thelia\Core\Translation\Translator;
|
||||
use Thelia\Exception\TheliaProcessException;
|
||||
use Thelia\Log\Tlog;
|
||||
use Thelia\Model\Lang;
|
||||
use Thelia\Model\Newsletter;
|
||||
use Thelia\Model\NewsletterQuery;
|
||||
|
||||
class EventManager implements EventSubscriberInterface
|
||||
{
|
||||
public function subscribe(NewsletterEvent $event)
|
||||
{
|
||||
$this->doSubscribe($event->getEmail(), PhpList::getConfigValue(PhpList::LIST_NAME));
|
||||
}
|
||||
|
||||
public function unsubscribe(NewsletterEvent $event)
|
||||
{
|
||||
$this->doUnsubscribe($event->getEmail(), PhpList::getConfigValue(PhpList::LIST_NAME));
|
||||
}
|
||||
|
||||
public function bulkAdd()
|
||||
{
|
||||
$theliaSubscribers = NewsletterQuery::create()->select('email')->find()->toArray();
|
||||
|
||||
foreach ($theliaSubscribers as $subscriber) {
|
||||
$this->doSubscribe($subscriber, PhpList::getConfigValue(PhpList::LIST_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected function doSubscribe($email, $list)
|
||||
{
|
||||
$api = $this->createApiClient();
|
||||
|
||||
try {
|
||||
$subscriberId = $this->getSubscriberId($api, $email, true);
|
||||
|
||||
if ($api->listSubscriberAdd($list, $subscriberId)) {
|
||||
Tlog::getInstance()->info(
|
||||
sprintf(
|
||||
"Email address %s successfully added to phpList ID %s.",
|
||||
$email,
|
||||
$list
|
||||
)
|
||||
);
|
||||
} else {
|
||||
throw new \Exception("Unknown error");
|
||||
}
|
||||
} catch (\Exception $ex) {
|
||||
Tlog::getInstance()->error(
|
||||
sprintf(
|
||||
"Failed to add email address %s to phpList ID %s. Error is %s",
|
||||
$email,
|
||||
$list,
|
||||
$ex->getMessage()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected function doUnsubscribe($email, $list)
|
||||
{
|
||||
$api = $this->createApiClient();
|
||||
|
||||
try {
|
||||
$subscriberId = $this->getSubscriberId($api, $email, true);
|
||||
|
||||
if ($api->listSubscriberDelete($list, $subscriberId)) {
|
||||
Tlog::getInstance()->info(
|
||||
sprintf(
|
||||
"Email address %s successfully removed from phpList ID %s.",
|
||||
$email,
|
||||
$list
|
||||
)
|
||||
);
|
||||
}
|
||||
} catch (\Exception $ex) {
|
||||
Tlog::getInstance()->error(
|
||||
sprintf(
|
||||
"Failed to remove email address %s from phpList ID %s. Error is %s",
|
||||
$email,
|
||||
$list,
|
||||
$ex->getMessage()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PhpListRESTApiClient $api
|
||||
* @param string $email
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function getSubscriberId($api, $email, $createIfNotExists = false)
|
||||
{
|
||||
if (false !== $subscriberId = $api->subscriberFindByEmail($email)) {
|
||||
return $subscriberId;
|
||||
} elseif ($createIfNotExists) {
|
||||
if (false !== $subscriberId = $api->subscriberAdd($email, true, $email . time())) {
|
||||
return $subscriberId;
|
||||
} else {
|
||||
throw new \Exception("Failed to create customer with email $email");
|
||||
}
|
||||
} else {
|
||||
throw new \Exception("Subscriber was not found in phpList");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return PhpListRESTApiClient
|
||||
*/
|
||||
protected function createApiClient()
|
||||
{
|
||||
if (null === $url = PhpList::getConfigValue(PhpList::LIST_NAME, null)) {
|
||||
throw new TheliaProcessException(
|
||||
Translator::getInstance()->trans(
|
||||
"Cannot create Php List REST client. Module is not initialized.",
|
||||
[],
|
||||
PhpList::DOMAIN_NAME
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$api = new PhpListRESTApiClient(
|
||||
PhpList::getConfigValue(PhpList::REST_URL),
|
||||
PhpList::getConfigValue(PhpList::API_LOGIN_NAME),
|
||||
PhpList::getConfigValue(PhpList::API_PASSWORD),
|
||||
PhpList::getConfigValue(PhpList::API_SECRET)
|
||||
);
|
||||
|
||||
if (false === $api->login()) {
|
||||
throw new TheliaProcessException(
|
||||
Translator::getInstance()->trans(
|
||||
"Failed to login to phpList, please check credentials.",
|
||||
[],
|
||||
PhpList::DOMAIN_NAME
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $api;
|
||||
}
|
||||
|
||||
public function resync()
|
||||
{
|
||||
$list = PhpList::getConfigValue(PhpList::LIST_NAME);
|
||||
|
||||
$api = $this->createApiClient();
|
||||
|
||||
if (false !== $subscribers = $api->listSubscribers($list)) {
|
||||
|
||||
$theliaSubscribers = NewsletterQuery::create()->select('email')->find()->toArray();
|
||||
|
||||
$locale = Lang::getDefaultLanguage()->getLocale();
|
||||
|
||||
$phpListSubscribers = [];
|
||||
|
||||
// Add to Thelia the subscribers which are not in the Newsletter table
|
||||
foreach ($subscribers as $subscriber) {
|
||||
if ($subscriber->confirmed) {
|
||||
$phpListSubscribers[] = $subscriber->email;
|
||||
|
||||
if (!in_array($subscriber->email, $theliaSubscribers)) {
|
||||
$newsletter = new Newsletter();
|
||||
$newsletter
|
||||
->setEmail($subscriber->email)
|
||||
->setLocale($locale)
|
||||
->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from Thelia the subscribers which are not in phpList
|
||||
foreach ($theliaSubscribers as $theliaSubscriber) {
|
||||
if (!in_array($theliaSubscriber, $phpListSubscribers)) {
|
||||
NewsletterQuery::create()->findOneByEmail($theliaSubscriber)->delete();
|
||||
}
|
||||
}
|
||||
|
||||
// Add to phpList the missing Thelia subscribers, ignoring unsubscribed emails
|
||||
$theliaSubscribers = NewsletterQuery::create()
|
||||
->filterByUnsubscribed(false)
|
||||
->select('email')
|
||||
->find()
|
||||
->toArray();
|
||||
|
||||
foreach ($theliaSubscribers as $theliaSubscriber) {
|
||||
if (!in_array($theliaSubscriber, $phpListSubscribers)) {
|
||||
$this->doSubscribe($theliaSubscriber, $list);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from phpList unsubscribed Thelia emails
|
||||
$theliaSubscribers = NewsletterQuery::create()
|
||||
->filterByUnsubscribed(true)
|
||||
->select('email')
|
||||
->find()
|
||||
->toArray();
|
||||
|
||||
foreach ($theliaSubscribers as $theliaSubscriber) {
|
||||
if (in_array($theliaSubscriber, $phpListSubscribers)) {
|
||||
$this->doUnsubscribe($theliaSubscriber, $list);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
TheliaEvents::NEWSLETTER_SUBSCRIBE => ["subscribe", 130],
|
||||
TheliaEvents::NEWSLETTER_UNSUBSCRIBE => ["unsubscribe", 130],
|
||||
PhpList::RESYNC_EVENT => ['resync', 128],
|
||||
PhpList::BULK_ADD => ['bulkAdd', 128]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* Copyright (c) Franck Allimant, CQFDev */
|
||||
/* email : thelia@cqfdev.fr */
|
||||
/* web : http://www.cqfdev.fr */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace PhpList\Form;
|
||||
use PhpList\Api\PhpListRESTApiClient;
|
||||
use PhpList\PhpList;
|
||||
use Symfony\Component\Validator\Constraints\Callback;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Thelia\Form\BaseForm;
|
||||
|
||||
/**
|
||||
* Created by Franck Allimant, CQFDev <franck@cqfdev.fr>
|
||||
* Date: 18/07/2016 20:10
|
||||
*/
|
||||
class ConfigurationForm extends BaseForm
|
||||
{
|
||||
protected function buildForm()
|
||||
{
|
||||
$this->formBuilder
|
||||
->add(PhpList::REST_URL, "text", array(
|
||||
"label" => $this->translator->trans("Php List REST API URL", [], PhpList::DOMAIN_NAME),
|
||||
"required" => true,
|
||||
"constraints" => array(
|
||||
new NotBlank(),
|
||||
),
|
||||
"label_attr" => [
|
||||
'help' => $this->translator->trans("The URL to the REST API, sometehing like http://your.phplist.domain/lists/admin/?page=call&pi=restapi", [], PhpList::DOMAIN_NAME),
|
||||
]
|
||||
))
|
||||
->add(PhpList::API_LOGIN_NAME, "text", array(
|
||||
"label" => $this->translator->trans("Login name", [], PhpList::DOMAIN_NAME),
|
||||
"required" => true,
|
||||
"constraints" => array(
|
||||
new NotBlank(),
|
||||
),
|
||||
"label_attr" => [
|
||||
'help' => $this->translator->trans("The username of an account with administration rights", [], PhpList::DOMAIN_NAME),
|
||||
]
|
||||
))
|
||||
->add(PhpList::API_PASSWORD, "text", array(
|
||||
"label" => $this->translator->trans("Password", [], PhpList::DOMAIN_NAME),
|
||||
"required" => true,
|
||||
"constraints" => array(
|
||||
new NotBlank(),
|
||||
new Callback(array(
|
||||
"methods" => array(
|
||||
array($this, "checkApiLogin")
|
||||
),
|
||||
)),
|
||||
),
|
||||
"label_attr" => [
|
||||
'help' => $this->translator->trans("The password of the admin user above", [], PhpList::DOMAIN_NAME),
|
||||
]
|
||||
))
|
||||
->add(PhpList::API_SECRET, "text", array(
|
||||
"label" => $this->translator->trans("API secret key", [], PhpList::DOMAIN_NAME),
|
||||
"required" => true,
|
||||
"label_attr" => [
|
||||
'help' => $this->translator->trans("This is the secret code defined in the PhpList settings. Enter this code only if \"Require the secret code for Rest API calls\" is set to \"Yes\".", [], PhpList::DOMAIN_NAME),
|
||||
]
|
||||
))
|
||||
;
|
||||
|
||||
if (null !== PhpList::getConfigValue(PhpList::REST_URL)) {
|
||||
$this->formBuilder->add(
|
||||
PhpList::LIST_NAME,
|
||||
"choice",
|
||||
array(
|
||||
"label" => $this->translator->trans("User list name", [], PhpList::DOMAIN_NAME),
|
||||
"required" => true,
|
||||
"choices" => $this->getListNames(),
|
||||
"label_attr" => [
|
||||
'help' => $this->translator->trans("The name of the list the users are added or removed", [], PhpList::DOMAIN_NAME),
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function checkApiLogin($value, ExecutionContextInterface $context)
|
||||
{
|
||||
$data = $context->getRoot()->getData();
|
||||
|
||||
$message = false;
|
||||
|
||||
try {
|
||||
$api = new PhpListRESTApiClient(
|
||||
$data[PhpList::REST_URL],
|
||||
$data[PhpList::API_LOGIN_NAME],
|
||||
$data[PhpList::API_PASSWORD],
|
||||
$data[PhpList::API_SECRET]
|
||||
);
|
||||
|
||||
if (! $api->login()) {
|
||||
$message = $this->translator->trans(
|
||||
"Failed to login to the phpList REST API. Please check credentials.",
|
||||
[],
|
||||
PhpList::DOMAIN_NAME
|
||||
);
|
||||
}
|
||||
} catch (\Error $ex) {
|
||||
$message = $this->translator->trans(
|
||||
"Failed to login to the phpList REST API. Unexpected error occured: %err",
|
||||
[ '%err' => $ex->getMessage() ],
|
||||
PhpList::DOMAIN_NAME
|
||||
);
|
||||
}
|
||||
|
||||
if ($message) {
|
||||
$context->addViolation($message);
|
||||
}
|
||||
}
|
||||
|
||||
private function getListNames()
|
||||
{
|
||||
$api = new PhpListRESTApiClient(
|
||||
PhpList::getConfigValue(PhpList::REST_URL),
|
||||
PhpList::getConfigValue(PhpList::API_LOGIN_NAME),
|
||||
PhpList::getConfigValue(PhpList::API_PASSWORD),
|
||||
PhpList::getConfigValue(PhpList::API_SECRET)
|
||||
);
|
||||
|
||||
$result = ['(none)' => $this->translator->trans("No list found !", [], PhpList::DOMAIN_NAME)];
|
||||
|
||||
if ($api->login()) {
|
||||
if (false !== $listsData = $api->listsGet()) {
|
||||
$result = [];
|
||||
|
||||
foreach ($listsData as $item) {
|
||||
$result[$item->id] = $item->name;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new \Exception($this->translator->trans(
|
||||
"Failed to login to the phpList REST API. Please check credentials",
|
||||
[],
|
||||
PhpList::DOMAIN_NAME
|
||||
));
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* */
|
||||
/* Copyright (c) Franck Allimant, CQFDev */
|
||||
/* email : thelia@cqfdev.fr */
|
||||
/* web : http://www.cqfdev.fr */
|
||||
/* */
|
||||
/*************************************************************************************/
|
||||
|
||||
/**
|
||||
* Created by Franck Allimant, CQFDev <franck@cqfdev.fr>
|
||||
* Date: 05/03/2016 18:11
|
||||
*/
|
||||
|
||||
namespace PhpList\Hook;
|
||||
|
||||
use PhpList\PhpList;
|
||||
use RupturesDeStock\Model\RupturesDeStockQuery;
|
||||
use RupturesDeStock\RupturesDeStock;
|
||||
use Thelia\Core\Event\Hook\HookRenderBlockEvent;
|
||||
use Thelia\Core\Event\Hook\HookRenderEvent;
|
||||
use Thelia\Core\Hook\BaseHook;
|
||||
use Thelia\Tools\URL;
|
||||
|
||||
class HookManager extends BaseHook
|
||||
{
|
||||
public function onModuleConfigure(HookRenderEvent $event)
|
||||
{
|
||||
$vars = [
|
||||
PhpList::REST_URL => PhpList::getConfigValue(PhpList::REST_URL, ''),
|
||||
PhpList::API_LOGIN_NAME => PhpList::getConfigValue(PhpList::API_LOGIN_NAME, ''),
|
||||
PhpList::API_PASSWORD => PhpList::getConfigValue(PhpList::API_PASSWORD, ''),
|
||||
PhpList::API_SECRET => PhpList::getConfigValue(PhpList::API_SECRET, ''),
|
||||
PhpList::LIST_NAME => PhpList::getConfigValue(PhpList::LIST_NAME, '')
|
||||
];
|
||||
|
||||
$event->add(
|
||||
$this->render('phplist/module-configuration.html', $vars)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
<?php
|
||||
return array(
|
||||
// 'an english string' => 'The displayed english string',
|
||||
);
|
||||
@@ -1,4 +0,0 @@
|
||||
<?php
|
||||
return array(
|
||||
// 'an english string' => 'La traduction française de la chaine',
|
||||
);
|
||||
@@ -1,30 +0,0 @@
|
||||
<?php
|
||||
/*************************************************************************************/
|
||||
/* This file is part of the Thelia package. */
|
||||
/* */
|
||||
/* Copyright (c) OpenStudio */
|
||||
/* email : dev@thelia.net */
|
||||
/* web : http://www.thelia.net */
|
||||
/* */
|
||||
/* For the full copyright and license information, please view the LICENSE.txt */
|
||||
/* file that was distributed with this source code. */
|
||||
/*************************************************************************************/
|
||||
|
||||
namespace PhpList;
|
||||
|
||||
use Thelia\Module\BaseModule;
|
||||
|
||||
class PhpList extends BaseModule
|
||||
{
|
||||
/** @var string */
|
||||
const DOMAIN_NAME = 'phplist';
|
||||
|
||||
const REST_URL = 'rest_url';
|
||||
const API_LOGIN_NAME = 'api_login_name';
|
||||
const API_PASSWORD = 'api_password';
|
||||
const API_SECRET = 'api_secret';
|
||||
const LIST_NAME = 'list_name';
|
||||
|
||||
const RESYNC_EVENT = 'PhpList.resync';
|
||||
const BULK_ADD = 'PhpList.bulk_add';
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
# PhpList interface
|
||||
|
||||
This module synchronize a [phpList](https://www.phplist.org/) contact list of your choice whith the newsletter
|
||||
subscriptions and unsubscriptions on your shop :
|
||||
|
||||
- When a user subscribe to your newsletter on your shop, it is automatically added to the phpList contact list.
|
||||
- When a user unsubscribe from your list, it is also deleted from the phpList contact list.
|
||||
|
||||
Author: Franck Allimant, [CQFDev](https://www.cqfdev.fr) <franck@cqfdev.fr>
|
||||
|
||||
## Prerequistites
|
||||
|
||||
For this module to work, you need a working phpList instance,. The [RESTAPI plugin](https://resources.phplist.com/plugin/restapi)
|
||||
shoud be installed and configured on this instance.
|
||||
|
||||
## Installation
|
||||
|
||||
Install the module as usual, activate it, and go to the module configuration to define configuration parameters
|
||||
|
||||
To configure this module, please enter the required information, and click the "Save" button.
|
||||
|
||||
Once the proper crendentials have been entered, you'll have to choose the list that will be updated when a customer
|
||||
subscribe or unsubscribe to the newsletter.
|
||||
|
||||
## phpList / Thelia synchronization
|
||||
|
||||
To get instant synchronization between phpList and Thelia, be sure to use in the various phpList messages and templates:
|
||||
|
||||
- https://yourshop.tld/newsletter instead of `[SUBSCRIBEURL]`
|
||||
- https://yourshop.tld/newsletter-unsubscribe instead of `[UNSUBSCRIBEURL]`
|
||||
|
||||
You can also configure an automatic synchronisation :
|
||||
- in your cron by running the command `Thelia phplist:sync`
|
||||
- in your webcron by invoking the following URL: https://yourshop.tld/admin/module/phplist/sync
|
||||
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"name": "cqfdev/php-list-module",
|
||||
"description": "A Thelia 2 module to synchronize a phpList contact list of your choice with the newsletter subscriptions and unsubscriptions on your Thelia shop",
|
||||
"keywords": [
|
||||
"thelia",
|
||||
"thelia-module",
|
||||
"phplist",
|
||||
"email",
|
||||
"newsletter"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Franck Allimant",
|
||||
"email": "franck@cqfdev.fr",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"license": "LGPL-3.0+",
|
||||
"type": "thelia-module",
|
||||
"require": {
|
||||
"thelia/installer": "~1.1"
|
||||
},
|
||||
"extra": {
|
||||
"installer-name": "PhpList"
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12 general-block-decorator">
|
||||
<div class="row">
|
||||
<div class="col-md-12 title title-without-tabs">
|
||||
{intl d='phplist.bo.default' l="PHP List interface Configuration"}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
{form name="phplist.configuration.form"}
|
||||
|
||||
<form action="{url path="/admin/module/phplist/configure"}" method="post">
|
||||
{form_hidden_fields form=$form}
|
||||
|
||||
{include file = "includes/inner-form-toolbar.html"
|
||||
hide_flags = true
|
||||
page_url = "{url path='/admin/module/PhpList'}"
|
||||
close_url = "{url path='/admin/modules'}"
|
||||
hide_save_and_close_button = true
|
||||
}
|
||||
|
||||
{if $form_error}
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-danger">{$form_error_message}</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
{render_form_field form=$form field='rest_url' value=$rest_url}
|
||||
{render_form_field form=$form field='api_login_name' value=$api_login_name}
|
||||
{render_form_field form=$form field='api_password' value=$api_password}
|
||||
{render_form_field form=$form field='api_secret' value=$api_secret}
|
||||
{if $rest_url}
|
||||
{render_form_field form=$form field='list_name' value=$list_name}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<p class="title title-without-tabs">
|
||||
{intl d='phplist.bo.default' l="Prerequistites"}
|
||||
</p>
|
||||
|
||||
{intl d='phplist.bo.default' l='<p>For this module to work, the <a href="https://github.com/phpList/phplist-plugin-restapi" target="_blank">RESTAPI plugin</a> shoud be installed and configured on your PHP List instance.</p>
|
||||
<p>To configure this module, please enter the required information, and click the "Save" button.</p>
|
||||
<p>Once the proper crendentials have been entered, you\'ll have to choose the list that will be updated when a customer subscribe or unsubscribe to the newsletter.</p>'}
|
||||
|
||||
<p> </p> {* please kill me *}
|
||||
|
||||
<p class="title title-without-tabs">
|
||||
{intl d='phplist.bo.default' l="phpList / Thelia synchronization"}
|
||||
</p>
|
||||
|
||||
{intl d='phplist.bo.default' l='To get instant synchronization between phpList and Thelia, be sure to use in the various phpList messages and templates:'}
|
||||
<ul>
|
||||
<li>{intl d='phplist.bo.default' l='<strong>%sub</strong> instead of [SUBSCRIBEURL]' sub={url path="/newsletter"}}</li>
|
||||
<li>{intl d='phplist.bo.default' l='<strong>%unsub</strong> instead of [UNSUBSCRIBEURL]' unsub={url path="/newsletter-unsubscribe"}}</li>
|
||||
</ul>
|
||||
|
||||
<p>{intl d='phplist.bo.default' l='You can also configure an automatic synchronisation in your cron or webcron by invoking the following URL: %sync.' sync={url path='/admin/module/phplist/sync'}}</p>
|
||||
|
||||
{if $rest_url}
|
||||
<p><a href="{url path='/admin/module/phplist/sync'}" class="btn btn-primary">{intl d='phplist.bo.default' l="Synchronize now"}</a></p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{/form}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user