By rpayanm


2015-01-31 15:57:24 8 Comments

I have two identical sites on Drupal 8 (A and B), on (A) I created a custom block(block/add), and then I export the configuration(drush config-export) and import this config on (B) (drush config-import), no error, but on the site (B) the block that I imported said on any region that I put "This block is broken or missing. You may be missing content or you might need to enable the original module." And I cant edit the content of this block. How I can fix that?

Site A:

Site A

Site B:

enter image description here

5 comments

@Manuel Adan 2017-10-29 18:12:10

I have just publish a contributed module that solves this. Essentially, the module provides a type of block based on configuration (the fixed block) that wraps a custom block (the content block). The fixed block is stored in the website configuration along as the default content. In your scenario, the custom block is created automatically in the site B with the default content as soon as needed.

Everything is done through UI, no special files or custom module are needed. I named it "Fixed block content" and it is published at:

https://www.drupal.org/project/fixed_block_content

@mlncn 2016-09-20 12:48:49

Another approach for keeping content that is added as part of development also pushed to test and live environments is to use the Default Content module to export the content. It is built for the content to be exported to an installation profile's 'content' folder, and then the module, if enabled, automatically brings the content in when the site is installed, but it's also possible to import the content one item at a time, such as in an update hook, with the below code in your example.install or example.profile:

<?php
/**
* Import a piece of content exported by default content module.
*/
function example_import_default_content($path_to_content_json) {
  list($entity_type_id, $filename) = explode('/', $path_to_content_json);
  $p = drupal_get_path('profile', 'guts');
  $encoded_content = file_get_contents($p . '/content/' . $path_to_content_json);
  $serializer = \Drupal::service('serializer');
  $content = $serializer->decode($encoded_content, 'hal_json');
  global $base_url;
  $url = $base_url . base_path();
  $content['_links']['type']['href'] = str_replace('http://drupal.org/', $url, $content['_links']['type']['href']);
  $contents = $serializer->encode($content, 'hal_json');
  $class = 'Drupal\\' . $entity_type_id . '\Entity\\' . str_replace(' ', '', ucwords(str_replace('_', ' ', $entity_type_id)));
  $entity = $serializer->deserialize($contents, $class, 'hal_json', array('request_method' => 'POST'));
  $entity->enforceIsNew(TRUE);
  $entity->save();
}

Export a custom block with an ID of 8:

drush dcer block_content 8

(If you don't set your profile path in Drush settings you'll have to specify it above.)

And use the resultant export in your example.install file like this:

<?php
/**
* Add the footer block content.
*
* Implements hook_update_N().
*/
function example_update_8001() {
  example_import_default_content('block_content/136efd63-021e-42ea-8202-8b97305cc07f.json');
}

See easily add default content with update hooks for more.

@miscellaniac 2016-03-15 18:49:29

Thanks to everyone that posted here; we were really confused about this issue (blocks, block configuration, and the CMI).

Using the information here as a starting point, I created the following script (you can run it with drush php-script from your Drupal root) which will fix the UUIDs in the database. This assumes that the blocks you want to use are already there however (which isn't a problem for us since our blocks come over from a Drupal 6 migration). The script also assumes that the 'info' value for each block, a.k.a. its 'label' is unique, so your mileage may vary.

We will need to figure out a way to script the creation of new blocks as well so that any configuration exported from our development environment can be used in our production environment as well, but that is another story.

<?php
  use Drupal\Component\Serialization\Yaml;

  // we need to parse all uuids from deploy dir and override local uuids
  $dir = \Drupal::root() . '/' . config_get_config_directory('sync') . '/';
  $files = scandir($dir);

  \Drupal\Core\Database\Database::setActiveConnection();
  $connection = \Drupal\Core\Database\Database::getConnection();

  $logger = \Drupal::logger('code_driven_devel');

  // Gather all 'block_content' blocks; store the label and uuid
  $labelled_blocks_to_uuids = array();
  $labels = array();
  foreach($files as $file) {
    $ext = substr($file, -4);
    if ($ext == '.yml') {
      $info = Yaml::decode(file_get_contents($dir . $file));
      $name = substr($file, 0, -4);
      $isblock = substr($name, 0, 5);
      if($isblock == 'block') {
        if(is_array($info) && array_key_exists('settings', $info)) {
          $id = $info['settings']['id'];
          $id_parts = explode(':', $id);
          if ($id_parts[0] == 'block_content') {
            $uuid = $id_parts[1];
            $label = $info['settings']['label'];
            if (strlen($label) > 0) {
              $labelled_blocks_to_uuids[$label] = $uuid;
              array_push($labels, $label);
            }
          }
        }
      }
    }
  }
  $logger->notice(t("Found " . count($labels) . " block_content config YML that may need to have their UUIDs synched..."));
  $results = $connection->select('block_content_field_data', 't')
    ->fields('t')
    ->condition('info', $labels, 'IN')
    ->execute();
  if ($results) {
    foreach ($results as $record) {
      $logger->notice(t("Updating UUID for block_content with id = " . $record->id . "..."));
      $uuid = $labelled_blocks_to_uuids[$record->info];
      $connection->update('block_content')
        ->fields(array(
            'uuid' => $uuid,
            ))
        ->condition('id', $record->id, '=')
        ->execute();
      $logger->notice(t("UUID for block_content with id = " . $record->id . " updated to " . $uuid));
    }
  }

@jimafisk 2015-11-10 21:00:05

For anyone looking for a quick and dirty way to export all custom block data from site A and import it into site B (IMPORTANT: This will overwrite any custom block content on site B):

On site A:

Make a directory to put migration data in:

mkdir block_migration

Export block specific tables to individual files:

mysqldump -u mysql_username -p database_name block_content > block_migration/block_content.sql

mysqldump -u mysql_username -p database_name block_content__body > block_migration/block_content__body.sql

mysqldump -u mysql_username -p database_name block_content_field_data > block_migration/block_content_field_data.sql

mysqldump -u mysql_username -p database_name block_content_field_revision > block_migration/block_content_field_revision.sql

mysqldump -u mysql_username -p database_name block_content_revision > block_migration/block_content_revision.sql

mysqldump -u mysql_username -p database_name block_content_revision__body > block_migration/block_content_revision__body.sql

Transfer these new sql files to Site B via git or other method. If you're doing this often, you could even get fancy and put the above mysqldump commands into a pre-commit hook.

On Site B:

Assuming you've pulled in the code that contains the table drop/create logic we exported above, import the tables into your database:

mysql -u mysql_username -p database_name < block_migration/block_content.sql

mysql -u mysql_username -p database_name < block_migration/block_content__body.sql

mysql -u mysql_username -p database_name < block_migration/block_content_field_data.sql

mysql -u mysql_username -p database_name < block_migration/block_content_field_revision.sql

mysql -u mysql_username -p database_name < block_migration/block_content_revision.sql

mysql -u mysql_username -p database_name < block_migration/block_content_revision__body.sql

Clearing your caches is always a good idea. If you're using drush:

drush cr

@calebtr 2016-04-12 06:42:14

This works great, no messing with UUIDs. One note: you can use drush sqlc < table.sql

@jimafisk 2016-04-13 13:20:12

I'm glad it worked for you @calebtr! Thanks for mentioning drush sqlc! I created git hooks to automate this process for blocks, nodes, users, and menu items: https://github.com/jimafisk/content_mig

@larowlan 2015-01-31 20:35:28

A custom block is made of two entities, one for the placement and the actual content. Only the actual placement can be exported with cmi. The content can not. You can either send your content via REST or you can manually edit the uuid of another block to match. In the future you'll be able to use Entity Pilot (entitypilot.com) - disclaimer - this is my product.

@Johan Haest 2015-06-20 15:58:37

Where would we edit this UID? In the database?

@ya.teck 2015-10-26 14:21:38

UPDATE block_content SET uuid = 'uuid from block yml file' WHERE id = 'id of another block to match';

Related Questions

Sponsored Content

1 Answered Questions

All instances of block are not getting removed

6 Answered Questions

[SOLVED] How to fix a broken or missing block?

  • 2018-01-31 19:14:11
  • madan kumar
  • 6809 View
  • 6 Score
  • 6 Answer
  • Tags:   8 blocks

2 Answered Questions

[SOLVED] Delete a block of all layouts

1 Answered Questions

[SOLVED] How do I recreate the system.site configuration element?

7 Answered Questions

[SOLVED] How do I migrate block content from a dev to a production site?

  • 2015-05-21 14:01:21
  • user1731
  • 6832 View
  • 24 Score
  • 7 Answer
  • Tags:   blocks 8

2 Answered Questions

2 Answered Questions

2 Answered Questions

Sponsored Content