The Flexibility of Drupal

A presentation at NERDSummit in September 2015 in Amherst, MA, USA by Michael Miles

Slide 1

Slide 1

The Flexibility of Drupal 9 Ways to Alter a Menu item http://nerdsummit.org/node/2069 NERDSummit 2015 #NERDsummit

Slide 2

Slide 2

Michael Miles From: Boston, MA USA Work: Genuine @WeAreGenuine(.com) Exp: Working with Drupal since 2008. Acquia Grand Master. 2014 Acquia MVP. Twitter: @mikemiles86 Drupal.org: mikemiles86 All the Places: mikemiles86

Slide 3

Slide 3

Goals of this Session To show there is no “right” way, just “right for me” way. To demonstrate data manipulation methods. To teach considerations to take into account.

Slide 4

Slide 4

Examples we’ll use Manipulating Menu Items On just about every Drupal site. Can be manipulated in many places. What we’ll focus on changing Title Destination Display

Slide 5

Slide 5

Why should you care? Menu items are just data. Everything in Drupal is just data. If it is data, it can be manipulated! Everything in Drupal can be manipulated!

Slide 6

Slide 6

Environments Drupal 7.37: Standard. bartik subtheme. slate color. Drupal 8.0.0­beta11: Standard. bartik subtheme. slate color. !!WARNING!! Drupal 8 still in development. Not live. (blame Murphy)

Slide 7

Slide 7

Drupal 7: before

Slide 8

Slide 8

Drupal 7: after

Slide 9

Slide 9

Drupal 8: before

Slide 10

Slide 10

Drupal 8: after

Slide 11

Slide 11

Data Manipulation Three general layers Database (MySQL, etc…) Server side (PHP) Client side (HTML/JS/CSS)

Slide 12

Slide 12

Database Layer Data stored in, sent to or retrieved from the database.

Slide 13

Slide 13

#1. Drupal Core UI Pros Cons Nothing extra is needed. Capabilities are limited. “Easy” to use interface. Solution may be too broad. Changes made are stable. Solution may be too narrow. tl;dr Easy to use, but limited functionality.

Slide 14

Slide 14

Use Drupal 7 core to change menu item title and destination.

Slide 15

Slide 15

First item in Drupal 7 menu now has changed title and destination.

Slide 16

Slide 16

Use Drupal 8 core to change menu item title and destination.

Slide 17

Slide 17

First item in Drupal 8 menu now has changed title and destination.

Slide 18

Slide 18

#2. Modules Pros Cons Extend Drupal core. Can add complexity. Variety of solutions available. May do more then needed. Nothing exists? Build your own. Potential security issues. tl;dr Extensive but with added complexity.

Slide 19

Slide 19

Menu Attributes module, allows setting additional Menu item settings.

Slide 20

Slide 20

Use drush to download and enable Menu Attributes into D7 site.

Slide 21

Slide 21

Altering Drupal 7 menu item from admin, can now set style and target.

Slide 22

Slide 22

Second item in Drupal 7 menu now has changed title, destination and style.

Slide 23

Slide 23

No Drupal 8 release of Menu Attributes.

Slide 24

Slide 24

Create a custom “dflex_demo” module for Drupal 8.

Slide 25

Slide 25

dflex_demo.info.yml name: Drupal Flex Demo type: module description: This is a demo module to show off Drupal Flexibility core: 8.x package: Other

Slide 26

Slide 26

dflex_demo.module use Drupal\Core\Entity\EntityInterface; use Drupal\menu_link_content\Entity\MenuLinkContent; /** * Implements hook_entity_update(). */ function dflex_demo_entity_update(EntityInterface $entity) { if ($entity instanceof MenuLinkContent) { if ($entity->getTitle() == ‘DB #2’ && $entity->getMenuName() == ‘main’) { db_update(‘menu_tree’) ->fields(array( ‘options’ => serialize(array( ‘attributes’ => array( ‘style’ => ‘background:#FF0;color:#000’, ‘target’ => ‘_blank’, ), )), )) ->condition(‘id’, ‘menu_link_content:’ . $entity->uuid()) ->execute(); } } } Drupal 8 custom module dflex_demo implement instance of entity update hook, to alter menu link data saved to the database.

Slide 27

Slide 27

Use Drush to enable custom Drupal 8 module on Drupal 8 site.

Slide 28

Slide 28

Use Drupal 8 core to change second menu item title and destination. Custom module alters before saving to database.

Slide 29

Slide 29

Second item in Drupal 8 menu now has changed title, destination and style.

Slide 30

Slide 30

#3. Direct Queries Pros Cons Extremely powerful method. Queries can be complex. Direct data manipulation. Changes may not be stable. Can be implemented quickly. Can be very dangerous. tl;dr Powerful, but can be dangerous.

Slide 31

Slide 31

menu_links database table contains Drupal 7 menu link data.

Slide 32

Slide 32

UPDATE menu_links SET options = “a:1:{s:10:attributes;a:1:{s:5:’style’;s:26:’background:#C93;color:# FFF’;}}”, link_title = ‘DB #3’, link_path = ‘node/10’ WHERE menu_name = ‘main-menu’ AND link_path = ‘node/3’; Write custom query to update a Menu link in Drupal 7.

Slide 33

Slide 33

Use Drush sqlq command to run custom query to change menu link data in Drupal 7.

Slide 34

Slide 34

Third item in Drupal 7 menu now has changed title, destination and style.

Slide 35

Slide 35

menu_tree database table contains Drupal 8 menu link data.

Slide 36

Slide 36

UPDATE menu_tree SET options = “a:1:{s:10:attributes;a:1:{s:5:’style’;s:26:’background:#C93;color:# FFF’;}}”, title = ‘DB #3’, route_param_key = ‘node=10’, route_parameters = “a:1:{s:4:’node’;s:2:’10’;}” WHERE menu_name = ‘main’ AND route_param_key = ‘node=3’; Write custom query to update a Menu link in Drupal 8.

Slide 37

Slide 37

Use Drush sqlq command to run custom query to change menu link data in Drupal 8.

Slide 38

Slide 38

Third item in Drupal 8 menu now has changed title, destination and style.

Slide 39

Slide 39

Server Side Layer Data retrieved from the database, before being rendered.

Slide 40

Slide 40

#4. Hooks Pros Cons Provided by core and modules. Not used everywhere. Easiest way to extend core. Hooks maybe to broad/narrow. Many are available. May be early/late in a process. tl;dr The core method for extending…core.

Slide 41

Slide 41

Custom Drupal 7 theme ‘dflex’. Includes template.php for hooks.

Slide 42

Slide 42

dflex/template.php /** * Implements THEME_links__MENUNAME(). */ function dflex_links__system_main_menu($variables) { foreach ($variables[‘links’] as &$menu_link) { if ($menu_link[‘href’] == ‘node/4’) { $menu_link[‘href’] = ‘node/10’; $menu_link[‘title’] = ‘SS #1’; $menu_link[‘attributes’][‘style’] = ‘background:#F00;color:#FFF;’; $menu_link[‘attributes’][‘target’] = ‘_blank’; } } return theme_links($variables); } Implements an instance of a menu theme hook, to alter a menu link.

Slide 43

Slide 43

Before render, menu hits template hook. Fourth menu link in Drupal 7 has changed title, destination and style.

Slide 44

Slide 44

Custom Drupal 8 module ‘dflex_demo’. Will add another hook to .module file.

Slide 45

Slide 45

dflex_demo.module /** * Implements hook_preprocess_HOOK(). */ function dflex_demo_preprocess_menu(&$variables) { if ($variables[‘theme_hook_original’] == ‘menu__main’) { foreach ($variables[‘items’] as &$menu_link) { if ($menu_link[‘url’]->toString() == ‘/node/4’) { $menu_link[‘title’] = ‘SS #1’; $menu_link[‘url’] = \Drupal\Core\Url::fromUri(‘entity:node/10’, array( ‘attributes’ => array( ‘style’ => ‘background:#F00;color:#FFF’, ‘target’ => ‘_blank’, ), )); } } } } Implements an instance of a preprocess menu, to alter a menu link.

Slide 46

Slide 46

Before render, menu hits preprocess hook. Fourth menu link in Drupal 8 has changed title, destination and style.

Slide 47

Slide 47

#5. Hack Core Pros Cons Absolute control of Drupal. May cause unknown issues. Change any core functionality. Will break upgrade ability. Extremly powerful. Introduces security risks. tl;dr “With great power, comes great responsibility” ~ Uncle Ben.

Slide 48

Slide 48

Drupal 7 core file menu.inc, controls menu functionality.

Slide 49

Slide 49

includes/menu.inc /** * Returns an array of links for a navigation menu. * … */ function menu_navigation_links($menu_name, $level = 0) { // … $router_item = menu_get_item(); $links = array(); foreach ($tree as $item) { if (!$item[‘link’][‘hidden’]) { if (($menu_name == ‘main-menu’) && ($item[‘link’][‘href’] == ‘node/5’)) { $item[‘link’][‘href’] = ‘node/10’; $item[‘link’][‘title’] = t(‘SS #2’); $style = ‘background:#000;color:#FFF’; $item[‘link’][‘localized_options’][‘attributes’][‘style’] = $style; $item[‘link’][‘localized_options’][‘attributes’][‘target’] = ‘_blank’; } $class = ”; //… } Alter menu_navigation_links function to change a menu link in main menu.

Slide 50

Slide 50

Drupal 7 menu loads, uses hacked function. Fifth menu item now has altered title, destination and style.

Slide 51

Slide 51

Drupal 8 core class MenuLinkTree.php controls menu functionality.

Slide 52

Slide 52

core/lib/Drupal/Core/Menu/MenuLinkTree.php /** * Implements the loading, transforming and rendering of menu link trees. */ class MenuLinkTree implements MenuLinkTreeInterface { // … protected function buildItems(array $tree, …) { // … $url = $element[‘url’]->toString(); if(($link->getMenuName() == ‘main’) && ($url == ‘/node/5’)) { $element[‘title’] = ‘SS #2’; $element[‘url’] = \Drupal\Core\Url::fromUri(‘entity:node/10’, array( ‘attributes’ => array( ‘style’ => ‘background:#000;color:#FFF’, ‘target’ => ‘_blank’, ), )); } // Index using the link’s unique ID. $items[$link->getPluginId()] = $element; } return $items; } //… } Alter buildItems() function to change menu link in main menu.

Slide 53

Slide 53

Drupal 8 menu loads, uses hacked function. Fifth menu item now has altered title, destination and style.

Slide 54

Slide 54

Credit: Greg Dunlap (@heyrocker) (around 2008?) Hacking core is the “Wrong way”. Don’t do it or God willl kill a kitten.

Slide 55

Slide 55

#6. Services Pros Cons Change any core functionality. Only available for Drupal 8. No hacking required! Can be complex. Can be very powerful. Not for everything. tl;dr It’s hacking core without hacking core.

Slide 56

Slide 56

Drupal 8 custom module ‘dflex_demo’ with an ‘src’ directory, containting two new files.

Slide 57

Slide 57

DflexDemoServiceProvider.php namespace Drupal\dflex_demo; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\DependencyInjection\ServiceProviderBase; class DflexDemoServiceProvider extends ServiceProviderBase { /** * {@inheritdoc} */ public function alter(ContainerBuilder $container) { // Override the menu_link_tree class with a new class. $definition = $container->getDefinition(‘menu.link_tree’); $definition->setClass(‘Drupal\dflex_demo\DflexDemoMenuLinkTree’); } } Extend ServiceProviderBase and override alter function. Retrieve correct dependency injection container and tell Drupal what class to use as the service.

Slide 58

Slide 58

DflexDemoMenuLinkTree.php namespace Drupal\dflex_demo; use Drupal\Core\Menu\MenuLinkTree; class DflexDemoMenuLinkTree extends MenuLinkTree { /** * Overrides \Drupal\Core\Menu\MenuLinkTree::build(); */ public function build(array $tree) { $build = parent::build($tree); if (isset($build[‘#items’]) && ($build[‘#theme’] == ‘menu__main’)) { foreach ($build[‘#items’] as &$item) { if ($item[‘url’]->toString() == ‘/node/6’) { $item[‘title’] = ‘SS #3’; $item[‘url’] = \Drupal\Core\Url::fromUri(‘entity:node/10’, array( ‘attributes’ => array( ‘style’ => ‘background:#33C;color:#FFF’, ‘target’ => ‘_blank’, ))); } } } return $build; } } Custom MenuLinkTree Service. Extend the core service and override the build function, to alter menu link.

Slide 59

Slide 59

Drupal 8 menu is loaded using custom service to build menu. Sixth menu item has altered title, destination and style.

Slide 60

Slide 60

Client Side Layer Data when being rendered.

Slide 61

Slide 61

#7. Template Overrides Pros Cons Override Drupal markup. Not everything is templated. Make specific with suggestions. Template may be wrong level. Changes are cached. May still require server side. tl;dr Control of the rendered HTML.

Slide 62

Slide 62

Drupal 7 custom theme ‘dflex’, now contains a templates folder. Contains a template file for rendering links.

Slide 63

Slide 63

dflex/templates/link.tpl.php <?php $url = check_plain(url($variables[‘path’], $variables[‘options’])); $attributes = drupal_attributes($variables[‘options’][‘attributes’]); $text = ($variables[‘options’][‘html’] ? $variables[‘text’] : check_plain($var iables[‘text’])); ?> <?php if(isset($variables[‘path’]) == ‘node/7’): ?> <a href=”/alt-path” style=”background:#6CF;color:#FFF;”>CS #1</a> <?php else: ?> <a href=”<?php print $url; ?>” <?php print $attributes; ?>><?php print $text; ?></a> <?php endif; ?> Add additional logic to link template to display static link for particular path.

Slide 64

Slide 64

When Drupal 7 renders links will use overrriden template. Seventh menu item now has altered title, destination and style.

Slide 65

Slide 65

#8. Twig Pros Cons Override Drupal markup. Only in Drupal 8. More secure then old engine. Logic abilities are limited. Serverside logic with no PHP. Requires more processing. tl;dr More secure template overrides.

Slide 66

Slide 66

Custom Drupal 8 theme ‘dflex_demo’ contains a templates directory. Along with a twig template suggestion for main menu

Slide 67

Slide 67

dflex/templates/menu—main.html.twig {{ menus.menu_links(items, attributes, 0) }} {% macro menu_links(items, attributes, menu_level) %} {% import _self as menus %} {% if items %} {% if menu_level == 0 %} <ul{{ attributes.addClass(‘menu’) }}> {% else %} <ul class=”menu”> {% endif %} {% for item in items %} <li{{ item.attributes }}> {% if item.title == ‘Page 8’ and item.url.toString() == ‘/node/8’ %} <a href=”/alt-path” style=”background:#C3F;color:#FFF;”>CS #2</a> {% else %} {{ link(item.title, item.url) }} {% endif %} {% if item.below %} {{ menus.menu_links(item.below, attributes, menu_level + 1) }} {% endif %} </li> {% endfor %} </ul> {% endif %} {% endmacro %} Template override contains twig logic to display static link for specific menu item.

Slide 68

Slide 68

When rendering main menu, Drupal 8 users overriden twig menu template. Eighth menu item has altered title, destination and style.

Slide 69

Slide 69

#9. Custom JS Pros Cons Manipulate any DOM item. Dependent on browser support. Can be based on user actions. Delay before it is executed. Less server processing. Alterable by client. tl;dr Fancy client side functionality.

Slide 70

Slide 70

Drupal 7 custom theme ‘dflex’ contains a javascript file ‘dflex.js’.

Slide 71

Slide 71

dflex/dflex.info name = Drupal Flex Demo description = Demo subtheme for displaying Drupal Flexibility package = Core version = VERSION core = 7.x base theme = bartik stylesheets[all][] = css/colors.css scripts[] = dflex.js … Tell Drupal 7 about js file by adding it to the themes scripts array in the .info file.

Slide 72

Slide 72

dflex/dflex.js (function($){ Drupal.behaviors.dflex = { attach: function (context, settings) { $(‘#main-menu-links li > a’).each(function(){ if ($(this).attr(‘href’) == ‘/node/9’) { $(this).attr(‘style’, ‘background:#0F0;color:#000;’); $(this).attr(‘target’, ‘_blank’); $(this).attr(‘href’, ‘/alt-path’); $(this).text(‘CS #3’); } }) } } })(jQuery); Add custom Drupal behavour to alter main menu link after DOM loads.

Slide 73

Slide 73

After Drupal 7 page loads, custom javascript runs. Ninth menu item now has altered title, destination and style.

Slide 74

Slide 74

Drupal 8 custom module ‘dflex_demo’ has new libraries.yml file and a js directory with a javascript file.

Slide 75

Slide 75

dflex_demo/dflex_demo.libraries.yml dflex_demo: version: VERSION js: js/dflex-demo.js: {} dependencies: - core/jquery - core/jquery.once - core/drupal libraries.yml tells Drupal 8 what javacript libraries are provided and any dependencies they may have.

Slide 76

Slide 76

dflex_demo/js/dflex-demo.js (function ($, Drupal) { “use strict”; Drupal.behaviors.dflexDemo = { attach: function (context) { $(‘nav.menu—main > ul.menu li > a’).each(function(){ if ($(this).attr(‘href’) == ‘/node/9’) { $(this).attr(‘style’, ‘background:#0F0;color:#000;’); $(this).attr(‘target’, ‘_blank’); $(this).attr(‘href’, ‘/alt-path’); $(this).text(‘CS #3’); } }); } } })(jQuery, Drupal); Add custom Drupal behavour to alter main menu link after DOM loads.

Slide 77

Slide 77

dflex_demo/dflex_demo.module /** * Implements hook_page_attachments(). */ function dflex_demo_page_attachments(&$page) { // Add the dflex_demo js library to all pages. $page[‘#attached’][‘library’][] = ‘dflex_demo/dflex_demo’; } drupal_add_js() no longer exists in Drupal 8. Must add library to a render array. Custom module does so by implementing the hook_page_attachments.

Slide 78

Slide 78

When Drupal 8 page loads, drupal adds custom javascript library. After DOM loads custom behaviour is run. Ninth menu item now has altered title, destination and style.

Slide 79

Slide 79

Review Drupal provides many, many ways to manipulate data. No “right” way or “wrong” way. Just “right for me” way.

Slide 80

Slide 80

Slides & Notes bit.ly/NERD15Flex bit.ly/NERD15FlexSlides

Slide 81

Slide 81

Feedback @mikemiles86 #NERDsummit

Slide 82

Slide 82

Thank You! Questions? #NERDsummit @WeAreGenuine Drupal Flexibility / Michael Miles