Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve profile validation #75

Merged
merged 3 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 41 additions & 91 deletions CRM/Twingle/Form/Profile.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@
declare(strict_types = 1);

use CRM_Twingle_ExtensionUtil as E;
use Civi\Twingle\Exceptions\ProfileException;
use Civi\Twingle\Exceptions\BaseException;
use Civi\Twingle\Exceptions\ProfileException;
use Civi\Twingle\Exceptions\ProfileValidationError;

/**
* Form controller class
Expand Down Expand Up @@ -148,7 +149,7 @@
// Verify that a profile with the given id exists.
if ($this->_op != 'copy' && $this->_op != 'create') {
$this->profile_id = CRM_Utils_Request::retrieve('id', 'Int', $this);
$this->profile = CRM_Twingle_Profile::getProfile($this->profile_id);

Check failure on line 152 in CRM/Twingle/Form/Profile.php

View workflow job for this annotation

GitHub Actions / PHPStan with PHP 7.4 prefer-lowest

Parameter #1 $id of static method CRM_Twingle_Profile::getProfile() expects int|null, mixed given.
}

// Set redirect destination.
Expand All @@ -163,12 +164,12 @@
/**
* Builds the form structure.
*/
public function buildQuickForm(): void {

Check warning on line 167 in CRM/Twingle/Form/Profile.php

View workflow job for this annotation

GitHub Actions / PHP_CodeSniffer

Function's cyclomatic complexity (17) exceeds 10; consider refactoring the function
$profile_name = (isset($this->profile) ? $this->profile->getName() : NULL);

switch ($this->_op) {
case 'delete':
if ($this->profile) {
if (isset($this->profile)) {
CRM_Utils_System::setTitle(E::ts('Delete Twingle API profile <em>%1</em>', [1 => $this->profile->getName()]));
$this->addButtons([
[
Expand All @@ -185,31 +186,37 @@
// Retrieve the source profile name.
$source_id = CRM_Utils_Request::retrieve('source_id', 'Int', $this);
// When copying without a valid profile id, copy the default profile.
if (!$source_id) {
if (!is_int($source_id)) {
$this->profile = CRM_Twingle_Profile::createDefaultProfile();
} else {
}
else {
try {
$source_profile = CRM_Twingle_Profile::getProfile($source_id);
$this->profile = $source_profile->copy();

Check failure on line 195 in CRM/Twingle/Form/Profile.php

View workflow job for this annotation

GitHub Actions / PHPStan with PHP 7.4 prefer-lowest

Cannot call method copy() on CRM_Twingle_Profile|null.
$this->profile->validate();
} catch (ProfileValidationError $e) {
if ($e->getErrorCode() == ProfileValidationError::ERROR_CODE_PROFILE_VALIDATION_FAILED) {
Civi::log()->error($e->getLogMessage());
}
catch (ProfileValidationError $exception) {
if ($exception->getErrorCode() == ProfileValidationError::ERROR_CODE_PROFILE_VALIDATION_FAILED) {
Civi::log()->error($exception->getLogMessage());
CRM_Core_Session::setStatus(E::ts('The profile is invalid and cannot be copied.'), E::ts('Error'));
CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/admin/settings/twingle/profiles', 'reset=1'));
return;
}
} catch (ProfileException $e) {
if ($e->getErrorCode() == ProfileException::ERROR_CODE_PROFILE_NOT_FOUND) {
Civi::log()->error($e->getLogMessage());
}
catch (ProfileException $exception) {
if ($exception->getErrorCode() == ProfileException::ERROR_CODE_PROFILE_NOT_FOUND) {
Civi::log()->error($exception->getLogMessage());
CRM_Core_Session::setStatus(E::ts('The profile to be copied could not be found.'), E::ts('Error'));
CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/admin/settings/twingle/profiles', 'reset=1'));
return;
}
}
catch (Civi\Core\Exception\DBQueryException $e) {

Check failure on line 214 in CRM/Twingle/Form/Profile.php

View workflow job for this annotation

GitHub Actions / PHPStan with PHP 7.4 prefer-lowest

Caught class Civi\Core\Exception\DBQueryException not found.
Civi::log()->error($e->getMessage());

Check failure on line 215 in CRM/Twingle/Form/Profile.php

View workflow job for this annotation

GitHub Actions / PHPStan with PHP 7.4 prefer-lowest

Call to method getMessage() on an unknown class Civi\Core\Exception\DBQueryException.
CRM_Core_Session::setStatus(E::ts('A database error has occurred. See the log for details.'), E::ts('Error'));
CRM_Core_Session::setStatus(
E::ts('A database error has occurred. See the log for details.'),
E::ts('Error')
);
CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/admin/settings/twingle/profiles', 'reset=1'));
return;
}
Expand All @@ -218,7 +225,9 @@
break;

case 'edit':
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()])

Check failure on line 229 in CRM/Twingle/Form/Profile.php

View workflow job for this annotation

GitHub Actions / PHPStan with PHP 7.4 prefer-lowest

Cannot call method getName() on CRM_Twingle_Profile|null.
);
break;

case 'create':
Expand Down Expand Up @@ -553,90 +562,31 @@
* TRUE when the form was successfully validated.
*/
public function validate() {
$values = $this->exportValues();

// Validate new profile names.
if (
isset($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']) && 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.');
}

// Validate custom field mapping.
try {
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 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 BaseException(
E::ts('Could not parse custom field mapping.')
);
}
[$twingle_field_name, $custom_field_name] = $custom_field_map;
$custom_field_id = substr($custom_field_name, strlen('custom_'));

// Check for custom field existence
try {
$custom_field = civicrm_api3('CustomField', 'getsingle', [
'id' => $custom_field_id,
]);
}
catch (CRM_Core_Exception $exception) {
throw new BaseException(
E::ts(
'Custom field custom_%1 does not exist.',
[1 => $custom_field_id]
),
NULL,
$exception
);
}
if (in_array($this->_op, ['create', 'edit', 'copy'], TRUE)) {
// Create profile with new values.
$profile_values = $this->exportValues();

Check failure on line 568 in CRM/Twingle/Form/Profile.php

View workflow job for this annotation

GitHub Actions / PHPStan with PHP 7.4 prefer-lowest

Call to an undefined method CRM_Twingle_Form_Profile::exportValues().
$profile = new CRM_Twingle_Profile(
$profile_values['name'],
$profile_values,
$this->profile_id
);

// Only allow custom fields on relevant entities.
try {
$custom_group = civicrm_api3('CustomGroup', 'getsingle', [
'id' => $custom_field['custom_group_id'],
'extends' => [
'IN' => [
'Contact',
'Individual',
'Organization',
'Contribution',
'ContributionRecur',
],
],
]);
}
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
);
}
// Validate profile data
try {
$profile->validate();
}
catch (ProfileValidationError $e) {
switch ($e->getErrorCode()) {
case ProfileValidationError::ERROR_CODE_PROFILE_VALIDATION_FAILED:
$this->setElementError($e->getAffectedFieldName(), $e->getMessage());

Check failure on line 582 in CRM/Twingle/Form/Profile.php

View workflow job for this annotation

GitHub Actions / PHPStan with PHP 7.4 prefer-lowest

Call to an undefined method CRM_Twingle_Form_Profile::setElementError().
break;

case ProfileValidationError::ERROR_CODE_PROFILE_VALIDATION_WARNING:
CRM_Core_Session::setStatus($e->getMessage(), E::ts('Warning'));
}
}
}
catch (BaseException $exception) {
$this->_errors['custom_field_mapping'] = $exception->getMessage();
}

return parent::validate();
}
Expand All @@ -648,8 +598,8 @@
*/
public function setDefaultValues() {
$defaults = parent::setDefaultValues();
if (in_array($this->_op, ['create', 'edit', 'copy'])) {

Check failure on line 601 in CRM/Twingle/Form/Profile.php

View workflow job for this annotation

GitHub Actions / PHPStan with PHP 7.4 prefer-lowest

Call to function in_array() requires parameter #3 to be set.
if (!$this->profile) {

Check failure on line 602 in CRM/Twingle/Form/Profile.php

View workflow job for this annotation

GitHub Actions / PHPStan with PHP 7.4 prefer-lowest

Only booleans are allowed in a negated boolean, CRM_Twingle_Profile|null given.
$this->profile = CRM_Twingle_Profile::createDefaultProfile()->copy();
}
$defaults['name'] = $this->profile->getName();
Expand All @@ -662,7 +612,7 @@
$defaults['campaign_targets'] = ['contribution', 'contact'];
}
}
return $defaults;

Check failure on line 615 in CRM/Twingle/Form/Profile.php

View workflow job for this annotation

GitHub Actions / PHPStan with PHP 7.4 prefer-lowest

Method CRM_Twingle_Form_Profile::setDefaultValues() should return array<string, mixed> but returns array|null.
}

/**
Expand Down
Loading
Loading