diff --git a/CRM/TwingleCampaign/BAO/Campaign.php b/CRM/TwingleCampaign/BAO/Campaign.php new file mode 100644 index 0000000..89fb9bf --- /dev/null +++ b/CRM/TwingleCampaign/BAO/Campaign.php @@ -0,0 +1,624 @@ +className = get_class($this); + + // If values come from CiviCRM Campaign API + if ($origin == self::CIVICRM) { + + // Set id (campaign id) attribute + $this->id = $campaign['id']; + + // Translate custom field names into Twingle field names + self::translateCustomFields($campaign, self::OUT); + + // Translate keys and values + self::formatValues($campaign, self::OUT); + self::translateKeys($campaign, self::OUT); + + } + + // Set project values attribute + $this->values = $campaign; + + } + + + /** + * Check if a campaign already exists and if so set attributes + * to the values of the existing campaign. + * + * @return bool + * Returns TRUE id the campaign already exists + * + * @throws CiviCRM_API3_Exception + * + * @throws Exception + */ + public function exists() { + + $result = []; + $single = FALSE; + + // If there is more than one campaign for this entity, handle the duplicates + while (!$single) { + $result = civicrm_api3('Campaign', 'get', [ + 'sequential' => 1, + 'is_active' => 1, + $this->id_custom_field => $this->values['id'], + ]); + + if ($result['count'] > 1) { + // TODO: abort loop if function fails + self::handleDuplicates($result); + } + else { + $single = TRUE; + } + } + + // If this campaign already exists, get its attributes + if ($result['count'] == 1) { + + // Extract campaign values from result array + $values = $result['values'][0]; + + // Set id attribute + $this->id = $values['id']; + + // Translate custom field names back + self::translateCustomFields($values, self::OUT); + + // Translate keys from CiviCRM format to Twingle format + self::translateKeys($values, self::OUT); + + // Set attributes to the values of the existing TwingleEvent campaign + // to reflect the state of the actual campaign in the database + $this->update($values); + + return TRUE; + } + else { + return FALSE; + } + } + + /** + * Create the Campaign as a campaign in CiviCRM if it does not exist + * + * @param bool $is_test + * If true: don't do any changes + * + * @return array + * Returns a response array that contains title, id, project_id and status + * + * @throws CiviCRM_API3_Exception + * @throws Exception + */ + public function create(bool $is_test = FALSE) { + + // Create campaign only if it does not already exist + if (!$is_test) { + + // Prepare project values for import into database + $values_prepared_for_import = $this->values; + self::formatValues( + $values_prepared_for_import, + self::IN + ); + self::translateKeys( + $values_prepared_for_import, + self::IN + ); + self::translateCustomFields( + $values_prepared_for_import, + self::IN + ); + + // Set id + $values_prepared_for_import['id'] = $this->id; + + // Create campaign + $result = civicrm_api3('Campaign', 'create', $values_prepared_for_import); + + // Update id + $this->id = $result['id']; + + // Check if campaign was created successfully + if ($result['is_error'] == 0) { + $response = $this->getResponse("$this->className created"); + } + else { + $response = $this->getResponse("$this->className creation failed"); + } + + } + // If this is a test, do not create campaign + else { + $response = $this->getResponse("$this->className not yet created"); + } + + return $response; + } + + + /** + * Update an existing campaign + * + * @param array $values + * Array with values to update + * + * @throws Exception + */ + public function update(array $values) { + + // Update campaign values + $this->values = array_merge($this->values, $values); + } + + + /** + * Instantiate an existing campaign by its id + * + * @param $id + * + * @return TwingleProject|TwingleEvent|TwingleCampaign|NULL + * @throws CiviCRM_API3_Exception + * @throws Exception + */ + public static function fetch($id) { + $result = civicrm_api3('Campaign', 'getsingle', [ + 'sequential' => 1, + 'id' => $id, + ]); + + $twingle_campaign_types = Cache::getInstance() + ->getCampaigns()['campaign_types']; + + $twingle_campaign_type_values = []; + + foreach ($twingle_campaign_types as $twingle_campaign_type) { + $twingle_campaign_type_values[$twingle_campaign_type['name']] = + CampaignType::fetch($twingle_campaign_type['name'])->getValue(); + } + + switch ($result->values['campaign_type_id']) { + case $twingle_campaign_type_values['twingle_project']: + return new TwingleProject( + $result['values'], + self::CIVICRM + ); + break; + case $twingle_campaign_type_values['twingle_event']: + return new TwingleEvent( + $result['values'], + self::CIVICRM + ); + case $twingle_campaign_type_values['twingle_campaign']: + return new TwingleCampaign( + $result['values'], + self::CIVICRM + ); + break; + default: + return NULL; + } + } + + + /** + * Deactivate all duplicates of a project but the newest one + * + * @param array $result + * The $result array of a civicrm_api3-get-project call + * + * @throws CiviCRM_API3_Exception + */ + protected function handleDuplicates(array $result) { + + // Sort projects ascending by the value of the last_modified_date + uasort($result['values'], function ($a, $b) { + return $a['last_modified_date'] <=> $b['last_modified_date']; + }); + + // Delete the newest project from array to keep it active + array_shift($result['values']); + + // deactivate the projects + foreach ($result['values'] as $p) { + self::deactivateById($p['id']); + } + + } + + + /** + * Translate array keys between CiviCRM Campaigns and Twingle + * + * @param array $values + * array of which keys shall be translated + * + * @param string $direction + * Campaign::IN -> translate array keys from Twingle format into + * CiviCRM format
+ * Campaign::OUT -> translate array keys from CiviCRM format into + * Twingle format + * + * @throws Exception + */ + public static function translateKeys(array &$values, string $direction) { + + // Get translations for fields + $field_translations = Cache::getInstance()->getTranslations()['fields']; + + // Set the direction of the translation + if ($direction == self::OUT) { + $field_translations = array_flip($field_translations); + } + // Throw error if $direction constant does not match IN or OUT + elseif ($direction != self::IN) { + throw new Exception( + "Invalid Parameter $direction for translateKeys()" + ); + // TODO: use specific exception or create own + } + + // Translate keys + foreach ($field_translations as $origin => $translation) { + $values[$translation] = $values[$origin]; + unset($values[$origin]); + } + } + + + /** + * Translate values between CiviCRM Campaigns and Twingle + * + * @param array $values + * array of which values shall be translated + * + * @param string $direction + * Campaign::IN -> translate array values from Twingle to CiviCRM
+ * Campaign::OUT -> translate array values from CiviCRM to Twingle + * + * @throws Exception + */ + private function formatValues(array &$values, string $direction) { + + if ($direction == self::IN) { + + // Change timestamp into DateTime string + if ($values['last_update']) { + $values['last_update'] = + self::getDateTime($values['last_update']); + } + + // empty project_type to 'default' + if (!$values['type']) { + $values['type'] = 'default'; + } + } + elseif ($direction == self::OUT) { + + // Change DateTime string into timestamp + $values['last_update'] = + self::getTimestamp($values['last_update']); + + // Default project_type to '' + $values['type'] = $values['type'] == 'default' + ? '' + : $values['type']; + + // Cast project target to integer + $values['project_target'] = (int) $values['project_target']; + + } + else { + + throw new Exception( + "Invalid Parameter $direction for formatValues()" + ); + + } + } + + + /** + * Translate between Twingle field names and custom field names + * + * @param array $values + * array of which keys shall be translated + * + * @param string $direction + * Campaign::IN -> translate field names into custom field names
+ * Campaign::OUT -> translate custom field names into Twingle field + * names + * + */ + public static function translateCustomFields(array &$values, string $direction) { + + // Translate from Twingle field name to custom field name + if ($direction == self::IN) { + + foreach (Cache::getInstance() + ->getCustomFieldMapping() as $field => $custom) { + + if (array_key_exists( + str_replace( + 'twingle_project_', + '', + $field + ), + $values) + ) { + + $values[$custom] = $values[str_replace( + 'twingle_project_', + '', + $field + )]; + + unset($values[str_replace( + 'twingle_project_', + '', + $field + )] + ); + } + } + } + // Translate from custom field name to Twingle field name + elseif ($direction == self::OUT) { + + foreach (Cache::getInstance() + ->getCustomFieldMapping() as $field => $custom) { + + if (array_key_exists( + $custom, + $values + ) + ) { + $values[str_replace( + 'twingle_project_', + '', + $field + )] = $values[$custom]; + unset($values[$custom]); + } + } + } + } + + /** + * Set embed data fields + * + * @param array $embedData + * Array with embed data from Twingle API + */ + public function setEmbedData(array $embedData) { + + // 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) { + $this->values[$key] = htmlspecialchars($embedData[$key]); + } + } + + /** + * Set counter url + * + * @param String $counterUrl + * URL of the counter + */ + public function setCounterUrl(string $counterUrl) { + $this->values['counter'] = $counterUrl; + } + + /** + * Deactivate this Campaign campaign + * + * @return bool + * TRUE if deactivation was successful + * + * @throws CiviCRM_API3_Exception + */ + public function deactivate() { + + return self::deactivateByid($this->id); + + } + + /** + * Deactivate a Campaign campaign by ID + * + * @param $id + * ID of the campaign that should get deactivated + * + * @return bool + * TRUE if deactivation was successful + * + * @throws CiviCRM_API3_Exception + */ + public static function deactivateById($id) { + + $result = civicrm_api3('Campaign', 'getsingle', [ + 'id' => $id, + 'sequential' => 1, + ]); + + $result = civicrm_api3('Campaign', 'create', [ + 'title' => $result['title'], + 'id' => $id, + 'is_active' => '0', + ]); + + // Return TRUE if the campaign was deactivated successfully + if ($result['is_error'] == 0) { + return TRUE; + } + // Return FALSE if deactivation failed + else { + return FALSE; + } + + } + + + /** + * Get a response that describes the status of a Campaign + * + * @param string $status + * status of the Campaign you want the response for + * + * @return array + * Returns a response array that contains title, id, project_id and status + */ + public function getResponse(string $status) { + return [ + 'title' => $this->values['name'], + 'id' => (int) $this->id, + 'project_id' => (int) $this->values['id'], + 'project_type' => $this->values['type'], + 'status' => $status, + ]; + } + + + /** + * Validates $input to be either a DateTime string or an Unix timestamp + * + * @param $input + * Pass a DateTime string or a Unix timestamp + * + * @return int + * Returns a Unix timestamp or NULL if $input is invalid + */ + public static function getTimestamp($input) { + + // Check whether $input is a Unix timestamp + if ( + $dateTime = DateTime::createFromFormat('U', $input) + ) { + return $input; + } + // ... or a DateTime string + elseif ( + $dateTime = DateTime::createFromFormat('Y-m-d H:i:s', $input) + ) { + return $dateTime->getTimestamp(); + } + // ... or invalid + else { + return NULL; + } + + } + + + /** + * Validates $input to be either a DateTime string or an Unix timestamp + * + * @param $input + * Pass a DateTime string or a Unix timestamp + * + * @return string + * Returns a DateTime string or NULL if $input is invalid + */ + public static function getDateTime($input) { + + // Check whether $input is a Unix timestamp + if ( + $dateTime = DateTime::createFromFormat('U', $input) + ) { + return $dateTime->format('Y-m-d H:i:s'); + } + // ... or a DateTime string + elseif ( + $dateTime = DateTime::createFromFormat('Y-m-d H:i:s', $input) + ) { + return $input; + } + // ... or invalid + else { + return NULL; + } + + } + + + /** + * Return a timestamp of the last update of the Campaign + * + * @return int|null + */ + public function lastUpdate() { + + return self::getTimestamp($this->values['last_update']); + } + + + /** + * Returns the project_id of a Campaign + * + * @return int + */ + public function getProjectId() { + return $this->values['id']; + } + + + /** + * @return mixed + */ + public function getId() { + return $this->id; + } + +} diff --git a/CRM/TwingleCampaign/BAO/CampaignType.php b/CRM/TwingleCampaign/BAO/CampaignType.php index 261fd75..36f6ed3 100644 --- a/CRM/TwingleCampaign/BAO/CampaignType.php +++ b/CRM/TwingleCampaign/BAO/CampaignType.php @@ -51,8 +51,8 @@ class CampaignType { $this->value = array_column($this->results['values'], 'value')[0]; if ($this->results['is_error'] == 0) { - \Civi::log()->info("Twingle Extension has created a new campaign type.\n - label: $this->label\n + \Civi::log()->info("Twingle Extension has created a new campaign type. + label: $this->label name: $this->name" ); } @@ -104,12 +104,12 @@ class CampaignType { } /** - * @param $name + * @param string $name * * @return \CRM\TwingleCampaign\BAO\CampaignType|null * @throws \CiviCRM_API3_Exception */ - public static function fetch($name) { + public static function fetch(string $name) { $campaign_type = civicrm_api3( 'OptionValue', 'get', diff --git a/CRM/TwingleCampaign/BAO/CustomField.php b/CRM/TwingleCampaign/BAO/CustomField.php index a73ea81..ebc5868 100644 --- a/CRM/TwingleCampaign/BAO/CustomField.php +++ b/CRM/TwingleCampaign/BAO/CustomField.php @@ -89,10 +89,10 @@ class CustomField { // Log field creation if ($this->result['is_error'] == 0) { - \Civi::log()->info("Twingle Extension has created a new custom field.\n - label: $this->label\n - name: $this->name\n - id: $this->id\n + \Civi::log()->info("Twingle Extension has created a new custom field. + label: $this->label + name: $this->name + id: $this->id group: $this->custom_group_id" ); } @@ -224,10 +224,10 @@ class CustomField { // Check if custom field was deleted successfully if ($this->result['is_error'] == 0) { - \Civi::log()->info("Twingle Extension has deleted custom field.\n - label: $this->label\n - name: $this->name\n - id: $this->id\n + \Civi::log()->info("Twingle Extension has deleted custom field. + label: $this->label + name: $this->name + id: $this->id group: $this->custom_group_id" ); } diff --git a/CRM/TwingleCampaign/BAO/CustomGroup.php b/CRM/TwingleCampaign/BAO/CustomGroup.php index 87d2238..91c7529 100644 --- a/CRM/TwingleCampaign/BAO/CustomGroup.php +++ b/CRM/TwingleCampaign/BAO/CustomGroup.php @@ -49,11 +49,11 @@ class CustomGroup { $this->id = $this->results['id']; if ($this->results['is_error'] == 0) { - \Civi::log()->info("Twingle Extension has created a new custom group.\n - title: $this->title\n - name: $this->name\n - extends: $this->extends\n - id: $this->id\n + \Civi::log()->info("Twingle Extension has created a new custom group. + title: $this->title + name: $this->name + extends: $this->extends + id: $this->id column_value: $this->extends_entity_column_value" ); } @@ -134,10 +134,10 @@ class CustomGroup { ); if ($this->results['is_error'] == 0) { - \Civi::log()->info("Twingle Extension has deleted custom group.\n - title: $this->title\n - name: $this->name\n - extends: $this->extends\n + \Civi::log()->info("Twingle Extension has deleted custom group. + title: $this->title + name: $this->name + extends: $this->extends id : $this->id" ); diff --git a/CRM/TwingleCampaign/BAO/TwingleCampaign.php b/CRM/TwingleCampaign/BAO/TwingleCampaign.php index b6ebe28..749d6c4 100644 --- a/CRM/TwingleCampaign/BAO/TwingleCampaign.php +++ b/CRM/TwingleCampaign/BAO/TwingleCampaign.php @@ -4,6 +4,6 @@ namespace CRM\TwingleCampaign\BAO; -class TwingleCampaign { +class TwingleCampaign extends Campaign { } \ No newline at end of file diff --git a/CRM/TwingleCampaign/BAO/TwingleEvent.php b/CRM/TwingleCampaign/BAO/TwingleEvent.php index 1c21ce1..c086d5c 100644 --- a/CRM/TwingleCampaign/BAO/TwingleEvent.php +++ b/CRM/TwingleCampaign/BAO/TwingleEvent.php @@ -3,7 +3,156 @@ namespace CRM\TwingleCampaign\BAO; +use Civi; +use CRM\TwingleCampaign\Utils\ExtensionCache as Cache; +use CRM_TwingleCampaign_ExtensionUtil as E; +use CRM_Utils_Array; +use DateTime; +use CRM\TwingleCampaign\BAO\CustomField as CustomField; +use Exception; +use CiviCRM_API3_Exception; -class TwingleEvent { +include_once E::path() . '/CRM/TwingleCampaign/BAO/CustomField.php'; -} \ No newline at end of file + +class TwingleEvent extends Campaign { + + /** + * TwingleEvent constructor. + * + * @param array $event + * Result array of Twingle API call to + * https://project.twingle.de/api/$project_id/event + * + * @param string $origin + * Origin of the arrays. It can be one of two constants: + * TwingleEvent::TWINGLE|CIVICRM + * + * @throws Exception + */ + public function __construct(array $event, string $origin) { + parent::__construct($event, $origin); + + $this->className = get_class($this); + + // Add value for campaign type + $event['campaign_type_id'] = 'twingle_event'; + + // Get custom field name for event_id + $this->id_custom_field = Cache::getInstance() + ->getCustomFieldMapping()['twingle_event_id']; + + } + + + /** + * Translate values between CiviCRM Campaigns and Twingle + * + * @param array $values + * array of which values shall be translated + * + * @param string $direction + * TwingleEvent::IN -> translate array values from Twingle to CiviCRM
+ * TwingleEvent::OUT -> translate array values from CiviCRM to Twingle + * + * @throws Exception + */ + private function formatValues(array &$values, string $direction) { + + if ($direction == self::IN) { + + // Change timestamp into DateTime string + if ($values['last_update']) { + $values['last_update'] = + self::getDateTime($values['last_update']); + } + + // empty project_type to 'default' + if (!$values['type']) { + $values['type'] = 'default'; + } + } + elseif ($direction == self::OUT) { + + // Change DateTime string into timestamp + $values['last_update'] = + self::getTimestamp($values['last_update']); + + // Default project_type to '' + $values['type'] = $values['type'] == 'default' + ? '' + : $values['type']; + + // Cast project target to integer + $values['project_target'] = (int) $values['project_target']; + + } + else { + + throw new Exception( + "Invalid Parameter $direction for formatValues()" + ); + + } + } + + + /** + * Set embed data fields + * + * @param array $embedData + * Array with embed data from Twingle API + */ + public function setEmbedData(array $embedData) { + + // Get all embed_data keys from template + $embed_data_keys = Cache::getInstance() + ->getTemplates()['event_embed_data']; + + // Transfer all embed_data values + foreach ($embed_data_keys as $key) { + $this->values[$key] = htmlspecialchars($embedData[$key]); + } + } + + + /** + * Get a response that describes the status of a TwingleEvent + * + * @param string $status + * status of the TwingleEvent you want the response for + * + * @return array + * Returns a response array that contains title, id, project_id and status + */ + public function getResponse(string $status) { + return [ + 'title' => $this->values['name'], + 'id' => (int) $this->id, + 'event_id' => (int) $this->values['id'], + 'project_id' => (int) $this->values['project_id'], + 'status' => $status, + ]; + } + + + /** + * Returns the project_id of a TwingleEvent + * + * @return int + */ + public function getProjectId() { + return (int) $this->values['project_id']; + } + + + /** + * Returns the event_id of a TwingleEvent + * + * @return int + */ + public function getEventId() { + return (int) $this->values['id']; + } + +} diff --git a/CRM/TwingleCampaign/BAO/TwingleProject.php b/CRM/TwingleCampaign/BAO/TwingleProject.php index fd8796c..abb2f1f 100644 --- a/CRM/TwingleCampaign/BAO/TwingleProject.php +++ b/CRM/TwingleCampaign/BAO/TwingleProject.php @@ -10,24 +10,7 @@ use Exception; use CiviCRM_API3_Exception; -class TwingleProject { - - // IN means: heading into CiviCRM database - public const IN = 'IN'; - - // OUT means: coming from the CiviCRM database - public const OUT = 'OUT'; - - public const CIVICRM = 'CIVICRM'; - - public const TWINGLE = 'TWINGLE'; - - private static $bInitialized = FALSE; - - private $id; - - private $values; - +class TwingleProject extends Campaign { /** * TwingleProject constructor. @@ -43,101 +26,17 @@ class TwingleProject { * @throws Exception */ public function __construct(array $project, string $origin) { + parent::__construct($project, $origin); - // If values come from CiviCRM Campaign API - if ($origin == self::CIVICRM) { - - // Set id (campaign id) attribute - $this->id = $project['id']; - - // Translate custom field names into Twingle field names - self::translateCustomFields($project, self::OUT); - - // Translate keys and values - self::formatValues($project, self::OUT); - self::translateKeys($project, self::OUT); - - } + $this->className = get_class($this); // Add value for campaign type - $project['campaign_type_id'] = 'twingle_project'; + $this->values['campaign_type_id'] = 'twingle_project'; - // Set project values attribute - $this->values = $project; - } + // Get custom field name for project_id + $this->id_custom_field = Cache::getInstance() + ->getCustomFieldMapping()['twingle_project_id']; - - /** - * Create the TwingleProject as a campaign in CiviCRM if it does not exist - * - * @param bool $is_test - * If true: don't do any changes - * - * @return array - * Returns a response array that contains title, id, project_id and status - * - * @throws CiviCRM_API3_Exception - * @throws Exception - */ - public function create(bool $is_test = FALSE) { - - // Create campaign only if it does not already exist - if (!$is_test) { - - // Prepare project values for import into database - $values_prepared_for_import = $this->values; - self::formatValues( - $values_prepared_for_import, - self::IN - ); - self::translateKeys( - $values_prepared_for_import, - self::IN - ); - self::translateCustomFields( - $values_prepared_for_import, - self::IN - ); - - // Set id - $values_prepared_for_import['id'] = $this->id; - - // Create campaign - $result = civicrm_api3('Campaign', 'create', $values_prepared_for_import); - - // Update id - $this->id = $result['id']; - - // Check if campaign was created successfully - if ($result['is_error'] == 0) { - $response = $this->getResponse('TwingleProject created'); - } - else { - $response = $this->getResponse('TwingleProject creation failed'); - } - - } - // If this is a test, do not create campaign - else { - $response = $this->getResponse('TwingleProject not yet created'); - } - - return $response; - } - - - /** - * Update an existing project - * - * @param array $values - * Array with values to update - * - * @throws Exception - */ - public function update(array $values) { - - // Update project values - $this->values = array_merge($this->values, $values); } @@ -156,7 +55,7 @@ class TwingleProject { self::formatValues($values, self::OUT); // Get template for project - $project = self::$templates['project']; + $project = Cache::getInstance()->getTemplates()['project']; // Replace array items which the Twingle API does not expect foreach ($values as $key => $value) { @@ -169,158 +68,6 @@ class TwingleProject { } - /** - * Check if a TwingleProject campaign already exists and if so set attributes - * to the values of the existing campaign. - * - * @return bool - * Returns TRUE id the TwingleProject campaign already exists - * - * @throws CiviCRM_API3_Exception - * - * @throws Exception - */ - public function exists() { - - $result = []; - $single = FALSE; - - // Get custom field name for project_id - $cf_project_id = Cache::getInstance() - ->getCustomFieldMapping()['twingle_project_id']; - - // If there is more than one campaign for a project, handle the duplicates - while (!$single) { - $result = civicrm_api3('Campaign', 'get', [ - 'sequential' => 1, - 'is_active' => 1, - $cf_project_id => $this->values['id'], - ]); - - if ($result['count'] > 1) { - // TODO: abort loop if function fails - TwingleProject::handleDuplicates($result); - } - else { - $single = TRUE; - } - } - - // If the campaign for the TwingleProject already exists, some of the - // project's attributes must be updated from the campaign - if ($result['count'] == 1) { - - // Extract project values from result array - $values = $result['values'][0]; - - // Set campaign id attribute - $this->id = $values['id']; - - // Translate custom field names back - self::translateCustomFields($values, self::OUT); - - // Translate keys from CiviCRM format to Twingle format - self::translateKeys($values, self::OUT); - - // Set attributes to the values of the existing TwingleProject campaign - // to reflect the state of the actual campaign in the database - $this->update($values); - - return TRUE; - } - else { - return FALSE; - } - } - - - /** - * Instantiate an existing project by campaign id - * - * @param $id - * - * @return TwingleProject - * @throws CiviCRM_API3_Exception - * @throws Exception - */ - public static function fetch($id) { - $result = civicrm_api3('Campaign', 'getsingle', [ - 'sequential' => 1, - 'id' => $id, - ]); - - return new TwingleProject( - $result['values'], - self::CIVICRM - ); - } - - - /** - * Deactivate all duplicates of a project but the newest one - * - * @param array $result - * The $result array of a civicrm_api3-get-project call - * - * @throws CiviCRM_API3_Exception - */ - private function handleDuplicates(array $result) { - - // Sort projects ascending by the value of the last_modified_date - uasort($result['values'], function ($a, $b) { - return $a['last_modified_date'] <=> $b['last_modified_date']; - }); - - // Delete the newest project from array to keep it active - array_shift($result['values']); - - // deactivate the projects - foreach ($result['values'] as $p) { - self::deactivateById($p['id']); - } - - } - - - /** - * Translate array keys between CiviCRM Campaigns and Twingle - * - * @param array $values - * array of which keys shall be translated - * - * @param string $direction - * TwingleProject::IN -> translate array keys from Twingle format into - * CiviCRM format
- * TwingleProject::OUT -> translate array keys from CiviCRM format into - * Twingle format - * - * @throws Exception - */ - public static function translateKeys(array &$values, string $direction) { - - // Get translations for fields - $field_translations = Cache::getInstance()->getTranslations()['fields']; - - // Set the direction of the translation - if ($direction == self::OUT) { - $field_translations = array_flip($field_translations); - } - // Throw error if $direction constant does not match IN or OUT - elseif ($direction != self::IN) { - throw new Exception( - "Invalid Parameter $direction for translateKeys()" - ); - // TODO: use specific exception or create own - } - - // Translate keys - foreach ($field_translations as $origin => $translation) { - $values[$translation] = $values[$origin]; - unset($values[$origin]); - } - } - - /** * Translate values between CiviCRM Campaigns and Twingle * @@ -373,72 +120,6 @@ class TwingleProject { } - /** - * Translate between Twingle field names and custom field names - * - * @param array $values - * array of which keys shall be translated - * - * @param string $direction - * TwingleProject::IN -> translate field names into custom field names
- * TwingleProject::OUT -> translate custom field names into Twingle field - * names - * - */ - public static function translateCustomFields(array &$values, string $direction) { - - // Translate from Twingle field name to custom field name - if ($direction == self::IN) { - - foreach (Cache::getInstance() - ->getCustomFieldMapping() as $field => $custom) { - - if (array_key_exists( - str_replace( - 'twingle_project_', - '', - $field - ), - $values) - ) { - - $values[$custom] = $values[str_replace( - 'twingle_project_', - '', - $field - )]; - - unset($values[str_replace( - 'twingle_project_', - '', - $field - )] - ); - } - } - } - // Translate from custom field name to Twingle field name - elseif ($direction == self::OUT) { - - foreach (Cache::getInstance() - ->getCustomFieldMapping() as $field => $custom) { - - if (array_key_exists( - $custom, - $values - ) - ) { - $values[str_replace( - 'twingle_project_', - '', - $field - )] = $values[$custom]; - unset($values[$custom]); - } - } - } - } - /** * Set embed data fields * @@ -481,41 +162,6 @@ class TwingleProject { } - /** - * Deactivate a TwingleProject campaign by ID - * - * @param $id - * ID of the TwingleProject campaign that should get deactivated - * - * @return bool - * TRUE if deactivation was successful - * - * @throws CiviCRM_API3_Exception - */ - public static function deactivateById($id) { - - $result = civicrm_api3('Campaign', 'getsingle', [ - 'id' => $id, - 'sequential' => 1, - ]); - - $result = civicrm_api3('Campaign', 'create', [ - 'title' => $result['title'], - 'id' => $id, - 'is_active' => '0', - ]); - - // Return TRUE if TwingleProject campaign was deactivated successfully - if ($result['is_error'] == 0) { - return TRUE; - } - // Return FALSE if deactivation failed - else { - return FALSE; - } - - } - /** * Get a response that describes the status of a TwingleProject @@ -537,94 +183,13 @@ class TwingleProject { } - /** - * Validates $input to be either a DateTime string or an Unix timestamp - * - * @param $input - * Pass a DateTime string or a Unix timestamp - * - * @return int - * Returns a Unix timestamp or NULL if $input is invalid - */ - public static function getTimestamp($input) { - - // Check whether $input is a Unix timestamp - if ( - $dateTime = DateTime::createFromFormat('U', $input) - ) { - return $input; - } - // ... or a DateTime string - elseif ( - $dateTime = DateTime::createFromFormat('Y-m-d H:i:s', $input) - ) { - return $dateTime->getTimestamp(); - } - // ... or invalid - else { - return NULL; - } - - } - - - /** - * Validates $input to be either a DateTime string or an Unix timestamp - * - * @param $input - * Pass a DateTime string or a Unix timestamp - * - * @return string - * Returns a DateTime string or NULL if $input is invalid - */ - public static function getDateTime($input) { - - // Check whether $input is a Unix timestamp - if ( - $dateTime = DateTime::createFromFormat('U', $input) - ) { - return $dateTime->format('Y-m-d H:i:s'); - } - // ... or a DateTime string - elseif ( - $dateTime = DateTime::createFromFormat('Y-m-d H:i:s', $input) - ) { - return $input; - } - // ... or invalid - else { - return NULL; - } - - } - - - /** - * Return a timestamp of the last update of the TwingleProject - * - * @return int|null - */ - public function lastUpdate() { - - return self::getTimestamp($this->values['last_update']); - } - - /** * Returns the project_id of a TwingleProject * * @return int */ public function getProjectId() { - return $this->values['id']; - } - - - /** - * @return mixed - */ - public function getId() { - return $this->id; + return (int) $this->values['id']; } } diff --git a/CRM/TwingleCampaign/resources/twingle_api_templates.json b/CRM/TwingleCampaign/resources/twingle_api_templates.json index 2e5fe85..620ed40 100644 --- a/CRM/TwingleCampaign/resources/twingle_api_templates.json +++ b/CRM/TwingleCampaign/resources/twingle_api_templates.json @@ -19,5 +19,16 @@ "eventlist" ], "event": [ + ], + "event_embed_data": [ + "page", + "eventpage", + "widget", + "form", + "widget-single", + "form-single", + "eventall", + "eventlist", + "eventeditcreate" ] } \ No newline at end of file diff --git a/api/v3/TwingleSync/BAO/TwingleApiCall.php b/api/v3/TwingleSync/BAO/TwingleApiCall.php index d71ad4e..9ee3b30 100644 --- a/api/v3/TwingleSync/BAO/TwingleApiCall.php +++ b/api/v3/TwingleSync/BAO/TwingleApiCall.php @@ -54,7 +54,8 @@ class TwingleApiCall { * If $id parameter is empty, this function returns all projects for all * organisations this API key is assigned to. * - * TODO: Keys can only get assigned to one organisation. Save multiple keys in settings instead. + * TODO: Keys can only get assigned to one organisation. Save multiple keys + * in settings instead. * * If $id parameter is given, this function returns a single project. * @@ -184,7 +185,7 @@ class TwingleApiCall { $result['status'] = $result['status'] == 'TwingleProject created' ? 'TwingleProject updated' : 'TwingleProject Update failed'; - } catch (Exception $e){ + } catch (Exception $e) { // Log Exception Civi::log()->error( "Could not update TwingleProject campaign: $e->getMessage()" @@ -304,8 +305,98 @@ class TwingleApiCall { } + /** + * @param array $values + * @param bool $isTest + * + * @return array | NULL + * @throws \CiviCRM_API3_Exception + */ + public function syncEvent(array $values, bool $isTest) { + // If $event is an array + if (is_array($values)) { - public function updateEvent() { + // Instantiate TwingleEvent + try { + $event = new TwingleEvent( + $values, + TwingleEvent::TWINGLE + ); + } catch (Exception $e) { + + // Log Exception + Civi::log()->error( + "Failed to instantiate TwingleEvent: $e->getMessage()" + ); + + // Return result array with error description + return [ + "title" => $values['description'], + "event_id" => (int) $values['id'], + "project_id" => (int) $values['id'], + "status" => + "Failed to instantiate TwingleEvent: $e->getMessage()", + ]; + } + + // Check if the TwingleProject campaign already exists + if (!$event->exists()) { + + // ... if not, get embed data and create project + try { + $this->getEmbedData($event); // TODO: ! + $result = $event->create($is_test); + } catch (Exception $e) { + + // Log Exception + Civi::log()->error( + "Could not create campaign from TwingleEvent: $e->getMessage()" + ); + + // Return result array with error description + return [ + "title" => $values['description'], + "event_id" => (int) $values['id'], + "project_id" => (int) $values['id'], + "Could not create campaign from TwingleEvent: $e->getMessage()", + ]; + } + } + else { + $result = $event->getResponse('TwingleEvent exists'); + + // If Twingle's version of the event is newer than the CiviCRM + // TwingleEvent campaign update the campaign + if ($values['last_update'] > $event->lastUpdate()) { + try { + $event->update($values); + $this->getEmbedData($event); // TODO: ! + $result = $event->create(); + $result['status'] = $result['status'] == 'TwingleEvent created' + ? 'TwingleEvent updated' + : 'TwingleEvent Update failed'; + } catch (Exception $e) { + // Log Exception + Civi::log()->error( + "Could not update TwingleEvent campaign: $e->getMessage()" + ); + // Return result array with error description + $result = $event->getResponse( + "Could not update TwingleProject campaign: $e->getMessage()" + ); + } + } + elseif ($result['status'] == 'TwingleEvent exists') { + $result = $event->getResponse('TwingleEvent up to date'); + } + } + + // Return a response of the synchronization + return $result; + } + else { + return NULL; + } } @@ -382,10 +473,10 @@ class TwingleApiCall { */ private function getResultArray($values, $status) { return [ - "title" => $values['name'], - "project_id" => (int) $values['id'], + "title" => $values['name'], + "project_id" => (int) $values['id'], "project_type" => $values['project_type'], - "status" => $status + "status" => $status, ]; } diff --git a/api/v3/TwingleSync/Post.php b/api/v3/TwingleSync/Post.php index 0b214dc..1bf8264 100644 --- a/api/v3/TwingleSync/Post.php +++ b/api/v3/TwingleSync/Post.php @@ -55,7 +55,7 @@ function civicrm_api3_twingle_sync_Post($params) { : $params['twingle_api_key']; $twingleApi = new TwingleApiCall($apiKey); - // Get all projects from Twingle and store them in $projects + // Get all projects from Twingle $projects = $twingleApi->getProject(); // Create projects as campaigns if they do not exist and store results in @@ -68,5 +68,22 @@ function civicrm_api3_twingle_sync_Post($params) { } } + // Get all events from projects of event type + foreach ($result_values['sync']['projects'] as $project) { + if ($project['project_type'] == 'event') { + $events = $twingleApi->getEvent($project['project_id']); + } + } + + // Create events them as campaigns if they do not exist and store results in + // $result_values + $j = 0; + if ($events) { + foreach ($events as $event) { + $result_values['sync']['events'][$j++] = $twingleApi + ->syncEvent($event, $is_test); + } + } + return civicrm_api3_create_success($result_values); }