From f3d242fce7c37b4d74c268b11db8482fa9498114 Mon Sep 17 00:00:00 2001 From: Marc Michalsky forumZFD Date: Mon, 8 Feb 2021 13:56:54 +0100 Subject: [PATCH] implement TwingleCampaign --- CRM/TwingleCampaign/BAO/TwingleCampaign.php | 345 ++++++++++++++++++- CRM/TwingleCampaign/resources/campaigns.json | 39 +++ api/v3/TwingleCampaign/Create.php | 79 +++-- 3 files changed, 438 insertions(+), 25 deletions(-) diff --git a/CRM/TwingleCampaign/BAO/TwingleCampaign.php b/CRM/TwingleCampaign/BAO/TwingleCampaign.php index d8373ce..b227cbb 100644 --- a/CRM/TwingleCampaign/BAO/TwingleCampaign.php +++ b/CRM/TwingleCampaign/BAO/TwingleCampaign.php @@ -1,28 +1,363 @@ prefix = 'twingle_campaign_'; + $this->id = $values['id'] ?? NULL; + $this->values['campaign_type_id'] = 'twingle_campaign'; + + if ($this->id) { + $this->fetch(); + if ($values) { + $this->update($values); + } + $this->getParentProject(); + $this->createCid(); + $this->createUrl(); + } + elseif ($values) { + $this->update($values); + } + } + + + /** + * ## Create TwingleCampaign + * Create this TwingleCampaign as a campaign in CiviCRM + * + * @param bool $no_hook + * + * @throws \CiviCRM_API3_Exception + */ + public function create(bool $no_hook = FALSE) { + + // Set a flag to not trigger the hook + if ($no_hook) { + $_SESSION['CiviCRM']['de.forumzfd.twinglecampaign']['no_hook'] = TRUE; + } + + $values = $this->values; + + $this->translateCustomFields($values, self::IN); + + $result = civicrm_api3('Campaign', 'create', $values); + + if ($result['is_error'] != 1) { + $this->id = $result['id']; + $this->values = $result['values']; + } + } + + + /** + * ## Fetch TwingleCampaign + * Populate this instance with values from an existing TwingleCampaign. + * + * @throws CiviCRM_API3_Exception + */ + private function fetch() { + $this->values = civicrm_api3('Campaign', 'getsingle', + ['id' => $this->id]); + } + + + /** + * ## Get Parent Project + * Determines the id of the parent TwingleProject. If there is no parent + * TwingleProject, an error message is shown on UI and the campaign gets + * deleted. + * + * @throws \CiviCRM_API3_Exception + */ + private function getParentProject(): void { + + // Get campaign type id for TwingleProject + $twingle_project_campaign_type_id = + ExtensionCache::getInstance() + ->getCampaigns()['campaign_types']['twingle_project']['id']; + + // Determine the parent project id by looping through the campaign tree + // until the parent campaign type is a TwingleProject + $parent_id = $this->values['parent_id']; + + $parent_campaign_type_id = NULL; + + while ($parent_id && $parent_campaign_type_id != $twingle_project_campaign_type_id) { + // Get parent campaign + $parent_campaign = civicrm_api3('Campaign', 'getsingle', + ['id' => $parent_id]); + + if ($parent_campaign['is_error'] != 1) { + $parent_campaign_type_id = $parent_campaign['campaign_type_id']; + if ($parent_campaign['parent_id']) { + $parent_id = $parent_campaign['parent_id']; + } + else { + break; + } + } + else { + throw new CiviCRM_API3_Exception($parent_campaign['error_message']); + } + } + + // Set parent_project_id and retrieve parent_project_url + if ($parent_campaign_type_id == $twingle_project_campaign_type_id) { + $this->values['parent_project_id'] = $parent_id; + + // Get custom field name for twingle_project_page field + $cf_page = ExtensionCache::getInstance() + ->getCustomFieldMapping('twingle_project_page'); + + // Extract twingle_project_page url from parent_campaign + if ($parent_campaign[$cf_page]) { + $this->values['parent_project_url'] = $parent_campaign[$cf_page]; + } + + // If twingle_project_widget value is missing, try a synchronization + else { + $parent_campaign = civicrm_api3('TwingleProject', 'sync', + ['id' => $parent_id]); + + // Now try again to extract the twingle_project_widget url + if ($parent_campaign[$cf_url]) { + $this->values['parent_project_url'] = $parent_campaign[$cf_url]; + } + + // If twingle_project_widget value is still missing, show an alert on + // UI, log an error and delete this TwingleCampaign + else { + CRM_Core_Session::setStatus( + ts("Could not determine parent TwingleProject URL. This URL is + needed to create the TwingleEvent URL. Please check the logs."), + ts('Parent project URL missing'), + 'alert' + ); + Civi::log()->error( + E::LONG_NAME . + ' could not determine parent TwingleProject URL.', + $this->getResponse() + ); + $this->delete(); + } + } + + // If this TwingleCampaign has no parent TwingleProject above it in the + // campaign tree + } + else { + CRM_Core_Session::setStatus( + ts("TwingleCampaigns can only get created as a child of a + TwingleProject in the campaign tree."), + ts('No parent TwingleProject found'), + 'alert' + ); + $this->delete(); + } + } + + /** + * ## Create URL + * Create a URL by adding a tw_cid + */ + private function createUrl() { + $this->values['url'] = + $this->values['parent_project_url'] . '?tw_cid=' . $this->values['cid']; + } + + + /** + * + */ + private function createCid() { + $this->values['cid'] = md5($this->id . '_' . $this->values['name']); + } + + /** + * + */ + private function validateCid() { } - public function create() { + + /** + * + */ + private function decodeCid() { } - public function exists() { + /** + * ## Translate field names and custom field names + * + * Constants for **$direction**:
+ * **Campaign::IN** Translate field name to custom field name
+ * **Campaign::OUT** Translate from custom field name to field name + * + * @param array $values + * array of keys to translate + * + * @param string $direction + * const: Campaign::OUT or Campaign::OUT + */ + public function translateCustomFields(array &$values, string $direction) { + + // Translate field name to custom field name + if ($direction == self::IN) { + + foreach (ExtensionCache::getInstance() + ->getCustomFieldMapping() as $field => $custom) { + + if (array_key_exists( + str_replace( + $this->prefix, + '', + $field + ), + $values) + ) { + + $values[$custom] = $values[str_replace( + $this->prefix, + '', + $field + )]; + + unset($values[str_replace( + $this->prefix, + '', + $field + )] + ); + } + } + } + // Translate from custom field name to field name + elseif ($direction == self::OUT) { + + foreach (ExtensionCache::getInstance() + ->getCustomFieldMapping() as $field => $custom) { + + if (array_key_exists( + $custom, + $values + ) + ) { + $values[str_replace( + $this->prefix, + '', + $field + )] = $values[$custom]; + unset($values[$custom]); + } + } + } } + /** + * ## Delete TwingleCampaign + * Deletes this TwingleCampaign from CiviCRM + */ + private function delete() { + if ($this->id) { + try { + civicrm_api3('Campaign', 'delete', ['id' => $this->id]); + } catch (CiviCRM_API3_Exception $e) { + Civi::log()->error( + E::LONG_NAME . + ' could delete TwingleCampaign: ' . + $e->getMessage(), + $this->getResponse() + ); + } + } + } + + + /** + * ## Get a response + * Get a response that describes the status of this TwingleCampaign instance. + * Returns an array that contains **title**, **id**, **parent_project_id** + * (if available) and **status** (if provided) + * + * @param string|null $status + * status of the TwingleCampaign you want to give back along with the response + * + * @return array + */ + public function getResponse(string $status = NULL): array { + $response = [ + 'title' => $this->values['title'], + 'id' => (int) $this->id, + ]; + if ($status) { + $response['status'] = $status; + } + if ($this->values['parent_project_id']) { + $response['parent_project_id'] = (int) $this->values['parent_project_id']; + } + return $response; + } + + + /** + * ## Update values + * Updates all values in **$this->values** array. + * + * @param array $values + * values that should get updated + */ + private function update(array $values) { + $filter = ExtensionCache::getInstance()->getTemplates()['TwingleCampaign']; + foreach ($values as $key => $value) { + if (in_array($key, $filter)) { + $this->values[$key] = $values[$key]; + } + } + } + + /** + * ## Clone this TwingleProject + * + * This method removes the id from this instance and in the next step it + * creates the clone as a new TwingleCampaign with the same values to + * Twingle. + * + * @throws \CiviCRM_API3_Exception + */ + public function clone() { + // TODO: implement cloning + } + } \ No newline at end of file diff --git a/CRM/TwingleCampaign/resources/campaigns.json b/CRM/TwingleCampaign/resources/campaigns.json index 329261d..a38863c 100644 --- a/CRM/TwingleCampaign/resources/campaigns.json +++ b/CRM/TwingleCampaign/resources/campaigns.json @@ -130,6 +130,19 @@ "is_view": 0, "weight": 5 }, + "twingle_project_page": { + "custom_group_id": "Twingle_Project_Embed_Codes", + "label": "Twingle Project Page", + "name": "twingle_project_page", + "is_required": 0, + "is_searchable": 0, + "data_type": "Memo", + "html_type": "TextArea", + "text_length": 600, + "is_active": 1, + "is_view": 0, + "weight": 1 + }, "twingle_project_widget": { "custom_group_id": "Twingle_Project_Embed_Codes", "label": "Twingle Project Widget", @@ -399,6 +412,32 @@ "is_active": 1, "is_view": 1, "weight": 15 + }, + "twingle_campaign_parent_project_id": { + "custom_group_id": "Twingle_Campaign_Information", + "label": "Parent TwingleProject ID", + "name": "twingle_campaign_parent_project_id", + "is_required": 0, + "is_searchable": 1, + "data_type": "String", + "html_type": "Text", + "text_length": 16, + "is_active": 1, + "is_view": 1, + "weight": 1 + }, + "twingle_campaign_url": { + "custom_group_id": "Twingle_Campaign_Information", + "label": "Twingle Campaign URL", + "name": "twingle_campaign_url", + "is_required": 0, + "is_searchable": 0, + "data_type": "Memo", + "html_type": "TextArea", + "text_length": 600, + "is_active": 1, + "is_view": 1, + "weight": 2 } } } diff --git a/api/v3/TwingleCampaign/Create.php b/api/v3/TwingleCampaign/Create.php index f276bdf..c1aa084 100644 --- a/api/v3/TwingleCampaign/Create.php +++ b/api/v3/TwingleCampaign/Create.php @@ -1,4 +1,6 @@ 'id', + 'title' => E::ts('Twingle Project ID'), + 'type' => CRM_Utils_Type::T_INT, + 'api.required' => 0, + 'description' => E::ts('The Twingle Project ID'), + ]; + $spec['name'] = [ + 'name' => 'name', + 'title' => E::ts('Twingle Campaign Name'), + 'type' => CRM_Utils_Type::T_STRING, + 'api.required' => 0, + 'description' => E::ts('Name of the Twingle Project'), + ]; + $spec['title'] = [ + 'name' => 'title', + 'title' => E::ts('Twingle Campaign Title'), + 'type' => CRM_Utils_Type::T_STRING, + 'api.required' => 1, + 'description' => E::ts('Title of the Twingle Campaign'), + ]; + $spec['parent_id'] = [ + 'name' => 'parent_id', + 'title' => E::ts('Parent Campaign'), + 'type' => CRM_Utils_Type::T_INT, + 'api.required' => 1, + 'description' => E::ts('Optional parent id for this Campaign'), + ]; } + /** - * TwingleCampaign.Create API + * # TwingleCampaign.Create API * * @param array $params * * @return array * API result descriptor * + * @throws CiviCRM_API3_Exception * @see civicrm_api3_create_success - * - * @throws API_Exception */ -function civicrm_api3_twingle_campaign_Create($params) { - if (array_key_exists('magicword', $params) && $params['magicword'] == 'sesame') { - $returnValues = array( - // OK, return several data rows - 12 => ['id' => 12, 'name' => 'Twelve'], - 34 => ['id' => 34, 'name' => 'Thirty four'], - 56 => ['id' => 56, 'name' => 'Fifty six'], - ); - // ALTERNATIVE: $returnValues = []; // OK, success - // ALTERNATIVE: $returnValues = ["Some value"]; // OK, return a single value +function civicrm_api3_twingle_campaign_Create(array $params): array { - // Spec: civicrm_api3_create_success($values = 1, $params = [], $entity = NULL, $action = NULL) - return civicrm_api3_create_success($returnValues, $params, 'TwingleCampaign', 'Create'); - } - else { - throw new API_Exception(/*error_message*/ 'Everyone knows that the magicword is "sesame"', /*error_code*/ 'magicword_incorrect'); + // instantiate TwingleCampaign + $campaign = new TwingleCampaign($params); + + // Try to create the TwingleCampaign + try { + $campaign->create(); + return civicrm_api3_create_success( + $campaign->getResponse('TwingleCampaign created'), + $params, + 'TwingleCampaign', + 'Create' + ); + } catch(Exception $e){ + Civi::log()->error( + E::LONG_NAME . + ' could not create TwingleCampaign: ' . + $e->getMessage(), + $campaign->getResponse() + ); + return civicrm_api3_create_error( + 'Could not create TwingleCampaign: ' . $e->getMessage(), + $campaign->getResponse() + ); } + }