#NERDsummit
@WeAreGenuine
Drupal Flexibility /
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
Everything in Drupal can be manipulated!
Three general layers
tl;dr Easy to use, but limited functionality.
tl;dr Extensive but with added complexity.
name: Drupal Flex Demo
type: module
description: This is a demo module to show off Drupal Flexibility
core: 8.x
package: Other
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();
}
}
}
tl;dr Powerful, but can be dangerous.
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';
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';
Data retrieved from the database, before being rendered.
tl;dr The core method for extending...core.
/**
* 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 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',
),
));
}
}
}
}
tl;dr "With great power, comes great responsibility" ~ Uncle Ben.
/**
* 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 = '';
//...
}
/**
* 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;
}
//...
}
tl;dr It's hacking core without hacking core.
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');
}
}
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;
}
}
tl;dr Control of the rendered HTML.
<?php
$url = check_plain(url($variables['path'], $variables['options']));
$attributes = drupal_attributes($variables['options']['attributes']);
$text = ($variables['options']['html'] ? $variables['text'] : check_plain($variables['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; ?>
tl;dr More secure template overrides.
{{ 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 %}
tl;dr Fancy client side functionality.
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
...
(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);
dflex_demo:
version: VERSION
js:
js/dflex-demo.js: {}
dependencies:
- core/jquery
- core/jquery.once
- core/drupal
(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);
/**
* 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';
}
Questions?