️ implement validation of TwingleProject settings

This commit is contained in:
Marc Michalsky forumZFD 2021-04-13 01:05:57 +02:00
parent 060f7ec2dc
commit 34e2e77d83
Signed by untrusted user who does not match committer: marc.koch
GPG key ID: 12406554CFB028B9
7 changed files with 652 additions and 183 deletions

View file

@ -33,7 +33,7 @@ abstract class CRM_TwingleCampaign_BAO_Campaign {
* @param array $values * @param array $values
* @param int|null $id * @param int|null $id
*/ */
protected function __construct(array $values, int $id = NULL) { protected function __construct(array $values = [], int $id = NULL) {
$this->id = $id; $this->id = $id;
$tmpClassName = explode('_', get_class($this)); $tmpClassName = explode('_', get_class($this));

View file

@ -129,10 +129,14 @@ class CRM_TwingleCampaign_BAO_TwingleApiCall {
*/ */
public function pushProject(array $project): array { public function pushProject(array $project): array {
if (isset($project['project_options'])) {
$projectOptions = $project['project_options']; $projectOptions = $project['project_options'];
unset($project['project_options']); unset($project['project_options']);
}
if (isset($project['payment_methods'])) {
$paymentMethods = $project['payment_methods']; $paymentMethods = $project['payment_methods'];
unset($project['payment_methods']); unset($project['payment_methods']);
}
try { try {
if (!isset($project['id'])) { if (!isset($project['id'])) {
@ -153,14 +157,22 @@ class CRM_TwingleCampaign_BAO_TwingleApiCall {
} }
// Post project_options // Post project_options
if (isset($projectOptions)) {
$updatedProject['project_options'] = $updatedProject['project_options'] =
$this->curlPost($url . '/options', $projectOptions); $this->curlPost($url . '/options', $projectOptions);
}
else {
$updatedProject['project_options'] = $this->getProjectOptions($updatedProject['id']);
}
// Post payment_methods // Post payment_methods
if (isset ($paymentMethods)) {
$this->curlPost($url . '/payment-methods', $paymentMethods); $this->curlPost($url . '/payment-methods', $paymentMethods);
}
$updatedProject['payment_methods'] = $updatedProject['payment_methods'] =
$this->getProjectPaymentMethods($updatedProject['id']); $this->getProjectPaymentMethods($updatedProject['id']);
// Set last update time // Set last update time
$updatedProject['last_update'] = max( $updatedProject['last_update'] = max(
$updatedProject['last_update'], $updatedProject['last_update'],
@ -171,14 +183,14 @@ class CRM_TwingleCampaign_BAO_TwingleApiCall {
unset($updatedProject['payment_methods']['updated_at']); unset($updatedProject['payment_methods']['updated_at']);
return $updatedProject; return $updatedProject;
} catch (Exception $e) { }
catch
(Exception $e) {
throw new Exception( throw new Exception(
E::SHORT_NAME . 'Call to Twingle API failed: ' . E::SHORT_NAME . 'Call to Twingle API failed: ' .
$e->getMessage() $e->getMessage()
); );
} }
} }
@ -266,6 +278,7 @@ class CRM_TwingleCampaign_BAO_TwingleApiCall {
/** /**
* ## Get project options * ## Get project options
* Gets all project options from the Twingle API * Gets all project options from the Twingle API
*
* @param $projectId * @param $projectId
* *
* @return array * @return array
@ -279,6 +292,7 @@ class CRM_TwingleCampaign_BAO_TwingleApiCall {
/** /**
* ## Get project payment methods * ## Get project payment methods
* Gets all project payment methods from the Twingle API * Gets all project payment methods from the Twingle API
*
* @param $projectId * @param $projectId
* *
* @return array * @return array
@ -360,7 +374,8 @@ class CRM_TwingleCampaign_BAO_TwingleApiCall {
} }
if ($curl_status_code == 404) { if ($curl_status_code == 404) {
throw new Exception('http status code 404 (not found)'); throw new Exception('http status code 404 (not found)');
} elseif ($curl_status_code == 500) { }
elseif ($curl_status_code == 500) {
throw new Exception('https status code 500 (internal error)'); throw new Exception('https status code 500 (internal error)');
} }
return $response; return $response;
@ -402,7 +417,8 @@ class CRM_TwingleCampaign_BAO_TwingleApiCall {
} }
if ($curl_status_code == 404) { if ($curl_status_code == 404) {
throw new Exception('http status code 404 (not found)'); throw new Exception('http status code 404 (not found)');
} elseif ($curl_status_code == 500) { }
elseif ($curl_status_code == 500) {
throw new Exception('https status code 500 (internal error)'); throw new Exception('https status code 500 (internal error)');
} }
if (sizeof($response) == 1 && isset($response['message'])) { if (sizeof($response) == 1 && isset($response['message'])) {
@ -448,7 +464,8 @@ class CRM_TwingleCampaign_BAO_TwingleApiCall {
} }
if ($curl_status_code == 404) { if ($curl_status_code == 404) {
throw new Exception('http status code 404 (not found)'); throw new Exception('http status code 404 (not found)');
} elseif ($curl_status_code == 500) { }
elseif ($curl_status_code == 500) {
throw new Exception('https status code 500 (internal error)'); throw new Exception('https status code 500 (internal error)');
} }
return ($curl_status_code == 200); return ($curl_status_code == 200);

View file

@ -26,7 +26,7 @@ class CRM_TwingleCampaign_BAO_TwingleCampaign {
* *
* @throws \CiviCRM_API3_Exception * @throws \CiviCRM_API3_Exception
*/ */
public function __construct(array $values, int $id = NULL) { public function __construct(array $values = [], int $id = NULL) {
$this->prefix = 'twingle_campaign_'; $this->prefix = 'twingle_campaign_';
$this->id = $id ?? NULL; $this->id = $id ?? NULL;

View file

@ -19,7 +19,7 @@ class CRM_TwingleCampaign_BAO_TwingleEvent extends Campaign {
* *
* @throws \Exception * @throws \Exception
*/ */
public function __construct(array $event, int $id = NULL) { public function __construct(array $event = [], int $id = NULL) {
parent::__construct($event, $id); parent::__construct($event, $id);
$this->prefix = 'twingle_event_'; $this->prefix = 'twingle_event_';

View file

@ -40,7 +40,7 @@ class CRM_TwingleCampaign_BAO_TwingleProject extends Campaign {
* @param int|null $id * @param int|null $id
* CiviCRM Campaign id * CiviCRM Campaign id
*/ */
public function __construct(array $values, int $id = NULL) { public function __construct(array $values = [], int $id = NULL) {
parent::__construct($values, $id); parent::__construct($values, $id);
$this->prefix = 'twingle_project_'; $this->prefix = 'twingle_project_';
@ -85,7 +85,9 @@ class CRM_TwingleCampaign_BAO_TwingleProject extends Campaign {
* ## Int to bool * ## Int to bool
* Changes all project values that are defined as CiviCRM 'Boolean' types * Changes all project values that are defined as CiviCRM 'Boolean' types
* from strings to booleans. * from strings to booleans.
*
* @param array $values * @param array $values
*
* @throws \Exception * @throws \Exception
*/ */
private function intToBool(array &$values) { private function intToBool(array &$values) {
@ -104,7 +106,10 @@ class CRM_TwingleCampaign_BAO_TwingleProject extends Campaign {
} }
elseif (in_array($key, $boolArrays)) { elseif (in_array($key, $boolArrays)) {
foreach ($values[$key] as $_key => $_value) { foreach ($values[$key] as $_key => $_value) {
if (is_numeric($_value) && $_value < 2 || empty($_value)) { if (is_bool($_value)) {
// nothing to do here
}
elseif (is_numeric($_value) && $_value < 2 || empty($_value)) {
$values[$key][$_key] = (bool) $_value; $values[$key][$_key] = (bool) $_value;
} }
else { else {
@ -122,7 +127,9 @@ class CRM_TwingleCampaign_BAO_TwingleProject extends Campaign {
* ## Int to bool * ## Int to bool
* Changes all project values that are strings but originally came as integers * Changes all project values that are strings but originally came as integers
* back to integers. * back to integers.
*
* @param array $values * @param array $values
*
* @throws \Exception * @throws \Exception
*/ */
private function strToInt(array &$values) { private function strToInt(array &$values) {
@ -214,8 +221,33 @@ class CRM_TwingleCampaign_BAO_TwingleProject extends Campaign {
} }
/** /**
* ## Clone this TwingleProject * ## Complement campaign values
* Complement existing campaign values with new ones
* *
* @param array $arrayToComplement
*
* @overrides CRM_TwingleCampaign_BAO_Campaign
*/
public function complement(array $arrayToComplement) {
// Set all contact fields to false
if (
isset($arrayToComplement['values']['project_options']['contact_fields'])
) {
foreach (
$arrayToComplement['values']['project_options']['contact_fields']
as $key => $value
) {
$arrayToComplement['values']['project_options']['contact_fields'][$key]
= FALSE;
}
}
parent::complement($arrayToComplement);
}
/**
* ## Clone this TwingleProject
* This method removes the id and the identifier from this instance and in * This method removes the id and the identifier from this instance and in
* the next step it pushes the clone as a new project with the same values to * the next step it pushes the clone as a new project with the same values to
* Twingle. * Twingle.
@ -223,11 +255,163 @@ class CRM_TwingleCampaign_BAO_TwingleProject extends Campaign {
* @throws \Exception * @throws \Exception
*/ */
public function clone() { public function clone() {
$this->values['id'] = 0; $this->values['id'] = '';
$this->values['identifier'] = 0; $this->values['identifier'] = '';
$this->create(); // this will also trigger the postSave hook $this->create(); // this will also trigger the postSave hook
} }
/**
* ## Validate
* Validates project values and returns an array containing the result and
* another array with eventual error messages.
*
* @return array ['valid' => bool, 'messages' => array]
*/
public function validate(): array {
$valid = TRUE;
$messages = [];
// Validate email address
if (
!filter_var(
$this->values['project_options']['bcc_email_address'],
FILTER_VALIDATE_EMAIL
)
&& !empty($this->values['project_options']['bcc_email_address'])
) {
$valid = FALSE;
$messages[] = E::ts("BCC email invalid");
}
// Validate hexadecimal color fields
$colorFields =
[
'design_background_color',
'design_primary_color',
'design_font_color',
];
foreach ($colorFields as $colorField) {
if (
!empty($this->values['project_options'][$colorField]) &&
(
!(
ctype_xdigit($this->values['project_options'][$colorField]) ||
is_integer($this->values['project_options'][$colorField])
) ||
strlen((string) $this->values['project_options'][$colorField]) > 6
)
) {
$valid = FALSE;
$messages[] =
E::ts("Invalid hexadecimal value in color field: %1",
[1 => $colorField]);
}
}
// Check if donation values are integers and if proposed donation value
// lies between max and min values
if (
// Is integer and >= 0 or empty
(
empty($this->values['project_options']['donation_value_default']) ||
(
is_integer($this->values['project_options']['donation_value_default']) ||
ctype_digit($this->values['project_options']['donation_value_default'])
) && (
$this->values['project_options']['donation_value_default'] >= 0
)
) && (
empty($this->values['project_options']['donation_value_min']) ||
(
is_integer($this->values['project_options']['donation_value_min']) ||
ctype_digit($this->values['project_options']['donation_value_min'])
) && (
$this->values['project_options']['donation_value_max'] >= 0
)
) && (
empty($this->values['project_options']['donation_value_max']) ||
(
is_integer($this->values['project_options']['donation_value_max']) ||
ctype_digit($this->values['project_options']['donation_value_max'])
) && (
$this->values['project_options']['donation_value_max'] >= 0
)
)
) {
if (
// all empty
empty($this->values['project_options']['donation_value_default']) &&
empty($this->values['project_options']['donation_value_min']) &&
empty($this->values['project_options']['donation_value_max'])
) {
// nothing to validate
}
elseif (
// Max empty, min not empty
(!empty($this->values['project_options']['donation_value_min']) &&
empty($this->values['project_options']['donation_value_max'])) ||
// Max empty, default not empty
(!empty($this->values['project_options']['donation_value_default']) &&
empty($this->values['project_options']['donation_value_max']))
) {
$valid = FALSE;
$messages[] =
E::ts("Missing maximum donation value");
}
else {
if (
// Min >= Max
$this->values['project_options']['donation_value_min'] >=
$this->values['project_options']['donation_value_max']
) {
$valid = FALSE;
$messages[] =
E::ts("Maximum donation value must be higher than the minimum");
}
elseif (
// Default < min or default > max
$this->values['project_options']['donation_value_default'] <
$this->values['project_options']['donation_value_min'] ||
$this->values['project_options']['donation_value_default'] >
$this->values['project_options']['donation_value_max']
) {
$valid = FALSE;
$messages[] =
E::ts("Default donation value must lie in between maximum and minimum values");
}
}
}
else {
$valid = FALSE;
$messages[] =
E::ts("Donation values (Min, Max, Default) must be positive integers");
}
// Validate sharing url
$urlFields =
[
'custom_css',
'share_url',
];
foreach ($urlFields as $urlField) {
if (!empty($this->values['project_options'][$urlField])) {
if (
!filter_var(
$this->values['project_options'][$urlField],
FILTER_VALIDATE_URL
) || empty($this->values['project_options'][$urlField])
) {
$valid = FALSE;
$messages[] =
E::ts("Invalid URL: %1", [1 => $urlField]);
}
}
}
return ['valid' => $valid, 'messages' => $messages];
}
/** /**
* ## Translate values between CiviCRM Campaigns and Twingle formats * ## Translate values between CiviCRM Campaigns and Twingle formats
@ -246,7 +430,8 @@ class CRM_TwingleCampaign_BAO_TwingleProject extends Campaign {
* *
* @throws Exception * @throws Exception
*/ */
public static function formatValues(array &$values, string $direction) { public
static function formatValues(array &$values, string $direction) {
if ($direction == self::IN) { if ($direction == self::IN) {
@ -467,7 +652,8 @@ class CRM_TwingleCampaign_BAO_TwingleProject extends Campaign {
* @return array * @return array
* *
*/ */
public function getResponse(string $status = NULL): array { public
function getResponse(string $status = NULL): array {
$project_type = empty($this->values['type']) ? 'default' : $this->values['type']; $project_type = empty($this->values['type']) ? 'default' : $this->values['type'];
$response = $response =
[ [
@ -489,7 +675,8 @@ class CRM_TwingleCampaign_BAO_TwingleProject extends Campaign {
* *
* @return int|string|null * @return int|string|null
*/ */
public function lastUpdate() { public
function lastUpdate(): ?int {
return self::getTimestamp($this->values['last_update']); return self::getTimestamp($this->values['last_update']);
} }
@ -500,8 +687,72 @@ class CRM_TwingleCampaign_BAO_TwingleProject extends Campaign {
* *
* @return int * @return int
*/ */
public function getProjectId(): int { public
function getProjectId(): int {
return (int) $this->values['id']; return (int) $this->values['id'];
} }
/**
* ## Get the payment methods array of this project
*
* @return array
*/
public function getValues(): array {
if (isset($this->values)) {
return $this->values;
}
else {
return [];
}
}
/**
* ## Get the project options array of this project
*
* @return array
*/
public function getOptions(): array {
if (isset($this->values['project_options'])) {
return $this->values['project_options'];
}
else {
return [];
}
}
/**
* ## Get the payment methods array of this project
*
* @return array
*/
public function getPaymentMethods(): array {
if (isset($this->values['payment_methods'])) {
return $this->values['payment_methods'];
}
else {
return [];
}
}
/**
* ## Get the payment methods array of this project
*/
public function deleteValues(): void {
unset ($this->values);
}
/**
* ## Get the project options array of this project
*/
public function deleteOptions(): void {
unset($this->values['project_options']);
}
/**
* ## Get the payment methods array of this project
*/
public function deletePaymentMethods(): void {
unset($this->values['payment_methods']);
}
} }

View file

@ -131,6 +131,20 @@ function civicrm_api3_twingle_project_Sync(array $params): array {
// instantiate project // instantiate project
$project = new TwingleProject($result, $id); $project = new TwingleProject($result, $id);
// Send project information to Tingle and update project with the
// answer
$projectOptions = $project->getOptions();
$project->deleteOptions();
$paymentMethods = $project->getPaymentMethods();
$project->deletePaymentMethods();
$projectFromTwingle = $twingleApi->pushProject($project->export());
$project = new TwingleProject($projectFromTwingle, $project->getId());
$projectValues = $project->getValues();
$projectValues['project_options'] = $projectOptions;
$projectValues['payment_methods'] = $paymentMethods;
$project->update($projectValues);
$project->complement($projectFromTwingle);
// Push project to Twingle // Push project to Twingle
return _pushProjectToTwingle($project, $twingleApi, $params); return _pushProjectToTwingle($project, $twingleApi, $params);
} }
@ -342,17 +356,19 @@ function _updateProjectLocally(array $project_from_twingle,
* @param \CRM_TwingleCampaign_BAO_TwingleProject $project * @param \CRM_TwingleCampaign_BAO_TwingleProject $project
* @param \CRM_TwingleCampaign_BAO_TwingleApiCall $twingleApi * @param \CRM_TwingleCampaign_BAO_TwingleApiCall $twingleApi
* @param array $params * @param array $params
* @param bool $update Update project after push?
* *
* @return array * @return array
* @throws \CiviCRM_API3_Exception * @throws \CiviCRM_API3_Exception
*/ */
function _pushProjectToTwingle(TwingleProject $project, function _pushProjectToTwingle(TwingleProject $project,
TwingleApiCall $twingleApi, TwingleApiCall $twingleApi,
array $params): array { array $params = [],
bool $update = TRUE): array {
// If this is a test, do not make db changes // If this is a test, do not make db changes
if ($params['is_test']) { if (isset($params['is_test']) && $params['is_test']) {
$response[$project->getId] = $response[$project->getId()] =
$project->getResponse('TwingleProject ready to push to Twingle'); $project->getResponse('TwingleProject ready to push to Twingle');
return civicrm_api3_create_success( return civicrm_api3_create_success(
$response, $response,
@ -379,6 +395,7 @@ function _pushProjectToTwingle(TwingleProject $project,
} }
// Update local campaign with data returning from Twingle // Update local campaign with data returning from Twingle
if ($update) {
if ($result) { if ($result) {
$project->update($result); $project->update($result);
try { try {
@ -419,6 +436,17 @@ function _pushProjectToTwingle(TwingleProject $project,
); );
} }
} }
else {
$response[$project->getId()] =
$project->getResponse('TwingleProject pushed to Twingle');
return civicrm_api3_create_success(
$response,
$params,
'TwingleProject',
'Sync'
);
}
}
/** /**

View file

@ -1,6 +1,8 @@
<?php <?php
use CRM_TwingleCampaign_Utils_ExtensionCache as ExtensionCache; use CRM_TwingleCampaign_Utils_ExtensionCache as ExtensionCache;
use CRM_TwingleCampaign_BAO_TwingleProject as TwingleProject;
use CRM_TwingleCampaign_BAO_TwingleApiCall as TwingleApiCall;
use CRM_TwingleCampaign_ExtensionUtil as E; use CRM_TwingleCampaign_ExtensionUtil as E;
require_once 'twinglecampaign.civix.php'; require_once 'twinglecampaign.civix.php';
@ -9,6 +11,7 @@ require_once 'twinglecampaign.civix.php';
* Implements hook_civicrm_config(). * Implements hook_civicrm_config().
* *
* @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config/ * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_config/
*
* @param $config * @param $config
*/ */
function twinglecampaign_civicrm_config(&$config) { function twinglecampaign_civicrm_config(&$config) {
@ -63,15 +66,20 @@ function twinglecampaign_civicrm_postSave_civicrm_campaign($dao) {
// If the db transaction is still running, add a function to it that will // If the db transaction is still running, add a function to it that will
// be called afterwards // be called afterwards
if (CRM_Core_Transaction::isActive()) { if (CRM_Core_Transaction::isActive()) {
if (_validateAndSendInput($dao->id, $dao->campaign_type_id)) {
CRM_Core_Transaction::addCallback( CRM_Core_Transaction::addCallback(
CRM_Core_Transaction::PHASE_POST_COMMIT, CRM_Core_Transaction::PHASE_POST_COMMIT,
'twinglecampaign_postSave_campaign_callback', 'twinglecampaign_postSave_campaign_update_callback',
[$dao->id, $dao->campaign_type_id] [$dao->id, $dao->campaign_type_id]
); );
} }
}
// If the transaction is already finished, call the function directly // If the transaction is already finished, call the function directly
else { else {
twinglecampaign_postSave_campaign_callback($dao->id, $dao->campaign_type_id); twinglecampaign_postSave_campaign_update_callback($dao->id, $dao->campaign_type_id);
} }
} }
@ -89,21 +97,13 @@ function twinglecampaign_civicrm_postSave_civicrm_campaign($dao) {
* *
* @throws \CiviCRM_API3_Exception * @throws \CiviCRM_API3_Exception
*/ */
function twinglecampaign_postSave_campaign_callback ( function twinglecampaign_postSave_campaign_update_callback(
int $campaign_id, int $campaign_id,
int $campaign_type_id int $campaign_type_id
) { ) {
// Get campaign type id for TwingleProject $twingle_project_campaign_type_id = _get_campaign_type_id_twingle_project();
$twingle_project_campaign_type_id = $twingle_campaign_campaign_type_id = _get_campaign_type_id_twingle_campaign();
ExtensionCache::getInstance()
->getCampaigns()['campaign_types']['twingle_project']['id'];
// Get campaign type id for TwingleCampaign
$twingle_campaign_campaign_type_id =
ExtensionCache::getInstance()
->getCampaigns()['campaign_types']['twingle_campaign']['id'];
// If $campaign_type_id is a TwingleProject or TwingleCampaign campaign, // If $campaign_type_id is a TwingleProject or TwingleCampaign campaign,
// synchronize it // synchronize it
@ -121,15 +121,14 @@ function twinglecampaign_postSave_campaign_callback (
} }
if (isset($_POST['action'])) { if (isset($_POST['action'])) {
if ($_POST['action'] == 'clone') { if ($_POST['action'] == 'clone' && $entity == 'TwingleProject') {
unset($_POST['action']); unset($_POST['action']);
$result = civicrm_api3($entity, 'getsingle', $result = civicrm_api3('TwingleProject', 'getsingle',
['id' => $campaign_id] ['id' => $campaign_id]
)['values'][$campaign_id]; );
$className = 'CRM_TwingleCampaign_BAO_' . $entity;
$id = $result['id']; $id = $result['id'];
unset($result['id']); unset($result['id']);
$project = new $className($result, $id); $project = new TwingleProject($result, $id);
try { try {
$project->clone(); $project->clone();
} catch (Exception $e) { } catch (Exception $e) {
@ -137,23 +136,42 @@ function twinglecampaign_postSave_campaign_callback (
E::LONG_NAME . E::LONG_NAME .
' could not clone ' . $entity . ': ' . $e->getMessage() ' could not clone ' . $entity . ': ' . $e->getMessage()
); );
CRM_Utils_System::setUFMessage($entity . ' could not get cloned.'); CRM_Core_Session::setStatus(
$e->getMessage(),
E::ts("Campaign cloning failed"),
error,
[unique => TRUE]
);
} }
} }
}
// If a TwingleProject is getting saved
elseif ($entity == 'TwingleProject') { elseif ($entity == 'TwingleProject') {
// Synchronize all child TwingleCampaign campaigns
try { try {
civicrm_api3('TwingleProject', 'sync', ['id' => $campaign_id]); civicrm_api3(
CRM_Utils_System::setUFMessage('TwingleProject was saved.'); 'TwingleCampaign',
'sync',
['parent_id' => $campaign_id]);
} catch (CiviCRM_API3_Exception $e) { } catch (CiviCRM_API3_Exception $e) {
CRM_Core_Session::setStatus(
$e->getMessage(),
E::ts("TwingleCampaign update failed"),
error, [unique => TRUE]
);
Civi::log()->error( Civi::log()->error(
'twinglecampaign_postSave_callback ' . $e->getMessage() E::SHORT_NAME .
' Update of TwingleCampaigns failed: ' . $e->getMessage()
); );
} }
} }
else { else {
try { try {
civicrm_api3('TwingleCampaign', 'create', ['id' => $campaign_id]); civicrm_api3('TwingleCampaign', 'create',
CRM_Utils_System::setUFMessage('TwingleCampaign was saved.'); ['id' => $campaign_id, 'parent_id' => $_POST['parent_id']]);
CRM_Utils_System::setUFMessage(E::ts('TwingleCampaign was saved.'));
} catch (CiviCRM_API3_Exception $e) { } catch (CiviCRM_API3_Exception $e) {
Civi::log()->error( Civi::log()->error(
'twinglecampaign_postSave_callback ' . $e->getMessage() 'twinglecampaign_postSave_callback ' . $e->getMessage()
@ -161,18 +179,7 @@ function twinglecampaign_postSave_campaign_callback (
} }
} }
} }
elseif ($entity == 'TwingleProject') {
// Also synchronize all child TwingleCampaign campaigns
try {
civicrm_api3('TwingleCampaign', 'sync', ['project_id' => $campaign_id]);
} catch (CiviCRM_API3_Exception $e) {
Civi::log()->error(
'twinglecampaign_postSave_callback ' . $e->getMessage()
);
} }
try {
civicrm_api3('TwingleProject', 'sync', ['id' => $campaign_id]);
CRM_Utils_System::setUFMessage('TwingleProject was saved.');
function _get_campaign_type_id_twingle_project() { function _get_campaign_type_id_twingle_project() {
return ExtensionCache::getInstance() return ExtensionCache::getInstance()
@ -184,11 +191,177 @@ function _get_campaign_type_id_twingle_campaign() {
->getCampaignIds()['campaign_types']['twingle_campaign']['id']; ->getCampaignIds()['campaign_types']['twingle_campaign']['id'];
} }
'twinglecampaign_postSave_callback ' . $e->getMessage() /**
* Callback to sync a project after its creation.
* @param int $campaign_id
*/
function twinglecampaign_postSave_project_create_callback(
int $campaign_id
) {
try {
civicrm_api3(
'TwingleProject',
'sync',
['id' => $campaign_id]);
} catch (Exception $e) {
CRM_Core_Session::setStatus(
$e->getMessage(),
E::ts("TwingleProject creation failed"),
error, [unique => TRUE]
);
Civi::log()->error(
E::SHORT_NAME .
' Update of TwingleProject creation failed: ' . $e->getMessage()
); );
} }
} }
/**
* First validate and then sends the input of this transaction to Twinge.
* If the call to the Twingle API succeeded, this function returns TRUE;
*
* @param $id
* @param $campaign_type_id
*
* @return bool
* @throws \CiviCRM_API3_Exception
*/
function _validateAndSendInput($id, $campaign_type_id): bool {
// Set callback for cloning
if (isset($_POST['action'])) {
CRM_Core_Transaction::addCallback(
CRM_Core_Transaction::PHASE_POST_COMMIT,
'twinglecampaign_postSave_campaign_update_callback',
[$id, $campaign_type_id]
);
return FALSE;
} }
if ($campaign_type_id == _get_campaign_type_id_twingle_project()) {
// Instantiate project
$project = new TwingleProject();
// Translate custom fields from $_POST
$customFields = [];
$customFieldsKeys = preg_grep('/^custom_/', array_keys($_POST));
foreach ($customFieldsKeys as $key) {
$customFields[preg_replace('/_-?\d*$/', '', $key)] =
$_POST[$key];
}
$project->translateCustomFields(
$customFields,
TwingleProject::OUT
);
TwingleProject::formatValues($customFields, TwingleProject::OUT);
// Update project
$project->update($customFields);
// Validate project values
$validation = $project->validate();
// If the input is valid, send it to Twingle
if ($validation['valid']) {
// Try to retrieve twingleApi from cache or create a new
$twingleApi = Civi::cache()->get('twinglecampaign_twingle_api');
if (NULL === $twingleApi) {
try {
$twingleApi =
new TwingleApiCall(Civi::settings()->get('twingle_api_key'));
} catch (Exception $e) {
// Roll back transaction if input validation failed
CRM_Core_Transaction::rollbackIfFalse(FALSE);
CRM_Core_Session::setStatus(
$e->getMessage(),
E::ts("Could not retrieve Twingle API key"),
error,
[unique => TRUE]
);
Civi::log()->error(
E::SHORT_NAME .
' Could not retrieve Twingle API key: ' . $e->getMessage()
);
}
Civi::cache('long')->set('twinglecampaign_twingle_api', $twingleApi);
}
try {
// Complement project values with values from Twingle if it has a
// project_id
if ($project->getProjectId()) {
$project_from_twingle = $twingleApi->getProject($project->getProjectId());
$project->complement($project_from_twingle);
}
// If this campaign is just about to become created, add a callback to
// sync it after the transaction has finished
else {
CRM_Core_Transaction::addCallback(
CRM_Core_Transaction::PHASE_POST_COMMIT,
'twinglecampaign_postSave_project_create_callback', [$id]
);
return FALSE;
}
// Push project
require E::path() . '/api/v3/TwingleProject/Sync.php';
$result = _pushProjectToTwingle($project, $twingleApi, [], FALSE);
if ($result['is_error'] != 0) {
throw new \CiviCRM_API3_Exception($result['error_message']);
}
} catch (Exception $e) {
// Roll back transaction if input validation failed
CRM_Core_Transaction::rollbackIfFalse(FALSE);
// Display and log error message
CRM_Core_Session::setStatus(
$e->getMessage(),
E::ts("TwingleProject synchronization failed: %1",
[1 => $e->getMessage()]),
error,
[unique => TRUE]
);
Civi::log()->error(
E::SHORT_NAME .
' TwingleProject synchronization failed: ' . $e->getMessage()
);
// Push failed
return FALSE;
}
// Push succeeded
return TRUE;
}
// Display error message if validation failed
else {
// Roll back transaction if input validation failed
CRM_Core_Transaction::rollbackIfFalse(FALSE);
// Build error message
$errorMessage = '<ul>';
foreach ($validation['messages'] as $message) {
$errorMessage = $errorMessage . '<li>' . $message . '</li>';
}
$errorMessage = $errorMessage . '</ul>';
CRM_Core_Session::setStatus(
$errorMessage,
E::ts("Input validation failed"),
error,
[unique => TRUE]
);
// Validation failed
return FALSE;
}
}
// TwingleCampaigns always return TRUE;
return TRUE;
} }