️ implement formatting of settings values on import and export

This commit is contained in:
Marc Michalsky forumZFD 2021-04-12 11:44:11 +02:00
parent 0d9b312a9b
commit 0b1128fce5
Signed by untrusted user who does not match committer: marc.koch
GPG key ID: 12406554CFB028B9
6 changed files with 436 additions and 59 deletions

View file

@ -10,6 +10,10 @@ abstract class CRM_TwingleCampaign_BAO_Campaign {
// OUT means: coming from the CiviCRM database // OUT means: coming from the CiviCRM database
public const OUT = 'OUT'; public const OUT = 'OUT';
public const PROJECT = 'TwingleProject';
public const EVENT = 'TwingleEvent';
protected $className; protected $className;
protected $id; protected $id;
@ -60,6 +64,7 @@ abstract class CRM_TwingleCampaign_BAO_Campaign {
); );
$this->translateKeys( $this->translateKeys(
$values_prepared_for_import, $values_prepared_for_import,
$this->className,
self::IN self::IN
); );
$this->formattedValues = $values_prepared_for_import; $this->formattedValues = $values_prepared_for_import;
@ -76,6 +81,16 @@ abstract class CRM_TwingleCampaign_BAO_Campaign {
$_SESSION['CiviCRM']['de.forumzfd.twinglecampaign']['no_hook'] = TRUE; $_SESSION['CiviCRM']['de.forumzfd.twinglecampaign']['no_hook'] = TRUE;
} }
// Cast booleans to integers
foreach ($values_prepared_for_import as $key => $value) {
if ($value === false) {
$values_prepared_for_import[$key] = 0;
}
elseif ($value === true) {
$values_prepared_for_import[$key] = 1;
}
}
// Create campaign // Create campaign
$result = civicrm_api3('Campaign', 'create', $values_prepared_for_import); $result = civicrm_api3('Campaign', 'create', $values_prepared_for_import);
@ -156,17 +171,27 @@ abstract class CRM_TwingleCampaign_BAO_Campaign {
* *
* @param array $values * @param array $values
* array of keys to translate * array of keys to translate
* * @param string $campaignType
* const: Campaign::PROJECT or Campaign::EVENT
* @param string $direction * @param string $direction
* const: Campaign::OUT or Campaign::OUT * const: Campaign::OUT or Campaign::OUT
* *
* @throws Exception * @throws \Exception
*/ */
public function translateKeys(array &$values, string $direction) { public static function translateKeys(
array &$values,
string $campaignType,
string $direction) {
if ($campaignType != self::PROJECT && $campaignType != self::EVENT) {
throw new Exception(
"Invalid Parameter $campaignType for translateKeys()"
);
}
// Get translations for fields // Get translations for fields
$field_translations = Cache::getInstance() $field_translations = Cache::getInstance()
->getTranslations()[$this->className]; ->getTranslations()[$campaignType];
// Set the direction of the translation // Set the direction of the translation
if ($direction == self::OUT) { if ($direction == self::OUT) {
@ -188,7 +213,6 @@ abstract class CRM_TwingleCampaign_BAO_Campaign {
} }
} }
/** /**
* ## Translate field names and custom field names * ## Translate field names and custom field names
* *
@ -256,18 +280,6 @@ abstract class CRM_TwingleCampaign_BAO_Campaign {
} }
} }
/**
* ## Set counter url
*
* @param String $counterUrl
* URL of the counter
*/
public function setCounterUrl(string $counterUrl) {
$this->values['counter'] = $counterUrl;
}
/** /**
* ## Delete Campaign * ## Delete Campaign
* Deletes this Campaign from CiviCRM * Deletes this Campaign from CiviCRM
@ -287,7 +299,6 @@ abstract class CRM_TwingleCampaign_BAO_Campaign {
return ($result['is_error'] == 0); return ($result['is_error'] == 0);
} }
/** /**
* ## Deactivate this campaign * ## Deactivate this campaign
* *
@ -300,7 +311,6 @@ abstract class CRM_TwingleCampaign_BAO_Campaign {
return self::deactivateByid($this->id); return self::deactivateByid($this->id);
} }
/** /**
* ## Deactivate campaign by ID * ## Deactivate campaign by ID
* *
@ -335,10 +345,8 @@ abstract class CRM_TwingleCampaign_BAO_Campaign {
} }
} }
public abstract function getResponse(string $status): array; public abstract function getResponse(string $status): array;
/** /**
* ## Get timestamp * ## Get timestamp
* Validates **$input** to be either a *DateTime string* or an *Unix * Validates **$input** to be either a *DateTime string* or an *Unix
@ -370,10 +378,8 @@ abstract class CRM_TwingleCampaign_BAO_Campaign {
} }
} }
public abstract function lastUpdate(); public abstract function lastUpdate();
/** /**
* ## Get DateTime * ## Get DateTime
* Validates **$input** to be either a *DateTime string* or an *Unix * Validates **$input** to be either a *DateTime string* or an *Unix
@ -407,7 +413,6 @@ abstract class CRM_TwingleCampaign_BAO_Campaign {
} }
} }
/** /**
* ## Get id * ## Get id
* Returns the **id** of this campaign. * Returns the **id** of this campaign.
@ -418,4 +423,52 @@ abstract class CRM_TwingleCampaign_BAO_Campaign {
return (int) $this->id; return (int) $this->id;
} }
/**
* Helper function to search a value in a multidimensional array.
*
* @param $needle
* @param $haystack
* @param bool $strict
*
* @return bool
*/
protected function in_array_r($needle, $haystack, $strict = false): bool {
foreach ($haystack as $item) {
if (($strict ? $item === $needle : $item == $needle) ||
(is_array($item) && $this->in_array_r($needle, $item, $strict))) {
return true;
}
}
return false;
}
/**
* Helper function to check if the provided field is of type Boolean
*
* @param $fieldName
* @param string $campaignType Campaign::PROJECT or Campaign::EVENT
*
* @return bool
* @throws \Exception
*/
public static function isBoolean($fieldName, string $campaignType): bool {
$fields = Cache::getInstance()->getCampaigns()['custom_fields'];
if ($campaignType == self::PROJECT) {
if (isset($fields['twingle_project_' . $fieldName])) {
return $fields['twingle_project_' . $fieldName]['data_type'] == 'Boolean';
}
else {
return FALSE;
}
}
elseif ($campaignType == self::EVENT) {
if (isset($fields['twingle_event_' . $fieldName])) {
return $fields['twingle_event_' . $fieldName]['data_type'] == 'Boolean';
}
else {
return FALSE;
}
}
throw new Exception('Unknown campaign type');
}
} }

View file

@ -100,7 +100,7 @@ class CRM_TwingleCampaign_BAO_TwingleEvent extends Campaign {
* *
* @throws Exception * @throws Exception
*/ */
public function formatValues(array &$values, string $direction) { public static function formatValues(array &$values, string $direction) {
if ($direction == self::IN) { if ($direction == self::IN) {

View file

@ -2,10 +2,35 @@
use CRM_TwingleCampaign_Utils_ExtensionCache as Cache; use CRM_TwingleCampaign_Utils_ExtensionCache as Cache;
use CRM_TwingleCampaign_BAO_Campaign as Campaign; use CRM_TwingleCampaign_BAO_Campaign as Campaign;
use CRM_TwingleCampaign_Utils_StringOperations as StringOperations;
use CRM_TwingleCampaign_ExtensionUtil as E;
class CRM_TwingleCampaign_BAO_TwingleProject extends Campaign { class CRM_TwingleCampaign_BAO_TwingleProject extends Campaign {
// All available contact fields in Twingle project forms
const contactFields = [
'salutation',
'firstname',
'lastname',
'company',
'birthday',
'street',
'postal_code',
'city',
'country',
'telephone',
];
// All available donation rhythms in Twingle project forms
const donationRhythm = [
'yearly',
'halfyearly',
'quarterly',
'monthly',
'one_time',
];
/** /**
* ## TwingleProject constructor * ## TwingleProject constructor
* *
@ -25,36 +50,91 @@ class CRM_TwingleCampaign_BAO_TwingleProject extends Campaign {
} }
/** /**
* ## Export values * ## Export values
* Ensures that only those values will be exported which the Twingle API * Change all values to a format accepted by the Twingle API.
* expects. These values are defined in
* *CRM/TwingleCampaign/resources/twingle_api_templates.json*
* *
* @return array * @return array
* Array with all values to send to the Twingle API * Array with all values ready to send to the Twingle API
* * @throws \Exception
* @throws Exception
*/ */
public function export(): array { public function export(): array {
// copy project values
$values = $this->values; $values = $this->values;
self::formatValues($values, self::OUT);
// Get template for project // Strings to booleans
$project = Cache::getInstance()->getTemplates()['TwingleProject']; $this->intToBool($values);
// Replace array items which the Twingle API does not expect // Strings to integers
foreach ($values as $key => $value) { $this->strToInt($values);
if (!in_array($key, $project)) {
unset($values[$key]); // Build counter-url array
} if (isset($values['counter-url']) && is_string($values['counter-url'])) {
$url = $values['counter-url'];
unset($values['counter-url']);
$values['counter-url']['url'] = $url;
} }
// Remove campaign_type_id
unset($values['campaign_type_id']);
return $values; return $values;
} }
/**
* ## Int to bool
* Changes all project values that are defined as CiviCRM 'Boolean' types
* from strings to booleans.
* @param array $values
* @throws \Exception
*/
private function intToBool(array &$values) {
$boolArrays = [
'payment_methods',
'donation_rhythm',
];
foreach ($values as $key => $value) {
if (CRM_TwingleCampaign_BAO_Campaign::isBoolean(
$key,
CRM_TwingleCampaign_BAO_Campaign::PROJECT
)) {
$values[$key] = (bool) $value;
}
elseif (in_array($key, $boolArrays)) {
foreach ($values[$key] as $_key => $_value) {
if (is_numeric($_value) && $_value < 2 || empty($_value)) {
$values[$key][$_key] = (bool) $_value;
}
else {
unset($values[$key][$_key]);
}
}
}
elseif (is_array($value)) {
$this->intToBool($values[$key]);
}
}
}
/**
* ## Int to bool
* Changes all project values that are strings but originally came as integers
* back to integers.
* @param array $values
* @throws \Exception
*/
private function strToInt(array &$values) {
foreach ($values as $key => $value) {
if (ctype_digit($value)) {
$values[$key] = intval($value);
}
elseif (is_array($value)) {
$this->strToInt($values[$key]);
}
}
}
/** /**
* ## Create this TwingleProject as a campaign in CiviCRM * ## Create this TwingleProject as a campaign in CiviCRM
@ -80,6 +160,58 @@ class CRM_TwingleCampaign_BAO_TwingleProject extends Campaign {
} }
} }
/**
* ## Update instance values
*
* @param array $values
*
* @override CRM_TwingleCampaign_BAO_Campaign::update()
*/
public function update(array $values) {
// Remove old values
unset($this->values);
// Get allowed values
$projectOptionsKeys = Cache::getInstance()
->getTemplates()['TwingleProject']['project_options'];
$projectEmbedDataKeys = Cache::getInstance()
->getTemplates()['TwingleProject']['project_embed_data'];
$projectPaymentMethodsKeys = Cache::getInstance()
->getTemplates()['TwingleProject']['payment_methods'];
// Sort allowed values into arrays
foreach ($values as $key => $value) {
if ($key == 'project_options') {
foreach ($value as $optionKey => $optionValue) {
if (in_array($optionKey, $projectOptionsKeys)) {
$this->values['project_options'][$optionKey] = $optionValue;
}
}
}
elseif ($key == 'embed') {
foreach ($value as $embedKey => $embedValue) {
if (in_array($embedKey, $projectEmbedDataKeys)) {
$this->values['embed'][$embedKey] = $embedValue;
}
}
}
elseif ($key == 'payment_methods') {
foreach ($value as $paymentMethodKey => $paymentMethodValue) {
if (in_array($paymentMethodKey, $projectPaymentMethodsKeys)) {
$this->values['payment_methods'][$paymentMethodKey] =
$paymentMethodValue;
}
}
}
elseif ($key == 'counter-url' && is_array($value)) {
$this->values['counter-url'] = $value['url'];
}
else {
parent::update([$key => $value]);
}
}
}
/** /**
* ## Clone this TwingleProject * ## Clone this TwingleProject
@ -114,23 +246,127 @@ class CRM_TwingleCampaign_BAO_TwingleProject extends Campaign {
* *
* @throws Exception * @throws Exception
*/ */
public function formatValues(array &$values, string $direction) { public static function formatValues(array &$values, string $direction) {
if ($direction == self::IN) { if ($direction == self::IN) {
// Change timestamp into DateTime string // Change timestamp into DateTime string
if ($values['last_update']) { if (isset($values['last_update'])) {
$values['last_update'] = $values['last_update'] =
self::getDateTime($values['last_update']); self::getDateTime($values['last_update']);
} }
// empty project_type to 'default' // empty project_type to 'default'
if (!$values['type']) { if (empty($values['type'])) {
$values['type'] = 'default'; $values['type'] = 'default';
} }
// Flatten project options array
foreach ($values['project_options'] as $key => $value) {
$values[$key] = $value;
}
unset($values['project_options']);
// Flatten embed codes array
foreach ($values['embed'] as $key => $value) {
$values[$key] = $value;
}
unset($values['embed']);
// Flatten button array
if (isset($values['buttons'])) {
foreach (
$values['buttons'] as $button_key => $button
) {
$values[$button_key] = $button['amount'];
}
unset($values['buttons']);
}
// Invert and explode exclude_contact_fields
if (isset($values['exclude_contact_fields'])) {
$values['contact_fields'] =
array_diff(
self::contactFields,
explode(',', $values['exclude_contact_fields'])
);
unset($values['exclude_contact_fields']);
}
// Explode mandatory_contact_fields
if (isset($values['mandatory_contact_fields'])) {
$values['mandatory_contact_fields'] =
explode(
',',
$values['mandatory_contact_fields']
);
unset($values['mandatory_contact_fields']);
}
// Explode languages
if (isset($values['languages'])) {
$values['languages'] =
explode(',', $values['languages']);
}
// Divide payment methods array into one time and recurring payment
// methods arrays containing only TRUE payment methods
foreach ($values['payment_methods'] as $key => $value) {
if ($value) {
if (StringOperations::endsWith($key, 'recurring')) {
$values['payment_methods_recurring'][] = $key;
}
else {
$values['payment_methods'][] = $key;
}
}
unset($values['payment_methods'][$key]);
}
// Transform donation rhythm array to contain only TRUE elements
foreach ($values['donation_rhythm'] as $key => $value) {
if ($value) {
$values['donation_rhythm'][] = $key;
}
unset($values['donation_rhythm'][$key]);
}
} }
elseif ($direction == self::OUT) { elseif ($direction == self::OUT) {
$projectOptionsKeys = Cache::getInstance()
->getTemplates()['TwingleProject']['project_options'];
$projectEmbedDataKeys = Cache::getInstance()
->getTemplates()['TwingleProject']['project_embed_data'];
// Merge payment_methods and payment_methods_recurring arrays and change
// keys to values and values to TRUE
if (isset($values['payment_methods'])) {
foreach ($values['payment_methods'] as $key => $value) {
unset($values['payment_methods'][$key]);
$values['payment_methods'][$value] = TRUE;
}
}
if (isset($values['payment_methods_recurring'])) {
foreach ($values['payment_methods_recurring'] as $value) {
$values['payment_methods'][$value] = TRUE;
}
unset($values['payment_methods_recurring']);
}
// Move options, embed data and payment methods into own arrays
foreach ($values as $key => $value) {
if (in_array($key, $projectOptionsKeys)) {
$values['project_options'][$key]
= $value;
unset($values[$key]);
}
elseif (in_array($key, $projectEmbedDataKeys)) {
$values['embed_data'][$key]
= $value;
unset($values[$key]);
}
}
// Change DateTime string into timestamp // Change DateTime string into timestamp
$values['last_update'] = $values['last_update'] =
self::getTimestamp($values['last_update']); self::getTimestamp($values['last_update']);
@ -141,19 +377,80 @@ class CRM_TwingleCampaign_BAO_TwingleProject extends Campaign {
: $values['type']; : $values['type'];
// Cast project target to integer // Cast project target to integer
$values['project_target'] = (int) $values['project_target']; if (isset($values['project_target'])) {
$values['project_target'] = (int) $values['project_target'];
}
// Set default for 'allow_more' // Set default for 'allow_more'
$values['allow_more'] = empty($values['allow_more']) $values['allow_more'] = !empty($values['allow_more']);
? FALSE
: TRUE; // Invert and concatenate contact fields
if (isset($values['project_options']['contact_fields'])) {
// Invert contact_fields to exclude_contact_fields
$values['project_options']['exclude_contact_fields'] =
array_diff(
self::contactFields,
$values['project_options']['contact_fields']
);
unset($values['project_options']['contact_fields']);
// Concatenate contact_fields array
$values['project_options']['exclude_contact_fields'] =
implode(
',',
$values['project_options']['exclude_contact_fields']
);
}
// Concatenate mandatory project contact fields
if (isset($values['project_options']['mandatory_contact_fields'])) {
$values['project_options']['mandatory_contact_fields'] =
implode(
',',
$values['project_options']['mandatory_contact_fields']
);
}
// Concatenate project languages
if (isset($values['project_options']['languages'])) {
$values['project_options']['languages'] =
implode(',', $values['project_options']['languages']);
}
// Build donation_rhythm array
if (isset($values['project_options']['donation_rhythm'])) {
$tmp_array = [];
foreach (self::donationRhythm as $donationRhythm) {
$tmp_array[$donationRhythm] =
in_array(
$donationRhythm,
$values['project_options']['donation_rhythm']
);
}
$values['project_options']['donation_rhythm'] = $tmp_array;
}
// Build payment_methods_array
if (isset($values['payment_methods'])) {
$payment_methods = array_fill_keys(Cache::getInstance()
->getTemplates()['TwingleProject']['payment_methods'],
FALSE);
$values['payment_methods'] =
array_merge($payment_methods, $values['payment_methods']);
}
// Build buttons array
for ($i = 1; $i <= 4; $i++) {
if (isset($values['button' . $i])) {
$values['project_options']['buttons']['button' . $i] =
['amount' => $values['button' . $i]];
unset($values['button' . $i]);
}
}
} }
else { else {
throw new Exception( throw new Exception(
"Invalid Parameter $direction for formatValues()" "Invalid Parameter $direction for formatValues()"
); );
} }
} }

View file

@ -51,4 +51,24 @@ class CRM_TwingleCampaign_Utils_StringOperations {
} }
return $string; return $string;
} }
/**
* Checks if a string ands with another string.
* @param $haystack
* @param $needle
* @return bool
*/
public static function endsWith($haystack, $needle): bool {
return substr_compare($haystack, $needle, -strlen($needle)) === 0;
}
/**
* Checks if a string starts with another string.
* @param $haystack
* @param $needle
* @return bool
*/
public static function startsWith($haystack, $needle): bool {
return substr_compare($haystack, $needle, 0, strlen($needle)) === 0;
}
} }

View file

@ -157,6 +157,7 @@ function civicrm_api3_twingle_event_Get(array $params): array {
$tmp_event = new TwingleEvent([]); $tmp_event = new TwingleEvent([]);
$tmp_event->translateKeys( $tmp_event->translateKeys(
$returnValues[$event['id']], $returnValues[$event['id']],
TwingleEvent::EVENT,
TwingleEvent::OUT TwingleEvent::OUT
); );
TwingleEvent::formatValues( TwingleEvent::formatValues(

View file

@ -156,22 +156,28 @@ function civicrm_api3_twingle_project_Get(array $params): array {
$returnValues[$project['id']][$key] = $value; $returnValues[$project['id']][$key] = $value;
} }
} }
foreach($returnValues[$project['id']] as $key => $value) { foreach ($returnValues[$project['id']] as $key => $value) {
if ($key != 'twingle_project_id' && strpos($key, 'twingle_project_') === 0) { if ($key != 'twingle_project_id' && strpos($key, 'twingle_project_') === 0) {
$returnValues[$project['id']][str_replace('twingle_project_', '', $key)] $key_short = str_replace('twingle_project_', '', $key);
= $value; $returnValues[$project['id']][$key_short] = $value;
unset($returnValues[$project['id']][$key]); unset($returnValues[$project['id']][$key]);
} elseif($key == 'twingle_project_id'){ }
elseif ($key == 'twingle_project_id') {
$returnValues[$project['id']]['project_id'] = $value; $returnValues[$project['id']]['project_id'] = $value;
unset($returnValues[$project['id']]['twingle_project_id']); unset($returnValues[$project['id']]['twingle_project_id']);
} }
} }
try { try {
$tmp_project = new TwingleProject([]); TwingleProject::translateKeys(
$tmp_project->translateKeys($returnValues[$project['id']], TwingleProject::OUT); $returnValues[$project['id']],
$tmp_project->formatValues($returnValues[$project['id']], TwingleProject::OUT); TwingleProject::PROJECT,
} TwingleProject::OUT
catch (Exception $e) { );
TwingleProject::formatValues(
$returnValues[$project['id']],
TwingleProject::OUT
);
} catch (Exception $e) {
throw new API_Exception($e->getMessage()); throw new API_Exception($e->getMessage());
} }
} }