Building
rednoseday.com
on Drupal 8
Peter Vanhee

Tech Lead at Comic Relief

@pvhee
DrupalCamp London 2017
• Comic Relief strives to create a just world free from
poverty
• We raise millions of pounds through two big fundraising
campaigns – Red Nose Day and Sport Relief.
• We spend that money in the best possible way to tackle
the root causes of poverty and social injustice.
• We use the power of our brand to raise awareness of the
issues that we care most about.
Who am I?
• Tech Lead at Comic Relief 

https://technology.comicrelief.com
• Founder of Marzee Labs, a tech shop from
Barcelona @marzeelabs
• Long-time Drupal contributor @pvhee
Drupal at Comic Relief
• 2014: comicrelief.com
• 2015: 2015.rednoseday.com
• 2016: sportrelief.com
Drupal at Comic Relief
• 2014: comicrelief.com
• 2015: 2015.rednoseday.com
• 2016: sportrelief.com
• 2017: rednoseday.com
A fresh start?
• Build a campaign website
• Build a product that can build campaign websites
• Build a product that allows editors to reorganise
row components to build a website
• Continuously iterate over our codebase
A product should…
• support iterative development
• have a clear versioning scheme
• have tests to guarantee quality
• provide a sensible default start for any campaign
• allow for customisation
Technology Choice?
Drupal 8, because of
• embracing industry PHP standards
• built on top of Symfony, use of Twig and Composer
• editorial features “out of the box”
• accessibility features
• built-in REST capabilities
• a development challenge
You can’t be that kid
standing at the top
of the waterslide,
overthinking it.
You have to go
down the chute.

Tina Fey
Ingredients to build our product
• Focus on Editor Experience
• Automate and streamline
• Decoupled services
Editor Experience
Landing pages as the norm
• First iteration: panels with panelizer
• Second iteration: panels with panelizer and
embedded paragraphs
• Third iteration: paragraphs with block reference
Header with Menu
Footer
Paragraph 1
Paragraph 2
Paragraph 3
Paragraph n
… library of blocks
custom blocks 

(like email signup)
Editorial blocks
Paragraph types
“content wall”

(rows + teasers are blocks)
Cards
blocks are reusable
paragraphs are not reusable
Quotes
Embed
Node Block reference
BLOCKS
PARAGRAPHS
reusable
not reusable
libraryquick edit
contextual links
only editable via node
no quick-edit

(see node …)
content fields
layout fields
reference
Living Style Guide
Living Style Guide
• Using KSS, “a documentation syntax for CSS”
• Our new “incubation area”
• Code and style guide are one — no need to update
one or the other independently thus guaranteeing
it stays fresh!
Style-guide driven development
Component
idea
Style Guide

(or Pattern Lab)



HTML, SCSS, JS
Review

Multi-device QA
Sign-off
Drupal development



Content model

View modes

Component module

Twig / PHP

Improved editor experience
Red Nose Day 2015

(Drupal 7)
Red Nose Day 2017

(Drupal 8)
Automate & streamline
everything
A build in one step
• Package up our website product using an
installation profile and using CMI, via
config_devel
• Default content in JSON
• Use a build tool — we use Phing and run 

phing build often
Config modules
Collaborative coding
No automation without tests
• Code quality checks using CodeSniffer, phpmd, phpcpd
• Configuration checks using config_devel
• Distribution installation tests using phing build
• Behavior tests on distribution using Behat
• Drupal log checks (no errors, warnings generated by Behat)
• Visual regression tests using BBC Wraith
Behat tests
Continuous Integration
Preview branches
Decoupled services
Slide from Chet Haase
Minimise custom code
• we have around 2000 custom lines of PHP code
(options callbacks, custom Display Suite fields, Solr
tweaks)
• “Non-glue code” is contributed back as standalone
Drupal modules: rabbitmq, social_links
The Embed Pattern
The Queue Pattern
Producer = Drupal Queue Consumer = 3rd party
an example: email list subscription

- Producer: Email Signup Form

- Queue: holds email address, template ID, etc.

- Consumer: PHP / nodejs app talking to MailChimp

Drupal Producer/**
* Our signup form.
*/
class ExampleForm extends FormBase {
public function buildForm(array $form, Form…face $form_state) {
$form['email'] = [
'#type' => 'email',
'#title' => $this->t(‘Your email address.'),
];
$form['show'] = [
'#type' => 'submit',
'#value' => $this->t('Submit'),
];
return $form;
}
public function submitForm(array &$form, Form…face $form_state) {
// Get the data you want to send to the queue.
$data = $form_state->getValue('email');
// Get the queue config and send it to the data to the queue.
$queue_name = 'queue1';
$queue_factory = Drupal::service('queue');
$queue = $queue_factory->get($queue_name);
$queue->createItem($data);
// Send some feedback.
drupal_set_message(
$this->t(‘Added data to queue @queue: @email', [
'@queue' => $queue_name,
'@email' => $form_state->getValue('email'),
])
);
}
}
// Our rabbitmq.config.yml configuration
exchanges:
my_exchange:
type: 'direct'
passive: false
durable: true
auto_delete: false
internal: false
nowait: false
queues:
my_queue:
passive: false
durable: false
exclusive: false
auto_delete: true
nowait: false
routing_keys:
- 'my_exchange.my_queue'
// Add to settings.php
$settings['queue_service_queue1'] = 

'queue.rabbitmq';
rabbitmq module at 

drupal.org/project/rabbitmq
using Drupal 8 Queue API
RabbitMQ Queue
3-rd party Consumer
class QueueConsumer implements ConsumerInterface
{
/* @var stdClass */
private $processingService;
/**
* Parse message as JSON and send to processor
*
* @param AMQPMessage $msg
* @return bool
*/
public function execute(AMQPMessage $msg)
{
if ($decodedMessage = json_decode($msg->body)) {
try {
return $this->processingService->process($decodedMessage);
} catch (Exception $e) {
$this->logger->alert(‘Queue process error:' . $e->getMessage());
}
} else {
$this->logger->info(sprintf('Unable to parse as JSON: "%s"',
$msg->body));
}
return false;
}
}
Towards micro-services
• oEmbed / iFrame for integrating 3rd-party apps in
the CMS
• Message Queues for decoupling logic
What about rednoseday.com?
From product to website
campaign 

distribution

- v1.1

- … 

- v1.36

- … 

- v2.x
rednoseday.com

comicrelief.com
rednoseday.org
(USA)
sportrelief.com
From product to website
campaign 

distribution

- v1.1

- … 

- v1.36

- … 

- v2.x
rednoseday.com

- campaign v1.36 as
dependency
- env vars (queue info, db
info, …)

- RND17 theme

- YML site configuration sites/
default/config
- composer / make
- hook_update

- drush config-devel-import
comicrelief.com
rednoseday.org
(USA)
sportrelief.com
you?
Questions?

@pvhee

www.rednoseday.com
Friday 24 March

Building rednoseday.com on Drupal 8

  • 1.
    Building rednoseday.com on Drupal 8 PeterVanhee
 Tech Lead at Comic Relief
 @pvhee DrupalCamp London 2017
  • 2.
    • Comic Reliefstrives to create a just world free from poverty • We raise millions of pounds through two big fundraising campaigns – Red Nose Day and Sport Relief. • We spend that money in the best possible way to tackle the root causes of poverty and social injustice. • We use the power of our brand to raise awareness of the issues that we care most about.
  • 3.
    Who am I? •Tech Lead at Comic Relief 
 https://technology.comicrelief.com • Founder of Marzee Labs, a tech shop from Barcelona @marzeelabs • Long-time Drupal contributor @pvhee
  • 4.
    Drupal at ComicRelief • 2014: comicrelief.com • 2015: 2015.rednoseday.com • 2016: sportrelief.com
  • 6.
    Drupal at ComicRelief • 2014: comicrelief.com • 2015: 2015.rednoseday.com • 2016: sportrelief.com • 2017: rednoseday.com
  • 7.
    A fresh start? •Build a campaign website • Build a product that can build campaign websites • Build a product that allows editors to reorganise row components to build a website • Continuously iterate over our codebase
  • 8.
    A product should… •support iterative development • have a clear versioning scheme • have tests to guarantee quality • provide a sensible default start for any campaign • allow for customisation
  • 9.
    Technology Choice? Drupal 8,because of • embracing industry PHP standards • built on top of Symfony, use of Twig and Composer • editorial features “out of the box” • accessibility features • built-in REST capabilities • a development challenge
  • 10.
    You can’t bethat kid standing at the top of the waterslide, overthinking it. You have to go down the chute.
 Tina Fey
  • 11.
    Ingredients to buildour product • Focus on Editor Experience • Automate and streamline • Decoupled services
  • 12.
  • 13.
    Landing pages asthe norm • First iteration: panels with panelizer • Second iteration: panels with panelizer and embedded paragraphs • Third iteration: paragraphs with block reference
  • 14.
    Header with Menu Footer Paragraph1 Paragraph 2 Paragraph 3 Paragraph n … library of blocks custom blocks 
 (like email signup) Editorial blocks Paragraph types “content wall”
 (rows + teasers are blocks) Cards blocks are reusable paragraphs are not reusable Quotes Embed Node Block reference
  • 15.
    BLOCKS PARAGRAPHS reusable not reusable libraryquick edit contextuallinks only editable via node no quick-edit
 (see node …) content fields layout fields reference
  • 16.
  • 19.
    Living Style Guide •Using KSS, “a documentation syntax for CSS” • Our new “incubation area” • Code and style guide are one — no need to update one or the other independently thus guaranteeing it stays fresh!
  • 20.
    Style-guide driven development Component idea StyleGuide
 (or Pattern Lab)
 
 HTML, SCSS, JS Review
 Multi-device QA Sign-off Drupal development
 
 Content model
 View modes
 Component module
 Twig / PHP

  • 21.
    Improved editor experience RedNose Day 2015
 (Drupal 7) Red Nose Day 2017
 (Drupal 8)
  • 27.
  • 28.
    A build inone step • Package up our website product using an installation profile and using CMI, via config_devel • Default content in JSON • Use a build tool — we use Phing and run 
 phing build often
  • 29.
  • 30.
  • 31.
    No automation withouttests • Code quality checks using CodeSniffer, phpmd, phpcpd • Configuration checks using config_devel • Distribution installation tests using phing build • Behavior tests on distribution using Behat • Drupal log checks (no errors, warnings generated by Behat) • Visual regression tests using BBC Wraith
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
    Minimise custom code •we have around 2000 custom lines of PHP code (options callbacks, custom Display Suite fields, Solr tweaks) • “Non-glue code” is contributed back as standalone Drupal modules: rabbitmq, social_links
  • 38.
  • 39.
    The Queue Pattern Producer= Drupal Queue Consumer = 3rd party an example: email list subscription
 - Producer: Email Signup Form
 - Queue: holds email address, template ID, etc.
 - Consumer: PHP / nodejs app talking to MailChimp

  • 40.
    Drupal Producer/** * Oursignup form. */ class ExampleForm extends FormBase { public function buildForm(array $form, Form…face $form_state) { $form['email'] = [ '#type' => 'email', '#title' => $this->t(‘Your email address.'), ]; $form['show'] = [ '#type' => 'submit', '#value' => $this->t('Submit'), ]; return $form; } public function submitForm(array &$form, Form…face $form_state) { // Get the data you want to send to the queue. $data = $form_state->getValue('email'); // Get the queue config and send it to the data to the queue. $queue_name = 'queue1'; $queue_factory = Drupal::service('queue'); $queue = $queue_factory->get($queue_name); $queue->createItem($data); // Send some feedback. drupal_set_message( $this->t(‘Added data to queue @queue: @email', [ '@queue' => $queue_name, '@email' => $form_state->getValue('email'), ]) ); } } // Our rabbitmq.config.yml configuration exchanges: my_exchange: type: 'direct' passive: false durable: true auto_delete: false internal: false nowait: false queues: my_queue: passive: false durable: false exclusive: false auto_delete: true nowait: false routing_keys: - 'my_exchange.my_queue' // Add to settings.php $settings['queue_service_queue1'] = 
 'queue.rabbitmq'; rabbitmq module at 
 drupal.org/project/rabbitmq using Drupal 8 Queue API
  • 41.
  • 42.
    3-rd party Consumer classQueueConsumer implements ConsumerInterface { /* @var stdClass */ private $processingService; /** * Parse message as JSON and send to processor * * @param AMQPMessage $msg * @return bool */ public function execute(AMQPMessage $msg) { if ($decodedMessage = json_decode($msg->body)) { try { return $this->processingService->process($decodedMessage); } catch (Exception $e) { $this->logger->alert(‘Queue process error:' . $e->getMessage()); } } else { $this->logger->info(sprintf('Unable to parse as JSON: "%s"', $msg->body)); } return false; } }
  • 43.
    Towards micro-services • oEmbed/ iFrame for integrating 3rd-party apps in the CMS • Message Queues for decoupling logic
  • 44.
  • 45.
    From product towebsite campaign 
 distribution
 - v1.1
 - … 
 - v1.36
 - … 
 - v2.x rednoseday.com
 comicrelief.com rednoseday.org (USA) sportrelief.com
  • 46.
    From product towebsite campaign 
 distribution
 - v1.1
 - … 
 - v1.36
 - … 
 - v2.x rednoseday.com
 - campaign v1.36 as dependency - env vars (queue info, db info, …)
 - RND17 theme
 - YML site configuration sites/ default/config - composer / make - hook_update
 - drush config-devel-import comicrelief.com rednoseday.org (USA) sportrelief.com you?
  • 47.