diff --git a/composer.json b/composer.json index db6adf2..62301ec 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,8 @@ "patches": { "drupal/core": { "#2865059: Multiple machine name elements from same source": "https://www.drupal.org/files/issues/2865059-13-allow_multiple_machine_name_per_source.patch", - "#736066: ajax.js insert command sometimes wraps content in a div": "https://www.drupal.org/files/issues/736066-296.patch" + "#736066: ajax.js insert command sometimes wraps content in a div": "https://www.drupal.org/files/issues/736066-296.patch", + "#2584729: Hook entity_field_storage_info is broken": "https://www.drupal.org/files/issues/core-entity-sql-content-entity-storage-2583111-1_0.patch" }, "drupal/ctools": { "#2667652: Option to expose filters in block on views block display": "https://www.drupal.org/files/issues/ctools-option_to_expose-2667652-3.patch", diff --git a/config/install/contacts.contact_tab.crm_communications_tab.yml b/config/install/contacts.contact_tab.crm_communications_tab.yml new file mode 100644 index 0000000..8c8f5f0 --- /dev/null +++ b/config/install/contacts.contact_tab.crm_communications_tab.yml @@ -0,0 +1,34 @@ +langcode: en +status: true +dependencies: + config: + - profile.type.crm_communications + module: + - profile +id: crm_communications_tab +label: Communications +path: communications +layout: contacts_tab_content.stacked +weight: null +relationships: + profile_crm_communications: + id: 'typed_data_entity_relationship:entity:user:profile_crm_communications' + name: profile_crm_communications + source: user +blocks: + contacts_entity_profile_crm_communications: + id: 'contacts_entity:profile-crm_communications' + label: 'Communication Preferences' + provider: contacts + label_display: '0' + mode: view + create: true + operation: contacts_dashboard + view_mode: contacts_dashboard + edit_link: content + edit_id: edit + region: top + context_mapping: + entity: profile_crm_communications + user: user + name: contacts_entity_profile_crm_communications diff --git a/config/install/profile.type.crm_communications.yml b/config/install/profile.type.crm_communications.yml new file mode 100644 index 0000000..23dcfd4 --- /dev/null +++ b/config/install/profile.type.crm_communications.yml @@ -0,0 +1,16 @@ +langcode: en +status: true +dependencies: { } +id: crm_communications +label: Communications +registration: false +multiple: false +roles: + authenticated: '0' + administrator: '0' + crm_indiv: '0' + crm_org: '0' + student: '0' +weight: 0 +use_revisions: false +description: '' diff --git a/contacts.module b/contacts.module index 88f5a7f..56bc015 100644 --- a/contacts.module +++ b/contacts.module @@ -5,6 +5,8 @@ * Module related hook implementations for the contacts module. */ +use Drupal\Core\Entity\EntityTypeInterface; +use Drupal\Core\Field\BaseFieldDefinition; use Drupal\Core\Template\Attribute; use Drupal\Core\Url; use Drupal\Core\Link; @@ -723,3 +725,51 @@ function contacts_preprocess_block(&$variables, $hook) { function contacts_form_user_register_form_alter(&$form, FormStateInterface $form_state, $form_id) { $form['account']['roles']['#default_value'][] = 'crm_indiv'; } + +/** + * Implements hook_entity_bundle_field_info(). + */ +function contacts_entity_bundle_field_info(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) { + // Create the crm_opt_out field. + if ($entity_type->id() == 'profile' && $bundle == 'crm_communications') { + $fields['crm_opt_out'] = BaseFieldDefinition::create('boolean') + ->setName('crm_opt_out') + ->setLabel('Opt Out') + ->setTargetEntityTypeId('profile') + ->setTargetBundle('crm_communications') + ->setDefaultValue(0) + ->setDisplayConfigurable("form", TRUE) + ->setDisplayConfigurable("view", TRUE) + ->setDisplayOptions("form", [ + 'type' => 'boolean_checkbox', + 'region' => 'content', + 'settings' => ['display_label', TRUE], + 'weight' => 0, + ]) + ->setDisplayOptions("view", [ + 'type' => 'boolean', + 'region' => 'content', + 'settings' => ['display_label', TRUE], + 'weight' => 0, + ]) + ->setSettings(['on_label' => 'Yes', 'off_label' => 'No']); + + return $fields; + } +} + +/** + * Implements hook_entity_field_storage_info(). + */ +function contacts_entity_field_storage_info(EntityTypeInterface $entity_type) { + // Ensure backing storage exists for crm_communications. + if ($entity_type->id() == 'profile') { + $storage['crm_opt_out'] = BaseFieldDefinition::create('boolean') + ->setName('crm_opt_out') + ->setCardinality(1) + ->setTargetBundle('crm_communications') + ->setTranslatable(TRUE) + ->setTargetEntityTypeId('profile'); + return $storage; + } +} diff --git a/modules/contacts_mailchimp/contacts_mailchimp.info.yml b/modules/contacts_mailchimp/contacts_mailchimp.info.yml new file mode 100644 index 0000000..ed53c97 --- /dev/null +++ b/modules/contacts_mailchimp/contacts_mailchimp.info.yml @@ -0,0 +1,8 @@ +name: Contacts MailChimp Integration +description: "MailChimp integration for the Contacts module" +type: module +core: 8.x + +dependencies: + - contacts + - mailchimp_lists (>=8.x-1.6) diff --git a/modules/contacts_mailchimp/contacts_mailchimp.module b/modules/contacts_mailchimp/contacts_mailchimp.module new file mode 100644 index 0000000..f79d599 --- /dev/null +++ b/modules/contacts_mailchimp/contacts_mailchimp.module @@ -0,0 +1,60 @@ +getEntityTypeId() == 'profile' && $entity->bundle() == 'crm_communications' && $entity->hasField($field_name)) { + $opt_out = $entity->get($field_name)->value; + + if (isset($entity->original)) { + $original = $entity->original->get($field_name)->value; + } + else { + $original = NULL; + } + + // TODO: Investigate making this asynchronous. + if (($original === 0 || $original == NULL) && $opt_out === 1 && $entity->uid->entity->hasField('mail')) { + $email = $entity->uid->entity->mail->value; + $lists = mailchimp_get_lists_for_email($email); + foreach ($lists as $list) { + mailchimp_unsubscribe($list->id, $email); + } + } + } +} + +/** + * Implements hook_mailchimp_process_webhook(). + */ +function contacts_mailchimp_mailchimp_process_webhook($type, $data) { + // If user unsubscribes from mailchimp, treat it as a full opt-out. + if ($type == 'unsubscribe') { + $ids = \Drupal::entityQuery('profile') + ->condition('uid.entity.mail', $data['email']) + ->condition('type', 'crm_communications') + ->execute(); + + $profiles = Profile::loadMultiple($ids); + + /* @var $profile \Drupal\profile\Entity\Profile */ + foreach ($profiles as $profile) { + if ($profile->hasField('crm_opt_out')) { + $profile->set('crm_opt_out', 1); + $profile->save(); + } + } + } +}