diff --git a/CRM/TwingleCampaign/BAO/Campaign.php b/CRM/TwingleCampaign/BAO/Campaign.php index 71f0245..593a358 100644 --- a/CRM/TwingleCampaign/BAO/Campaign.php +++ b/CRM/TwingleCampaign/BAO/Campaign.php @@ -104,12 +104,11 @@ abstract class CRM_TwingleCampaign_BAO_Campaign { return TRUE; } - /** * ## Update instance values * This method updates the **$values** array of this instance with the values * from the provided array if they are defined in - * *CRM/TwingleCampaign/resources/twingle_api_templates.json* + * *CRM/TwingleCampaign/resources/twingle_api_templates.php* * * @param array $values * Array with values to update @@ -118,47 +117,33 @@ abstract class CRM_TwingleCampaign_BAO_Campaign { // Update campaign values $filter = Cache::getInstance()->getTemplates()[$this->className]; foreach ($values as $key => $value) { - if ($this->className == "TwingleProject" && $key == 'project_id') { - $this->values['id'] = $value; - } - elseif (in_array($key, $filter)) { + if ($this->in_array_r($key, $filter)) { $this->values[$key] = $value; } - elseif ($key == 'embed') { - self::setEmbedData($value); - } - elseif ($key == 'counter-url') { - self::setCounterUrl($value['url']); - } } } - /** - * ## Set embed data fields - * This method sets all embed data fields that are defined in - * *CRM/TwingleCampaign/resources/twingle_api_templates.json* - * - * @param array $embedData - * Array with embed data from Twingle API + * ## Complement campaign values + * Complement existing campaign values with new ones + * @param array $arrayToComplement */ - public function setEmbedData(array $embedData) { + public function complement(array $arrayToComplement) { + $this->complement_r($this->values, $arrayToComplement); + $this->values = $arrayToComplement; + } - // Get all embed_data keys from template - $embed_data_keys = Cache::getInstance() - ->getTemplates()['project_embed_data']; - - // Transfer all embed_data values - foreach ($embed_data_keys as $key) { - if (array_key_exists($key, $embedData)) { - $this->values[$key] = $embedData[$key]; + private function complement_r($orig, &$fill) { + foreach ($orig as $key => $value) { + if (is_array($value)) { + $this->complement_r($orig[$key], $fill[$key]); + } else { + $fill[$key] = $value; } } } - - public abstract function formatValues(array &$values, string $direction); - + public static abstract function formatValues(array &$values, string $direction); /** * ## Translate array keys between CiviCRM Campaigns and Twingle diff --git a/CRM/TwingleCampaign/BAO/TwingleApiCall.php b/CRM/TwingleCampaign/BAO/TwingleApiCall.php index 5d6d037..cb3999d 100644 --- a/CRM/TwingleCampaign/BAO/TwingleApiCall.php +++ b/CRM/TwingleCampaign/BAO/TwingleApiCall.php @@ -1,6 +1,5 @@ protocol . 'project' . $this->baseUrl . 'by-organisation/' . $this->organisationId - : $this->protocol . 'project' . $this->baseUrl . $projectId; + // If no project id is provided, return all projects + if (empty($projectId)) { + $response = []; + $projects = $this->curlGet($this->protocol . 'project' . + $this->baseUrl . 'by-organisation/' . $this->organisationId); + foreach ($projects as $project) { + $response[] = $this->getProject($project['id']); + } + return $response; + } + // If a project id is provided, return only one project + else { - return $this->curlGet($url); + // Get all general project information + $project = $this->curlGet($this->protocol . 'project' . + $this->baseUrl . $projectId); + + // Get project options + $project['project_options'] = $this->getProjectOptions($projectId); + + // Get project payment methods + $project['payment_methods'] = + $this->getProjectPaymentMethods($projectId); + + // Set last update time + $project['last_update'] = max( + $project['last_update'], + $project['project_options']['last_update'], + $project['payment_methods']['updated_at'] + ); + unset($project['project_options']['last_update']); + unset($project['payment_methods']['updated_at']); + + return $project; + } } /** @@ -92,30 +121,67 @@ class CRM_TwingleCampaign_BAO_TwingleApiCall { * * Returns an array with all project values. * - * @param TwingleProject $project - * The TwingleProject object that should get pushed to Twingle + * @param array $project + * The project values array that should get pushed to Twingle * * @return array * @throws \Exception */ - public function pushProject(TwingleProject &$project): array { + public function pushProject(array $project): array { - // Get only those values from the TwingleProject object which Twingle expects - $values = $project->export(); + $projectOptions = $project['project_options']; + unset($project['project_options']); + $paymentMethods = $project['payment_methods']; + unset($project['payment_methods']); - // Prepare url for curl - if ($values['id']) { - $url = $this->protocol . 'project' . $this->baseUrl . $values['id']; - } - else { - $url = $this->protocol . 'project' . $this->baseUrl . 'by-organisation/' . - $this->organisationId; + try { + if (!isset($project['id'])) { + $url = $this->protocol . 'project' . $this->baseUrl . 'by-organisation/' . + $this->organisationId; + + // Post project values + $updatedProject = $this->curlPost($url, $project); + + $url = $this->protocol . 'project' . $this->baseUrl . + $updatedProject['id']; + } + else { + $url = $this->protocol . 'project' . $this->baseUrl . $project['id']; + + // Post project values + $updatedProject = $this->curlPost($url, $project); + } + + // Post project_options + $updatedProject['project_options'] = + $this->curlPost($url . '/options', $projectOptions); + + // Post payment_methods + $this->curlPost($url . '/payment-methods', $paymentMethods); + $updatedProject['payment_methods'] = + $this->getProjectPaymentMethods($updatedProject['id']); + + // Set last update time + $updatedProject['last_update'] = max( + $updatedProject['last_update'], + $updatedProject['project_options']['last_update'], + $updatedProject['payment_methods']['updated_at'] + ); + unset($updatedProject['project_options']['last_update']); + unset($updatedProject['payment_methods']['updated_at']); + + return $updatedProject; + } catch (Exception $e) { + throw new Exception( + E::SHORT_NAME . 'Call to Twingle API failed: ' . + $e->getMessage() + ); } - // Send curl and return result - return $this->curlPost($url, $values); + } + /** * ## Get event from Twingle * Returns all events for the provided **$projectId** or a single event if an @@ -197,6 +263,33 @@ class CRM_TwingleCampaign_BAO_TwingleApiCall { } } + /** + * ## Get project options + * Gets all project options from the Twingle API + * @param $projectId + * + * @return array + * @throws \Exception + */ + public function getProjectOptions($projectId): array { + $url = $this->protocol . 'project' . $this->baseUrl . $projectId . '/options'; + return $this->curlGet($url); + } + + /** + * ## Get project payment methods + * Gets all project payment methods from the Twingle API + * @param $projectId + * + * @return array + * @throws \Exception + */ + public function getProjectPaymentMethods($projectId): array { + $url = $this->protocol . 'project' . $this->baseUrl . $projectId + . '/payment-methods'; + return $this->curlGet($url); + } + /** * ## Delete Project diff --git a/api/v3/TwingleProject/Sync.php b/api/v3/TwingleProject/Sync.php index db1ebb2..b55f8a3 100644 --- a/api/v3/TwingleProject/Sync.php +++ b/api/v3/TwingleProject/Sync.php @@ -189,11 +189,6 @@ function civicrm_api3_twingle_project_Sync(array $params): array { $project = new TwingleProject($project_from_twingle); try { - // Get embed data - $project->setEmbedData( - $twingleApi->getProjectEmbedData($project->getProjectId()) - ); - // If this is a test, do not make db changes if (isset($params['is_test']) && $params['is_test']) { $returnValues[$project->getId()] = @@ -231,14 +226,18 @@ function civicrm_api3_twingle_project_Sync(array $params): array { $project = new TwingleProject($project_from_civicrm, $id); // sync project - $result = _projectSync($project, $project_from_twingle, $twingleApi, $params); - if ($result['is_error'] != 0) { - $errors_occurred++; + $result = _projectSync( + $project, + $project_from_twingle, + $twingleApi, + $params); + if (!$result['is_error'] == 0) { + $errors[$result['id']] = $result['error_message']; $returnValues[$project->getId()] = $project->getResponse($result['error_message']); } else { - $returnValues[$project->getId()] = $result['values']; + $returnValues[$result['id']] = $result['values'][$result['id']]; } break; } @@ -284,9 +283,7 @@ function _updateProjectLocally(array $project_from_twingle, try { $project->update($project_from_twingle); - $project->setEmbedData( - $twingleApi->getProjectEmbedData($project->getProjectId()) - ); + // If this is a test, do not make db changes if (array_key_exists('is_test', $params) && $params['is_test']) { return civicrm_api3_create_success( @@ -346,7 +343,7 @@ function _pushProjectToTwingle(TwingleProject $project, // Push project to Twingle try { - $result = $twingleApi->pushProject($project); + $result = $twingleApi->pushProject($project->export()); } catch (Exception $e) { Civi::log()->error( E::LONG_NAME . @@ -363,11 +360,7 @@ function _pushProjectToTwingle(TwingleProject $project, // Update local campaign with data returning from Twingle if ($result) { $project->update($result); - // Get embed data try { - $project->setEmbedData( - $twingleApi->getProjectEmbedData($project->getProjectId()) - ); // Create updated campaign $project->create(TRUE); $response = $project->getResponse('TwingleProject pushed to Twingle'); @@ -432,6 +425,17 @@ function _projectSync(TwingleProject $project, // If the CiviCRM TwingleProject campaign was changed, update the project // on Twingle's side elseif ($project_from_twingle['last_update'] < $project->lastUpdate()) { + // Make sure that the project hast a correct project_id. This is important + // to avoid an accidental cloning of project. + if (empty($project->getProjectId())) { + throw new \CiviCRM_API3_Exception( + 'Missing project_id for project that is meant to get updated on Twingle side.'); + } + + // By merging the project values with the values coming from Twingle, we + // make sure that the project contains all values needed to get pushed + $project->complement($project_from_twingle); + return _pushProjectToTwingle($project, $twingleApi, $params); }