Feed aggregator

Red Route: Jumping through hoops with the golden flexbox hammer

Planet Drupal -

I've been a big proponent of using Flexbox for a while, especially since hearing Zoe Mickley Gillenwater speaking about it at Smashing Conference Oxford 2014.

In particular, I use justify-content: space-between a lot. But one issue with it is what happens in the last row. If the number of child items doesn't divide nicely into the number of items per row, there will be a big gap between them, as you can see from this Codepen example:

See the Pen space-between by malcomio (@malcomio) on CodePen.

It can look pretty ugly, especially if the parent element is wide. One possible solution is to have the items in the last row fill the available space. But for the tiles layout on the Gallery Guide, that wouldn't work - it would make the last row items much too big. Ideally, the last row would be given a different behaviour - perhaps using a different justify-content value, perhaps using floats, but as far as I'm aware, there isn't a nice CSS way to achieve this.

The suggestion I found on StackOverflow is to add extra elements. Given that the rows are being generated by a Drupal view, we can achieve this using a preprocess function, adding dummy rows, which don't affect small screens because their height is set to zero.

Here's a Codepen example showing the idea:

See the Pen space-between with dummy rows by malcomio (@malcomio) on CodePen.

The relevant views all use the unformatted list format, so in the implementation of template_preprocess_views_view_unformatted we add a variable to say how many extra rows are needed to make it fit nicely:

define('GALL_VIEWS_ITEMS_PER_ROW', 4); /** * Implements template_preprocess_views_view_unformatted(). */ function gall_preprocess_views_view_unformatted(&$variables) { // Add dummy rows so that flexbox looks nice. $view_id = $variables['view']->id(); $tiles_views = _gall_tiles_views(); if (in_array($view_id, $tiles_views)) { $remainder = count($variables['view']->result) % GALL_VIEWS_ITEMS_PER_ROW; $rows_to_add = GALL_VIEWS_ITEMS_PER_ROW - $remainder; if ($remainder && $rows_to_add) { $variables['extra_rows'] = $rows_to_add; } } }

Once we've added this counter, we can use it to create a loop in our views-view-unformatted.html.twig template:

{% if extra_rows %} {% for i in 1..extra_rows %} {% endfor %} {% endif %}

And, as if by magic, the view rows are aligned left. Problem solved.

But maybe the problem was one of my own making. Even before I'd finished building this, I was realising that maybe it would have been easier to just use floats. To paraphrase Abraham Maslow, or perhaps Abraham Kaplan, someone who has just discovered a hammer will always be looking for nails. As always, there's another way I could have solved this, and the new way isn't always better than the old way. Having said that, I do like the way that flexbox helps to keep my margins tidy...

Tags:  Drupal Drupal 8 The Gallery Guide CSS flexbox All tags

Darryl Norris's Blog: Get Your Libraries And Breakpoint Information From The UI

Planet Drupal -

Have you ever try to get data from your libraries and/or breakpoints in Drupal 8 ? Drupal 8 core does not provide a UI for this information.  And sometimes is nice to have the ability to know your data from the UI. Instead of trying to hunt down all that information by searching many files. For this reason, I decide to write few modules that will allow you to get some of the libraries and breakpoint information from the UI. 

Libraries UI

  • Project Page: https://www.drupal.org/project/libraries_ui
  • Module Description: This module will provide a UI to display all libraries provide by modules and themes. Once libraries_ui is been installed visit /admin/config/media/libraries_ui to get all breakpoints information.

Breakpoints UI

  • Project Page: https://www.drupal.org/project/breakpoints_ui
  • Module Description: This module will provide a UI to display all breakpoints provide by modules and themes. Once breakpoints_ui is been installed visit /admin/config/media/breakpoints_ui to get all
More

Attiks: Dream fields for Drupal 8

Planet Drupal -

I went to Drupalcon NOLA and was looking for a new way to contribute, since there've been a lot of discussion about the front-end part, and after reading @dries blog post Turning Drupal outside-in I started looking at the field UI. I stumbled upon the core issue titled The options under the Add field drop-down describe the data you want to store, but the user was imagining the widget it would produce and decided that the outside-in approach might be a good approach.

By Peter Droogmans

Joachim's blog: What goes on in Drupal Code Builder?

Planet Drupal -

Drupal Code Builder library is the new library which powers Module Builder. I recently split Module Builder up, so Drupal Code Builder (DCB) is the engine for generating Drupal code, while what remains in the Module Builder module is just the UI.

DCB is an extensible framework, so if you wanted to have DCB create scaffold code for a particular Drupal component or system, you can.

DCB's API is documented in the README. It's based on the idea of tasks: for example, list the hooks and plugin types that DCB has parsed from the site code, analyze the site code to update that list, or generate code for a module. There are Task classes, and you call public methods on these to do something.

The generators

Broadly, there are three things you want to do with DCB: collect and analyze data about a Drupal codebase to learn about hooks and plugin types, report on that data, and actually generate some code.

The Generate task class is where the work of creating code begins. The other task classes are all pretty simple, or at least self-contained, but the Generate task is accompanied by a large number of classes in the DrupalCodeBuilder\Generate namespace. You can see from the file names that these represent all the different components that make up generated code.

Furthermore, as well as all inheriting from BaseGenerator, there are hierarchies which can probably be deduced from the names alone, where more specialized generators inherit from generic ones. For example, we have:

  • File
    • PHPFile
    • ModuleCodeFile
    • PHPClassFile
      • Plugin
      • Service
    • API (this one's for your mymodule.api.php file)
    • YMLFile
    • Readme

and also:

  • PHPFunction
    • HookImplementation
    • HookMenu
    • HookPermission

However, these hierarchies are only about code re-use. In terms of PHP code, HookImplementation is only related to ModuleCodeFile by the common BaseGenerator base class. As the process of code generation takes place, there will be a tree of components that represents components containing each other, but it's important to remember that class inheritance doesn't come into it.

Also, while the generators in the hierarchies above clearly represent some tangible part of the code we're going to generate, some are more abstract, such as Module and Hooks. These aren't abstract in the OO sense, as they will get instantiated, but I think of them as abstract in the sense that they're not concrete and are responsible for code across different files. (Suggestions for a better word to describe them please!)

The process of generating code starts with a call to the Generate task's generateComponent() method. The host UI application (such as Module Builder module, or the Drush command) passes it an array of data that looks something like this:

[ 'base' => 'module', 'root_name' => 'mymodule, 'readable_name' => 'My module', 'hooks' => [ 'form_alter' => TRUE, 'install' => TRUE, ], 'plugins => [ 0 => [ 'plugin_type' => 'block', 'plugin_name' => 'my_plugin', 'injected_services' => [ 'current_user', ], ], ], 'settings_form' => TRUE, 'readme' => TRUE, ]

(How you get the specification for this array as a list of properties and their expected format is a detailed topic of its own, which will be covered later. For now, we're jumping in at the point where code is generated.)

Assembling components

The first job for the Generate task class is to turn this array of data into a list of generator classes for the necessary components.

This list is built up in a cascade, where each component gets to request further components, and those get to request components too, and so on, until we reach components that don't request anything. We start with the root component that was initially requested, Module, let that request components, and then repeat the process.

This is best illustrated with the AdminSettingsForm generator. This implements the requiredComponents() method to request:

  • a permission
  • a router item (on Drupal 7 that's a menu item, but in DCB we refer to these a router item whatever the core Drupal version)
  • a form

In turn, the Permission generator requests a permissions YAML file. You'll see that there are two Permission generators, each with a version suffix. The Permission7 generator requests a hook_permission() hook, which in turn requests a .module file. The Permission8 generator is somewhat simpler, and just requests a YMLFile component.

Meanwhile, the router item requests a routing.yml file on D8, and a hook_menu() on D7.

These two parts of the cascade end when we reach the various file generators: ModuleCodeFile and YMLFile don't request anything. The process that gathers all these generators works iteratively: every iteration it calls requiredComponents() on all the components the previous iteration gave it, and it only stops once an iteration produces no new components. It's safe to request the same component multiple times; in the D7 version of our example, both our hook_menu() and hook_permission() will request a ModuleCodeFile component that represents the .module file. The cascade system knows to either combine these two requests into one component, or ignore the second if it's identical to what's already been requested.

We now have a list of about a dozen or so components, each of which is an instantiated Generator object. Some represent files, some represent functions, and some like Hooks represent a more vague concept of the module 'having some hooks'. There's also the Module generator which started the whole process, whose requiredComponents() did most of the work of interpreting the given array of data.

Assembling a tree of components

The second part of the process is to assemble this flat list of components into a tree. This is where the notion of which component contains others does come into play. This is a different concept from requested components: a component can request something that it won't end up containing, as we saw with the AdminSettingsForm, which requests a permission.

The Generate task calls the containingComponent() method on each component, and this is used to assemble an array of parentage data. There's nothing fancy or recursive going on here; the tree is just an array whose keys are the identifiers of components, and whose values are arrays of the child component identifiers.

This tree now represents a structure of components where child items will produce code to be included in their parents. One part of this structure could be represented like this:

  • module
    • routing.yml
    • router item
    • permission.yml
    • permission
    • .install
    • hook_install()

Some components, such as the Hooks component, are no longer around now: their job was to be a sort of broker for other components in the requesting phase, and they're no longer involved. The root component, Module, is the root of the tree. All the files we'll be outputting are its immediate children. (This is not a file hierarchy, folders are not represented here.)

Assembling file contents

We now have everything we need to start actually generating some code. This is done in a way that's very similar to Drupal's Render API: we recurse into the tree, asking each component to return some content both from itself and its children.

So for example, the router items contribute some lines to the routing.yml file, which then turns them into YAML. The .install component, which is an instance of ModuleCodeFile, produces a @file docblock, and then gets the docblock, function declaration, and function body from the hook_install component, and glues them all together.

Finally, each file component (the immediate children of the module component in the tree) gets to say what its file name and path should be.

So the Generate task has an array of data about files, where each item has a file name, file path, and file contents. This is returned to the caller to be output to the user, or written to the filesystem. Module Builder presents the files in a form, and allows the files to be written. The Drush command outputs them to terminal and optionally writes them too.

Extending it with new components

The best way to add new things for DCB to generate is to inherit from existing basic classes. If these don’t provide the flexibility, there’s always a case to be made to give them more configurable options: for example, the AdminSettingsForm class inherits from Form, but neither of those do very little for the actual generated form class, as the work for that is mostly done by the PHPClass class.

The roadmap for DCB at the moment consists of the following:

  • Generalize the injected services functionality that’s already in Plugins, so generated Form classes and Services can have them too.
  • Add Forms as a basic component that you can request to generate. (It’s currently there only as a base for the AdminSettingsForm generator.)

And as ever, keep adding tests, keep refactoring and improving the code. But I'm always interested in hearing new ideas (or you know, better yet, patches) in the issue queue.

Blue Drop Shop: Drupal Camp Session Recordings: A Year in Review

Planet Drupal -

It has been nearly a year since I’ve updated the status of my camp recording kits. Since DCSTL15, two other camps took me up on my proposal to sponsor my travel and hotel in exchange for me recording and posting their sessions: TCDrupal and BADCamp. And, of course, as a MidCamp organizer, that counts too. And with each those camps, I’ve iterated and learned from invaluable successes and failures.

First off, here is a link to the current kit.

With everything, each kit is still under $450. In addition, zip ties to hold the VGA to HDMI dongle tight and some gaffers tape to secure everything to the podium are needed.

Recap

At Twin Cities, I learned that, while I try, I cannot reasonably start and stop every recording in every room, especially at camps with five concurrent sessions spread over multiple floors and buildings. The amount of volunteer participation at TCDrupal is incredibly impressive. I had loads of help at my disposal, but only a few moments to outline how the kits work, so I spent a lot of time troubleshooting from room to room.

BADCamp is another camp that sprawls over a campus and is a bit looser on the room monitor support. So this time, I came armed with printed instructions at each podium for hooking up to the kit (link). I added some basic troubleshooting and my phone number. I missed about half the session starts, but speakers were mostly able to follow the instructions and run things without me. That was a huge win. Unfortunately, remembering to also start/stop the audio record was hit or miss.

By the time MidCamp rolled around, I simplified the instructions further and also set the backup audio record to just run all day, removing the failure point of missed audio. The big red button is easy and enticing. The little button on the audio recorder remote...not so much. MidCamp, with two days of four concurrent sessions was my first 100% captured camp since St. Louis.

Pain Points

There are four recurring issues with this setup:

  • VGA-only laptops
  • Recurring audio problems
  • File segmenting
  • Random projector problems

Hopefully, the time of laptops that only have VGA out is coming to an end. I've tried several different VGA-to-HDMI converters with basically no luck. And to spend hundreds of dollars or more for a fool-proof converter when modern laptops have better video output is a hard pill to swallow. I don't foresee this being a long-term problem.

The audio issues are baffling. In some cases, no audio at all is recorded with the screen capture, while other times it is sped up and choppy, hence the importance of the backup audio files from the voice recorder. But this means post-processing time which delays uploads. I intend to contact Hauppauge support, but honestly don't expect to get very far as I am using their device as it was not intended. Lastly, the capture device has a touch panel for adjusting gain and muting the audio. It is a little to easy to accidentally mute the audio.

Minor annoyance: occasionally, the recordings will split into two or more files, meaning I have to stitch them together in post.

At MidCamp for the past two years (both held at different locations on UIC campus), some of the projectors would intermittently go dark during presentations. While this has no impact on the recording, it is extremely unsettling for the presenter and annoying for the attendees. I recall this happening in some cases at Twin Cities, but not at BADCamp. So this one currently has me stumped with no good plan of resolution at this time.

Next Steps

For obvious reasons, I can't record all the sessions at all the camps. And already I have firm plans to record Twin Cities in June, St. Louis in September, and BADCamp in October. Talking to folks at Drupalcon, I also now have soft commitments with Drupal GovCon in July and Drupal Camp New Jersey in January. And other camps have reached out, but I have conflicts.

I managed to pack up a complete kit into a 10" Pelican case. This means that if I can start training some proxies and write up some detailed instructions and troubleshooting, then this solution can scale. Maybe folks won’t have experience with the post-production, but I can help with that remotely, if needed. The beauty of these kits is that with timely starts and stops and good audio, the MP4 file on the thumb drive can be uploaded as soon as it is collected.

The good news is that the more camps I can record, the more data I can collect and the more I can refine the process to make it scalable.

Stay tuned!

Tags:

Drupal Association News: Hello, World! (Goodbye, Drupal Association)

Planet Drupal -

My first day on the job, I got on an airplane and flew to Australia to attend DrupalCon Sydney. As first days on the job go, that’s gotta be up there as one of the best. It definitely set the tone for life in the Drupal community - it’s been an exciting adventure every single day. I’ve traveled around the world, worked with incredibly smart people, and learned four or five Git commands (thanks Cathy!).

So it’s not without some sadness that I share that my last day on this job will be June 3. Why am I leaving? Simply put, because I can. Drupal 8 is out and thriving. The Association is doing more and doing it better than it ever has. Now is the time for me to take a step back, eat some cake, and then find something new to jump into (after a nap, and probably some more cake).

Luckily, the Drupal community has an amazing individual ready to step in to lead the Association. I’m proud beyond words to see Megan Sanicki take on these challenges and work with you all as the next Executive Director of the Association. I know she will continue to build an Association that operates from its values for and with the Drupal community. We’ve been working together on this transition for a little while now, and I can’t wait to see what she does.

I just want to share a couple of thanks before I go. First, I’m deeply proud of the team that we have built at the Drupal Association. The Drupal Association staff are the rainbow unicorns of teams. They are honest about their opinions, but kind in their delivery. They are fierce in their loyalty to the community, and even more so in their loyalty to each other. They genuinely care about every interaction, and even when things go sideways, you can trust that their intentions were nothing but good. I learned from them. Every. Single. Day. I owe them a heck of a lot more than this thank you, but I wanted to get it out in the world. They are the best. Treat them well.

Secondly, I want to thank the dozens of community members who have gone out of their way to support me in this role. I’ll be following up personally with as many of you as I can, but I wanted to call out a few of you in particular. Angie taught me that introverts can learn to like hugs. George and Tiffany taught me to take my time and find the exact right words. Paul taught me that you can’t have too many passion projects. Donna taught me that it’s not summer everywhere. Cathy taught me Git (well, four or five commands that I can remember). There is so much generosity in Drupal.

The Association board and Megan will be working hard over the next few weeks on this transition to make sure that we continue to grow our support of the community, keep producing amazing DrupalCons, and ensure that Drupal remains the best darn CMS out there. I’ll be over here rooting for all of you. You’ll find me next to the cake.

Radium on Drupal: Deploying Drupal Sites with Docker Compose

Planet Drupal -

Deploying a Drupal site (or any website) could sometimes be cumbersome, in particular if you have multiple websites running on one server. The amount of time wasted in configuring the server could be considerable. Docker is one of the tools that can save us from the "configuration hell". Thanks to pre-built images, I no longer have to worry about dependencies since they can be all included in one image. Also, unlike virtual machine, Docker is fast and take only a few seconds to start. Another benefit is that now you can have the same environment on your local machine and on the server -- just use the same image. In this post I will quickly walk through the steps of using Docker Compose to deploy Drupal.

ActiveLAMP: Encapsulation, Inheritance, Polymorphism with Drupal Entities - SandCamp 2016

Planet Drupal -

One of the best things to happen with the Drupal 7 release was the introduction of Entities. Drupal Entities have been around forever, but it seems like a lot of developers still refer back to using Nodes when creating content that requires more functionality than what Nodes give you out of the box. In this video, I talk about why it’s a good idea to create your own Entities when the content you’re adding requires extended functionality. I talk about the “what” and the “why” of Entities, not necessarily “how” to create an Entity. There are a bunch of resources already out there on the Internet for that. I talk about using the Entity API module, and defining your own Class for your custom Entities. This presentation was given at SandCamp 2016.

Read more...

DrupalCon News: Let us know what you thought about the Con

Planet Drupal -

Thank you so much for attending DrupalCon New Orleans.  We had an amazing time and hope that you did too.  

After each Con, we ask that you please let us know how it went so we can see what we can improve for next time.  Please

Fill Out the Survey

We also understand that you may be interested in receiving a Certificate of Attendance.  If so, please fill out the request form and we will get back to you shortly.

tanay.co.in: Announcing www.d8cards.com - A simple Drupal 8 ladder for small study groups

Planet Drupal -

At my workplace, we had earlier formed a study group to try out some very simple Drupal 8 stuff.

 

As we progressed, we had built a bunch of activity cards that we used for the group.

 

They are now available @ www.d8cards.com.

 

Check them out. Each activity card has a tutorial and an exercise that you could try out. There are 21 cards covering varied Drupal 8 Topics.

Registration for Twin Cities Drupal Camp 2016 (June 23rd – 26th) is open!

Wisconsin Drupal Group -

Start:  2016-06-23 (All day) - 2016-06-26 (All day) America/Chicago Event type:  Drupalcamp or Regional Summit

http://2016.tcdrupal.org/register

Registration for Twin Cities Drupal Camp 2016 (June 23rd – 26th) is open!
Sign up today to get early-bird pricing before it’s gone.

The biggest local Drupal event of the year is coming up fast, and it will have something for everyone. Four chock-full days of training, sessions and keynotes by nationally known trainers and speakers, famously fun social events, Drupal code sprints, and much more. Whether you are a Drupal expert, intermediate, a keen novice or just curious, we will have something for you.

As a bonus, this year we will have a special higher education component to the camp with a dedicated Higher Education Summit on Thursday 23 and related sessions throughout the camp. All are welcome to attend any of these, so spread the word.

Check the website often for training, keynote and event details, which will be announced soon. Note: you will need to register for these on the website once they are announced.

Submit that session topic you have been thinking about

Feel like sharing what you've learned about Drupal, business and other Web topics such as project management, favorite modules, development processes, design/UX, SEO, content strategy, back-end wizardry, front-end tips or whatever subject interests you? Submit your session idea by May 20 on the website.

Get the limited edition t-shirt

Again this year we're offering a Twin Cities Drupal Community t-shirt. When you choose the Community Sponsor option ($50) you'll get this exclusive t-shirt along with camp registration. Funds support our camp and local Drupal events throughout the year.

Don't miss the Friday night party spectacular, June 24

Join fellow Drupal Camp attendees on Friday from 6-10 PM at The House of Balls – a quirky and wonderful event space for the annual Advantage Labs Party.
http://houseofballs.com/events.html

There will be snacks, libations, games, and time for open lightning talks. Come to hang out, socialize or show off that cool site or module you just finished! Advantage Labs has a national rep for hosting some of the best parties of any Drupal Camp. This is a not-to-be-missed event.

Finally, help us spread the good news. Please tell colleagues, friends and anyone else who might be interested in the Twin Cities Drupal Camp.

Important dates

Early bird registration ends: May 20
Sessions announced: end of May
Higher Education Summit and Training Day: June 23 (all day)
Workshops and sessions: Friday and Saturday, June 24-26 (all day)
Camp party: Friday, June 26 (evening)
Community sprints: Sunday, June 27 (all day)

Registration for Twin Cities Drupal Camp 2016 (June 23rd – 26th) is open!

Twin Cities Drupal Group -

Start:  2016-06-23 (All day) - 2016-06-26 (All day) America/Chicago Event type:  Drupalcamp or Regional Summit

http://2016.tcdrupal.org/register

Registration for Twin Cities Drupal Camp 2016 (June 23rd – 26th) is open!
Sign up today to get early-bird pricing before it’s gone.

The biggest local Drupal event of the year is coming up fast, and it will have something for everyone. Four chock-full days of training, sessions and keynotes by nationally known trainers and speakers, famously fun social events, Drupal code sprints, and much more. Whether you are a Drupal expert, intermediate, a keen novice or just curious, we will have something for you.

As a bonus, this year we will have a special higher education component to the camp with a dedicated Higher Education Summit on Thursday 23 and related sessions throughout the camp. All are welcome to attend any of these, so spread the word.

Check the website often for training, keynote and event details, which will be announced soon. Note: you will need to register for these on the website once they are announced.

Submit that session topic you have been thinking about

Feel like sharing what you've learned about Drupal, business and other Web topics such as project management, favorite modules, development processes, design/UX, SEO, content strategy, back-end wizardry, front-end tips or whatever subject interests you? Submit your session idea by May 20 on the website.

Get the limited edition t-shirt

Again this year we're offering a Twin Cities Drupal Community t-shirt. When you choose the Community Sponsor option ($50) you'll get this exclusive t-shirt along with camp registration. Funds support our camp and local Drupal events throughout the year.

Don't miss the Friday night party spectacular, June 24

Join fellow Drupal Camp attendees on Friday from 6-10 PM at The House of Balls – a quirky and wonderful event space for the annual Advantage Labs Party.
http://houseofballs.com/events.html

There will be snacks, libations, games, and time for open lightning talks. Come to hang out, socialize or show off that cool site or module you just finished! Advantage Labs has a national rep for hosting some of the best parties of any Drupal Camp. This is a not-to-be-missed event.

Finally, help us spread the good news. Please tell colleagues, friends and anyone else who might be interested in the Twin Cities Drupal Camp.

Important dates

Early bird registration ends: May 20
Sessions announced: end of May
Higher Education Summit and Training Day: June 23 (all day)
Workshops and sessions: Friday and Saturday, June 24-26 (all day)
Camp party: Friday, June 26 (evening)
Community sprints: Sunday, June 27 (all day)

Mediacurrent: Friday 5: 5 Tips to Integrate 3rd Party APIs

Planet Drupal -

TGIF! We hope you've had a great week.

We're hot off the heels of DrupalCon but couldn't disappoint and skip this week. We give you, Episode 8! This Friday, Senior Drupal Developer David Younker joins us to discuss 5 Tips to Integrate 3rd Party APIs.

He provides some great tips for integrating 3rd party APIs and feeds in Drupal 7 sites. Watch the video below to learn more about Using Aggregator, Using Feeds, Custom Solutions, API Keys, and OAuth.

Using HTTP/2 Server Push with PHP

Cloudflare Blog -

Two weeks ago CloudFlare announced that it was supporting HTTP/2 Server Push for all our customers. By simply adding a Link header to an HTTP response specifying preload CloudFlare would automatically push items to web browsers that support Server Push.

To illustrate how easy this is I create a small PHP page that uses the PHP header function to insert appropriate Link headers to push images to the web browser via CloudFlare. The web page looks like this when loaded:

There are two images loaded from the same server both of which are pushed if the web browser supports Server Push. This is achieved by inserting two Link headers in the HTTP response. The response looks like:

HTTP/1.1 200 OK Server: nginx/1.9.15 Date: Fri, 13 May 2016 10:52:13 GMT Content-Type: text/html Transfer-Encoding: chunked Connection: keep-alive Link: </images/drucken.jpg>; rel=preload; as=image Link: </images/empire.jpg>; rel=preload; as=image

At the bottom are the two Link headers corresponding to the two images on the page with the rel=preload directive as specified in W3C preload draft.

The complete code can be found in this gist but the core of the code looks like this:

<?php function pushImage($uri) { header("Link: <{$uri}>; rel=preload; as=image", false); return <<<HTML <img src="{$uri}"> HTML; } $image1 = pushImage("/images/drucken.jpg"); $image2 = pushImage("/images/empire.jpg"); ?> <html> <head><title>PHP Server Push</title></head> <body> <h1>PHP Server Push</h1> <?php echo ccbysa($image1, "https://bit.ly/1Wu5bYx", "https://www.flickr.com/photos/hiperactivo/", "Javier Candeira"); echo ccbynd($image2, "https://bit.ly/24PHue3", "https://www.flickr.com/photos/bobsfever/", "Robert McGoldrick"); ?> </body> </html>

Since you have to call the PHP header function before any output (such as HTML) has occurred the code makes two calls to a helper function called pushImage first. pushImage adds the appropriate Link header and returns the HTML needed to insert the actual image in the page.

Later the variables $image1 and $image2 (which contain the HTML needed to display the images) is inserted use two other helper functions (ccbysa and ccbynd) that add captions. Those two helper functions don't play any part in the Server Push, they just ensure that the HTML is placed correctly on the page with a caption.

Notice that the Link header is added as follows:

header("Link: <{$uri}>; rel=preload; as=image", false);

The false second parameter tells header to not override an existing Link header. With that option specified multiple Link headers can be added. Without it the last call to pushImage would win.

Effect of Server Push

To understand the effect of Server Push on the example I used Google Chrome Canary and loaded the page twice (once without the Link headers so that no push would occur and once with).

Here's the simple waterfall over HTTP/2 with no Server Push:

The page load time was 651ms. You can see the initial page HTML load in 175ms followed by the two images.

How here's the waterfall with HTTP/2 Server Push:

The page load time was 251ms (the HTML loaded in 168ms) and Chrome is showing that the two images were pushed (see the Initiator column).

It's not 100% obvious what happened there but digging into chrome://net-internals/ it's possible to see a detailed HTTP/2 timeline. I've edited out a few details of the protocol (such as the window size changes) to focus in on the requests and responses.

The t value gives the tick time (1ms per tick).

t=910212 [st= 1] HTTP2_SESSION_SEND_HEADERS --> exclusive = true --> fin = true --> has_priority = true --> :method: GET :scheme: https :path: /index.php pragma: no-cache cache-control: no-cache upgrade-insecure-requests: 1 accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 accept-encoding: gzip, deflate, sdch, br accept-language: en-US,en;q=0.8 cookie: [52 bytes were stripped] --> parent_stream_id = 0 --> priority = 0 --> stream_id = 1 [...] t=910404 [st= 193] HTTP2_SESSION_RECV_HEADERS --> fin = false --> :status: 200 date: Fri, 13 May 2016 11:28:16 GMT content-type: text/html content-encoding: gzip --> stream_id = 1 t=910405 [st= 194] HTTP2_SESSION_RECV_PUSH_PROMISE --> :method: GET :path: /images/drucken.jpg :scheme: https accept-encoding: gzip, deflate, sdch, br --> id = 1 --> promised_stream_id = 2 t=910405 [st= 194] HTTP2_SESSION_RECV_PUSH_PROMISE --> :method: GET :path: /images/empire.jpg :scheme: https accept-encoding: gzip, deflate, sdch, br --> id = 1 --> promised_stream_id = 4 t=910405 [st= 194] HTTP2_SESSION_RECV_DATA --> fin = false --> size = 298 --> stream_id = 1 t=910405 [st= 194] HTTP2_SESSION_RECV_DATA --> fin = true --> size = 0 --> stream_id = 1 t=910409 [st= 198] HTTP2_SESSION_RECV_HEADERS --> fin = false --> :status: 200 date: Fri, 13 May 2016 11:28:16 GMT content-type: image/jpeg content-length: 49852 set-cookie: [124 bytes were stripped] etag: "5735aac0-12f99" last-modified: Fri, 13 May 2016 10:21:52 GMT cf-cache-status: HIT vary: Accept-Encoding expires: Fri, 13 May 2016 15:28:16 GMT cache-control: public, max-age=14400 accept-ranges: bytes server: cloudflare-nginx --> stream_id = 2 t=910409 [st= 198] HTTP2_SESSION_RECV_DATA --> fin = false --> size = 987 --> stream_id = 2 t=910409 [st= 198] HTTP2_SESSION_RECV_DATA --> fin = false --> size = 1369 --> stream_id = 2 t=910410 [st= 199] HTTP2_SESSION_RECV_DATA --> fin = false --> size = 1369 --> stream_id = 2

The web browser sends an HTTP2_SESSION_SEND_HEADERS request asking for the web page (that's the first item above) and subsequently receives the response headers and page followed immediately by two push promises for the two images. It then immediately starts receiving image data (see the stream_id 2).

CloudFlare's web server is pushing the image contents before the browser asked for them. When CloudFlare pushes an item specified in a Link header the header itself is stripped (to prevent the browser from re-requesting the resource).

The Future Starts Here

We released HTTP/2 Server Push support to help kick start innovative use of this critical feature of HTTP/2. We would love people to start experimenting with it to see how much of a speed improvement is possible with their specific websites.

As can be seen from this blog post making use of Server Push in a web application is easy: just insert Link headers with the appropriate format. Server Push will be particularly fast if the items being pushed are also stored in CloudFlare's cache (as was the case with the examples above).

Please let us know how you use Server Push. We're particularly interested in experiences with pushing different types of resources (images vs. styles vs. scripts) and working out the optimal number of items to push (we currently allow up to 50 resources per page).

Red Route: There's more than one way to Drupalise a cat

Planet Drupal -

One of the components in the design is something I'm calling tiles - as always, naming things is one of the hardest parts.

The component includes an image with a transparent overlay, showing the title. On hover and focus, some extra information becomes visible. For instance, for a gallery, the address will be shown, and for an exhibition, the artists and tags will be shown. Here's a Codepen which gives you an idea:

See the Pen Tiles... by malcomio (@malcomio) on CodePen.

Different versions of this component are used in quite a few places. On an exhibition page, it applies to a teaser view of the gallery linked via a node reference field. In various views, it applies to the views fields. To get the markup right for the views fields, I needed to create a custom template. But I didn't want to create the same template for each view that needed to use the tile pattern - that would be a nightmare to maintain.

Having read about Twig template extends I was tempted to try them for this use case - it seems like an interesting new feature, so why not try it out?

I created an initial template called views-view-fields--tiles.html.twig, and then set the view template to use it. For instance, I wanted to apply this markup for the exhibitions_new view, so I created a template called views-view-fields--exhibitions-new.html.twig, which contained just one line:

{% extends "themes/gall/templates/views/views-view-fields--tiles.html.twig" %}

It seemed to work OK, but didn't seem like the right approach. For one thing, it would leave me with a theme cluttered with loads of one-line templates, which would get pretty annoying pretty quickly. For another, it felt like a gratuitous use of a solution - a hammer looking for some nails to bash.

The solution I went with in the end was much more familiar from previous Drupal versions, although it uses the new hook_theme_suggestions_HOOK_alter hook:

/** * Implements hook_theme_suggestions_HOOK_alter(). */ function gall_theme_suggestions_views_view_fields_alter(array &$suggestions, array $variables) { // Set up views to use the tiles template. $tiles_views = array( 'exhibitions', 'exhibitions_a_z', 'exhibitions_new', 'exhibitions_this_gallery', 'galleries_a_z', 'galleries_new', ); $view_id = $variables['view']->id(); if (in_array($view_id, $tiles_views)) { $suggestions[] = 'views_view_fields__tiles'; } }

For someone familiar with previous versions of Drupal, it's another thing which is similar but different. More to learn, and some things to unlearn, but we're not starting from scratch, and we can have more options in our toolkit.

Tags:  Drupal Drupal 8 The Gallery Guide All tags

Open sourcing our NGINX HTTP/2 + SPDY code

Cloudflare Blog -

In December, we released HTTP/2 support for all customers and on April 28 we released HTTP/2 Server Push support as well.

The release of HTTP/2 by CloudFlare had a huge impact on the number of sites supporting and using the protocol. Today, 50% of sites that use HTTP/2 are served via CloudFlare.

CC BY 2.0 image by JD Hancock

When we released HTTP/2 support we decided not to deprecate SPDY immediately because it was still in widespread use and we promised to open source our modifications to NGINX as it was not possible to support both SPDY and HTTP/2 together with the standard release of NGINX.

We've extracted our changes and they are available as a patch here. This patch should build cleanly against NGINX 1.9.7.

The patch means that NGINX can be built with both --with-http_v2_module and --with-http_spdy_module. And it will accept both the spdy and http2 keywords to the listen directive.

To configure both HTTP/2 and SPDY in NGINX you'll need to run:

./configure --with-http_spdy_module --with-http_v2_module --with-http_ssl_module

Note that you need SSL support for both SPDY and HTTP/2.

Then it will be possible to configure an NGINX server to support both HTTP/2 and SPDY on the same port as follows:

server { listen 443 ssl spdy http2; server_name www.example.com; ssl_certificate cert.pem; ssl_certificate_key cert.key; location / { root html; index index.html index.htm; } }

Our patch uses ALPN and NPN to advertise the availability of the two protocols. To test that the two protocols are being advertised you can use the OpenSSL client as follows (sending an empty ALPN/NPN extension in the ClientHello causes the server to return a list of available protocols).

openssl s_client -connect www.example.com:443 -nextprotoneg '' CONNECTED(00000003) Protocols advertised by server: h2, spdy/3.1, http/1.1

Many other tools for testing and debugging HTTP/2 connections can be found here.

The patch puts HTTP/2 before SPDY/3.1 and will prefer HTTP/2 over SPDY/3.1. If a web browser offers both, HTTP/2 will be preferred and used for the connection.

We continue to support SPDY and HTTP/2 across all CloudFlare sites and will keep an eye on the percentage of connections that use SPDY before making a decision on its eventual deprecation.

Dries Buytaert: State of Drupal presentation (May 2016)

Planet Drupal -

DrupalCon New Orleans comes at an important time in the history of Drupal. Now that Drupal 8 has launched, we have a lot of work to do to accelerate Drupal 8's adoption as well as plan what is next.

In my keynote presentation, I shared my thoughts on where we should focus our efforts in order for Drupal to continue its path to become the leading platform for assembling the world's best digital experiences.

Based on recent survey data, I proposed key initiatives for Drupal, as well as shared my vision for building cross-channel customer experiences that span various devices, including conversational technologies like Amazon Echo.

You can watch a recording of my keynote (starting at 3:43) or download a copy of my slides (162 MB).

Take a look, and as always feel free to leave your opinions in the comments!

ThinkDrop Consulting: Onward with OpenDevShop Inc

Planet Drupal -

Today I am awaking to the last "official" day of DrupalCon New Orleans with a huge new wind at my back.

It felt like an appropriate time to post what is likely my last blog post as ThinkDrop Consulting LLC.

My partners and I have been in a whirlwind tour of the convention, spreading the news of our product, and our new company: OpenDevShop Inc. In order to focus entirely on development and hosting tools, I am closing up ThinkDrop Consulting.

We've been building the OpenDevShop platform since late 2011 for my clients and myself, and in January of this year, we incorporated.

Our mission: to make hosting, testing, and scaling websites as easy as possible; to make infrastructure management as easy to deal with as content; and to foster a community around these types of tools.

OpenDevShop Inc is a member of the newly formed Aegir Coop. The cooperative is a group of companies and individuals who have organized together to support and develop the Aegir ecosystem.

We are working hard to not only grow this business but to grow the Aegir community, both users but especially contributors. We have a lot of work to do if Aegir is going to live up to modern expectations of infrastructure management and deployment tools.

Today we have two Birds of a Feather sessions back to back at DrupalCon: one for OpenDevShop and one for the Aegir Coop.

The energy behind the Drupal Community has never been higher. Let's follow in their footsteps and bring together everyone that cares about better Infrastructure Management and DevOps.

Come join the Aegir & OpenDevShop communities in room 292 (AshDay) from 1pm - 3pm, and maybe later if they let us stay.

Please check out our new company website, opendevshop.com.

We will see you around the community!

Tags: devshopPlanet Drupal

Pages

Subscribe to Cruiskeen Consulting LLC aggregator