Commit 2f530809 authored by ErikHommel's avatar ErikHommel
Browse files

version 2.0 - not using Koalect User Id as identifier

parent aa6de3f7
## Version 1.3 (not released yet)
## Version 2.0
* Koalect User ID can not be used as identifier so form processor changed and relevant parts of code removed
## Version 1.3
* make delete log function backwards compatible
* fix conceptual error on project create - custom group should be changed on campaign create
* add temporary debug statement to dump data to log during process so error analysis is easier
......
<?php
use CRM_Koalecttocivi_ExtensionUtil as E;
/**
* Class for Koalect Identifier (contact identity tracker)
*
* @author Erik Hommel (CiviCooP) <erik.hommel@civicoop.org>
* @license AGPL-3.0
*/
class CRM_Koalecttocivi_KoalectIdentifier {
private $_tableName;
private $_customGroupName;
/**
* Constructor
*/
public function __construct() {
$this->_tableName = "civicrm_value_contact_id_history";
$this->_customGroupName = "contact_id_history";
}
/**
* Method to add a koalect user id to a contact as an identifier
*
* @param int $contactId
* @param string $userId
* @return void
*/
public function addUserIdIdentifier(int $contactId, string $userId) {
if ($contactId && $userId) {
if (!$this->exists($contactId, $userId)) {
if (CRM_Core_DAO::checkTableExists($this->_tableName)) {
$usedSinceDate = new DateTime('now');
$insert = "INSERT INTO " . $this->_tableName . " (entity_id, identifier_type, identifier, used_since) VALUES(%1, %2, %3, %4)";
CRM_Core_DAO::executeQuery($insert, [
1 => [$contactId, "Integer"],
2 => [Civi::service('koalecttocivi')->getKoalectIdentifierTypeValue(), "String"],
3 => [$userId, "String"],
4 => [$usedSinceDate->format("Y-m-d"), "String"],
]);
}
}
}
}
/**
* Method to check if koalect user id identifier already exists for contact
*
* @param int $contactId
* @param string $userId
* @return bool
*/
private function exists(int $contactId, string $userId) {
if ($contactId && $userId && CRM_Core_DAO::checkTableExists($this->_tableName)) {
$query = "SELECT COUNT(*) FROM " . $this->_tableName . " WHERE identifier_type = %1 AND identifier = %2 AND entity_id = %3";
$count = CRM_Core_DAO::singleValueQuery($query, [
1 => [Civi::service('koalecttocivi')->getKoalectIdentifierTypeValue(), "String"],
2 => [$userId, "String"],
3 => [$contactId, "Integer"],
]);
if ($count > 0) {
return TRUE;
}
}
return FALSE;
}
/**
* Method to get the custom field id of the identifier type field
*
* @return false|int
*/
public function getTypeCustomFieldId() {
$customFieldName = "id_history_entry_type";
if (function_exists('civicrm_api4')) {
try {
$customFields = \Civi\Api4\CustomField::get()
->addSelect('id')
->addWhere('custom_group_id:name', '=', $this->_customGroupName)
->addWhere('name', '=', $customFieldName)
->execute();
$customField = $customFields->first();
if (isset($customField['id'])) {
return (int) $customField['id'];
}
}
catch (API_Exception $ex) {
}
}
else {
try {
$id = civicrm_api3('CustomField', 'getvalue', [
'return' => "id",
'custom_group_id' => $this->_customGroupName,
'name' => $customFieldName,
]);
if ($id) {
return (int) $id;
}
}
catch (CiviCRM_API3_Exception $ex) {
}
}
return FALSE;
}
/**
* Method to validate identifier - not allowed to add Koalect type in UI
*
* @param array $fields
* @param array $errors
* @return void
*/
public static function validateIdentifier(array $fields, array &$errors) {
$ki = new CRM_Koalecttocivi_KoalectIdentifier();
$typeCustomFieldId = $ki->getTypeCustomFieldId();
if ($typeCustomFieldId) {
$typeCustomField = "custom_" . $typeCustomFieldId;
foreach ($fields as $key => $value) {
if (strpos($key, $typeCustomField) !== FALSE) {
if ($value == Civi::service('koalecttocivi')->getKoalectIdentifierTypeValue()) {
$errors[$key] = E::ts('You can not manually add a Koalect contact identity.');
}
}
}
}
}
}
<?php
use CRM_Koalecttocivi_ExtensionUtil as E;
/**
* Class for Koalect Project Owner processing
*
* @author Erik Hommel (CiviCooP) <erik.hommel@civicoop.org>
* @license AGPL-3.0
*/
class CRM_Koalecttocivi_KoalectProjectOwner {
/**
* Method to find or create project owner using XCM
*
* @param string $projectOwnerUserId
* @param string $firstName
* @param string $lastName
* @param string $email
* @param string $xcmProfile
* @return int|null
*/
public function findOrCreateProjectOwner(string $projectOwnerUserId, string $firstName, string $lastName, string $email, string $xcmProfile = "default") {
$contactId = NULL;
try {
$apiParams = [
'contact_type' => "Individual",
'xcm_profile' => $xcmProfile,
];
if (!$firstName && !$lastName) {
if (!$email) {
$firstName = "KoalectUser";
$lastName = $projectOwnerUserId;
}
}
if ($firstName) {
$apiParams['first_name'] = $firstName;
}
if ($lastName) {
$apiParams['last_name'] = $lastName;
}
if ($email) {
$apiParams['email'] = $email;
}
$result = civicrm_api3('Contact', 'getorcreate', $apiParams);
if ($result['id']) {
// add identifier for koalect user id and koalect custom data
$id = new CRM_Koalecttocivi_KoalectIdentifier();
$id->addUserIdIdentifier((int) $result['id'], $projectOwnerUserId);
$this->addProjectOwnerKoalectData((int) $result['id'], $firstName, $lastName, $email, $projectOwnerUserId);
$contactId = (int) $result['id'];
}
}
catch (\CiviCRM_API3_Exception $ex) {
}
return $contactId;
}
/**
* Method to add custom data to koalect project owner contact
*
* @param int $contactId
* @param string $firstName
* @param string $lastName
* @param string $email
* @param string $projectOwnerUserId
* @return void
*/
private function addProjectOwnerKoalectData(int $contactId, string $firstName, string $lastName, string $email, string $projectOwnerUserId) {
if ($contactId) {
if ($firstName || $lastName || $email || $projectOwnerUserId) {
if (function_exists('civicrm_api4')) {
try {
$cgName = \Civi::service('koalecttocivi')->getContactCustomGroupName();
\Civi\Api4\Contact::update()
->addWhere('id', '=', $contactId)
->addValue($cgName . '.' . \Civi::service('koalecttocivi')->getKtContactEmailCfName(), $email)
->addValue($cgName . '.' . \Civi::service('koalecttocivi')->getKtContactFirstNameCfName(), $firstName)
->addValue($cgName . '.' . \Civi::service('koalecttocivi')->getKtContactLastNameCfName(), $lastName)
->addValue($cgName . '.' . \Civi::service('koalecttocivi')->getKtContactUserIdCfName(), $projectOwnerUserId)
->execute();
}
catch (API_Exception $ex) {
Civi::log()->error(E::ts("Could not add custom data to Koalect Project Owner with ID ") . $contactId
. E::ts(" in ") . __METHOD__ . E::ts(", error from API4 Contact Update: ") . $ex->getMessage());
}
}
else {
try {
$cgId = \Civi::service('koalecttocivi')->getContactCustomGroupId();
$cfEmail = "custom_" . \Civi::service('koalecttocivi')->getKtContactEmailCfId();
$cfFirstName = "custom_" . \Civi::service('koalecttocivi')->getKtContactFirstNameCfId();
$cfLastName = "custom_" . \Civi::service('koalecttocivi')->getKtContactLastNameCfId();
$cfUserId = "custom_" . \Civi::service('koalecttocivi')->getKtContactuserIdCfId();
$cgParams = [
'id' => $contactId,
$cfEmail => $email,
$cfFirstName => $firstName,
$cfLastName => $lastName,
$cfUserId => $projectOwnerUserId
];
civicrm_api3('Contact', 'create', $cgParams);
}
catch (CiviCRM_API3_Exception $ex) {
Civi::log()->error(E::ts("Could not add custom data to Koalect Project Owner with ID ") . $contactId
. E::ts(" in ") . __METHOD__ . E::ts(", error from API3 Contact create: ") . $ex->getMessage());
}
}
}
}
}
}
......@@ -17,17 +17,14 @@ class CRM_Koalecttocivi_KoalectService {
// option values
private $_koalectCampaignTypeId = NULL;
private $_koalectSoftCreditTypeId = NULL;
private $_koalectIdentifierTypeValue = NULL;
private $_koalectPaymentMethodId = NULL;
private $_completedContributionStatusId = NULL;
private $_pendingContributionStatusId = NULL;
// option groups
private $_campaignTypeOptionGroupId = NULL;
private $_identifierTypeOptionGroupId = NULL;
private $_softCreditOptionGroupId = NULL;
private $_genderOptionGroupId = NULL;
// custom groups and fields
private $_contactIdentityCustomGroupId = NULL;
private $_campaignCustomGroupId = NULL;
private $_campaignCustomGroupName = NULL;
private $_contactCustomGroupId = NULL;
......@@ -42,8 +39,6 @@ class CRM_Koalecttocivi_KoalectService {
private $_ktCampaignProjectNameCfName = NULL;
private $_ktCampaignCampaignIdCfId = NULL;
private $_ktCampaignCampaignIdCfName = NULL;
private $_ktContactUserIdCfId = NULL;
private $_ktContactUserIdCfName = NULL;
private $_ktContactEmailCfId = NULL;
private $_ktContactEmailCfName = NULL;
private $_ktContactFirstNameCfId = NULL;
......@@ -150,20 +145,6 @@ class CRM_Koalecttocivi_KoalectService {
return $this->_genderOptionGroupId;
}
/**
* @param int $id
*/
public function setIdentifierTypeOptionGroupId(int $id) {
$this->_identifierTypeOptionGroupId = $id;
}
/**
* @return null
*/
public function getIdentifierTypeOptionGroupId() {
return $this->_identifierTypeOptionGroupId;
}
/**
* @param int $id
*/
......@@ -192,20 +173,6 @@ class CRM_Koalecttocivi_KoalectService {
return $this->_koalectCampaignTypeId;
}
/**
* @param string $value
*/
public function setKoalectIdentifierTypeValue(string $value) {
$this->_koalectIdentifierTypeValue = $value;
}
/**
* @return null
*/
public function getKoalectIdentifierTypeValue() {
return $this->_koalectIdentifierTypeValue;
}
/**
* @param int $id
*/
......@@ -234,20 +201,6 @@ class CRM_Koalecttocivi_KoalectService {
return $this->_koalectSoftCreditTypeId;
}
/**
* @param int $id
*/
public function setContactIdentityCustomGroupId(int $id) {
$this->_contactIdentityCustomGroupId = $id;
}
/**
* @return null
*/
public function getContactIdentityCustomGroupId() {
return $this->_contactIdentityCustomGroupId;
}
/**
* @param int $id
*/
......@@ -360,34 +313,6 @@ class CRM_Koalecttocivi_KoalectService {
return $this->_infoCustomGroupName;
}
/**
* @param int $id
*/
public function setKtContactUserIdCfId(int $id) {
$this->_ktContactUserIdCfId = $id;
}
/**
* @return null
*/
public function getKtContactUserIdCfId() {
return $this->_ktContactUserIdCfId;
}
/**
* @param string $name
*/
public function setKtContactUserIdCfName(string $name) {
$this->_ktContactUserIdCfName = $name;
}
/**
* @return null
*/
public function getKtContactUserIdCfName() {
return $this->_ktContactUserIdCfName;
}
/**
* @param int $id
*/
......@@ -1137,28 +1062,6 @@ class CRM_Koalecttocivi_KoalectService {
return $name;
}
/**
* Method to find a contact id using the koalect user id
*
* @param string $koalectUserId
* @return int|null
*/
public function findContactIdWithKoalectUserId(string $koalectUserId) {
$contactId = NULL;
try {
$result = civicrm_api3('Contact', 'findbyidentity', [
'identifier' => $koalectUserId,
'identifier_type' => $this->getKoalectIdentifierTypeValue(),
]);
if (isset($result['id'])) {
$contactId= (int) $result['id'];
}
}
catch (\CiviCRM_API3_Exception $ex) {
}
return $contactId;
}
/**
* Method to get the numeric parts of a phone number
*
......
......@@ -10,13 +10,14 @@ use CRM_Koalecttocivi_ExtensionUtil as E;
class CRM_Koalecttocivi_KoalectTransaction {
public function create(array $apiData) {
$result = [];
// store incoming data in log file
$this->log($apiData);
// actual processing is in the Form Processor koalect_transaction
$data = $this->sanitizeIncomingParams($apiData);
if ($data) {
// temp debug during initial testing
Civi::log()->debug('Koalect params after sanitation: ' . json_encode($data));
Civi::log()->debug('Koalect params that go into the Form Processor: ' . json_encode($data));
try {
$fp = civicrm_api3('FormProcessor', 'koalect_transaction', $data);
Civi::log()->debug('Koalect Form Processor result: ' . json_encode($fp));
......@@ -32,12 +33,14 @@ class CRM_Koalecttocivi_KoalectTransaction {
}
}
catch (CiviCRM_API3_Exception $ex) {
Civi::log()->debug('Koalect Form Processor error: ' . $ex->getMessage());
$result = [
'is_error' => 1,
'error_message' => $ex->getMessage()
];
}
}
Civi::log()->debug('Result that will be returned: ' . json_encode($result));
return $result;
}
......@@ -377,7 +380,7 @@ class CRM_Koalecttocivi_KoalectTransaction {
if (!empty($customer['address'])) {
$this->setIncomingAddress($data);
}
$contactFields = ['email', 'firstname', 'lastname', 'user_id', 'newsletter', 'gender', 'iban'];
$contactFields = ['email', 'firstname', 'lastname', 'newsletter', 'gender', 'iban'];
foreach ($contactFields as $contactField) {
$data[$contactField] = $customer[$contactField];
}
......@@ -444,7 +447,7 @@ class CRM_Koalecttocivi_KoalectTransaction {
*/
private function prepareResult(array $formProcessorResult) {
$result = [];
$resultFields = ['campaign_id', 'campaign_type_id', 'contact_id', 'contribution_id', 'soft_credit_id', 'transaction_id', 'user_id', 'gender_id'];
$resultFields = ['campaign_id', 'campaign_type_id', 'contact_id', 'contribution_id', 'soft_credit_id', 'transaction_id', 'gender_id'];
if (isset($formProcessorResult['action'])) {
foreach ($formProcessorResult['action'] as $actionData) {
foreach ($actionData['output'] as $outputName => $outputValue) {
......
......@@ -13,13 +13,10 @@ class CRM_Koalecttocivi_Upgrader extends CRM_Koalecttocivi_Upgrader_Base {
* Example: Run an external SQL script when the module is installed.
*/
public function install() {
// warning that campaign and identity tracker are recommended to use this extension
// warning that campaign is recommended to use this extension
if (!CRM_Koalecttocivi_Utils::isCiviCampaignActive()) {
CRM_Core_Session::setStatus(E::ts('Switching on the CiviCampaign component is HIGHLY recommended when using this extension.'), E::ts("No CiviCampaign detected"));
}
if (!CRM_Koalecttocivi_Utils::isIdentityTrackerActive()) {
CRM_Core_Session::setStatus(E::ts('Installing the Identity Tracker extension (de.systopia.identitytracker) is HIGHLY recommended when using this extension.'), E::ts("No Identity Tracker extension detected"));
}
CRM_Koalecttocivi_Utils::createKoalectLocationType();
$optionValue = new CRM_Koalecttocivi_OptionValue();
// create koalect soft credit type
......@@ -51,18 +48,6 @@ class CRM_Koalecttocivi_Upgrader extends CRM_Koalecttocivi_Upgrader_Base {
]);
}
}
// create koalect ID identifier type if Identity Tracker active
if (CRM_Koalecttocivi_Utils::isIdentityTrackerActive()) {
$identifierTypeOptionGroupId = CRM_Koalecttocivi_Utils::getOptionGroupIdWithName('contact_id_history_type');
if ($identifierTypeOptionGroupId) {
$optionValue->create($identifierTypeOptionGroupId, [
'label' => "Koalect User ID",
'name' => "koalect_user_id",
'value' => "koalect_user_id",
'description' => E::ts("Koalect Contact ID Identity Tracker Type"),
]);
}
}
// create custom data
$this->createKoalectCustomData();
}
......
......@@ -24,20 +24,6 @@ class CRM_Koalecttocivi_Utils {
return FALSE;
}
/**
* Method to check if the identity tracker extension is installed
*
* @return bool
*/
public static function isIdentityTrackerActive() {
$query = "SELECT COUNT(*) FROM civicrm_extension WHERE full_name = %1 AND is_active = TRUE";
$count = CRM_Core_DAO::singleValueQuery($query, [1 => ["de.systopia.identitytracker", "String"]]);
if ($count > 0) {
return TRUE;
}
return FALSE;
}
/**
* Method to get custom data definitions for installation upon install
*
......
<?php
namespace Civi\Koalecttocivi\Actions;
use \Civi\ActionProvider\Action\AbstractAction;
use Civi\ActionProvider\Exception\ExecutionException;
use \Civi\ActionProvider\Exception\InvalidParameterException;
use Civi\ActionProvider\Parameter\OptionGroupByNameSpecification;
use \Civi\ActionProvider\Parameter\ParameterBagInterface;
use \Civi\ActionProvider\Parameter\SpecificationBag;
use \Civi\ActionProvider\Parameter\Specification;
use Civi\Core\Lock\NullLock;
use Civi\FormProcessor\API\Exception;
use CRM_Koalecttocivi_ExtensionUtil as E;
/**
* Class FindKoalectContact - find contact with the transferred koalect user id
*
* @package Civi\Koalecttocivi\Actions
* @author Erik Hommel (CiviCooP) <erik.hommel@civicoop.org>
* @license AGPL-3.0
*/
class FindKoalectContact extends AbstractAction {
/**
* @return SpecificationBag
*/
public function getParameterSpecification() {
$specs = new SpecificationBag();
$specs->addSpecification(new Specification('koalect_user_id', 'String', E::ts('Koalect User ID'), TRUE, NULL));
return $specs;
}
/**
* @return SpecificationBag
*/
public function getConfigurationSpecification() {
return new SpecificationBag();
}
/**
* Do the actual action - find a contact_id with the koalect user id
* @param ParameterBagInterface $parameters
* @param ParameterBagInterface $output
* @throws ExecutionException
*/
public function doAction(ParameterBagInterface $parameters, ParameterBagInterface $output) {
$koalectUserId = $parameters->getParameter('koalect_user_id');
if ($koalectUserId) {
$contactId = \Civi::service('koalecttocivi')->findContactIdWithKoalectUserId($koalectUserId);
if ($contactId) {
$output->setParameter('contact_id', $contactId);
}
}
else {
throw new ExecutionException(E::ts('Koalect User ID is mandatory and can not be empty.'));
}
}
/**
* Returns the specification of the output parameters of this action.
*
* This function could be overriden by child classes.
*
* @return SpecificationBag
*/
public function getOutputSpecification() {
return new SpecificationBag([
new Specification('contact_id', 'Integer', E::ts('Contact ID'), FALSE, NULL)
]);
}
}
......@@ -29,6 +29,7 @@ class KoalectDonation extends AbstractAction {
public function getParameterSpecification() {
$specs = new SpecificationBag();
$specs->addSpecification(new Specification('contact_id', 'Integer', E::ts('Contact ID'), TRUE, NULL));
$specs->addSpecification(new Specification('project_owner_contact_id', 'Integer', E::ts('Project Owner Contact ID'), FALSE, NULL));
$specs->addSpecification(new Specification('donation_amount', 'Float', E::ts('Koalect Donation Amount'), TRUE, NULL));
$specs->addSpecification(new Specification('donation_total_fee', 'Float', E::ts('Koalect Donation Total Fee'), FALSE, NULL));
$specs->addSpecification(new Specification('donation_status', 'String', E::ts('Koalect Donation Status'), FALSE, NULL));
......@@ -54,7 +55,6 @@ class KoalectDonation extends AbstractAction {
$specs->addSpecification(new Specification('stripe_payout_id', 'String', E::ts("Stripe Payout ID"), FALSE, NULL));
$specs->addSpecification(new Specification('koalect_subscription_id', 'String', E::ts("Koalect Subscription ID"), FALSE, NULL));
$specs->addSpecification(new Specification('contribution_source', 'String', E::ts("Source for Contribution"), FALSE, NULL));
$specs->addSpecification(new Specification('project_owner_id', 'String', E::ts("Koalect Project Owner User ID"), FALSE, NULL));
$specs->addSpecification(new Specification('project_owner_first_name', 'String', E::ts("Koalect Project Owner First Name"), FALSE, NULL));
$specs->addSpecification(new Specification('project_owner_last_name', 'String', E::ts("Koalect Project Owner Last Name"), FALSE, NULL));
$specs->addSpecification(new Specification('project_owner_email', 'String', E::ts("Koalect Project Owner Email"), FALSE, NULL));
......@@ -94,9 +94,6 @@ class KoalectDonation extends AbstractAction {
if ($koalectTransactionId) {
$output->setParameter('transaction_id', $koalectTransactionId);
$koalectUserId = $parameters->getParameter('koalect_user_id');