Skip to content

Commit

Permalink
fix issue #1
Browse files Browse the repository at this point in the history
  • Loading branch information
Rich Lott / Artful Robot committed Aug 29, 2019
1 parent bff5ec3 commit 4bb3aa8
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 21 deletions.
91 changes: 74 additions & 17 deletions CRM/Iparl/Page/IparlWebhook.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* Finds/creates contact, creates action. Success is indicated by simply responding "OK".
*
* @author Rich Lott / Artful Robot
* @copyright Rich Lott 2016
* @copyright Rich Lott 2019
* see licence.
*
* At time of writing, the iParl API provides:
Expand All @@ -16,8 +16,8 @@
*
* - actionid The ID number of the action. This displays in the URL of each action and can also be accessed as an XML file using the 'List actions API' referred to here.
* - secret Secret string set when you set up this function. Testing for this in your script will allow you to filter out other, potentially hostile, attempts to feed information into your system. Not used in the redirect data string.
* - name Where only one name field is gathered it will display here. If a last name is also gathered, this will be the first name.
* - lastname Last name, if gathered
* - name if the action gathered two name fields, this will be the first name, otherwise it will be the complete first/surname combination
* - surname Surname, if gathered (update Aug 2019, was lastname)
* - address1 Address line 1
* - address2 Address line 2
* - town Town
Expand Down Expand Up @@ -50,6 +50,11 @@ class CRM_Iparl_Page_IparlWebhook extends CRM_Core_Page {
/** @var mixed FALSE or (for test purposes) a callback to use in place of simplexml_load_file */
public static $simplexml_load_file = 'simplexml_load_file';
public $iparl_logging;

/** @var string parsed first name */
public $first_name;
/** @var string parsed last name */
public $last_name;
/**
* Log, if logging is enabled.
*/
Expand Down Expand Up @@ -97,12 +102,15 @@ public function run() {
*/
public function processWebhook($data) {
// Check secret.
foreach (array('secret', 'name', 'lastname', 'email') as $_) {
foreach (array('secret', 'email') as $_) {
if (empty($data[$_])) {
$this->iparlLog("POSTed data missing (at least) $_");
throw new Exception("POST data is invalid or incomplete.");
}
}

$this->parseNames($data);

$key = civicrm_api3('Setting', 'getvalue', array( 'name' => "iparl_webhook_key" ));
if (empty($key)) {
$this->iparlLog("no iparl_webhook_key set. Will not process.");
Expand All @@ -121,6 +129,53 @@ public function processWebhook($data) {
return TRUE;
}

/**
* Ensure we have name data in incoming data.
*
* If "Separate fields for first & last names" is not checked in the config
*
* iParl docs say of the 'name' data key:
*
* > name - if the action gathered two name fields, this will be the first name,
* > otherwise it will be the complete first/surname combination
* >
* > surname - surname if gathered for this action
*
* (29 Aug 2019) https://iparlsetup.com/setup/help#supporterwebhook
*
* This function looks for 'surname' - if it's set it uses 'name' as first
* name and surname as last name. Otherwise it tries to separate 'name' into
* first and last - the first name is the first word before a space, the rest
* is considered the surname. (Because this is not always right it's better
* to collect separate first, last names yourself.)
*/
public function parseNames($data) {
$this->first_name = '';
$this->last_name = '';

$input_surname = trim($data['surname'] ?? '');
$input_name = trim($data['name'] ?? '');

if (!empty($input_surname)) {
$this->last_name = $data['surname'];
$this->first_name = $data['name'];
}
elseif (!empty($input_name)) {
$parts = preg_split('/\s+/', $input_name);
if (count($parts) === 1) {
// User only supplied one name.
$this->first_name = $parts[0];
$this->last_name = '';
}
else {
$this->first_name = array_shift($parts);
$this->last_name = implode(' ', $parts);
}
}
else {
throw new Exception("iParl webhook requires data in the 'name' field.");
}
}
public function findOrCreate($input) {
// Look up the email first.
$result = civicrm_api3('Email', 'get', array(
Expand All @@ -131,7 +186,7 @@ public function findOrCreate($input) {

if ($result['count'] == 0) {
$result = $this->createContact($input);
$this->iparlLog("Created contact $result[id]");
$this->iparlLog("Created contact $result[id] because email was not found.");
return $result;
}
elseif ($result['count'] == 1) {
Expand All @@ -150,26 +205,28 @@ public function findOrCreate($input) {
}
}
foreach ($unique_contacts as $contact_id => $contact) {
if ($input['name'] == $contact['first_name']
&& $input['lastname'] == $contact['last_name']) {
if ($this->first_name == $contact['first_name']
&& (!empty($this->last_name) && $this->last_name == $contact['last_name'])) {
// Found a match on name and email, return that.
$this->iparlLog("Found contact $contact[id] by email and name match.");
return $contact;
}
}

// If we were unable to match on first and last name, try last name only.
foreach ($unique_contacts as $contact_id => $contact) {
if ($input['lastname'] == $contact['last_name']) {
// Found a match on last name and email, return that.
$this->iparlLog("Found contact $contact[id] by email and last name match.");
return $contact;
if ($this->last_name) {
foreach ($unique_contacts as $contact_id => $contact) {
if ($this->last_name == $contact['last_name']) {
// Found a match on last name and email, return that.
$this->iparlLog("Found contact $contact[id] by email and last name match.");
return $contact;
}
}
}

// If we were unable to match on first and last name, try first name only.
foreach ($unique_contacts as $contact_id => $contact) {
if ($input['firstname'] == $contact['first_name']) {
if ($this->first_name == $contact['first_name']) {
// Found a match on last name and email, return that.
$this->iparlLog("Found contact $contact[id] by email and first name match.");
return $contact;
Expand All @@ -179,18 +236,18 @@ public function findOrCreate($input) {
// We know the email, but we think it belongs to someone else.
// Create new contact.
$result = $this->createContact($input);
$this->iparlLog("Created contact $result[id]");
$this->iparlLog("Created contact $result[id] because could not match on email and name \n" . json_encode($result));
return $result;
}
/**
* Create a contact.
*/
public function createContact($input) {
$params = array(
'first_name' => $input['name'],
'last_name' => $input['lastname'],
'first_name' => $this->first_name,
'last_name' => $this->last_name,
'contact_type' => 'Individual',
'email' => $input['email'],
'email' => $input['email'],
);
$result = civicrm_api3('Contact', 'create', $params);
return $result;
Expand Down
2 changes: 1 addition & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Package: uk.artfulrobot.iparl
Copyright (C) 2016, Rich Lott <forums@artfulrobot.uk>
Copyright (C) 2016-2019, Rich Lott <forums@artfulrobot.uk>
Licensed under the GNU Affero Public License 3.0 (below).

-------------------------------------------------------------------------------
Expand Down
66 changes: 63 additions & 3 deletions tests/phpunit/iparlTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public function testAction() {
'actionid' => 123,
'secret' => 'helloHorseHeadLikeYourJumper',
'name' => 'Wilma',
'lastname' => 'Flintstone',
'surname' => 'Flintstone',
'address1' => 'Cave 123',
'address2' => 'Cave Street',
'town' => 'Rocksville',
Expand All @@ -96,7 +96,7 @@ public function testAction() {
$contact_id = current($result['values'])['id'];

$this->assertEquals([
"Created contact $contact_id",
"Created contact $contact_id because email was not found.",
"Created phone",
"Created address",
"Cache miss on looking up iparl_titles_action",
Expand Down Expand Up @@ -135,7 +135,7 @@ public function testAction() {
'actionid' => 123,
'secret' => 'helloHorseHeadLikeYourJumper',
'name' => 'Wilma',
'lastname' => 'Flintstone',
'surname' => 'Flintstone',
'address1' => 'Cave 123',
'address2' => 'Cave Street',
'town' => 'Rocksville',
Expand Down Expand Up @@ -189,6 +189,66 @@ public function testAction() {
$this->assertEquals(2, $calls);
}

/**
* Check name splitting works.
*/
public function testNamesSeparateFirstAndLast() {

$webhook = new CRM_Iparl_Page_IparlWebhook();
$webhook->iparl_logging = 'phpunit';

$webhook->parseNames([
'name' => 'Wilma',
'surname' => 'Flintstone',
]);
$this->assertEquals('Wilma', $webhook->first_name);
$this->assertEquals('Flintstone', $webhook->last_name);
}

/**
* Check name splitting works.
*/
public function testNamesCombinedFirstAndLast() {

$webhook = new CRM_Iparl_Page_IparlWebhook();
$webhook->iparl_logging = 'phpunit';

$webhook->parseNames([
'name' => 'Wilma Flintstone',
]);
$this->assertEquals('Wilma', $webhook->first_name);
$this->assertEquals('Flintstone', $webhook->last_name);
}

/**
* Check name splitting works.
*/
public function testNamesCombinedOneWord() {

$webhook = new CRM_Iparl_Page_IparlWebhook();
$webhook->iparl_logging = 'phpunit';

$webhook->parseNames([
'name' => 'Wilma',
]);
$this->assertEquals('Wilma', $webhook->first_name);
$this->assertEquals('', $webhook->last_name);
}

/**
* Check name splitting works.
*
* @expectedException Exception
* @expectedExceptionMessage iParl webhook requires data in the 'name' field.
*/
public function testNamesNone() {

$webhook = new CRM_Iparl_Page_IparlWebhook();
$webhook->iparl_logging = 'phpunit';

$webhook->parseNames([]);
}

/**
* Check system status warnings/errors.
*/
Expand Down

0 comments on commit 4bb3aa8

Please sign in to comment.