Drupal

Webform Confirmation File

New Drupal Modules - 5 December 2018 - 8:02am

Provides a webform handler that streams the contents of a file to a user after completing a webform.

Categories: Drupal

Custom Checkboxes

New Drupal Modules - 5 December 2018 - 6:55am

This module allows you fastly style the checkboxes of your site. You will only have to adapt the CSS a little bit.

Sponsorship

Categories: Drupal

Custom View Filters

New Drupal Modules - 5 December 2018 - 6:31am

This module adds two new filters to views:

  • Custom Az Filter: You can filter by the first letter of first/second word for a given text field you will have to define.
  • Node granular date filter: You can filter by year or/and month given a date field.
Sponsorship

Categories: Drupal

Community: Values & Principles Committee Update - November 2018

Planet Drupal - 5 December 2018 - 4:00am

The Values & Principles Committee has formed and has started its work. It has started by looking at Principle 8.

Why are we doing this?

As Dries said, when announcing the first iteration of the Drupal Values & Principles, the Drupal project has had a set of Values & Principles for a very long time. Historically, they were mostly communicated by word of mouth and this meant that some in our community were more aware of them than others.

Writing down the Values & Principles was a great first step. What we need to do now is continually refine the common understanding of these Values & Principles across our whole community and ensure that they are built-in to everything we do.

How will we work?

The Values & Principles are held very closely to the heart of the members of our community and we absolutely recognise that any work on them must be inclusive, clear, structured and accountable.

We are, therefore, going to be open about the work we are doing. While there are members of a committee that will focus on this task, it is not the committee’s job to make decisions “behind closed doors”. Instead, the committee is responsible for enabling the whole community to refine and communicate our common Values & Principles.

We will record actions and progress in the Drupal Governance Project so that all in our community will be able to have the necessary input.

How will we communicate?

We will continue to post updates on the Drupal Community Blog and, as already mentioned, you will always be able to see and, most importantly, participate in issues in the Governance Project. We even have a board on ContribKanban!

Who is on the committee?

Hussain Abbas (hussainweb) works as an Engineering Manager at Axelerant. He started writing programs in 1997 for school competitions and never stopped. His work focus is helping people architect solutions using Drupal and enforcing best practices. He also participates in the local developer community meetup for PHP in general and Drupal in particular. He often speaks at these events and camps in other cities.

Alex Burrows (aburrows), from UK, is the Technical Director of Digidrop and has over 10 years working in Drupal, as well as an avid contributor and a member of the Drupal Community Working Group. As well as this he is a DrupalCamp London Director and Organizer and the author of Drupal 8 Blueprints book.

Jordana Fung (jordana) is a freelance, full-stack Drupal developer from Suriname, a culturally diverse country where the main language is Dutch. She has been steadily increasing her participation in the Drupal community over the past few years and currently has a role on the Drupal Community Working Group. She loves to spend her time learning new things, meeting new people and sharing knowledge and ideas.

Suchi Garg (gargsuchi), living in Melbourne Australia is a Tech Lead at Salsa Digital. She has been a part of the Drupal community for more than 12 years as a site builder, developer, contributor, mentor, speaker and trainer. She had been a part of the Indian community before moving to Australia and is now an active Drupal community down under.

John Kennedy (johnkennedy), lives in Boston, works as a Product Manager for AWS. Over 10 years in Drupal as a site-builder, developer, speaker and on the business side. Co-organiser of Drupal Camp London 2012-2015. PM for Acquia Lightning and the Drupal 8 Module Acceleration Program.

Rachel Lawson (rachel_norfolk), UK and the Community Liaison at the Drupal Association will finally be providing logistical support to the committee and helping wherever she can. Having been in the Drupal community for 11 years as a site builder, a contributor and a mentor, she has had opportunity to experience how the community understands its collective Values & Principles.

In order to be as transparent and forthcoming as possible we wanted to address the fact that there are currently 2 CWG members on the committee. The initial call for people to join the Values & Principles committee happened at the same time as the Community Working Group was calling for new members and, as luck would have it, Alex Burrows applied for both.

In October 2018 a current member of the CWG, Jordana Fung joined the Values & Principles committee and at same time he was being vetted for potential membership to the CWG, Alex joined the Values & Principles committee as well. After the vetting process, Alex officially became a member of the CWG in November. So as it stands now, there are 2 CWG members on the V&P committee.

There are a few possible options going forward, some are:

  • Both CWG members continue for now (whilst the V&P committee is in the very early formation stages) and then possibly:
    • One member drops off
    • They act as a team and only one member (whichever is available) participates in meetings
  • The CWG decides which member is on the VP committee
    • We may need to add another member to the VP committee to take the place of the member that will no longer attend.
So, what’s next?

We have started by compiling a summary of feedback from the community so far that relates to the project’s Values & Principles from such places as the Whitney Hess Interviews, community-led conversations around governance and some anonymized feedback from the Governance Taskforce. We will be adding this summary to an issue in the project.

Call to action

We recognize, though, that what we really want to understand is how you understand what we already have written in Principle 8. THis is how we intend to do that…

The members of the committee have each written stories from their own memories of the Drupal community that demonstrate Principle 8 in action.

We invite you all to write your own stories, from your memories of the Drupal community, other tech communities or indeed any other aspect of life, that demonstrate Principle 8 to you. You should add your story to this issue we have created:

Add my story about Principle 8

One thing we do ask, though, is that you only add your own stories (as many as you like!) and NOT comment or question others’ stories. All stories are valid.

By the end of the year, we hope to have a rich set of stories that show how we, as a global community, interpret Principle 8 and we can then look to see if any changes need to be made to the words or, maybe, it is more a case of linking the Principle to the stories or providing other statements supporting Principle 8.

Categories: Drupal

Simplenews Unsubscribe List

New Drupal Modules - 5 December 2018 - 3:56am
Categories: Drupal

Kristof De Jaeger: Send me a webmention with Drupal!

Planet Drupal - 5 December 2018 - 3:07am

After months of reading, experimenting and a lot of coding, I'm happy that the first release candidate of the Drupal IndieWeb module is out. I guess this makes the perfect time to try it out for yourself, no? There are a lot of concepts within the IndieWeb universe, and many are supported by the module. In fact, there are 8 submodules, so it might be daunting to start figuring out which ones to enable and what they exactly allow you to do. To kick start anyone interested, I'll publish a couple of articles detailing how to set up several concepts using the Drupal module. The first one will explain in a few steps how you can send a webmention to this page. Can you mention me?

Step 1: enabling modules

After you downloaded the module and installed the required composer packages, enable following modules: IndieWeb, Webmention and Microformats2. In case you are not authenticated as user 1, also toggle the following permissions: 'Administer IndieWeb configuration' and 'Send webmention'.

Step 2: expose author information

To discover the author of a website after receiving a webmention, your homepage, or the canonical url of a post needs author information. The module comes with an Author block so you can quickly expose a block where you can configure your name. Your real name or nickname is fine, as long as there's something. The minimal markup should look like something like this:

Your name


Note: this can be anywhere in your HTML, even hidden. Step 3: configure webmention module

All configuration exposed by the modules lives under 'Web services' > 'IndieWeb' at /admin/config/services/indieweb. To configure sending webmentions go to /admin/config/services/indieweb/webmention/send. Ignore the ' Syndication targets' fieldset and scroll down to ' Custom URL's for content' and toggle the 'Expose textfield' checkbox.

Scroll down a bit more and configure how you want to send webmentions, either by cron or drush (webmentions are stored in a queue first for performance reasons)

Step 4: configure Microformats module

When sending a webmention to me, it would be nice to be able to figure out what exactly your post is. To achieve this, we need to add markup to the HTML by using CSS classes. Let's configure the minimal markup at /admin/config/services/indieweb/microformats by toggling following checkboxes:

  • h-entry on node wrappers
  • e-content on standard body fields. In case your node type does not use the standard body field, enter the field name in the 'e-content on other textarea fields' textarea.
  • dt-published, p-name, u-author and u-url in a hidden span element on nodes.
Now create a post!

Create a post with a title and body. Your body needs to contain a link with a class so that when I receive your webmention, I know that this page is valid. As an example, we're going to write a reply:

Hi swentel! I just read your article and it's awesome!

Save the post and verify the markup more or less looks like underneath. Make sure you see following classes: h-entry, u-url, p-name, dt-published, e-content, u-author.


 
    
       
          Published on Tue, 04/12/2018 - 22:39
       
       
         
            Test send!
            2018-12-04T22:39:57+01:00
         

         
       
     
     
 
  Hi swentel! I just read your article and it's awesome!
 

If everything looks fine, go to the node form again. Open the 'Publish to' fieldset where you can enter 'https://realize.be/blog/send-me-webmention-drupal' in the custom URL textfield. Save again and check the send list at /admin/content/webmention/send-list. It should tell that there is one item in the queue. As a final step, run cron or the 'indieweb-send-webmentions' drush command. After that the queue should be empty and one entry will be in the send list and I should have received your webmention!

Note: You can vary between the 'u-in-reply-to', 'u-like-of' or 'u-repost-of' class. Basically, the class determines your response type. The first class will create a comment on this post. The other two classes will be a mention in the sidebar.

What's next?

Well, a lot of course. But the next step should be receiving webmentions no? If you go to /admin/config/services/indieweb/webmention, you can enable receiving webmentions by using the built-in endpoint. Make sure you expose the link tag so I know where to mention you!

I tried it, and it didn't work!

Maybe I missed something in the tutorial. Or you have found a bug :) Feel free to ping me on irc.freenode.net on #indieweb-dev or #drupal-contribute. You may also open an issue at https://github.com/swentel/indieweb

Categories: Drupal

Drupal's commitment to accessibility

Dries Buytaert - 5 December 2018 - 2:56am

Last week, WordPress Tavern picked up my blog post about Drupal 8's upcoming Layout Builder.

While I'm grateful that WordPress Tavern covered Drupal's Layout Builder, it is not surprising that the majority of WordPress Tavern's blog post alludes to the potential challenges with accessibility. After all, Gutenberg's lack of accessibility has been a big topic of debate, and a point of frustration in the WordPress community.

I understand why organizations might be tempted to de-prioritize accessibility. Making a complex web application accessible can be a lot of work, and the pressure to ship early can be high.

In the past, I've been tempted to skip accessibility features myself. I believed that because accessibility features benefited a small group of people only, they could come in a follow-up release.

Today, I've come to believe that accessibility is not something you do for a small group of people. Accessibility is about promoting inclusion. When the product you use daily is accessible, it means that we all get to work with a greater number and a greater variety of colleagues. Accessibility benefits everyone.

As you can see in Drupal's Values and Principles, we are committed to building software that everyone can use. Accessibility should always be a priority. Making capabilities like the Layout Builder accessible is core to Drupal's DNA.

Drupal's Values and Principles translate into our development process, as what we call an accessibility gate, where we set a clearly defined "must-have bar". Prioritizing accessibility also means that we commit to trying to iteratively improve accessibility beyond that minimum over time.

Together with the accessibility maintainers, we jointly agreed that:

  1. Our first priority is WCAG 2.0 AA conformance. This means that in order to be released as a stable system, the Layout Builder must reach Level AA conformance with WCAG. Without WCAG 2.0 AA conformance, we won't release a stable version of Layout Builder.
  2. Our next priority is WCAG 2.1 AA conformance. We're thrilled at the greater inclusion provided by these new guidelines, and will strive to achieve as much of it as we can before release. Because these guidelines are still new (formally approved in June 2018), we won't hold up releasing the stable version of Layout Builder on them, but are committed to implementing them as quickly as we're able to, even if some of the items are after initial release.
  3. While WCAG AAA conformance is not something currently being pursued, there are aspects of AAA that we are discussing adopting in the future. For example, the new 2.1 AAA "Animations from Interactions", which can be framed as an achievable design constraint: anywhere an animation is used, we must ensure designs are understandable/operable for those who cannot or choose not to use animations.

Drupal's commitment to accessibility is one of the things that makes Drupal's upcoming Layout Builder special: it will not only bring tremendous and new capabilities to Drupal, it will also do so without excluding a large portion of current and potential users. We all benefit from that!

Categories: Drupal

Dries Buytaert: Drupal's commitment to accessibility

Planet Drupal - 5 December 2018 - 2:56am

Last week, WordPress Tavern picked up my blog post about Drupal 8's upcoming Layout Builder.

While I'm grateful that WordPress Tavern covered Drupal's Layout Builder, it is not surprising that the majority of WordPress Tavern's blog post alludes to the potential challenges with accessibility. After all, Gutenberg's lack of accessibility has been a big topic of debate, and a point of frustration in the WordPress community.

I understand why organizations might be tempted to de-prioritize accessibility. Making a complex web application accessible can be a lot of work, and the pressure to ship early can be high.

In the past, I've been tempted to skip accessibility features myself. I believed that because accessibility features benefited a small group of people only, they could come in a follow-up release.

Today, I've come to believe that accessibility is not something you do for a small group of people. Accessibility is about promoting inclusion. When the product you use daily is accessible, it means that we all get to work with a greater number and a greater variety of colleagues. Accessibility benefits everyone.

As you can see in Drupal's Values and Principles, we are committed to building software that everyone can use. Accessibility should always be a priority. Making capabilities like the Layout Builder accessible is core to Drupal's DNA.

Drupal's Values and Principles translate into our development process, as what we call an accessibility gate, where we set a clearly defined "must-have bar". Prioritizing accessibility also means that we commit to trying to iteratively improve accessibility beyond that minimum over time.

Together with the accessibility maintainers, we jointly agreed that:

  1. Our first priority is WCAG 2.0 AA conformance. This means that in order to be released as a stable system, the Layout Builder must reach Level AA conformance with WCAG. Without WCAG 2.0 AA conformance, we won't release a stable version of Layout Builder.
  2. Our next priority is WCAG 2.1 AA conformance. We're thrilled at the greater inclusion provided by these new guidelines, and will strive to achieve as much of it as we can before release. Because these guidelines are still new (formally approved in June 2018), we won't hold up releasing the stable version of Layout Builder on them, but are committed to implementing them as quickly as we're able to, even if some of the items are after initial release.
  3. While WCAG AAA conformance is not something currently being pursued, there are aspects of AAA that we are discussing adopting in the future. For example, the new 2.1 AAA "Animations from Interactions", which can be framed as an achievable design constraint: anywhere an animation is used, we must ensure designs are understandable/operable for those who cannot or choose not to use animations.

Drupal's commitment to accessibility is one of the things that makes Drupal's upcoming Layout Builder special: it will not only bring tremendous and new capabilities to Drupal, it will also do so without excluding a large portion of current and potential users. We all benefit from that!

Categories: Drupal

Remove html from node form menu items

New Drupal Modules - 5 December 2018 - 1:28am

Using menu_html menu allows to add html to menu items. But then on creation of new content in menu section menu parent items appear with that html tags.
This module hides a html tags used in menu items on node form.

Categories: Drupal

User Points Drupal 8

New Drupal Modules - 5 December 2018 - 12:32am

User Points module provides an entity to store the points accumulated by a user. It provides API functions for storing/removing points for various scenarios such as Login, Register, Posting Comment or Creating an Article. Points can be easily retrieved using API functions and use for various promotional stuff in the website.

The administrator can also set custom scenarios and award points on basis of those.

It is currently in active development and will be fit for production website very soon.

Categories: Drupal

Custom Entity Pager

New Drupal Modules - 5 December 2018 - 12:09am

The Custom Entity Pager module provides a twig extension that create a pager of content types.

Similar modules
  • Entity Pager: This module is similar but Custom Entity Pager is better performant because it avoids the usage of views. So far, our module is compatible with nodes.
Sponsorship

Categories: Drupal

Content Snippets

New Drupal Modules - 4 December 2018 - 2:48pm

Content Snippets is a simple module that provides an administrative and content editor interface for editing small bits of text that can then be used by developers when they would be otherwise tempted to hard-code content into custom code, themes, etc. Then these bits of text can be controlled by Content Editors without developer intervention. The snippets are also available as global tokens.

Categories: Drupal

Kanopi Studios: Kanopi 2019 DrupalCon Seattle Sessions

Planet Drupal - 4 December 2018 - 12:29pm

We are thrilled to have had three of our sessions chosen for DrupalCon Seattle in April 2019. You’ll find us at the booth, in the hallway, and out and about in Seattle, but make sure to visit us in our three Builder Track sessions:

Keep Living the Dream! How to work remotely AND foster a happy, balanced life

Virtual. Remote. Distributed. Pick your label. This style of organization is becoming wildly more in demand and popular among many Drupal shops. While many folks have gone remote, some people find the experience quite isolating and disconnected.

In this session we will talk about how to be the best remote employee, as well as provide ideas if you are a leader of a remote team. We will talk about key tactics to keep you (and all other staff) inspired, creative, productive and most importantly, happy!

Presenter: Anne Stefanyk

Beyond the Screen Reader: Humanizing Accessibility

We talk a lot about the basics of accessibility. But what does it really mean to be accessible? How do we ensure we are including everyone and empowering every user in every scenario to use our sites, products, and devices? We think about deaf and blind users, we check contrast for colorblind users. We consider the elderly and sometimes those with dyslexia. Are we including trans folks? Parents? The chronically ill? People with limited literacy? The injured? People in a major emergency? Who are we designing for? What should we be considering?

If you’re wondering how these folks might be affected by accessibility and you want your website to be inclusive for everyone, this is the session for you.

Presenter: Alanna Burke

Deep Cleaning: Creating franchise model efficiencies with Drupal 8

COIT offers cleaning and 24/7 emergency restoration services. Their 100+ locations serve more than 12 million homes & businesses across the United States and Canada.

It had been years since the COIT site had been updated, and it posed a host of technical challenges. Franchise content optimizations resulted in redundant updates for the SEO team. The mobile experience wasn’t optimized for conversions. There was a mountain of custom technical debt. And despite the current content administrative challenges, the localized experience lacked the level of context-awareness that consumers have come to expect. It was time for COIT to clean up its own mess.

In this case study we will cover the more technical parts of this Drupal 8 implementation: how we kept a multinational but distinctly separate brand presence with geolocative features, maintained custom promotions tailored to each franchise location, and kept the existing hard-won SEO and SEM business drivers intact.

Presenters: Anne Stefanyk and Katherine White

The post Kanopi 2019 DrupalCon Seattle Sessions appeared first on Kanopi Studios.

Categories: Drupal

qualpay-commerce-drupal7

New Drupal Modules - 4 December 2018 - 10:41am
Categories: Drupal

NonAutocomplete Widget

New Drupal Modules - 4 December 2018 - 8:09am

This module provides an entityreference widget that does not show autocomplete suggestions. Note that this does not change any access restrictions on the referenced entities nor change the autocomplete callback (so technically versatile people can use it to get the autocomplete suggestions anyway).

Categories: Drupal

Elfsight PayPal Button

New Drupal Modules - 4 December 2018 - 6:53am
Introduction

Elfsight PayPal Button provides a simple way to allow your customers to make payments via PayPal on your website. It enables you to create buttons for single payments that can include taxes and shipping price; subscriptions with recurring option; and donations with specified or open value. Localization and wide choice of currencies will help make transactions comfortable for you and your clients. Set up a smooth and secure payment procedure for your clients using one of the most reputable payment tools in seconds.

Categories: Drupal

Commerce Balance Field

New Drupal Modules - 4 December 2018 - 6:19am

Adds a pseudo-field to the Manage Display tab of the commerce order which will display the order balance of the order.

Categories: Drupal

ComputerMinds.co.uk: Open links in popups with Foundation

Planet Drupal - 4 December 2018 - 3:16am

Let me take you on a journey. We'll pass by Drupal content renderer services, AJAX commands, javascript libraries and a popular front-end framework. If you've only heard of one or two of those things, come lean on the experience I took diving deep into Drupal. I'm pleased with where my adventure took me to, and maybe what I learned will be useful to you too.

Here's the end result: a contact form, launched from a button link in the site header, with the page beneath obscured by an overlay. The form allows site visitors to get in touch from any page, without leaving what they were looking at.

Drupal has its own API for making links launch dialogs (leveraging jQuery UI Dialogs). But our front-end of the site was built with Foundation, the super-popular theming framework, which provides components of its own that are much better for styling. We often base our bespoke themes on Foundation, and manipulate Drupal to fit.

We had already done some styling of Foundation's Reveal component. In those places, the markup to show in the popup is already in the page, but I didn't really want the form to be in the page until it was needed. Instead, AJAX could fetch it in. So I wondered if I could combine Drupal's AJAX APIs with Foundation's Reveal markup and styling. Come with me down the rabbit hole...

There are quite a few components in making this possible. Here's a diagram:

So it comes down to the following parts, which we'll explore together. Wherever custom code is needed, I've posted it in full later in this article.

  • A link that uses AJAX, with a dialog type set in an attribute.
  • Drupal builds the content of the page that was linked to.
  • Drupal's content view subscriber picks up that response and looks for a content renderer service that matches the dialog type.
  • The content renderer returns an AJAX command PHP class in its response, and attaches a javascript library that will contain a javascript AJAX command (a method).
  • That command returns the content to show in the popup, and that javascript method name.
  • The javascript method launches the popup containing the HTML content.

Let's start at the beginning: the link. Drupal's AJAX API for links is pretty neat. We trigger it with two things:

  1. A use-ajax class, which tells it to open that link via an AJAX call, returning just the main page content (e.g. without headers & footers), to be presented in your existing page.
  2. A data-dialog-type attribute, to instruct how that content should be presented. This can be used for the jQuery UI dialogs (written up elsewhere) or the newer off-canvas sidebar, for example.

I wanted to have a go at creating my own 'dialog type', which would be a Foundation Reveal popup. The HTML fetched by the AJAX call would be shown in it. Let's start with the basic markup I wanted to my link to have:

Enquire

This could either just be part of content, or I could get this into a template using a preprocess function that would build the link. Something like this:

<?php // $url could have come from $node->toUrl(), Url::fromRoute() or similar. // For this example, it's come from a contact form entity. $url->setOption('attributes', [ 'class' => [ 'use-ajax', ], // This attribute tells it to use our kind of dialog 'data-dialog-type' => 'reveal', ]); // The variable 'popup_launcher' is to be used in the template. $variables['popup_launcher'] = \Drupal\Core\Link::fromTextAndUrl(t('Enquire'), $url);

After much reading around and breakpoint debugging to figure it out, I discovered that dialog types are matched up to content rendering services. So I needed to define a new one of those, which I could base closely on Drupal's own DialogRenderer. Here's the definition from my module's mymodule.services.yml file:

services: main_content_renderer.foundation_reveal: class: Drupal\mymodule\Render\MainContent\FoundationReveal arguments: ['@title_resolver'] tags: - { name: render.main_content_renderer, format: drupal_reveal }

Adding the tag named 'render.main_content_renderer' means my class will be picked up by core's MainContentRenderersPass when building the container. Drupal's MainContentViewSubscriber will then consider it as a service that can render responses.

The 'format' part of the tag needs to be the value that our data-dialog-type attribute has, with (somewhat arbitrarily?) 'drupal_' prepended. The arguments will just be whatever the constructor for the class needs. I often write my class first and then go back to adjust the service definition once I know what it needs. But I'll be a good tour guide and show you things in order, rather than shuttling you backwards and forwards!

Onto that FoundationReveal service class now. I started out with a copy of core's own ModalRenderer which is a simple extension to the DialogRenderer class. Ultimately, that renderer is geared around returning an AJAX command (see the AJAX API documentation), which comes down to specifying a command to invoke in the client-side javascript with some parameters.

I would need my own command, and my FoundationReveal renderer would need to specify it to be used. That only two functional differences were needed in comparison to core's DialogRenderer:

  1. Attach a custom library, which would contain the actual javascript command to be invoked:
$main_content['#attached']['library'][] = 'mymodule/dialog.ajax';
  1. Return an AJAX command class, that will specify that javascript command (rather than the OpenDialogCommand command that DialogRenderer uses) - i.e. adding this to the returned $response:
new OpenFoundationRevealCommand('#mymodule-reveal')

We'll learn about that command class later!

So the renderer file, mymodule/src/Render/MainContent/FoundationReveal.php (in that location in order to match the namespace in the service file definition), looks like this - look out for those two tweaks:

<?php namespace Drupal\mymodule\Render\MainContent; use Drupal\Core\Ajax\AjaxResponse; use Drupal\Core\Render\MainContent\DialogRenderer; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\mymodule\Ajax\OpenFoundationRevealCommand; use Symfony\Component\HttpFoundation\Request; /** * Default main content renderer for foundation reveal requests. */ class FoundationReveal extends DialogRenderer { /** * {@inheritdoc} */ public function renderResponse(array $main_content, Request $request, RouteMatchInterface $route_match) { $response = new AjaxResponse(); // First render the main content, because it might provide a title. $content = drupal_render_root($main_content); // Attach the library necessary for using the OpenFoundationRevealCommand // and set the attachments for this Ajax response. $main_content['#attached']['library'][] = 'core/drupal.dialog.ajax'; $main_content['#attached']['library'][] = 'mymodule/dialog.ajax'; $response->setAttachments($main_content['#attached']); // Determine the title: use the title provided by the main content if any, // otherwise get it from the routing information. $title = isset($main_content['#title']) ? $main_content['#title'] : $this->titleResolver->getTitle($request, $route_match->getRouteObject()); // Determine the dialog options and the target for the OpenDialogCommand. $options = $request->request->get('dialogOptions', []); $response->addCommand(new OpenFoundationRevealCommand('#mymodule-reveal', $title, $content, $options)); return $response; } }

That AJAX command class, OpenFoundationRevealCommand sits in mymodule/src/Ajax/OpenFoundationRevealCommand.php. Its render() method is the key, it returns the command which will map to a javascript function, and the actual HTML under 'data'. Here's the code:

<?php namespace Drupal\mymodule\Ajax; use Drupal\Core\Ajax\OpenDialogCommand; use Drupal\Core\StringTranslation\StringTranslationTrait; /** * Defines an AJAX command to open certain content in a foundation reveal popup. * * @ingroup ajax */ class OpenFoundationRevealCommand extends OpenDialogCommand { use StringTranslationTrait; /** * Implements \Drupal\Core\Ajax\CommandInterface:render(). */ public function render() { return [ 'command' => 'openFoundationReveal', 'selector' => $this->selector, 'settings' => $this->settings, 'data' => $this->getRenderedContent(), 'dialogOptions' => $this->dialogOptions, ]; } /** * {@inheritdoc} */ protected function getRenderedContent() { if (empty($this->dialogOptions['title'])) { $title = ''; } else { $title = '' . $this->dialogOptions['title'] . ''; } $button = 't('Close') . '" type="button">×'; return '' . $title . parent::getRenderedContent() . '' . $button; } }

Now, I've mentioned that the command needs to match a javascript function. That means adding some new javascript to the page, which, in Drupal 8, we do by defining a library. My 'mymodule/dialog.ajax' library was attached in the middle of FoundationReveal above. My library file defines what actual javascript file to include - it is mymodule.libraries.yml and looks like this:

dialog.ajax: version: VERSION js: js/dialog.ajax.js: {} dependencies: - core/drupal.dialog.ajax

Then here's that actual mymodule/js/dialog.ajax.js file. It adds the 'openFoundationReveal' method to the prototype of the globally-accessible Drupal.AjaxCommands. That matches the command name returned by my OpenFoundationRevealCommand::render() method that we saw.

(function ($, Drupal) { Drupal.AjaxCommands.prototype.openFoundationReveal = function (ajax, response, status) { if (!response.selector) { return false; } // An element matching the selector will be added to the page if it does not exist yet. var $dialog = $(response.selector); if (!$dialog.length) { // Foundation expects certain things on a Reveal container. $dialog = $('').appendTo('body'); } if (!ajax.wrapper) { ajax.wrapper = $dialog.attr('id'); } // Get the markup inserted into the page. response.command = 'insert'; response.method = 'html'; ajax.commands.insert(ajax, response, status); // The content is ready, now open the dialog! var popup = new Foundation.Reveal($dialog); popup.open(); }; })(jQuery, Drupal);

There we have it - that last bit of the command opens the Foundation Reveal popup dialog!

I should also add that since I was showing a contact form in the popup, I installed the Contact ajax module. This meant that a site visitor would stay within the popup once they submit the form, which meant for a clean user experience.

Thanks for following along with me!

Categories: Drupal

WeKnow: Improving Drupal and Gatsby Integration - The Drupal Modules

Planet Drupal - 4 December 2018 - 3:15am
Improving Drupal and Gatsby Integration - The Drupal Modules

At weKnow we are not only using Drupal, we also take contributing back very seriously and now is the time for improving the Drupal and Gatsby integration.

As mentioned in my personal blog, Moving weKnow's personal blog sites from Drupal to GatsbyJS, we have been using Gatsby with Drupal for projects as our decouple strategy lately, and after building a few sites with Drupal and Gatsby we found some challenges, which we resolved writing custom code. But now we’ve decided to share our knowledge as contributed modules.

Toast UI Editor

This module provides a markdown WYSIWYG editor integration for Toast UI Editor

jmolivas Tue, 12/04/2018 - 11:15
Categories: Drupal

Web Omelette: Simple Guzzle API mocking for functional testing in Drupal 8

Planet Drupal - 4 December 2018 - 12:05am

In this article I am going to show you a technique I used recently to mock a relatively simple external service for functional tests in Drupal 8.

Imagine the following scenario: you have an API with one or a few endpoints and you write a service that handles the interaction with it. For example, one of the methods of this service takes an ID and calls the API in order to return the resource for that ID (using the Guzzle service available in Drupal 8). You then cast the Guzzle response stream to a string and return whatever from there to use in your application. How can you test your application with this kind of requirements?

The first thing you can do is unit test your service. In doing so, you can pass to it a mock client that can return whatever you set to it. Guzzle even provides a MockHandler that you can use with the client and specify what you want returned. Fair enough. But what about things like Kernel or Functional tests that need to use your client and make requests to this API? How can you handle this?

It’s not a good idea to use the live API endpoint in your tests for a number of reasons. For example, your testing pipeline would depend on an external, unpredictable service which can go down at any moment. Sure, it’s good to catch when this happens but clearly this is not the way to do it. Or you may have a limited amount of requests you can make to the endpoint. All these test runs will burn through your budget. And let’s not forget you need a network connection to run the tests.

So let’s see an interesting way of doing this using the Guzzle middleware architecture. Before diving into that, however, let’s cover a few theoretical aspects of this process.

Guzzle middlewares

A middleware is a piece of functionality that can be added to the pipeline of a process. For example, the process of turning a request into a response. Check out the StackPHP middlewares for a nice intro to this concept.

In Guzzle, middlewares are used inside the Guzzle handler stack that is responsible for turning a Guzzle request into a response. In this pipeline, middlewares are organised as part of the HandlerStack object which wraps the base handler that does the job, and are used to augment this pipeline. For example, let’s say a Guzzle client uses the base Curl handler to make the request. We can add a middleware to the handler stack to make changes to the outgoing request or to the incoming response. I highly recommend you read the Guzzle documentation on handlers and middlewares for more information.

Guzzle in Drupal 8

Guzzle is the default HTTP client used in Drupal 8 and is exposed as a service (http_client). So whenever we need to make external requests, we just inject that service and we are good to go. This service is instantiated by a ClientFactory that uses the default Guzzle handler stack (with some specific options for Drupal). The handler stack that gets injected into the client is configured by Drupal’s own HandlerStackConfigurator which also registers all the middlewares it finds.

Middlewares can be defined in Drupal as tagged services, with the tag http_client_middleware. There is currently only one available to look at as an example, used for the testing framework: TestHttpClientMiddleware.

Our OMDb (Open Movie Database) Mock

Now that we have an idea about how Guzzle processes a request, let’s see how we can use this to mock requests made to an example API: OMDb.

The client

Let’s assume a module called omdb which has this simple service that interacts with the OMDb API:

<?php namespace Drupal\omdb; use Drupal\Core\Site\Settings; use Drupal\Core\Url; use GuzzleHttp\ClientInterface; /** * Client to interact with the OMDb API. */ class OmdbClient { /** * @var \GuzzleHttp\ClientInterface */ protected $client; /** * Constructor. * * @param \GuzzleHttp\ClientInterface $client */ public function __construct(ClientInterface $client) { $this->client = $client; } /** * Get a movie by ID. * * @param \Drupal\omdb\string $id * * @return \stdClass */ public function getMovie(string $id) { $settings = $this->getSettings(); $url = Url::fromUri($settings['url'], ['query' => ['apiKey' => $settings['key'], 'i' => $id]]); $response = $this->client->get($url->toString()); return json_decode($response->getBody()->__toString()); } /** * Returns the OMDb settings. * * @return array */ protected function getSettings() { return Settings::get('omdb'); } }

We inject the http_client (Guzzle) and have a single method that retrieves a single movie from the API by its ID. Please disregard the complete lack of validation and error handling, I tried to keep things simple and to the point. To note, however, is that the API endpoint and key is stored in the settings.php file under the omdb key of $settings. That is if you want to play around with this example.

So assuming that we have defined this service inside omdb.services.yml as omdb.client and cleared the cache, we can now use this like so:

$client = \Drupal::service('omdb.client'); $movie = $client->getMovie('tt0068646');

Where $movie would become a stdClass representation of the movie The Godfather from the OMDb.

The mock

Now, let’s assume that we use this client to request movies all over the place in our application and we need to write some Kernel tests that verify that functionality, including the use of this movie data. One option we have is to switch out our OmdbClient client service completely as part of the test, with another one that has the same interface but returns whatever we want. This is ok, but it’s tightly connected to that test. Meaning that we cannot use it elsewhere, such as in Behat tests for example.

So let’s explore an alternative way by which we use middlewares to take over any requests made towards the API endpoint and return our own custom responses.

The first thing we need to do is create a test module where our middleware will live. This module will, of course, only be enabled during test runs or any time we want to play around with the mocked data. So the module can be called omdb_tests and we can place it inside the tests/module directory of the omdb module.

Next, inside the namespace of the test module we can create our middleware which looks like this:

<?php namespace Drupal\omdb_tests; use Drupal\Core\Site\Settings; use GuzzleHttp\Promise\FulfilledPromise; use GuzzleHttp\Psr7\Response; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; /** * Guzzle middleware for the OMDb API. */ class OmdbMiddleware { /** * Invoked method that returns a promise. */ public function __invoke() { return function ($handler) { return function (RequestInterface $request, array $options) use ($handler) { $uri = $request->getUri(); $settings = Settings::get('omdb'); // API requests to OMDb. if ($uri->getScheme() . '://' . $uri->getHost() . $uri->getPath() === $settings['url']) { return $this->createPromise($request); } // Otherwise, no intervention. We defer to the handler stack. return $handler($request, $options); }; }; } /** * Creates a promise for the OMDb request. * * @param RequestInterface $request * * @return \GuzzleHttp\Promise\PromiseInterface */ protected function createPromise(RequestInterface $request) { $uri = $request->getUri(); $params = \GuzzleHttp\Psr7\parse_query($uri->getQuery()); $id = $params['i']; $path = drupal_get_path('module', 'omdb_tests') . '/responses/movies'; $json = FALSE; if (file_exists("$path/$id.json")) { $json = file_get_contents("$path/$id.json"); } if ($json === FALSE) { $json = file_get_contents("$path/404.json"); } $response = new Response(200, [], $json); return new FulfilledPromise($response); } }

Before explaining what all this code does, we need to make sure we register this as a tagged service inside our test module:

services: omdb_tests.client_middleware: class: Drupal\omdb_tests\OmdbMiddleware tags: - { name: http_client_middleware }

Guzzle middleware services in Drupal have one single (magic) method called __invoke. This is because the service is treated as a callable. What the middleware needs to do is return a (callable) function which gets as a parameter the next handler from the stack that needs to be called. The returned function then has to return another function that takes the RequestInterface and some options as parameters. At this point, we can modify the request. Lastly, this function needs to make a call to that next handler by passing the RequestInterface and options, which in turn will return a PromiseInterface. Take a look at TestHttpClientMiddleware for an example in which Drupal core tampers with the request headers when Guzzle makes requests during test runs.

So what are we doing here?

We start by defining the first two (callable) functions I mentioned above. In the one which receives the current RequestInterface, we check for the URI of the request to see if it matches the one of our OMDb endpoint. If it doesn’t we simply call the next handler in the stack (which should return a PromiseInterface). If we wanted to alter the response that came back from the next handler(s) in the stack, we could call then() on the PromiseInterface returned by the stack, and pass to it a callback function which receives the ResponseInterface as a parameter. In there we could make the alterations. But alas, we don’t need to do that in our case.

A promise represents the eventual result of an asynchronous operation. The primary way of interacting with a promise is through its then method, which registers callbacks to receive either a promise's eventual value or the reason why the promise cannot be fulfilled.

Read this for more information on what promises are and how they work.

Now for the good stuff. If the request is made to the OMDb endpoint, we create our own PromiseInterface. And very importantly, we do not call the next handler. Meaning that we break out of the handler stack and skip the other middlewares and the base handler. This way we prevent Guzzle from going to the endpoint and instead have it return our own PromiseInterface.

In this example I decided to store a couple of JSON responses for OMDb movies in files located in the responses/movies folder of the test module. In these JSON files, I store actual JSON responses made by the endpoint for given IDs, as well as a catch-all for whenever a missing ID is being requested. And the createPromise() method is responsible for determining which file to load. Depending on your application, you can choose exactly based on what you would like to build the mocked responses.

The loaded JSON is then added to a new Response object that can be directly added to the FulfilledPromise object we return. This tells Guzzle that the process is done, the promise has been fulfilled, and there is a response to return. And that is pretty much it.

Considerations

This is a very simple implementation. The API has many other ways of querying for data and you could extend this to store also lists of movies based on a title keyword search, for example. Anything that serves the needs of the application. Moreover, you can dispatch an event and allow other modules to provide their own resources in JSON format for various types of requests. There are quite a lot of possibilities.

Finally, this approach is useful for “simple” APIs such as this one. Once you need to implement things like Oauth or need the service to call back to your application, some more complex mocking will be needed such as a dedicated library and/or containerised application that mocks the production one. But for many such cases in which we read data from an endpoint, we can go far with this approach.

Categories: Drupal

Pages

Subscribe to As If Productions aggregator - Drupal