You are here

function panelizer_update_7115 in Panelizer 7.3

Ensure that each Panelizer display is only used once, so that each revision has a separate record.

File

./panelizer.install, line 687
Install, update and uninstall functions for the panelizer module.

Code

function panelizer_update_7115(&$sandbox) {

  // This loops through all of the records in {panelizer_entity}, looks for any
  // displays that are used more than once and clones any additional copies
  // that are needed. The goal is to have each display only used once.
  // Process records by groups of 1 (arbitrary value). Doing this one at a time
  // because some sites can have a LOT of revisions.
  $limit = 1;

  // When ran through Drush it's Ok to process a larger number of objects at a
  // time.
  if (drupal_is_cli()) {
    $limit = 10;
  }

  // The update hasn't been ran before.
  if (!isset($sandbox['progress'])) {

    // The count of records visited so far.
    $sandbox['progress'] = 0;

    // Load any 'did' values that are used in more than one revision.
    $records = db_query("SELECT did\n      FROM {panelizer_entity}\n      WHERE revision_id > 0 AND did <> 0 AND did IS NOT NULL\n      GROUP BY did\n      HAVING count(revision_id) > 1");

    // If there are no records, there's nothing to do.
    if ($records
      ->rowCount() == 0) {
      return t('No Panelizer display records need fixing.');
    }

    // Total records that must be processed.
    $sandbox['max'] = $records
      ->rowCount();
    watchdog('panelizer', 'Need to fix @count duplicate displays.', array(
      '@count' => $records
        ->rowCount(),
    ));
  }

  // Loop through the records in smaller chunks.
  $dids = db_query_range("SELECT did\n    FROM {panelizer_entity}\n    WHERE revision_id > 0 AND did <> 0 AND did IS NOT NULL\n    GROUP BY did\n    HAVING count(revision_id) > 1", 0, $limit)
    ->fetchCol();

  // Track the entities that need to be reset.
  $cache_clear = array();
  ctools_include('plugins', 'panels');
  ctools_include('content');

  // Load all of the requested displays.
  foreach (panels_load_displays($dids) as $original_did => $display) {

    // Load each panelizer_entity record for this display.
    $panelizers = db_query("SELECT * FROM {panelizer_entity} WHERE did=:did", array(
      ':did' => $display->did,
    ));
    $ctr = 0;
    foreach ($panelizers as $panelizer) {
      $ctr++;

      // Skip the first record.
      if ($ctr === 1) {
        $ctr++;
        continue;
      }

      // Reset the 'did' value so that a new record can be created.
      unset($display->did);

      // Regenerate the UUID.
      $display->uuid = ctools_uuid_generate();

      // Save the display, thus creating a new 'did' value. Also, using
      // db_insert() as it's safer than using drupal_write_record() directly
      // during an update script. Also, doing each field individually to avoid
      // corrupting data during hook_panels_display_save.
      $display->did = db_insert('panels_display')
        ->fields(array(
        'layout' => $display->layout,
        'layout_settings' => serialize($display->layout_settings),
        'panel_settings' => serialize($display->panel_settings),
        'cache' => serialize($display->cache),
        'title' => $display->title,
        'hide_title' => $display->hide_title,
        'title_pane' => $display->title_pane,
        'uuid' => $display->uuid,
      ))
        ->execute();
      $message = 'Panelizer update 7115: created display %did for %entity_type %entity_id';
      $message_args = array(
        '%did' => $display->did,
        '%entity_type' => $panelizer->entity_type,
        '%entity_id' => $panelizer->entity_id,
      );
      watchdog('panelizer', $message, $message_args, WATCHDOG_NOTICE);

      // Reset the 'pid' values of each pane, using a new UUID. Because its
      // non-numeric, when the display is saved it'll create a new record for
      // each pane, but still keep all of the internal pointers accurate.
      foreach ($display->panels as $region => $panes) {
        foreach ((array) $panes as $position => $pid) {

          // Pane not found. Shouldn't happen, but you never know.
          if (!isset($display->content[$pid])) {
            watchdog('panelizer', 'Panelizer update 7115: couldn\'t load pane %pid for display %did', array(
              '%pid' => $pid,
              '%did' => $display->did,
            ), WATCHDOG_WARNING);
            continue;
          }

          // Load the pane.
          $new_pane = clone $display->content[$pid];

          // This appears to only serve the purpose of ensuring necessary APIs
          // and include files are loaded.
          ctools_get_content_type($new_pane->type);

          // Remove the pid, it'll be created during the database insertion.
          unset($new_pane->pid);

          // Generate a new UUID.
          $new_pane->uuid = ctools_uuid_generate();

          // Update the pane's did.
          $new_pane->did = $display->did;

          // Create the pane record, save the return ID as the pane pid. Again,
          // using db_insert directly because it's safer than
          // drupal_write_record during an update script. Also, doing the fields
          // individually so that the data isn't corrupted during
          // hook_panels_pane_insert.
          $new_pane->pid = db_insert('panels_pane')
            ->fields(array(
            'did' => $new_pane->did,
            'panel' => $new_pane->panel,
            'type' => $new_pane->type,
            'subtype' => $new_pane->subtype,
            'shown' => $new_pane->shown,
            'access' => serialize($new_pane->access),
            'configuration' => serialize($new_pane->configuration),
            'cache' => serialize($new_pane->cache),
            'style' => serialize($new_pane->style),
            'css' => serialize($new_pane->css),
            'extras' => serialize($new_pane->extras),
            'locks' => serialize($new_pane->locks),
            'uuid' => $new_pane->uuid,
            'position' => $new_pane->position,
          ))
            ->execute();

          // Tell the world.
          module_invoke_all('panels_pane_insert', $new_pane);
          watchdog('panelizer', 'Panelizer update 7115: created pane %pid for display %did', array(
            '%pid' => $new_pane->pid,
            '%did' => $new_pane->did,
          ), WATCHDOG_NOTICE);

          // Update the optional title pane.
          if (isset($display->title_pane) && $display->title_pane == $pid) {
            $display->title_pane = $new_pane->pid;

            // Do a simple update query to write it so we don't have to rewrite
            // the whole record. We can't just save writing the whole record
            // here because it was needed to get the did. Chicken, egg, more
            // chicken.
            db_update('panels_display')
              ->fields(array(
              'title_pane' => $new_pane->pid,
            ))
              ->condition('did', $display->did)
              ->execute();
          }
        }
      }

      // Allow other modules to take action when a display is saved.
      module_invoke_all('panels_display_save', $display);

      // Assign the new display to this Panelizer record.
      $panelizer->did = $display->did;

      // Update the {panelizer} record.
      db_update('panelizer_entity')
        ->fields((array) $panelizer)
        ->condition('entity_type', $panelizer->entity_type)
        ->condition('entity_id', $panelizer->entity_id)
        ->condition('revision_id', $panelizer->revision_id)
        ->condition('view_mode', $panelizer->view_mode)
        ->execute();

      // Clear this entity's cache.
      $cache_clear[$panelizer->entity_type][] = $panelizer->entity_id;
    }
    watchdog('panelizer', 'Panelizer update 7115: finished fixing %did', array(
      '%did' => $original_did,
    ), WATCHDOG_NOTICE);
    $sandbox['progress']++;
  }

  // Clear the caches for any entities that are updated.
  foreach ($cache_clear as $entity_type => $entity_ids) {
    entity_get_controller($entity_type)
      ->resetCache($entity_ids);
  }
  $sandbox['#finished'] = $sandbox['progress'] >= $sandbox['max'] ? TRUE : $sandbox['progress'] / $sandbox['max'];
  return t('Fixed @count Panelizer record(s) (out of @total) that were using the same display.', array(
    '@count' => $sandbox['progress'],
    '@total' => $sandbox['max'],
  ));
}