diff --git a/CRM/TwingleCampaign/BAO/Campaign.php b/CRM/TwingleCampaign/BAO/Campaign.php index 86b0bdc..b22666e 100644 --- a/CRM/TwingleCampaign/BAO/Campaign.php +++ b/CRM/TwingleCampaign/BAO/Campaign.php @@ -125,7 +125,9 @@ abstract class CRM_TwingleCampaign_BAO_Campaign { /** * ## Complement campaign values - * Complement existing campaign values with new ones + * Complement existing campaign values with new ones. + * Existing values will not get overwritten. + * * @param array $arrayToComplement */ public function complement(array $arrayToComplement) { @@ -143,6 +145,27 @@ abstract class CRM_TwingleCampaign_BAO_Campaign { } } + /** + * ## Merge campaign values + * Merge existing campaign values with new ones. + * Existing values will be overwritten! + * + * @param array $arrayToMerge + */ + public function merge(array $arrayToMerge) { + $this->complement_r($arrayToMerge, $this->values); + } + + private function merge_r($orig, &$fill) { + foreach ($orig as $key => $value) { + if (is_array($value)) { + $this->complement_r($orig[$key], $fill[$key]); + } else { + $fill[$key] = $value; + } + } + } + public static abstract function formatValues(array &$values, string $direction); /** diff --git a/CRM/TwingleCampaign/BAO/Configuration.php b/CRM/TwingleCampaign/BAO/Configuration.php index 38e8d5e..4d26232 100644 --- a/CRM/TwingleCampaign/BAO/Configuration.php +++ b/CRM/TwingleCampaign/BAO/Configuration.php @@ -6,8 +6,9 @@ class CRM_TwingleCampaign_BAO_Configuration { private static $settingsKeys = [ 'twingle_api_key', 'twinglecampaign_xcm_profile', - 'twinglecampaign_start_case', - 'twinglecampaign_soft_credits' + 'twinglecampaign_default_case', + 'twinglecampaign_soft_credits', + 'twinglecampaign_matomo_integration' ]; @@ -27,6 +28,12 @@ class CRM_TwingleCampaign_BAO_Configuration { Civi::settings()->set('twinglecampaign_soft_credits', 0); } + // Set twinglecampaign_matomo_integration to '0' if checkbox is unchecked + if (!array_key_exists('twinglecampaign_matomo_integration', $settings)) { + Civi::settings()->set('twinglecampaign_matomo_integration', 0); + } + + Civi::settings()->add($settings); } @@ -63,4 +70,4 @@ class CRM_TwingleCampaign_BAO_Configuration { } } -} \ No newline at end of file +} diff --git a/CRM/TwingleCampaign/BAO/CustomField.php b/CRM/TwingleCampaign/BAO/CustomField.php index dea24f9..da2c824 100644 --- a/CRM/TwingleCampaign/BAO/CustomField.php +++ b/CRM/TwingleCampaign/BAO/CustomField.php @@ -56,16 +56,17 @@ class CRM_TwingleCampaign_BAO_CustomField { } } - /** * Creates a CustomField by calling CiviCRM API v.3 * * @param bool $upgrade * If true: Does not show UF message if custom field already exists * + * @returns array|False Result of custom field creation api call. False if + * field already existed und wasn't upgraded. * @throws \CiviCRM_API3_Exception */ - public function create(bool $upgrade = false) { + public function create(bool $upgrade = FALSE) { // Check if the field already exists $field = civicrm_api3( @@ -79,46 +80,185 @@ class CRM_TwingleCampaign_BAO_CustomField { // If the field does not exist, create it if ($field['count'] == 0) { - $this->result = civicrm_api3( - 'CustomField', - 'create', - $this->getSetAttributes()); - // Set field id - $this->id = $this->result['id']; + try { + $this->result = civicrm_api3( + 'CustomField', + 'create', + $this->getSetAttributes()); - // Log field creation - if ($this->result['is_error'] == 0) { - Civi::log()->info("$this->extensionName has created a new custom field. - label: $this->label - name: $this->name - id: $this->id - group: $this->custom_group_id" - ); - } - // If the field could not get created: log error - else { + if ($this->result['is_error'] == 0) { + + // Set field id + $this->id = $this->result['id']; + + // Log field creation + Civi::log()->info("$this->extensionName has created a new custom field. + label: $this->label + name: $this->name + id: $this->id + group: $this->custom_group_id" + ); + return $this->result; + } + else { + throw new CiviCRM_API3_Exception($this->result['error_message']); + } + } catch (CiviCRM_API3_Exception $e) { + $errorMessage = $e->getMessage(); + // If the field could not get created: log error if ($this->name && $this->custom_group_id) { Civi::log() - ->error("$this->extensionName could not create new custom field - \"$this->name\" for group \"$this->custom_group_id\": - $this->result['error_message']"); - CRM_Utils_System::setUFMessage(E::ts('Creation of custom field \'%1\' failed. Find more information in the logs.', [1 => $this->name])); + ->error("$this->extensionName could not create new custom field \"$this->name\" for group \"$this->custom_group_id\": $errorMessage"); + CRM_Utils_System::setUFMessage(E::ts('%1: Creation of custom field \'%2\' failed. Find more information in the logs.', + [1 => $this->extensionName, 2 => $this->name] + )); } // If there is not enough information: log simple error message else { Civi::log() - ->error("$this->extensionName could not create new custom field: - $this->result['error_message']"); + ->error("$this->extensionName could not create new custom field: $errorMessage"); CRM_Utils_System::setUFMessage(E::ts("Creation of custom field failed. Find more information in the logs.")); } + return $this->result; } } elseif (!$upgrade) { CRM_Utils_System::setUFMessage(E::ts('Creation of custom field \'%1\' failed, because a custom field with that name already exists. Find more information in the logs.', [1 => $this->name])); Civi::log() ->error("$this->extensionName could not create new custom field \"$this->name\" for group \"$this->custom_group_id\" because a field with that name already exists."); + return NULL; } + else { + $this->id = $field['values'][0]['id']; + $this->custom_group_id = $field['values'][0]['custom_group_id']; + return $this->upgrade($field['values'][0]); + } + } + + /** + * Upgrade an existing custom field + * + * @param $attributes + * Custom Field data + * @returns array|False Result of custom field creation api call, False if + * field was not upgraded + */ + private function upgrade($attributes) { + $upgrade_necessary = False; + + if (key_exists('option_group_id', $attributes)) { + $this->addOptions($attributes); + } + + foreach ($this as $var => $value) { + // put array items into attributes + if (array_key_exists($var, $attributes) && $attributes[$var] != $value) { + $this->$var = $attributes[$var]; + $upgrade_necessary = True; + } + } + + if ($upgrade_necessary) { + try { + $this->result = civicrm_api3( + 'CustomField', + 'create', + $this->getSetAttributes()); + + // Log field creation + if ($this->result['is_error'] == 0) { + Civi::log()->info("$this->extensionName has updated a custom field. + label: $this->label + name: $this->name + id: $this->id + group: $this->custom_group_id" + ); + return $this->result; + } + else { + throw new CiviCRM_API3_Exception($this->result['error_message']); + } + } + catch (CiviCRM_API3_Exception $e) { + // If the field could not get created: log error + $errorMessage = $e->getMessage(); + if ($this->name && $this->custom_group_id) { + Civi::log() + ->error("$this->extensionName could not create new custom field \"$this->name\" for group \"$this->custom_group_id\": $errorMessage"); + CRM_Utils_System::setUFMessage(E::ts('Creation of custom field \'%1\' failed. Find more information in the logs.', [1 => $this->name])); + } + // If there is not enough information: log simple error message + else { + Civi::log() + ->error("$this->extensionName could not create new custom field: $errorMessage"); + CRM_Utils_System::setUFMessage(E::ts("Creation of custom field failed. Find more information in the logs.")); + } + return $this->result; + } + } + return False; + } + + /** + * Add additional options to custom field + * + * @param array $options + * + * @return array + */ + public function addOptions(array $options): array { + $result = []; + + try { + foreach ($this->option_values as $key => $value) { + + $option_value_exists = civicrm_api3( + 'OptionValue', + 'get', + [ + 'sequential' => 1, + 'option_group_id' => $options['option_group_id'], + 'value' => $key, + ] + ); + + // If the option value does not yet exist, create it + if ($option_value_exists['count'] == 0) { + $result[] = civicrm_api3( + 'OptionValue', + 'create', + [ + 'option_group_id' => $options['option_group_id'], + 'value' => $key, + 'label' => $value, + ] + ); + } + // If the option value already exist, update it + else { + $result[] = civicrm_api3( + 'OptionValue', + 'create', + [ + 'id' => $option_value_exists['values'][0]['id'], + 'label' => $value, + 'value' => $key, + ] + ); + } + } + } catch (CiviCRM_API3_Exception $e) { + $errorMessage = $e->getMessage(); + Civi::log() + ->error("$this->extensionName could not create additional option values for custom field \"$this->name\": $errorMessage"); + CRM_Utils_System::setUFMessage( + E::ts('%1 could not create additional option values for custom field \'%2\'. Find more information in the logs.', + [1 => $this->extensionName, 2 => $this->name]) + ); + } + + return $result; } /** @@ -137,7 +277,6 @@ class CRM_TwingleCampaign_BAO_CustomField { return $setAttributes; } - /** * Get an instance of a CustomField by its name or get an array with all * custom fields by leaving parameters empty. @@ -216,12 +355,12 @@ class CRM_TwingleCampaign_BAO_CustomField { if ($this->label && $this->custom_group_id) { Civi::log() ->error("$this->extensionName could not delete custom field - \"$this->label\" for group \"$this->custom_group_id\": + \"$this->label\" for group \"$this->custom_group_id\": $this->result['error_message']"); } else { Civi::log() - ->error("$this->extensionName could not delete custom field: + ->error("$this->extensionName could not delete custom field: $this->result['error_message']"); } } @@ -356,6 +495,13 @@ class CRM_TwingleCampaign_BAO_CustomField { */ public function getId() { return $this->id; - }# + } -} \ No newline at end of file + /** + * @param mixed $option_values + */ + public function setOptionValues($option_values): void { + $this->option_values = $option_values; + } + +} diff --git a/CRM/TwingleCampaign/BAO/OptionValue.php b/CRM/TwingleCampaign/BAO/OptionValue.php index 9de6594..1d808aa 100644 --- a/CRM/TwingleCampaign/BAO/OptionValue.php +++ b/CRM/TwingleCampaign/BAO/OptionValue.php @@ -158,7 +158,7 @@ class CRM_TwingleCampaign_BAO_OptionValue { // If a specific option value is required try { $option_value = civicrm_api3( - 'CustomValue', + 'OptionValue', 'get', [ 'sequential' => 1, diff --git a/CRM/TwingleCampaign/BAO/TwingleCampaign.php b/CRM/TwingleCampaign/BAO/TwingleCampaign.php index 58433ed..2cbc865 100644 --- a/CRM/TwingleCampaign/BAO/TwingleCampaign.php +++ b/CRM/TwingleCampaign/BAO/TwingleCampaign.php @@ -2,6 +2,7 @@ use CRM_TwingleCampaign_Utils_ExtensionCache as ExtensionCache; use CRM_TwingleCampaign_ExtensionUtil as E; +use CRM_TwingleCampaign_Exceptions_TwingleCampaignException as TwingleCampaignException; class CRM_TwingleCampaign_BAO_TwingleCampaign { @@ -17,32 +18,46 @@ class CRM_TwingleCampaign_BAO_TwingleCampaign { private $values; - /** * ## TwingleCampaign constructor * - * @param array|null $values - * @param int|null $id + * @param array $values * * @throws \CiviCRM_API3_Exception + * @throws \CRM_TwingleCampaign_Exceptions_TwingleCampaignException */ - public function __construct(array $values = [], int $id = NULL) { + public function __construct(array $values = []) { $this->prefix = 'twingle_campaign_'; - $this->id = $id ?? NULL; + $this->id = $values['id'] ?? NULL; $this->values['campaign_type_id'] = 'twingle_campaign'; - if ($this->id) { - $this->update($values); - $this->getParentProject(); - $this->createCid(); - $this->createUrl(); + // If there is already an ID for this TwingleProject, get its values from + // the database + if ($this->id != NULL) { + $this->fetch($this->id); } - else { - $this->update($values); - } - } + // Update the campaign values + $this->update($values); + + // Get the parent TwingleProject + // (it doesn't matter how many levels above in the campaign tree it is) + $this->getParentProject(); + + // If this is a new TwingleCampaign or if it is a cloned TwingleCampaign, + // calculate a cid + if ( + !isset($this->values['cid']) || + (isset($values['clone']) && $values['clone']) + ) { + $this->createCid(); + } + + // Create an url from the parent TwingleProject url and the cid of this + // TwingleCampaign + $this->createUrl(); + } /** * ## Create TwingleCampaign @@ -70,19 +85,17 @@ class CRM_TwingleCampaign_BAO_TwingleCampaign { } } - /** * ## Fetch TwingleCampaign * Populate this instance with values from an existing TwingleCampaign. * * @throws CiviCRM_API3_Exception */ - public function fetch() { + public function fetch(int $id) { $this->values = civicrm_api3('TwingleCampaign', 'getsingle', - ['id' => $this->id]); + ['id' => $id]); } - /** * ## Get Parent Project * Determines the id of the parent TwingleProject. If there is no parent @@ -90,6 +103,7 @@ class CRM_TwingleCampaign_BAO_TwingleCampaign { * deleted. * * @throws \CiviCRM_API3_Exception + * @throws \CRM_TwingleCampaign_Exceptions_TwingleCampaignException */ private function getParentProject(): void { @@ -100,10 +114,9 @@ class CRM_TwingleCampaign_BAO_TwingleCampaign { // 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_id = $parent_id ?? civicrm_api3( - 'TwingleCampaign', - 'getsingle', + $parent_id = $this->values['parent_id'] ?? civicrm_api3( + 'TwingleCampaign', + 'getsingle', ['id' => $this->id] )['parent_id']; @@ -121,7 +134,7 @@ class CRM_TwingleCampaign_BAO_TwingleCampaign { } $parent_campaign_type_id = $parent_campaign['campaign_type_id']; - if (isset($parent_campaign['parent_id'])) { + if ($parent_campaign_type_id != $twingle_project_campaign_type_id && isset($parent_campaign['parent_id'])) { $parent_id = $parent_campaign['parent_id']; } else { @@ -129,7 +142,6 @@ class CRM_TwingleCampaign_BAO_TwingleCampaign { } } - // 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; @@ -172,7 +184,7 @@ class CRM_TwingleCampaign_BAO_TwingleCampaign { ' could not determine parent TwingleProject URL.', $this->getResponse() ); - $this->delete(); + throw new TwingleCampaignException('Parent project URL missing'); } } @@ -185,7 +197,7 @@ class CRM_TwingleCampaign_BAO_TwingleCampaign { ts('No parent TwingleProject found'), 'alert' ); - $this->delete(); + throw new TwingleCampaignException('No parent TwingleProject found'); } } @@ -206,16 +218,14 @@ class CRM_TwingleCampaign_BAO_TwingleCampaign { $this->values['parent_project_url'] . '?tw_cid=' . $this->values['cid']; } - /** * */ private function createCid() { - $this->values['cid'] = md5($this->id . '_' . $this->values['name']); + $this->values['cid'] = uniqid(); } - /** * ## Translate field names and custom field names * @@ -284,7 +294,6 @@ class CRM_TwingleCampaign_BAO_TwingleCampaign { } } - /** * ## Delete TwingleCampaign * Deletes this TwingleCampaign from CiviCRM @@ -296,7 +305,7 @@ class CRM_TwingleCampaign_BAO_TwingleCampaign { } catch (CiviCRM_API3_Exception $e) { Civi::log()->error( E::LONG_NAME . - ' could delete TwingleCampaign: ' . + ' could not delete TwingleCampaign: ' . $e->getMessage(), $this->getResponse() ); @@ -304,7 +313,6 @@ class CRM_TwingleCampaign_BAO_TwingleCampaign { } } - /** * ## Get a response * Get a response that describes the status of this TwingleCampaign instance. @@ -318,7 +326,15 @@ class CRM_TwingleCampaign_BAO_TwingleCampaign { */ public function getResponse(string $status = NULL): array { - $keys = ['id', 'name', 'title', 'parent_project_id', 'parent_id', 'cid', 'url']; + $keys = [ + 'id', + 'name', + 'title', + 'parent_project_id', + 'parent_id', + 'cid', + 'url', + ]; $response = []; foreach ($keys as $key) { if (isset($this->values[$key])) { @@ -341,35 +357,22 @@ class CRM_TwingleCampaign_BAO_TwingleCampaign { */ private function update(array $values) { - $filter = ExtensionCache::getInstance()->getTemplates()['TwingleCampaign']; + $filter = ExtensionCache::getInstance() + ->getTemplates()['TwingleCampaign']['campaign_data']; foreach ($values as $key => $value) { if (in_array($key, $filter)) { - $this->values[$key] = $values[$key]; + $this->values[$key] = $value; } } } - /** - * ## 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 - } - /** * ## Get ID + * * @return mixed|null */ public function getId(): int { return (int) $this->id; } - } \ No newline at end of file diff --git a/CRM/TwingleCampaign/BAO/TwingleEvent.php b/CRM/TwingleCampaign/BAO/TwingleEvent.php index 19de97b..faf609b 100644 --- a/CRM/TwingleCampaign/BAO/TwingleEvent.php +++ b/CRM/TwingleCampaign/BAO/TwingleEvent.php @@ -27,7 +27,7 @@ class CRM_TwingleCampaign_BAO_TwingleEvent extends Campaign { $this->id_custom_field = Cache::getInstance() ->getCustomFieldMapping()['twingle_event_id']; - if (isset($this->values['parent_id'])) { + if (!isset($this->values['parent_id'])) { try { $this->values['parent_id'] = $this->getParentCampaignId(); } catch (CiviCRM_API3_Exception $e) { @@ -35,6 +35,7 @@ class CRM_TwingleCampaign_BAO_TwingleEvent extends Campaign { throw new Exception("Could not identify parent Campaign: $errorMessage"); } } + } @@ -52,32 +53,39 @@ class CRM_TwingleCampaign_BAO_TwingleEvent extends Campaign { if (parent::create()) { - // check for existence - $result = civicrm_api3('Case', 'get', [ - 'contact_id' => $this->formattedValues['contact'], - 'case_type_id' => Configuration::get('twinglecampaign_start_case'), - 'subject' => $this->formattedValues['title'] . ' | Event-ID: ' . - $this->formattedValues['id'], - ]); + // Get case type + $parentProject = civicrm_api3( + 'TwingleProject', + 'getsingle', + ['id' => $this->values['parent_id']] + ); + $caseType = $parentProject['case'] + ?? Configuration::get('twinglecampaign_default_case'); - // Open a case - if ( - Configuration::get('twinglecampaign_start_case') && - $result['count'] == 0 - ) { - $result = civicrm_api3('Case', 'create', [ + if ($caseType) { + // check for existence + $result = civicrm_api3('Case', 'get', [ 'contact_id' => $this->formattedValues['contact'], - 'case_type_id' => Configuration::get('twinglecampaign_start_case'), + 'case_type_id' => $caseType, 'subject' => $this->formattedValues['title'] . ' | Event-ID: ' . $this->formattedValues['id'], - 'start_date' => $this->formattedValues['created_at'], - 'status_id' => "Open", ]); - } - if ($result['is_error'] != 0) { - throw new Exception('Could not create case'); - } + // Open a case + if ($result['count'] == 0) { + $result = civicrm_api3('Case', 'create', [ + 'contact_id' => $this->formattedValues['contact'], + 'case_type_id' => $caseType, + 'subject' => $this->formattedValues['title'] . ' | Event-ID: ' . + $this->formattedValues['id'], + 'start_date' => $this->formattedValues['created_at'], + 'status_id' => "Open", + ]); + } + if ($result['is_error'] != 0) { + throw new Exception('Could not create case'); + } + } return TRUE; } return FALSE; @@ -211,13 +219,15 @@ class CRM_TwingleCampaign_BAO_TwingleEvent extends Campaign { private static function matchContact(string $names, string $email): ?int { $names = StringOps::split_names($names); // Hopefully just a temporary solution - $firstnames = $names['firstnames']; - $lastname = $names['lastname']; + $firstnames = $names['firstnames'] ?? NULL; + $lastname = $names['lastname'] ?? NULL; + $display_name = $names['display_name'] ?? NULL; try { $contact = civicrm_api3('Contact', 'getorcreate', [ 'xcm_profile' => Civi::settings()->get('twinglecampaign_xcm_profile'), 'first_name' => $firstnames, 'last_name' => $lastname, + 'display_name' => $display_name, 'email' => $email, ]); return (int) $contact['id']; diff --git a/CRM/TwingleCampaign/BAO/TwingleProject.php b/CRM/TwingleCampaign/BAO/TwingleProject.php index b92313e..d63dd42 100644 --- a/CRM/TwingleCampaign/BAO/TwingleProject.php +++ b/CRM/TwingleCampaign/BAO/TwingleProject.php @@ -31,6 +31,15 @@ class CRM_TwingleCampaign_BAO_TwingleProject extends Campaign { 'one_time', ]; + // All fields for hex color codes + const colorFields = [ + 'design_background_color', + 'design_primary_color', + 'design_font_color', + 'design_button_font_color', + 'design_button_font_color_light', + ]; + /** * ## TwingleProject constructor * @@ -143,11 +152,13 @@ class CRM_TwingleCampaign_BAO_TwingleProject extends Campaign { */ 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]); + if (!in_array($key, self::colorFields)) { + if (ctype_digit($value)) { + $values[$key] = intval($value); + } + elseif (is_array($value)) { + $this->strToInt($values[$key]); + } } } } @@ -165,15 +176,13 @@ class CRM_TwingleCampaign_BAO_TwingleProject extends Campaign { */ public function create(bool $no_hook = FALSE): bool { - $result = parent::create($no_hook); + # If this project is not meant to be synced, do not create a campaign + if (!$this->values['project_options']['has_civi_crm_activated']) { + return FALSE; + } + + return parent::create($no_hook); - // Check if campaign was created successfully - if ($result['is_error'] == 0) { - return TRUE; - } - else { - throw new Exception($result['error_message']); - } } /** @@ -293,21 +302,18 @@ class CRM_TwingleCampaign_BAO_TwingleProject extends Campaign { } // Validate hexadecimal color fields - $colorFields = - [ - 'design_background_color', - 'design_primary_color', - 'design_font_color', - ]; - foreach ($colorFields as $colorField) { + foreach (self::colorFields as $colorField) { if ( - !empty($this->values['project_options'][$colorField]) && + ( + !empty($this->values['project_options'][$colorField]) || + $this->values['project_options'][$colorField] === "0" + ) && ( !( ctype_xdigit($this->values['project_options'][$colorField]) || is_integer($this->values['project_options'][$colorField]) ) || - strlen((string) $this->values['project_options'][$colorField]) > 6 + strlen((string) $this->values['project_options'][$colorField]) != 6 ) ) { $valid = FALSE; @@ -562,13 +568,17 @@ class CRM_TwingleCampaign_BAO_TwingleProject extends Campaign { } // Change DateTime string into timestamp - $values['last_update'] = - self::getTimestamp($values['last_update']); + if (isset($values['last_update'])) { + $values['last_update'] = + self::getTimestamp($values['last_update']); + } // Default project_type to '' - $values['type'] = $values['type'] == 'default' - ? '' - : $values['type']; + if (isset($values['type'])) { + $values['type'] = $values['type'] == 'default' + ? '' + : $values['type']; + } // Cast project target to integer if (isset($values['project_target'])) { @@ -761,4 +771,14 @@ class CRM_TwingleCampaign_BAO_TwingleProject extends Campaign { unset($this->values['payment_methods']); } + + /** + * ## Set project name + * + * @param string $name + */ + public function setName(string $name): void { + $this->values['name'] = $name; + } + } diff --git a/CRM/TwingleCampaign/Exceptions/TwingleCampaignException.php b/CRM/TwingleCampaign/Exceptions/TwingleCampaignException.php new file mode 100644 index 0000000..61baf6b --- /dev/null +++ b/CRM/TwingleCampaign/Exceptions/TwingleCampaignException.php @@ -0,0 +1,8 @@ +addElement( 'select', - 'twinglecampaign_start_case', - E::ts('Start a case for event initiators'), - $this->getCaseTypes(), + 'twinglecampaign_default_case', + E::ts('Default case to open for event initiators'), + getCaseTypes(), ['class' => 'crm-select2 huge'] ); @@ -45,6 +46,13 @@ class CRM_TwingleCampaign_Form_Settings extends CRM_Core_Form { FALSE ); + $this->addElement( + 'checkbox', + 'twinglecampaign_matomo_integration', + E::ts('Use Matomo to track user interaction with Twingle forms'), + FALSE + ); + $this->addButtons([ [ 'type' => 'submit', @@ -92,30 +100,5 @@ class CRM_TwingleCampaign_Form_Settings extends CRM_Core_Form { return $xcmProfiles; } - /** - * Retrieves all case types - * - * @return array - */ - private function getCaseTypes(): array { - $caseTypes = [NULL => E::ts('none')]; - try { - $result = civicrm_api3('CaseType', 'get', [ - 'sequential' => 1, - 'options' => ['limit' => 0] - ]); - if (is_array($result['values'])) { - foreach ($result['values'] as $case) { - $caseTypes[$case['name']] = $case['title']; - } - } - } catch (CiviCRM_API3_Exception $e) { - Civi::log()->error( - E::LONG_NAME . ' could not retrieve case types: ' . - $e->getMessage()); - } - return $caseTypes; - } - } diff --git a/CRM/TwingleCampaign/Upgrader.php b/CRM/TwingleCampaign/Upgrader.php index 6cecff9..f103551 100644 --- a/CRM/TwingleCampaign/Upgrader.php +++ b/CRM/TwingleCampaign/Upgrader.php @@ -13,13 +13,14 @@ use CRM_TwingleCampaign_ExtensionUtil as E; */ class CRM_TwingleCampaign_Upgrader extends CRM_TwingleCampaign_Upgrader_Base { - // By convention, functions that look like "function upgrade_NNNN()" are - // upgrade tasks. They are executed in order (like Drupal's hook_update_N). - /** + * This update function checks whether all custom fields defined in + * CRM/TwingleCampaign/resources/campaigns.php exist and creates them if not. + * To ensure that all newly created custom fields get filled with data, all + * changed campaigns will get pulled from Twingle. * @throws \CiviCRM_API3_Exception */ - public function upgrade_01() { + public function upgrade_03(): bool { $campaign_info = require E::path() . '/CRM/TwingleCampaign/resources/campaigns.php'; @@ -31,7 +32,7 @@ class CRM_TwingleCampaign_Upgrader extends CRM_TwingleCampaign_Upgrader_Base { new CampaignType($campaign_type); } foreach (CampaignType::getCampaignTypes() as $campaign_type) { - $campaign_type->create(true); + $campaign_type->create(TRUE); } // Create custom groups @@ -42,13 +43,61 @@ class CRM_TwingleCampaign_Upgrader extends CRM_TwingleCampaign_Upgrader_Base { } } $cg = new CustomGroup($custom_group); - $cg->create(true); + $cg->create(TRUE); } + // If new fields get created during the update, set a flag to set all + // last_update values of the affected campaigns to "0" and trigger a + // synchronization. This ensures that settings on Twingle's side will not + // get overwritten with empty values. + $updatedCampaignTypes = []; + // Create custom fields foreach ($campaign_info['custom_fields'] as $custom_field) { $cf = new CustomField($custom_field); - $cf->create(true); + $result = $cf->create(TRUE); + + if (!empty($result)) { + preg_match( + '/^Twingle_[a-yA-Z]*/', + $cf->getCustomGroupId(), + $updatedCampaignTypes[] + ); + } + } + + // Filter changed campaign types + foreach ($updatedCampaignTypes as $key => $value) { + $updatedCampaignTypes[str_replace('_', '', $value[0])] + = TRUE; + unset($updatedCampaignTypes[$key]); + } + + // Pull changed campaigns to fill new created fields with data + try { + foreach ($updatedCampaignTypes as $key => $value) { + if ($value === TRUE) { + civicrm_api3( + $key, + 'sync', + ['pull' => TRUE] + ); + } + } + } catch (Exception $e) { + Civi::log()->error( + E::LONG_NAME . + ' could not pull campaigns from Twingle to fill the campaign fields that were created on update.' . + $e->getMessage() + ); + CRM_Core_Session::setStatus( + E::ts( + 'Could not pull campaigns from Twingle to fill the campaign fields that were created within this update: %1', + [1 => $e->getMessage()] + ), + E::ts('Scheduled Job'), + 'error' + ); } // Create option values @@ -124,7 +173,6 @@ class CRM_TwingleCampaign_Upgrader extends CRM_TwingleCampaign_Upgrader_Base { E::ts('Scheduled Job'), error ); - CRM_Utils_System::setUFMessage(E::ts('Could not create scheduled job "TwingleSync". Your Campaigns will not get synchronized to Twingle.')); } } @@ -246,83 +294,4 @@ class CRM_TwingleCampaign_Upgrader extends CRM_TwingleCampaign_Upgrader_Base { Civi::settings()->revert('twingle_api_key'); } - - /** - * Example: Run a couple simple queries. - * - * @return TRUE on success - * @throws Exception - * - * public function upgrade_4200() { - * $this->ctx->log->info('Applying update 4200'); - * CRM_Core_DAO::executeQuery('UPDATE foo SET bar = "whiz"'); - * CRM_Core_DAO::executeQuery('DELETE FROM bang WHERE willy = wonka(2)'); - * return TRUE; - * } // */ - - - /** - * Example: Run an external SQL script. - * - * @return TRUE on success - * @throws Exception - * public function upgrade_4201() { - * $this->ctx->log->info('Applying update 4201'); - * // this path is relative to the extension base dir - * $this->executeSqlFile('sql/upgrade_4201.sql'); - * return TRUE; - * } // */ - - - /** - * Example: Run a slow upgrade process by breaking it up into smaller chunk. - * - * @return TRUE on success - * @throws Exception - * public function upgrade_4202() { - * $this->ctx->log->info('Planning update 4202'); // PEAR Log interface - * - * $this->addTask(E::ts('Process first step'), 'processPart1', $arg1, $arg2); - * $this->addTask(E::ts('Process second step'), 'processPart2', $arg3, $arg4); - * $this->addTask(E::ts('Process second step'), 'processPart3', $arg5); - * return TRUE; - * } - * public function processPart1($arg1, $arg2) { sleep(10); return TRUE; } - * public function processPart2($arg3, $arg4) { sleep(10); return TRUE; } - * public function processPart3($arg5) { sleep(10); return TRUE; } - * // */ - - - /** - * Example: Run an upgrade with a query that touches many (potentially - * millions) of records by breaking it up into smaller chunks. - * - * @return TRUE on success - * @throws Exception - * public function upgrade_4203() { - * $this->ctx->log->info('Planning update 4203'); // PEAR Log interface - * - * $minId = CRM_Core_DAO::singleValueQuery('SELECT coalesce(min(id),0) FROM - * civicrm_contribution'); - * $maxId = CRM_Core_DAO::singleValueQuery('SELECT coalesce(max(id),0) FROM - * civicrm_contribution'); for ($startId = $minId; $startId <= $maxId; - * $startId += self::BATCH_SIZE) { - * $endId = $startId + self::BATCH_SIZE - 1; - * $title = E::ts('Upgrade Batch (%1 => %2)', array( - * 1 => $startId, - * 2 => $endId, - * )); - * $sql = ' - * UPDATE civicrm_contribution SET foobar = whiz(wonky()+wanker) - * WHERE id BETWEEN %1 and %2 - * '; - * $params = array( - * 1 => array($startId, 'Integer'), - * 2 => array($endId, 'Integer'), - * ); - * $this->addTask($title, 'executeSql', $sql, $params); - * } - * return TRUE; - * } // */ - } diff --git a/CRM/TwingleCampaign/Utils/APIWrapper.php b/CRM/TwingleCampaign/Utils/APIWrapper.php index 5afe426..06ff396 100644 --- a/CRM/TwingleCampaign/Utils/APIWrapper.php +++ b/CRM/TwingleCampaign/Utils/APIWrapper.php @@ -40,8 +40,8 @@ class CRM_TwingleCampaign_Utils_APIWrapper { $response_copy = $response; // Create soft credit for contribution - if (array_key_exists('contribution', $response['values'])) { - $contribution = array_shift($response_copy['values']['contribution']); + if (array_key_exists('contribution', $response)) { + $contribution = array_shift($response_copy['contribution']); if (array_key_exists('campaign_id', $contribution)) { try { $twingle_event = civicrm_api3( @@ -49,7 +49,7 @@ class CRM_TwingleCampaign_Utils_APIWrapper { 'getsingle', ['id' => $contribution['campaign_id']] ); - $response['values']['soft_credit'] = + $response['soft_credit'] = self::createSoftCredit($contribution, $twingle_event)['values']; $event->setResponse($response); } catch (CiviCRM_API3_Exception $e) { @@ -58,8 +58,8 @@ class CRM_TwingleCampaign_Utils_APIWrapper { } } // Create soft credit for sepa mandate - elseif (array_key_exists('sepa_mandate', $response['values'])) { - $sepa_mandate = array_pop($response_copy['values']['sepa_mandate']); + elseif (array_key_exists('sepa_mandate', $response)) { + $sepa_mandate = array_pop($response_copy['sepa_mandate']); try { $contribution = civicrm_api3( @@ -84,7 +84,7 @@ class CRM_TwingleCampaign_Utils_APIWrapper { 'getsingle', ['id' => $contribution['contribution_campaign_id']] ); - $response['values']['soft_credit'] = + $response['soft_credit'] = self::createSoftCredit($contribution, $twingle_event)['values']; $event->setResponse($response); } catch (CiviCRM_API3_Exception $e) { @@ -103,12 +103,12 @@ class CRM_TwingleCampaign_Utils_APIWrapper { * the de.systopia.twingle extension can include the campaign into the * contribution which it will create. * - * @param $apiRequest - * @param $callsame + * @param array $apiRequest + * @param callable $callsame * * @return mixed */ - public function mapDonation($apiRequest, $callsame) { + public static function mapDonation(array $apiRequest, callable $callsame) { if (array_key_exists( 'campaign_id', @@ -129,11 +129,7 @@ class CRM_TwingleCampaign_Utils_APIWrapper { } } } - elseif (array_key_exists( - 'event', - $apiRequest['params']['custom_fields']) && - !empty($apiRequest['params']['custom_fields']['event']) - ) { + elseif (!empty($apiRequest['params']['custom_fields']['event'])) { try { $targetCampaign = civicrm_api3( 'TwingleEvent', @@ -204,7 +200,9 @@ class CRM_TwingleCampaign_Utils_APIWrapper { 'contribution_id' => $contribution['id'], ] ); + + throw $e; } } -} \ No newline at end of file +} diff --git a/CRM/TwingleCampaign/Utils/CaseTypes.php b/CRM/TwingleCampaign/Utils/CaseTypes.php new file mode 100644 index 0000000..b05282c --- /dev/null +++ b/CRM/TwingleCampaign/Utils/CaseTypes.php @@ -0,0 +1,27 @@ + 1, + 'options' => ['limit' => 0] + ]); + if (is_array($result['values'])) { + foreach ($result['values'] as $case) { + $caseTypes[$case['name']] = $case['title']; + } + } + } catch (CiviCRM_API3_Exception $e) { + Civi::log()->error( + E::LONG_NAME . ' could not retrieve case types: ' . + $e->getMessage()); + } + return $caseTypes; +} diff --git a/CRM/TwingleCampaign/Utils/MatomoSnippet.php b/CRM/TwingleCampaign/Utils/MatomoSnippet.php new file mode 100644 index 0000000..f6b62af --- /dev/null +++ b/CRM/TwingleCampaign/Utils/MatomoSnippet.php @@ -0,0 +1,75 @@ +", + "", + "", + ]); + } + + /** + * Returns JavaScript snippet to track events in Matomo. + * + * @return string + */ + public static function get_event_tracker() { + $code = "_paq.push(['trackEvent', 'twingle', 'donation', event.data.value.recurringRythm, event.data.value.amount]);"; + return self::embed_in_base_function($code); + } + + /** + * Returns JavaScript snippet to track Matomo goals. + * + * @param $goal_id + * The ID of your Matomo goal. + * + * @return string + */ + public static function get_goal_tracker($goal_id) { + $code = "_paq.push(['trackGoal', $goal_id]);"; + return self::embed_in_base_function($code); + } + + /** + * Returns JavaScript snippet to track ecommerce activity in Matomo. + * + * @return string + */ + public static function get_ecommerce_tracker() { + $code = implode("\n", [ + "_paq.push(['addEcommerceItem', event.data.value.rythm, '', event.data.value.target, event.data.value.amount]);", + "_paq.push(['trackEcommerceOrder', 'anonymizedData', event.data.value.amount]);", + ]); + return self::embed_in_base_function($code); + } + + /** + * Appends the given code to the original code. + * + * @param $original + * The original code. + * @param $appendix + * The code you want to append to the original code. + * + * @return string + * The combined code after appending the appendix. + */ + public static function append_code($original, $appendix) { + return $original . $appendix; + } + +} diff --git a/CRM/TwingleCampaign/Utils/StringOperations.php b/CRM/TwingleCampaign/Utils/StringOperations.php index 19c0fd3..98e6657 100644 --- a/CRM/TwingleCampaign/Utils/StringOperations.php +++ b/CRM/TwingleCampaign/Utils/StringOperations.php @@ -49,7 +49,7 @@ class CRM_TwingleCampaign_Utils_StringOperations { $firstnames = implode(" ", $names); return ['firstnames' => $firstnames, 'lastname' => $lastname]; } - return $string; + return ['display_name' => $string]; } /** @@ -71,4 +71,4 @@ class CRM_TwingleCampaign_Utils_StringOperations { public static function startsWith($haystack, $needle): bool { return substr_compare($haystack, $needle, 0, strlen($needle)) === 0; } -} \ No newline at end of file +} diff --git a/CRM/TwingleCampaign/resources/campaigns.php b/CRM/TwingleCampaign/resources/campaigns.php index 1dbeba8..9c59709 100644 --- a/CRM/TwingleCampaign/resources/campaigns.php +++ b/CRM/TwingleCampaign/resources/campaigns.php @@ -1,6 +1,7 @@ [ @@ -112,7 +113,9 @@ return [ "option_values" => [ "default" => E::ts("Default"), "event" => E::ts("Events"), - "membership" => E::ts("Membership") + "membership" => E::ts("Membership"), + "shop" => E::ts("Shop"), + "giftshop" => E::ts("Gift Shop") ], "text_length" => 32, "is_active" => 1, @@ -121,6 +124,21 @@ return [ "help_post" => E::ts("Choose the project type. Allow users to create own events or to pay a membership fee."), "default_value" => "default" ], + "twingle_project_case" => [ + "custom_group_id" => "Twingle_Project_Information", + "label" => E::ts("Case"), + "name" => "twingle_project_case", + "is_required" => FALSE, + "is_searchable" => 1, + "data_type" => "String", + "html_type" => "Select", + "option_values" => getCaseTypes(), + "text_length" => 32, + "is_active" => 1, + "is_view" => FALSE, + "weight" => 3, + "help_post" => E::ts("Which case should get opened for event creators?") + ], "twingle_project_allow_more" => [ "custom_group_id" => "Twingle_Project_Information", "label" => E::ts("allow more"), diff --git a/README.md b/README.md index 60025fa..59ba268 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,8 @@ decrease this interval by changing the configuration of the scheduled job named Furthermore, the synchronization may get triggered also if a donation for an unknown event arrives via the Twingle API extension. +A CiviCRM site running [not in productive mode](https://docs.civicrm.org/sysadmin/en/latest/misc/staging-production/) will by default not run any scheduled jobs. In that case you can individually enable the TwingleSync job by adding the parameter runInNonProductionEnvironment=TRUE to the job. + ## Known Issues - The **Campaign Manager** displays a *«Campaign XY has been saved»* message even if the input validation failed and the @@ -81,4 +83,4 @@ extension. - [ ] Make the Twingle Event Settings for contact matching, case creation and creation of soft credits an individual setting in each project -- [ ] Make more payment methods available \ No newline at end of file +- [ ] Make more payment methods available diff --git a/api/v3/TwingleCampaign/Create.php b/api/v3/TwingleCampaign/Create.php index 6033871..e42c00a 100644 --- a/api/v3/TwingleCampaign/Create.php +++ b/api/v3/TwingleCampaign/Create.php @@ -40,6 +40,13 @@ function _civicrm_api3_twingle_campaign_Create_spec(array &$spec) { 'api.required' => 1, 'description' => E::ts('Optional parent id for this Campaign'), ]; + $spec['clone'] = [ + 'name' => 'clone', + 'title' => E::ts('Clone'), + 'type' => CRM_Utils_Type::T_BOOLEAN, + 'api.required' => 0, + 'description' => E::ts('Set this value to true if this campaign is about to be cloned to recreate cid'), + ]; } @@ -61,11 +68,10 @@ function civicrm_api3_twingle_campaign_Create(array $params): array { _civicrm_api3_twingle_campaign_Create_spec($allowed_params); $params = array_intersect_key($params, $allowed_params); - // instantiate TwingleCampaign - $campaign = new TwingleCampaign($params, $params['id']); - - // Try to create the TwingleCampaign try { + // instantiate TwingleCampaign + $campaign = new TwingleCampaign($params); + // try to create the TwingleCampaign $campaign->create(TRUE); return civicrm_api3_create_success( $campaign->getResponse('TwingleCampaign created'), @@ -74,15 +80,8 @@ function civicrm_api3_twingle_campaign_Create(array $params): array { '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() + 'Could not create TwingleCampaign: ' . $e->getMessage() ); } diff --git a/api/v3/TwingleCampaign/Get.php b/api/v3/TwingleCampaign/Get.php index b0670e1..36c6df2 100644 --- a/api/v3/TwingleCampaign/Get.php +++ b/api/v3/TwingleCampaign/Get.php @@ -33,6 +33,13 @@ function _civicrm_api3_twingle_campaign_Get_spec(array &$spec) { 'api.required' => 0, 'description' => E::ts('Twingle ID of the parent TwingleProject'), ]; + $spec['parent_id'] = [ + 'name' => 'parent_id', + 'title' => E::ts('Parent Project ID'), + 'type' => CRM_Utils_Type::T_INT, + 'api.required' => 0, + 'description' => E::ts('ID of the parent TwingleProject'), + ]; $spec['name'] = [ 'name' => 'name', 'title' => E::ts('Campaign Name'), @@ -100,6 +107,9 @@ function civicrm_api3_twingle_campaign_Get(array $params): array { _civicrm_api3_twingle_campaign_Get_spec($allowed_params); $params = array_intersect_key($params, $allowed_params); + // Do not limit the number of results + $params['options'] = ['limit' => 0]; + $returnValues = []; // Get campaign type id for TwingleCampaign @@ -122,13 +132,12 @@ function civicrm_api3_twingle_campaign_Get(array $params): array { // Include parent TwingleProject id in $params $params['parent_id'] = $project['id']; + } + elseif (isset($params['parent_id'])) { // Include campaign type ot TwingleCampaigns in $params $params['campaign_type_id'] = $twingle_campaign_campaign_type_id; - // Do not limit the number of results - $params['options'] = ['limit' => 0]; - // Get TwingleCampaign children campaigns of the TwingleProject $campaigns = civicrm_api3('Campaign', 'get', @@ -157,7 +166,7 @@ function civicrm_api3_twingle_campaign_Get(array $params): array { } // Translate custom fields - if (!empty($campaigns)) { + if (!empty($campaigns['values'])) { $custom_field_mapping_reverse = array_flip(Cache::getInstance()->getCustomFieldMapping()); @@ -171,7 +180,7 @@ function civicrm_api3_twingle_campaign_Get(array $params): array { $returnValues[$campaign['id']][$key] = $value; } } - foreach($returnValues[$campaign['id']] as $key => $value) { + foreach ($returnValues[$campaign['id']] as $key => $value) { if ($key != 'twingle_campaign_id' && strpos($key, 'twingle_campaign_') === 0) { $returnValues[$campaign['id']][str_replace('twingle_campaign_', '', $key)] = $value; diff --git a/api/v3/TwingleCampaign/Sync.php b/api/v3/TwingleCampaign/Sync.php index 4ba8332..240c896 100644 --- a/api/v3/TwingleCampaign/Sync.php +++ b/api/v3/TwingleCampaign/Sync.php @@ -20,12 +20,19 @@ function _civicrm_api3_twingle_campaign_Sync_spec(array &$spec) { 'description' => E::ts('The Twingle Campaign ID'), ]; $spec['project_id'] = [ - 'name' => 'project_id', + 'name' => 'parent_project_id', 'title' => E::ts('Parent Twingle Project ID'), 'type' => CRM_Utils_Type::T_INT, 'api.required' => 0, 'description' => E::ts('Twingle ID of the parent TwingleProject'), ]; + $spec['parent_id'] = [ + 'name' => 'parent_id', + 'title' => E::ts('Parent Project ID'), + 'type' => CRM_Utils_Type::T_INT, + 'api.required' => 0, + 'description' => E::ts('ID of the parent TwingleProject'), + ]; } /** @@ -52,7 +59,17 @@ function civicrm_api3_twingle_campaign_Sync(array $params): array { $returnValues = []; $errors_occurred = 0; - if ($campaigns['is_error'] == 0 && $campaigns['count'] > 0) { + // Abort if TwingleProject does not have TingleCampaign children + if ($campaigns['count'] == 0) { + return civicrm_api3_create_success( + $returnValues, + $params, + 'TwingleCampaign', + 'Sync' + ); + } + + if ($campaigns['is_error'] == 0) { // Instantiate and re-create TwingleCampaigns foreach ($campaigns['values'] as $campaign) { @@ -96,8 +113,7 @@ function civicrm_api3_twingle_campaign_Sync(array $params): array { } else { return civicrm_api3_create_error( - 'Could not get TwingleCampaigns: ' . - $campaigns['error_message'], + 'Could not get TwingleCampaigns: ' . $campaigns['error_message'], $params ); } diff --git a/api/v3/TwingleEvent/Create.php b/api/v3/TwingleEvent/Create.php deleted file mode 100644 index 0ae5390..0000000 --- a/api/v3/TwingleEvent/Create.php +++ /dev/null @@ -1,45 +0,0 @@ - ['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 - - // Spec: civicrm_api3_create_success($values = 1, $params = [], $entity = NULL, $action = NULL) - return civicrm_api3_create_success($returnValues, $params, 'TwingleEvent', 'Create'); - } - else { - throw new API_Exception(/*error_message*/ 'Everyone knows that the magicword is "sesame"', /*error_code*/ 'magicword_incorrect'); - } -} diff --git a/api/v3/TwingleEvent/Sync.php b/api/v3/TwingleEvent/Sync.php index 9de54cb..23a8b88 100644 --- a/api/v3/TwingleEvent/Sync.php +++ b/api/v3/TwingleEvent/Sync.php @@ -41,6 +41,13 @@ function _civicrm_api3_twingle_event_Sync_spec(array &$spec) { 'api.required' => 0, 'description' => E::ts('If this is set true, no database change will be made'), ]; + $spec['pull'] = [ + 'name' => 'pull', + 'title' => E::ts('Pull'), + 'type' => CRM_Utils_Type::T_BOOLEAN, + 'api.required' => 0, + 'description' => E::ts('If this is set true, the event(s) will be pulled from Twingle and updated locally'), + ]; $spec['twingle_api_key'] = [ 'name' => 'twingle_api_key', 'title' => E::ts('Twingle API key'), @@ -111,6 +118,10 @@ function civicrm_api3_twingle_event_Sync(array $params): array { Civi::cache('long')->set('twinglecampaign_twingle_api', $twingleApi); } + // Set pull flag + $pull = (isset($params['pull']) && $params['pull']); + unset($params['pull']); + // If an id or a event_id is provided, synchronize only this one campaign if (isset($params['id']) || isset($params['event_id'])) { @@ -141,7 +152,13 @@ function civicrm_api3_twingle_event_Sync(array $params): array { } // Synchronize events if (!empty($event_from_twingle)) { - return _eventSync($event, $event_from_twingle, $twingleApi, $params); + return _eventSync( + $event, + $event_from_twingle, + $twingleApi, + $params, + $pull + ); } // If Twingle does not know an event with the given event_id, give error @@ -271,7 +288,13 @@ function civicrm_api3_twingle_event_Sync(array $params): array { $event = _instantiateEvent($event_from_civicrm, $event_from_civicrm['id']); // sync event - $result = _eventSync($event, $event_from_twingle, $twingleApi, $params); + $result = _eventSync( + $event, + $event_from_twingle, + $twingleApi, + $params, + $pull + ); if ($result['is_error'] != 0) { $errors_occurred++; $result_values[$event->getId()] = @@ -394,6 +417,7 @@ function _updateEventLocally(array $event_from_twingle, * @param array $event_from_twingle * @param \CRM_TwingleCampaign_BAO_TwingleApiCall $twingleApi * @param array $params + * @param bool $pull Force pulling event from Twingle and update local campaign * * @return array * @throws \CiviCRM_API3_Exception @@ -401,12 +425,13 @@ function _updateEventLocally(array $event_from_twingle, function _eventSync(TwingleEvent $event, array $event_from_twingle, TwingleApiCall $twingleApi, - array $params): array { + array $params, + bool $pull = FALSE): array { // If Twingle's timestamp of the event differs from the timestamp of the // CiviCRM TwingleEvent campaign, update the campaign on CiviCRM's side. // NOTE: Changes on TwingleEvents are not meant to get pushed to Twingle - if ($event_from_twingle['updated_at'] != $event->lastUpdate()) { + if ($event_from_twingle['updated_at'] != $event->lastUpdate() || $pull) { return _updateEventLocally($event_from_twingle, $event, $params, $twingleApi); } diff --git a/api/v3/TwingleForm/Get.php b/api/v3/TwingleForm/Get.php index 3e2f867..86a5475 100644 --- a/api/v3/TwingleForm/Get.php +++ b/api/v3/TwingleForm/Get.php @@ -2,6 +2,7 @@ use CRM_TwingleCampaign_ExtensionUtil as E; use CRM_TwingleCampaign_Utils_ExtensionCache as Cache; +use CRM_TwingleCampaign_Utils_MatomoSnippet as MatomoSnippet; /** * TwingleForm.Get API specification (optional) @@ -14,6 +15,13 @@ use CRM_TwingleCampaign_Utils_ExtensionCache as Cache; function _civicrm_api3_twingle_form_Get_spec(array &$spec) { $spec['id'] = [ 'name' => 'id', + 'title' => E::ts('Campaign ID'), + 'type' => CRM_Utils_Type::T_INT, + 'api.required' => 0, + 'description' => E::ts('The campaign ID'), + ]; + $spec['twingle_project_id'] = [ + 'name' => 'twingle_project_id', 'title' => E::ts('TwingleProject ID'), 'type' => CRM_Utils_Type::T_INT, 'api.required' => 0, @@ -66,6 +74,13 @@ function civicrm_api3_twingle_form_Get(array $params): array { // Get custom fields $custom_field_mapping = Cache::getInstance()->getCustomFieldMapping(); + // Replace twingle_project_id key with custom field name + if (key_exists('twingle_project_id', $params)) { + $params[$custom_field_mapping['twingle_project_id']] = + $params['twingle_project_id']; + unset($params['twingle_project_id']); + } + // Replace twingle_project_type key with custom field name if (key_exists('twingle_project_type', $params)) { $params[$custom_field_mapping['twingle_project_type']] = @@ -96,16 +111,49 @@ function civicrm_api3_twingle_form_Get(array $params): array { 'project_type' => $value[$custom_field_mapping['twingle_project_type']], 'counter' => $value[$custom_field_mapping['twingle_project_counter']] ]; + $matomo_integration_enabled = Civi::settings()->get('twinglecampaign_matomo_integration', False); switch ($value[$custom_field_mapping['twingle_project_type']]) { case 'event': - $returnValues[$value['id']]['embed_code'] = - $value[$custom_field_mapping['twingle_project_eventall']]; + if ($matomo_integration_enabled) { + $returnValues[$value['id']]['embed_code'] = + MatomoSnippet::append_code( + $value[$custom_field_mapping['twingle_project_eventall']], + MatomoSnippet::get_event_tracker() + ); + } + else { + $returnValues[$value['id']]['embed_code'] = + $value[$custom_field_mapping['twingle_project_eventall']]; + } + break; + case 'shop': + if ($matomo_integration_enabled) { + $returnValues[$value['id']]['embed_code'] = + MatomoSnippet::append_code( + $value[$custom_field_mapping['twingle_project_widget']], + MatomoSnippet::get_ecommerce_tracker() + ); + } + else { + $returnValues[$value['id']]['embed_code'] = + $value[$custom_field_mapping['twingle_project_widget']]; + } break; default: - $returnValues[$value['id']]['embed_code'] = - $value[$custom_field_mapping['twingle_project_widget']]; + if ($matomo_integration_enabled) { + $returnValues[$value['id']]['embed_code'] = + MatomoSnippet::append_code( + $value[$custom_field_mapping['twingle_project_widget']], + MatomoSnippet::get_event_tracker() + ); + } + else { + $returnValues[$value['id']]['embed_code'] = + $value[$custom_field_mapping['twingle_project_widget']]; + } } } + return civicrm_api3_create_success($returnValues, $query, 'TwingleForm', 'Get'); } else { diff --git a/api/v3/TwingleForm/Getsingle.php b/api/v3/TwingleForm/Getsingle.php index e763683..09e18d5 100644 --- a/api/v3/TwingleForm/Getsingle.php +++ b/api/v3/TwingleForm/Getsingle.php @@ -12,6 +12,13 @@ use CRM_TwingleCampaign_ExtensionUtil as E; function _civicrm_api3_twingle_form_Getsingle_spec(array &$spec) { $spec['id'] = [ 'name' => 'id', + 'title' => E::ts('Campaign ID'), + 'type' => CRM_Utils_Type::T_INT, + 'api.required' => 0, + 'description' => E::ts('The campaign ID'), + ]; + $spec['twingle_project_id'] = [ + 'name' => 'twingle_project_id', 'title' => E::ts('TwingleProject ID'), 'type' => CRM_Utils_Type::T_INT, 'api.required' => 0, diff --git a/api/v3/TwingleProject/Create.php b/api/v3/TwingleProject/Create.php index 6d2bcd8..2c60095 100644 --- a/api/v3/TwingleProject/Create.php +++ b/api/v3/TwingleProject/Create.php @@ -86,8 +86,7 @@ function civicrm_api3_twingle_project_Create(array $params): array { unset($result['project_id']); $project = new TwingleProject($result, $params['id']); unset($params['id']); - $project->update($params); - $project->setEmbedData($params); + $project->merge($params); } // If no id is provided, try to create a new project with provided values else { diff --git a/api/v3/TwingleProject/Sync.php b/api/v3/TwingleProject/Sync.php index 7768997..ca72d0e 100644 --- a/api/v3/TwingleProject/Sync.php +++ b/api/v3/TwingleProject/Sync.php @@ -35,6 +35,13 @@ function _civicrm_api3_twingle_project_Sync_spec(array &$spec) { 'api.required' => 0, 'description' => E::ts('If this is set true, no database change will be made'), ]; + $spec['pull'] = [ + 'name' => 'pull', + 'title' => E::ts('Pull from Twingle'), + 'type' => CRM_Utils_Type::T_BOOLEAN, + 'api.required' => 0, + 'description' => E::ts('If this is set true, the project(s) will be pulled from Twingle and updated locally'), + ]; $spec['twingle_api_key'] = [ 'name' => 'twingle_api_key', 'title' => E::ts('Twingle API key'), @@ -90,6 +97,10 @@ function civicrm_api3_twingle_project_Sync(array $params): array { Civi::cache('long')->set('twinglecampaign_twingle_api', $twingleApi); } + // Set pull flag + $pull = (isset($params['pull']) && $params['pull']); + unset($params['pull']); + // If an id or a project_id is given, synchronize only this one campaign if (isset($params['id']) || isset($params['project_id'])) { @@ -106,9 +117,15 @@ function civicrm_api3_twingle_project_Sync(array $params): array { $id = $result['id']; $project = new TwingleProject($result, $id); - // Synchronize projects + // Synchronize project if (!empty($project_from_twingle)) { - return _projectSync($project, $project_from_twingle, $twingleApi, $params); + return _projectSync( + $project, + $project_from_twingle, + $twingleApi, + $params, + $pull + ); } // If Twingle does not know a project with the given project_id, give error @@ -159,8 +176,7 @@ function civicrm_api3_twingle_project_Sync(array $params): array { $projects_from_twingle = $twingleApi->getProject(); // Get all TwingleProjects from CiviCRM - $projects_from_civicrm = civicrm_api3('TwingleProject', 'get', - ['is_active' => 1,]); + $projects_from_civicrm = civicrm_api3('TwingleProject', 'get'); // If call to TwingleProject.get failed, forward error message if ($projects_from_civicrm['is_error'] != 0) { @@ -175,8 +191,10 @@ function civicrm_api3_twingle_project_Sync(array $params): array { // Push missing projects to Twingle $returnValues = []; foreach ($projects_from_civicrm['values'] as $project_from_civicrm) { - if (!in_array($project_from_civicrm['project_id'], - array_column($projects_from_twingle, 'id'))) { + if ( + !in_array($project_from_civicrm['project_id'], + array_column($projects_from_twingle, 'id'), + ) && $project_from_civicrm['is_active'] == 1) { // store campaign id in $id $id = $project_from_civicrm['id']; unset($project_from_civicrm['id']); @@ -197,8 +215,11 @@ function civicrm_api3_twingle_project_Sync(array $params): array { // Create missing projects as campaigns in CiviCRM foreach ($projects_from_twingle as $project_from_twingle) { - if (!in_array($project_from_twingle['id'], - array_column($projects_from_civicrm['values'], 'project_id'))) { + if ( + !in_array($project_from_twingle['id'], + array_column($projects_from_civicrm['values'], + 'project_id') + )) { $project = new TwingleProject($project_from_twingle); try { @@ -208,11 +229,16 @@ function civicrm_api3_twingle_project_Sync(array $params): array { $project->getResponse('Ready to create TwingleProject'); } - $project->create(TRUE); - $returnValues[$project->getId()] = - $project->getResponse('TwingleProject created'); + if ($project->create(TRUE)) { + $returnValues[$project->getId()] = + $project->getResponse('TwingleProject created'); + } + else { + $returnValues[$project->getId()] = + $project->getResponse('TwingleProject not selected for synchronization'); + } } catch (Exception $e) { - $errors[$result['id']] = $result['error_message']; + $errors[$project_from_twingle['id']] = $e->getMessage(); Civi::log()->error( E::LONG_NAME . ' could not create TwingleProject: ' . @@ -241,10 +267,12 @@ function civicrm_api3_twingle_project_Sync(array $params): array { // sync project $result = _projectSync( - $project, - $project_from_twingle, - $twingleApi, - $params); + $project, + $project_from_twingle, + $twingleApi, + $params, + $pull + ); if (!$result['is_error'] == 0) { $errors[$result['id']] = $result['error_message']; $returnValues[$project->getId()] = @@ -457,6 +485,7 @@ function _pushProjectToTwingle(TwingleProject $project, * @param array $project_from_twingle * @param \CRM_TwingleCampaign_BAO_TwingleApiCall $twingleApi * @param array $params + * @param bool $pull Force pulling project from Twingle and update local campaign * * @return array * @throws \CiviCRM_API3_Exception @@ -464,11 +493,13 @@ function _pushProjectToTwingle(TwingleProject $project, function _projectSync(TwingleProject $project, array $project_from_twingle, TwingleApiCall $twingleApi, - array $params): array { + array $params, + bool $pull = FALSE): array { // If Twingle's version of the project is newer than the CiviCRM // TwingleProject campaign, update the campaign - if ($project_from_twingle['last_update'] > $project->lastUpdate()) { + if ($project_from_twingle['last_update'] > $project->lastUpdate() || + $pull) { return _updateProjectLocally($project_from_twingle, $project, $params, $twingleApi); } diff --git a/info.xml b/info.xml index f0bd158..7971b85 100644 --- a/info.xml +++ b/info.xml @@ -14,13 +14,12 @@ https://lab.civicrm.org/Marc_Michalsky/de-forumzfd-twinglecampaign/-/issues http://www.gnu.org/licenses/agpl-3.0.html - 2020-09-25 - 1.0 - alpha + 2024-06-15 + 1.0.8 + stable - 5.14.0 + 5.74 - This is a new, undeveloped module de.systopia.xcm de.systopia.campaign diff --git a/l10n/de_DE/LC_MESSAGES/de_DE.mo b/l10n/de_DE/LC_MESSAGES/de_DE.mo new file mode 100644 index 0000000..7a64892 Binary files /dev/null and b/l10n/de_DE/LC_MESSAGES/de_DE.mo differ diff --git a/l10n/de_DE/LC_MESSAGES/twinglecampaign.po b/l10n/de_DE/LC_MESSAGES/de_DE.po similarity index 92% rename from l10n/de_DE/LC_MESSAGES/twinglecampaign.po rename to l10n/de_DE/LC_MESSAGES/de_DE.po index 7bfae67..5f83de4 100644 --- a/l10n/de_DE/LC_MESSAGES/twinglecampaign.po +++ b/l10n/de_DE/LC_MESSAGES/de_DE.po @@ -3,21 +3,21 @@ msgstr "" "Project-Id-Version: \n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" +"Last-Translator: \n" "Language-Team: \n" +"Language: de_DE\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 2.4.2\n" -"Last-Translator: \n" +"X-Generator: Poedit 3.0\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"Language: de_DE\n" #: CRM/TwingleCampaign/BAO/CustomField.php msgid "" -"Creation of custom field '%1' failed. Find more information in the logs." +"%1: Creation of custom field '%2' failed. Find more information in the logs." msgstr "" -"Erstellung von Custom Field '%1' fehlgeschlagen. Mehr Informationen in den " -"Logs." +"%1: Erstellen des benutzerdefinierten Feldes '%2' fehlgeschlagen. Mehr " +"Informationen in den Logs." #: CRM/TwingleCampaign/BAO/CustomField.php msgid "Creation of custom field failed. Find more information in the logs." @@ -33,6 +33,29 @@ msgstr "" "Erstellung der Custom Field '%1' fehlgeschlagen, weil bereits eine Custom " "Field mit selbem Namen existiert. Mehr Informationen in den Logs." +#: CRM/TwingleCampaign/BAO/CustomField.php +msgid "" +"Creation of custom field '%1' failed. Find more information in the logs." +msgstr "" +"Erstellung von Custom Field '%1' fehlgeschlagen. Mehr Informationen in den " +"Logs." + +#: CRM/TwingleCampaign/BAO/CustomField.php +msgid "" +"%1 could not get option group id for custom field '%2'. Find more " +"information in the logs." +msgstr "" +"%1 konnte die Option-Group-ID für das benutzerdefinierte Feld '%2' nicht " +"finden. Mehr Informationen in den Logs." + +#: CRM/TwingleCampaign/BAO/CustomField.php +msgid "" +"%1 could not create additional option values for custom field '%2'. Find " +"more information in the logs." +msgstr "" +"%1 konnte dem benutzerdefinierte Feld '%2' keine zusätzlichen Optionen " +"hinzufügen. Mehr Informationen in den Logs." + #: CRM/TwingleCampaign/BAO/CustomGroup.php msgid "" "Creation of custom group '%1' failed. Find more information in the logs." @@ -120,8 +143,8 @@ msgid "XCM Profile to match event initiators" msgstr "XCM-Profil zum Abgleichen von Event-Initiatoren" #: CRM/TwingleCampaign/Form/Settings.php -msgid "Start a case for event initiators" -msgstr "Einen Fall für Event-Initiatoren anlegen" +msgid "Default case to open for event initiators" +msgstr "Standarfdall, der für Event-Initiatoren eröffnet werden soll" #: CRM/TwingleCampaign/Form/Settings.php msgid "Create soft credits for event initiators" @@ -135,14 +158,22 @@ msgstr "Speichern" msgid "TwingleCampaign configuration saved" msgstr "TwingleCampaign Konfiguration gespeichert" -#: CRM/TwingleCampaign/Form/Settings.php -msgid "none" -msgstr "keine" - #: CRM/TwingleCampaign/Upgrader/Base.php msgid "Upgrade %1 to revision %2" msgstr "Upgrade %1 auf Revision %2" +#: CRM/TwingleCampaign/Upgrader.php +msgid "" +"Could not pull campaigns from Twingle to fill the campaign fields that were " +"created within this update: %1" +msgstr "" +"Die zusätzlichen Kampagnenfelder, die in diesem Update erstellt wurden, " +"konnten nicht von Twingle bezogen werden: %1" + +#: CRM/TwingleCampaign/Upgrader.php +msgid "Scheduled Job" +msgstr "Geplante Audgabe" + #: CRM/TwingleCampaign/Upgrader.php msgid "" "Syncronizes all TwingleProjects an TwingleEvents between CiviCRM and Twingle" @@ -154,18 +185,6 @@ msgstr "" msgid "Could not create scheduled job \"TwingleSync\"." msgstr "Geplante Aufgabe \"TwingleSync\" konnte nicht erstellt werden." -#: CRM/TwingleCampaign/Upgrader.php -msgid "Scheduled Job" -msgstr "Geplante Audgabe" - -#: CRM/TwingleCampaign/Upgrader.php -msgid "" -"Could not create scheduled job \"TwingleSync\". Your Campaigns will not get " -"synchronized to Twingle." -msgstr "" -"Geplante Aufgabe \"TwingleSync\" konnte nicht erstellt werden. Ihre " -"Kampagnen werden nicht mit Twingle synchronisiert werden." - #: CRM/TwingleCampaign/Upgrader.php msgid "Could not delete scheduled job \"TwingleSync\"." msgstr "Geplante Aufgabe \"TwingleSync\" konnte nicht gelöscht werden." @@ -182,6 +201,10 @@ msgstr "" msgid "Could not disable scheduled job \"TwingleSync\"." msgstr "Geplante Aufgabe \"TwingleSync\" konnte nicht deaktiviert werden." +#: CRM/TwingleCampaign/Utils/CaseTypes.php +msgid "none" +msgstr "keine" + #: CRM/TwingleCampaign/resources/campaigns.php msgid "Twingle Information" msgstr "Twingle Informationen" @@ -250,6 +273,14 @@ msgstr "" "Wähle den Projekt-Typ: erlaube Benutzern ihre eigenen Spenden-Events " "anzulegen oder eine Mitgliedschaft zu bezahlen." +#: CRM/TwingleCampaign/resources/campaigns.php +msgid "Case" +msgstr "Fall" + +#: CRM/TwingleCampaign/resources/campaigns.php +msgid "Which case should get opened for event creators?" +msgstr "Welcher Fall soll für Event-Initiatoren eröffnet werden?" + #: CRM/TwingleCampaign/resources/campaigns.php msgid "allow more" msgstr "Mehr zulassen" @@ -876,6 +907,17 @@ msgstr "Elternkampagne" msgid "Optional parent id for this Campaign" msgstr "Optionale Eltern-ID für diese Kampagne" +#: api/v3/TwingleCampaign/Create.php +msgid "Clone" +msgstr "Klonen" + +#: api/v3/TwingleCampaign/Create.php +msgid "" +"Set this value to true if this campaign is about to be cloned to recreate cid" +msgstr "" +"Setze diesen Wert auf true, wenn diese Kampagne gerade geklont wird, damit " +"die cid neu berechnet wird" + #: api/v3/TwingleCampaign/Get.php api/v3/TwingleCampaign/Getsingle.php msgid "Twingle Campaign CID" msgstr "Twingle Campaign CID" @@ -892,6 +934,14 @@ msgstr "Eltern Twingle Project ID" msgid "Twingle ID of the parent TwingleProject" msgstr "Twingle ID des Eltern-TwingleProject" +#: api/v3/TwingleCampaign/Get.php api/v3/TwingleCampaign/Sync.php +msgid "Parent Project ID" +msgstr "Eltern-Projekt-ID" + +#: api/v3/TwingleCampaign/Get.php api/v3/TwingleCampaign/Sync.php +msgid "ID of the parent TwingleProject" +msgstr "ID des Eltern-TWinglePRoject" + #: api/v3/TwingleCampaign/Get.php api/v3/TwingleCampaign/Getsingle.php #: api/v3/TwingleEvent/Get.php api/v3/TwingleEvent/Getsingle.php #: api/v3/TwingleProject/Get.php api/v3/TwingleProject/Getsingle.php @@ -1021,6 +1071,18 @@ msgstr "Bestätigt am" msgid "When the Event was confirmed by its initiator" msgstr "Wenn das Event vom Initiator bestätigt wurrde" +#: api/v3/TwingleEvent/Sync.php +msgid "Pull" +msgstr "Ziehen" + +#: api/v3/TwingleEvent/Sync.php +msgid "" +"If this is set true, the event(s) will be pulled from Twingle and updated " +"locally" +msgstr "" +"Wenn dies auf wahr gesetzt ist, werden die Events von Twingle gezogen und " +"lokal geupdated." + #: api/v3/TwingleEvent/Sync.php api/v3/TwingleSync/Sync.php msgid "Limit" msgstr "Limit" @@ -1122,6 +1184,18 @@ msgstr "Twingle Organisations ID" msgid "Your Twingle Organisation ID" msgstr "Ihre Twingle Organisations ID" +#: api/v3/TwingleProject/Sync.php +msgid "Pull from Twingle" +msgstr "Von Twingle ziehen" + +#: api/v3/TwingleProject/Sync.php +msgid "" +"If this is set true, the project(s) will be pulled from Twingle and updated " +"locally" +msgstr "" +"Wenn dies auf wahr gesetzt ist, werden die Projekte von Twingle gezogen und " +"lokal geupdated." + #: templates/CRM/TwingleCampaign/Form/Settings.tpl msgid "General Settings" msgstr "Allgemeine Einstellungen" @@ -1134,6 +1208,10 @@ msgstr "Twingle Event Einstellungen" msgid "Campaign cloning failed" msgstr "Klonen der Kampagne fehlgeschlagen" +#: twinglecampaign.php +msgid "TwingleCampaign was cloned." +msgstr "TwingleCampaign wurde geklont" + #: twinglecampaign.php msgid "TwingleCampaign update failed" msgstr "TwingleCampaign-Update fehlgeschlagen" diff --git a/l10n/de_DE/LC_MESSAGES/twinglecampaign.mo b/l10n/de_DE/LC_MESSAGES/twinglecampaign.mo deleted file mode 100644 index e4a39ee..0000000 Binary files a/l10n/de_DE/LC_MESSAGES/twinglecampaign.mo and /dev/null differ diff --git a/l10n/pot/twinglecampaign.pot b/l10n/pot/twinglecampaign.pot index 0516610..62ac159 100644 --- a/l10n/pot/twinglecampaign.pot +++ b/l10n/pot/twinglecampaign.pot @@ -1,5 +1,5 @@ #: ./CRM/TwingleCampaign/BAO/CustomField.php -msgid "Creation of custom field '%1' failed. Find more information in the logs." +msgid "%1: Creation of custom field '%2' failed. Find more information in the logs." msgstr "" #: ./CRM/TwingleCampaign/BAO/CustomField.php @@ -10,6 +10,18 @@ msgstr "" msgid "Creation of custom field '%1' failed, because a custom field with that name already exists. Find more information in the logs." msgstr "" +#: ./CRM/TwingleCampaign/BAO/CustomField.php +msgid "Creation of custom field '%1' failed. Find more information in the logs." +msgstr "" + +#: ./CRM/TwingleCampaign/BAO/CustomField.php +msgid "%1 could not get option group id for custom field '%2'. Find more information in the logs." +msgstr "" + +#: ./CRM/TwingleCampaign/BAO/CustomField.php +msgid "%1 could not create additional option values for custom field '%2'. Find more information in the logs." +msgstr "" + #: ./CRM/TwingleCampaign/BAO/CustomGroup.php msgid "Creation of custom group '%1' failed. Find more information in the logs." msgstr "" @@ -75,7 +87,7 @@ msgid "XCM Profile to match event initiators" msgstr "" #: ./CRM/TwingleCampaign/Form/Settings.php -msgid "Start a case for event initiators" +msgid "Default case to open for event initiators" msgstr "" #: ./CRM/TwingleCampaign/Form/Settings.php @@ -90,14 +102,18 @@ msgstr "" msgid "TwingleCampaign configuration saved" msgstr "" -#: ./CRM/TwingleCampaign/Form/Settings.php -msgid "none" -msgstr "" - #: ./CRM/TwingleCampaign/Upgrader/Base.php msgid "Upgrade %1 to revision %2" msgstr "" +#: ./CRM/TwingleCampaign/Upgrader.php +msgid "Could not pull campaigns from Twingle to fill the campaign fields that were created within this update: %1" +msgstr "" + +#: ./CRM/TwingleCampaign/Upgrader.php +msgid "Scheduled Job" +msgstr "" + #: ./CRM/TwingleCampaign/Upgrader.php msgid "Syncronizes all TwingleProjects an TwingleEvents between CiviCRM and Twingle" msgstr "" @@ -106,14 +122,6 @@ msgstr "" msgid "Could not create scheduled job \"TwingleSync\"." msgstr "" -#: ./CRM/TwingleCampaign/Upgrader.php -msgid "Scheduled Job" -msgstr "" - -#: ./CRM/TwingleCampaign/Upgrader.php -msgid "Could not create scheduled job \"TwingleSync\". Your Campaigns will not get synchronized to Twingle." -msgstr "" - #: ./CRM/TwingleCampaign/Upgrader.php msgid "Could not delete scheduled job \"TwingleSync\"." msgstr "" @@ -126,6 +134,10 @@ msgstr "" msgid "Could not disable scheduled job \"TwingleSync\"." msgstr "" +#: ./CRM/TwingleCampaign/Utils/CaseTypes.php +msgid "none" +msgstr "" + #: ./CRM/TwingleCampaign/resources/campaigns.php msgid "Twingle Information" msgstr "" @@ -186,6 +198,14 @@ msgstr "" msgid "Choose the project type. Allow users to create own events or to pay a membership fee." msgstr "" +#: ./CRM/TwingleCampaign/resources/campaigns.php +msgid "Case" +msgstr "" + +#: ./CRM/TwingleCampaign/resources/campaigns.php +msgid "Which case should get opened for event creators?" +msgstr "" + #: ./CRM/TwingleCampaign/resources/campaigns.php msgid "allow more" msgstr "" @@ -786,6 +806,14 @@ msgstr "" msgid "Optional parent id for this Campaign" msgstr "" +#: ./api/v3/TwingleCampaign/Create.php +msgid "Clone" +msgstr "" + +#: ./api/v3/TwingleCampaign/Create.php +msgid "Set this value to true if this campaign is about to be cloned to recreate cid" +msgstr "" + #: ./api/v3/TwingleCampaign/Get.php ./api/v3/TwingleCampaign/Getsingle.php msgid "Twingle Campaign CID" msgstr "" @@ -802,6 +830,14 @@ msgstr "" msgid "Twingle ID of the parent TwingleProject" msgstr "" +#: ./api/v3/TwingleCampaign/Get.php ./api/v3/TwingleCampaign/Sync.php +msgid "Parent Project ID" +msgstr "" + +#: ./api/v3/TwingleCampaign/Get.php ./api/v3/TwingleCampaign/Sync.php +msgid "ID of the parent TwingleProject" +msgstr "" + #: ./api/v3/TwingleCampaign/Get.php ./api/v3/TwingleCampaign/Getsingle.php ./api/v3/TwingleEvent/Get.php ./api/v3/TwingleEvent/Getsingle.php ./api/v3/TwingleProject/Get.php ./api/v3/TwingleProject/Getsingle.php msgid "Campaign Name" msgstr "" @@ -890,6 +926,14 @@ msgstr "" msgid "When the Event was confirmed by its initiator" msgstr "" +#: ./api/v3/TwingleEvent/Sync.php +msgid "Pull" +msgstr "" + +#: ./api/v3/TwingleEvent/Sync.php +msgid "If this is set true, the event(s) will be pulled from Twingle and updated locally" +msgstr "" + #: ./api/v3/TwingleEvent/Sync.php ./api/v3/TwingleSync/Sync.php msgid "Limit" msgstr "" @@ -982,6 +1026,14 @@ msgstr "" msgid "Your Twingle Organisation ID" msgstr "" +#: ./api/v3/TwingleProject/Sync.php +msgid "Pull from Twingle" +msgstr "" + +#: ./api/v3/TwingleProject/Sync.php +msgid "If this is set true, the project(s) will be pulled from Twingle and updated locally" +msgstr "" + #: ./templates/CRM/TwingleCampaign/Form/Settings.tpl msgid "General Settings" msgstr "" @@ -994,6 +1046,10 @@ msgstr "" msgid "Campaign cloning failed" msgstr "" +#: ./twinglecampaign.php +msgid "TwingleCampaign was cloned." +msgstr "" + #: ./twinglecampaign.php msgid "TwingleCampaign update failed" msgstr "" diff --git a/templates/CRM/TwingleCampaign/Form/Settings.tpl b/templates/CRM/TwingleCampaign/Form/Settings.tpl index 9ba2175..288c13e 100644 --- a/templates/CRM/TwingleCampaign/Form/Settings.tpl +++ b/templates/CRM/TwingleCampaign/Form/Settings.tpl @@ -17,8 +17,8 @@
-
{$form.twinglecampaign_start_case.label}
-
{$form.twinglecampaign_start_case.html}
+
{$form.twinglecampaign_default_case.label}
+
{$form.twinglecampaign_default_case.html}
@@ -26,6 +26,11 @@
{$form.twinglecampaign_soft_credits.html}
+
+
{$form.twinglecampaign_matomo_integration.label}
+
{$form.twinglecampaign_matomo_integration.html}
+
+
diff --git a/twinglecampaign.civix.php b/twinglecampaign.civix.php index 7d7e079..575f31e 100644 --- a/twinglecampaign.civix.php +++ b/twinglecampaign.civix.php @@ -7,9 +7,9 @@ * extension. */ class CRM_TwingleCampaign_ExtensionUtil { - const SHORT_NAME = "twinglecampaign"; - const LONG_NAME = "de.forumzfd.twinglecampaign"; - const CLASS_PREFIX = "CRM_TwingleCampaign"; + const SHORT_NAME = 'twinglecampaign'; + const LONG_NAME = 'de.forumzfd.twinglecampaign'; + const CLASS_PREFIX = 'CRM_TwingleCampaign'; /** * Translate a string using the extension's domain. @@ -193,8 +193,9 @@ function _twinglecampaign_civix_civicrm_disable() { * @param $op string, the type of operation being performed; 'check' or 'enqueue' * @param $queue CRM_Queue_Queue, (for 'enqueue') the modifiable list of pending up upgrade tasks * - * @return mixed based on op. for 'check', returns array(boolean) (TRUE if upgrades are pending) - * for 'enqueue', returns void + * @return mixed + * based on op. for 'check', returns array(boolean) (TRUE if upgrades are pending) + * for 'enqueue', returns void * * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_upgrade */ @@ -220,41 +221,18 @@ function _twinglecampaign_civix_upgrader() { * Search directory tree for files which match a glob pattern. * * Note: Dot-directories (like "..", ".git", or ".svn") will be ignored. - * Note: In Civi 4.3+, delegate to CRM_Utils_File::findFiles() + * Note: Delegate to CRM_Utils_File::findFiles(), this function kept only + * for backward compatibility of extension code that uses it. * * @param string $dir base dir * @param string $pattern , glob pattern, eg "*.txt" * - * @return array(string) + * @return array */ function _twinglecampaign_civix_find_files($dir, $pattern) { - if (is_callable(['CRM_Utils_File', 'findFiles'])) { - return CRM_Utils_File::findFiles($dir, $pattern); - } - - $todos = [$dir]; - $result = []; - while (!empty($todos)) { - $subdir = array_shift($todos); - foreach (_twinglecampaign_civix_glob("$subdir/$pattern") as $match) { - if (!is_dir($match)) { - $result[] = $match; - } - } - if ($dh = opendir($subdir)) { - while (FALSE !== ($entry = readdir($dh))) { - $path = $subdir . DIRECTORY_SEPARATOR . $entry; - if ($entry{0} == '.') { - } - elseif (is_dir($path)) { - $todos[] = $path; - } - } - closedir($dh); - } - } - return $result; + return CRM_Utils_File::findFiles($dir, $pattern); } + /** * (Delegated) Implements hook_civicrm_managed(). * @@ -362,7 +340,7 @@ function _twinglecampaign_civix_civicrm_themes(&$themes) { * @link http://php.net/glob * @param string $pattern * - * @return array, possibly empty + * @return array */ function _twinglecampaign_civix_glob($pattern) { $result = glob($pattern); @@ -470,8 +448,6 @@ function _twinglecampaign_civix_civicrm_alterSettingsFolders(&$metaDataFolders = * * @link https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_entityTypes */ - function _twinglecampaign_civix_civicrm_entityTypes(&$entityTypes) { - $entityTypes = array_merge($entityTypes, array ( - )); + $entityTypes = array_merge($entityTypes, []); } diff --git a/twinglecampaign.php b/twinglecampaign.php index e210c34..04ff8ca 100644 --- a/twinglecampaign.php +++ b/twinglecampaign.php @@ -4,6 +4,7 @@ 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_BAO_CustomField as CustomField; require_once 'twinglecampaign.civix.php'; @@ -46,6 +47,14 @@ function twinglecampaign_civicrm_config(&$config) { } } +function twinglecampaign_civicrm_postSave_civicrm_case_type() { + $twingle_project_case_custom_field = + CustomField::fetch('twingle_project_case'); + $newCaseType = json_decode($_POST['json'], True); + $twingle_project_case_custom_field->addOptions( + [$newCaseType['name'] => $newCaseType['title']] + ); +} /** * Implements hook_civicrm_postSave_Campaign(). @@ -59,16 +68,47 @@ function twinglecampaign_civicrm_config(&$config) { */ function twinglecampaign_civicrm_postSave_civicrm_campaign($dao) { - if (empty($_SESSION['CiviCRM']['de.forumzfd.twinglecampaign']['no_hook']) || - $_SESSION['CiviCRM']['de.forumzfd.twinglecampaign']['no_hook'] != TRUE) { + $twingle_project_campaign_type_id = _get_campaign_type_id_twingle_project(); + $twingle_campaign_campaign_type_id = _get_campaign_type_id_twingle_campaign(); + // If $campaign_type_id is a TwingleProject or TwingleCampaign campaign, + // synchronize it + if ( + $dao->campaign_type_id == $twingle_project_campaign_type_id || + $dao->campaign_type_id == $twingle_campaign_campaign_type_id + ) { - // If the db transaction is still running, add a function to it that will - // be called afterwards - if (CRM_Core_Transaction::isActive()) { + if (empty($_SESSION['CiviCRM']['de.forumzfd.twinglecampaign']['no_hook']) || + $_SESSION['CiviCRM']['de.forumzfd.twinglecampaign']['no_hook'] != TRUE) { - if (_validateAndSendInput($dao->id, $dao->campaign_type_id)) { + // If request is not an API-Call + if ( + ((isset($_GET['action']) && $_GET['action'] != 'create') || + (isset($_POST['action']) && $_POST['action'] != 'create')) || + (!isset($_GET['action']) && !isset($_POST['action'])) + ) { + // If the db transaction is still running, add a function to it that will + // be called afterwards + if (CRM_Core_Transaction::isActive()) { + + if (_validateAndSendInput($dao->id, $dao->campaign_type_id)) { + + CRM_Core_Transaction::addCallback( + CRM_Core_Transaction::PHASE_POST_COMMIT, + 'twinglecampaign_postSave_campaign_update_callback', + [$dao->id, $dao->campaign_type_id] + ); + } + } + + // If the transaction is already finished, call the function directly + else { + twinglecampaign_postSave_campaign_update_callback($dao->id, $dao->campaign_type_id); + } + + } + else { CRM_Core_Transaction::addCallback( CRM_Core_Transaction::PHASE_POST_COMMIT, 'twinglecampaign_postSave_campaign_update_callback', @@ -76,12 +116,6 @@ function twinglecampaign_civicrm_postSave_civicrm_campaign($dao) { ); } } - - // If the transaction is already finished, call the function directly - else { - twinglecampaign_postSave_campaign_update_callback($dao->id, $dao->campaign_type_id); - } - } // Remove no hook flag unset($_SESSION['CiviCRM']['de.forumzfd.twinglecampaign']['no_hook']); @@ -105,73 +139,58 @@ function twinglecampaign_postSave_campaign_update_callback( $twingle_project_campaign_type_id = _get_campaign_type_id_twingle_project(); $twingle_campaign_campaign_type_id = _get_campaign_type_id_twingle_campaign(); - // If $campaign_type_id is a TwingleProject or TwingleCampaign campaign, - // synchronize it - if ( - $campaign_type_id == $twingle_project_campaign_type_id || - $campaign_type_id == $twingle_campaign_campaign_type_id - ) { + // Set $entity for $campaign_type_id + if ($campaign_type_id == $twingle_project_campaign_type_id) { + $entity = 'TwingleProject'; + } + elseif ($campaign_type_id == $twingle_campaign_campaign_type_id) { + $entity = 'TwingleCampaign'; + } + else { + Civi::log()->error( + E::SHORT_NAME . + " Update of TwingleCampaigns failed: Unknown campaign type (id: $campaign_type_id)" + ); + CRM_Core_Session::setStatus( + E::ts('Unknown campaign type'), + E::ts('Campaign type id: %1', [1 => $campaign_type_id]), + error, + [unique => TRUE] + ); + return; + } - // Set $entity for $campaign_type_id - if ($campaign_type_id == $twingle_project_campaign_type_id) { - $entity = 'TwingleProject'; - } - else { - $entity = 'TwingleCampaign'; - } - - if (isset($_POST['action'])) { - if ($_POST['action'] == 'clone' && $entity == 'TwingleProject') { - unset($_POST['action']); - $result = civicrm_api3('TwingleProject', 'getsingle', - ['id' => $campaign_id] - ); - $id = $result['id']; - unset($result['id']); - $project = new TwingleProject($result, $id); - try { - $project->clone(); - } catch (Exception $e) { - Civi::log()->error( - E::LONG_NAME . - ' could not clone ' . $entity . ': ' . $e->getMessage() - ); - CRM_Core_Session::setStatus( - $e->getMessage(), - E::ts("Campaign cloning failed"), - error, - [unique => TRUE] - ); - } - } - } - - // If a TwingleProject is getting saved - elseif ($entity == 'TwingleProject') { - - // Synchronize all child TwingleCampaign campaigns + if (isset($_POST['action'])) { + if ($_POST['action'] == 'clone' && $entity == 'TwingleProject') { + unset($_POST['action']); + $result = civicrm_api3('TwingleProject', 'getsingle', + ['id' => $campaign_id] + ); + $id = $result['id']; + unset($result['id']); + $project = new TwingleProject($result, $id); try { - civicrm_api3( - 'TwingleCampaign', - 'sync', - ['parent_id' => $campaign_id]); - } catch (CiviCRM_API3_Exception $e) { + $project->clone(); + } catch (Exception $e) { + Civi::log()->error( + E::LONG_NAME . + ' could not clone ' . $entity . ': ' . $e->getMessage() + ); CRM_Core_Session::setStatus( $e->getMessage(), - E::ts("TwingleCampaign update failed"), - error, [unique => TRUE] - ); - Civi::log()->error( - E::SHORT_NAME . - ' Update of TwingleCampaigns failed: ' . $e->getMessage() + E::ts("Campaign cloning failed"), + error, + [unique => TRUE] ); } } - else { + if ($_POST['action'] == 'clone' && $entity == 'TwingleCampaign') { + unset($_POST['action']); try { civicrm_api3('TwingleCampaign', 'create', - ['id' => $campaign_id, 'parent_id' => $_POST['parent_id']]); - CRM_Utils_System::setUFMessage(E::ts('TwingleCampaign was saved.')); + ['id' => $campaign_id, 'clone' => true] + ); + CRM_Utils_System::setUFMessage(E::ts('TwingleCampaign was cloned.')); } catch (CiviCRM_API3_Exception $e) { Civi::log()->error( 'twinglecampaign_postSave_callback ' . $e->getMessage() @@ -179,6 +198,39 @@ function twinglecampaign_postSave_campaign_update_callback( } } } + + // If a TwingleProject is getting saved + elseif ($entity == 'TwingleProject') { + + // Synchronize all child TwingleCampaign campaigns + try { + civicrm_api3( + 'TwingleCampaign', + 'sync', + ['parent_id' => $campaign_id]); + } catch (CiviCRM_API3_Exception $e) { + CRM_Core_Session::setStatus( + $e->getMessage(), + E::ts("TwingleCampaign update failed"), + error, [unique => TRUE] + ); + Civi::log()->error( + E::SHORT_NAME . + ' Update of TwingleCampaigns failed: ' . $e->getMessage() + ); + } + } + else { + try { + civicrm_api3('TwingleCampaign', 'create', + ['id' => $campaign_id, 'parent_id' => $_POST['parent_id']]); + CRM_Utils_System::setUFMessage(E::ts('TwingleCampaign was saved.')); + } catch (CiviCRM_API3_Exception $e) { + Civi::log()->error( + 'twinglecampaign_postSave_callback ' . $e->getMessage() + ); + } + } } function _get_campaign_type_id_twingle_project() { @@ -193,6 +245,7 @@ function _get_campaign_type_id_twingle_campaign() { /** * Callback to sync a project after its creation. + * * @param int $campaign_id */ function twinglecampaign_postSave_project_create_callback( @@ -225,6 +278,7 @@ function twinglecampaign_postSave_project_create_callback( * * @return bool * @throws \CiviCRM_API3_Exception + * @throws \Exception */ function _validateAndSendInput($id, $campaign_type_id): bool { @@ -259,6 +313,11 @@ function _validateAndSendInput($id, $campaign_type_id): bool { // Update project $project->update($customFields); + // Set name if provided + if (isset($_POST['title'])) { + $project->setName($_POST['title']); + } + // Validate project values $validation = $project->validate(); @@ -352,8 +411,7 @@ function _validateAndSendInput($id, $campaign_type_id): bool { CRM_Core_Session::setStatus( $errorMessage, E::ts("Input validation failed"), - error, - [unique => TRUE] + error ); // Validation failed return FALSE;