Fix PHPStan issues

This commit is contained in:
Jens Schuppe 2024-04-05 12:24:11 +02:00
parent d7b066751a
commit 8bcdff4a85
16 changed files with 385 additions and 364 deletions

View file

@ -27,7 +27,7 @@ class CRM_Twingle_Config {
* Get the options for protecting a recurring contribution linked Twingle * Get the options for protecting a recurring contribution linked Twingle
* against ending or cancellation (because Twingle would keep on collecting them) * against ending or cancellation (because Twingle would keep on collecting them)
* *
* @return array * @return array<int, string>
*/ */
public static function getRecurringProtectionOptions() { public static function getRecurringProtectionOptions() {
return [ return [

View file

@ -16,7 +16,8 @@
declare(strict_types = 1); declare(strict_types = 1);
use CRM_Twingle_ExtensionUtil as E; use CRM_Twingle_ExtensionUtil as E;
use Civi\Twingle\Exceptions\ProfileException as ProfileException; use Civi\Twingle\Exceptions\ProfileException;
use Civi\Twingle\Exceptions\BaseException;
/** /**
* Form controller class * Form controller class
@ -30,7 +31,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
* *
* The profile object the form is acting on. * The profile object the form is acting on.
*/ */
protected $profile; protected ?CRM_Twingle_Profile $profile = NULL;
/** /**
* @var string * @var string
@ -40,120 +41,128 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
protected $_op; protected $_op;
/** /**
* @var array * @var array<string, string>
* *
* A static cache of retrieved payment instruments found within * A static cache of retrieved payment instruments found within
* self::getPaymentInstruments(). * self::getPaymentInstruments().
*/ */
protected static $_paymentInstruments = NULL; protected static $_paymentInstruments;
/** /**
* @var array * @var array<int, string>
* *
* A static cache of retrieved contribution statuses found within * A static cache of retrieved contribution statuses found within
* static::getContributionStatusOptions(). * static::getContributionStatusOptions().
*/ */
protected static $_contributionStatusOptions = NULL; protected static array $_contributionStatusOptions;
/** /**
* @var array * @var array<int, string>
* *
* A static cache of retrieved groups found within static::getGroups(). * A static cache of retrieved groups found within static::getGroups().
*/ */
protected static $_groups = NULL; protected static array $_groups;
/** /**
* @var array * @var array<string, string>
* *
* A static cache of retrieved newsletter groups found within * A static cache of retrieved newsletter groups found within
* static::getNewsletterGroups(). * static::getNewsletterGroups().
*/ */
protected static $_newsletterGroups = NULL; protected static array $_newsletterGroups;
/** /**
* @var array * @var array<int, string>
* *
* A static cache of retrieved campaigns found within static::getCampaigns(). * A static cache of retrieved campaigns found within static::getCampaigns().
*/ */
protected static $_campaigns = NULL; protected static array $_campaigns;
/** /**
* @var array * @var array<int, string>
* *
* A static cache of retrieved financial types found within * A static cache of retrieved financial types found within
* static::getFinancialTypes(). * static::getFinancialTypes().
*/ */
protected static $_financialTypes = NULL; protected static array $_financialTypes;
/** /**
* @var array * @var array<int, string>
* *
* A static cache of retrieved genders found within * A static cache of retrieved genders found within
* static::getGenderOptions(). * static::getGenderOptions().
*/ */
protected static $_genderOptions = NULL; protected static array $_genderOptions;
/** /**
* @var array * @var array<int, string>
* *
* A static cache of retrieved prefixes found within * A static cache of retrieved prefixes found within
* static::getGenderOptions(). * static::getGenderOptions().
*/ */
protected static $_prefixOptions = NULL; protected static array $_prefixOptions;
/** /**
* @var array * @var array<int, string>
* *
* A static cache of retrieved location types found within * A static cache of retrieved location types found within
* static::getLocationTypes(). * static::getLocationTypes().
*/ */
protected static $_locationTypes = NULL; protected static array $_locationTypes;
/** /**
* @var array * @var array<string, string>
* *
* A static cache of retrieved location types found within * A static cache of retrieved location types found within
* static::getXCMProfiles(). * static::getXCMProfiles().
*/ */
protected static $_xcm_profiles = NULL; protected static array $_xcm_profiles;
/** /**
* @var array * @var array<int, string>
* *
* A static cache of retrieved membership types found within * A static cache of retrieved membership types found within
* static::getMembershipTypes(). * static::getMembershipTypes().
*/ */
protected static $_membershipTypes = NULL; protected static array $_membershipTypes;
/** /**
* @var array * @var array<int, string>
* *
* A static cache of retrieved CiviSEPA creditors found within * A static cache of retrieved CiviSEPA creditors found within
* static::getSepaCreditors(). * static::getSepaCreditors().
*/ */
protected static $_sepaCreditors = NULL; protected static array $_sepaCreditors;
public function preProcess(): void {
// "Create" is the default operation.
$op = CRM_Utils_Request::retrieve('op', 'String', $this);
$this->_op = is_string($op) ? $op : 'create';
// Verify that a profile with the given name exists.
$profile_name = CRM_Utils_Request::retrieve('name', 'String', $this);
if (is_string($profile_name)) {
$this->profile = CRM_Twingle_Profile::getProfile($profile_name);
}
// Set redirect destination.
$this->controller->_destination = CRM_Utils_System::url(
'civicrm/admin/settings/twingle/profiles',
'reset=1'
);
parent::preProcess();
}
/** /**
* Builds the form structure. * Builds the form structure.
*/ */
public function buildQuickForm() { public function buildQuickForm(): void {
// "Create" is the default operation. $profile_name = (isset($this->profile) ? $this->profile->getName() : NULL);
if (!$this->_op = CRM_Utils_Request::retrieve('op', 'String', $this)) {
$this->_op = 'create';
}
// Verify that a profile with the given name exists.
$profile_name = CRM_Utils_Request::retrieve('name', 'String', $this);
if (!$this->profile = CRM_Twingle_Profile::getProfile($profile_name)) {
$profile_name = NULL;
}
// Set redirect destination.
$this->controller->_destination = CRM_Utils_System::url('civicrm/admin/settings/twingle/profiles', 'reset=1');
switch ($this->_op) { switch ($this->_op) {
case 'delete': case 'delete':
if ($profile_name) { if (isset($profile_name)) {
CRM_Utils_System::setTitle(E::ts('Delete Twingle API profile <em>%1</em>', [1 => $profile_name])); CRM_Utils_System::setTitle(E::ts('Delete Twingle API profile <em>%1</em>', [1 => $profile_name]));
$this->addButtons([ $this->addButtons([
[ [
@ -168,10 +177,13 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
case 'edit': case 'edit':
// When editing without a valid profile name, edit the default profile. // When editing without a valid profile name, edit the default profile.
if (!$profile_name) { if (!isset($profile_name)) {
$profile_name = 'default'; $profile_name = 'default';
$this->profile = CRM_Twingle_Profile::getProfile($profile_name); $this->profile = CRM_Twingle_Profile::getProfile($profile_name);
} }
if (!isset($this->profile)) {
throw new BaseException(E::ts('Could not retrieve profile with name "%1"', [1 => $profile_name]));
}
CRM_Utils_System::setTitle(E::ts('Edit Twingle API profile <em>%1</em>', [1 => $this->profile->getName()])); CRM_Utils_System::setTitle(E::ts('Edit Twingle API profile <em>%1</em>', [1 => $this->profile->getName()]));
break; break;
@ -179,10 +191,14 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
// Retrieve the source profile name. // Retrieve the source profile name.
$profile_name = CRM_Utils_Request::retrieve('source_name', 'String', $this); $profile_name = CRM_Utils_Request::retrieve('source_name', 'String', $this);
// When copying without a valid profile name, copy the default profile. // When copying without a valid profile name, copy the default profile.
if (!$profile_name) { if (!is_string($profile_name)) {
$profile_name = 'default'; $profile_name = 'default';
} }
$this->profile = clone CRM_Twingle_Profile::getProfile($profile_name); $originalProfile = CRM_Twingle_Profile::getProfile($profile_name);
if (!isset($originalProfile)) {
throw new BaseException(E::ts('Could not retrieve profile with name "%1"', [1 => $profile_name]));
}
$this->profile = clone $originalProfile;
// Propose a new name for this profile. // Propose a new name for this profile.
$profile_name = $profile_name . '_copy'; $profile_name = $profile_name . '_copy';
@ -192,7 +208,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
case 'create': case 'create':
// Load factory default profile values. // Load factory default profile values.
$this->profile = CRM_Twingle_Profile::createDefaultProfile($profile_name); $this->profile = CRM_Twingle_Profile::createDefaultProfile($profile_name ?? 'default');
CRM_Utils_System::setTitle(E::ts('New Twingle API profile')); CRM_Utils_System::setTitle(E::ts('New Twingle API profile'));
break; break;
} }
@ -354,10 +370,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
// field name // field name
'newsletter_double_opt_in', 'newsletter_double_opt_in',
// field label // field label
E::ts('Use Double-Opt-In for newsletter'), E::ts('Use Double-Opt-In for newsletter')
// is not required
FALSE,
[]
); );
$this->add( $this->add(
@ -461,8 +474,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
$this->add( $this->add(
'text', 'text',
'membership_postprocess_call', 'membership_postprocess_call',
E::ts('API Call for Membership Postprocessing'), E::ts('API Call for Membership Postprocessing')
FALSE
); );
$this->addRule( $this->addRule(
'membership_postprocess_call', 'membership_postprocess_call',
@ -521,9 +533,8 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
/** /**
* Validates the profile form. * Validates the profile form.
* *
* @return bool|array * @return bool
* TRUE when the form was successfully validated, or an array of error * TRUE when the form was successfully validated.
* messages, keyed by form element name.
*/ */
public function validate() { public function validate() {
$values = $this->exportValues(); $values = $this->exportValues();
@ -531,14 +542,14 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
// Validate new profile names. // Validate new profile names.
if ( if (
isset($values['name']) isset($values['name'])
&& ($values['name'] != $this->profile->getName() || $this->_op != 'edit') && (!isset($this->profile) || $values['name'] != $this->profile->getName() || $this->_op != 'edit')
&& !empty(CRM_Twingle_Profile::getProfile($values['name'])) && NULL !== CRM_Twingle_Profile::getProfile($values['name'])
) { ) {
$this->_errors['name'] = E::ts('A profile with this name already exists.'); $this->_errors['name'] = E::ts('A profile with this name already exists.');
} }
// Restrict profile names to alphanumeric characters and the underscore. // Restrict profile names to alphanumeric characters and the underscore.
if (isset($values['name']) && preg_match('/[^A-Za-z0-9\_]/', $values['name'])) { if (isset($values['name']) && 1 === preg_match('/[^A-Za-z0-9\_]/', $values['name'])) {
$this->_errors['name'] = $this->_errors['name'] =
E::ts('Only alphanumeric characters and the underscore (_) are allowed for profile names.'); E::ts('Only alphanumeric characters and the underscore (_) are allowed for profile names.');
} }
@ -548,14 +559,14 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
if (isset($values['custom_field_mapping'])) { if (isset($values['custom_field_mapping'])) {
$custom_field_mapping = preg_split('/\r\n|\r|\n/', $values['custom_field_mapping'], -1, PREG_SPLIT_NO_EMPTY); $custom_field_mapping = preg_split('/\r\n|\r|\n/', $values['custom_field_mapping'], -1, PREG_SPLIT_NO_EMPTY);
if (!is_array($custom_field_mapping)) { if (!is_array($custom_field_mapping)) {
throw new Exception( throw new BaseException(
E::ts('Could not parse custom field mapping.') E::ts('Could not parse custom field mapping.')
); );
} }
foreach ($custom_field_mapping as $custom_field_map) { foreach ($custom_field_mapping as $custom_field_map) {
$custom_field_map = explode('=', $custom_field_map); $custom_field_map = explode('=', $custom_field_map);
if (count($custom_field_map) !== 2) { if (count($custom_field_map) !== 2) {
throw new Exception( throw new BaseException(
E::ts('Could not parse custom field mapping.') E::ts('Could not parse custom field mapping.')
); );
} }
@ -568,12 +579,14 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
'id' => $custom_field_id, 'id' => $custom_field_id,
]); ]);
} }
catch (CiviCRM_API3_Exception $exception) { catch (CRM_Core_Exception $exception) {
throw new Exception( throw new BaseException(
E::ts( E::ts(
'Custom field custom_%1 does not exist.', 'Custom field custom_%1 does not exist.',
[1 => $custom_field_id] [1 => $custom_field_id]
) ),
NULL,
$exception
); );
} }
@ -592,18 +605,20 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
], ],
]); ]);
} }
catch (CiviCRM_API3_Exception $exception) { catch (CRM_Core_Exception $exception) {
throw new Exception( throw new BaseException(
E::ts( E::ts(
'Custom field custom_%1 is not in a CustomGroup that extends one of the supported CiviCRM entities.', 'Custom field custom_%1 is not in a CustomGroup that extends one of the supported CiviCRM entities.',
[1 => $custom_field['id']] [1 => $custom_field['id']]
) ),
NULL,
$exception
); );
} }
} }
} }
} }
catch (Exception $exception) { catch (BaseException $exception) {
$this->_errors['custom_field_mapping'] = $exception->getMessage(); $this->_errors['custom_field_mapping'] = $exception->getMessage();
} }
@ -612,12 +627,14 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
/** /**
* Set the default values (i.e. the profile's current data) in the form. * Set the default values (i.e. the profile's current data) in the form.
*
* @return array<string, mixed>
*/ */
public function setDefaultValues() { public function setDefaultValues() {
$defaults = parent::setDefaultValues(); $defaults = parent::setDefaultValues();
if (in_array($this->_op, ['create', 'edit', 'copy'])) { if (in_array($this->_op, ['create', 'edit', 'copy'], TRUE)) {
$defaults['name'] = $this->profile->getName(); $defaults['name'] = isset($this->profile) ? $this->profile->getName() : NULL;
$profile_data = $this->profile->getData(); $profile_data = isset($this->profile) ? $this->profile->getData() : [];
foreach ($profile_data as $element_name => $value) { foreach ($profile_data as $element_name => $value) {
$defaults[$element_name] = $value; $defaults[$element_name] = $value;
} }
@ -632,11 +649,14 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
/** /**
* Store the values submitted with the form in the profile. * Store the values submitted with the form in the profile.
*/ */
public function postProcess() { public function postProcess(): void {
$values = $this->exportValues(); $values = $this->exportValues();
try { try {
if (in_array($this->_op, ['create', 'edit', 'copy'])) { if (!isset($this->profile)) {
if (empty($values['name'])) { throw new BaseException(E::ts('No profile set.'));
}
if (in_array($this->_op, ['create', 'edit', 'copy'], TRUE)) {
if (!is_string($values['name'])) {
$values['name'] = 'default'; $values['name'] = 'default';
} }
$this->profile->setName($values['name']); $this->profile->setName($values['name']);
@ -670,9 +690,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
* Retrieves location types present within the system as options for select * Retrieves location types present within the system as options for select
* form elements. * form elements.
* *
* @return array * @return array<int, string>
*
* @throws \CiviCRM_API3_Exception
*/ */
public static function getLocationTypes() { public static function getLocationTypes() {
if (!isset(static::$_locationTypes)) { if (!isset(static::$_locationTypes)) {
@ -682,7 +700,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
'is_active' => 1, 'is_active' => 1,
]); ]);
foreach ($query['values'] as $type) { foreach ($query['values'] as $type) {
static::$_locationTypes[$type['id']] = $type['name']; static::$_locationTypes[(int) $type['id']] = $type['name'];
} }
} }
return static::$_locationTypes; return static::$_locationTypes;
@ -691,11 +709,11 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
/** /**
* Retrieves XCM profiles (if supported). 'default' profile is always available * Retrieves XCM profiles (if supported). 'default' profile is always available
* *
* @return array * @return array<string, string>
*/ */
public static function getXCMProfiles() { public static function getXCMProfiles() {
if (!isset(static::$_xcm_profiles)) { if (!isset(static::$_xcm_profiles)) {
if (method_exists('CRM_Xcm_Configuration', 'getProfileList')) { if (class_exists('CRM_Xcm_Configuration')) {
static::$_xcm_profiles = [ static::$_xcm_profiles = [
'' => E::ts('&lt;select profile&gt;'), '' => E::ts('&lt;select profile&gt;'),
]; ];
@ -712,9 +730,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
* Retrieves financial types present within the system as options for select * Retrieves financial types present within the system as options for select
* form elements. * form elements.
* *
* @return array * @return array<int, string>
*
* @throws \CiviCRM_API3_Exception
*/ */
public static function getFinancialTypes() { public static function getFinancialTypes() {
if (!isset(static::$_financialTypes)) { if (!isset(static::$_financialTypes)) {
@ -725,7 +741,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
'return' => 'id,name', 'return' => 'id,name',
]); ]);
foreach ($query['values'] as $type) { foreach ($query['values'] as $type) {
static::$_financialTypes[$type['id']] = $type['name']; static::$_financialTypes[(int) $type['id']] = $type['name'];
} }
} }
return static::$_financialTypes; return static::$_financialTypes;
@ -735,9 +751,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
* Retrieves membership types present within the system as options for select * Retrieves membership types present within the system as options for select
* form elements. * form elements.
* *
* @return array * @return array<int, string>
*
* @throws \CiviCRM_API3_Exception
*/ */
public static function getMembershipTypes() { public static function getMembershipTypes() {
if (!isset(static::$_membershipTypes)) { if (!isset(static::$_membershipTypes)) {
@ -758,9 +772,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
* Retrieves genders present within the system as options for select form * Retrieves genders present within the system as options for select form
* elements. * elements.
* *
* @return array * @return array<int, string>
*
* @throws \CiviCRM_API3_Exception
*/ */
public static function getGenderOptions() { public static function getGenderOptions() {
if (!isset(static::$_genderOptions)) { if (!isset(static::$_genderOptions)) {
@ -775,7 +787,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
], ],
]); ]);
foreach ($query['values'] as $gender) { foreach ($query['values'] as $gender) {
static::$_genderOptions[$gender['value']] = $gender['label']; static::$_genderOptions[(int) $gender['value']] = $gender['label'];
} }
} }
return static::$_genderOptions; return static::$_genderOptions;
@ -785,9 +797,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
* Retrieves prefixes present within the system as options for select form * Retrieves prefixes present within the system as options for select form
* elements. * elements.
* *
* @return array * @return array<int, string>
*
* @throws \CiviCRM_API3_Exception
*/ */
public static function getPrefixOptions() { public static function getPrefixOptions() {
if (!isset(static::$_prefixOptions)) { if (!isset(static::$_prefixOptions)) {
@ -802,7 +812,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
], ],
]); ]);
foreach ($query['values'] as $prefix) { foreach ($query['values'] as $prefix) {
static::$_prefixOptions[$prefix['value']] = $prefix['label']; static::$_prefixOptions[(int) $prefix['value']] = $prefix['label'];
} }
} }
return static::$_prefixOptions; return static::$_prefixOptions;
@ -811,9 +821,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
/** /**
* Retrieves CiviSEPA creditors as options for select form elements. * Retrieves CiviSEPA creditors as options for select form elements.
* *
* @return array * @return array<int, string>
*
* @throws \CiviCRM_API3_Exception
*/ */
public static function getSepaCreditors() { public static function getSepaCreditors() {
if (!isset(static::$_sepaCreditors)) { if (!isset(static::$_sepaCreditors)) {
@ -823,7 +831,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
'option.limit' => 0, 'option.limit' => 0,
]); ]);
foreach ($result['values'] as $sepa_creditor) { foreach ($result['values'] as $sepa_creditor) {
static::$_sepaCreditors[$sepa_creditor['id']] = $sepa_creditor['name']; static::$_sepaCreditors[(int) $sepa_creditor['id']] = $sepa_creditor['name'];
} }
} }
} }
@ -834,9 +842,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
* Retrieves payment instruments present within the system as options for * Retrieves payment instruments present within the system as options for
* select form elements. * select form elements.
* *
* @return array * @return array<string, string>
*
* @throws \CiviCRM_API3_Exception
*/ */
public static function getPaymentInstruments() { public static function getPaymentInstruments() {
if (!isset(self::$_paymentInstruments)) { if (!isset(self::$_paymentInstruments)) {
@ -869,9 +875,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
/** /**
* Retrieves contribution statuses as options for select form elements. * Retrieves contribution statuses as options for select form elements.
* *
* @return array * @return array<int, string>
*
* @throws \CiviCRM_API3_Exception
*/ */
public static function getContributionStatusOptions() { public static function getContributionStatusOptions() {
if (!isset(self::$_contributionStatusOptions)) { if (!isset(self::$_contributionStatusOptions)) {
@ -889,7 +893,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
); );
foreach ($query['values'] as $contribution_status) { foreach ($query['values'] as $contribution_status) {
self::$_contributionStatusOptions[$contribution_status['value']] = $contribution_status['label']; self::$_contributionStatusOptions[(int) $contribution_status['value']] = $contribution_status['label'];
} }
} }
@ -900,9 +904,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
* Retrieves active groups used as mailing lists within the system as options * Retrieves active groups used as mailing lists within the system as options
* for select form elements. * for select form elements.
* *
* @return array * @return array<string, string>
*
* @throws \CiviCRM_API3_Exception
* *
*/ */
public static function getNewsletterGroups() { public static function getNewsletterGroups() {
@ -935,9 +937,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
/** /**
* Retrieves active groups as options for select form elements. * Retrieves active groups as options for select form elements.
* *
* @return array * @return array<int, string>
*
* @throws \CiviCRM_API3_Exception
*/ */
public static function getGroups() { public static function getGroups() {
if (!isset(static::$_groups)) { if (!isset(static::$_groups)) {
@ -948,7 +948,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
'return' => 'id,name', 'return' => 'id,name',
]); ]);
foreach ($query['values'] as $group) { foreach ($query['values'] as $group) {
static::$_groups[$group['id']] = $group['name']; static::$_groups[(int) $group['id']] = $group['name'];
} }
} }
return static::$_groups; return static::$_groups;
@ -958,9 +958,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
* Retrieves active groups used as postal mailing lists within the system as * Retrieves active groups used as postal mailing lists within the system as
* options for select form elements. * options for select form elements.
* *
* @return array * @return array<int, string>
*
* @throws \CiviCRM_API3_Exception
*/ */
public static function getPostinfoGroups() { public static function getPostinfoGroups() {
return static::getGroups(); return static::getGroups();
@ -970,9 +968,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
* Retrieves active groups used as donation receipt requester lists within the * Retrieves active groups used as donation receipt requester lists within the
* system as options for select form elements. * system as options for select form elements.
* *
* @return array * @return array<int, string>
*
* @throws \CiviCRM_API3_Exception
*/ */
public static function getDonationReceiptGroups() { public static function getDonationReceiptGroups() {
return static::getGroups(); return static::getGroups();
@ -981,9 +977,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
/** /**
* Retrieves campaigns as options for select elements. * Retrieves campaigns as options for select elements.
* *
* @return array * @return array<int, string>
*
* @throws \CiviCRM_API3_Exception
*/ */
public static function getCampaigns() { public static function getCampaigns() {
if (!isset(static::$_campaigns)) { if (!isset(static::$_campaigns)) {
@ -996,7 +990,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
], ],
]); ]);
foreach ($query['values'] as $campaign) { foreach ($query['values'] as $campaign) {
static::$_campaigns[$campaign['id']] = $campaign['title']; static::$_campaigns[(int) $campaign['id']] = $campaign['title'];
} }
} }
return static::$_campaigns; return static::$_campaigns;

View file

@ -25,7 +25,8 @@ use CRM_Twingle_ExtensionUtil as E;
class CRM_Twingle_Form_Settings extends CRM_Core_Form { class CRM_Twingle_Form_Settings extends CRM_Core_Form {
/** /**
* @var arraylistofallsettingsoptions * @var array<string>
* List of all settings options.
*/ */
public static $SETTINGS_LIST = [ public static $SETTINGS_LIST = [
'twingle_prefix', 'twingle_prefix',
@ -41,7 +42,7 @@ class CRM_Twingle_Form_Settings extends CRM_Core_Form {
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function buildQuickForm() { public function buildQuickForm(): void {
// Set redirect destination. // Set redirect destination.
$this->controller->_destination = CRM_Utils_System::url('civicrm/admin/settings/twingle', 'reset=1'); $this->controller->_destination = CRM_Utils_System::url('civicrm/admin/settings/twingle', 'reset=1');
@ -131,15 +132,14 @@ class CRM_Twingle_Form_Settings extends CRM_Core_Form {
parent::validate(); parent::validate();
// if activity creation is active, make sure the fields are set // if activity creation is active, make sure the fields are set
$protection_mode = CRM_Utils_Array::value('twingle_protect_recurring', $this->_submitValues); $protection_mode = $this->_submitValues['twingle_protect_recurring'] ?? NULL;
if ($protection_mode == CRM_Twingle_Config::RCUR_PROTECTION_ACTIVITY) { if ($protection_mode == CRM_Twingle_Config::RCUR_PROTECTION_ACTIVITY) {
foreach (['twingle_protect_recurring_activity_type', foreach (['twingle_protect_recurring_activity_type',
'twingle_protect_recurring_activity_subject', 'twingle_protect_recurring_activity_subject',
'twingle_protect_recurring_activity_status', 'twingle_protect_recurring_activity_status',
'twingle_protect_recurring_activity_assignee', 'twingle_protect_recurring_activity_assignee',
] as $activity_field) { ] as $activity_field) {
$current_value = CRM_Utils_Array::value($activity_field, $this->_submitValues); if (NULL !== ($this->_submitValues[$activity_field] ?? NULL)) {
if (empty($current_value)) {
$this->_errors[$activity_field] = E::ts('This is required for activity creation'); $this->_errors[$activity_field] = E::ts('This is required for activity creation');
} }
} }
@ -151,12 +151,12 @@ class CRM_Twingle_Form_Settings extends CRM_Core_Form {
/** /**
* @inheritdoc * @inheritdoc
*/ */
public function postProcess() { public function postProcess(): void {
$values = $this->exportValues(); $values = $this->exportValues();
// store settings // store settings
foreach (self::$SETTINGS_LIST as $setting) { foreach (self::$SETTINGS_LIST as $setting) {
Civi::settings()->set($setting, CRM_Utils_Array::value($setting, $values)); Civi::settings()->set($setting, $values[$setting]);
} }
parent::postProcess(); parent::postProcess();
@ -164,11 +164,13 @@ class CRM_Twingle_Form_Settings extends CRM_Core_Form {
/** /**
* Get a list of option group items * Get a list of option group items
* @param $group_id string group ID or name * @param string $group_id
* @return array list of ID(value) => label * Group ID or name.
* @throws CiviCRM_API3_Exception * @param array<int> $reserved
* @return array<int|string, string> list of ID(value) => label
* @throws \CRM_Core_Exception
*/ */
protected function getOptionValueList($group_id, $reserved = [0, 1]) { protected function getOptionValueList(string $group_id, array $reserved = [0, 1]): array {
$list = ['' => E::ts('-select-')]; $list = ['' => E::ts('-select-')];
$query = civicrm_api3('OptionValue', 'get', [ $query = civicrm_api3('OptionValue', 'get', [
'option_group_id' => $group_id, 'option_group_id' => $group_id,

View file

@ -19,7 +19,7 @@ use CRM_Twingle_ExtensionUtil as E;
class CRM_Twingle_Page_Configuration extends CRM_Core_Page { class CRM_Twingle_Page_Configuration extends CRM_Core_Page {
public function run() { public function run(): void {
parent::run(); parent::run();
} }

View file

@ -19,7 +19,7 @@ use CRM_Twingle_ExtensionUtil as E;
class CRM_Twingle_Page_Profiles extends CRM_Core_Page { class CRM_Twingle_Page_Profiles extends CRM_Core_Page {
public function run() { public function run():void {
CRM_Utils_System::setTitle(E::ts('Twingle API Profiles')); CRM_Utils_System::setTitle(E::ts('Twingle API Profiles'));
$profiles = []; $profiles = [];
foreach (CRM_Twingle_Profile::getProfiles() as $profile_name => $profile) { foreach (CRM_Twingle_Profile::getProfiles() as $profile_name => $profile) {

View file

@ -28,20 +28,20 @@ class CRM_Twingle_Profile {
* @var string * @var string
* The name of the profile. * The name of the profile.
*/ */
protected $name = NULL; protected $name;
/** /**
* @var array * @var array<string, mixed>
* The properties of the profile. * The properties of the profile.
*/ */
protected $data = NULL; protected $data;
/** /**
* CRM_Twingle_Profile constructor. * CRM_Twingle_Profile constructor.
* *
* @param string $name * @param string $name
* The name of the profile. * The name of the profile.
* @param array $data * @param array<string, mixed> $data
* The properties of the profile * The properties of the profile
*/ */
public function __construct($name, $data) { public function __construct($name, $data) {
@ -56,7 +56,7 @@ class CRM_Twingle_Profile {
/** /**
* Logs (production) access to this profile * Logs (production) access to this profile
*/ */
public function logAccess() { public function logAccess(): void {
CRM_Core_DAO::executeQuery(' CRM_Core_DAO::executeQuery('
UPDATE civicrm_twingle_profile UPDATE civicrm_twingle_profile
SET SET
@ -80,28 +80,38 @@ class CRM_Twingle_Profile {
}, },
explode(',', $selector) explode(',', $selector)
); );
return in_array($project_id, $project_ids); return in_array($project_id, $project_ids, TRUE);
} }
/** /**
* @return array * Retrieves the profile's configured custom field mapping.
*
* @return array<string, string>
* The profile's configured custom field mapping * The profile's configured custom field mapping
*/ */
public function getCustomFieldMapping() { public function getCustomFieldMapping() {
$custom_field_mapping = []; $custom_field_mapping = [];
if (!empty($custom_field_definition = $this->getAttribute('custom_field_mapping'))) { if (is_string($custom_field_definition = $this->getAttribute('custom_field_mapping'))) {
foreach (preg_split('/\r\n|\r|\n/', $custom_field_definition, -1, PREG_SPLIT_NO_EMPTY) as $custom_field_map) { $custom_field_maps = preg_split(
'/\r\n|\r|\n/',
$custom_field_definition,
-1,
PREG_SPLIT_NO_EMPTY
);
if (FALSE !== $custom_field_maps) {
foreach ($custom_field_maps as $custom_field_map) {
[$twingle_field_name, $custom_field_name] = explode('=', $custom_field_map); [$twingle_field_name, $custom_field_name] = explode('=', $custom_field_map);
$custom_field_mapping[$twingle_field_name] = $custom_field_name; $custom_field_mapping[$twingle_field_name] = $custom_field_name;
} }
} }
}
return $custom_field_mapping; return $custom_field_mapping;
} }
/** /**
* Retrieves all data attributes of the profile. * Retrieves all data attributes of the profile.
* *
* @return array * @return array<string, mixed>
*/ */
public function getData() { public function getData() {
return $this->data; return $this->data;
@ -119,9 +129,9 @@ class CRM_Twingle_Profile {
/** /**
* Sets the profile name. * Sets the profile name.
* *
* @param $name * @param string $name
*/ */
public function setName($name) { public function setName(string $name): void {
$this->name = $name; $this->name = $name;
} }
@ -146,8 +156,8 @@ class CRM_Twingle_Profile {
* @throws \Civi\Twingle\Exceptions\ProfileException * @throws \Civi\Twingle\Exceptions\ProfileException
* When the attribute name is not known. * When the attribute name is not known.
*/ */
public function setAttribute($attribute_name, $value) { public function setAttribute($attribute_name, $value): void {
if (!in_array($attribute_name, self::allowedAttributes())) { if (!in_array($attribute_name, self::allowedAttributes(), TRUE)) {
throw new ProfileException( throw new ProfileException(
E::ts('Unknown attribute %1.', [1 => $attribute_name]), E::ts('Unknown attribute %1.', [1 => $attribute_name]),
ProfileException::ERROR_CODE_UNKNOWN_PROFILE_ATTRIBUTE ProfileException::ERROR_CODE_UNKNOWN_PROFILE_ATTRIBUTE
@ -160,27 +170,19 @@ class CRM_Twingle_Profile {
/** /**
* Get the CiviCRM transaction ID (to be used in contributions and recurring contributions) * Get the CiviCRM transaction ID (to be used in contributions and recurring contributions)
* *
* @param $twingle_id string Twingle ID * @param string $twingle_id Twingle ID
* @return string CiviCRM transaction ID * @return string CiviCRM transaction ID
*/ */
public function getTransactionID($twingle_id) { public function getTransactionID(string $twingle_id) {
$prefix = Civi::settings()->get('twingle_prefix'); $prefix = Civi::settings()->get('twingle_prefix');
if (empty($prefix)) { return ($prefix ?? '') . $twingle_id;
return $twingle_id;
}
else {
return $prefix . $twingle_id;
}
} }
/** /**
* Verifies whether the profile is valid (i.e. consistent and not colliding * Verifies whether the profile is valid (i.e. consistent and not colliding
* with other profiles). * with other profiles).
*
* @throws Exception
* When the profile could not be successfully validated.
*/ */
public function verifyProfile() { public function verifyProfile(): void {
// TODO: check // TODO: check
// data of this profile consistent? // data of this profile consistent?
// conflicts with other profiles? // conflicts with other profiles?
@ -189,14 +191,14 @@ class CRM_Twingle_Profile {
/** /**
* Persists the profile within the CiviCRM settings. * Persists the profile within the CiviCRM settings.
*/ */
public function saveProfile() { public function saveProfile(): void {
// make sure it's valid // make sure it's valid
$this->verifyProfile(); $this->verifyProfile();
// check if the profile exists // check if the profile exists
$profile_id = CRM_Core_DAO::singleValueQuery( $profile_id = CRM_Core_DAO::singleValueQuery(
'SELECT id FROM civicrm_twingle_profile WHERE name = %1', [1 => [$this->name, 'String']]); 'SELECT id FROM civicrm_twingle_profile WHERE name = %1', [1 => [$this->name, 'String']]);
if ($profile_id) { if (isset($profile_id)) {
// existing profile -> just update the config // existing profile -> just update the config
CRM_Core_DAO::executeQuery( CRM_Core_DAO::executeQuery(
'UPDATE civicrm_twingle_profile SET config = %2 WHERE name = %1', 'UPDATE civicrm_twingle_profile SET config = %2 WHERE name = %1',
@ -219,7 +221,7 @@ class CRM_Twingle_Profile {
/** /**
* Deletes the profile from the database * Deletes the profile from the database
*/ */
public function deleteProfile() { public function deleteProfile(): void {
CRM_Core_DAO::executeQuery( CRM_Core_DAO::executeQuery(
'DELETE FROM civicrm_twingle_profile WHERE name = %1', 'DELETE FROM civicrm_twingle_profile WHERE name = %1',
[1 => [$this->name, 'String']] [1 => [$this->name, 'String']]
@ -229,7 +231,7 @@ class CRM_Twingle_Profile {
/** /**
* Returns an array of attributes allowed for a profile. * Returns an array of attributes allowed for a profile.
* *
* @return array * @return array<string>
*/ */
public static function allowedAttributes() { public static function allowedAttributes() {
return array_merge( return array_merge(
@ -273,9 +275,9 @@ class CRM_Twingle_Profile {
/** /**
* Retrieves a list of supported payment methods. * Retrieves a list of supported payment methods.
* *
* @return array * @return array<string, string>
*/ */
public static function paymentInstruments() { public static function paymentInstruments(): array {
return [ return [
'pi_banktransfer' => E::ts('Bank transfer'), 'pi_banktransfer' => E::ts('Bank transfer'),
'pi_debit_manual' => E::ts('Debit manual'), 'pi_debit_manual' => E::ts('Debit manual'),
@ -364,10 +366,9 @@ class CRM_Twingle_Profile {
* which is responsible for processing the project's data. * which is responsible for processing the project's data.
* Returns the default profile if no match was found. * Returns the default profile if no match was found.
* *
* @param $project_id * @param string $project_id
* *
* @return CRM_Twingle_Profile * @return CRM_Twingle_Profile
* @throws \Civi\Core\Exception\DBQueryException
*/ */
public static function getProfileForProject($project_id) { public static function getProfileForProject($project_id) {
$profiles = self::getProfiles(); $profiles = self::getProfiles();
@ -379,9 +380,8 @@ class CRM_Twingle_Profile {
} }
// If none matches, use the default profile. // If none matches, use the default profile.
$default_profile = $profiles['default']; if (isset($profiles['default'])) {
if (!empty($default_profile)) { return $profiles['default'];
return $default_profile;
} }
else { else {
throw new ProfileException( throw new ProfileException(
@ -399,23 +399,18 @@ class CRM_Twingle_Profile {
* @return CRM_Twingle_Profile|NULL * @return CRM_Twingle_Profile|NULL
*/ */
public static function getProfile($name) { public static function getProfile($name) {
if (!empty($name)) {
$profile_data = CRM_Core_DAO::singleValueQuery( $profile_data = CRM_Core_DAO::singleValueQuery(
'SELECT config FROM civicrm_twingle_profile WHERE name = %1', 'SELECT config FROM civicrm_twingle_profile WHERE name = %1',
[1 => [$name, 'String']] [1 => [$name, 'String']]
); );
if ($profile_data) { return isset($profile_data) ? new CRM_Twingle_Profile($name, (array) json_decode($profile_data, TRUE)) : NULL;
return new CRM_Twingle_Profile($name, json_decode($profile_data, 1));
}
}
return NULL;
} }
/** /**
* Retrieves the list of all profiles persisted within the current CiviCRM * Retrieves the list of all profiles persisted within the current CiviCRM
* settings, including the default profile. * settings, including the default profile.
* *
* @return array * @return array<string, \CRM_Twingle_Profile>
* profile_name => CRM_Twingle_Profile * profile_name => CRM_Twingle_Profile
*/ */
public static function getProfiles() { public static function getProfiles() {
@ -439,7 +434,9 @@ class CRM_Twingle_Profile {
*/ */
public static function getProfileStats() { public static function getProfileStats() {
$stats = []; $stats = [];
$profile_data = CRM_Core_DAO::executeQuery('SELECT name, last_access, access_counter FROM civicrm_twingle_profile'); $profile_data = CRM_Core_DAO::executeQuery(
'SELECT name, last_access, access_counter FROM civicrm_twingle_profile'
);
while ($profile_data->fetch()) { while ($profile_data->fetch()) {
// phpcs:disable Drupal.Arrays.Array.ArrayIndentation // phpcs:disable Drupal.Arrays.Array.ArrayIndentation
$stats[$profile_data->name] = [ $stats[$profile_data->name] = [

View file

@ -16,6 +16,7 @@
declare(strict_types = 1); declare(strict_types = 1);
use CRM_Twingle_ExtensionUtil as E; use CRM_Twingle_ExtensionUtil as E;
use Civi\Twingle\Exceptions\BaseException;
class CRM_Twingle_Submission { class CRM_Twingle_Submission {
@ -40,18 +41,18 @@ class CRM_Twingle_Submission {
public const EMPLOYER_RELATIONSHIP_TYPE_ID = 5; public const EMPLOYER_RELATIONSHIP_TYPE_ID = 5;
/** /**
* @param array &$params * @param array<string, mixed> &$params
* A reference to the parameters array of the submission. * A reference to the parameters array of the submission.
* *
* @param \CRM_Twingle_Profile $profile * @param \CRM_Twingle_Profile $profile
* The Twingle profile to use for validation, defaults to the default * The Twingle profile to use for validation, defaults to the default
* profile. * profile.
* *
* @throws \CiviCRM_API3_Exception * @throws \CRM_Core_Exception
* When invalid parameters have been submitted. * When invalid parameters have been submitted.
*/ */
public static function validateSubmission(&$params, $profile = NULL) { public static function validateSubmission(&$params, $profile = NULL): void {
if (!$profile) { if (!isset($profile)) {
$profile = CRM_Twingle_Profile::createDefaultProfile(); $profile = CRM_Twingle_Profile::createDefaultProfile();
} }
@ -62,8 +63,8 @@ class CRM_Twingle_Submission {
'quarterly', 'quarterly',
'yearly', 'yearly',
'monthly', 'monthly',
])) { ], TRUE)) {
throw new CiviCRM_API3_Exception( throw new CRM_Core_Exception(
E::ts('Invalid donation rhythm.'), E::ts('Invalid donation rhythm.'),
'invalid_format' 'invalid_format'
); );
@ -71,8 +72,9 @@ class CRM_Twingle_Submission {
// Get the payment instrument defined within the profile, or return an error // Get the payment instrument defined within the profile, or return an error
// if none matches (i.e. an unknown payment method was submitted). // if none matches (i.e. an unknown payment method was submitted).
if (!$payment_instrument_id = $profile->getAttribute('pi_' . $params['payment_method'])) { $payment_instrument_id = $profile->getAttribute('pi_' . $params['payment_method']);
throw new CiviCRM_API3_Exception( if (!isset($payment_instrument_id)) {
throw new CRM_Core_Exception(
E::ts('Payment method could not be matched to existing payment instrument.'), E::ts('Payment method could not be matched to existing payment instrument.'),
'invalid_format' 'invalid_format'
); );
@ -80,16 +82,16 @@ class CRM_Twingle_Submission {
$params['payment_instrument_id'] = $payment_instrument_id; $params['payment_instrument_id'] = $payment_instrument_id;
// Validate date for parameter "confirmed_at". // Validate date for parameter "confirmed_at".
if (!DateTime::createFromFormat('YmdHis', $params['confirmed_at'])) { if (FALSE === DateTime::createFromFormat('YmdHis', $params['confirmed_at'])) {
throw new CiviCRM_API3_Exception( throw new CRM_Core_Exception(
E::ts('Invalid date for parameter "confirmed_at".'), E::ts('Invalid date for parameter "confirmed_at".'),
'invalid_format' 'invalid_format'
); );
} }
// Validate date for parameter "user_birthdate". // Validate date for parameter "user_birthdate".
if (!empty($params['user_birthdate']) && !DateTime::createFromFormat('Ymd', $params['user_birthdate'])) { if (!empty($params['user_birthdate']) && FALSE === DateTime::createFromFormat('Ymd', $params['user_birthdate'])) {
throw new CiviCRM_API3_Exception( throw new CRM_Core_Exception(
E::ts('Invalid date for parameter "user_birthdate".'), E::ts('Invalid date for parameter "user_birthdate".'),
'invalid_format' 'invalid_format'
); );
@ -97,9 +99,10 @@ class CRM_Twingle_Submission {
// Get the gender ID defined within the profile, or return an error if none // Get the gender ID defined within the profile, or return an error if none
// matches (i.e. an unknown gender was submitted). // matches (i.e. an unknown gender was submitted).
if (!empty($params['user_gender'])) { if (is_string($params['user_gender'])) {
if (!$gender_id = $profile->getAttribute('gender_' . $params['user_gender'])) { $gender_id = $profile->getAttribute('gender_' . $params['user_gender']);
throw new CiviCRM_API3_Exception( if (!isset($gender_id)) {
throw new CRM_Core_Exception(
E::ts('Gender could not be matched to existing gender.'), E::ts('Gender could not be matched to existing gender.'),
'invalid_format' 'invalid_format'
); );
@ -108,12 +111,12 @@ class CRM_Twingle_Submission {
} }
// Validate custom fields parameter, if given. // Validate custom fields parameter, if given.
if (!empty($params['custom_fields'])) { if (isset($params['custom_fields'])) {
if (is_string($params['custom_fields'])) { if (is_string($params['custom_fields'])) {
$params['custom_fields'] = json_decode($params['custom_fields'], TRUE); $params['custom_fields'] = json_decode($params['custom_fields'], TRUE);
} }
if (!is_array($params['custom_fields'])) { if (!is_array($params['custom_fields'])) {
throw new CiviCRM_API3_Exception( throw new CRM_Core_Exception(
E::ts('Invalid format for custom fields.'), E::ts('Invalid format for custom fields.'),
'invalid_format' 'invalid_format'
); );
@ -121,13 +124,13 @@ class CRM_Twingle_Submission {
} }
// Validate campaign_id, if given. // Validate campaign_id, if given.
if (!empty($params['campaign_id'])) { if (isset($params['campaign_id'])) {
// Check whether campaign_id is a numeric string and cast it to an integer. // Check whether campaign_id is a numeric string and cast it to an integer.
if (is_numeric($params['campaign_id'])) { if (is_numeric($params['campaign_id'])) {
$params['campaign_id'] = intval($params['campaign_id']); $params['campaign_id'] = intval($params['campaign_id']);
} }
else { else {
throw new CiviCRM_API3_Exception( throw new CRM_Core_Exception(
E::ts('campaign_id must be a numeric string. '), E::ts('campaign_id must be a numeric string. '),
'invalid_format' 'invalid_format'
); );
@ -140,7 +143,7 @@ class CRM_Twingle_Submission {
['id' => $params['campaign_id']] ['id' => $params['campaign_id']]
); );
} }
catch (CiviCRM_API3_Exception $e) { catch (CRM_Core_Exception $e) {
unset($params['campaign_id']); unset($params['campaign_id']);
} }
} }
@ -152,28 +155,33 @@ class CRM_Twingle_Submission {
* *
* @param string $contact_type * @param string $contact_type
* The contact type to look for/to create. * The contact type to look for/to create.
* @param array $contact_data * @param array<string, mixed> $contact_data
* Data to use for contact lookup/to create a contact with. * Data to use for contact lookup/to create a contact with.
* @param CRM_Twingle_Profile $profile * @param CRM_Twingle_Profile $profile
* Profile used for this process * Profile used for this process
* @param array $submission * @param array<string, mixed> $submission
* Submission data * Submission data
* *
* @return int|NULL * @return int|NULL
* The ID of the matching/created contact, or NULL if no matching contact * The ID of the matching/created contact, or NULL if no matching contact
* was found and no new contact could be created. * was found and no new contact could be created.
* @throws \CiviCRM_API3_Exception * @throws \CRM_Core_Exception
* When invalid data was given. * When invalid data was given.
*/ */
public static function getContact($contact_type, $contact_data, $profile, $submission = []) { public static function getContact(
string $contact_type,
array $contact_data,
CRM_Twingle_Profile $profile,
array $submission = []
) {
// If no parameters are given, do nothing. // If no parameters are given, do nothing.
if (empty($contact_data)) { if ([] === $contact_data) {
return NULL; return NULL;
} }
// add xcm profile // add xcm profile
$xcm_profile = $profile->getAttribute('xcm_profile'); $xcm_profile = $profile->getAttribute('xcm_profile');
if (!empty($xcm_profile)) { if (isset($xcm_profile) && '' !== $xcm_profile) {
$contact_data['xcm_profile'] = $xcm_profile; $contact_data['xcm_profile'] = $xcm_profile;
} }
@ -181,7 +189,7 @@ class CRM_Twingle_Submission {
CRM_Twingle_Submission::setCampaign($contact_data, 'contact', $submission, $profile); CRM_Twingle_Submission::setCampaign($contact_data, 'contact', $submission, $profile);
// Prepare values: country. // Prepare values: country.
if (!empty($contact_data['country'])) { if (isset($contact_data['country'])) {
if (is_numeric($contact_data['country'])) { if (is_numeric($contact_data['country'])) {
// If a country ID is given, update the parameters. // If a country ID is given, update the parameters.
$contact_data['country_id'] = $contact_data['country']; $contact_data['country_id'] = $contact_data['country'];
@ -190,12 +198,12 @@ class CRM_Twingle_Submission {
else { else {
// Look up the country depending on the given ISO code. // Look up the country depending on the given ISO code.
$country = civicrm_api3('Country', 'get', ['iso_code' => $contact_data['country']]); $country = civicrm_api3('Country', 'get', ['iso_code' => $contact_data['country']]);
if (!empty($country['id'])) { if (isset($country['id'])) {
$contact_data['country_id'] = $country['id']; $contact_data['country_id'] = $country['id'];
unset($contact_data['country']); unset($contact_data['country']);
} }
else { else {
throw new \CiviCRM_API3_Exception( throw new \CRM_Core_Exception(
E::ts('Unknown country %1.', [1 => $contact_data['country']]), E::ts('Unknown country %1.', [1 => $contact_data['country']]),
'invalid_format' 'invalid_format'
); );
@ -204,7 +212,7 @@ class CRM_Twingle_Submission {
} }
// Prepare values: language. // Prepare values: language.
if (!empty($contact_data['preferred_language'])) { if (is_string($contact_data['preferred_language']) && '' !== $contact_data['preferred_language']) {
$mapping = CRM_Core_I18n_PseudoConstant::longForShortMapping(); $mapping = CRM_Core_I18n_PseudoConstant::longForShortMapping();
// Override the default mapping for German. // Override the default mapping for German.
$mapping['de'] = 'de_DE'; $mapping['de'] = 'de_DE';
@ -214,39 +222,31 @@ class CRM_Twingle_Submission {
// Pass to XCM. // Pass to XCM.
$contact_data['contact_type'] = $contact_type; $contact_data['contact_type'] = $contact_type;
$contact = civicrm_api3('Contact', 'getorcreate', $contact_data); $contact = civicrm_api3('Contact', 'getorcreate', $contact_data);
if (empty($contact['id'])) {
return NULL;
}
return $contact['id']; return isset($contact['id']) ? (int) $contact['id'] : NULL;
} }
/** /**
* Shares an organisation's work address, unless the contact already has one. * Shares an organisation's work address, unless the contact already has one.
* *
* @param $contact_id * @param int $contact_id
* The ID of the contact to share the organisation address with. * The ID of the contact to share the organisation address with.
* @param $organisation_id * @param int $organisation_id
* The ID of the organisation whose address to share with the contact. * The ID of the organisation whose address to share with the contact.
* @param $location_type_id * @param int $location_type_id
* The ID of the location type to use for address lookup. * The ID of the location type to use for address lookup.
* *
* @return boolean * @return boolean
* Whether the organisation address has been shared with the contact. * Whether the organisation address has been shared with the contact.
* *
* @throws \CiviCRM_API3_Exception * @throws \CRM_Core_Exception
* When looking up or creating the shared address failed. * When looking up or creating the shared address failed.
*/ */
public static function shareWorkAddress( public static function shareWorkAddress(
$contact_id, int $contact_id,
$organisation_id, int $organisation_id,
$location_type_id = self::LOCATION_TYPE_ID_WORK int $location_type_id = self::LOCATION_TYPE_ID_WORK
) { ) {
if (empty($organisation_id)) {
// Only if organisation exists.
return FALSE;
}
// Check whether organisation has a WORK address. // Check whether organisation has a WORK address.
$existing_org_addresses = civicrm_api3('Address', 'get', [ $existing_org_addresses = civicrm_api3('Address', 'get', [
'contact_id' => $organisation_id, 'contact_id' => $organisation_id,
@ -285,13 +285,9 @@ class CRM_Twingle_Submission {
* @param int $organisation_id * @param int $organisation_id
* The ID of the employer contact. * The ID of the employer contact.
* *
* @throws \CiviCRM_API3_Exception * @throws \CRM_Core_Exception
*/ */
public static function updateEmployerRelation($contact_id, $organisation_id) { public static function updateEmployerRelation(int $contact_id, int $organisation_id): void {
if (empty($contact_id) || empty($organisation_id)) {
return;
}
// see if there is already one // see if there is already one
$existing_relationship = civicrm_api3('Relationship', 'get', [ $existing_relationship = civicrm_api3('Relationship', 'get', [
'relationship_type_id' => self::EMPLOYER_RELATIONSHIP_TYPE_ID, 'relationship_type_id' => self::EMPLOYER_RELATIONSHIP_TYPE_ID,
@ -318,15 +314,15 @@ class CRM_Twingle_Submission {
* functionality is activated within the Twingle extension settings. * functionality is activated within the Twingle extension settings.
* *
* @return bool * @return bool
* @throws \CiviCRM_API3_Exception * @throws \CRM_Core_Exception
*/ */
public static function civiSepaEnabled() { public static function civiSepaEnabled() {
$sepa_extension = civicrm_api3('Extension', 'get', [ $sepa_extension = civicrm_api3('Extension', 'get', [
'full_name' => 'org.project60.sepa', 'full_name' => 'org.project60.sepa',
'is_active' => 1, 'is_active' => 1,
]); ]);
return Civi::settings()->get('twingle_use_sepa') return (bool) Civi::settings()->get('twingle_use_sepa')
&& $sepa_extension['count']; && $sepa_extension['count'] >= 0;
} }
/** /**
@ -337,7 +333,7 @@ class CRM_Twingle_Submission {
* The submitted "donation_rhythm" paramter according to the API action * The submitted "donation_rhythm" paramter according to the API action
* specification. * specification.
* *
* @return array * @return array{'frequency_unit'?: string, 'frequency_interval'?: int}
* An array with "frequency_unit" and "frequency_interval" keys, to be added * An array with "frequency_unit" and "frequency_interval" keys, to be added
* to contribution parameter arrays. * to contribution parameter arrays.
*/ */
@ -378,19 +374,21 @@ class CRM_Twingle_Submission {
* @return int * @return int
* The next possible day of this or the next month to start collecting. * The next possible day of this or the next month to start collecting.
*/ */
public static function getSEPACycleDay($start_date, $creditor_id) { public static function getSEPACycleDay($start_date, $creditor_id): int {
$buffer_days = (int) CRM_Sepa_Logic_Settings::getSetting('pp_buffer_days'); $buffer_days = (int) CRM_Sepa_Logic_Settings::getSetting('pp_buffer_days');
$frst_notice_days = (int) CRM_Sepa_Logic_Settings::getSetting('batching.FRST.notice', $creditor_id); $frst_notice_days = (int) CRM_Sepa_Logic_Settings::getSetting('batching.FRST.notice', $creditor_id);
$earliest_rcur_date = strtotime("$start_date + $frst_notice_days days + $buffer_days days"); if (FALSE === ($earliest_rcur_date = strtotime("$start_date + $frst_notice_days days + $buffer_days days"))) {
throw new BaseException(E::ts('Could not calculate SEPA cycle day from configuration.'));
}
// Find the next cycle day // Find the next cycle day
$cycle_days = CRM_Sepa_Logic_Settings::getListSetting('cycledays', range(1, 28), $creditor_id); $cycle_days = CRM_Sepa_Logic_Settings::getListSetting('cycledays', range(1, 28), $creditor_id);
$earliest_cycle_day = $earliest_rcur_date; $earliest_cycle_day = $earliest_rcur_date;
while (!in_array(date('j', $earliest_cycle_day), $cycle_days)) { while (!in_array(date('j', $earliest_cycle_day), $cycle_days, TRUE)) {
$earliest_cycle_day = strtotime('+ 1 day', $earliest_cycle_day); $earliest_cycle_day = strtotime('+ 1 day', $earliest_cycle_day);
} }
return date('j', $earliest_cycle_day); return (int) date('j', $earliest_cycle_day);
} }
/** /**
@ -399,16 +397,21 @@ class CRM_Twingle_Submission {
* from the submission data. Should that be empty, the profile's default * from the submission data. Should that be empty, the profile's default
* campaign is used. * campaign is used.
* *
* @param array $entity_data * @param array<string, mixed> $entity_data
* the data set where the campaign_id should be set * the data set where the campaign_id should be set
* @param string $context * @param string $context
* defines the type of the entity_data: one of 'contribution', 'membership','mandate', 'recurring', 'contact' * defines the type of the entity_data: one of 'contribution', 'membership','mandate', 'recurring', 'contact'
* @param array $submission * @param array<string, mixed> $submission
* the submitted data * the submitted data
* @param CRM_Twingle_Profile $profile * @param CRM_Twingle_Profile $profile
* the twingle profile used * the twingle profile used
*/ */
public static function setCampaign(&$entity_data, $context, $submission, $profile) { public static function setCampaign(
array &$entity_data,
string $context,
array $submission,
CRM_Twingle_Profile $profile
): void {
// first: make sure it's not set from other workflows // first: make sure it's not set from other workflows
unset($entity_data['campaign_id']); unset($entity_data['campaign_id']);
@ -418,13 +421,13 @@ class CRM_Twingle_Submission {
// backward compatibility: // backward compatibility:
$enabled_contexts = ['contribution', 'contact']; $enabled_contexts = ['contribution', 'contact'];
} }
if (in_array($context, $enabled_contexts)) { if (in_array($context, $enabled_contexts, TRUE)) {
// use the submitted campaign if set // use the submitted campaign if set
if (!empty($submission['campaign_id'])) { if (is_numeric($submission['campaign_id'])) {
$entity_data['campaign_id'] = $submission['campaign_id']; $entity_data['campaign_id'] = $submission['campaign_id'];
} }
// otherwise use the profile's // otherwise use the profile's
elseif (!empty($campaign = $profile->getAttribute('campaign'))) { elseif (is_numeric($campaign = $profile->getAttribute('campaign'))) {
$entity_data['campaign_id'] = $campaign; $entity_data['campaign_id'] = $campaign;
} }
} }

View file

@ -16,6 +16,7 @@
declare(strict_types = 1); declare(strict_types = 1);
use CRM_Twingle_ExtensionUtil as E; use CRM_Twingle_ExtensionUtil as E;
use Civi\Twingle\Exceptions\BaseException;
class CRM_Twingle_Tools { class CRM_Twingle_Tools {
@ -29,11 +30,11 @@ class CRM_Twingle_Tools {
* Check if the attempted modification of the recurring contribution is allowed. * Check if the attempted modification of the recurring contribution is allowed.
* If not, an exception will be raised * If not, an exception will be raised
* *
* @param $recurring_contribution_id int * @param int $recurring_contribution_id
* @param $change array * @param array<mixed> $change
* @throws Exception if the change is not allowed * @throws Exception if the change is not allowed
*/ */
public static function checkRecurringContributionChange($recurring_contribution_id, $change) { public static function checkRecurringContributionChange(int $recurring_contribution_id, array $change): void {
// check if a change to the status is planned // check if a change to the status is planned
if (empty($change['contribution_status_id'])) { if (empty($change['contribution_status_id'])) {
return; return;
@ -77,14 +78,17 @@ class CRM_Twingle_Tools {
} }
// this _IS_ on of the cases where we should step in: // this _IS_ on of the cases where we should step in:
CRM_Twingle_Tools::processRecurringContributionTermination($recurring_contribution_id, $recurring_contribution); CRM_Twingle_Tools::processRecurringContributionTermination(
$recurring_contribution_id,
$recurring_contribution
);
} }
/** /**
* @param $recurring_contribution_id int recurring contribution ID to check * @param $recurring_contribution_id int recurring contribution ID to check
* @param $recurring_contribution array recurring contribution data, optional * @param $recurring_contribution array recurring contribution data, optional
* @return bool|null true, false or null if can't be determined * @return bool|null true, false or null if can't be determined
* @throws CiviCRM_API3_Exception * @throws \CRM_Core_Exception
*/ */
public static function isTwingleRecurringContribution($recurring_contribution_id, $recurring_contribution = NULL) { public static function isTwingleRecurringContribution($recurring_contribution_id, $recurring_contribution = NULL) {
// this currently only works with prefixes // this currently only works with prefixes
@ -106,11 +110,13 @@ class CRM_Twingle_Tools {
/** /**
* Execute the recurring contribution protection * Execute the recurring contribution protection
* *
* @param $recurring_contribution_id int recurring contribution ID * @param int $recurring_contribution_id
* @param $recurring_contribution array recurring contribution fields * Recurring contribution ID.
* @param array<mixed> $recurring_contribution
* Recurring contribution fields.
* @throws Exception could be one of the measures * @throws Exception could be one of the measures
*/ */
public static function processRecurringContributionTermination($recurring_contribution_id, $recurring_contribution) { public static function processRecurringContributionTermination(int $recurring_contribution_id, array $recurring_contribution) {
// check if we're suspended // check if we're suspended
if (self::$protection_suspended) { if (self::$protection_suspended) {
return; return;
@ -124,7 +130,7 @@ class CRM_Twingle_Tools {
case CRM_Twingle_Config::RCUR_PROTECTION_EXCEPTION: case CRM_Twingle_Config::RCUR_PROTECTION_EXCEPTION:
// phpcs:disable Generic.Files.LineLength.TooLong // phpcs:disable Generic.Files.LineLength.TooLong
throw new Exception(E::ts( throw new BaseException(E::ts(
'This is a Twingle recurring contribution. It should be terminated through the Twingle interface, otherwise it will still be collected.' 'This is a Twingle recurring contribution. It should be terminated through the Twingle interface, otherwise it will still be collected.'
)); ));
@ -183,10 +189,10 @@ class CRM_Twingle_Tools {
/** /**
* Check if the given payment instrument is SEPA * Check if the given payment instrument is SEPA
* *
* @param $payment_instrument_id string payment instrument * @param string $payment_instrument_id
* @return boolean * @return boolean
*/ */
public static function isSDD($payment_instrument_id) { public static function isSDD(string $payment_instrument_id) {
static $sepa_payment_instruments = NULL; static $sepa_payment_instruments = NULL;
if ($sepa_payment_instruments === NULL) { if ($sepa_payment_instruments === NULL) {
// init with instrument names // init with instrument names
@ -208,11 +214,10 @@ class CRM_Twingle_Tools {
/** /**
* Get a CiviSEPA mandate for the given contribution ID * Get a CiviSEPA mandate for the given contribution ID
* *
* @param $contribution_id integer contribution ID *or* recurring contribution ID * @param int $contribution_id contribution ID *or* recurring contribution ID
* @return integer mandate ID or null * @return array<string, mixed>|null mandate or null
*/ */
public static function getMandateFor($contribution_id) { public static function getMandateFor(int $contribution_id): ?array {
$contribution_id = (int) $contribution_id;
if ($contribution_id) { if ($contribution_id) {
try { try {
// try recurring mandate // try recurring mandate

View file

@ -25,7 +25,7 @@ class CRM_Twingle_Upgrader extends CRM_Extension_Upgrader_Base {
/** /**
* Installer script * Installer script
*/ */
public function install() { public function install(): void {
// create a DB table for the twingle profiles // create a DB table for the twingle profiles
$this->executeSqlFile('sql/civicrm_twingle_profile.sql'); $this->executeSqlFile('sql/civicrm_twingle_profile.sql');
@ -43,7 +43,7 @@ class CRM_Twingle_Upgrader extends CRM_Extension_Upgrader_Base {
* /** * /**
* Copy financial_type_id setting to new setting financial_type_id_recur. * Copy financial_type_id setting to new setting financial_type_id_recur.
*/ */
public function upgrade_4000() { public function upgrade_4000(): bool {
$this->ctx->log->info('Applying update 4000: Copying Financial type to new setting Financial type (recurring).'); $this->ctx->log->info('Applying update 4000: Copying Financial type to new setting Financial type (recurring).');
foreach (CRM_Twingle_Profile::getProfiles() as $profile) { foreach (CRM_Twingle_Profile::getProfiles() as $profile) {
$profile->setAttribute('financial_type_id_recur', $profile->getAttribute('financial_type_id')); $profile->setAttribute('financial_type_id_recur', $profile->getAttribute('financial_type_id'));
@ -57,7 +57,7 @@ class CRM_Twingle_Upgrader extends CRM_Extension_Upgrader_Base {
* *
* @link https://civicrm.org/advisory/civi-sa-2019-21-poi-saved-search-and-report-instance-apis * @link https://civicrm.org/advisory/civi-sa-2019-21-poi-saved-search-and-report-instance-apis
*/ */
public function upgrade_5011() { public function upgrade_5011(): bool {
// Do not use CRM_Core_BAO::getItem() or Civi::settings()->get(). // Do not use CRM_Core_BAO::getItem() or Civi::settings()->get().
// Extract and unserialize directly from the database. // Extract and unserialize directly from the database.
$twingle_profiles_query = CRM_Core_DAO::executeQuery(" $twingle_profiles_query = CRM_Core_DAO::executeQuery("
@ -78,14 +78,14 @@ class CRM_Twingle_Upgrader extends CRM_Extension_Upgrader_Base {
* @return TRUE on success * @return TRUE on success
* @throws Exception * @throws Exception
*/ */
public function upgrade_5140() { public function upgrade_5140(): bool {
$this->ctx->log->info('Converting twingle profiles.'); $this->ctx->log->info('Converting twingle profiles.');
// create a DB table for the twingle profiles // create a DB table for the twingle profiles
$this->executeSqlFile('sql/civicrm_twingle_profile.sql'); $this->executeSqlFile('sql/civicrm_twingle_profile.sql');
// migrate the current profiles // migrate the current profiles
if ($profiles_data = Civi::settings()->get('twingle_profiles')) { if (is_array($profiles_data = Civi::settings()->get('twingle_profiles'))) {
foreach ($profiles_data as $profile_name => $profile_data) { foreach ($profiles_data as $profile_name => $profile_data) {
$profile = new CRM_Twingle_Profile($profile_name, $profile_data); $profile = new CRM_Twingle_Profile($profile_name, $profile_data);
$data = json_encode($profile->getData()); $data = json_encode($profile->getData());

View file

@ -35,14 +35,16 @@ class BaseException extends \Exception {
/** /**
* BaseException Constructor * BaseException Constructor
* @param string $message * @param string|null $message
* Error message * Error message
* @param string $error_code * @param string|null $error_code
* A meaningful error code * A meaningful error code
* @param \Throwable|null $previous
* A previously thrown exception to include.
*/ */
public function __construct(string $message = '', string $error_code = '') { public function __construct(?string $message = '', ?string $error_code = '', ?\Throwable $previous = NULL) {
parent::__construct($message, 1); parent::__construct($message, 1, $previous);
$this->log_message = !empty($message) ? E::LONG_NAME . ': ' . $message : ''; $this->log_message = '' !== $message ? E::LONG_NAME . ': ' . $message : '';
$this->code = $error_code; $this->code = $error_code;
} }
@ -59,7 +61,7 @@ class BaseException extends \Exception {
* @return string * @return string
*/ */
public function getErrorCode() { public function getErrorCode() {
return $this->code; return (string) $this->code;
} }
} }

View file

@ -21,7 +21,7 @@ use CRM_Twingle_ExtensionUtil as E;
* TwingleDonation.Cancel API specification (optional) * TwingleDonation.Cancel API specification (optional)
* This is used for documentation and validation. * This is used for documentation and validation.
* *
* @param array $params description of fields supported by this API call * @param array<string, array<string, mixed>> $params description of fields supported by this API call
* *
* @return void * @return void
* *
@ -61,8 +61,8 @@ function _civicrm_api3_twingle_donation_Cancel_spec(&$params) {
/** /**
* TwingleDonation.Cancel API * TwingleDonation.Cancel API
* *
* @param array $params * @param array<string, mixed> $params
* @return array API result descriptor * @return array<string, mixed> API result descriptor
* @see civicrm_api3_create_success * @see civicrm_api3_create_success
* @see civicrm_api3_create_error * @see civicrm_api3_create_error
*/ */
@ -75,7 +75,7 @@ function civicrm_api3_twingle_donation_Cancel($params) {
try { try {
// Validate date for parameter "cancelled_at". // Validate date for parameter "cancelled_at".
if (!DateTime::createFromFormat('YmdHis', $params['cancelled_at'])) { if (!DateTime::createFromFormat('YmdHis', $params['cancelled_at'])) {
throw new CiviCRM_API3_Exception( throw new CRM_Core_Exception(
E::ts('Invalid date for parameter "cancelled_at".'), E::ts('Invalid date for parameter "cancelled_at".'),
'invalid_format' 'invalid_format'
); );
@ -89,7 +89,7 @@ function civicrm_api3_twingle_donation_Cancel($params) {
]); ]);
$contribution_type = 'Contribution'; $contribution_type = 'Contribution';
} }
catch (CiviCRM_API3_Exception $exception) { catch (CRM_Core_Exception $exception) {
$contribution = civicrm_api3('ContributionRecur', 'getsingle', [ $contribution = civicrm_api3('ContributionRecur', 'getsingle', [
'trxn_id' => $default_profile->getTransactionID($params['trx_id']), 'trxn_id' => $default_profile->getTransactionID($params['trx_id']),
]); ]);
@ -101,9 +101,9 @@ function civicrm_api3_twingle_donation_Cancel($params) {
&& CRM_Twingle_Tools::isSDD($contribution['payment_instrument_id']) && CRM_Twingle_Tools::isSDD($contribution['payment_instrument_id'])
) { ) {
// End SEPA mandate if applicable. // End SEPA mandate if applicable.
$mandate = CRM_Twingle_Tools::getMandateFor($contribution['id']); $mandate = CRM_Twingle_Tools::getMandateFor((int) $contribution['id']);
if (!$mandate) { if (!$mandate) {
throw new CiviCRM_API3_Exception( throw new CRM_Core_Exception(
E::ts('SEPA Mandate for contribution [%1 not found.', [1 => $contribution['id']]), E::ts('SEPA Mandate for contribution [%1 not found.', [1 => $contribution['id']]),
'api_error' 'api_error'
); );
@ -112,7 +112,7 @@ function civicrm_api3_twingle_donation_Cancel($params) {
// Mandates can not be terminated in the past. // Mandates can not be terminated in the past.
$end_date = date_create_from_format('YmdHis', $params['cancelled_at']); $end_date = date_create_from_format('YmdHis', $params['cancelled_at']);
if ($end_date) { if (FALSE !== $end_date) {
// Mandates can not be terminated in the past: // Mandates can not be terminated in the past:
$end_date = date('Ymd', max( $end_date = date('Ymd', max(
time(), time(),
@ -128,7 +128,7 @@ function civicrm_api3_twingle_donation_Cancel($params) {
$end_date, $end_date,
$params['cancel_reason'] $params['cancel_reason']
)) { )) {
throw new CiviCRM_API3_Exception( throw new CRM_Core_Exception(
E::ts('Could not terminate SEPA mandate'), E::ts('Could not terminate SEPA mandate'),
'api_error' 'api_error'
); );

View file

@ -21,7 +21,7 @@ use CRM_Twingle_ExtensionUtil as E;
* TwingleDonation.Endrecurring API specification (optional) * TwingleDonation.Endrecurring API specification (optional)
* This is used for documentation and validation. * This is used for documentation and validation.
* *
* @param array $params description of fields supported by this API call * @param array<string, array<string, mixed>> $params description of fields supported by this API call
* *
* @return void * @return void
* *
@ -54,8 +54,8 @@ function _civicrm_api3_twingle_donation_endrecurring_spec(&$params) {
/** /**
* TwingleDonation.Endrecurring API * TwingleDonation.Endrecurring API
* *
* @param array $params * @param array<string, mixed> $params
* @return array API result descriptor * @return array<string, mixed> API result descriptor
* @see civicrm_api3_create_success * @see civicrm_api3_create_success
* @see civicrm_api3_create_error * @see civicrm_api3_create_error
*/ */
@ -67,8 +67,8 @@ function civicrm_api3_twingle_donation_endrecurring($params) {
try { try {
// Validate date for parameter "ended_at". // Validate date for parameter "ended_at".
if (!DateTime::createFromFormat('YmdHis', $params['ended_at'])) { if (FALSE === DateTime::createFromFormat('YmdHis', $params['ended_at'])) {
throw new CiviCRM_API3_Exception( throw new CRM_Core_Exception(
E::ts('Invalid date for parameter "ended_at".'), E::ts('Invalid date for parameter "ended_at".'),
'invalid_format' 'invalid_format'
); );
@ -86,9 +86,9 @@ function civicrm_api3_twingle_donation_endrecurring($params) {
&& CRM_Twingle_Tools::isSDD($contribution['payment_instrument_id']) && CRM_Twingle_Tools::isSDD($contribution['payment_instrument_id'])
) { ) {
// END SEPA MANDATE // END SEPA MANDATE
$mandate = CRM_Twingle_Tools::getMandateFor($contribution['id']); $mandate = CRM_Twingle_Tools::getMandateFor((int) $contribution['id']);
if (!$mandate) { if (!isset($mandate)) {
throw new CiviCRM_API3_Exception( throw new CRM_Core_Exception(
E::ts('SEPA Mandate for recurring contribution [%1 not found.', [1 => $contribution['id']]), E::ts('SEPA Mandate for recurring contribution [%1 not found.', [1 => $contribution['id']]),
'api_error' 'api_error'
); );
@ -96,7 +96,7 @@ function civicrm_api3_twingle_donation_endrecurring($params) {
$mandate_id = $mandate['id']; $mandate_id = $mandate['id'];
$end_date = date_create_from_format('YmdHis', $params['ended_at']); $end_date = date_create_from_format('YmdHis', $params['ended_at']);
if ($end_date) { if (FALSE !== $end_date) {
// Mandates can not be terminated in the past: // Mandates can not be terminated in the past:
$end_date = date('Ymd', max( $end_date = date('Ymd', max(
time(), time(),
@ -109,7 +109,7 @@ function civicrm_api3_twingle_donation_endrecurring($params) {
// verify that the mandate has not been terminated in the past // verify that the mandate has not been terminated in the past
if ($mandate['status'] != 'FRST' && $mandate['status'] != 'RCUR') { if ($mandate['status'] != 'FRST' && $mandate['status'] != 'RCUR') {
throw new CiviCRM_API3_Exception( throw new CRM_Core_Exception(
E::ts('SEPA Mandate [%1] already terminated.', [1 => $mandate_id]), E::ts('SEPA Mandate [%1] already terminated.', [1 => $mandate_id]),
'api_error' 'api_error'
); );
@ -120,7 +120,7 @@ function civicrm_api3_twingle_donation_endrecurring($params) {
$end_date, $end_date,
E::ts('Mandate closed by TwingleDonation.Endrecurring API call') E::ts('Mandate closed by TwingleDonation.Endrecurring API call')
)) { )) {
throw new CiviCRM_API3_Exception( throw new CRM_Core_Exception(
E::ts('Could not terminate SEPA mandate'), E::ts('Could not terminate SEPA mandate'),
'api_error' 'api_error'
); );

View file

@ -16,12 +16,13 @@
declare(strict_types = 1); declare(strict_types = 1);
use CRM_Twingle_ExtensionUtil as E; use CRM_Twingle_ExtensionUtil as E;
use Civi\Twingle\Exceptions\BaseException;
/** /**
* TwingleDonation.Submit API specification * TwingleDonation.Submit API specification
* This is used for documentation and validation. * This is used for documentation and validation.
* *
* @param array $params * @param array<string,array<string,mixed>> $params
* Description of fields supported by this API call. * Description of fields supported by this API call.
* *
* @return void * @return void
@ -259,8 +260,8 @@ function _civicrm_api3_twingle_donation_Submit_spec(&$params) {
/** /**
* TwingleDonation.Submit API * TwingleDonation.Submit API
* *
* @param array $params * @param array<string, mixed> $params
* @return array API result descriptor * @return array<string, mixed> API result descriptor
* @see civicrm_api3_create_success * @see civicrm_api3_create_success
* @see civicrm_api3_create_error * @see civicrm_api3_create_error
*/ */
@ -294,7 +295,7 @@ function civicrm_api3_twingle_donation_Submit($params) {
'trxn_id' => $profile->getTransactionID($params['trx_id']), 'trxn_id' => $profile->getTransactionID($params['trx_id']),
]); ]);
if ($existing_contribution['count'] > 0 || $existing_contribution_recur['count'] > 0) { if ($existing_contribution['count'] > 0 || $existing_contribution_recur['count'] > 0) {
throw new CiviCRM_API3_Exception( throw new CRM_Core_Exception(
E::ts('Contribution with the given transaction ID already exists.'), E::ts('Contribution with the given transaction ID already exists.'),
'api_error' 'api_error'
); );
@ -303,7 +304,7 @@ function civicrm_api3_twingle_donation_Submit($params) {
// Extract custom field values using the profile's mapping of Twingle fields // Extract custom field values using the profile's mapping of Twingle fields
// to CiviCRM custom fields. // to CiviCRM custom fields.
$custom_fields = []; $custom_fields = [];
if (!empty($params['custom_fields'])) { if (is_array($params['custom_fields'])) {
$custom_field_mapping = $profile->getCustomFieldMapping(); $custom_field_mapping = $profile->getCustomFieldMapping();
// Make all params available for custom field mapping // Make all params available for custom field mapping
@ -343,13 +344,11 @@ function civicrm_api3_twingle_donation_Submit($params) {
'user_city' => 'city', 'user_city' => 'city',
'user_country' => 'country', 'user_country' => 'country',
] as $address_param => $address_component) { ] as $address_param => $address_component) {
if (!empty($params[$address_param])) { if (isset($params[$address_param]) && '' !== $params[$address_param]) {
$params[$address_component] = $params[$address_param]; $params[$address_component] = $params[$address_param];
if ($address_param != $address_component) {
unset($params[$address_param]); unset($params[$address_param]);
} }
} }
}
// Remove address data when any address component that is configured as // Remove address data when any address component that is configured as
// required is missing. // required is missing.
@ -369,13 +368,13 @@ function civicrm_api3_twingle_donation_Submit($params) {
} }
// Prepare parameter mapping for organisation. // Prepare parameter mapping for organisation.
if (!empty($params['user_company'])) { if (is_string($params['user_company']) && '' !== $params['user_company']) {
$params['organization_name'] = $params['user_company']; $params['organization_name'] = $params['user_company'];
unset($params['user_company']); unset($params['user_company']);
} }
// Remove parameter "id". // Remove parameter "id".
if (!empty($params['id'])) { if (isset($params['id'])) {
unset($params['id']); unset($params['id']);
} }
@ -419,67 +418,67 @@ function civicrm_api3_twingle_donation_Submit($params) {
} }
// Get the prefix ID defined within the profile // Get the prefix ID defined within the profile
if (!empty($params['user_gender'])) { if (
$prefix_id = (int) $profile->getAttribute('prefix_' . $params['user_gender']); isset($params['user_gender'])
if ($prefix_id) { && NULL !== ($prefix_id = $profile->getAttribute('prefix_' . $params['user_gender']))
) {
$contact_data['prefix_id'] = $prefix_id; $contact_data['prefix_id'] = $prefix_id;
} }
}
// Add custom field values. // Add custom field values.
if (!empty($custom_fields['Contact'])) { if (isset($custom_fields['Contact'])) {
$contact_data += $custom_fields['Contact']; $contact_data += $custom_fields['Contact'];
} }
if (!empty($custom_fields['Individual'])) { if (isset($custom_fields['Individual'])) {
$contact_data += $custom_fields['Individual']; $contact_data += $custom_fields['Individual'];
} }
// Organisation lookup. // Organisation lookup.
if (!empty($params['organization_name'])) { if (is_string($params['organization_name']) && '' !== $params['organization_name']) {
$organisation_data = [ $organisation_data = [
'organization_name' => $params['organization_name'], 'organization_name' => $params['organization_name'],
]; ];
// Add custom field values. // Add custom field values.
if (!empty($custom_fields['Organization'])) { if (isset($custom_fields['Organization'])) {
$organisation_data += $custom_fields['Organization']; $organisation_data += $custom_fields['Organization'];
} }
if (!empty($submitted_address)) { if ([] !== $submitted_address) {
$organisation_data += $submitted_address; $organisation_data += $submitted_address;
// Use configured location type for organisation address. // Use configured location type for organisation address.
$organisation_data['location_type_id'] = (int) $profile->getAttribute('location_type_id_organisation'); $organisation_data['location_type_id'] = (int) $profile->getAttribute('location_type_id_organisation');
} }
if (!$organisation_id = CRM_Twingle_Submission::getContact( if (!is_int($organisation_id = CRM_Twingle_Submission::getContact(
'Organization', 'Organization',
$organisation_data, $organisation_data,
$profile, $profile,
$params $params
)) { ))) {
throw new CiviCRM_API3_Exception( throw new CRM_Core_Exception(
E::ts('Organisation contact could not be found or created.'), E::ts('Organisation contact could not be found or created.'),
'api_error' 'api_error'
); );
} }
} }
elseif (!empty($submitted_address)) { elseif ([] !== $submitted_address) {
$contact_data += $submitted_address; $contact_data += $submitted_address;
} }
if (!$contact_id = CRM_Twingle_Submission::getContact( if (!is_int($contact_id = CRM_Twingle_Submission::getContact(
'Individual', 'Individual',
$contact_data, $contact_data,
$profile, $profile,
$params $params
)) { ))) {
throw new CiviCRM_API3_Exception( throw new CRM_Core_Exception(
E::ts('Individual contact could not be found or created.'), E::ts('Individual contact could not be found or created.'),
'api_error' 'api_error'
); );
} }
// Save user_extrafield as contact note. // Save user_extrafield as contact note.
if (!empty($params['user_extrafield'])) { if (isset($params['user_extrafield']) && '' != $params['user_extrafield']) {
civicrm_api3('Note', 'create', [ civicrm_api3('Note', 'create', [
'entity_table' => 'civicrm_contact', 'entity_table' => 'civicrm_contact',
'entity_id' => $contact_id, 'entity_id' => $contact_id,
@ -512,13 +511,13 @@ function civicrm_api3_twingle_donation_Submit($params) {
// If usage of double opt-in is selected, use MailingEventSubscribe.create // If usage of double opt-in is selected, use MailingEventSubscribe.create
// to add contact to newsletter groups defined in the profile // to add contact to newsletter groups defined in the profile
$result_values['newsletter']['newsletter_double_opt_in'] $result_values['newsletter']['newsletter_double_opt_in']
= ($profile->getAttribute('newsletter_double_opt_in')) = (bool) $profile->getAttribute('newsletter_double_opt_in')
? 'true' ? 'true'
: 'false'; : 'false';
if ( if (
$profile->getAttribute('newsletter_double_opt_in') && (bool) $profile->getAttribute('newsletter_double_opt_in')
!empty($params['newsletter']) && && isset($params['newsletter'])
!empty($groups = $profile->getAttribute('newsletter_groups')) && is_array($groups = $profile->getAttribute('newsletter_groups'))
) { ) {
$group_memberships = array_column( $group_memberships = array_column(
civicrm_api3( civicrm_api3(
@ -539,7 +538,7 @@ function civicrm_api3_twingle_donation_Submit($params) {
'id' => (int) $group_id, 'id' => (int) $group_id,
] ]
)['visibility'] == 'Public Pages'; )['visibility'] == 'Public Pages';
if (!in_array($group_id, $group_memberships) && $is_public_group) { if (!in_array($group_id, $group_memberships, FALSE) && $is_public_group) {
$result_values['newsletter'][][$group_id] = civicrm_api3( $result_values['newsletter'][][$group_id] = civicrm_api3(
'MailingEventSubscribe', 'MailingEventSubscribe',
'create', 'create',
@ -557,8 +556,8 @@ function civicrm_api3_twingle_donation_Submit($params) {
// If requested, add contact to newsletter groups defined in the profile. // If requested, add contact to newsletter groups defined in the profile.
} }
elseif ( elseif (
!empty($params['newsletter']) isset($params['newsletter'])
&& !empty($groups = $profile->getAttribute('newsletter_groups')) && is_array($groups = $profile->getAttribute('newsletter_groups'))
) { ) {
foreach ($groups as $group_id) { foreach ($groups as $group_id) {
civicrm_api3( civicrm_api3(
@ -575,7 +574,10 @@ function civicrm_api3_twingle_donation_Submit($params) {
} }
// If requested, add contact to postinfo groups defined in the profile. // If requested, add contact to postinfo groups defined in the profile.
if (!empty($params['postinfo']) && !empty($groups = $profile->getAttribute('postinfo_groups'))) { if (
isset($params['postinfo'])
&& is_array($groups = $profile->getAttribute('postinfo_groups'))
) {
foreach ($groups as $group_id) { foreach ($groups as $group_id) {
civicrm_api3('GroupContact', 'create', [ civicrm_api3('GroupContact', 'create', [
'group_id' => $group_id, 'group_id' => $group_id,
@ -588,7 +590,10 @@ function civicrm_api3_twingle_donation_Submit($params) {
// If requested, add contact to donation_receipt groups defined in the // If requested, add contact to donation_receipt groups defined in the
// profile. // profile.
if (!empty($params['donation_receipt']) && !empty($groups = $profile->getAttribute('donation_receipt_groups'))) { if (
isset($params['donation_receipt'])
&& is_array($groups = $profile->getAttribute('donation_receipt_groups'))
) {
foreach ($groups as $group_id) { foreach ($groups as $group_id) {
civicrm_api3('GroupContact', 'create', [ civicrm_api3('GroupContact', 'create', [
'group_id' => $group_id, 'group_id' => $group_id,
@ -611,18 +616,18 @@ function civicrm_api3_twingle_donation_Submit($params) {
]; ];
// Add custom field values. // Add custom field values.
if (!empty($custom_fields['Contribution'])) { if (isset($custom_fields['Contribution'])) {
$contribution_data += $custom_fields['Contribution']; $contribution_data += $custom_fields['Contribution'];
} }
if (!empty($params['purpose'])) { if (isset($params['purpose'])) {
$contribution_data['note'] = $params['purpose']; $contribution_data['note'] = $params['purpose'];
} }
// set campaign, subject to configuration // set campaign, subject to configuration
CRM_Twingle_Submission::setCampaign($contribution_data, 'contribution', $params, $profile); CRM_Twingle_Submission::setCampaign($contribution_data, 'contribution', $params, $profile);
if (!empty($contribution_source = $profile->getAttribute('contribution_source'))) { if (NULL !== ($contribution_source = $profile->getAttribute('contribution_source'))) {
$contribution_data['source'] = $contribution_source; $contribution_data['source'] = $contribution_source;
} }
@ -637,8 +642,8 @@ function civicrm_api3_twingle_donation_Submit($params) {
'debit_iban', 'debit_iban',
'debit_bic', 'debit_bic',
] as $sepa_attribute) { ] as $sepa_attribute) {
if (empty($params[$sepa_attribute])) { if (!isset($params[$sepa_attribute])) {
throw new CiviCRM_API3_Exception( throw new CRM_Core_Exception(
E::ts('Missing attribute %1 for SEPA mandate', [ E::ts('Missing attribute %1 for SEPA mandate', [
1 => $sepa_attribute, 1 => $sepa_attribute,
]), ]),
@ -648,6 +653,11 @@ function civicrm_api3_twingle_donation_Submit($params) {
} }
$creditor_id = $profile->getAttribute('sepa_creditor_id'); $creditor_id = $profile->getAttribute('sepa_creditor_id');
if (!is_int($creditor_id)) {
throw new BaseException(
E::ts('SEPA creditor is not configured for profile "%1".', [1 => $profile->getName()])
);
}
// Compose mandate data from contribution data, ... // Compose mandate data from contribution data, ...
$mandate_data = $mandate_data =
@ -669,10 +679,10 @@ function civicrm_api3_twingle_donation_Submit($params) {
// phpcs:ignore Drupal.Formatting.SpaceUnaryOperator.PlusMinus // phpcs:ignore Drupal.Formatting.SpaceUnaryOperator.PlusMinus
+ CRM_Twingle_Submission::getFrequencyMapping($params['donation_rhythm']); + CRM_Twingle_Submission::getFrequencyMapping($params['donation_rhythm']);
// Add custom field values. // Add custom field values.
if (!empty($custom_fields['ContributionRecur'])) { if (isset($custom_fields['ContributionRecur'])) {
$mandate_data += $custom_fields['ContributionRecur']; $mandate_data += $custom_fields['ContributionRecur'];
} }
if (!empty($mandate_source = $profile->getAttribute('contribution_source'))) { if (NULL !== ($mandate_source = $profile->getAttribute('contribution_source'))) {
$mandate_data['source'] = $mandate_source; $mandate_data['source'] = $mandate_source;
} }
@ -691,7 +701,7 @@ function civicrm_api3_twingle_donation_Submit($params) {
// If requested, let CiviSEPA generate the mandate reference // If requested, let CiviSEPA generate the mandate reference
$use_own_mandate_reference = Civi::settings()->get('twingle_dont_use_reference'); $use_own_mandate_reference = Civi::settings()->get('twingle_dont_use_reference');
if (!empty($use_own_mandate_reference)) { if ((bool) $use_own_mandate_reference) {
unset($mandate_data['reference']); unset($mandate_data['reference']);
} }
@ -730,7 +740,7 @@ function civicrm_api3_twingle_donation_Submit($params) {
+ CRM_Twingle_Submission::getFrequencyMapping($params['donation_rhythm']); + CRM_Twingle_Submission::getFrequencyMapping($params['donation_rhythm']);
// Add custom field values. // Add custom field values.
if (!empty($custom_fields['ContributionRecur'])) { if (isset($custom_fields['ContributionRecur'])) {
$contribution_recur_data += $custom_fields['ContributionRecur']; $contribution_recur_data += $custom_fields['ContributionRecur'];
$contribution_data += $custom_fields['ContributionRecur']; $contribution_data += $custom_fields['ContributionRecur'];
} }
@ -740,7 +750,7 @@ function civicrm_api3_twingle_donation_Submit($params) {
$contribution_recur = civicrm_api3('ContributionRecur', 'create', $contribution_recur_data); $contribution_recur = civicrm_api3('ContributionRecur', 'create', $contribution_recur_data);
if ($contribution_recur['is_error']) { if ($contribution_recur['is_error']) {
throw new CiviCRM_API3_Exception( throw new CRM_Core_Exception(
E::ts('Could not create recurring contribution.'), E::ts('Could not create recurring contribution.'),
'api_error' 'api_error'
); );
@ -766,7 +776,7 @@ function civicrm_api3_twingle_donation_Submit($params) {
]); ]);
$contribution_data['contribution_recur_id'] = $parent_contribution['id']; $contribution_data['contribution_recur_id'] = $parent_contribution['id'];
} }
catch (Exception $exception) { catch (CRM_Core_Exception $exception) {
$result_values['parent_contribution'] = E::ts( $result_values['parent_contribution'] = E::ts(
'Could not find recurring contribution with given parent transaction ID.' 'Could not find recurring contribution with given parent transaction ID.'
); );
@ -775,7 +785,7 @@ function civicrm_api3_twingle_donation_Submit($params) {
$contribution = civicrm_api3('Contribution', 'create', $contribution_data); $contribution = civicrm_api3('Contribution', 'create', $contribution_data);
if ($contribution['is_error']) { if ($contribution['is_error']) {
throw new CiviCRM_API3_Exception( throw new CRM_Core_Exception(
E::ts('Could not create contribution'), E::ts('Could not create contribution'),
'api_error' 'api_error'
); );
@ -799,12 +809,12 @@ function civicrm_api3_twingle_donation_Submit($params) {
} }
else { else {
// this is a follow-up recurring payment // this is a follow-up recurring payment
$membership_type_id = FALSE; $membership_type_id = NULL;
} }
} }
// CREATE the membership if required // CREATE the membership if required
if (!empty($membership_type_id)) { if (isset($membership_type_id)) {
$membership_data = [ $membership_data = [
'contact_id' => $contact_id, 'contact_id' => $contact_id,
'membership_type_id' => $membership_type_id, 'membership_type_id' => $membership_type_id,
@ -821,7 +831,7 @@ function civicrm_api3_twingle_donation_Submit($params) {
// call the postprocess API // call the postprocess API
$postprocess_call = $profile->getAttribute('membership_postprocess_call'); $postprocess_call = $profile->getAttribute('membership_postprocess_call');
if (!empty($postprocess_call)) { if (is_string($postprocess_call)) {
[$pp_entity, $pp_action] = explode('.', $postprocess_call, 2); [$pp_entity, $pp_action] = explode('.', $postprocess_call, 2);
try { try {
// gather the contribution IDs // gather the contribution IDs
@ -829,7 +839,7 @@ function civicrm_api3_twingle_donation_Submit($params) {
if (isset($contribution_recur['id'])) { if (isset($contribution_recur['id'])) {
$recurring_contribution_id = $contribution_recur['id']; $recurring_contribution_id = $contribution_recur['id'];
} }
elseif (!empty($result_values['sepa_mandate'])) { elseif (isset($result_values['sepa_mandate'])) {
$mandate = reset($result_values['sepa_mandate']); $mandate = reset($result_values['sepa_mandate']);
if ($mandate['entity_table'] == 'civicrm_contribution_recur') { if ($mandate['entity_table'] == 'civicrm_contribution_recur') {
$recurring_contribution_id = (int) $mandate['entity_id']; $recurring_contribution_id = (int) $mandate['entity_id'];
@ -851,14 +861,20 @@ function civicrm_api3_twingle_donation_Submit($params) {
// refresh membership data // refresh membership data
$result_values['membership'] = civicrm_api3('Membership', 'getsingle', ['id' => $membership['id']]); $result_values['membership'] = civicrm_api3('Membership', 'getsingle', ['id' => $membership['id']]);
} }
catch (CiviCRM_API3_Exception $ex) { catch (CRM_Core_Exception $exception) {
// TODO: more error handling? // TODO: more error handling?
Civi::log() Civi::log()->warning(
->warning( sprintf(
"Twingle membership postprocessing call {$pp_entity}.{$pp_action} has failed: " . $ex->getMessage() 'Twingle membership postprocessing call %s.%s has failed: %s',
$pp_entity,
$pp_action,
$exception->getMessage()
)
); );
throw new Exception( throw new BaseException(
E::ts('Twingle membership postprocessing call has failed, see log for more information') E::ts('Twingle membership postprocessing call has failed, see log for more information'),
NULL,
$exception
); );
} }
} }

View file

@ -22,6 +22,7 @@
<!-- Conflicts with PHPStan type hints --> <!-- Conflicts with PHPStan type hints -->
<exclude name="Drupal.Commenting.VariableComment.IncorrectVarType"/> <exclude name="Drupal.Commenting.VariableComment.IncorrectVarType"/>
<exclude name="Drupal.Commenting.FunctionComment.ParamTypeSpaces"/> <exclude name="Drupal.Commenting.FunctionComment.ParamTypeSpaces"/>
<exclude name="Drupal.Commenting.FunctionComment.ReturnTypeSpaces"/>
<exclude name="Drupal.Commenting.VariableComment.MissingVar"/> <exclude name="Drupal.Commenting.VariableComment.MissingVar"/>
<!-- Don't enforce phpdoc type hint because it (might) only duplicate a PHP type hint --> <!-- Don't enforce phpdoc type hint because it (might) only duplicate a PHP type hint -->

View file

@ -9,5 +9,6 @@ parameters:
- {VENDOR_DIR}/civicrm/civicrm-core/Civi/ - {VENDOR_DIR}/civicrm/civicrm-core/Civi/
- {VENDOR_DIR}/civicrm/civicrm-core/CRM/ - {VENDOR_DIR}/civicrm/civicrm-core/CRM/
- {VENDOR_DIR}/civicrm/civicrm-core/api/ - {VENDOR_DIR}/civicrm/civicrm-core/api/
- {VENDOR_DIR}/civicrm/civicrm-packages/
bootstrapFiles: bootstrapFiles:
- {VENDOR_DIR}/autoload.php - {VENDOR_DIR}/autoload.php

View file

@ -8,7 +8,7 @@ use CRM_Twingle_ExtensionUtil as E;
*/ */
function twingle_civicrm_pre($op, $objectName, $id, &$params) { function twingle_civicrm_pre($op, $objectName, $id, &$params) {
if ($objectName == 'ContributionRecur' && $op == 'edit') { if ($objectName == 'ContributionRecur' && $op == 'edit') {
CRM_Twingle_Tools::checkRecurringContributionChange($id, $params); CRM_Twingle_Tools::checkRecurringContributionChange((int) $id, $params);
} }
} }