contacts_events-8.x-1.x-dev/contacts_events.install
contacts_events.install
<?php /** * @file * Install and update code for the Contacts Events module. */ use Drupal\commerce_order\Entity\OrderItemType; use Drupal\Core\Entity\Sql\SqlContentEntityStorage; use Drupal\Core\Entity\Sql\SqlContentEntityStorageException; use Drupal\Core\Field\BaseFieldDefinition; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\entity\BundleFieldDefinition; use Drupal\search_api\Item\Field; /** * Implements hook_install(). */ function contacts_events_install() { $fields['creator'] = BundleFieldDefinition::create('entity_reference') ->setName('creator') ->setLabel(t('Creator')) ->setTargetEntityTypeId('commerce_order') ->setTargetBundle('contacts_booking') ->setDescription(t('The user ID of the order creator.')) ->setRevisionable(TRUE) ->setSetting('target_type', 'user') ->setSetting('handler', 'default') ->setTranslatable(FALSE) ->setDisplayConfigurable('form', FALSE) ->setDisplayConfigurable('view', FALSE); $fields['state_log'] = BundleFieldDefinition::create('status_log') ->setName('state_log') ->setLabel(t('State History')) ->setTargetEntityTypeId('commerce_order') ->setTargetBundle('contacts_booking') ->setDescription(t('A log of when the status was changed and by whom.')) ->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) ->setSettings([ 'source_field' => 'state', ]) ->setDisplayConfigurable('view', FALSE) ->setDisplayConfigurable('form', FALSE); foreach ($fields as $field_name => $field) { \Drupal::entityDefinitionUpdateManager() ->installFieldStorageDefinition($field_name, 'commerce_order', 'contacts_events', $field); } /** @var \Drupal\search_api\IndexInterface $index */ $index = \Drupal::entityTypeManager()->getStorage('search_api_index')->load('contacts_index'); if ($index) { // Create the Booking Manager booking reference field. $bm_field = new Field($index, 'bm_order_number'); $bm_field->setType('text'); $bm_field->setPropertyPath('ce_bookings_managed:entity:order_number'); $bm_field->setDatasourceId('entity:user'); $bm_field->setLabel('Bookings (manager) » Order » Order number'); $bm_field->setBoost(8); // Create the Delegate booking reference field. $delegate_field = new Field($index, 'delegate_order_number'); $delegate_field->setType('text'); $delegate_field->setPropertyPath('ce_bookings_delegate:entity:order_number'); $delegate_field->setDatasourceId('entity:user'); $delegate_field->setLabel('Bookings (delegate) » Order » Order number'); $delegate_field->setBoost(5); // Add to fields to the index. $index->addField($bm_field); $index->addField($delegate_field); $index->save(); } } /** * Adds the creator field to Booking orders. */ function contacts_events_update_8001() { $field = BundleFieldDefinition::create('entity_reference') ->setLabel(t('Creator')) ->setTargetEntityTypeId('commerce_order') ->setTargetBundle('contacts_booking') ->setDescription(t('The user ID of the order creator.')) ->setRevisionable(TRUE) ->setSetting('target_type', 'user') ->setSetting('handler', 'default') ->setTranslatable(FALSE) ->setDisplayConfigurable('form', FALSE) ->setDisplayConfigurable('view', FALSE); \Drupal::entityDefinitionUpdateManager() ->installFieldStorageDefinition('creator', 'commerce_order', 'contacts_events', $field); } /** * Adds creator field to payments. */ function contacts_events_update_8002() { $field = BaseFieldDefinition::create('entity_reference') ->setName('creator') ->setTargetEntityTypeId('commerce_payment') ->setLabel(new TranslatableMarkup('Creator')) ->setDescription(new TranslatableMarkup('The user ID of the payment creator.')) ->setRevisionable(TRUE) ->setSetting('target_type', 'user') ->setSetting('handler', 'default') ->setTranslatable(FALSE) ->setDisplayConfigurable('form', FALSE) ->setDisplayConfigurable('view', FALSE); \Drupal::entityDefinitionUpdateManager() ->installFieldStorageDefinition('creator', 'commerce_payment', 'contacts_events', $field); } /** * Adds the link text overrides field. */ function contacts_events_update_8003() { $field = BaseFieldDefinition::create('contacts_events_text_overrides') ->setLabel('Link text overrides') ->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) ->setDisplayConfigurable('view', FALSE) ->setDisplayConfigurable('form', TRUE) ->setDisplayOptions('view', [ 'region' => 'hidden', ]); \Drupal::entityDefinitionUpdateManager() ->installFieldStorageDefinition('link_overrides', 'contacts_event', 'contacts_events', $field); } /** * Adds the settings field. */ function contacts_events_update_8004() { $field = BundleFieldDefinition::create('contacts_events_settings') ->setLabel('Settings') ->setDisplayConfigurable('view', FALSE) ->setDisplayOptions('view', ['region' => 'hidden']) ->setDisplayConfigurable('form', FALSE) ->setDisplayOptions('form', ['region' => 'hidden']); \Drupal::entityDefinitionUpdateManager() ->installFieldStorageDefinition('settings', 'contacts_event', 'contacts_events', $field); } /** * Adds the state log field to Booking orders. */ function contacts_events_update_8005() { $field = BundleFieldDefinition::create('status_log') ->setName('state_log') ->setLabel(t('Status History')) ->setTargetEntityTypeId('commerce_order') ->setTargetBundle('contacts_booking') ->setDescription(t('A log of when the status was changed and by whom.')) ->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) ->setSettings([ 'source_field' => 'state', ]) ->setDisplayConfigurable('view', FALSE) ->setDisplayConfigurable('form', FALSE); \Drupal::entityDefinitionUpdateManager() ->installFieldStorageDefinition('state_log', 'commerce_order', 'contacts_events', $field); } /** * Install Booking Transfer payment gateway. */ function contacts_events_update_8006() { $config_name = 'commerce_payment.commerce_payment_gateway.booking_transfer'; if (!\Drupal::service('config.storage')->exists($config_name)) { $config = \Drupal::configFactory()->getEditable($config_name); $values = [ 'uuid' => \Drupal::service('uuid')->generate(), 'langcode' => 'en', 'status' => FALSE, 'dependencies' => [], 'id' => 'booking_transfer', 'label' => 'Booking Transfer', 'weight' => 10, 'plugin' => 'manual', 'configuration' => [ 'instructions' => [ 'value' => '', 'format' => 'plain_text', ], 'display_label' => 'Booking Transfer', 'mode' => 'n/a', 'payment_method_types' => [ 'credit_card', ], ], 'conditions' => [], 'conditionOperator' => 'AND', ]; foreach ($values as $id => $value) { $config->set($id, $value); } $config->save(); } } /** * Add the cut_off_confirmed column to booking window field tables. */ function contacts_events_update_8007() { $schema = \Drupal::database()->schema(); $entity_type_manager = \Drupal::entityTypeManager(); /** @var \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager */ $entity_field_manager = \Drupal::service('entity_field.manager'); $entity_field_map = $entity_field_manager->getFieldMapByFieldType('booking_windows'); $entity_storage_schema_sql = \Drupal::keyValue('entity.storage_schema.sql'); $entity_definitions_installed = \Drupal::keyValue('entity.definitions.installed'); foreach ($entity_field_map as $entity_type_id => $field_map) { $entity_storage = $entity_type_manager->getStorage($entity_type_id); // Only SQL storage based entities are supported / throw known exception. if (!($entity_storage instanceof SqlContentEntityStorage)) { continue; } $entity_type = $entity_type_manager->getDefinition($entity_type_id); $field_storage_definitions = $entity_field_manager->getFieldStorageDefinitions($entity_type_id); /** @var Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */ $table_mapping = $entity_storage->getTableMapping($field_storage_definitions); // Only need field storage definitions of booking windows fields. /** @var \Drupal\Core\Field\FieldStorageDefinitionInterface $field_storage_definition */ foreach (array_intersect_key($field_storage_definitions, $field_map) as $field_storage_definition) { $field_name = $field_storage_definition->getName(); try { $table = $table_mapping->getFieldTableName($field_name); $cut_off_col_name = $table_mapping->getFieldColumnName($field_storage_definition, 'cut_off'); } catch (SqlContentEntityStorageException $e) { // Custom storage? Broken site? No matter what, if there is no table // or column, there's little we can do. continue; } // See if the field has a revision table. $revision_table = NULL; if ($entity_type->isRevisionable() && $field_storage_definition->isRevisionable()) { if ($table_mapping->requiresDedicatedTableStorage($field_storage_definition)) { $revision_table = $table_mapping->getDedicatedRevisionTableName($field_storage_definition); } elseif ($table_mapping->allowsSharedTableStorage($field_storage_definition)) { $revision_table = $entity_type->getRevisionDataTable() ?: $entity_type->getRevisionTable(); } } // Load the installed field schema so that it can be updated. $schema_key = "$entity_type_id.field_schema_data.$field_name"; $field_schema_data = $entity_storage_schema_sql->get($schema_key); // Get the existing spec. $cut_off_spec = [ 'description' => 'The paid in full by date.', 'type' => 'varchar', 'length' => 20, ]; // Update the new column. $schema->changeField($table, $cut_off_col_name, $cut_off_col_name, $cut_off_spec); if ($revision_table) { $schema->changeField($revision_table, $cut_off_col_name, $cut_off_col_name, $cut_off_spec); } // Update the existing column in the installed field schema. if ($field_schema_data) { $field_schema_data[$table]['fields'][$cut_off_col_name] = $cut_off_spec; $field_schema_data[$table]['fields'][$cut_off_col_name]['not null'] = FALSE; if ($revision_table) { $field_schema_data[$revision_table]['fields'][$cut_off_col_name] = $cut_off_spec; } $entity_storage_schema_sql->set($schema_key, $field_schema_data); } // Get the new column name and spec. $cut_off_confirmed_col_name = $table_mapping->getFieldColumnName($field_storage_definition, 'cut_off_confirmed'); $cut_off_confirmed_spec = [ 'description' => 'The confirmed by date.', 'type' => 'varchar', 'length' => 20, ]; // Add the new column. $install_spec = $cut_off_confirmed_spec + ['initial_from_field' => $cut_off_col_name]; $schema->addField($table, $cut_off_confirmed_col_name, $install_spec); if ($revision_table) { $schema->addField($revision_table, $cut_off_confirmed_col_name, $install_spec); } // Add the new column to the installed field schema. if ($field_schema_data) { $field_schema_data[$table]['fields'][$cut_off_confirmed_col_name] = $cut_off_confirmed_spec; $field_schema_data[$table]['fields'][$cut_off_confirmed_col_name]['not null'] = FALSE; if ($revision_table) { $field_schema_data[$revision_table]['fields'][$cut_off_confirmed_col_name] = $cut_off_confirmed_spec; } $entity_storage_schema_sql->set($schema_key, $field_schema_data); } // Update the installed field storage definition. if ($table_mapping->allowsSharedTableStorage($field_storage_definition)) { $key = "$entity_type_id.field_storage_definitions"; if ($definitions = $entity_definitions_installed->get($key)) { $definitions[$field_name] = $field_storage_definition; $entity_definitions_installed->set($key, $definitions); } } } } } /** * Add missing fields to order item bundles. */ function contacts_events_update_8008() { /** @var \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager */ $entity_field_manager = \Drupal::service('entity_field.manager'); /** @var \Drupal\Core\Field\FieldDefinitionListener $field_definition_listener*/ $field_definition_listener = \Drupal::service('field_definition.listener'); /** @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundle_manager */ $bundle_manager = \Drupal::service('entity_type.bundle.info'); $bundle_info = $bundle_manager->getBundleInfo('commerce_order_item'); $order_item_types = OrderItemType::loadMultiple(array_keys($bundle_info)); $state = []; foreach ($order_item_types as $order_item_type) { if ($order_item_type->getOrderTypeId() == 'contacts_booking') { // Check each bundle for commerce_order_item. If it's for the booking // order type, add the state and confirmed fields if they don't have it // already. $field_definitions = $entity_field_manager->getFieldDefinitions('commerce_order_item', $order_item_type->id()); if (!isset($field_definitions['state'])) { $state_field = BundleFieldDefinition::create('state') ->setName('state') ->setTargetEntityTypeId('commerce_order_item') ->setTargetBundle($order_item_type->id()) ->setLabel(new TranslatableMarkup('State')) ->setDescription(new TranslatableMarkup('The order item state.')) ->setRequired(TRUE) ->setSetting('max_length', 255) ->setSetting('workflow', 'contacts_events_order_item_process') ->setDisplayOptions('view', [ 'label' => 'hidden', 'type' => 'state_transition_form', 'weight' => 10, ]) ->setDisplayConfigurable('form', TRUE) ->setDisplayConfigurable('view', TRUE); $field_definition_listener->onFieldDefinitionCreate($state_field); $state[$order_item_type->id()][] = 'state'; } if (!isset($field_definitions['confirmed'])) { $confirmed_field = BundleFieldDefinition::create('timestamp') ->setName('confirmed') ->setTargetEntityTypeId('commerce_order_item') ->setTargetBundle($order_item_type->id()) ->setLabel(new TranslatableMarkup('Confirmed')) ->setDescription(new TranslatableMarkup('The time when the order item was confirmed.')) ->setReadOnly(TRUE) ->setDisplayConfigurable('form', FALSE) ->setDisplayConfigurable('view', TRUE); $field_definition_listener->onFieldDefinitionCreate($confirmed_field); $state[$order_item_type->id()][] = 'confirmed'; } } } if (!empty($state)) { // Cache which fields were created so they can be populated using the // populate-order-item-missing-fields.php script. \Drupal::state()->set('contacts_events_install_order_item_fields', $state); } } /** * Migrate rules expression to new date range config. */ function contacts_events_update_8009() { // Load all event classes. $storage = \Drupal::entityTypeManager()->getStorage('contacts_events_class'); /** @var \Drupal\contacts_events\Entity\EventClass[] $classes */ $classes = $storage->loadMultiple(); $to_save = []; foreach ($classes as $class) { $expr = $class->get('expression'); $min_age = NULL; $max_age = NULL; if (!empty($expr)) { if ($expr['id'] !== 'rules_and') { throw new \Exception("Can't automatically migrate class {$class->label()}"); } foreach ($expr['conditions'] as $condition) { if (isset($condition['context_values']['operation']) && $condition['context_values']['operation'] == '<') { if ($condition['context_mapping']['data'] != 'order_item.purchased_entity.entity.date_of_birth.date') { throw new \Exception("Can't automatically migrate class {$class->label()} - not acting upon DOB."); } if (isset($min_age)) { throw new \Exception("Can't automatically migrate class {$class->label()} - found multiple minimum ages"); } if (empty($condition['context_processors']['value'])) { // No minimum. continue; } $interval = $condition['context_processors']['value']['rules_dynamic_date']['interval'] ?? NULL; if ($interval == NULL) { throw new \Exception("Can't automatically migrate class {$class->label()} - can't find date interval."); } if (substr($interval, 0, 1) !== 'P' || substr($interval, -1) !== 'Y') { throw new \Exception("Can't automatically migrate class {$class->label()} - interval in wrong format"); } $age = substr($interval, 1, strlen($interval) - 2); if (!is_numeric($age)) { throw new \Exception("Couldn't migrate class {$class->label()} - can't extract min age"); } $min_age = (int) $age; } elseif (isset($condition['context_values']['operation']) && $condition['context_values']['operation'] == '>') { if ($condition['context_mapping']['data'] != 'order_item.purchased_entity.entity.date_of_birth.date') { throw new \Exception("Can't automatically migrate class {$class->label()} - not acting upon DOB."); } if (isset($max_age)) { throw new \Exception("Can't automatically migrate class {$class->label()} - found multiple max ages"); } if (empty($condition['context_processors']['value'])) { // No maximum. continue; } $interval = $condition['context_processors']['value']['rules_dynamic_date']['interval'] ?? NULL; if ($interval == NULL) { throw new \Exception("Can't automatically migrate class {$class->label()} - can't find date interval."); } if (substr($interval, 0, 1) !== 'P' || substr($interval, -1) !== 'Y') { throw new \Exception("Can't automatically migrate class {$class->label()} - interval in wrong format"); } $age = substr($interval, 1, strlen($interval) - 2); if (!is_numeric($age)) { throw new \Exception("Couldn't migrate class {$class->label()} - can't extract min age"); } $age = (int) $age; // Old format max age is exclusive, so decrement by 1. $age--; $max_age = $age; } } } if ($min_age !== NULL) { $class->set('min_age', $min_age); } if ($max_age !== NULL) { $class->set('max_age', $max_age); } $to_save[] = $class; } foreach ($to_save as $entity) { $entity->save(); } }