March 3, 2015
Drupal

Rebuilding the Cheppers website with Drupal 8: The Age of Innocence

Image

Getting started Luckily, we have only hit a few minor snags in the process so far. For the most part, these first steps in rebuilding our website have been fairly simple, but we will still present them in detail. Any problems we had in this beginning stage were solved relatively easily. There are a few prerequisite steps you must take to begin working with Drupal 8. We usually use drush and make files to deploy our sites and we want to keep using this method with the new Drupal 8 site too.

Image

Upgrade drush

The first thing we learned was that we had to upgrade our drush to version 7 because only drush 7 is compatible with Drupal 8. It is backward compatible with Drupal 6 and 7 so you can safely upgrade. We found two very helpful articles here and here.

Download Drupal 8

After upgrading drush we created a webroot folder and put in a project-core.make.yml file with the content below:
 

  1. api: 2
  2. core: 8.x
  3. projects: 
  4.   drupal:
  5.     type: "core" 
  6.     version: "8.0.0-beta6"

Then in terminal just type: drush make project-core.make.yml and it will pull the given version of Drupal 8 to the current directory.

Add Install Profile

We decided to make the Cheppers install profile on the base of the standard profile. We added a profiles/cheppers folder to the webroot and copied the content of the core/profiles/standard folder to it. The following files were renamed to cheppers: standard.info.yml → cheppers.info.yml standard.install → cheppers.install standard.profile → cheppers.profile We learned a very important lesson through trial and error after extensive googling with no results. Within the [profile_name].info.yml file add the name of your desired default theme to the themes section. If you don’t do this, your theme will appear among uninstalled themes. So <your_theme>.info.yml should look like this:
 

  1. name: Cheppers
  2. type: profile
  3. description: Install Cheppers site.
  4. # version: 8.x-1.x-dev
  5. core: 8.x
  6. dependencies:
  7. (...)
  8. themes:
  9.   - "your_theme"
  10.   - seven


Although we wouldn’t do this in a Drupal 7 project, in this case we decided to use the Bartik theme as the starting point for our own custom theme because it is the default theme of Drupal 8 and is guaranteed to follow best practices. In the profiles/cheppers/config/install folder, we renamed all files with the name bartik to our theme and modified their content accordingly. In system.theme.yml we replaced default theme to ours:
 

  1. admin: seven
  2. default: "your_theme"


As a result, while installing your site you will have, besides the minimal and standard profile, an additional option to select.
 

Create content during install

The configuration files (.yml) do not store content so one of the very first issues we faced was how to create blocks with content during the install process. We needed to solve this because we want to make developers’ collaboration easier by having a fully functional and configured site right after installation. Initially, we only had vague ideas about a solution but then we have received a tip from Gábor Hojtsy, who sent us this link: http://cgit.drupalcode.org/multilingual_demo/tree/multilingual_demo.install#n306 After that, it was an easy task to accomplish, so thank you to Gábor for the help. Our first blocks’ aim was to display contact data in the footer. Let’s see how to do that in Drupal 8.

Create block content

In our profile folder we created a content subfolder to keep things together. In this folder we created the following files:
 

  • content-block.csv
  1. "Id","yaml","Title","Body","Language"
  2. "0","<your_theme>_footer_address","Cheppers address","49 Dessewffy Str.
  3. Budapest, 1066
  4. Hungary","en"
  5. "1","<your_theme>_footer_email","Cheppers e-mail","info@cheppers.com","en"
  6. "2","<your_theme>_footer_powered","Cheppers powered by","© Cheppers Ltd. 2015. Powered by <a drupal-8.0="" href="" https:="" www.drupal.org="">Drupal 8</a>","en"


This file stores content for 3 blocks. We could have put it all in one block, but we wanted to make it more flexible.

  • block.block.<your_theme>_footer_email.yml
  • block.block.<your_theme>_footer_powered.yml
  • block.block.<your_theme>_footer_address.yml


block.block.<your_theme>_footer_address.yml
 

  1. langcode: en
  2. status: true
  3. dependencies:
  4.   module:
  5.     - block_content
  6.   theme:
  7.     - "<your_theme>"
  8. id: "<your_theme>_footer_address"
  9. theme: "<your_theme>"
  10. region: footer_cheppers_address
  11. weight: -5
  12. provider: null
  13. plugin: 'block_content:REPLACEME'
  14. settings:
  15.   id: 'block_content:REPLACEME'
  16.   label: 'Footer address'
  17.   provider: block_content
  18.   label_display: '0'
  19.   cache:
  20.     max_age: -1
  21.     contexts: {  }
  22.   status: true
  23.   info: ''
  24.   view_mode: full
  25. visibility: {  }


In case you were wondering why there are apostrophes around some values, but not all of them, it is simply because one word values don’t need apostrophes. As you can see, we set the region to a custom one called footer_cheppers_address because we would like our blocks to be shown in a new custom region. We’ll skip the other two blocks’ .yml files because I’m sure you get the idea, let’s move on instead and add our new regions in the <your_theme>.info.yml file within your theme folder: <your_theme>.info.yml
 

  1. name: Cheppers Theme
  2. type: theme
  3. base theme: classy
  4. description: 'A flexible, recolorable theme with many regions and a responsive, mobile-first layout.'
  5. package: Cheppers
  6. version: 8.x-1.0
  7. core: 8.x
  8. logo:
  9.   logo_path: logo.png
  10. libraries:
  11.   - "<your_theme>/global-styling"
  12. ckeditor_stylesheets:
  13.   - css/base/elements.css
  14.   - css/base/typography.css
  15.   - css/components/captions.css
  16.   - css/components/content.css
  17.   - css/components/table.css
  18. regions:
  19.   header: Header
  20.   primary_menu: 'Primary menu'
  21.   help: Help
  22.   content: Content
  23.   footer_cheppers_address: 'Footer Cheppers address'
  24.   footer_cheppers_email: 'Footer Cheppers email'
  25.   footer_message: 'Footer message'
  26.   footer_socialmenu: 'Footer social menu'


We have added two regions, footer_cheppers_address and footer_cheppers_email. For the block <your_theme>_footer_powered we will simply use the already existing footer_message region. You can, of course, configure this as you wish. It’s just an example of how to add a custom region to your theme. Ok, so now we have created custom regions for our blocks in the footer. Now, let’s move on to how to modify the <your_profile>.install file in our profiles/<your_profile> folder in order to add the content to this block during installation. <your_profile>.install
 

  1. use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
  2. use Drupal\user\Entity\User;
  3. 
     
  4. use Drupal\menu_link_content\Entity\MenuLinkContent;
  5. use Symfony\Component\Yaml\Yaml;
  6. use Drupal\block_content\Entity\BlockContent;
  7. use Drupal\block\Entity\Block;
  8. 
     
  9. **
  10.  * Implements hook_install().
  11.  *
  12.  * Perform actions to set up the site for this profile.
  13.  *
  14.  * @see system_install()
  15.  */
  16. function <your_profile>_install() {
  17.   (...)
  18. 
     
  19.   // Add custom blocks.
  20.  <your_profile>_add_blocks_with_contents(__DIR__ .'/content/content-block.csv');
  21. }


<your_profile>.install::<your_profile>_add_blocks_with_contents
 

  1. /**
  2.  * Add custom blocks with content taken from a CSV file.
  3.  *
  4.  * @param string $file_name
  5.  *   The name of the CSV file.
  6.  */
  7. function <your_profile>_add_blocks_with_contents($file_name) { 
  8.   /** @var Drupal\block_content\Entity\BlockContent[] $blocks */
  9.   $blocks = array();
  10.   $keys = (object)array_flip(array('id', 'yaml_file', 'title', 'body', 'lang',));
  11.   $yaml_files = array();
  12.   $handle = fopen($file_name, 'r');
  13.   // Read the first row with column titles.
  14.   $line = fgetcsv($handle);
  15.   while ($line = fgetcsv($handle)) {
  16.     if ($line[$keys->lang] == 'en') {
  17.       $item = BlockContent::create(
  18.     	array(
  19.       	  'info' => $line[$keys->title],
  20.       	  'type' => 'basic',
  21.       	  'body' => array('value' => $line[$keys->body], 'format' => 'restricted_html'),
  22.       	  'langcode' => $line[$keys->lang],
  23.     	)
  24.       );
  25.       $item->save();
  26.       $blocks[$line[$keys->id]] = $item;
  27.       $yaml_files[$line[$keys->id]] = $line[$keys->yaml_file];
  28.     }
  29.   }
  30.   fclose($handle);
  31.   // Read block yaml files and add blocks to regions.
  32.   <your_profile>_add_blocks_to_regions($blocks, $yaml_files);
  33. }

 

<your_profile>.install::<your_profile>_add_blocks_to_regions
 

  1. /**
  2.  * Add blocks to regions defined in yaml_files.
  3.  *
  4.  * @param array $blocks
  5.  * @param array $yaml_files
  6.  */
  7. function <your_profile>_add_blocks_to_regions($blocks, $yaml_files = array()) {
  8.   // The custom block placements need to be adjusted with the content UUID. 
  9.   while (list($block_id, $item) = each($yaml_files)) {
  10.     if (isset($blocks[$block_id])) {
  11.       $input = __DIR__ . '/content/block.block.' . $item . '.yml';
  12.       $yaml = Yaml::parse($input);
  13.       $yaml['plugin'] = $yaml['settings']['id'] = 'block_content:' . $blocks[$block_id]->uuid();
  14.       $block_placement = Block::create($yaml);
  15.       $block_placement->save();
  16.     }
  17.   }
  18. }


You can see the result in the next two pictures. 
 

Image
Image

To edit the contents of the blocks we have just created, go to admin/structure/block/block-content page and click the [Edit] button.

 

Create footer menu links

Creating footer menu links is even easier than creating blocks, just add content-menu.csv to the content folder mentioned above and a new function into <your_profile>.install file. content-menu.csv
 

  1. Id,Title,Menu,Path,Language,Weight
  2. 0,Drupal account,footer,https://www.drupal.org/drupal-8.0,en,20
  3. 1,Facebook account,footer,https://www.facebook.com/cheppers.hu?fref=ts,en,30
  4. 2,LinkedIn account,footer,http://www.linkedin.com/company/cheppers,en,40
  5. 3,Twitter account,footer,https://twitter.com/cheppers,en,50
  6. 4,Google+ account,footer,https://plus.google.com/103371252165927373857,en,60


<your_profile>.install
 

  1. /**
  2.  * Implements hook_install().
  3.  *
  4.  * Perform actions to set up the site for this profile.
  5.  *
  6.  * @see system_install()
  7.  */
  8. function <your_profile>_install() {
  9. (...)
  10.  // Add footer menu items.
  11.  <your_profile>_add_footer_menu_items(__DIR__ . '/content/content-menu.csv');
  12. }


<your_profile>.install::<your_profile>_add_footer_menu_items
 

  1. /**
  2.  * Create menu items based on a CSV file.
  3.  *
  4.  * @param string $file_name
  5.  *   The name of the CSV file.
  6.  */
  7. function <your_profile>_add_footer_menu_items($file_name) {
  8.   /** @var \Drupal\menu_link_content\Entity\MenuLinkContent[] $menus */
  9.   $menus = array();
  10.   $handle = fopen($file_name, 'r');
  11.   /** @var \Drupal\Core\Path\PathValidatorInterface $path_validator */
  12.   $path_validator = \Drupal::service('path.validator');
  13.   $keys = (object)array_flip(array('id', 'title', 'menu', 'path', 'lang', 'weight',));
  14.   // Read the column titles.
  15.   $line = fgetcsv($handle);
  16.   while ($line = fgetcsv($handle)) {
  17.     if ($line[$keys->lang] == 'en') {
  18.       $route = $path_validator->getUrlIfValid($line[$keys->path]);
  19.       // For more parameters see:
  20.       // \Drupal\menu_link_content\Entity\MenuLinkContent.php#getPluginDefinition
  21.       $item = MenuLinkContent::create(
  22.         array(
  23.          'title' => $line[$keys->title],
  24.            'menu_name' => $line[$keys->menu],
  25.     'url' => $line[$keys->path],
  26.           'options' => array('target' => '_blank'),
  27.           'langcode' => $line[$keys->lang],
  28.            'weight' => $line[$keys->weight],
  29.            'bundle' => 'menu_link_content'
  30.         )
  31.       );
  32.       $item->save();
  33.       $menus[$line[$id]] = $item;
  34.     }
  35.   }
  36.   fclose($handle);
  37. }


Now you have footer menu links with the content given in your content-menu.csv file: admin/structure/menu/manage/footer

Image

Did you notice that there is one more link on the footer menu? The first item, Contact, isn’t in our csv file. By default, the contact module creates this menu item in the footer menu, but what if we need this in the main menu, not the footer menu? Just create a .yml file in your config/install directory with the name: core.menu.static_menu_link_overrides.yml, then override menu_name, like this:

 

  1. definitions:
  2.   contact__site_page:
  3.     enabled: true
  4.     weight: 0
  5.     expanded: false
  6.     menu_name: main
  7.     parent: ''


The main achievement of this method is that you can add any new block with content by modifying/adding only .yml files and .csv files. So far, we have only presented you with the easier parts of this beginning stage. Please share any ideas you might have about how we could have improved our methods, and, of course, questions are always welcome.

Related posts

Image
Image
March 19, 2015
Drupal

This post will focus on exporting and importing site configuration.