Commit 8ba606fc authored by Eliot Scott's avatar Eliot Scott
Browse files

add support for users, paragraphs, extend to other entities

parent e67cda06
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
<?php
/**
 * @file
 * Install, update and uninstall functions for the profilename install profile.
 * Contains install and update functions for Bulk Update Fields.
 */

 /**
@@ -16,3 +16,12 @@ function bulk_update_fields_update_8001(&$sandbox) {
    ->save();
  $actions_storage->resetCache();
}

/**
 * Implements hook_install().
 */
function bulk_update_fields_install() {
  foreach (\Drupal::entityTypeManager()->getDefinitions() as $entity_type_machine_name => $entity_type) {
    _bulk_update_fields_create_action($entity_type_machine_name, $entity_type->getLabel());
  }
}
+53 −0
Original line number Diff line number Diff line
<?php

/**
 * @file
 * Contains bulk_update_fields.module..
 */

use Drupal\system\Entity\Action;
use Drupal\Core\Database\Database;
use Drupal\Core\Entity\EntityInterface;

/**
 * Implements hook_entity_operation_alter().
 */
function bulk_update_fields_entity_operation_alter(array &$operations, EntityInterface $entity) {
  // TODO: tried to do this as suggested at https://www.drupal.org/node/2020549
  // with // Loading all configured actions for the comment entity type.
  // $actions = entity_load_multiple_by_properties('action')
  // but caused an oom error.
  // so we load the config table and do this manually instead.
  // terrible.
  // TODO: a hook to utilize on entity type creation is not obvious.
  // tried hook_entity_type_build but got oom errors.
  // This seems to work, but doesnt feel right.
  $bulk_update_fields_config = 'system.action.bulk_update_fields_on_';
  $db = Database::getConnection();
  $query = $db->select('config')
    ->fields('config', ['name'])
    ->condition('config.name', "%" . $db->escapeLike($bulk_update_fields_config) . "%", 'LIKE');
  $existing_config = $query->execute()->fetchAll(\PDO::FETCH_COLUMN);
  foreach (\Drupal::entityTypeManager()->getDefinitions() as $entity_type_machine_name => $entity_type) {
    if (!in_array($bulk_update_fields_config . $entity_type_machine_name, $existing_config)) {
      _bulk_update_fields_create_action($entity_type_machine_name, $entity_type->getLabel());
    }
  }
}

/**
 * Create Action.
 */
function _bulk_update_fields_create_action($entity_type_machine_name, $entity_type_label) {
  $label = 'Bulk Update ' . $entity_type_label . ' Fields';
  // Creating a new configured action.
  $action = Action::create([
    'id' => 'bulk_update_fields_on_' . $entity_type_machine_name,
    'label' => $label,
    'type' => $entity_type_machine_name,
    'configuration' => [],
    'plugin' => 'bulk_update_fields_action_base',
  ]);
  $action->save();
  drupal_set_message(t('Action @label created from bulk_update_fields module.', ['@label' => $label]));
}
+85 −13
Original line number Diff line number Diff line
@@ -2,6 +2,9 @@

namespace Drupal\bulk_update_fields;

use Drupal\paragraphs\Entity\Paragraph;
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;

/**
 * BulkUpdateFields.
 */
@@ -10,27 +13,92 @@ class BulkUpdateFields {
  /**
   * {@inheritdoc}
   */
  public static function updateFields($entities, $fields, &$context) {
    $message = 'Updating Fields...';
    $results_entities = [];
    $results_fields = [];
    $update = FALSE;
    foreach ($entities as $entity) {
      foreach ($fields as $field_name => $field_value) {
        if ($entity->hasField($field_name)) {
          if ($field_value == $field_name ) { continue; } // this is the case for field images for some reason
  public static function processDate($date, $date_type) {
    $date->setTimezone(new \DateTimezone(DateTimeItemInterface::STORAGE_TIMEZONE));
    //date or datetime (cannot believe this isnt handled!)
    if ($date_type == 'date') {
      $date = $date->format(DateTimeItemInterface::DATE_STORAGE_FORMAT);
    }
    // its datetime. (others?)
    else {
     $date = $date->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT);
    }

    return $date;
  }

  /**
   * {@inheritdoc}
   */
  public static function processField($value, $field_definition) {
    // see if datetime, daterange
    if (strpos($field_definition->getType(), 'date') !== false) {
      $datetime_type = $field_definition->getFieldStorageDefinition()->getSettings()['datetime_type'];
      $value['value'] = self::processDate($value['value'], $datetime_type);
      if ($field_definition->getType() == 'daterange') {
        $value['end_value'] = self::processDate($value['end_value'], $datetime_type);
      }
    }

    return $value;
  }

  /**
   * {@inheritdoc}
   */
  public static function preprocessField($field_value) {
    // not sure if this is still valid but leaving in case
    if (isset($field_value['target_id'][0])) {
      $field_value = $field_value['target_id'];
    }
    // this caused a failure in core/entity/plugin/datatype/entityreference. removing.
          if (isset($field_value[0]['target_id']) && isset($field_value['add_more'])) {
    if (isset($field_value['add_more'])) {
      unset($field_value['add_more']);
    }
    // this occurs in fields like office hours.
    if (isset($field_value['value'])) {
      $field_value = $field_value['value'];
    }

    return $field_value;
  }

  /**
   * {@inheritdoc}
   */
  public static function updateFields($entities, $fields, &$context) {
    $message = 'Updating Fields...';
    $results_entities = [];
    $results_fields = [];
    $update = FALSE;
    foreach ($entities as $entity) {
      foreach ($fields as $field_name => $field_value) {
        if ($entity->hasField($field_name)) {
          $field_value = self::preprocessField($field_value);
          $field_definition = $entity->get($field_name)->getFieldDefinition();
          foreach ($field_value as $key => $value) {
            if ($value == $field_name ) { continue; } // this is the case for field images for some reason
            if (!is_array($value)) { continue; } // some objects returned
            $value = self::processField($value, $field_definition);
            if (is_array($value) && isset($value['subform']) && isset($value['paragraph_type'])) {
              $paragraph = Paragraph::create(['type' => $value['paragraph_type']]);
              foreach ($value['subform'] as $p_field_name => $p_field_value) {
                if ($paragraph->hasField($p_field_name)) {
                  $p_field_value = self::preprocessField($p_field_value);
                  $p_field_definition = $paragraph->get($p_field_name)->getFieldDefinition();
                  foreach ($p_field_value as $p_key => $p_value) {
                    if ($p_value == $p_field_name ) { continue; } // this is the case for field images for some reason
                    if (!is_array($p_value)) { continue; } // some objects returned
                    $p_field_value[$p_key] = self::processField($p_value, $p_field_definition);
                  }
                }
                $paragraph->get($p_field_name)->setValue($p_field_value);
              }
              $paragraph->save();
              $value = $paragraph;
            }
            $field_value[$key] = $value;
          }
          $entity->get($field_name)->setValue($field_value);
          $update = TRUE;
          if (!in_array($field_name, $results_fields)) {
@@ -39,7 +107,11 @@ class BulkUpdateFields {
        }
      }
      if ($update) {
        // setNewRevision method exists on user but throws an error if called.
        // TODO?: Do other entity types need revisions set?
        if ($entity->getEntityTypeId() == 'node' && method_exists($entity, 'setNewRevision')) {
          $entity->setNewRevision();
        }
        $entity->save();
        $results_entities[] = $entity->id();
      }
+52 −14
Original line number Diff line number Diff line
@@ -11,6 +11,8 @@ use Drupal\Core\Session\SessionManagerInterface;
use Drupal\user\PrivateTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Routing\RouteBuilderInterface;
use Drupal\default_paragraphs\Plugin\Field\FieldWidget\DefaultParagraphsWidget;
use Drupal\paragraphs\Plugin\Field\FieldWidget\ParagraphsWidget;

/**
 * BulkUpdateFieldsForm.
@@ -111,6 +113,7 @@ class BulkUpdateFieldsForm extends FormBase implements FormInterface {
        ],
      ],
      'finished' => '\Drupal\bulk_update_fields\BulkUpdateFields::bulkUpdateFieldsFinishedCallback',
      'file' => '\Drupal\bulk_update_fields\BulkUpdateFields',
    ];
    batch_set($batch);
    return 'All fields were updated successfully';
@@ -127,7 +130,23 @@ class BulkUpdateFieldsForm extends FormBase implements FormInterface {
        break;

      case 2:
        $this->userInput['fields'] = array_merge($this->userInput['fields'], $form_state->getValues()['default_value_input']);
        $form_state_values = $form_state->getValues();
        foreach ($form_state_values as $field_name => $form_state_value) {
          // Paragraphs dont allow defaults.
          // Force it with just values.
          if ($field_name != 'default_value_input' && is_array($form_state_value)) {
            $form_state_values['default_value_input'][$field_name] = $form_state_value;
            foreach ($form_state_value as $key => $value) {
              if (!is_numeric($key)) {
                unset($form_state_values['default_value_input'][$field_name][$key]);
              }
              elseif (isset($form['default_value_input'][$field_name]['widget'][$key]['#paragraph_type'])) {
                $form_state_values['default_value_input'][$field_name][$key]['paragraph_type'] = $form['default_value_input'][$field_name]['widget'][$key]['#paragraph_type'];
              }
            }
          }
        }
        $this->userInput['fields'] = array_merge($this->userInput['fields'], $form_state_values['default_value_input']);
        $form_state->setRebuild();
        break;

@@ -176,7 +195,11 @@ class BulkUpdateFieldsForm extends FormBase implements FormInterface {
          'revision_timestamp',
          'revision_log',
          'created',
          'changed'
          'changed',
          'pass',
          'name',
          'mail',
          'init'
        ];
        foreach ($this->userInput['entities'] as $entity) {
          $this->entity = $entity;
@@ -208,12 +231,26 @@ class BulkUpdateFieldsForm extends FormBase implements FormInterface {
            if ($field = $entity->getFieldDefinition($field_name)) {
              // TODO Dates fields are incorrect due to TODOs below.
              if ($field->getType() == 'datetime') {
                drupal_set_message($this->t('Cannot update field @field_name. Date field types are not yet updatable.',
                  [
                    '@field_name' => $field_name,
                  ]), 'error');
                continue;
                $type = \Drupal::service('plugin.manager.field.widget');
                $plugin_definition = $type->getDefinition('datetime_default');
                $widget = new \Drupal\datetime\Plugin\Field\FieldWidget\DateTimeWidgetBase('datetime', $plugin_definition, $entity->get($field_name)->getFieldDefinition(), [], []);
                $form['#parents'] = [];
                $form['default_value_input'][$field_name] = $widget->form($entity->get($field_name), $form, $form_state);
              }
              elseif ($field->getType() == 'entity_reference_revisions') {
                // TODO - allow other types of entity_reference_revisions
                // currently paragraphs only.
                $type = \Drupal::service('plugin.manager.field.widget');
                $plugin_definition = $type->getDefinition('paragraphs');
                $widget = new ParagraphsWidget('paragraphs', $plugin_definition, $entity->get($field_name)->getFieldDefinition(), [], []);
                $form['#parents'] = [];
                $form['default_value_input'][$field_name] = $widget->form($entity->get($field_name), $form, $form_state);
                if ($form_state->getTriggeringElement()['#name'] != 'op') {
                  $paragraph_type = $form_state->getTriggeringElement()['#bundle_machine_name'];
                  $form_state->set('paragraph_type', $paragraph_type);
                }
              }
              else {
                // TODO
                // I cannot figure out how to get a form element for only a field.
                // Maybe someone else can.
@@ -223,6 +260,7 @@ class BulkUpdateFieldsForm extends FormBase implements FormInterface {
              }
            }
          }
        }
        $form['#title'] .= ' - ' . $this->t('Enter New Values in Appropriate Fields');
        break;

@@ -237,7 +275,7 @@ class BulkUpdateFieldsForm extends FormBase implements FormInterface {

        break;
    }
    drupal_set_message($this->t('This module is experiemental. PLEASE do not use on production databases without prior testing and a complete database dump.'), 'warning');
    drupal_set_message($this->t('This module is experiemental. PLEASE do not use on production databases without prior testing and a complete database dump. Some field widgets do not work, Entity Browser for instance. Change your widgets to core widgets in entity form displays where possible.'), 'warning');
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $submit_label,