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
* against ending or cancellation (because Twingle would keep on collecting them)
*
* @return array
* @return array<int, string>
*/
public static function getRecurringProtectionOptions() {
return [

View file

@ -16,7 +16,8 @@
declare(strict_types = 1);
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
@ -30,7 +31,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
*
* The profile object the form is acting on.
*/
protected $profile;
protected ?CRM_Twingle_Profile $profile = NULL;
/**
* @var string
@ -40,120 +41,128 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
protected $_op;
/**
* @var array
* @var array<string, string>
*
* A static cache of retrieved payment instruments found within
* self::getPaymentInstruments().
*/
protected static $_paymentInstruments = NULL;
protected static $_paymentInstruments;
/**
* @var array
* @var array<int, string>
*
* A static cache of retrieved contribution statuses found within
* 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().
*/
protected static $_groups = NULL;
protected static array $_groups;
/**
* @var array
* @var array<string, string>
*
* A static cache of retrieved newsletter groups found within
* 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().
*/
protected static $_campaigns = NULL;
protected static array $_campaigns;
/**
* @var array
* @var array<int, string>
*
* A static cache of retrieved financial types found within
* static::getFinancialTypes().
*/
protected static $_financialTypes = NULL;
protected static array $_financialTypes;
/**
* @var array
* @var array<int, string>
*
* A static cache of retrieved genders found within
* static::getGenderOptions().
*/
protected static $_genderOptions = NULL;
protected static array $_genderOptions;
/**
* @var array
* @var array<int, string>
*
* A static cache of retrieved prefixes found within
* 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
* 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
* 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
* 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
* 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.
*/
public function buildQuickForm() {
// "Create" is the default operation.
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');
public function buildQuickForm(): void {
$profile_name = (isset($this->profile) ? $this->profile->getName() : NULL);
switch ($this->_op) {
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]));
$this->addButtons([
[
@ -168,10 +177,13 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
case 'edit':
// When editing without a valid profile name, edit the default profile.
if (!$profile_name) {
if (!isset($profile_name)) {
$profile_name = 'default';
$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()]));
break;
@ -179,10 +191,14 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
// Retrieve the source profile name.
$profile_name = CRM_Utils_Request::retrieve('source_name', 'String', $this);
// When copying without a valid profile name, copy the default profile.
if (!$profile_name) {
if (!is_string($profile_name)) {
$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.
$profile_name = $profile_name . '_copy';
@ -192,7 +208,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
case 'create':
// 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'));
break;
}
@ -354,10 +370,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
// field name
'newsletter_double_opt_in',
// field label
E::ts('Use Double-Opt-In for newsletter'),
// is not required
FALSE,
[]
E::ts('Use Double-Opt-In for newsletter')
);
$this->add(
@ -461,8 +474,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
$this->add(
'text',
'membership_postprocess_call',
E::ts('API Call for Membership Postprocessing'),
FALSE
E::ts('API Call for Membership Postprocessing')
);
$this->addRule(
'membership_postprocess_call',
@ -521,9 +533,8 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
/**
* Validates the profile form.
*
* @return bool|array
* TRUE when the form was successfully validated, or an array of error
* messages, keyed by form element name.
* @return bool
* TRUE when the form was successfully validated.
*/
public function validate() {
$values = $this->exportValues();
@ -531,14 +542,14 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
// Validate new profile names.
if (
isset($values['name'])
&& ($values['name'] != $this->profile->getName() || $this->_op != 'edit')
&& !empty(CRM_Twingle_Profile::getProfile($values['name']))
&& (!isset($this->profile) || $values['name'] != $this->profile->getName() || $this->_op != 'edit')
&& NULL !== CRM_Twingle_Profile::getProfile($values['name'])
) {
$this->_errors['name'] = E::ts('A profile with this name already exists.');
}
// 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'] =
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'])) {
$custom_field_mapping = preg_split('/\r\n|\r|\n/', $values['custom_field_mapping'], -1, PREG_SPLIT_NO_EMPTY);
if (!is_array($custom_field_mapping)) {
throw new Exception(
throw new BaseException(
E::ts('Could not parse custom field mapping.')
);
}
foreach ($custom_field_mapping as $custom_field_map) {
$custom_field_map = explode('=', $custom_field_map);
if (count($custom_field_map) !== 2) {
throw new Exception(
throw new BaseException(
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,
]);
}
catch (CiviCRM_API3_Exception $exception) {
throw new Exception(
catch (CRM_Core_Exception $exception) {
throw new BaseException(
E::ts(
'Custom field custom_%1 does not exist.',
[1 => $custom_field_id]
)
),
NULL,
$exception
);
}
@ -592,18 +605,20 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
],
]);
}
catch (CiviCRM_API3_Exception $exception) {
throw new Exception(
catch (CRM_Core_Exception $exception) {
throw new BaseException(
E::ts(
'Custom field custom_%1 is not in a CustomGroup that extends one of the supported CiviCRM entities.',
[1 => $custom_field['id']]
)
);
),
NULL,
$exception
);
}
}
}
}
catch (Exception $exception) {
catch (BaseException $exception) {
$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.
*
* @return array<string, mixed>
*/
public function setDefaultValues() {
$defaults = parent::setDefaultValues();
if (in_array($this->_op, ['create', 'edit', 'copy'])) {
$defaults['name'] = $this->profile->getName();
$profile_data = $this->profile->getData();
if (in_array($this->_op, ['create', 'edit', 'copy'], TRUE)) {
$defaults['name'] = isset($this->profile) ? $this->profile->getName() : NULL;
$profile_data = isset($this->profile) ? $this->profile->getData() : [];
foreach ($profile_data as $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.
*/
public function postProcess() {
public function postProcess(): void {
$values = $this->exportValues();
try {
if (in_array($this->_op, ['create', 'edit', 'copy'])) {
if (empty($values['name'])) {
if (!isset($this->profile)) {
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';
}
$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
* form elements.
*
* @return array
*
* @throws \CiviCRM_API3_Exception
* @return array<int, string>
*/
public static function getLocationTypes() {
if (!isset(static::$_locationTypes)) {
@ -682,7 +700,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
'is_active' => 1,
]);
foreach ($query['values'] as $type) {
static::$_locationTypes[$type['id']] = $type['name'];
static::$_locationTypes[(int) $type['id']] = $type['name'];
}
}
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
*
* @return array
* @return array<string, string>
*/
public static function getXCMProfiles() {
if (!isset(static::$_xcm_profiles)) {
if (method_exists('CRM_Xcm_Configuration', 'getProfileList')) {
if (class_exists('CRM_Xcm_Configuration')) {
static::$_xcm_profiles = [
'' => 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
* form elements.
*
* @return array
*
* @throws \CiviCRM_API3_Exception
* @return array<int, string>
*/
public static function getFinancialTypes() {
if (!isset(static::$_financialTypes)) {
@ -725,7 +741,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
'return' => 'id,name',
]);
foreach ($query['values'] as $type) {
static::$_financialTypes[$type['id']] = $type['name'];
static::$_financialTypes[(int) $type['id']] = $type['name'];
}
}
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
* form elements.
*
* @return array
*
* @throws \CiviCRM_API3_Exception
* @return array<int, string>
*/
public static function getMembershipTypes() {
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
* elements.
*
* @return array
*
* @throws \CiviCRM_API3_Exception
* @return array<int, string>
*/
public static function getGenderOptions() {
if (!isset(static::$_genderOptions)) {
@ -775,7 +787,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
],
]);
foreach ($query['values'] as $gender) {
static::$_genderOptions[$gender['value']] = $gender['label'];
static::$_genderOptions[(int) $gender['value']] = $gender['label'];
}
}
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
* elements.
*
* @return array
*
* @throws \CiviCRM_API3_Exception
* @return array<int, string>
*/
public static function getPrefixOptions() {
if (!isset(static::$_prefixOptions)) {
@ -802,7 +812,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
],
]);
foreach ($query['values'] as $prefix) {
static::$_prefixOptions[$prefix['value']] = $prefix['label'];
static::$_prefixOptions[(int) $prefix['value']] = $prefix['label'];
}
}
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.
*
* @return array
*
* @throws \CiviCRM_API3_Exception
* @return array<int, string>
*/
public static function getSepaCreditors() {
if (!isset(static::$_sepaCreditors)) {
@ -823,7 +831,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
'option.limit' => 0,
]);
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
* select form elements.
*
* @return array
*
* @throws \CiviCRM_API3_Exception
* @return array<string, string>
*/
public static function getPaymentInstruments() {
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.
*
* @return array
*
* @throws \CiviCRM_API3_Exception
* @return array<int, string>
*/
public static function getContributionStatusOptions() {
if (!isset(self::$_contributionStatusOptions)) {
@ -889,7 +893,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
);
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
* for select form elements.
*
* @return array
*
* @throws \CiviCRM_API3_Exception
* @return array<string, string>
*
*/
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.
*
* @return array
*
* @throws \CiviCRM_API3_Exception
* @return array<int, string>
*/
public static function getGroups() {
if (!isset(static::$_groups)) {
@ -948,7 +948,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
'return' => 'id,name',
]);
foreach ($query['values'] as $group) {
static::$_groups[$group['id']] = $group['name'];
static::$_groups[(int) $group['id']] = $group['name'];
}
}
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
* options for select form elements.
*
* @return array
*
* @throws \CiviCRM_API3_Exception
* @return array<int, string>
*/
public static function getPostinfoGroups() {
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
* system as options for select form elements.
*
* @return array
*
* @throws \CiviCRM_API3_Exception
* @return array<int, string>
*/
public static function getDonationReceiptGroups() {
return static::getGroups();
@ -981,9 +977,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
/**
* Retrieves campaigns as options for select elements.
*
* @return array
*
* @throws \CiviCRM_API3_Exception
* @return array<int, string>
*/
public static function getCampaigns() {
if (!isset(static::$_campaigns)) {
@ -996,7 +990,7 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
],
]);
foreach ($query['values'] as $campaign) {
static::$_campaigns[$campaign['id']] = $campaign['title'];
static::$_campaigns[(int) $campaign['id']] = $campaign['title'];
}
}
return static::$_campaigns;

View file

@ -25,7 +25,8 @@ use CRM_Twingle_ExtensionUtil as E;
class CRM_Twingle_Form_Settings extends CRM_Core_Form {
/**
* @var arraylistofallsettingsoptions
* @var array<string>
* List of all settings options.
*/
public static $SETTINGS_LIST = [
'twingle_prefix',
@ -41,7 +42,7 @@ class CRM_Twingle_Form_Settings extends CRM_Core_Form {
/**
* @inheritdoc
*/
public function buildQuickForm() {
public function buildQuickForm(): void {
// Set redirect destination.
$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();
// 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) {
foreach (['twingle_protect_recurring_activity_type',
'twingle_protect_recurring_activity_subject',
'twingle_protect_recurring_activity_status',
'twingle_protect_recurring_activity_assignee',
] as $activity_field) {
$current_value = CRM_Utils_Array::value($activity_field, $this->_submitValues);
if (empty($current_value)) {
if (NULL !== ($this->_submitValues[$activity_field] ?? NULL)) {
$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
*/
public function postProcess() {
public function postProcess(): void {
$values = $this->exportValues();
// store settings
foreach (self::$SETTINGS_LIST as $setting) {
Civi::settings()->set($setting, CRM_Utils_Array::value($setting, $values));
Civi::settings()->set($setting, $values[$setting]);
}
parent::postProcess();
@ -164,11 +164,13 @@ class CRM_Twingle_Form_Settings extends CRM_Core_Form {
/**
* Get a list of option group items
* @param $group_id string group ID or name
* @return array list of ID(value) => label
* @throws CiviCRM_API3_Exception
* @param string $group_id
* Group ID or name.
* @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-')];
$query = civicrm_api3('OptionValue', 'get', [
'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 {
public function run() {
public function run(): void {
parent::run();
}

View file

@ -19,7 +19,7 @@ use CRM_Twingle_ExtensionUtil as E;
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'));
$profiles = [];
foreach (CRM_Twingle_Profile::getProfiles() as $profile_name => $profile) {

View file

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

View file

@ -16,6 +16,7 @@
declare(strict_types = 1);
use CRM_Twingle_ExtensionUtil as E;
use Civi\Twingle\Exceptions\BaseException;
class CRM_Twingle_Submission {
@ -40,18 +41,18 @@ class CRM_Twingle_Submission {
public const EMPLOYER_RELATIONSHIP_TYPE_ID = 5;
/**
* @param array &$params
* @param array<string, mixed> &$params
* A reference to the parameters array of the submission.
*
* @param \CRM_Twingle_Profile $profile
* The Twingle profile to use for validation, defaults to the default
* profile.
*
* @throws \CiviCRM_API3_Exception
* @throws \CRM_Core_Exception
* When invalid parameters have been submitted.
*/
public static function validateSubmission(&$params, $profile = NULL) {
if (!$profile) {
public static function validateSubmission(&$params, $profile = NULL): void {
if (!isset($profile)) {
$profile = CRM_Twingle_Profile::createDefaultProfile();
}
@ -62,8 +63,8 @@ class CRM_Twingle_Submission {
'quarterly',
'yearly',
'monthly',
])) {
throw new CiviCRM_API3_Exception(
], TRUE)) {
throw new CRM_Core_Exception(
E::ts('Invalid donation rhythm.'),
'invalid_format'
);
@ -71,8 +72,9 @@ class CRM_Twingle_Submission {
// Get the payment instrument defined within the profile, or return an error
// if none matches (i.e. an unknown payment method was submitted).
if (!$payment_instrument_id = $profile->getAttribute('pi_' . $params['payment_method'])) {
throw new CiviCRM_API3_Exception(
$payment_instrument_id = $profile->getAttribute('pi_' . $params['payment_method']);
if (!isset($payment_instrument_id)) {
throw new CRM_Core_Exception(
E::ts('Payment method could not be matched to existing payment instrument.'),
'invalid_format'
);
@ -80,16 +82,16 @@ class CRM_Twingle_Submission {
$params['payment_instrument_id'] = $payment_instrument_id;
// Validate date for parameter "confirmed_at".
if (!DateTime::createFromFormat('YmdHis', $params['confirmed_at'])) {
throw new CiviCRM_API3_Exception(
if (FALSE === DateTime::createFromFormat('YmdHis', $params['confirmed_at'])) {
throw new CRM_Core_Exception(
E::ts('Invalid date for parameter "confirmed_at".'),
'invalid_format'
);
}
// Validate date for parameter "user_birthdate".
if (!empty($params['user_birthdate']) && !DateTime::createFromFormat('Ymd', $params['user_birthdate'])) {
throw new CiviCRM_API3_Exception(
if (!empty($params['user_birthdate']) && FALSE === DateTime::createFromFormat('Ymd', $params['user_birthdate'])) {
throw new CRM_Core_Exception(
E::ts('Invalid date for parameter "user_birthdate".'),
'invalid_format'
);
@ -97,9 +99,10 @@ class CRM_Twingle_Submission {
// Get the gender ID defined within the profile, or return an error if none
// matches (i.e. an unknown gender was submitted).
if (!empty($params['user_gender'])) {
if (!$gender_id = $profile->getAttribute('gender_' . $params['user_gender'])) {
throw new CiviCRM_API3_Exception(
if (is_string($params['user_gender'])) {
$gender_id = $profile->getAttribute('gender_' . $params['user_gender']);
if (!isset($gender_id)) {
throw new CRM_Core_Exception(
E::ts('Gender could not be matched to existing gender.'),
'invalid_format'
);
@ -108,12 +111,12 @@ class CRM_Twingle_Submission {
}
// Validate custom fields parameter, if given.
if (!empty($params['custom_fields'])) {
if (isset($params['custom_fields'])) {
if (is_string($params['custom_fields'])) {
$params['custom_fields'] = json_decode($params['custom_fields'], TRUE);
}
if (!is_array($params['custom_fields'])) {
throw new CiviCRM_API3_Exception(
throw new CRM_Core_Exception(
E::ts('Invalid format for custom fields.'),
'invalid_format'
);
@ -121,13 +124,13 @@ class CRM_Twingle_Submission {
}
// 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.
if (is_numeric($params['campaign_id'])) {
$params['campaign_id'] = intval($params['campaign_id']);
}
else {
throw new CiviCRM_API3_Exception(
throw new CRM_Core_Exception(
E::ts('campaign_id must be a numeric string. '),
'invalid_format'
);
@ -140,7 +143,7 @@ class CRM_Twingle_Submission {
['id' => $params['campaign_id']]
);
}
catch (CiviCRM_API3_Exception $e) {
catch (CRM_Core_Exception $e) {
unset($params['campaign_id']);
}
}
@ -152,28 +155,33 @@ class CRM_Twingle_Submission {
*
* @param string $contact_type
* 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.
* @param CRM_Twingle_Profile $profile
* Profile used for this process
* @param array $submission
* @param array<string, mixed> $submission
* Submission data
*
* @return int|NULL
* The ID of the matching/created contact, or NULL if no matching contact
* was found and no new contact could be created.
* @throws \CiviCRM_API3_Exception
* @throws \CRM_Core_Exception
* 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 (empty($contact_data)) {
if ([] === $contact_data) {
return NULL;
}
// add xcm profile
$xcm_profile = $profile->getAttribute('xcm_profile');
if (!empty($xcm_profile)) {
if (isset($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);
// Prepare values: country.
if (!empty($contact_data['country'])) {
if (isset($contact_data['country'])) {
if (is_numeric($contact_data['country'])) {
// If a country ID is given, update the parameters.
$contact_data['country_id'] = $contact_data['country'];
@ -190,12 +198,12 @@ class CRM_Twingle_Submission {
else {
// Look up the country depending on the given ISO code.
$country = civicrm_api3('Country', 'get', ['iso_code' => $contact_data['country']]);
if (!empty($country['id'])) {
if (isset($country['id'])) {
$contact_data['country_id'] = $country['id'];
unset($contact_data['country']);
}
else {
throw new \CiviCRM_API3_Exception(
throw new \CRM_Core_Exception(
E::ts('Unknown country %1.', [1 => $contact_data['country']]),
'invalid_format'
);
@ -204,7 +212,7 @@ class CRM_Twingle_Submission {
}
// 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();
// Override the default mapping for German.
$mapping['de'] = 'de_DE';
@ -214,39 +222,31 @@ class CRM_Twingle_Submission {
// Pass to XCM.
$contact_data['contact_type'] = $contact_type;
$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.
*
* @param $contact_id
* @param int $contact_id
* 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.
* @param $location_type_id
* @param int $location_type_id
* The ID of the location type to use for address lookup.
*
* @return boolean
* 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.
*/
public static function shareWorkAddress(
$contact_id,
$organisation_id,
$location_type_id = self::LOCATION_TYPE_ID_WORK
int $contact_id,
int $organisation_id,
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.
$existing_org_addresses = civicrm_api3('Address', 'get', [
'contact_id' => $organisation_id,
@ -285,13 +285,9 @@ class CRM_Twingle_Submission {
* @param int $organisation_id
* The ID of the employer contact.
*
* @throws \CiviCRM_API3_Exception
* @throws \CRM_Core_Exception
*/
public static function updateEmployerRelation($contact_id, $organisation_id) {
if (empty($contact_id) || empty($organisation_id)) {
return;
}
public static function updateEmployerRelation(int $contact_id, int $organisation_id): void {
// see if there is already one
$existing_relationship = civicrm_api3('Relationship', 'get', [
'relationship_type_id' => self::EMPLOYER_RELATIONSHIP_TYPE_ID,
@ -318,15 +314,15 @@ class CRM_Twingle_Submission {
* functionality is activated within the Twingle extension settings.
*
* @return bool
* @throws \CiviCRM_API3_Exception
* @throws \CRM_Core_Exception
*/
public static function civiSepaEnabled() {
$sepa_extension = civicrm_api3('Extension', 'get', [
'full_name' => 'org.project60.sepa',
'is_active' => 1,
]);
return Civi::settings()->get('twingle_use_sepa')
&& $sepa_extension['count'];
return (bool) Civi::settings()->get('twingle_use_sepa')
&& $sepa_extension['count'] >= 0;
}
/**
@ -337,7 +333,7 @@ class CRM_Twingle_Submission {
* The submitted "donation_rhythm" paramter according to the API action
* specification.
*
* @return array
* @return array{'frequency_unit'?: string, 'frequency_interval'?: int}
* An array with "frequency_unit" and "frequency_interval" keys, to be added
* to contribution parameter arrays.
*/
@ -378,19 +374,21 @@ class CRM_Twingle_Submission {
* @return int
* 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');
$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
$cycle_days = CRM_Sepa_Logic_Settings::getListSetting('cycledays', range(1, 28), $creditor_id);
$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);
}
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
* campaign is used.
*
* @param array $entity_data
* @param array<string, mixed> $entity_data
* the data set where the campaign_id should be set
* @param string $context
* 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
* @param CRM_Twingle_Profile $profile
* 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
unset($entity_data['campaign_id']);
@ -418,13 +421,13 @@ class CRM_Twingle_Submission {
// backward compatibility:
$enabled_contexts = ['contribution', 'contact'];
}
if (in_array($context, $enabled_contexts)) {
if (in_array($context, $enabled_contexts, TRUE)) {
// use the submitted campaign if set
if (!empty($submission['campaign_id'])) {
if (is_numeric($submission['campaign_id'])) {
$entity_data['campaign_id'] = $submission['campaign_id'];
}
// otherwise use the profile's
elseif (!empty($campaign = $profile->getAttribute('campaign'))) {
elseif (is_numeric($campaign = $profile->getAttribute('campaign'))) {
$entity_data['campaign_id'] = $campaign;
}
}

View file

@ -16,6 +16,7 @@
declare(strict_types = 1);
use CRM_Twingle_ExtensionUtil as E;
use Civi\Twingle\Exceptions\BaseException;
class CRM_Twingle_Tools {
@ -29,11 +30,11 @@ class CRM_Twingle_Tools {
* Check if the attempted modification of the recurring contribution is allowed.
* If not, an exception will be raised
*
* @param $recurring_contribution_id int
* @param $change array
* @param int $recurring_contribution_id
* @param array<mixed> $change
* @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
if (empty($change['contribution_status_id'])) {
return;
@ -77,14 +78,17 @@ class CRM_Twingle_Tools {
}
// 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 array recurring contribution data, optional
* @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) {
// this currently only works with prefixes
@ -106,11 +110,13 @@ class CRM_Twingle_Tools {
/**
* Execute the recurring contribution protection
*
* @param $recurring_contribution_id int recurring contribution ID
* @param $recurring_contribution array recurring contribution fields
* @param int $recurring_contribution_id
* Recurring contribution ID.
* @param array<mixed> $recurring_contribution
* Recurring contribution fields.
* @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
if (self::$protection_suspended) {
return;
@ -124,7 +130,7 @@ class CRM_Twingle_Tools {
case CRM_Twingle_Config::RCUR_PROTECTION_EXCEPTION:
// 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.'
));
@ -183,10 +189,10 @@ class CRM_Twingle_Tools {
/**
* Check if the given payment instrument is SEPA
*
* @param $payment_instrument_id string payment instrument
* @param string $payment_instrument_id
* @return boolean
*/
public static function isSDD($payment_instrument_id) {
public static function isSDD(string $payment_instrument_id) {
static $sepa_payment_instruments = NULL;
if ($sepa_payment_instruments === NULL) {
// init with instrument names
@ -208,11 +214,10 @@ class CRM_Twingle_Tools {
/**
* Get a CiviSEPA mandate for the given contribution ID
*
* @param $contribution_id integer contribution ID *or* recurring contribution ID
* @return integer mandate ID or null
* @param int $contribution_id contribution ID *or* recurring contribution ID
* @return array<string, mixed>|null mandate or null
*/
public static function getMandateFor($contribution_id) {
$contribution_id = (int) $contribution_id;
public static function getMandateFor(int $contribution_id): ?array {
if ($contribution_id) {
try {
// try recurring mandate

View file

@ -25,7 +25,7 @@ class CRM_Twingle_Upgrader extends CRM_Extension_Upgrader_Base {
/**
* Installer script
*/
public function install() {
public function install(): void {
// create a DB table for the twingle profiles
$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.
*/
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).');
foreach (CRM_Twingle_Profile::getProfiles() as $profile) {
$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
*/
public function upgrade_5011() {
public function upgrade_5011(): bool {
// Do not use CRM_Core_BAO::getItem() or Civi::settings()->get().
// Extract and unserialize directly from the database.
$twingle_profiles_query = CRM_Core_DAO::executeQuery("
@ -78,14 +78,14 @@ class CRM_Twingle_Upgrader extends CRM_Extension_Upgrader_Base {
* @return TRUE on success
* @throws Exception
*/
public function upgrade_5140() {
public function upgrade_5140(): bool {
$this->ctx->log->info('Converting twingle profiles.');
// create a DB table for the twingle profiles
$this->executeSqlFile('sql/civicrm_twingle_profile.sql');
// 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) {
$profile = new CRM_Twingle_Profile($profile_name, $profile_data);
$data = json_encode($profile->getData());

View file

@ -35,14 +35,16 @@ class BaseException extends \Exception {
/**
* BaseException Constructor
* @param string $message
* Error message
* @param string $error_code
* A meaningful error code
* @param string|null $message
* Error message
* @param string|null $error_code
* A meaningful error code
* @param \Throwable|null $previous
* A previously thrown exception to include.
*/
public function __construct(string $message = '', string $error_code = '') {
parent::__construct($message, 1);
$this->log_message = !empty($message) ? E::LONG_NAME . ': ' . $message : '';
public function __construct(?string $message = '', ?string $error_code = '', ?\Throwable $previous = NULL) {
parent::__construct($message, 1, $previous);
$this->log_message = '' !== $message ? E::LONG_NAME . ': ' . $message : '';
$this->code = $error_code;
}
@ -59,7 +61,7 @@ class BaseException extends \Exception {
* @return string
*/
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)
* 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
*
@ -61,8 +61,8 @@ function _civicrm_api3_twingle_donation_Cancel_spec(&$params) {
/**
* TwingleDonation.Cancel API
*
* @param array $params
* @return array API result descriptor
* @param array<string, mixed> $params
* @return array<string, mixed> API result descriptor
* @see civicrm_api3_create_success
* @see civicrm_api3_create_error
*/
@ -75,7 +75,7 @@ function civicrm_api3_twingle_donation_Cancel($params) {
try {
// Validate date for parameter "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".'),
'invalid_format'
);
@ -89,7 +89,7 @@ function civicrm_api3_twingle_donation_Cancel($params) {
]);
$contribution_type = 'Contribution';
}
catch (CiviCRM_API3_Exception $exception) {
catch (CRM_Core_Exception $exception) {
$contribution = civicrm_api3('ContributionRecur', 'getsingle', [
'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'])
) {
// End SEPA mandate if applicable.
$mandate = CRM_Twingle_Tools::getMandateFor($contribution['id']);
$mandate = CRM_Twingle_Tools::getMandateFor((int) $contribution['id']);
if (!$mandate) {
throw new CiviCRM_API3_Exception(
throw new CRM_Core_Exception(
E::ts('SEPA Mandate for contribution [%1 not found.', [1 => $contribution['id']]),
'api_error'
);
@ -112,7 +112,7 @@ function civicrm_api3_twingle_donation_Cancel($params) {
// Mandates can not be terminated in the past.
$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:
$end_date = date('Ymd', max(
time(),
@ -128,7 +128,7 @@ function civicrm_api3_twingle_donation_Cancel($params) {
$end_date,
$params['cancel_reason']
)) {
throw new CiviCRM_API3_Exception(
throw new CRM_Core_Exception(
E::ts('Could not terminate SEPA mandate'),
'api_error'
);

View file

@ -21,7 +21,7 @@ use CRM_Twingle_ExtensionUtil as E;
* TwingleDonation.Endrecurring API specification (optional)
* 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
*
@ -54,8 +54,8 @@ function _civicrm_api3_twingle_donation_endrecurring_spec(&$params) {
/**
* TwingleDonation.Endrecurring API
*
* @param array $params
* @return array API result descriptor
* @param array<string, mixed> $params
* @return array<string, mixed> API result descriptor
* @see civicrm_api3_create_success
* @see civicrm_api3_create_error
*/
@ -67,8 +67,8 @@ function civicrm_api3_twingle_donation_endrecurring($params) {
try {
// Validate date for parameter "ended_at".
if (!DateTime::createFromFormat('YmdHis', $params['ended_at'])) {
throw new CiviCRM_API3_Exception(
if (FALSE === DateTime::createFromFormat('YmdHis', $params['ended_at'])) {
throw new CRM_Core_Exception(
E::ts('Invalid date for parameter "ended_at".'),
'invalid_format'
);
@ -86,9 +86,9 @@ function civicrm_api3_twingle_donation_endrecurring($params) {
&& CRM_Twingle_Tools::isSDD($contribution['payment_instrument_id'])
) {
// END SEPA MANDATE
$mandate = CRM_Twingle_Tools::getMandateFor($contribution['id']);
if (!$mandate) {
throw new CiviCRM_API3_Exception(
$mandate = CRM_Twingle_Tools::getMandateFor((int) $contribution['id']);
if (!isset($mandate)) {
throw new CRM_Core_Exception(
E::ts('SEPA Mandate for recurring contribution [%1 not found.', [1 => $contribution['id']]),
'api_error'
);
@ -96,7 +96,7 @@ function civicrm_api3_twingle_donation_endrecurring($params) {
$mandate_id = $mandate['id'];
$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:
$end_date = date('Ymd', max(
time(),
@ -109,7 +109,7 @@ function civicrm_api3_twingle_donation_endrecurring($params) {
// verify that the mandate has not been terminated in the past
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]),
'api_error'
);
@ -120,7 +120,7 @@ function civicrm_api3_twingle_donation_endrecurring($params) {
$end_date,
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'),
'api_error'
);

View file

@ -16,12 +16,13 @@
declare(strict_types = 1);
use CRM_Twingle_ExtensionUtil as E;
use Civi\Twingle\Exceptions\BaseException;
/**
* TwingleDonation.Submit API specification
* 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.
*
* @return void
@ -259,8 +260,8 @@ function _civicrm_api3_twingle_donation_Submit_spec(&$params) {
/**
* TwingleDonation.Submit API
*
* @param array $params
* @return array API result descriptor
* @param array<string, mixed> $params
* @return array<string, mixed> API result descriptor
* @see civicrm_api3_create_success
* @see civicrm_api3_create_error
*/
@ -294,7 +295,7 @@ function civicrm_api3_twingle_donation_Submit($params) {
'trxn_id' => $profile->getTransactionID($params['trx_id']),
]);
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.'),
'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
// to CiviCRM custom fields.
$custom_fields = [];
if (!empty($params['custom_fields'])) {
if (is_array($params['custom_fields'])) {
$custom_field_mapping = $profile->getCustomFieldMapping();
// Make all params available for custom field mapping
@ -343,11 +344,9 @@ function civicrm_api3_twingle_donation_Submit($params) {
'user_city' => 'city',
'user_country' => 'country',
] as $address_param => $address_component) {
if (!empty($params[$address_param])) {
if (isset($params[$address_param]) && '' !== $params[$address_param]) {
$params[$address_component] = $params[$address_param];
if ($address_param != $address_component) {
unset($params[$address_param]);
}
unset($params[$address_param]);
}
}
@ -369,13 +368,13 @@ function civicrm_api3_twingle_donation_Submit($params) {
}
// 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'];
unset($params['user_company']);
}
// Remove parameter "id".
if (!empty($params['id'])) {
if (isset($params['id'])) {
unset($params['id']);
}
@ -419,67 +418,67 @@ function civicrm_api3_twingle_donation_Submit($params) {
}
// Get the prefix ID defined within the profile
if (!empty($params['user_gender'])) {
$prefix_id = (int) $profile->getAttribute('prefix_' . $params['user_gender']);
if ($prefix_id) {
$contact_data['prefix_id'] = $prefix_id;
}
if (
isset($params['user_gender'])
&& NULL !== ($prefix_id = $profile->getAttribute('prefix_' . $params['user_gender']))
) {
$contact_data['prefix_id'] = $prefix_id;
}
// Add custom field values.
if (!empty($custom_fields['Contact'])) {
if (isset($custom_fields['Contact'])) {
$contact_data += $custom_fields['Contact'];
}
if (!empty($custom_fields['Individual'])) {
if (isset($custom_fields['Individual'])) {
$contact_data += $custom_fields['Individual'];
}
// Organisation lookup.
if (!empty($params['organization_name'])) {
if (is_string($params['organization_name']) && '' !== $params['organization_name']) {
$organisation_data = [
'organization_name' => $params['organization_name'],
];
// Add custom field values.
if (!empty($custom_fields['Organization'])) {
if (isset($custom_fields['Organization'])) {
$organisation_data += $custom_fields['Organization'];
}
if (!empty($submitted_address)) {
if ([] !== $submitted_address) {
$organisation_data += $submitted_address;
// Use configured location type for organisation address.
$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',
$organisation_data,
$profile,
$params
)) {
throw new CiviCRM_API3_Exception(
))) {
throw new CRM_Core_Exception(
E::ts('Organisation contact could not be found or created.'),
'api_error'
);
}
}
elseif (!empty($submitted_address)) {
elseif ([] !== $submitted_address) {
$contact_data += $submitted_address;
}
if (!$contact_id = CRM_Twingle_Submission::getContact(
if (!is_int($contact_id = CRM_Twingle_Submission::getContact(
'Individual',
$contact_data,
$profile,
$params
)) {
throw new CiviCRM_API3_Exception(
))) {
throw new CRM_Core_Exception(
E::ts('Individual contact could not be found or created.'),
'api_error'
);
}
// Save user_extrafield as contact note.
if (!empty($params['user_extrafield'])) {
if (isset($params['user_extrafield']) && '' != $params['user_extrafield']) {
civicrm_api3('Note', 'create', [
'entity_table' => 'civicrm_contact',
'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
// to add contact to newsletter groups defined in the profile
$result_values['newsletter']['newsletter_double_opt_in']
= ($profile->getAttribute('newsletter_double_opt_in'))
= (bool) $profile->getAttribute('newsletter_double_opt_in')
? 'true'
: 'false';
if (
$profile->getAttribute('newsletter_double_opt_in') &&
!empty($params['newsletter']) &&
!empty($groups = $profile->getAttribute('newsletter_groups'))
(bool) $profile->getAttribute('newsletter_double_opt_in')
&& isset($params['newsletter'])
&& is_array($groups = $profile->getAttribute('newsletter_groups'))
) {
$group_memberships = array_column(
civicrm_api3(
@ -539,7 +538,7 @@ function civicrm_api3_twingle_donation_Submit($params) {
'id' => (int) $group_id,
]
)['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(
'MailingEventSubscribe',
'create',
@ -557,8 +556,8 @@ function civicrm_api3_twingle_donation_Submit($params) {
// If requested, add contact to newsletter groups defined in the profile.
}
elseif (
!empty($params['newsletter'])
&& !empty($groups = $profile->getAttribute('newsletter_groups'))
isset($params['newsletter'])
&& is_array($groups = $profile->getAttribute('newsletter_groups'))
) {
foreach ($groups as $group_id) {
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 (!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) {
civicrm_api3('GroupContact', 'create', [
'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
// 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) {
civicrm_api3('GroupContact', 'create', [
'group_id' => $group_id,
@ -611,18 +616,18 @@ function civicrm_api3_twingle_donation_Submit($params) {
];
// Add custom field values.
if (!empty($custom_fields['Contribution'])) {
if (isset($custom_fields['Contribution'])) {
$contribution_data += $custom_fields['Contribution'];
}
if (!empty($params['purpose'])) {
if (isset($params['purpose'])) {
$contribution_data['note'] = $params['purpose'];
}
// set campaign, subject to configuration
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;
}
@ -637,8 +642,8 @@ function civicrm_api3_twingle_donation_Submit($params) {
'debit_iban',
'debit_bic',
] as $sepa_attribute) {
if (empty($params[$sepa_attribute])) {
throw new CiviCRM_API3_Exception(
if (!isset($params[$sepa_attribute])) {
throw new CRM_Core_Exception(
E::ts('Missing attribute %1 for SEPA mandate', [
1 => $sepa_attribute,
]),
@ -648,6 +653,11 @@ function civicrm_api3_twingle_donation_Submit($params) {
}
$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, ...
$mandate_data =
@ -669,10 +679,10 @@ function civicrm_api3_twingle_donation_Submit($params) {
// phpcs:ignore Drupal.Formatting.SpaceUnaryOperator.PlusMinus
+ CRM_Twingle_Submission::getFrequencyMapping($params['donation_rhythm']);
// Add custom field values.
if (!empty($custom_fields['ContributionRecur'])) {
if (isset($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;
}
@ -691,7 +701,7 @@ function civicrm_api3_twingle_donation_Submit($params) {
// If requested, let CiviSEPA generate the mandate 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']);
}
@ -730,7 +740,7 @@ function civicrm_api3_twingle_donation_Submit($params) {
+ CRM_Twingle_Submission::getFrequencyMapping($params['donation_rhythm']);
// Add custom field values.
if (!empty($custom_fields['ContributionRecur'])) {
if (isset($custom_fields['ContributionRecur'])) {
$contribution_recur_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);
if ($contribution_recur['is_error']) {
throw new CiviCRM_API3_Exception(
throw new CRM_Core_Exception(
E::ts('Could not create recurring contribution.'),
'api_error'
);
@ -766,7 +776,7 @@ function civicrm_api3_twingle_donation_Submit($params) {
]);
$contribution_data['contribution_recur_id'] = $parent_contribution['id'];
}
catch (Exception $exception) {
catch (CRM_Core_Exception $exception) {
$result_values['parent_contribution'] = E::ts(
'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);
if ($contribution['is_error']) {
throw new CiviCRM_API3_Exception(
throw new CRM_Core_Exception(
E::ts('Could not create contribution'),
'api_error'
);
@ -799,12 +809,12 @@ function civicrm_api3_twingle_donation_Submit($params) {
}
else {
// this is a follow-up recurring payment
$membership_type_id = FALSE;
$membership_type_id = NULL;
}
}
// CREATE the membership if required
if (!empty($membership_type_id)) {
if (isset($membership_type_id)) {
$membership_data = [
'contact_id' => $contact_id,
'membership_type_id' => $membership_type_id,
@ -821,7 +831,7 @@ function civicrm_api3_twingle_donation_Submit($params) {
// call the postprocess API
$postprocess_call = $profile->getAttribute('membership_postprocess_call');
if (!empty($postprocess_call)) {
if (is_string($postprocess_call)) {
[$pp_entity, $pp_action] = explode('.', $postprocess_call, 2);
try {
// gather the contribution IDs
@ -829,7 +839,7 @@ function civicrm_api3_twingle_donation_Submit($params) {
if (isset($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']);
if ($mandate['entity_table'] == 'civicrm_contribution_recur') {
$recurring_contribution_id = (int) $mandate['entity_id'];
@ -851,14 +861,20 @@ function civicrm_api3_twingle_donation_Submit($params) {
// refresh membership data
$result_values['membership'] = civicrm_api3('Membership', 'getsingle', ['id' => $membership['id']]);
}
catch (CiviCRM_API3_Exception $ex) {
catch (CRM_Core_Exception $exception) {
// TODO: more error handling?
Civi::log()
->warning(
"Twingle membership postprocessing call {$pp_entity}.{$pp_action} has failed: " . $ex->getMessage()
);
throw new Exception(
E::ts('Twingle membership postprocessing call has failed, see log for more information')
Civi::log()->warning(
sprintf(
'Twingle membership postprocessing call %s.%s has failed: %s',
$pp_entity,
$pp_action,
$exception->getMessage()
)
);
throw new BaseException(
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 -->
<exclude name="Drupal.Commenting.VariableComment.IncorrectVarType"/>
<exclude name="Drupal.Commenting.FunctionComment.ParamTypeSpaces"/>
<exclude name="Drupal.Commenting.FunctionComment.ReturnTypeSpaces"/>
<exclude name="Drupal.Commenting.VariableComment.MissingVar"/>
<!-- 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/CRM/
- {VENDOR_DIR}/civicrm/civicrm-core/api/
- {VENDOR_DIR}/civicrm/civicrm-packages/
bootstrapFiles:
- {VENDOR_DIR}/autoload.php

View file

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