let CRM_Twingle_Profile class handle its validation

This commit is contained in:
Marc Michalsky 2023-08-03 14:45:22 +02:00
parent 57b61d06c6
commit 1cdea9f15c
Signed by untrusted user who does not match committer: marc.koch
GPG key ID: 12406554CFB028B9
2 changed files with 156 additions and 97 deletions

View file

@ -462,92 +462,27 @@ class CRM_Twingle_Form_Profile extends CRM_Core_Form {
/** /**
* Validates the profile form. * Validates the profile form.
* * @return bool
* @param array $values * TRUE when the form was successfully validated.
* The submitted form values, keyed by form element name.
*
* @return bool | array
* TRUE when the form was successfully validated, or an array of error
* messages, keyed by form element name.
*/ */
public function validate() { public function validate() {
$values = $this->exportValues();
// Validate new profile names. if (in_array($this->_op, ['create', 'edit', 'copy'])) {
if ( // Create profile with new values
isset($values['name']) $profile_values = $this->exportValues();
&& ($values['name'] != $this->profile->getName() || $this->_op != 'edit') $profile = new CRM_Twingle_Profile(
&& !empty(CRM_Twingle_Profile::getProfile($values['name'])) $profile_values['name'],
) { $profile_values,
$this->_errors['name'] = E::ts('A profile with this name already exists.'); $this->profile_id
} );
// Restrict profile names to alphanumeric characters and the underscore. // Validate profile data
if (isset($values['name']) && preg_match("/[^A-Za-z0-9\_]/", $values['name'])) { try {
$this->_errors['name'] = E::ts('Only alphanumeric characters and the underscore (_) are allowed for profile names.'); $profile->validate();
} }
catch (ProfileValidationError $e) {
// Validate custom field mapping. $this->setElementError($e->getAffectedFieldName(), $e->getMessage());
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 Exception(
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(
E::ts('Could not parse custom field mapping.')
);
}
list($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', array(
'id' => $custom_field_id,
));
}
catch (CiviCRM_API3_Exception $exception) {
throw new Exception(
E::ts(
'Custom field custom_%1 does not exist.',
array(1 => $custom_field_id)
)
);
}
// Only allow custom fields on relevant entities.
try {
$custom_group = civicrm_api3('CustomGroup', 'getsingle', array(
'id' => $custom_field['custom_group_id'],
'extends' => array(
'IN' => array(
'Contact',
'Individual',
'Organization',
'Contribution',
'ContributionRecur',
),
),
));
} catch (CiviCRM_API3_Exception $exception) {
throw new Exception(
E::ts(
'Custom field custom_%1 is not in a CustomGroup that extends one of the supported CiviCRM entities.',
array(1 => $custom_field['id'])
)
);
}
}
} }
}
catch (Exception $exception) {
$this->_errors['custom_field_mapping'] = $exception->getMessage();
} }
return parent::validate(); return parent::validate();

View file

@ -94,14 +94,7 @@ class CRM_Twingle_Profile {
* @return bool * @return bool
*/ */
public function matches($project_id) { public function matches($project_id) {
$selector = $this->getAttribute('selector'); return in_array($project_id, $this->getProjectIds());
$project_ids = array_map(
function($project_id) {
return trim($project_id);
},
explode(',', $selector)
);
return in_array($project_id, $project_ids);
} }
/** /**
@ -173,6 +166,20 @@ class CRM_Twingle_Profile {
return $this->name == 'default'; return $this->name == 'default';
} }
/**
* Retrieves the profile's project IDs.
*
* @return array
*/
public function getProjectIds(): array {
return array_map(
function($project_id) {
return trim($project_id);
},
explode(',', $this->getAttribute("selector"))
);
}
/** /**
* Retrieves an attribute of the profile. * Retrieves an attribute of the profile.
* *
@ -229,21 +236,138 @@ class CRM_Twingle_Profile {
* 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 * @throws \CRM_Twingle_Exceptions_ProfileValidationError
* @throws \Civi\Core\Exception\DBQueryException
* When the profile could not be successfully validated. * When the profile could not be successfully validated.
*/ */
public function verifyProfile() { public function validate() {
// TODO: check
// data of this profile consistent? // Name cannot be empty
// conflicts with other profiles? if (empty($this->getName())) {
throw new ProfileValidationError(
'name',
E::ts('Profile name cannot be empty.'),
ProfileValidationError::ERROR_CODE_PROFILE_VALIDATION_FAILED
);
}
// Restrict profile names to alphanumeric characters, space and the underscore.
$contains_illegal_characters = preg_match("/[^A-Za-z0-9_\s]/", $this->getName());
if ($contains_illegal_characters) {
throw new ProfileValidationError(
'name',
E::ts('Only alphanumeric characters, space and the underscore (_) are allowed for profile names.'),
ProfileValidationError::ERROR_CODE_PROFILE_VALIDATION_FAILED
);
}
// Check if profile name is already used for other profile
$profile_name_duplicates = array_filter(
CRM_Twingle_Profile::getProfiles(), function($profile) {
return $profile->getName() == $this->getName() && $this->getId() != $profile->getId();
});
if (!empty($profile_name_duplicates)) {
throw new ProfileValidationError(
'name',
E::ts("A profile with the name '%1' already exists.", [1 => $this->getName()]),
ProfileValidationError::ERROR_CODE_PROFILE_VALIDATION_FAILED
);
}
// Check if project_id is already used in other profile
// FIXME: Check is not working
$profiles = $this::getProfiles();
foreach ($profiles as $profile) {
if ($profile->getId() == $this->getId() || $profile->is_default()) continue;
$project_ids = $this->getProjectIds();
$id_duplicates = array_intersect($profile->getProjectIds(), $project_ids);
if (!empty($id_duplicates)) {
throw new ProfileValidationError(
'selector',
E::ts(
"Project ID(s) [%1] already used in profile '%2'.",
[
1 => implode(", ", $id_duplicates),
2 => $profile->getName()
]
),
ProfileValidationError::ERROR_CODE_PROFILE_VALIDATION_FAILED
);
}
}
// Validate custom field mapping.
$custom_field_mapping = $this->getAttribute('custom_field_mapping', FALSE);
if ($custom_field_mapping) {
$custom_field_mapping = preg_split('/\r\n|\r|\n/', $custom_field_mapping, -1, PREG_SPLIT_NO_EMPTY);
$parsing_error = new ProfileValidationError(
'custom_field_mapping',
E::ts('Could not parse custom field mapping.'),
ProfileValidationError::ERROR_CODE_PROFILE_VALIDATION_FAILED
);
if (!is_array($custom_field_mapping)) {
throw $parsing_error;
}
foreach ($custom_field_mapping as $custom_field_map) {
$custom_field_map = explode("=", $custom_field_map);
if (count($custom_field_map) !== 2) {
throw $parsing_error;
}
[$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 (CiviCRM_API3_Exception $exception) {
throw new ProfileValidationError(
'custom_field_mapping',
E::ts(
'Custom field custom_%1 does not exist.',
[1 => $custom_field_id]
),
ProfileValidationError::ERROR_CODE_PROFILE_VALIDATION_FAILED
);
}
// Only allow custom fields on relevant entities.
try {
civicrm_api3('CustomGroup', 'getsingle',
[
'id' => $custom_field['custom_group_id'],
'extends' => [
'IN' => [
'Contact',
'Individual',
'Organization',
'Contribution',
'ContributionRecur',
],
],
]);
} catch (CiviCRM_API3_Exception $exception) {
throw new ProfileValidationError(
'custom_field_mapping',
E::ts(
'Custom field custom_%1 is not in a CustomGroup that extends one of the supported CiviCRM entities.',
[1 => $custom_field['id']]
),
ProfileValidationError::ERROR_CODE_PROFILE_VALIDATION_FAILED
);
}
}
}
} }
/** /**
* Persists the profile within the CiviCRM settings. * Persists the profile within the database.
*
* @throws \CRM_Twingle_Exceptions_ProfileException
*/ */
public function saveProfile() { public function saveProfile() {
// make sure it's valid
$this->verifyProfile();
try { try {
if ($this->id !== NULL) { if ($this->id !== NULL) {