This commit is contained in:
2024-12-31 11:07:09 +01:00
parent df7915205d
commit e089172b15
1916 changed files with 165422 additions and 271 deletions

View File

@ -0,0 +1,457 @@
<?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Integrations;
// No direct access
defined('_JEXEC') or die;
class ActiveCampaign extends Integration
{
/**
* Create a new instance
* @param array $options The service's required options
*/
public function __construct($options)
{
parent::__construct();
$this->setKey($options);
$this->setEndpoint($options['endpoint'] . '/api/3');
$this->options->set('headers.Api-Token', $this->key);
}
/**
* Subscribe user to ActiveCampaign List
*
* https://developers.activecampaign.com/v3/reference#create-contact
*
* @param string $email The Email of the Contact
* @param string $name The name of the Contact (Name can be also declared in Custom Fields)
* @param string $list List ID
* @param string $tags Tags for this contact (comma-separated). Example: "tag1, tag2, etc"
* @param array $customfields Custom Fields
* @param boolean $updateexisting Update Existing User
*
* @return void
*/
public function subscribe($email, $name, $lists, $tags = '', $customfields = [], $updateexisting = true)
{
// Detect name
$name = (is_null($name) || empty($name)) ? $this->getNameFromCustomFields($customfields) : explode(' ', $name, 2);
$apiAction = ($updateexisting) ? 'contact/sync' : 'contacts';
$data = [
'contact' => [
'email' => $email,
'phone' => $this->getPhone($customfields),
'ip4' => \NRFramework\User::getIP()
],
];
// Add first and last name only if they are not empty, as ActiveCampaign will empty the fields if they are empty.
if (isset($name[0]) && $name[0])
{
$data['contact']['firstName'] = $name[0];
}
if (isset($name[1]) && $name[1])
{
$data['contact']['lastName'] = $name[1];
}
$this->post($apiAction, $data);
if (!$this->request_successful)
{
return;
}
// Retrive the contact's ID
$contact_id = $this->getContactIDFromResponse();
// Add Lists to Contact
$this->addListsToContact($contact_id, $lists);
// Add Tags to Contact
if (!empty($tags))
{
$tags = is_array($tags) ? $tags : explode(',', $tags);
$tag_ids = $this->convertTagNamesToIDs($tags);
if ($tag_ids && !empty($tag_ids))
{
$this->addTagsToContact($tag_ids, $contact_id);
}
}
// Add Custom Fields to Contact
$this->addCustomFieldsToContact($customfields, $contact_id);
}
/**
* Returns the phone number of the contact.
*
* @param array $customfields
*
* @return string
*/
private function getPhone($customfields)
{
$phone = $this->getCustomFieldValue('phone', $customfields);
if (is_string($phone))
{
return $phone;
}
if (isset($phone['code']) && isset($phone['value']) && $phone['value'])
{
$calling_code = \NRFramework\Countries::getCallingCodeByCountryCode($phone['code']);
$calling_code = $calling_code !== '' ? '+' . $calling_code : '';
$phone = $calling_code . $phone['value'];
}
else
{
$phone = '';
}
return $phone;
}
/**
* Update Custom Field Values for a Contact
*
* API Reference: https://developers.activecampaign.com/v3/reference#fieldvalues
*
* @param array $custom_fields Array of custom field values
* @param integer $contact_id The contact's ID
*
* @return mixed Null on failure, void on success
*/
private function addCustomFieldsToContact($custom_fields, $contact_id)
{
if (empty($custom_fields))
{
return;
}
$custom_fields = array_change_key_case($custom_fields);
if (!$all_custom_fields = $this->getAllCustomFields())
{
return;
}
foreach ($custom_fields as $custom_field_key => $custom_field_value)
{
if (empty($custom_field_value))
{
continue;
}
$custom_field = strtolower(trim($custom_field_key));
if (!array_key_exists($custom_field, $all_custom_fields))
{
continue;
}
// Let's add Custom Field to our contact
$custom_field_data = $all_custom_fields[$custom_field];
// Radio buttons expect a string. Not an array.
if ($custom_field_data['type'] == 'checkbox' && is_array($custom_field_value))
{
$custom_field_value = implode('||', $custom_field_value);
$custom_field_value = '||' . $custom_field_value . '||';
}
$this->post('fieldValues', [
'fieldValue' => [
'contact' => $contact_id,
'field' => $custom_field_data['id'],
'value' => $custom_field_value
]
]);
}
}
/**
* Add tags to contact
*
* API Reference: https://developers.activecampaign.com/v3/reference#create-contact-tag
*
* @param array $tag_ids Array of tag IDs
* @param integer $contact_id The contact's ID
*
* @return void
*/
private function addTagsToContact($tag_ids, $contact_id)
{
foreach ($tag_ids as $tag_id)
{
$this->post('contactTags', [
'contactTag' => [
'contact' => $contact_id,
'tag' => $tag_id,
]
]);
}
}
/**
* Convert a list of tag names to tag IDs
*
* @param array $tags Array ot tag names
*
* @return mixed Null on failure, assosiative tag name-based array on success.
*/
private function convertTagNamesToIDs($tags)
{
if (!$account_tags = $this->getAllTags())
{
return;
}
$account_tags = array_map('strtolower', $account_tags);
$tag_ids = [];
foreach ($tags as $tag)
{
if (empty($tag))
{
continue;
}
$tag = strtolower(trim($tag));
if (!$tag_id = array_search($tag, $account_tags))
{
continue;
}
$tag_ids[] = $tag_id;
}
return $tag_ids;
}
/**
* Retrieve all contact-based tags
*
* API Reference: https://developers.activecampaign.com/v3/reference#list-all-tasks
*
* @return mixed Null on failure, assosiative array on success
*/
private function getAllTags()
{
$tags = $this->get('tags');
if (!$tags || !is_array($tags) || !isset($tags['tags']))
{
return;
}
$tags_ = [];
foreach ($tags['tags'] as $tag)
{
if ($tag['tagType'] != 'contact')
{
continue;
}
$tags_[$tag['id']] = $tag['tag'];
}
return $tags_;
}
/**
* Add lists to contact
*
* @param integer $contact_id The Active Campaign Contact ID
* @param mixed $lists The list ID to add the contact to.
*
* @return void
*/
private function addListsToContact($contact_id, $lists)
{
$lists = is_array($lists) ? $lists : explode(',', $lists);
foreach ($lists as $list)
{
$this->post('contactLists', [
'contactList' => [
'list' => $list,
'contact' => $contact_id,
'status' => 1
]
]);
}
}
/**
* Determine the newly created contact's ID
*
* @return string
*/
private function getContactIDFromResponse()
{
$response = $this->last_response;
if (isset($response->body) && isset($response->body['contact']) && isset($response->body['contact']['id']))
{
return $response->body['contact']['id'];
}
}
/**
* Search for First Name and Last Name in Custom Fields and return an array with both values.
*
* @param array $customfields The Custom Fields array passed by the user.
*
* @return array
*/
private function getNameFromCustomFields($customfields)
{
return [
(string) $this->getCustomFieldValue(['first_name', 'First Name'], $customfields),
(string) $this->getCustomFieldValue(['last_name', 'Last Name'], $customfields)
];
}
/**
* Retrieve all account lists
*
* API Reference: https://developers.activecampaign.com/v3/reference#retrieve-all-lists
*
* @return mixed Null on failure, Array on success
*/
public function getLists()
{
$data = $this->get('lists');
if (!$data || !isset($data['lists']) || count($data['lists']) == 0)
{
return;
}
$lists = [];
foreach ($data['lists'] as $list)
{
$lists[] = [
'id' => $list['id'],
'name' => $list['name']
];
}
return $lists;
}
/**
* Get the last error returned by either the network transport, or by the API.
*
* API Reference: https://developers.activecampaign.com/v3/reference#errors
*
* @return string
*/
public function getLastError()
{
$error_code = $this->last_response->code;
$error_message = 'Active Campaign Error';
switch ((int) $error_code)
{
case 403:
$error_message = 'The request could not be authenticated or the authenticated user is not authorized to access the requested resource.';
break;
case 404:
$error_message = 'The requested resource does not exist.';
break;
case 422:
$error_message = 'The request could not be processed, usually due to a missing or invalid parameter.';
if (isset($this->last_response->body['errors']) && isset($this->last_response->body['errors'][0]))
{
$error_message = $this->last_response->body['errors'][0]['title'];
}
break;
}
return $error_message;
}
/**
* Returns the Active Campaign Account's Custom Fields
*
* API Reference: https://developers.activecampaign.com/v3/reference#retrieve-fields-1
*
* @return array
*/
public function getAllCustomFields()
{
$fields = $this->get('fields');
if (!$fields || !isset($fields['fields']))
{
return;
}
// Make our life easier by creating a title-based assosiative array
$f = [];
foreach ($fields['fields'] as $key => $field)
{
if (!$field || !isset($field['title']))
{
continue;
}
$key = strtolower(trim($field['title']));
$f[$key] = $field;
}
return $f;
}
/**
* Make an HTTP GET request for retrieving data.
*
* ActiveCampaign has a limit of max 100 results per page.
* https://developers.activecampaign.com/reference#pagination
*
* @param string $method URL of the API request method
* @param array $args Assoc array of arguments (usually your data)
*
* @return array|false Assoc array of API response, decoded from JSON
*/
public function get($method, $args = array())
{
$args['limit'] = isset($args['limit']) ? $args['limit'] : 100;
$args['offset'] = isset($args['offset']) ? $args['offset'] : 0;
$response = parent::get($method, $args);
if ($args['offset'] < (int) $response['meta']['total'])
{
$args['offset'] += $args['limit'];
$response_next = $this->get($method, $args);
$response[$method] = array_merge($response[$method], $response_next[$method]);
}
return $response;
}
}

View File

@ -0,0 +1,118 @@
<?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Integrations;
// No direct access
defined('_JEXEC') or die;
class Brevo extends Integration
{
/**
* Create a new instance
* @param array $options The service's required options
* @throws \Exception
*/
public function __construct($options)
{
parent::__construct();
$this->setKey($options);
$this->setEndpoint('https://api.brevo.com/v3');
$this->options->set('headers.api-key', $this->key);
}
/**
* Subscribes a user to a Brevo Account
*
* API Reference v3:
* https://developers.brevo.com/reference/createcontact
*
* @param string $email The user's email
* @param array $params All the form fields
* @param string $listid The List ID
* @param boolean $update_existing Whether to update the existing contact (Only in v3)
*
* @return boolean
*/
public function subscribe($email, $params, $listid = false, $update_existing = true)
{
$data = [
'email' => $email,
'attributes' => (object) $params,
'updateEnabled' => $update_existing
];
if ($listid)
{
$data['listIds'] = [(int) $listid];
}
$this->post('contacts', $data);
return true;
}
/**
* Returns all Campaign lists
*
* API Reference v3:
* https://developers.brevo.com/reference/getlists-1
*
* @return array
*/
public function getLists()
{
$data = [
'offset' => 0,
'limit' => 50
];
$lists = [];
$data = $this->get('contacts/lists', $data);
// sanity check
if (!isset($data['lists']) || !is_array($data['lists']) || $data['count'] == 0)
{
return $lists;
}
foreach ($data['lists'] as $key => $list)
{
$lists[] = [
'id' => $list['id'],
'name' => $list['name']
];
}
return $lists;
}
/**
* Get the last error returned by either the network transport, or by the API.
*
* API Reference:
* https://developers.brevo.com/docs/how-it-works#error-codes
*
* @return string
*/
public function getLastError()
{
$body = $this->last_response->body;
$message = '';
if (!isset($body['code']))
{
return $message;
}
return $body['message'];
}
}

View File

@ -0,0 +1,205 @@
<?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Integrations;
// No direct access
defined('_JEXEC') or die;
class CampaignMonitor extends Integration
{
/**
* Create a new instance
*
* @param array $options The service's required options
*/
public function __construct($options)
{
parent::__construct();
$this->setKey($options);
$this->setEndpoint('https://api.createsend.com/api/v3.1');
$this->options->set('userauth', $this->key);
$this->options->set('passwordauth', 'nopass');
}
/**
* Subscribe user to Campaign Monitor
*
* API References:
* https://www.campaignmonitor.com/api/subscribers/#importing_many_subscribers
* Reminder:
* The classic add_subscriber method of Campaign Monitor's API is NOT instantaneous!
* It is suggested to use their import method for instantaneous subscriptions!
*
* @param string $email User's email address
* @param string $name User's Name
* @param string $list The Campaign Monitor list unique ID
* @param array $custom_fields Custom Fields
*
* @return void
*/
public function subscribe($email, $name, $list, $customFields = array())
{
$data = array(
'Subscribers' => array(
array(
'EmailAddress' => $email,
'Name' => $name,
'Resubscribe' => true,
),
),
);
if (is_array($customFields) && count($customFields))
{
$data['Subscribers'][0]['CustomFields'] = $this->validateCustomFields($customFields, $list);
}
$this->post('subscribers/' . $list . '/import.json', $data);
return true;
}
/**
* Returns a new array with valid only custom fields
*
* @param array $formCustomFields Array of custom fields
*
* @return array Array of valid only custom fields
*/
public function validateCustomFields($formCustomFields, $list)
{
$fields = array();
if (!is_array($formCustomFields))
{
return $fields;
}
$listCustomFields = $this->get('lists/' . $list . '/customfields.json');
if (!$this->request_successful)
{
return $fields;
}
$formCustomFieldsKeys = array_keys($formCustomFields);
foreach ($listCustomFields as $listCustomField)
{
$field_name = $listCustomField['FieldName'];
if (!in_array($field_name, $formCustomFieldsKeys))
{
continue;
}
$value = $formCustomFields[$field_name];
// Always convert custom field value to array, to support multiple values in a custom field.
$value = is_array($value) ? $value : (array) $value;
foreach ($value as $val)
{
$fields[] = array(
'Key' => $field_name,
'Value' => $val,
);
}
}
return $fields;
}
/**
* Get the last error returned by either the network transport, or by the API.
*
* @return string
*/
public function getLastError()
{
$body = $this->last_response->body;
$message = '';
if (isset($body['Message']))
{
$message = $body['Message'];
}
if (isset($body['ResultData']['FailureDetails'][0]['Message']))
{
$message .= ' - ' . $body['ResultData']['FailureDetails'][0]['Message'];
}
return $message;
}
/**
* Returns all Client lists
*
* https://www.campaignmonitor.com/api/clients/#getting-subscriber-lists
*
* @return array
*/
public function getLists()
{
$clients = $this->getClients();
if (!is_array($clients))
{
return;
}
$lists = array();
foreach ($clients as $key => $client)
{
if (!isset($client['ClientID']))
{
continue;
}
$clientLists = $this->get('/clients/' . $client['ClientID'] . '/lists.json');
if (!is_array($clientLists))
{
continue;
}
foreach ($clientLists as $key => $clientList)
{
$lists[] = array(
'id' => $clientList['ListID'],
'name' => $clientList['Name']
);
}
}
return $lists;
}
/**
* Get Clients
*
* https://www.campaignmonitor.com/api/account/
*
* @return mixed Array on success, Null on fail
*/
private function getClients()
{
$clients = $this->get('/clients.json');
if (!$this->success())
{
return;
}
return $clients;
}
}

View File

@ -0,0 +1,161 @@
<?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Integrations;
// No direct access
defined('_JEXEC') or die;
use Joomla\String\StringHelper;
class ConvertKit extends Integration
{
/**
* Create a new instance
*
* @param string $api_key Your ConvertKit API Key
*/
public function __construct($api_key)
{
parent::__construct();
$this->setKey($api_key);
$this->setEndpoint('https://api.convertkit.com/v3');
}
/**
* Subscribe a user to a ConvertKit Form
*
* API Reference:
* http://help.convertkit.com/article/33-api-documentation-v3
*
* @param string $email The subscriber's email
* @param string $formid The account owner's form id
* @param array $params The form's parameters
*
* @return boolean
*/
public function subscribe($email, $formid, $params)
{
$first_name = (isset($params['first_name'])) ? $params['first_name'] : '';
$tags = (isset($params['tags'])) ? $this->convertTagnamesToTagIDs($params['tags']) : '';
$fields = $this->validateCustomFields($params);
$data = array(
'api_key' => $this->key,
'email' => $email,
'first_name' => $first_name,
'tags' => $tags,
'fields' => $fields,
);
$this->post('forms/' . $formid . '/subscribe', $data);
return true;
}
/**
* Converts tag names to tag IDs for the subscribe method
*
* @param string $tagnames comma separated list of tagnames
*
* @return string comma separated list of tag IDs
*/
public function convertTagnamesToTagIDs($tagnames)
{
if (empty($tagnames))
{
return;
}
$tagArray = !is_array($tagnames) ? explode(',', $tagnames) : $tagnames;
$tagnames = array_map('trim', $tagArray);
$accountTags = $this->get('tags', array('api_key' => $this->key));
if (empty($accountTags) || !$this->request_successful)
{
return;
}
$tagIDs = array();
foreach ($accountTags['tags'] as $tag)
{
foreach ($tagnames as $tagname)
{
if (StringHelper::strcasecmp($tag['name'], $tagname) == 0)
{
$tagIDs[] = $tag['id'];
break;
}
}
}
return implode(',', $tagIDs);
}
/**
* Returns a new array with valid only custom fields
*
* @param array $formCustomFields Array of custom fields
*
* @return array Array of valid only custom fields
*/
public function validateCustomFields($formCustomFields)
{
if (!is_array($formCustomFields))
{
return;
}
$customFields = $this->get('custom_fields', array('api_key' => $this->key));
if (!$this->request_successful)
{
return;
}
$fields = array();
$formCustomFieldsKeys = array_keys($formCustomFields);
foreach ($customFields['custom_fields'] as $customField)
{
if (in_array($customField['key'], $formCustomFieldsKeys))
{
$fields[$customField['key']] = $formCustomFields[$customField['key']];
}
}
return $fields;
}
/**
* Get the last error returned by either the network transport, or by the API.
*
* @return string
*/
public function getLastError()
{
$body = $this->last_response->body;
$message = '';
if (isset($body['error']) && !empty($body['error']))
{
$message = $body['error'];
}
if (isset($body['message']) && !empty($body['message']))
{
$message .= ' - ' . $body['message'];
}
return $message;
}
}

View File

@ -0,0 +1,287 @@
<?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Integrations;
// No direct access
defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
class Drip extends Integration
{
/**
* Create a new instance
*
* @param string $key Your Drip API key
* @param string $account_id Your Drip Account ID
*/
public function __construct($options)
{
parent::__construct();
if (!(isset($options['api']) && isset($options['account_id']))) {
return;
}
$this->setKey($options['api']);
$this->setEndpoint('https://api.getdrip.com/v2/' . $options['account_id']);
$this->options->set('headers.Authorization', 'Basic ' . base64_encode($this->key . ':'));
}
/**
* Subscribe user to Drip
*
* API References:
* https://developer.drip.com/#create-or-update-a-subscriber
*
* @param string $email User's email address
* @param string $campaign_id The Campaign ID
* @param string $name The name of the Contact (Name can be also declared in Custom Fields)
* @param Object $custom_fields Custom Fields
* @param mixed $tags Tags for this contact (comma-separated). Example: 'tag1, tag2, etc'
* @param boolean $update_existing Update existing user
* @param boolean $double_optin Send MailChimp confirmation email?
*
* @return void
*/
public function subscribe($email, $campaign_id, $name = null, $custom_fields = array(), $tags = '', $update_existing = true, $double_optin = false)
{
// Detect name
$name = (is_null($name) || empty($name)) ? $this->getNameFromCustomFields($custom_fields) : explode(' ', $name, 2);
// We use this boolean to see if the user has subscribed the campaign
// This is used for the `update_existing` parameter
$subscriber_exists = $this->subscriberIsInCampaign($email, $campaign_id);
// Check if we need to update the user
if ($update_existing == false && $subscriber_exists)
{
throw new \Exception(Text::_('NR_YOU_ARE_ALREADY_A_SUBSCRIBER'), 1);
}
// Remove tags from custom fields
$custom_fields_parse = $custom_fields;
if (isset($custom_fields_parse['tags']))
{
unset($custom_fields_parse['tags']);
}
// Create or Update a Subscriber
$data = [
'subscribers' => [
[
'email' => $email,
'first_name' => isset($name[0]) ? $name[0] : '',
'last_name' => isset($name[1]) ? $name[1] : '',
'address1' => $this->getCustomFieldValue('address1', $custom_fields),
'address2' => $this->getCustomFieldValue('address2', $custom_fields),
'city' => $this->getCustomFieldValue('city', $custom_fields),
'state' => $this->getCustomFieldValue('state', $custom_fields),
'zip' => $this->getCustomFieldValue('zip', $custom_fields),
'country' => $this->getCustomFieldValue('country', $custom_fields),
'phone' => $this->getCustomFieldValue('phone', $custom_fields),
'custom_fields' => $custom_fields_parse,
'tags' => $this->getTags($tags)
]
]
];
$this->post('subscribers', $data);
// If we are updating a user, dont try re-assigning him to a campaign
// If we are updating a user but he just subscribed, then assign him to a campaign
if ($update_existing == false || $subscriber_exists == false)
{
// Assign the newly created subscriber to the campaign
$this->assignSubscriberToCampaign($email, $campaign_id, $double_optin);
}
return true;
}
/**
* Assign a Subscriber to a Campaign
*
* https://developer.drip.com/?shell#subscribe-someone-to-a-campaign
*
* @return void
*/
private function assignSubscriberToCampaign($email, $campaign_id, $double_optin)
{
// Subscribe user to a campaign
$campaignSubAPI = 'campaigns/' . $campaign_id . '/subscribers';
$data = [
'subscribers' => [
[
'email' => $email,
'double_optin' => (bool) $double_optin
]
]
];
$this->post($campaignSubAPI, $data);
}
/**
* Returns an array of tags or an empty string if no tags provided
*
* @return mixed
*/
private function getTags($tags) {
if (empty($tags))
{
return;
}
if (is_string($tags))
{
$tags = array_map('trim', explode(',', $tags));
}
return $tags;
}
/**
* Returns whether the subscriber is in a campaign
*
* https://developer.drip.com/?shell#list-all-of-a-subscriber-39-s-campaign-subscriptions
*
* @return bool
*/
private function subscriberIsInCampaign($email, $campaign_id)
{
$found_campaign = false;
$subscriber_id = $this->getSubscriberIdFromEmail($email);
// Use does not exist in Drip
if (empty($subscriber_id))
{
return false;
}
$subscriber_campaigns = $this->getSubscriberCampaigns($subscriber_id);
foreach ($subscriber_campaigns as $c)
{
if ($c['campaign_id'] == $campaign_id)
{
$found_campaign = true;
break;
}
}
return $found_campaign;
}
/**
* Returns the ID of the subscriber from email
*
* https://developer.drip.com/?shell#fetch-a-subscriber
*
* @return string
*/
private function getSubscriberIdFromEmail($email)
{
$data = $this->get('subscribers/' . $email);
return isset($data['subscribers']) ? $data['subscribers'][0]['id'] : '';
}
/**
* Returns all subscriber's campaigns
*
* https://developer.drip.com/?javascript#list-all-of-a-subscriber-39-s-campaign-subscriptions
*
* @return array
*/
private function getSubscriberCampaigns($subscriberId)
{
$data = $this->get('subscribers/' . $subscriberId . '/campaign_subscriptions');
return isset($data['campaign_subscriptions']) ? $data['campaign_subscriptions'] : array();
}
/**
* Returns all available Drip campaigns
*
* https://developer.drip.com/?shell#list-all-campaigns
*
* @return array
*/
public function getLists()
{
$data = $this->get('campaigns');
if (!$this->success())
{
return;
}
if (!isset($data['campaigns']) || !is_array($data['campaigns']))
{
return;
}
$campaigns = [];
foreach ($data['campaigns'] as $key => $campaign)
{
$campaigns[] = array(
'id' => $campaign['id'],
'name' => $campaign['name']
);
}
return $campaigns;
}
/**
* Search for First Name and Last Name in Custom Fields and return an array with both values.
*
* @param array $custom_fields The Custom Fields array passed by the user.
*
* @return array
*/
private function getNameFromCustomFields($custom_fields)
{
return [
(string) $this->getCustomFieldValue(['first_name', 'First Name'], $custom_fields),
(string) $this->getCustomFieldValue(['last_name', 'Last Name'], $custom_fields)
];
}
/**
* Get the last error returned by either the network transport, or by the API.
*
* @return string
*/
public function getLastError()
{
$body = $this->last_response->body;
$messages = '';
if (isset($body['errors']))
{
foreach ($body['errors'] as $error)
{
$messages .= ' - ' . $error['message'];
}
}
return $messages;
}
}

View File

@ -0,0 +1,184 @@
<?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Integrations;
// No direct access
defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
class ElasticEmail extends Integration
{
protected $endpoint = 'https://api.elasticemail.com/v2';
/**
* Create a new instance
*
* @param array $options The service's required options
* @throws \Exception
*/
public function __construct($options)
{
parent::__construct();
$this->setKey($options);
}
/**
* Subscribe user to ElasticEmail
*
* API References:
* http://api.elasticemail.com/public/help#Contact_Add
* http://api.elasticemail.com/public/help#Contact_Update
*
* @param string $email User's email address
* @param string $list The ElasticEmail List unique ID
* @param string $publicAccountID The ElasticEmail PublicAccountID
* @param array $params The form's parameters
* @param boolean $update_existing Update existing user
* @param boolean $double_optin Send ElasticEmail confirmation email?
*
* @return void
*/
public function subscribe($email, $list, $publicAccountID, $params = array(), $update_existing = true, $double_optin = false)
{
$data = array(
'apikey' => $this->key,
'email' => $email,
'publicAccountID' => $publicAccountID,
'publicListID' => $list,
'sendActivation' => $double_optin ? 'true' : 'false',
'consentIP' => \NRFramework\User::getIP()
);
if (is_array($params) && count($params))
{
foreach ($params as $param_key => $param_value)
{
$data[$param_key] = (is_array($param_value)) ? implode(',', $param_value) : $param_value;
}
}
if (!$update_existing)
{
return $this->get('/contact/add', $data);
}
if ($this->getContact($email))
{
$data['clearRestOfFields'] = 'false';
$this->get('/contact/update', $data);
}
else
{
$this->get('/contact/add', $data);
}
return true;
}
/**
* Returns all available ElasticEmail lists
*
* http://api.elasticemail.com/public/help#List_list
*
* @return array
*/
public function getLists()
{
$data = $this->get('/list/list', array('apikey' => $this->key));
if (!$this->success())
{
return;
}
$lists = array();
if (!isset($data['data']) || !is_array($data['data']))
{
return $lists;
}
foreach ($data['data'] as $key => $list)
{
$lists[] = array(
'id' => $list['publiclistid'],
'name' => $list['listname']
);
}
return $lists;
}
/**
* Check to see if a contact exists
*
* @param string $email The contact's email
*
* @return boolean
*/
public function getContact($email)
{
$contact = $this->get('/contact/loadcontact', array('apikey' => $this->key, 'email' => $email));
return (bool) $contact['success'];
}
/**
* Get the Elastic Email Public Account ID
*
* @return string
*/
public function getPublicAccountID()
{
$data = $this->get('/account/load', array('apikey' => $this->key));
if (isset($data['data']['publicaccountid']))
{
return $data['data']['publicaccountid'];
}
throw new \Exception(Text::_('NR_ELASTICEMAIL_UNRETRIEVABLE_PUBLICACCOUNTID'), 1);
}
/**
* Get the last error returned by either the network transport, or by the API.
*
* @return string
*/
public function getLastError()
{
$body = $this->last_response->body;
if (isset($body['error']))
{
return $body['error'];
}
}
/**
* Check if the response was successful or a failure. If it failed, store the error.
*
* @return bool If the request was successful
*/
protected function determineSuccess()
{
$code = $this->last_response->code;
$body = $this->last_response->body;
if ($code >= 200 && $code <= 299 && !isset($body['error']))
{
return ($this->request_successful = true);
}
$this->last_error = 'Unknown error, call getLastResponse() to find out what happened.';
return false;
}
}

View File

@ -0,0 +1,297 @@
<?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Integrations;
// No direct access
defined('_JEXEC') or die;
class GetResponse extends Integration
{
/**
* Create a new instance
*
* @param array $options The service's required options
*/
public function __construct($options)
{
parent::__construct();
$this->setKey($options);
$this->endpoint = 'https://api.getresponse.com/v3';
$this->options->set('headers.X-Auth-Token', 'api-key ' . $this->key);
$this->options->set('headers.Accept-Encoding', 'gzip,deflate');
}
/**
* Subscribe user to GetResponse Campaign
*
* https://apidocs.getresponse.com/v3/resources/contacts#contacts.create
*
* TODO: Update existing contact
*
* @param string $email Email of the Contact
* @param string $name The name of the Contact
* @param int $dayOfCycle Enter 0 to add to the start day of the cycle.
* @param object $campaign Campaign ID
* @param object $customFields Collection of custom fields
* @param object $update_existing Update existing contact
* @param array $tags Set user tags
* @param string $tags_replace Determines what changes to make to the subscriber's tags. Values: add_only, replace_all
*
* @return void
*/
public function subscribe($email, $name, $campaign, $customFields, $update_existing, $dayOfCycle = 0, $tags = [], $tags_replace = 'add_only')
{
$data = [
'email' => $email,
'name' => $name,
'dayOfCycle' => $dayOfCycle,
'campaign' => ['campaignId' => $campaign],
'customFieldValues' => $this->validateCustomFields($customFields),
'ipAddress' => \NRFramework\User::getIP()
];
if (empty($name) || is_null($name))
{
unset($data['name']);
}
$contactId = null;
$service_tags = [];
if ($tags)
{
$service_tags = $this->getServiceTags();
}
// Replace all existing contact tags with new ones
if ($tags && $tags_replace === 'replace_all')
{
$data['tags'] = $this->validateTags($tags, $service_tags, $tags_replace);
}
if ($update_existing)
{
$contactId = $this->getContact($email);
}
$endpoint = 'contacts';
$endpoint = !empty($contactId) ? $endpoint . '/' . $contactId : $endpoint;
$this->post($endpoint, $data);
// Add new tags to the contact
if ($tags && $tags_replace === 'add_only' && $contactId)
{
$data = ['tags' => $this->validateTags($tags, $service_tags, $tags_replace)];
$this->post('contacts/' . $contactId . '/tags', $data);
}
}
/**
* Return all service tags.
*
* @return array
*/
private function getServiceTags()
{
$tags = [];
foreach ($this->get('tags') as $tag)
{
$tags[$tag['tagId']] = $tag['name'];
}
return $tags;
}
/**
* Validates and returns the valid tags.
*
* @param array $tags
* @param array $service_tags
*
* @return array
*/
private function validateTags($tags = [], $service_tags = [], $tags_replace = 'add_only')
{
$final_tags = [];
foreach ($tags as $index => $tag)
{
$valid = false;
// Find tag in service tags and add it to final tags list
foreach ($service_tags as $tagId => $tagName)
{
if ($tagId === $tag || $tagName === $tag)
{
$valid = true;
// Add to final list
$final_tags[] = [
'tagId' => $tagId
];
}
}
// Add invalid tags
if (!$valid && $tags_replace == 'add_only')
{
$new_tag = $this->createTag($tag);
$final_tags[] = [
'tagId' => $new_tag['tagId']
];
}
}
return $final_tags;
}
private function createTag($tag)
{
$data = [
'name' => $tag
];
return $this->post('tags', $data);
}
/**
* Returns a new array with valid only custom fields
*
* @param array $customFields Array of custom fields
*
* @return array Array of valid only custom fields
*/
public function validateCustomFields($customFields)
{
$fields = [];
if (!is_array($customFields))
{
return $fields;
}
$accountCustomFields = $this->get('custom-fields');
if (!$this->request_successful)
{
return $fields;
}
foreach ($accountCustomFields as $key => $customField)
{
if (!isset($customFields[$customField['name']]))
{
continue;
}
$fields[] = [
'customFieldId' => $customField['customFieldId'],
'value' => [$customFields[$customField['name']]]
];
}
return $fields;
}
/**
* Get the last error returned by either the network transport, or by the API.
* If something didn't work, this should contain the string describing the problem.
*
* @return string describing the error
*/
public function getLastError()
{
$body = $this->last_response->body;
if (!isset($body['context']) || !isset($body['context'][0]))
{
return $body['codeDescription'] . ' - ' . $body['message'];
}
$error = $body['context'][0];
// GetResponse returns a JSON string as $error and we try to decode it so we can return a more human-friendly error message
$error = is_string($error) && json_encode($error, true) ? json_decode($error, true) : $error;
if (is_array($error) && isset($error['fieldName']))
{
$errorFieldName = is_array($error['fieldName']) ? implode(' ', $error['fieldName']) : $error['fieldName'];
return $errorFieldName . ': ' . $error['message'];
}
return (is_array($error)) ? implode(' ', $error) : $error;
}
/**
* Returns all available GetResponse campaigns
*
* https://apidocs.getresponse.com/v3/resources/campaigns#campaigns.get.all
*
* @return array
*/
public function getLists()
{
$data = $this->get('campaigns');
if (!$this->success())
{
return;
}
if (!is_array($data) || !count($data))
{
return;
}
$lists = [];
foreach ($data as $key => $list)
{
$lists[] = [
'id' => $list['campaignId'],
'name' => $list['name']
];
}
return $lists;
}
/**
* Get the Contact resource
*
* @param string $email The email of the contact which we want to retrieve
*
* @return string The Contact ID
*/
public function getContact($email)
{
if (!isset($email))
{
return;
}
$data = $this->get('contacts', ['query[email]' => $email]);
if (empty($data))
{
return;
}
// the returned data is an array with only one contact
$contactId = $data[0]['contactId'];
return ($contactId) ? $contactId : null;
}
}

View File

@ -0,0 +1,109 @@
<?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Integrations;
// No direct access
defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
/**
* The HCaptcha Wrapper
*/
class HCaptcha extends Integration
{
/**
* Service Endpoint
*
* @var string
*/
protected $endpoint = 'https://hcaptcha.com/siteverify';
/**
* Create a new instance
*
* @param array $options
*
* @throws \Exception
*/
public function __construct($options = [])
{
parent::__construct();
if (!array_key_exists('secret', $options))
{
$this->setError('NR_RECAPTCHA_INVALID_SECRET_KEY');
throw new \Exception($this->getLastError());
}
$this->setKey($options['secret']);
}
/**
* Calls the hCaptcha siteverify API to verify whether the user passes hCaptcha test.
*
* @param string $response Response string from hCaptcha verification.
* @param string $remoteip IP address of end user
*
* @return bool Returns true if the user passes hCaptcha test
*/
public function validate($response, $remoteip = null)
{
if (empty($response) || is_null($response))
{
return $this->setError('NR_RECAPTCHA_PLEASE_VALIDATE');
}
// remove these headers in order for hCaptcha to be abl to process the request
$this->options->remove('headers.Accept');
$this->options->remove('headers.Content-Type');
// do not encode request
$this->setEncode(false);
$data = [
'secret' => $this->key,
'response' => $response,
];
$this->post('', $data);
return true;
}
/**
* Check if the response was successful or a failure. If it failed, store the error.
*
* @return bool If the request was successful
*/
protected function determineSuccess()
{
$success = parent::determineSuccess();
$body = $this->last_response->body;
if ($body['success'] == false && array_key_exists('error-codes', $body) && count($body['error-codes']) > 0)
{
$success = $this->setError(implode(', ', $body['error-codes']));
}
return ($this->request_successful = $success);
}
/**
* Set wrapper error text
*
* @param String $error The error message to display
*/
private function setError($error)
{
$this->last_error = Text::_('NR_HCAPTCHA') . ': ' . Text::_($error);
return false;
}
}

View File

@ -0,0 +1,140 @@
<?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Integrations;
// No direct access
defined('_JEXEC') or die;
class HubSpot extends Integration
{
/**
* Create a new instance
*
* @param string $key Your HubSpot API key
*/
public function __construct($options)
{
parent::__construct();
$this->setKey(is_array($options) ? $options['api'] : $options);
$this->setEndpoint('https://api.hubapi.com');
}
/**
* Subscribe user to HubSpot
*
* API References:
* http://developers.hubspot.com/docs/methods/contacts/update_contact-by-email
*
* @param string $email User's email address
* @param string $params The forms extra fields
*
* @return void
*/
public function subscribe($email, $params)
{
$fields = $this->validateCustomFields($params);
$fields[] = array('property' => 'email', 'value' => $email);
$data = array(
'properties' => $fields
);
$this->post('contacts/v1/contact/createOrUpdate/email/' . $email . '/?hapikey=' . $this->key, $data);
return true;
}
/**
* Get the last error returned by either the network transport, or by the API.
*
* API References:
* http://developers.hubspot.com/docs/faq/api-error-responses
*
* @return string
*/
public function getLastError()
{
$body = $this->last_response->body;
$message = '';
if ((isset($body['status'])) && ($body['status'] == 'error'))
{
$message = $body['message'];
}
if (isset($body['validationResults']) && is_array($body['validationResults']) && count($body['validationResults']))
{
foreach ($body['validationResults'] as $key => $validation)
{
if ($validation['isValid'] === false)
{
$message .= ' - ' . $validation['message'];
}
}
}
return $message;
}
/**
* Returns a new array with valid only custom fields
*
* API References:
* http://developers.hubspot.com/docs/methods/contacts/v2/get_contacts_properties
*
* @param array $formCustomFields Array of custom fields
*
* @return array Array of valid only custom fields
*/
public function validateCustomFields($formCustomFields)
{
$fields = array();
if (!is_array($formCustomFields))
{
return $fields;
}
$accountFields = $this->get('properties/v1/contacts/properties?hapikey='.$this->key);
if (!$this->request_successful)
{
return $fields;
}
$accountFieldsNames = array_map(
function ($ar)
{
return $ar['name'];
}, $accountFields
);
$formCustomFieldsKeys = array_keys($formCustomFields);
foreach ($accountFieldsNames as $accountFieldsName)
{
if (!in_array($accountFieldsName, $formCustomFieldsKeys))
{
continue;
}
$fields[] = array(
"property" => $accountFieldsName,
"value" => $formCustomFields[$accountFieldsName],
);
}
return $fields;
}
}

View File

@ -0,0 +1,220 @@
<?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Integrations;
// No direct access
defined('_JEXEC') or die;
class HubSpot3 extends Integration
{
/**
* Create a new instance
*
* @param string $key Your HubSpot API key
*/
public function __construct($options)
{
parent::__construct();
$this->setKey($options);
$this->setEndpoint('https://api.hubapi.com/crm/v3');
$this->options->set('headers.Authorization', 'Bearer ' . $this->key);
}
/**
* Create/Update a HubSpot Contact
*
* API References:
* https://developers.hubspot.com/docs/api/crm/contacts
*
* @param string $email User's email address
* @param string $params The forms extra fields
* @param bool $update_existing Set whether to update an existing user
*
* @return void
*/
public function subscribe($email, $params, $update_existing = true)
{
$contact_data = $this->contactExists($email);
if (!$update_existing)
{
if ($contact_data)
{
throw new \Exception('Contact already exists.');
}
}
$default_property = ['email' => $email];
$other_properties = $this->validateCustomFields($params);
$data = [
'properties' => array_merge($default_property, $other_properties)
];
$method = 'post';
$endpoint = 'objects/contacts';
if ($update_existing && $contact_data)
{
$method = 'patch';
$endpoint .= '/' . $contact_data['id'];
};
$this->$method($endpoint, $data);
// If a list exists, add the contact to that list.
if ($this->success() && isset($params['list']) && !empty($params['list']))
{
$this->addContactToStaticList($email, $params['list']);
}
}
/**
* Returns all lists.
*
* @return array
*/
public function getLists()
{
$this->endpoint = $this->getV1Endpoint();
$data = $this->get('lists/static');
if (!$this->success())
{
return;
}
if (!is_array($data) || !count($data) || !isset($data['lists']))
{
return;
}
$lists = [];
foreach ($data['lists'] as $key => $list)
{
$lists[] = [
'id' => $list['listId'],
'name' => $list['name']
];
}
return $lists;
}
/**
* Add contact to a static list.
*
* @param string $email
* @param int $list_id
*
* @return void
*/
public function addContactToStaticList($email, $list_id)
{
$this->endpoint = $this->getV1Endpoint();
$data = (object) [ 'emails' => [ $email ] ];
$this->post('lists/' . $list_id . '/add', $data);
}
/**
* Return the v1 endpoint.
*
* @return string
*/
private function getV1Endpoint()
{
return 'https://api.hubapi.com/contacts/v1';
}
/**
* Check whether contact already exists.
*
* @param string $email
*
* @return bool
*/
public function contactExists($email)
{
$contact = $this->get('objects/contacts/' . $email . '?idProperty=email');
return $this->success() ? $contact : false;
}
/**
* Returns a new array with valid only custom fields
*
* API References:
* https://developers.hubspot.com/docs/api/crm/properties
*
* @param array $formCustomFields Array of custom fields
*
* @return array Array of valid only custom fields
*/
public function validateCustomFields($formCustomFields)
{
$fields = [];
if (!is_array($formCustomFields))
{
return $fields;
}
$contactCustomFields = $this->get('properties/Contact');
if (!$this->request_successful)
{
return $fields;
}
$customFieldNames = array_map(
function ($ar)
{
return $ar['name'];
}, $contactCustomFields['results']
);
$formCustomFieldsKeys = array_keys($formCustomFields);
foreach ($customFieldNames as $accountFieldName)
{
if (!in_array($accountFieldName, $formCustomFieldsKeys))
{
continue;
}
$fields[$accountFieldName] = $formCustomFields[$accountFieldName];
}
return $fields;
}
/**
* Get the last error returned by either the network transport, or by the API.
*
* @return string
*/
public function getLastError()
{
$body = $this->last_response->body;
if (isset($body['status']) && $body['status'] === 'error')
{
return $body['message'];
}
}
}

View File

@ -0,0 +1,209 @@
<?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Integrations;
// No direct access
defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
class IContact extends Integration
{
public $accountID;
public $clientFolderID;
/**
* Create a new instance
* @param array $options The service's required options
*/
public function __construct($options)
{
parent::__construct();
$this->endpoint = 'https://app.icontact.com/icp/a';
$this->options->set('headers.API-Version', '2.2');
$this->options->set('headers.API-AppId', $options['appID']);
$this->options->set('headers.API-Username', $options['username']);
$this->options->set('headers.API-Password', $options['appPassword']);
$this->setAccountID($options['accountID']);
$this->setClientFolderID($options['clientFolderID']);
}
/**
* Finds and sets the iContact AccountID
*
* @param mixed $accountID
*/
public function setAccountID($accountID = false)
{
if ($accountID)
{
$this->accountID = $accountID;
}
$accounts = $this->get('');
if (!$this->success())
{
throw new \Exception($this->getLastError());
}
// Make sure the account is active
if (intval($accounts['accounts'][0]['enabled']) === 1)
{
$this->accountID = (integer) $accounts['accounts'][0]['accountId'];
}
else
{
throw new \Exception(Text::_('NR_ICONTACT_ACCOUNTID_ERROR'), 1);
}
}
/**
* Finds and sets the iContact ClientFolderID
*
* @param mixed $clientFolderID
*/
public function setClientFolderID($clientFolderID = false)
{
if ($clientFolderID)
{
$this->clientFolderID = $clientFolderID;
}
// We need an existant accountID
if (empty($this->accountID))
{
try
{
$this->setAccountID();
}
catch (Exception $e)
{
throw $e;
}
}
if ($clientFolder = $this->get($this->accountID . '/c/'))
{
$this->clientFolderID = $clientFolder['clientfolders'][0]['clientFolderId'];
}
}
/**
* Subscribes a user to an iContact List
*
* API REFERENCE
* https://www.icontact.com/developerportal/documentation/contacts
*
* @param string $email
* @param object $params The extra form fields
* @param mixed $list The iContact List ID
*
* @return boolean
*/
public function subscribe($email, $params, $list)
{
$data = array('contact' => array_merge(array('email' => $email, 'status' => 'normal'), (array) $params));
try
{
$contact = $this->post($this->accountID .'/c/' . $this->clientFolderID . '/contacts', $data);
}
catch (Exception $e)
{
throw $e;
}
if ((isset($contact['contacts'])) && (is_array($contact['contacts'])) && (count($contact['contacts']) > 0))
{
$this->addToList($list, $contact['contacts'][0]['contactId']);
}
return true;
}
/**
* Adds a contact to an iContact List
*
* API REFERENCE
* https://www.icontact.com/developerportal/documentation/subscriptions
*
* @param string $listID
* @param string $contactID
*/
public function addToList($listID, $contactID)
{
$data = array(
array(
'contactId' => $contactID,
'listId' => $listID,
'status' => 'normal'
)
);
$this->post($this->accountID .'/c/' . $this->clientFolderID . '/subscriptions',$data);
}
/**
* Returns all Client lists
*
* API REFERENCE
* https://www.icontact.com/developerportal/documentation/lists
*
* @return array
*/
public function getLists()
{
$data = $this->get($this->accountID .'/c/' . $this->clientFolderID . '/lists');
if (!$this->success())
{
return;
}
$lists = array();
if (!isset($data["lists"]) || !is_array($data["lists"]))
{
return $lists;
}
foreach ($data["lists"] as $key => $list)
{
$lists[] = array(
'id' => $list['listId'],
'name' => $list['name']
);
}
return $lists;
}
/**
* Get the last error returned by either the network transport, or by the API.
* If something didn't work, this should contain the string describing the problem.
*
* @return string describing the error
*/
public function getLastError()
{
$body = $this->last_response->body;
$message = '';
if (isset($body['errors']))
{
foreach ($body['errors'] as $error) {
$message .= $error . ' ';
}
}
return trim($message);
}
}

View File

@ -0,0 +1,323 @@
<?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Integrations;
// No direct access
defined('_JEXEC') or die;
use Joomla\Registry\Registry;
use Joomla\CMS\Http\HttpFactory;
class Integration
{
protected $key;
protected $endpoint;
protected $request_successful = false;
protected $last_error = '';
protected $last_response = [];
protected $last_request = [];
protected $timeout = 60;
protected $options;
protected $encode = true;
protected $response_type = 'json';
public function __construct()
{
$this->options = new Registry;
$this->options->set('timeout', $this->timeout);
$this->options->set('headers.Accept', 'application/json');
$this->options->set('headers.Content-Type', 'application/json');
}
/**
* Setter method for the API Key or Access Token
*
* @param string $apiKey
*/
public function setKey($apiKey)
{
$apiKey = is_array($apiKey) && isset($apiKey['api']) ? $apiKey['api'] : $apiKey;
if (!is_string($apiKey) || empty($apiKey) || is_null($apiKey))
{
throw new \Exception('Invalid API Key supplied.');
}
$this->key = trim($apiKey);
}
/**
* Setter method for the endpoint
* @param string $url The URL which is set in the account's developer settings
* @throws \Exception
*/
public function setEndpoint($url)
{
if (!empty($url))
{
$this->endpoint = $url;
}
else
{
throw new \Exception("Invalid Endpoint URL `{$url}` supplied.");
}
}
/**
* Was the last request successful?
* @return bool True for success, false for failure
*/
public function success()
{
return $this->request_successful;
}
/**
* Get the last error returned by either the network transport, or by the API.
* If something didn't work, this should contain the string describing the problem.
* @return array|false describing the error
*/
public function getLastError()
{
return $this->last_error ?: false;
}
/**
* Get an array containing the HTTP headers and the body of the API response.
* @return array Assoc array with keys 'headers' and 'body'
*/
public function getLastResponse()
{
return $this->last_response;
}
/**
* Get an array containing the HTTP headers and the body of the API request.
* @return array Assoc array
*/
public function getLastRequest()
{
return $this->last_request;
}
/**
* Make an HTTP DELETE request - for deleting data
* @param string $method URL of the API request method
* @param array $args Assoc array of arguments (if any)
* @return array|false Assoc array of API response, decoded from JSON
*/
public function delete($method, $args = [])
{
return $this->makeRequest('delete', $method, $args);
}
/**
* Make an HTTP GET request - for retrieving data
* @param string $method URL of the API request method
* @param array $args Assoc array of arguments (usually your data)
* @return array|false Assoc array of API response, decoded from JSON
*/
public function get($method, $args = [])
{
return $this->makeRequest('get', $method, $args);
}
/**
* Make an HTTP PATCH request - for performing partial updates
* @param string $method URL of the API request method
* @param array $args Assoc array of arguments (usually your data)
* @return array|false Assoc array of API response, decoded from JSON
*/
public function patch($method, $args = [])
{
return $this->makeRequest('patch', $method, $args);
}
/**
* Make an HTTP POST request - for creating and updating items
* @param string $method URL of the API request method
* @param array $args Assoc array of arguments (usually your data)
* @return array|false Assoc array of API response, decoded from JSON
*/
public function post($method, $args = [])
{
return $this->makeRequest('post', $method, $args);
}
/**
* Make an HTTP PUT request - for creating new items
* @param string $method URL of the API request method
* @param array $args Assoc array of arguments (usually your data)
* @return array|false Assoc array of API response, decoded from JSON
*/
public function put($method, $args = [])
{
return $this->makeRequest('put', $method, $args);
}
/**
* Performs the underlying HTTP request. Not very exciting.
* @param string $http_verb The HTTP verb to use: get, post, put, patch, delete
* @param string $method The API method to be called
* @param array $args Assoc array of parameters to be passed
* @return array|false Assoc array of decoded result
* @throws \Exception
*/
protected function makeRequest($http_verb, $method, $args = [])
{
$url = $this->endpoint;
if (!empty($method) && !is_null($method) && strpos($url, '?') === false)
{
$url .= '/' . $method;
}
$this->last_error = '';
$this->request_successful = false;
$this->last_response = [];
$this->last_request = [
'method' => $http_verb,
'path' => $method,
'url' => $url,
'body' => '',
'timeout' => $this->timeout,
];
$http = HttpFactory::getHttp($this->options);
switch ($http_verb)
{
case 'post':
$this->attachRequestPayload($args);
$response = $http->post($url, $this->last_request['body']);
break;
case 'get':
$query = http_build_query($args, '', '&');
$this->last_request['body'] = $query;
$response = (strpos($url,'?') !== false) ? $http->get($url . '&' . $query) : $http->get($url . '?' . $query);
break;
case 'delete':
$response = $http->delete($url);
break;
case 'patch':
$this->attachRequestPayload($args);
$response = $http->patch($url, $this->last_request['body']);
break;
case 'put':
$this->attachRequestPayload($args);
$response = $http->put($url, $this->last_request['body']);
break;
}
// Do not touch directly the $response object to prevent the PHP 8.2 "Creation of dynamic property" deprecation notice.
$this->last_response = (object) [
'body' => $this->convertResponse($response->body),
'headers' => $response->headers,
'code' => $response->code
];
$this->determineSuccess();
return $this->last_response->body;
}
/**
* Encode the data and attach it to the request
* @param array $data Assoc array of data to attach
*/
protected function attachRequestPayload($data)
{
if (!$this->encode)
{
$this->last_request['body'] = http_build_query($data);
return;
}
$this->last_request['body'] = json_encode($data);
}
/**
* Check if the response was successful or a failure. If it failed, store the error.
*
* @return bool If the request was successful
*/
protected function determineSuccess()
{
$status = $this->last_response->code;
$success = ($status >= 200 && $status <= 299) ? true : false;
return ($this->request_successful = $success);
}
/**
* Converts the HTTP Call response to a traversable type
*
* @param json|xml $response
*
* @return array|object
*/
protected function convertResponse($response)
{
switch ($this->response_type)
{
case 'json':
return json_decode($response, true);
case 'xml':
return new \SimpleXMLElement($response);
case 'text':
return $response;
}
}
/**
* Search Custom Fields declared by the user for a specific custom field. If exists return its value.
*
* @param array $needles The custom field names
* @param array $haystack The custom fields array
*
* @return string The value of the custom field or an empty string if not found
*/
protected function getCustomFieldValue($needles, $haystack)
{
$needles = is_array($needles) ? $needles : (array) $needles;
$haystack = array_change_key_case($haystack);
$found = '';
foreach ($needles as $needle)
{
$needle = strtolower($needle);
if (array_key_exists($needle, $haystack))
{
$found = is_string($haystack[$needle]) ? trim($haystack[$needle]) : $haystack[$needle];
break;
}
}
return $found;
}
/**
* Set encode
*
* @param boolean $encode
*
* @return void
*/
public function setEncode($encode)
{
$this->encode = $encode;
}
}

View File

@ -0,0 +1,511 @@
<?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Integrations;
use NRFramework\Functions;
// No direct access
defined('_JEXEC') or die;
class MailChimp extends Integration
{
/**
* MailChimp Endpoint URL
*
* @var string
*/
protected $endpoint = 'https://<dc>.api.mailchimp.com/3.0';
/**
* Create a new instance
*
* @param array $options The service's required options
* @throws \Exception
*/
public function __construct($options)
{
parent::__construct();
$this->setKey($options);
if (strpos($this->key, '-') === false)
{
return;
}
list(, $data_center) = explode('-', $this->key);
$this->endpoint = str_replace('<dc>', $data_center, $this->endpoint);
$this->options->set('headers.Authorization', 'apikey ' . $this->key);
}
/**
* Subscribe user to MailChimp
*
* @param string $list_id The ID of the MailChimp list
* @param string $email The email address of the subscriber
* @param object $merge_fields The custom field that are associated with the subscriber where the keys are the merge tags.
* @param boolean $double_optin If true, the subscriber will be added with status "pending" and a confirmation email will be sent to the user.
* @param boolean $allow_update If true, the subscriber will be updated if it already exists. Otherwise, an error will be thrown.
* @param array $tags The tags that are associated with the subscriber.
* @param string $tags_replace Determines what changes to make to the subscriber's tags. Values: add_only, replace_all
* @param array $interests The interests that are associated with the subscriber.
* @param string $interests_replace Determines what changes to make to the subscriber's groups/interests. Values: add_only, replace_all
*
* @return void
*/
public function subscribeV2($list_id, $email, $merge_fields = null, $double_optin = true, $allow_update = true, $tags = null, $tags_replace = 'add_only', $interests = [], $interests_replace = 'add_only')
{
$data = [
'email_address' => $email,
'status' => $double_optin ? 'pending' : 'subscribed',
'merge_fields' => (object) $merge_fields,
'tags' => Functions::cleanArray($tags)
];
$member = $this->getMemberByEmail($list_id, $email);
// Prepare Interests
$interests = Functions::cleanArray($interests);
$interests = $interests ? array_fill_keys($interests, true) : [];
if ($member && isset($member['interests']) && $interests_replace == 'replace_all')
{
// Disable all existing groups
$memberInterests = array_fill_keys(array_keys($member['interests']), false);
// Merge new interests with existing interests
$interests = array_merge($memberInterests, $interests);
}
$data['interests'] = (object) $interests;
if (!$member)
{
// API Doc: https://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/#create-post_lists_list_id_members
$this->post('lists/' . $list_id . '/members', $data);
return;
}
// Member exists
// Since member exists and we don't allow updating existing member, throw an error.
if (!$allow_update)
{
throw new \Exception('Member already exists');
}
// Skip double opt-in if the existing member is already confirmed
if (isset($member['status']) && $member['status'] == 'subscribed')
{
$data['status'] = $member['status'];
}
// Update existing member
// API Doc: https://mailchimp.com/developer/marketing/api/list-members/add-or-update-list-member
$this->put('lists/' . $list_id . '/members/' . $member['id'], $data);
// Remove existing member tags not included in the given Tags.
if ($member['tags'] && $tags_replace == 'replace_all')
{
$currentTags = array_map(function($item) { return $item['name']; }, $member['tags']);
if ($removeTags = array_diff($currentTags, $data['tags']))
{
$rTags = [];
foreach ($removeTags as $removeTag)
{
$rTags[] = [
'name' => $removeTag,
'status' => 'inactive'
];
}
// API Doc: https://mailchimp.com/developer/marketing/api/list-member-tags/add-or-remove-member-tags/
$this->post('lists/' . $list_id . '/members/' . $member['id'] . '/tags', ['tags' => $rTags]);
}
}
}
/**
* Subscribe user to MailChimp
*
* API References:
* https://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/#edit-put_lists_list_id_members_subscriber_hash
* https://developer.mailchimp.com/documentation/mailchimp/reference/lists/members/#create-post_lists_list_id_members
*
* @param string $email User's email address
* @param string $list The MailChimp list unique ID
* @param Object $merge_fields Merge Fields
* @param boolean $update_existing Update existing user
* @param boolean $double_optin Send MailChimp confirmation email?
*
* @deprecated Use subscribeV2()
*
* @return void
*/
public function subscribe($email, $list, $merge_fields = array(), $update_existing = true, $double_optin = false)
{
$data = array(
'email_address' => $email,
'status' => $double_optin ? 'pending' : 'subscribed'
);
// add support for tags
if ($tags = $this->getTags($merge_fields))
{
$data['tags'] = $tags;
}
if (is_array($merge_fields) && count($merge_fields))
{
foreach ($merge_fields as $merge_field_key => $merge_field_value)
{
$value = is_array($merge_field_value) ? implode(',', $merge_field_value) : (string) $merge_field_value;
$data['merge_fields'][$merge_field_key] = $value;
}
}
$interests = $this->validateInterestCategories($list, $merge_fields);
if (!empty($interests))
{
$data = array_merge($data, array('interests' => $interests));
}
if ($update_existing)
{
// Get subscriber information.
$subscriberHash = md5(strtolower($email));
$member = $this->get('lists/' . $list . '/members/' . $subscriberHash);
// Skip double opt-in if the subscriber exists and it's confirmed
if (isset($member['status']) && $member['status'] == 'subscribed')
{
$data['status'] = $member['status'];
}
$this->put('lists/' . $list . '/members/' . $subscriberHash, $data);
if ($tags)
{
$tags_ = [];
foreach ($tags as $tag)
{
$tags_[] = [
'name' => $tag,
'status' => 'active'
];
}
$currentTags = $this->getMemberTags($list, $subscriberHash);
if ($removeTags = array_diff($currentTags, $tags))
{
foreach ($removeTags as $removeTag)
{
$tags_[] = [
'name' => $removeTag,
'status' => 'inactive'
];
}
}
$this->post('lists/' . $list . '/members/' . $subscriberHash . '/tags', ['tags' => $tags_]);
}
} else
{
$this->post('lists/' . $list . '/members', $data);
}
return true;
}
/**
* @deprecated Use subscribeV2()
*/
private function getMemberTags($list, $subscriberHash)
{
$tags = $this->get('lists/' . $list . '/members/' . $subscriberHash . '/tags');
$return = [];
if (isset($tags['tags']))
{
foreach ($tags['tags'] as $tag)
{
$return[] = $tag['name'];
}
}
return $return;
}
/**
* Find and return all unique tags
*
* @param array $merge_fields
*
* @deprecated use subscribeV2()
*
* @return array
*/
private function getTags($merge_fields)
{
$tags = [];
// ensure tags are added in the form
if (!isset($merge_fields['tags']))
{
return $tags;
}
$mergeFieldsTags = $merge_fields['tags'];
// make string array
if (is_string($mergeFieldsTags))
{
$tags = explode(',', $mergeFieldsTags);
}
// ensure we have array to manipulate
if (is_array($mergeFieldsTags) || is_object($mergeFieldsTags))
{
$tags = (array) $mergeFieldsTags;
}
// remove empty values, keep uniques and reset keys
$tags = array_filter($tags);
$tags = array_unique($tags);
$tags = array_values($tags);
$tags = array_map('trim', $tags);
return $tags;
}
/**
* Returns all available MailChimp lists
*
* https://developer.mailchimp.com/documentation/mailchimp/reference/lists/#read-get_lists
*
* @return array
*/
public function getLists()
{
$data = $this->get('/lists');
if (!$this->success())
{
return;
}
if (!isset($data['lists']) || !is_array($data['lists']))
{
return;
}
$lists = [];
foreach ($data['lists'] as $key => $list)
{
$lists[] = array(
'id' => $list['id'],
'name' => $list['name']
);
}
return $lists;
}
/**
* Gets the Interest Categories from MailChimp
*
* @param string $listID The List ID
*
* @deprecated Use subscribeV2()
*
* @return array
*/
public function getInterestCategories($listID)
{
if (!$listID)
{
return;
}
$data = $this->get('/lists/' . $listID . '/interest-categories');
if (!$this->success())
{
return;
}
if (isset($data['total_items']) && $data['total_items'] == 0)
{
return;
}
return $data['categories'];
}
/**
* Gets the values accepted for the particular Interest Category
*
* @param string $listID The List ID
* @param string $interestCategoryID The Interest Category ID
*
* @deprecated Use subscribeV2()
*
* @return array
*/
public function getInterestCategoryValues($listID, $interestCategoryID)
{
if (!$interestCategoryID || !$listID)
{
return array();
}
$data = $this->get('/lists/' . $listID . '/interest-categories/' . $interestCategoryID . '/interests');
if (isset($data['total_items']) && $data['total_items'] == 0)
{
return array();
}
return $data['interests'];
}
/**
* Filters the interests categories through the form fields
* and constructs the interests array for the subscribe method
*
* @param string $listID The List ID
* @param array $params The Form fields
*
* @deprecated Use subscribeV2()
*
* @return array
*/
public function validateInterestCategories($listID, $params)
{
if (!$params || !$listID)
{
return array();
}
$interestCategories = $this->getInterestCategories($listID);
if (!$interestCategories)
{
return array();
}
$categories = array();
foreach ($interestCategories as $category)
{
if (array_key_exists($category['title'], $params))
{
$categories[] = array('id' => $category['id'], 'title' => $category['title']);
}
}
if (empty($categories))
{
return array();
}
$interests = array();
foreach ($categories as $category)
{
$data = $this->getInterestCategoryValues($listID, $category['id']);
if (isset($data['total_items']) && $data['total_items'] == 0)
{
continue;
}
foreach ($data as $interest)
{
if (in_array($interest['name'], (array) $params[$category['title']]))
{
$interests[$interest['id']] = true;
}
else
{
$interests[$interest['id']] = false;
}
}
}
return $interests;
}
/**
* Find a subscriber in a list by email address
*
* @param string $list_id The MailChimp list ID
* @param string $email The email address
*
* @return mixed Object on success, false on failure
*/
public function getMemberByEmail($list_id, $email)
{
$subscriberHash = md5(strtolower($email));
$result = $this->get('lists/' . $list_id . '/members/' . $subscriberHash);
return $this->success() ? $result : false;
}
/**
* Get the last error returned by either the network transport, or by the API.
*
* @return string
*/
public function getLastError()
{
$body = $this->last_response->body;
if (isset($body['errors']))
{
$error = $body['errors'][0];
return $error['field'] . ': ' . $error['message'];
}
if (isset($body['detail']))
{
return $body['detail'];
}
}
/**
* The get() method overridden so that it handles
* the default item paging of MailChimp which is 10
*
* @param string $method URL of the API request method
* @param array $args Assoc array of arguments (usually your data)
* @return array|false Assoc array of API response, decoded from JSON
*/
public function get($method, $args = array())
{
$data = $this->makeRequest('get', $method, $args);
if ($data && isset($data['total_items']) && (int) $data['total_items'] > 10)
{
$args['count'] = $data['total_items'];
return $this->makeRequest('get', $method, $args);
}
return $data;
}
}

View File

@ -0,0 +1,144 @@
<?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Integrations;
// No direct access
defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
class MailerLite extends Integration
{
/**
* Create a new instance
* @param array $options The service's required options
* @throws \Exception
*/
public function __construct($options)
{
parent::__construct();
$this->setKey($options);
$this->setEndpoint('https://connect.mailerlite.com/api');
$this->options->set('headers.Authorization', 'Bearer ' . $this->key);
}
/**
* Subscribes a user to a MailerLite Account
*
* API Reference v3:
* https://developers.mailerlite.com/docs/subscribers.html#create-upsert-subscriber
*
* @param string $email The user's email
* @param array $fields All the custom fields
* @param string $groupIds The Group IDs
* @param string $subscriber_status The subscriber status (active, unsubscribed, unconfirmed, bounced, junk)
* @param boolean $update_existing Whether to update the existing contact (Only in v3)
*
* @return boolean
*/
public function subscribe($email, $fields, $groupIds = [], $subscriber_status = '', $update_existing = true)
{
// Abort if we don't want to update existing subscribers and the subscriber already exists
if (!$update_existing && $this->subscriberExists($email))
{
throw new \Exception(Text::_('NR_YOU_ARE_ALREADY_A_SUBSCRIBER'), 1);
}
$data = [
'email' => $email,
'fields' => $fields
];
if ($subscriber_status)
{
$data['status'] = $subscriber_status;
}
if ($groupIds)
{
$data['groups'] = $groupIds;
}
$this->post('subscribers', $data);
return true;
}
/**
* Check if a subscriber exists
*
* @param string $email The subscriber's email
*
* @return boolean
*/
private function subscriberExists($email = '')
{
if (!$email)
{
return false;
}
$response = $this->get('subscribers/' . $email);
return isset($response['data']['email']) && $response['data']['email'] == $email;
}
/**
* Returns all groups
*
* API Reference v3:
* https://developers.mailerlite.com/docs/groups.html#list-all-groups
*
* @return array
*/
public function getGroups()
{
$data = [
'offset' => 0,
'limit' => 50
];
$lists = [];
$data = $this->get('groups', $data);
// sanity check
if (!isset($data['data']) || !is_array($data['data']))
{
return $lists;
}
foreach ($data['data'] as $key => $list)
{
$lists[] = [
'id' => $list['id'],
'name' => $list['name']
];
}
return $lists;
}
/**
* Get the last error returned by either the network transport, or by the API.
*
* API Reference:
* https://developers.mailerlite.com/docs/#validation-errors
*
* @return string
*/
public function getLastError()
{
$body = $this->last_response->body;
return isset($body['message']) ? $body['message'] : 'An error has occurred.';
}
}

View File

@ -0,0 +1,32 @@
<?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Integrations;
defined('_JEXEC') or die;
class Notion extends Integration
{
protected $endpoint = 'https://api.notion.com/v1';
/**
* Create a new instance
*
* @param array $options The service's required options
*/
public function __construct($options)
{
parent::__construct();
$this->setKey($options);
$this->options->set('headers.Authorization', 'Bearer ' . $this->key);
$this->options->set('headers.Notion-Version', '2022-06-28');
}
}

View File

@ -0,0 +1,103 @@
<?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Integrations;
// No direct access
defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
/**
* The reCAPTCHA Wrapper
*/
class ReCaptcha extends Integration
{
/**
* Service Endpoint
*
* @var string
*/
protected $endpoint = 'https://www.google.com/recaptcha/api/siteverify';
/**
* Create a new instance
*
* @param array $options
*
* @throws \Exception
*/
public function __construct($options = array())
{
parent::__construct();
if (!array_key_exists('secret', $options))
{
$this->setError('NR_RECAPTCHA_INVALID_SECRET_KEY');
throw new \Exception($this->getLastError());
}
$this->setKey($options['secret']);
}
/**
* Calls the reCAPTCHA siteverify API to verify whether the user passes reCAPTCHA test.
*
* @param string $response Response string from recaptcha verification.
* @param string $remoteip IP address of end user
*
* @return bool Returns true if the user passes reCAPTCHA test
*/
public function validate($response, $remoteip = null)
{
if (empty($response) || is_null($response))
{
return $this->setError('NR_RECAPTCHA_PLEASE_VALIDATE');
}
$data = array(
'secret' => $this->key,
'response' => $response,
'remoteip' => $remoteip ?: \NRFramework\User::getIP(),
);
$this->get('', $data);
return true;
}
/**
* Check if the response was successful or a failure. If it failed, store the error.
*
* @return bool If the request was successful
*/
protected function determineSuccess()
{
$success = parent::determineSuccess();
$body = $this->last_response->body;
if ($body['success'] == false && array_key_exists('error-codes', $body) && count($body['error-codes']) > 0)
{
$success = $this->setError(implode(', ', $body['error-codes']));
}
return ($this->request_successful = $success);
}
/**
* Set wrapper error text
*
* @param String $error The error message to display
*/
private function setError($error)
{
$this->last_error = Text::_('NR_RECAPTCHA') . ': ' . Text::_($error);
return false;
}
}

View File

@ -0,0 +1,114 @@
<?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Integrations;
// No direct access
defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
class SalesForce extends Integration
{
/**
* Service API Endpoint
*
* @var string
*/
protected $endpoint = 'https://{{ENV}}.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8';
/**
* Encode data before sending the request
*
* @var boolean
*/
protected $encode = false;
/**
* Create a new instance
*
* @param array $options
*
* @throws \Exception
*/
public function __construct($options = [])
{
parent::__construct();
$this->setKey($options);
$this->prepareEndpoint($options);
$this->options->set('headers.Content-Type', 'application/x-www-form-urlencoded');
}
private function prepareEndpoint($options = [])
{
if (isset($options['test_mode']) && $options['test_mode'])
{
$this->setEndpoint(str_replace('{{ENV}}', 'test', $this->endpoint));
}
else
{
$this->setEndpoint(str_replace('{{ENV}}', 'webto', $this->endpoint));
}
}
/**
* Subscribe user to SalesForce
*
* API References:
* https://developer.salesforce.com/page/Wordpress-to-lead
*
* @param string $email User's email address
* @param array $params All the form fields
*
* @return void
*/
public function subscribe($email, $params)
{
$data = array(
"email" => $email,
"oid" => $this->key
);
if (is_array($params) && count($params))
{
$data = array_merge($data, $params);
}
$this->post('', $data);
return true;
}
/**
* Determine if the Lead has been stored successfully in SalesForce
*
* @return string
*/
public function determineSuccess()
{
$status = $this->last_response->code;
if ($status < 200 && $status > 299)
{
return false;
}
$headers = $this->last_response->headers;
if (isset($headers['Is-Processed']) && (strpos($headers['Is-Processed'], 'Exception') !== false))
{
$this->last_error = Text::_('NR_SALESFORCE_ERROR');
return false;
}
return ($this->request_successful = true);
}
}

View File

@ -0,0 +1,114 @@
<?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Integrations;
// No direct access
defined('_JEXEC') or die;
class SendInBlue extends Integration
{
/**
* Create a new instance
* @param array $options The service's required options
* @throws \Exception
*/
public function __construct($options)
{
parent::__construct();
$this->setKey($options['api']);
$this->setEndpoint('https://api.sendinblue.com/v2.0');
$this->options->set('headers.api-key', $this->key);
}
/**
* Subscribes a user to a SendinBlue Account
*
* API Reference:
* https://apidocs.sendinblue.com/user/#1
*
* @param string $email The user's email
* @param array $params All the form fields
* @param string $listid The List ID
*
* @return boolean
*/
public function subscribe($email, $params, $listid = false)
{
$data = array(
'email' => $email,
'attributes' => $params,
);
if ($listid)
{
$data['listid'] = array($listid);
}
$this->post('user/createdituser', $data);
return true;
}
/**
* Returns all Campaign lists
*
* https://apidocs.sendinblue.com/list/#1
*
* @return array
*/
public function getLists()
{
$data = array(
'page' => 1,
'page_limit' => 50
);
$lists = array();
$data = $this->get('/list', $data);
if (!isset($data['data']['lists']) || !is_array($data['data']['lists']) || $data['data']['total_list_records'] == 0)
{
return $lists;
}
foreach ($data['data']['lists'] as $key => $list)
{
$lists[] = array(
'id' => $list['id'],
'name' => $list['name']
);
}
return $lists;
}
/**
* Get the last error returned by either the network transport, or by the API.
*
* API Reference:
* https://apidocs.sendinblue.com/response/
*
* @return string
*/
public function getLastError()
{
$body = $this->last_response->body;
$message = '';
if (isset($body['code']) && ($body['code'] == 'failure'))
{
$message = $body['message'];
}
return $message;
}
}

View File

@ -0,0 +1,118 @@
<?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Integrations;
// No direct access
defined('_JEXEC') or die;
class SendInBlue3 extends Integration
{
/**
* Create a new instance
* @param array $options The service's required options
* @throws \Exception
*/
public function __construct($options)
{
parent::__construct();
$this->setKey($options['api']);
$this->setEndpoint('https://api.sendinblue.com/v3');
$this->options->set('headers.api-key', $this->key);
}
/**
* Subscribes a user to a SendinBlue Account
*
* API Reference v3:
* https://developers.sendinblue.com/reference#createcontact
*
* @param string $email The user's email
* @param array $params All the form fields
* @param string $listid The List ID
* @param boolean $update_existing Whether to update the existing contact (Only in v3)
*
* @return boolean
*/
public function subscribe($email, $params, $listid = false, $update_existing = true)
{
$data = [
'email' => $email,
'attributes' => (object) $params,
'updateEnabled' => $update_existing
];
if ($listid)
{
$data['listIds'] = [(int) $listid];
}
$this->post('contacts', $data);
return true;
}
/**
* Returns all Campaign lists
*
* API Reference v3:
* https://developers.sendinblue.com/reference#getlists-1
*
* @return array
*/
public function getLists()
{
$data = [
'page' => 1,
'page_limit' => 50
];
$lists = [];
$data = $this->get('contacts/lists', $data);
// sanity check
if (!isset($data['lists']) || !is_array($data['lists']) || $data['count'] == 0)
{
return $lists;
}
foreach ($data['lists'] as $key => $list)
{
$lists[] = [
'id' => $list['id'],
'name' => $list['name']
];
}
return $lists;
}
/**
* Get the last error returned by either the network transport, or by the API.
*
* API Reference:
* https://developers.sendinblue.com/docs/how-it-works#error-codes
*
* @return string
*/
public function getLastError()
{
$body = $this->last_response->body;
$message = '';
if (!isset($body['code']))
{
return $message;
}
return $body['message'];
}
}

View File

@ -0,0 +1,99 @@
<?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Integrations;
// No direct access
defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
class Turnstile extends Integration
{
/**
* Service Endpoint
*
* @var string
*/
protected $endpoint = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
/**
* Create a new instance
*
* @param array $options
*
* @throws \Exception
*/
public function __construct($options = [])
{
parent::__construct();
if (!array_key_exists('secret', $options))
{
$this->setError('NR_RECAPTCHA_INVALID_SECRET_KEY');
throw new \Exception($this->getLastError());
}
$this->setKey($options['secret']);
}
/**
* Calls the Cloudflare Turnstile siteverify API to verify whether the user passes the test.
*
* @param string $response Response string from Cloudflare Turnstile verification.
* @param string $remoteip IP address of end user
*
* @return bool Returns true if the user passes the test
*/
public function validate($response, $remoteip = null)
{
if (empty($response) || is_null($response))
{
return $this->setError('NR_RECAPTCHA_PLEASE_VALIDATE');
}
$data = [
'secret' => $this->key,
'response' => $response,
];
$this->post('', $data);
return true;
}
/**
* Check if the response was successful or a failure. If it failed, store the error.
*
* @return bool If the request was successful
*/
protected function determineSuccess()
{
$success = parent::determineSuccess();
$body = $this->last_response->body;
if ($body['success'] == false && array_key_exists('error-codes', $body) && count($body['error-codes']) > 0)
{
$success = $this->setError(implode(', ', $body['error-codes']));
}
return ($this->request_successful = $success);
}
/**
* Set wrapper error text
*
* @param String $error The error message to display
*/
private function setError($error)
{
$this->last_error = Text::_('NR_TURNSTILE') . ': ' . Text::_($error);
return false;
}
}

View File

@ -0,0 +1,169 @@
<?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Integrations;
// No direct access
defined('_JEXEC') or die;
class Zoho extends Integration
{
/**
* Create a new instance
*
* @param array $options The service's required options
* @throws \Exception
*/
public function __construct($options)
{
parent::__construct();
$this->setKey($options['api']);
$this->endpoint = 'https://campaigns.zoho.com/api';
}
/**
* Subscribe user to ZoHo
*
* https://www.zoho.com/campaigns/help/api/contact-subscribe.html
*
* @param string $email User's email address
* @param string $list The ZoHo list unique ID
* @param Object $customFields Collection of custom fields
*
* @return void
*/
public function subscribe($email, $list, $customFields = array())
{
$contactinfo = json_encode(array_merge(array("Contact Email" => $email), $customFields));
$data = array(
"authtoken" => $this->key,
"scope" => "CampaignsAPI",
"version" => "1",
"resfmt" => "JSON",
"listkey" => $list,
"contactinfo" => $contactinfo
);
$this->get('json/listsubscribe', $data);
return true;
}
/**
* Returns all available ZoHo lists
*
* https://www.zoho.com/campaigns/help/api/get-mailing-lists.html
*
* @return array
*/
public function getLists()
{
if (!$this->key)
{
return;
}
$data = array(
'authtoken' => $this->key,
'scope' => 'CampaignsAPI',
'sort' => 'asc',
'resfmt' => 'JSON',
'range' => '1000' //ambiguously large range of total results to overwrite the default range which is 20
);
$data = $this->get("getmailinglists", $data);
if (!$this->success())
{
return;
}
$lists = array();
if (!isset($data["list_of_details"]) || !is_array($data["list_of_details"]))
{
return $lists;
}
foreach ($data["list_of_details"] as $key => $list)
{
$lists[] = array(
"id" => $list["listkey"],
"name" => $list["listname"]
);
}
return $lists;
}
/**
* Get the last error returned by either the network transport, or by the API.
*
* @return string
*/
public function getLastError()
{
$body = $this->last_response->body;
if (isset($body['message']))
{
return $body['message'];
}
return 'An unspecified error occured';
}
/**
* Check if the response was successful or a failure. If it failed, store the error.
*
* @return bool If the request was successful
*/
protected function determineSuccess()
{
$status = $this->findHTTPStatus();
// check if the status is equal to the arbitrary success codes of ZoHo
if (in_array($status, array(0, 200, 6101, 6201)))
{
return ($this->request_successful = true);
}
return false;
}
/**
* Find the HTTP status code from the headers or API response body
*
* @return int HTTP status code
*/
protected function findHTTPStatus()
{
$status = $this->last_response->code;
$success = ($status >= 200 && $status <= 299) ? true : false;
if (!$success)
{
return 418;
}
// ZoHo sometimes uses "Code" instead of "code"
// also they don't use HTTP status codes
// instead they store their own status code inside the response body
$data = array_change_key_case($this->last_response->body);
if (isset($data['code']))
{
return (int) $data['code'];
}
return 418;
}
}

View File

@ -0,0 +1,156 @@
<?php
/**
* @author Tassos Marinos <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Integrations;
// No direct access
defined('_JEXEC') or die;
class ZohoCRM extends Integration
{
/**
* Response Type
*
* @var string
*/
protected $response_type = 'xml';
/**
* Data Center API Endpoint
*
* @var string
*/
private $datacenter = 'crm.zoho.com';
/**
* Create a new instance
*
* @param array $options The service's required options
*/
public function __construct($options)
{
parent::__construct();
$this->setKey($options['authenticationToken']);
if (isset($options['datacenter']) && !is_null($options['datacenter']) && !empty($options['datacenter']))
{
$this->datacenter = $options['datacenter'];
}
}
/**
* Subscribe user to ZohoCRM
*
* https://www.zoho.eu/crm/help/api/insertrecords.html#Insert_records_into_Zoho_CRM_from_third-party_applications
*
* @param string $email User's email address
* @param array $fields Available form fields
* @param string $module Zoho module to be used
* @param boolean $update_existing Update existing users
* @param string $workflow Trigger the workflow rule while inserting record
* @param string $approve Approve records (Supports: Leads, Contacts, and Cases modules)
*
* @return void
*/
public function subscribe($email, $fields, $module = 'leads', $update_existing = true, $workflow = false, $approve = false)
{
$data = array(
'authtoken' => $this->key,
'scope' => 'crmapi',
'xmlData' => $this->buildModuleXML($email, $fields, $module),
'duplicateCheck' => $update_existing ? '2' : '1',
'wfTrigger' => $workflow ? 'true' : 'false',
'isApproval' => $approve ? 'true' : 'false',
'version' => '4'
);
$this->endpoint = 'https://' . $this->datacenter . '/crm/private/xml/' . ucfirst($module) . '/insertRecords?' . http_build_query($data);
$this->post('');
}
/**
* Build the XML for each module
*
* @param string $email User's email address
* @param array $fields Form fields
* @param string $module Module to be used
*
* @return string The XML
*/
private function buildModuleXML($email, $fields, $module)
{
$xml = new SimpleXMLElement('<' . ucfirst($module) . '/>');
$row = $xml->addChild('row');
$row->addAttribute('no', '1');
$xmlField = $row->addChild('FL', $email);
$xmlField->addAttribute('val', 'Email');
if (is_array($fields) && count($fields))
{
foreach ($fields as $field_key => $field_value)
{
$field_value = is_array($field_value) ? implode(',', $field_value) : $field_value;
$xmlField = $row->addChild('FL', $field_value);
$xmlField->addAttribute('val', $field_key);
}
}
return $xml->asXML();
}
/**
* Get the last error returned by either the network transport, or by the API.
*
* @return string
*/
public function getLastError()
{
$body = $this->last_response->body;
if (isset($body->error))
{
return $body->error->message;
}
if (isset($body->result->row->error))
{
return $body->result->row->error->details;
}
return 'Unknown error';
}
/**
* Check if the response was successful or a failure. If it failed, store the error.
*
* @return bool If the request was successful
*/
public function determineSuccess()
{
$status = $this->last_response->code;
$success = ($status >= 200 && $status <= 299) ? true : false;
if (!$success)
{
return false;
}
$body = $this->last_response->body;
if (!isset($body->result->row->success))
{
return false;
}
return ($this->request_successful = true);
}
}