Lullabot

Sharing Breakpoints Between Drupal 8 and Sass

Among the many great new features in Drupal 8 is the Breakpoint module. This module allows you to define sets of media queries that can be used in a Drupal site. The most notable place that Drupal core uses breakpoints is the Responsive Image module.

To add and configure breakpoints, you create a file named *theme-name*.breakpoints.yml in the root directory of your theme, replacing *theme-name* with the theme’s machine name. Modules can also have *module-name*.breakpoints.yml configuration files at their root. In this file you define each breakpoint by these properties:

  • label - the human readable name of the breakpoint
  • mediaQuery - a valid @media query
  • weight - where the breakpoint is ordered in relation to other breakpoints: the breakpoint targeting the smallest viewport size should have a smaller weight, and larger breakpoints should have larger weight values
  • multiplier - the ratio between the physical pixel size of the active device and the device-independent pixel size

Exposing all this information to Drupal from a single file is great. But having this file creates a problem. The breakpoints defined here are not exposed to the Sass code used to style our site.

A solution

One best practice in software development is avoiding repetition whenever possible, often called keeping things DRY (Don’t Repeat Yourself). To apply the DRY method to the breakpoints.yml file and Sass, I wrote drupal-sass-breakpoints. This is an Eyeglass module that allows importing breakpoints from our theme’s breakpoints.yml into our Sass code.

In this article we are going to setup Drupal Sass Breakpoint in a minimal Drupal 8 theme. If you would like an introduction to creating a Drupal 8 theme, I recommend going through John Hannah’s article on Drupal 8 Theming Fundamentals. For this article's example (neelix), we are going to start with the following files:

├── scss │ └── style.scss ├── css ├── neelix.libraries.yml ├── neelix.info.yml ├── neelix.breakpoints.yml

This gives us a directory for our Sass, a directory for the compiled CSS, a file to declare a library for our CSS, an *.info.yml file, and our breakpoints.yml file. Inside the neelix.breakpoints.yml file add the following:

neelix.small: label: small mediaQuery: '' weight: 0 multipliers: - 1x neelix.medium: label: medium mediaQuery: 'screen and (min-width: 560px)' weight: 1 multipliers: - 1x neelix.wide: label: wide mediaQuery: 'screen and (min-width: 600px)' weight: 2 multipliers: - 1x neelix.xwide: label: xwide mediaQuery: 'screen and (min-width: 860px)' weight: 2 multipliers: - 1x

Now that we have a starting point for the new theme and some breakpoints in place, we need to install drupal-sass-breakpoints. For the example here, we are going to use Grunt to compile Sass. But It is important to mention that drupal-sass-breakpoints can work with Gulp or any other task runner that supports Eyeglass modules. To get started with Grunt, we first need to set up dependencies. This can be done by defining the dependencies in a file named package.json in the root directory of the neelix theme:

{ "devDependencies": { "breakpoint-sass": "^2.6.1", "drupal-sass-breakpoints": "^1.0.1", "eyeglass": ^"0.7.1", "grunt": "^0.4.5", "grunt-contrib-watch": "^0.6.1", "grunt-sass": "^1.0.0" } }

Then run npm install this from the command line while in the theme’s root directory.

This installs all the necessary dependencies we need, which were defined in package.json. Next, we need to define Grunt tasks. Do so by adding the following in a Gruntfile.js file in the root directory of the theme:

'use strict'; var eyeglass = require("eyeglass"); module.exports = function(grunt) { grunt.initConfig({ watch: { sass: { files: ['scss/*.{scss,sass}', 'Gruntfile.js', 'neelix.breakpoints.yml'], tasks: ['sass:dist'] }, }, sass: { options: require("eyeglass").decorate({ outputStyle: 'expanded' }), dist: { files: { 'css/style.css': 'scss/style.scss' } } } }); grunt.registerTask('default', ['sass:dist', 'watch']); grunt.loadNpmTasks('grunt-sass'); grunt.loadNpmTasks('grunt-contrib-watch'); };

This creates all that we need to compile Sass with Eyeglass. Now we can set up a test case to our scss/style.scss file by adding the following:

@import "drupal-sass-breakpoints"; body { @include media('medium') { content: "Styles that apply to our 'medium' breakpoint"; } }

In the above example we are using the media() mixin. This is a mixin added by drupal-sass-breakpoints. The argument we pass to it is the label for the media query we are targeting ( medium). Now run this from the command line in the root directory of our theme to compile Sass:

grunt

Now check out the compiled results inside css/style.css:

@media (min-width: 560px) { body { content: "Styles that apply to our 'medium' breakpoint"; } }

That is it! We now have Sass importing the media queries from our theme’s breakpoints.yml file.

What about srcset and sizes?

If you find you're overwhelmed by the number of breakpoints in your theme, remember that there are many instances where you only need to use srcset for responsive image styles. The srcset attribute is particulary useful when used in conjunction with the sizes attribute. I recommend this article on CSS-Tricks which outlines the scenarios which srcset and sizes are best for. In these cases, it may not be necessary to define all of our breakpoints in the .breakpoints.yml file.

Feedback

Do have any feature requests or suggestions for improving drupal-sass-breakpoints? Feel free to contribute on GitHub.

You can find a fully working example of this article here. Happy styling!

My Four Kickstarter Mistakes Can Improve Your Software Projects

So I wrote a children’s book. To get it printed and published, I launched a Kickstarter that was successful thanks to the many people who believed in the idea and committed with their wallets.

But the campaign succeeded in spite of myself. I made a lot of mistakes. There are many things I would do differently if I were to launch it again, and will do differently for the next campaign. As I reflected on the mistakes and lessons learned, however, there was commonality with general best practices for implementing a project, and in particular, software projects.

So here are my mistakes (at least, the ones I know about) with the resulting lessons learned.

1. Rushing the Launch

Many of the mistakes that follow sprouted from impatience. That’s why this is first. If you don’t make this mistake, you’ll save yourself a lot of headaches.

There is such a thing as analysis paralysis, or fear of launching because you are scared to fail. But that was not my problem. My problem was pure impatience. I had worked on the book for months, on both editing and requisitioning sample illustrations. I had spent almost another month crafting the kickstarter page and putting together the video. By the time the video was done, I just wanted to get it out there.

However, I would have benefited from sitting down and doing more intentional research, and pulling in some advice from people I trust. I could have tested the video to see if it spurred people to action. I could have looked for the ideal time to launch a children’s book Kickstarter, and then actually waited. I could have spent some time and effort collecting a list of media contacts and prepared a message to send them on launch day. I could have set up a social media campaign to capture email addresses and test initial copy. I could have...done so many other things.

Instead of having a methodical plan to follow, I just started throwing things at the wall to see what stuck, desperate to try anything. It caused some unnecessary stress and worry, especially when the momentum started to slow down after the first few days of launch. I knew I hadn’t done everything I could to ensure a better chance at success, and at that point, there was less and less that I could do about it.

Lesson

Don’t be haphazard with your launch. If you feel sick of working on something and just want to get it out...stop. Think. Sleep on it. Your whims and emotional health are not the most important factors in a launch strategy. Take time to plan the launch and the weeks following the launch. Be intentional about establishing a calendar that makes sense, and do your best stick to it.

2. Underestimating Time and Cost Involved

The consequences of this mistake can be severe. If the funding of your Kickstarter doesn’t cover all of your costs, the remainder comes directly from your own pocket. If you can’t afford the overage, you risk breaking your promises to your backers. Or a potential lawsuit.

It is critical that you count the cost and attempt to measure possible risk. An unfunded, failed campaign is disappointing. Possibly heartbreaking. Worse, though, is a campaign that leaves you burned out, destitute, with the residue of broken promises polluting your wake.

Some things I underestimated or ignored:

  • The time required to finish illustration. I originally wanted to ship books to backers by April 2015, but ended up being over 3 months late. This is no fault of the illustrator, but rather myself forgetting about my own nitpicking tendencies, and the vast amount of back and forth communication that it required.
  • The time required to manage the campaign, both during and after. Posting updates, reaching out to blogs, the packing and shipping of the products (so many trips to the post office. So, so many.) Since I wasn’t paying myself anything for labor, this didn’t have a direct impact on costs, but it did have a negative impact on timelines.
  • The price of international shipping. This is always more expensive than you think it is, and it varies heavily from carrier to carrier, country to country. Total package weight and size matter much more, and a slight increase can mean a big bump in cost. I literally lost money on every single international shipment, even the ones to Canada. If you think you are charging enough for International shipping, you probably aren’t.
  • The cost of shipping materials. It turns out that a 9.5x9.5 book needs larger envelopes than a book that is just one inch narrower. Packaging a print so it doesn’t bend during shipment requires some extra materials. And on and on. The small things add up.
  • The cost of marketing. I found some niche newsletters to advertise in. Did I plan and budget for these ads? Nope.

I ended up spending a decent amount of my own money to cover the extra costs. Since it was a passion project for me, and additional amount needed was not astronomical, it wasn’t that big of a deal...but it could have been a disaster.

Lesson

Measure twice, cut once. Estimation is hard, and if you are at the mercy of other companies whose prices can change, be sure you under-promise so you set yourself up to over-deliver. Make sure you are accounting for as much as possible, and then add some more to your estimation to take into account possible risks and other uncertainties.

3. Rewards Didn’t Align With What People Actually Wanted

I thought I knew what potential backers would want. I myself was a connoisseur of children’s books, and so I had a lofty view of my own opinion. And while my desires intersected somewhat with what people wanted, I missed the mark badly on many rewards. This was either because the reward itself was undesirable, the price for the reward was too much, or a combination of the two.

For example, an original sketch from the illustrator was a reward that some people obviously wanted, but at only three takers, the price point probably kept many from claiming it. Instead of setting it at a round number that “felt” right, I should have taken the time to see if the numbers worked for a lower price point.

Likewise, I didn’t really highlight signed books as a potential benefit. This was me being oblivious and living inside my head too much. I personally don’t value signed books that much, even from my favorite authors, and so I didn’t expect others to care about my own sloppy signature. But that was stupid. Nearly every other book project on Kickstarter offers signed copies, and I should have taken the obvious hint.

While I did take time to see what rewards other successful campaigns had offered, I didn’t open myself up to learning everything I could. I had already chiseled some ideas in stone, as if they were footnotes to the Ten Commandments.

Lesson

Is your project actually offering what people want? Does your marketing message accurately reflect the benefits that your target users are looking for? Remember, most of the time, you are not your target audience. Do not assume you have the authoritative opinion on your product or service. Get some empathy, talk to people who aren’t you, and don’t ignore what other successful projects (in the same domain space) might have in common. Perhaps do some organized user research. Be prepared to change (or kill) your darlings.

4. Not Properly Determining a Minimum Viable Product

I aimed too high. If not for generous friends, family, and coworkers, I would have turned out like Icarus, with melted wings pushing me down toward Poseidon's ambivalent embrace. Or like something much less melodramatic, but equally disastrous.

If I was going to publish a book, I was going to do it right. The best materials, the best hardcover binding, book dimensions usually reserved for a ping pong table. Oh, and a dust jacket, of course. At one point, I’m sure I even entertained the idea of gold foil on the cover with blinking lights, or some such nonsense. Thankfully, the realities of book printing held me in check.

In my mind, this premium, sewn-bound, 9.5x9.5 hardcover book was the minimum product. I assumed that people would be more likely to back the campaign if they were getting something that could be showcased. That might have been true for some. For the most part, however, I projected my own excitement and gilded assumptions, and it hampered the campaign.

What should have been my minimum viable product? Something much cheaper to print, for starters. An 8x8 softcover book would have cut the print cost by at least 35%. This would have allowed me to set my campaign goal much lower, and have a much lower-priced reward tier where a backer would still get a physical copy.

I could have still offered the premium upgrades, but as stretch goals. If the campaign reached a certain amount, the softcover would be upgraded to a hardcover. Another could have added the dust jackets. I suspect I would have had a higher total, with more backers, and would have still been able to deliver a premium hardcover at the end.

But even if not, more people would have had my book in their hands, even if it was a more mundane softcover version. Instead, I outpriced my market a bit, and discouraged impulse backers.

Lesson

Make sure your Minimum Viable Product is actually your Minimum Viable Product. Are the features you think of as “minimal” actually required for people to extract value from your product or service? Be brutally honest over what are “nice-to-haves.” Otherwise, you might spend time and money chasing the wrong things, limiting both future growth and agility. Extra features and luxuries can be added later, once there is more demand.

Conclusion

Successful projects are hard to bring about. They don’t happen by accident, though you may get more luck than you deserve, like I did. Any one of the above mistakes has the potential to wreck a project, to leave the ruins scattered into the well-populated landfill of failed intentions. I hope some of my experiences can help you avoid some of these mistakes in the future.

What you made a project management mistake that has resulted in a valuable lesson? Let us know!

Rebuilding POP in D8

Outside my Drupal and Lullabot life, I help my girlfriend Nicole run a non-profit called Pinball Outreach Project, POP for short. POP brings pinball to kids by taking games to children’s hospitals, as well as working with organizations like Children’s Cancer Association, Girls Make Games, and Big Brothers Big Sisters to bring pinball to their events or host kids at our pinball location here in Portland.

The POP website was built in Wordpress and has served us well for three years, but as we have become more active and our needs have expanded, it has started to show its age. There are several parts of the site that are difficult to update because they are hardcoded in templates, it relies on some paid components to keep running, and the theme hides a lot of basic functionality that it would be nice to have access to.  On the plus side, it is a simple clean design that is fully responsive, so navigating the site is pretty easy.

We wanted to build a new site that kept the design elements we liked, but expanded the functionality to make the site easier to update and more flexible to allow for future growth and needs. Given my expertise, Drupal seemed like the obvious choice, and given where we're at in the release cycle I thought, "Why not do it in Drupal 8?" It would give me some real hands-on experience, and hopefully we'll end up with a modern tool that can help us grow into the future.

Thus began our odyssey, a new website for a small non-profit in Drupal 8. In the coming weeks I will outline this process from the standpoint of a Drupal 7 expert experiencing a real site build with D8 for the first time. While it is true that I have more D8 experience than many due to my role as lead of the Configuration Management Initiative, that experience is a couple years old and almost entirely involved backend code and architecture. The site building and theming changes are completely new to me, and in many cases I don't know what I don't know. In this way, I feel I am like many of you reading this who are also about to embark on this journey. Let’s discover the new stuff in Drupal 8 together, and we can all learn something along the way.


About the new site

Before we get into the build, let’s look into what we're building. The POP site has several high priority functions that we need to address:

  • Provide information about the organization and its mission.
  • Publicize upcoming events, as well as wrapup information and photos about events that have already happened.
  • Provide news about other POP happenings.
  • Provide information related to POP HQ, our location here in Portland (hours, league, party rental, etc.)
  • Allow users to get information about our current needs and donate.
  • Enable users to interact with us through social media and our newsletter.

For better or worse, we don't have a ton of design or UX resources at hand, so our goal is to shoot for a simple information architecture and wrap a super clean theme around it. We're going to start with some very basic wireframes, prototype the site, then head into the design/theming phase. Now I know what you're thinking, if a client came to me with this plan, I would scoff and laugh too. Nevertheless, remember that most clients are also extremely particular about their design, UX, and branding. From our perspective, we are far more interested in getting the functionality we need in place and the information we have up front to our site users. Beyond that, a theme that is reasonably equivalent to what we have now is perfectly fine. We are going in knowing exactly what our limitations and expertise is, and being willing to work within those constraints.

The one thing we want from a new design, that we don't have now, is to put our imagery much more front and center. Pinball is a hobby that has some fantastic visuals, and our events pair that up with kids having fun which makes it all the more appealing. We should find a way to put that in front of people as much as possible.

Beyond that, I picture a pretty straightforward site with about a half dozen of your usual content types (page, article, event, promo block, maybe images and galleries.) The site will be simple enough that I was figuring we could build it with Context to manage block placement and an assortment of Views. Add on a few custom blocks and I figured we would be most of the way there. So let’s see how that played out from day one.


Getting started

Getting Drupal 8 installed was not much different than it was in D7, so I won't spend a ton of time dwelling on that (although it was really nice that I could let the installer create my new database for me, thanks Angie!) If you'd like to read more about installation, I recommend Karen Stevenson's recent article on using Composer to install D8.

The first thing I wanted to check once I got Drupal installed was the state of a few contributed modules, and first on that list was Context, as I was planning on having it serve as the main organizational paradigm for the site. I was pretty surprised to see that a D8 port of Context had not even been started, much less in any usable state. I mentioned this on IRC, and someone said that perhaps the core block enhancements in D8 might meet my needs. Say what? Blocks are actually useful now? Hard to believe, but it is true! There are two main enhancements to blocks in D8 that make them super-powered over D7.

Instancing

Let’s say you have a block, and you want to place it in the sidebar in some contexts, and in the triptych in others. A huge problem in past versions of Drupal is that blocks can only be placed in one region per theme. If you want it in two different regions based on different visibility rules, you have to have two blocks. This limitation alone was a huge driver towards Context for many sites. In D8 blocks can be placed as many times as needed! This addition is a huge step up for blocks.

Drupal blocks being displayed in multiple places on a single page. Blocks are entities

Blocks are full-blown entities in D8. This means they have a ton of functionality they never had before.

Just like with content types, blocks also now have custom types. This means you can set up unique blocks with their own sets of fields, and use them for their own specific purposes. We can have the promo block type described above, but then also have one we use like a nodequeue, and then one that is just a big HTML block which we can use to embed something like a Mailchimp signup form or other non-fielded content. 

Drupal 8 block type administration screen.

Blocks also now have fields. On the surface this can sound like a case of over-engineering something that is supposed to be simple, but this actually has enormous utility. For instance, one of the things we often find ourselves doing on modern sites is creating a "Promo" content type which is not much more than a title, text, a photo and a link promoting another piece of content. 

We can now do that with core blocks by creating a Promo block type, and creating placement rules around where it appears. Since we also have instancing, we could have it in the sidebar on some content, in the content area on other content, and in the footer on the home page.

Drupal blocks being displayed in multiple places on a single page.

Fields on blocks can also allow us to replicate some simple nodequeue-like behaviors. Create a block with a multi-instance entity reference field and a title. You can now choose a set of entities for the block to display, and place that block using the normal core block placement rules. This could be expanded on in a lot of different ways, the combination of block placement with fields provides a ton of flexibility which we never had before.

Finally, having fields means that you can have blocks with efficiently “chunked” data, rather than just a big text field you dump HTML into. This has enormous implications for accessibility and having a more robust device-independent layouts.

Block Visibility Groups

While it’s not a part of core, the Block Visibility Groups module can help expand and organize the core blocks functionality even further. Block Visibilty Groups adds the concept of a group of blocks, which basically act is if they were one block. Say you need three blocks in the sidebar, but all of them appear at the same path, and only to authenticated users. You can add them all to the visibility group, and then apply the rules to the group. Down the road, if those rules change, you can change them in one place. It also organizes the blocks page better, limiting the number of individual entries you have to deal with. 

Administration screen for the Block Visibility Group module Conclusion

Fields and instancing, when combined with the existing block visibility rules and contrib modules like Block Visibility Groups, make blocks in D8 a killer feature. Based on my pretty basic needs, I no longer need to worry about whether or not Context is ported, because I'm pretty sure I can do everything I want with core. On top of all that, all my config for these blocks is now deployable through the Configuration Management System, which will be the topic of our next article. 

Special thanks go out to larowlan, timplunkett, and eclipsegc for pushing through the major patches that made blocks become super useful in Drupal 8!

A Front-end JavaScript Framework in Drupal Core?

Matt & Mike talk with Acquia's Preston So, Chapter Three's Drew Bolles, and Lullabot's own Sally Young on the possibility of a front-end JavaScript framework in Drupal core, and what that would look like.

The Genesis Of Lullabot

On January 1st, 2006, Matt Westgate and I announced our new company by launching a website at Lullabot.com. That was 10 years ago.

Matt and I got to know each other very slowly over the course of 2005. I was building my first Drupal site and we met through Drupal.org. Actually, my very first post on Drupal.org was an interaction with Matt. On February 14th of that year, I'd found a bug in Matt's, then-popular eCommerce module and I'd posted an issue trying to track down the error I was getting. Matt's response, our first interaction, posted one week later, was "This has been fixed. Thanks."

My first Drupal project used many of Matt's Drupal modules. It was an overwhelming and awful project. My wife (a designer) and I had vastly underestimated and underbid the complex web site which would be my first experience with Drupal. At the time, the Drupal community had big dreams, but the project was still in its early years. I found that much of the functionality that I needed for my project wasn't really mature yet. Most of Drupal's documentation existed only as comments in the code itself. There were no books about Drupal. There were no podcasts. There were no conferences or workshops. There were no companies that I could go to for practical help or advice. I thought my first Drupal project would take me 2 or 3 months. It ended up taking almost a year.

I posted desperately in the Drupal IRC channels and on the Drupal.org forums. Many of my questions were very basic. These questions posted in the #drupal IRC were simply met with links to PHP.net or MySQL.org. I was being told to go "read the fucking manual." It was painful. I felt ashamed. I felt unqualified, lost, and hopeless.

I decided to reach out to the friendly guy whose modules were being used all throughout my project. He had committed a bunch of my code to his modules and he always seemed upbeat and grateful about it. Even in that first interaction, he was upbeat: "This has been fixed." as well as thankful: "Thanks." He'd also amicably answered a few of my forum questions and he obviously knew his stuff. I sent Matt an IRC message and offered to pay him to get on the phone with me and answer my Drupal questions. He was a bit taken aback. In 2005, few of its core developers were getting paid for their Drupal work. I think we settled on $40/hr and sometime early in the summer, I spoke to Matt on the phone for the first time. He lived in Ames, Iowa. I lived near Providence, RI. We became internet buddies, having long conversations over AOL Instant Messenger about Drupal and life. He was friendly and knowledgeable. He was also curious and inquisitive with a positive attitude. There weren't many things that seemed to daunt him. My state of hopelessness moved to one of hope and gratitude.

Prior to 2005, my career success was in the music industry. It was amazing to see so many talented developers working together and contributing such amazing software… for free! In the same way that I was inspired by other musicians' music, I was inspired by Drupal's developers' code. Although I was overwhelmed, I was also inspired. I couldn't understand why there weren't crowds of people cheering when they released a new module. I was so thankful to Matt for his help and generosity. I told him, "You've got an amazing talent. I'm going to make this up to you one day. We're going to do something amazing together. I promise you." I imagined Matt rolling his eyes, but I was intent to follow through on my promise.

In an effort to finish this difficult project, I'd completely immersed myself in Drupal and the Drupal community with a tremendous amount of gratitude and hope. I contributed many Drupal modules over the next few years. My overwhelming first Drupal project began to wind down around October and I started telling Matt that we should start a company together. Drupal was so powerful, but no one was out there acting as an advocate, providing practical information or consulting services to help companies harness its power.

Around the same time, friends at Adaptive Path in San Francisco offered me a project for an up and coming film production company called Participant Productions (now Participant Media). They were about to release a new film called "An Inconvenient Truth" and they wanted to create a community action website where they could inspire visitors to take action on various social causes. Adaptive Path thought Drupal might be a good match and they thought of me. I said I'd do it, but only if we could get Matt to help.

I first met Matt Westgate in-person at the San Francisco airport. It was awkward. We'd been talking over the web and on the phone for almost 6 months and it was really weird to be together, in person. Also, he had flown to San Francisco in November with no clothing heavier than a t-shirt. We stopped at Old Navy on Market Street and bought Matt a sweatshirt. We sat in the Adaptive Path offices and coded up most of the site in about a week. We had flow. Adaptive Path was one of the most prestigious web consulting firms at the time and several of their founders, upon seeing our accomplishment, said we really should start a company doing Drupal. That was all the encouragement we needed.

Matt finally decided to leave the security of his university job some time in December. We spent a lot of time going back and forth trying to name our new company. With Matt's background as a Zen meditator and my background as a musician, we liked the combination of the calm music of "lullaby" with "robot", because we were doing technical work and also, robots are just cool. It also combined the organic and inorganic in a nice way, reminding us that it's important to remember the squishy stuff even while we're working with ones and zeroes.

In our first post on Lullabot.com, I wrote: "It is often said that open source software is 'made by developers for developers'. We hope to break down some of those walls and provide an entry for less technical users. We hope to break through the cacophony, and provide clear and understandable information and guidance."

We created Lullabot because we didn't want others to have the frustrating first experience that I had with Drupal. We wanted to make Drupal more accessible to a wider group of web developers. Over our first year as a company, we started the first Drupal podcast, we taught the first Drupal workshops, and Matt co-wrote the first Drupal book, Pro Drupal Development, which became required reading for aspiring Drupal developers. We also built some of the first household-name Drupal sites and helped to move Drupal toward the tipping point of acceptance and adoption.

While we started Lullabot with a lot of energy, hope, and competitive spirit, it's pretty safe to say that the company has been more successful than either of us had hoped in our wildest dreams. We've been blessed to work on some truly amazing project with some amazing clients. We've hired and worked with the best talent in the business. We've tried to infuse Lullabot with the sense of gratitude, hope, and excitement that we had back in 2006. We've built a stellar reputation for doing superlative work and we're constantly astounded by our opportunities and success.

In part 2, I'll talk about some of those successes and growing the company over the past 10 years.

Lullabot Celebrates 10 Years Today

On January 1st, 2006, Matt Westgate and I announced our new company by launching a website at Lullabot.com. That was 10 years ago today.

Over the past 10 years, we've worked with a long list of amazing clients on a long list of amazing projects. We've run workshops, and we've recorded podcasts and training videos. We've run conferences. We've written books. We've written and contributed a lot of Drupal code, modules, and themes. We printed up a ton of Lullabot t-shirts and handed out temporary tattoos (which people seem to promptly affix to their children). We've had legendary parties. We've met lots of great people. We've trained thousands of Drupal developers. We launched Drupalize.Me and grew it into its own company. We've delved deeply into decoupled Drupal sites and built expertise in React, Node.js, and mobile and connected-television development. We received the highest rating on Clutch's worldwide list of development companies. We created a video delivery platform called Videola (which kind of flopped… but we learned a lot!). We created a cool business-card app called Shoot. More recently, we've launched a new product for website testing called Tugboat.

Most importantly: we've built an amazing team of talented developers, designers, project managers, management, and admin staff. We've grown to over 60 people and we still feel like we've got much of the culture, participation, and collaborative spirit that marked our early years. Our team is dedicated, happy, hardworking, and they encourage each other to grow and become the best of themselves. I am so grateful to these people for helping us to build Lullabot. We feel a tremendous responsibility to our team and we are dedicated to be as good to them as they've been to us.

Over the next few weeks, we'll be posting a series of articles talking about Lullabot's conception and growth over the past 10 years, the technological changes that we've seen, changes in Drupal and the web/mobile, and what it's been like to work as a distributed company.

We'll keep this post short today though, because we know you're probably tired and/or hung-over from ringing in the new year. But check back on Monday as The Lullabot Anniversary Series begins.

Drupal 8 Theming--Twig and Responsive Images

Matt & Mike talk about new things a Drupal themer will find in Drupal 8, including Twig templates and responsive images. They're joined by Lullabot Front-end Developers Marc Drummond and Wes Ruvalcaba

The New Drupal 8 Configuration System

Matt & Mike talk to Alex Pott, Matthew Tift, and Greg Dunlap about all things Drupal Configuration Management and their experiences as owners of Drupal 8's Configuration Management Initiative known as CMI.

The Drupal 8 Developer Experience

Matt & Mike talk to Lullabot's Director of Technology Karen Stevenson and Senior Architect Andrew Berry about their experiences working using Drupal 8 in client work.

Suggestions for Avoiding the Workday Motivation Melt Down

Because Lullabot is a distributed company, I interact and communicate with my co-workers a bit differently than some in traditional, physical offices may. We kick-off projects with on-sites, in-person workshops and the like, but for much of the life of a project, I’m not physically in the same room as the rest of the team. We communicate and collaborate a lot on the phone, in Google Hangouts, and in Slack. While working with a distributed team can, at times, really help with productivity and give a great sense of autonomy, there are also times when you feel unmotivated and you can't rely on the energy of the people in the room with you to get you going. After a year and a half, I can finally say I’ve found myself in a good rhythm and feel like I’ve learned how to work efficiently and effectively in a way that keeps me motivated and focused throughout the day.  Below are just a few ideas and tips that have helped me maximize my productivity, creativity and enjoyment of my work.

Personalize your routine

Don’t be afraid to experiment and find a daily routine that fits you best. You can lean into your natural rhythms and have the flexibility to work during the time of day you’re most creative and productive. It’s okay to work outside of the 9-5 time box. Just make sure you’re still able to make all the necessary meetings and are in touch with your team. At Lullabot, we use Slack to stay connected with team members and projects when working odd hours.

Avoid Multitasking I will have Evernote open throughout the day to help me capture and search notes, so it makes sense for me to also use it as a tool for creating task lists. I’ve also heard great things about Wunderlist, Any.do, and todoist. It can be tempting to multitask, especially if you’re working from home. Washing the dishes and trying to run a client call at the same time is just a bad idea. Multitasking involves context switching, which often quickly depletes energy  and can lead to exhaustion. You actually can get more done if you focus on one task at a time. One of the great things about working for Lullabot is that we’re usually assigned to a single project for a duration of time. Because of the narrow focus, I’ve noticed that I often produce better quality work within a shorter amount of time. Creating a task checklist can also help you avoid distractions and multitasking and keep you focused throughout the day.

Create a dedicated space for work time

The boundaries of work and personal time can be very easily blurred when working from home. Creating a dedicated space for work allows you to mentally shift from work time to personal time when the work day is complete. Don’t have an entire room to dedicate to an office? A small area in the corner of a bedroom or dining room will do. Using a notification system such as a post-it-note on the door or a do not disturb sign can let family members or significant others know when you can or can’t be interrupted. When your work day is done, performing routine activities such as making dinner or going for an end of the day walk can help you mentally wind down the work day.

Complete tasks away from the computer

It’s good to keep in mind that the computer is only one of many tools at your disposal, and that you should whenever possible work in other mediums. Stepping away from the computer and using a different tool to complete a task can be mentally refreshing, encourage exploration and can help reset energy for a new task.  As a design team, we often take this approach and sketch out ideas on paper whenever possible before working on the computer.

Take breaks

Taking small breaks throughout the work day can help keep you motivated and focused. Short walks or doing small tasks like emptying the dishwasher can help clear your mind when you’re working on a tough problem. Shifting your focus to simple tasks during breaks can help reset your mind and inspire new solutions. It’s what designers like Cameron Moll refer to as “creative pause.” Sometimes taking a break can slip your mind when you’re secluded and are in the zone. Setting an alert that goes off during certain parts of the day can help remind you to get up, stretch and walk away for a bit.

Switch up your routine

Routines are great, but too much repetition can be boring and reduce your motivation. If you can’t seem to focus on a task, don’t be afraid to change things up. It can be something as small as removing yourself from your home office and working at a coffee shop, or moving to a standing desk for part of your day.

Stay connected

It’s important that you feel connected to your team and the work that you do. Feeling isolated can interfere with your motivation and focus, and the lack of personal connection can make you feel less accountable when working on a team. If you’re feeling disconnected,  don’t be afraid to reach out to coworkers for a quick non work-related chat. At Lullabot, we have several co-workers that join a morning coffee or afternoon lunch Hangout. You can also reach out into the community and join local meet-ups if you’re itching to talk shop in person with someone.

Hopefully by experimenting with a couple of these suggestions, you can more consistently maintain your motivation, focus and have productive, rewarding work days. Have other suggestions to add to this list? I’d love to hear what helps you to stay motivated throughout your workday.  

The Lullabot Podcast is back! Drupal 8! The Past! The Future!

Drupal 8 is here! The Lullabot Podcast is back! It's an exciting time to be alive. We talk about where we've been, before we look ahead to see where we're going. Oh, and we want to hear from you. Leave us listener feedback at 1-877-LULLABOT x789.

Creating a Custom Filter in Drupal 8

Do you want to make it easier for editors to insert a block of HTML by just including a short token? Maybe you want to add some custom Javascript or CSS, but only for content that contains a certain pattern. Or, maybe you want to filter out certain words that site visitors will find offensive.

In this article, we will create a custom filter for Drupal 8, one that replaces a pattern and adds the required CSS to the page. We’ll also add an option for the filter that users can toggle.

What are Drupal Filters and Text Formats?

Drupal allows you to create text formats, which are groupings of filters. Filters can serve several purposes, but one of the main use cases is to limit what HTML can be placed into content. This helps keeps the site more secure, and prevents editors from potentially breaking the layout.

One of the default text formats is “Basic HTML.” When you configure this format by going to admin/config/content/formats/manage/basic_html and scrolling down a bit, you can see all of the enabled filters for it.

Each filter can have optional settings. For example, you can view the options form for the “Limit allowed HTML tags” filter by scrolling down a bit more.

How do you create your own filters to enable on text formats, and how do you make them configurable?

Frame out the Module

First we create a ‘celebrate’ module folder and then our celebrate.info.yml file.

name: Celebrate description: Custom filter to replace a celebrate token. type: module package: custom core: 8.x

A custom filter is a type of plugin, so we will need to create the proper folder structure to adhere to PSR-4 standards. Our folder structure will be celebrate/src/Plugin/Filter.

In the Filter folder, create a file named FilterCelebrate.php. Add the proper namespace for our file and pull in the FilterBase class so we can extend it.

Our file looks like this so far:

namespace Drupal\celebrate\Plugin\Filter; use Drupal\filter\Plugin\FilterBase; class FilterCelebrate extends FilterBase { }

FilterBase is an abstract class that implements the FilterInterface, taking care of most of the mundane setup and configuration. The only function we are required to implement in our own filter class is process(). According to the FilterInterface::process documentation, the function must return the filtered text, wrapped in a FilterProcessResult object. This means we need to put another use statement in our file.

This may seem onerous. Why can’t we just return the text itself? Why do we need another object? This will become clear later, when we need to add some more advanced use cases. There are good reasons for it. For now, to prevent our IDE or code editor from yelling at us, we’ll  put a passthrough function as a placeholder, which does no transformations on the text.

Here is our updated code:

namespace Drupal\celebrate\Plugin\Filter; use Drupal\filter\FilterProcessResult; use Drupal\filter\Plugin\FilterBase; class FilterCelebrate extends FilterBase { public function process($text, $langcode) { return new FilterProcessResult($text); } } Get Drupal to Discover our Filter

Since a filter type is a plugin, Drupal needs us to add an annotation to our class so it knows exactly what its needs to do with our code. Annotations are comments placed before the class definition, arranged in a certain format.

The file, with our annotation, will look like this:

namespace Drupal\celebrate\Plugin\Filter; use Drupal\filter\FilterProcessResult; use Drupal\filter\Plugin\FilterBase; /** * @Filter( * id = "filter_celebrate", * title = @Translation("Celebrate Filter"), * description = @Translation("Help this text format celebrate good times!"), * type = Drupal\filter\Plugin\FilterInterface::TYPE_MARKUP_LANGUAGE, * ) */ class FilterCelebrate extends FilterBase { public function process($text, $langcode) { return new FilterProcessResult($text); } }

Every plugin declaration needs an id, so we give it a reasonable one. Title and description are what will be shown on the admin screens. After we enable the module, you should see something like this on the screen for a text format:

The “type” in the annotation needs a bit more of an explanation. This is a classification for the purpose of the filter, and there are a few constants that help us populate the property. From the documentation, we have the following options:

  • FilterInterface::TYPE_HTML_RESTRICTOR: HTML tag and attribute restricting filters.
  • FilterInterface::TYPE_MARKUP_LANGUAGE: Non-HTML markup language filters that generate HTML.
  • FilterInterface::TYPE_TRANSFORM_IRREVERSIBLE: Irreversible transformation filters.
  • FilterInterface::TYPE_TRANSFORM_REVERSIBLE: Reversible transformation filters.

For our purposes, we plan on taking a bit of non-HTML markup and turning it into HTML, so the second classification fits.

There are a few more optional properties for Filter annotations, and they can be found in the FilterInterface documentation.

Adding Basic Text Processing

For this filter, we want to replace every instance of the token “[celebrate]” with the HTML snippet “<span class=”celebrate-filter”>Good Times!</span>”. To do that, we add some code to our FilterCelebrate::process function.

public function process($text, $langcode) { $replace = '<span class="celebrate-filter">’ . $this->t(‘Good Times!’) . ‘</span>'; $new_text = str_replace('[celebrate]', $replace, $text); return new FilterProcessResult($new_text); }

Enable the Celebrate filter for the Basic HTML content filter, and create some test content that contains the [celebrate] token. You should see it replaced by the HTML snippet defined above. If not, check to make sure the field has the Basic HTML filter applied.

Adding a Settings Form for the Filter

But we want the user to be able to toggle an option regarding this filter. To do that, we need to define a settings form by overriding the settingsForm() method for our class.

We add the following code to our class to define a form array for our filter:

public function settingsForm(array $form, FormStateInterface $form_state) { $form['celebrate_invitation'] = array( '#type' => 'checkbox', '#title' => $this->t('Show Invitation?'), '#default_value' => $this->settings['celebrate_invitation'], '#description' => $this->t('Display a short invitation after the default text.'), ); return $form; }

For more details on using the Form API to define a form array, check out the Form API Documentation. If you have created for altered forms in Drupal 7, the convention should be familiar.

If we reload our text format admin page after adding this function, we’ll get an error:

Fatal error: Declaration of Drupal\celebrate\Plugin\Filter\FilterCelebrate::settingsForm() must be compatible with Drupal\filter\Plugin\FilterInterface::settingsForm(array $form, Drupal\Core\Form\FormStateInterface $form_state)

Essentially, it doesn’t know what a FormStateInterface, as designated in our settingsForm() method. We need to either add the full PSR-4 namespace to the method definition, or add another use statement. For our example, we’ll add another use statement to the top of our FilterCelebrate.php file.

use Drupal\Core\Form\FormStateInterface;

Now we can see our settings form in action.

To get access to these settings in our class, we can call $this->settings[‘celebrate_invitation’].

Our process method now looks like this:

public function process($text, $langcode) { $invitation = $this->settings['celebrate_invitation'] ? ' Come on!' : ''; $replace = '<span class="celebrate-filter">’ . $this->t(‘Good Times!' . $invitation) . ' </span>'; $new_text = str_replace('[celebrate]', $replace, $text); return new FilterProcessResult($new_text); }

Now, if the “Show Invitation?” setting is checked, the text “Come on!” is added to the end of the replacement text.

Adding CSS to the Page When the Filter is Applied

But now we want to add a shaking CSS animation to the replacement text on hover, because we want to celebrate like it's 1999. The CSS should only be loaded when the filter is being used. This is where the additional properties of the FilterProcessResult object come into play.

First, we’ll create a CSS file in the root of our module folder called “celebrate.theme.css”. The following CSS is everything we need to enable a shaking effect on hover:

.celebrate-filter { background-color: #000066; padding: 10px 5px; color: #fff; } .celebrate-filter:hover { animation: shake .3s ease-in-out infinite; background-color: #ff0000; } @keyframes shake { 0% { transform: translateX(0); } 20% { transform: translateX(-6px); } 40% { transform: translateX(6px); } 60% { transform: translateX(-6px); } 80% { transform: translateX(6px); } 100% { transform: translateX(0); } }

In order to attach our CSS file to the FilterProcessResult, it needs to be declared as a library. Create another file in the module root called “celebrate.libraries.yml” with the following text:

celebrate-shake: version: 1.x css: theme: celebrate.theme.css: {}

This defines a library called “celebrate-shake” that includes a CSS file. Multiple CSS and/or Javascript files can be included in a single library. For more details, see the documentation on defining a library.

Now that we have defined a library, we can add it to the page whenever our filter is being applied. We use the setAttachments() method of the FilterProcessResult object to add our library so our process function will look like this:

public function process($text, $langcode) { $invitation = $this->settings['celebrate_invitation'] ? ' Come on!' : ''; $replace = '<span class="celebrate-filter">’ . $this->t(‘Good Times!' . $invitation) . ' </span>'; $new_text = str_replace('[celebrate]', $replace, $text); $result = new FilterProcessResult($new_text); $result->setAttachments(array( 'library' => array('celebrate/celebrate-shake'), )); return $result; }

You will notice that we use the identifier “celebrate/celebrate-shake” to refer to our new library. The first half of the identifier is our module name and the second half is the library name itself. This is to help prevent name conflicts and collisions.

And as an added bonus, other modules will also be able to use our celebrate library.

You can download the full Celebrate Filter module here.

Remember, since filters can be mixed and stacked on top of each other, a good filter will do one thing really well. Keep the use case compact and discreet. If you find your filter code getting long, having to write a lot of exceptions, work around assumptions, and adding more and more options to the settings form, it might be a good idea to step back and see if it makes sense to create more than one type of custom filter.

Configuration Management in Drupal 8: The Key Concepts

While the new configuration system in Drupal 8 strives to make the process of exporting and importing site configuration feel almost effortless, immensely complex logic facilitates this process. Over the past five years, the entire configuration system code was written and rewritten multiple times, and we think we got much of it right in its present form. As a result of this work, it is now possible to store configuration data in a consistent manner and to manage changes to configuration. Although we made every attempt to document how and why decisions were made – and to always update issue queues, documentation, and change notices – it is not reasonable to expect everyone to read all of this material. But I did, and in this post I try to distill years of thinking, discussions, issue summaries, code sprints, and code to ease your transition to Drupal 8.

In this article I highlight nine concepts that are key to understanding the configuration system. This article is light on details and heavy on links to additional resources.

  1. It is called the “configuration system.” The Configuration Management Initiative (CMI) is, by most reasonable measures, feature complete. The number of CMI critical issues was reduced to zero back in the Spring and the #drupal-cmi IRC channel has been very quiet over the past few months. Drupal now has a functional configuration management system, but we only should call the former a CMS. While it is tempting to think of “CMI” as an orphaned initialism, like AARP or SAT, we aspire to avoid confusion. Our preferred phrase to describe the result of CMI is “configuration system.” This is the phrase we use in the issue queue and the configuration system documentation.

  2. DEV ➞ PROD. Perhaps the most important concept to understand is that the configuration system is designed to optimize the process of moving configuration between instances of the same site. It is not intended to allow exporting the configuration from one site to another. In order to move configuration data, the site and import files must have matching values for UUID in the system.site configuration item. In other words, additional environments should initially be set up as clones of the site. We did not, for instance, hope to facilitate exporting configuration from whitehouse.gov and importing it into harvard.edu.

  3. The configuration system is highly configurable. Out of the box the configuration system stores configuration data in the database. However, it allows websites to easily switch to file-based storage, MongoDB, Redis, or another favorite key/value store. In fact, there is a growing ecosystem of modules related to the configuration system, such as Configuration Update, Configuration Tools, Configuration Synchronizer, and Configuration Development.

  4. There is no “recommended” workflow. The configuration system is quite flexible and we can imagine multiple workflows. On one end of the spectrum, we expect some small sites will not ever use the configuration manager module to import and export configuration. For the sites that utilize the full capabilities of the configuration system, one key question they will need to answer regards the role that site administrators will play in managing configuration. I suspect many sites will disable configuration forms on their production sites – perhaps using modules like Configuration Read-Only Mode – and make all configuration changes in their version control system.

  5. Sites, not modules, own configuration. When a module is installed, and the configuration system imports the configuration data from the module’s config/install directory (and perhaps the config/optional directory), the configuration system assumes the site owner is now in control of the configuration data. This is a contentious point to some developers because module maintainers will need to use update hooks rather than making simple changes to their configuration. Changing the files in a module’s config/install directory after the module has been installed will have no effect on the site.

  6. Developers will still use Features. The Features module in Drupal 8 changes how the configuration system works to allow modules to control their configuration. Mike Potter, Nedjo Rogers, and others have been making Features in Drupal 8 do the kinds of things Features was originally intended to do, which is to bundle functionality, such as a “photo gallery feature.” The configuration system makes the work of the Features module maintainers exponentially easier and as a result, we all expect using Features to be more enjoyable in Drupal 8 than it was in Drupal 7.

  7. There are two kinds of configuration in Drupal 8: simple configuration and configuration entities. Simple configuration stores basic configuration, such as boolean values, integers, or texts. Simple configuration has exactly one copy or version, and is somewhat similar to using variable_get() and variable_set() in Drupal 7. Configuration entities, like content types, enable the creation of zero or more items and are far more complex. Examples of configuration entities include views, content types, and image styles.

  8. Multilingual needs drove many decisions. Many of the features of the configuration system exist to support multilingual sites. For example, the primary reason schema files were introduced was for multilingual support. And many of the benefits to enabling multilingual functionality resulted in enhancements that had much wider benefits. The multilingual initiative was perhaps the best organized and documented Drupal 8 initiative and their initiative website contains extensive information and documentation.

  9. Configuration can still be overridden in settings.php. The $config variable in the settings.php file provides a mechanism for overriding configuration data. This is called the configuration override system. Overrides in settings.php take precedence over values provided by modules. This is a good method for storing sensitive data that should not be stored in the database. Note, however, that the values in the active configuration – not the values from settings.php – are displayed on configuration forms. Of course, this behavior can be modified to match expected workflows. For example, some site administrators will want the configuration forms to indicate when form values are overridden in settings.php.

If you want more information about the configuration system, the best place to start is the Configuration API page on api.drupal.org. It contains numerous links to additional documentation. Additionally, Alex Pott, my fellow configuration system co-maintainer, wrote a series of blog posts concerning the “Principles of Configuration Management” that I enthusiastically recommend.

I hope you will agree that the configuration system is one of the more exciting features of Drupal 8.

This article benefitted from helpful conversations and reviews by Tim Plunkett, Jennifer Hodgdon, Andrew Berry, and Juampy NR.

Five-Fifteens: A Simple Way to Keep Information Flowing Across Teams

Five minutes to read, fifteen minutes to write.  

A five-fifteen is a communication tool that makes the task of reporting upwards a quick, painless, and easy thing to do. The basic idea is to sit down and write the answers to just enough questions that it would only take you fifteen minutes to write and someone else five minutes to read.

This is a process we encourage everyone to do at Lullabot. It gives your direct report or entire team – depending on who you want to send it to – an idea of how your week is going and the challenges that you may be facing so that they have a chance to empathize and try to help in any way they can. It gives you a chance to speak your mind about your work, your life, and to give your direct report the all-important feedback that they need to hear in order to better help you in your life and in your work.

We’ve written a tool to help the creation of these letters. You can use it by going to http://515.lullabot.com/ and following the few short steps that will help you open that line of communciation. It uses your default mail client and is totally client side, so no record of the emails are stored in our system. It’s a completely open source tool, so feel free to send us pull requests or fork it for your own purposes from GitHub: https://github.com/Lullabot/fivefifteen

What’s your favorite way to communicate valuable information to your direct report or team?
 

Goodbye Drush Make, Hello Composer!

I’ve built and rebuilt many demo Drupal 8 sites while trying out new D8 modules and themes and experimenting with new functionality like migrations. After installing D8 manually from scratch so many times, I decided to sit down and figure out how to build a Drupal site using Composer to make it easier. The process is actually very handy, sort of the way we’ve used Drush Make in the past, where you don’t actually store all the core and contributed module code in your repository, you just record which modules and versions you’re using and pull them in dynamically.

I was a little worried about changing the process I’ve used for a long time, but my worries were for nothing. Anyone who’s used to Drush would probably find it pretty easy to get this up and running. 

TLDR: How to go from an empty directory to a fully functional Drupal site in two command lines:

sudo composer create-project drupal-composer/drupal-project:~8.0 drupal --stability dev --no-interaction cd drupal/web ../vendor/bin/drush site-install --db-url=mysql://{username}:{password}@localhost/{database} Install Composer

Let's talk through the whole process, step by step. The first step is to install Composer on your local system. See https://getcomposer.org/download/ for more information about installing Composer.

Set Up A Project With Composer

To create a new Drupal project using Composer, type the following on the command line, where /var/drupal is the desired code location:

cd /var sudo composer create-project drupal-composer/drupal-project:~8.0 drupal --stability dev --no-interaction

The packaging process downloads all the core modules, Devel, Drush and Drush Console, and then moves all the Drupal code into a ‘web’ subdirectory. It also moves the vendor directory outside of the web root. The new file structure will look like this:

You will end up with a composer.json file at the base of the project that might look like the following. You can see the beginning of the module list in the ‘require’ section, and that Drush and Drupal Console are included by default. You can also see rules that move contributed modules into ‘/contrib’ subfolders as they’re downloaded.

{ "name": "drupal-composer/drupal-project", "description": "Project template for Drupal 8 projects with composer", "type": "project", "license": "GPL-2.0+", "authors": [ { "name": "", "role": "" } ], "repositories": [ { "type": "composer", "url": "https://packagist.drupal-composer.org" } ], "require": { "composer/installers": "^1.0.20", "drupal/core": "8.0.*", "drush/drush": "8.*", "drupal/console": "~0.8", }, "minimum-stability": "dev", "prefer-stable": true, "scripts": { "post-install-cmd": "scripts/composer/post-install.sh" }, "extra": { "installer-paths": { "web/core": ["type:drupal-core"], "web/modules/contrib/{$name}": ["type:drupal-module"], "web/profiles/contrib/{$name}": ["type:drupal-profile"], "web/themes/contrib/{$name}": ["type:drupal-theme"], "web/drush/commands/{$name}": ["type:drupal-drush"] } } }

That site organization comes from https://github.com/drupal-composer/drupal-project/tree/8.x. A README.md file there describes the process for doing things like updating core. The contributed modules are coming from Packagist rather than directly from Drupal.org. That’s because the current Drupal versioning system doesn’t qualify as the semantic versioning the system needs. There is an ongoing discussion https://www.drupal.org/node/1612910 about how to fix that.

Install Drupal

The right version of Drush for Drupal 8 comes built into this package. If you have an empty database you can then install Drupal using the Drush version in the package:

cd drupal/web ../vendor/bin/drush site-install --db-url=mysql://{username}:{password}@localhost/{database}

If you don’t do the installation with Drush you can do it manually, but the Drush installation handles all this for you. The manual process for installing Drupal 8 is:

  • Copy default.settings.php to settings.php and unprotect it
  • Copy default.license.yml to license.yml and unprotect it
  • Create sites/files and unprotect it
  • Navigate to EXAMPLE.COM/install to provide the database credentials and follow the instructions.
Add Contributed Modules From Packagist

Adding contributed modules is done a little differently. Instead of adding modules using drush dl, add additional modules by running composer commands from the Drupal root:

composer require drupal/migrate_upgrade 8.1.*@dev composer require drupal/migrate_plus 8.1.*@dev

As you go, each module will be downloaded from Packagist and composer.json will be updated to add this module to the module list. You can peek into the composer.json file at the root of the project and see the ‘require’ list evolving.

Repeat until all desired contributed modules have been added. The composer.json file will then become the equivalent of a Drush make file, with documentation of all your modules.

For even more parity with Drush Make, you can add external libraries to your composer.json as well, and, with a plugin, you can also add patches to it. See more details about all these options at https://www.drupal.org/node/2471553.

Commit Files to the Repo

Commit the composer.json changes to the repo. The files downloaded by Composer do not need to be added to the repo. You’ll see a .gitignore file that keeps them out (this was added as a part of the composer packaging). Only composer.json, .gitignore and the /sites subdirectory (except /sites/default/files) will be stored in the git repository.

.gitignore # Ignore directories generated by Composer vendor web/core web/modules/contrib web/themes/contrib web/profiles/contrib # Ignore Drupal's file directory web/sites/default/files Update Files

To update the files any time they might have changed, navigate to the Drupal root on the command line and run:

composer update

Add additional Drupal contributed modules, libraries, and themes at any time from the Drupal root with the same command used earlier:

composer require drupal/module_name 8.1.*@dev

That will add another line to the composer.json file for the new module. Then the change to composer.json needs to be committed and pushed to the repository. Other installations will pick this change up the next time they do git pull, and they will get the new module when they run composer update.

The composer update command should be run after any git pull or git fetch. So the standard routine for updating a repository might be:

git pull composer update drush updb ... New Checkout

The process for a new checkout of this repository on another machine would simply be to clone the repository, then cd into it and run the following, which will then download all the required modules, files, and libraries:

composer install That’s It

So that’s it. I was a little daunted at first but it turns out to be pretty easy to manage.  You can use the same process on a Drupal 7 site, with a few slight modifications.

Obviously the above process describes using the development versions of everything, since Drupal 8 is still in flux. As it stabilizes you’ll want to switch from using 8.1.*@dev to identifying specific stable releases for core and contributed modules.

See the links below for more information:

Why and how we migrated comments to Disqus

When we started working on the latest relaunch of Lullabot.com, we wanted to decouple the front end from the back end. While evaluating how to migrate comments, we realized the following:

  • User navigation at Lullabot.com is anonymous so Drupal had nothing to do with comments apart from storing them in the database.
  • Even though we were using Mollom, we were still getting spam comments.

After evaluating a few third party commenting tools, we chose Disqus for its moderation and auto-spam detection system. This article details how we exported a large amount of Drupal comments into a Disqus account. The process was tedious and it required us to do some debugging and polishing so in order to make things easier for everyone, and for those reasons it's worth documenting the steps we took.

Here is the list of steps that we will follow:

  1. Creating an account at Disqus.
  2. Finding a way to export Drupal comments for Disqus.
  3. Installing and configuring Disqus Migrate module.
  4. Exporting comments.
  5. Uploading comments to Disqus.
  6. Verifying the setup.
1. Creating an account at Disqus

In order to embed Disqus threads in our website, we need a Disqus account and a Disqus site. Once we have created an account we can open https://disqus.com/admin/create and register here our site:

Next, at the General tab, we need to set the following options:

At the top of this form we see that our site's shortname is lullabot. We will use this parameter later at the Drupal administration to identify ourselves against Disqus. Once we are done with this form we will move to the Advanced tab, where we can specify which domains are allowed to load our site's comments:

This setting will save you a lot of headaches as it prevents Disqus from rendering the Discussion Widget if the current hostname does not match. This in turn precludes developers or testers from creating new threads from development domains such as dev.lullabot.com or lullabot.local, or even worse: posting test comments that would appear later in the live site. Instead, they will see the following message:

Tip: If you need to test the Disqus Discussion Widget locally, you can edit your Hosts file in order to fake the trusted domain.

2. Finding a way to export Drupal comments for Disqus

Disqus can import comments into an XML file following the WXR (WordPress eXtended RSS) format. Here is the Disqus interface to upload the file:

What we need is a tool in Drupal to export Lullabot.com comments into a single WXR file. By looking at the Disqus module description, exporting comments was implemented for the Drupal 6 version, but never got completed for Drupal 7:

By searching further I found a sandbox called Disqus Migrate which is a port of the Drupal 6 sub-module for Drupal 7. The module's description did not look promising at all: a sandbox project, marked as Unsupported, encouraging not to use with real accounts, and other warnings: 

I was wrong though, as the module proved to be a solid project that simply needed some polishing. In fact, during the process I submitted a few patches to the module's issue queue and promoted it to a full project.

3. Installing and configuring Disqus Migrate module

Installing sandbox projects is not among best practices in Drupal but at Lullabot sometimes we discover sandbox projects that are mature enough to become full projects. In these cases, we help module maintainers to fix existing bugs and then we encourage them to promote the sandbox to a full project. 

I glanced through the module's source code at Drupal.org and my first impression was positive. I did not see any security holes and the code was easy to understand. Next, I downloaded and installed the module in my local instance of Lullabot.com by entering the following commands:

git clone --branch 7.x-1.x http://git.drupal.org/sandbox/dwkitchen/1483518.git sites/all/modules/sandbox/disqus_migrate drush en disqus_migrate

Disqus Migrate leverages the Disqus Drupal module by adding a tab called Migrate at admin/config/services/disqus. At the top of this administration form we must set the shortname, which we saw before at the Disqus admin panel. If we are going to use Drupal to render the Disqus widget, then we need a set of keys to connect with the Disqus API. Once you have registered your application there, you can enter the keys at the form like so:

Unless we are exporting comments from the production environment (which we don't recommend), we need to override the base URL so each thread will have the trusted domain. Here is the field where we can set this:

4. Exporting comments

Now we are ready to export comments. My local environment has a recent copy of the production environment's database. Disqus Migrate adds a task at admin/content/comment where we can export comments:

After confirming, we get all comments in an XML file:

Here is how the XML file looks:

In the above XML file, there is a <comment> entry for each comment which belongs to a thread. Each thread has a unique identifier which the Disqus Migrate module sets to the node path, such as node/30. Now we are ready to upload comments to Disqus.

5. Uploading comments to Disqus

Here is a screenshot of the Import tool in the Disqus administration. We have selected the XML file and after clicking Upload and import, the file will be uploaded and processed by Disqus.

That's all, after a few minutes we should see comments at the Comments tab:

If you have issues uploading comments to Disqus, then have a look at these troubleshooting tips. In our case, we used xmllint to discover bugs in the the XML file and then we submitted a few patches to the Disqus Migrate's issue queue to fix them.

6. Verifying the setup and wrapping up

If you are using Drupal to render the front end and you filled out the administration form that we saw at Installing and configuring Disqus Migrate module, Disqus comments should load automatically for your site. Lullabot.com has a decoupled front end powered by React so we wrote a React component which, given a thread title and identifer, renders the Disqus widget with its comments. You can see it in action below this article. Try it out and leave us a comment with your thoughts.

Image credit: https://nyibelieve.wordpress.com/2010/06/05/new-york-i-love-you

A Tale of Two Base Themes in Drupal 8 core

Hi, my name is Marc, and I am a markup nerd.

My skin crawls when I see HTML filled with divs tucked inside each other over and over again like a Russian nesting doll. My shoulders tense up when I see class names spilling out of a class attribute like clowns in a circus car. Conversely, my heart sings at the sight of cleanly crafted HTML that uses just the right elements to describe its contents, with sensible BEM-style classes that simplify styling and clarify how the markup has been structured.

Because I prefer being very particular about markup, I’ve been known to develop an eye twitch from time to time while theming Drupal sites.

In 2013 I started contributing fairly regularly to the development of Drupal 8. Working at Lullabot since September 2014 has made it possible to devote even more of my time to working on Drupal 8 front-end improvements. There’s a great team of developers around the world focused on improving the theming experience in Drupal 8, and it’s been a joy collaborating with them. I’m very excited about how we’ve worked together to make Drupal 8 better for front-end developers and themers.

Thanks to those efforts, I’m looking forward to having much better tools for carefully crafting markup in Drupal 8.

Making markup shine in Drupal 8

The new templating engine in Drupal 8, Twig, makes it easier to change markup without knowing the ins and outs of PHP. Most of the classes in Drupal are now added in the Twig templates themselves, rather than being buried deep in preprocessor and theme functions. Better yet, by turning on Twig Debug in the services.yml file in your sites/default folder, you can see which templates are generating each bit of markup when you view source. You’ll find which template file is currently in use along with suggestions for filenames you can use in your theme to override that template.

The markup itself has been greatly improved as well. Contributors worked to improve template logic so that only necessary divs or other elements are generated: yes, there are fewer wrapper divs in Drupal 8! In previous versions of Drupal, field markup introduced wrapper after wrapper, often unnecessarily. Now if you have only one value and no label for a field...there’s only one div.

Two base themes in Drupal 8: Classy and Stable

In general, your theme has the final say on Drupal’s markup, CSS, and JS. Themes can override templates, CSS, and JS that come from core modules, contributed modules and base theme. Themes can have parent-child relationships, with the parent theme serving as the base theme for a sub-theme. A sub-theme inherits templates, CSS, and JS from its base theme. Themes can also have grandparent themes or even great-grandparent themes: we’ll talk about chaining together contrib themes with core themes in just a bit.

Drupal’s markup comes in two flavors, depending on what you choose as your base theme.

  • If you want to start with markup with sensible classes you can use as styling hooks, then you can use Classy as your base theme. This is similar to the approach used with Drupal 7’s default markup, but with classes and markup significantly improved.
  • However, if you want markup that is even more lean, you can use Stable as your base theme; in fact that’s the default. Stable still has some classes needed for front-end UI components like the toolbar and contextual links, but in general has far fewer classes than Classy, and even fewer wrapper elements.

By providing two flavors of markup with the Stable and Classy base themes, Drupal 8 allows you to choose how you want to approach markup changes:

  • Tweak the classes Drupal provides as a starting point (Classy),
  • Only add classes where you think they are essential (Stable).

If you prefer to start from scratch in order to use highly customized markup patterns, Stable may be the right base theme for you. This approach can be useful when implementing a framework like Bootstrap or Foundation that relies upon very particular markup patterns.

Field markup: Stable vs Classy

Let’s look at one example of how markup can differ between Stable and Classy.

Here’s Classy field markup when you have a label and multiple field values:

<div class="field field--name-field-favorite-horses field--type-string field--label-above"> <div class="field__label">Favorite horses</div> <div class='field__items'> <div class="field__item">Black Stallion</div> <div class="field__item">Shadowfax</div> <div class="field__item">Hidalgo</div> </div> </div>

In contrast, here’s the same markup when using Stable:

<div> <div>Favorite horses</div> <div> <div>Black Stallion</div> <div>Shadowfax</div> <div>Hidalgo</div> </div> </div>

Just for fun, look how much slimmer Classy’s markup is when only one value is allowed on a field, and the label is hidden:

<div class="field field--name-field-best-horse field--type-string field--label-hidden field__item">Mister Ed</div>

That markup gets even leaner with Stable:

<div>Mister Ed</div>

To see how far we've come, here's how that markup would look by default in Drupal 7:

<div class="field field-name-field-best-horse field-type-text field-label-hidden"> <div class="field-items"> <div class="field-item even">Mister Ed</div> </div> </div>

That’s a lot more markup than you really need when you know there will only ever be one value for a field.

From this example, you can see that Stable is a starting point. You would want to customize Stable’s markup in your theme to add sensible classes. Writing CSS using only HTML elements in your selectors is painful. However, with Stable you can build up only the classes you need, rather than spending time evaluating which classes in Classy should be retained.

Keeping Core markup reliable

One of the primary purposes of the Stable theme, which is new to core, is to provide a backwards compatibility layer for Drupal’s core markup, CSS, and JS. Those will all be locked within the Stable theme as of Drupal 8 RC1. You can rely upon Stable as your default base theme without worry that the markup will change on you during the Drupal 8 cycle.

The Classy theme will also provide reliable markup, CSS and JS, most likely by using Stable as its base theme.

So whether you choose Stable or Classy as your base theme, you can also rely on its markup whether you have Drupal 8.0.5 or Drupal 8.3.4 installed. Both Stable and Classy will still receive bug fixes—for example, if the wrong variable is being printed by a template. The goal is to make these fixes in a way that ensures backwards compatibility.

Drupal 8 also ships with two other themes, Bartik and Seven.

  • Bartik is the default theme when you install Drupal 8 and demonstrates one possible front-end implementation.
  • Seven serves as the default admin theme.

Both have been improved in Drupal 8 and use Classy as their base theme. Bartik and Seven can also continue to change throughout Drupal 8, so you wouldn’t want to use Bartik or Seven as a base theme. Bartik or Seven’s markup could be tweaked in 8.1 or 8.2, which could break your site’s appearance if you use Bartik or Seven as a base for your theme.

In the meantime, during the Drupal 8 cycle, core markup can continue to evolve. Contributors can continue to clean things up so that we don’t need to spend nearly as much time working on tidying our markup when work on Drupal 9 begins.

How to define the base theme in .info.yml

Many of a theme’s basic settings are defined in a file called MYTHEME.info.yml within your theme folder, where MYTHEME is replaced with the name of your theme. This is similar to the .info file in Drupal 8.

If you want to use Classy as your theme, add the following in your theme’s .info.yml file:

base theme: classy

If you want Stable as your base theme, you do not need to add a base theme setting to your theme’s .info.yml. If no base theme is declared, by default your base theme will be set to Stable.

If you don’t want a base theme at all, add this to your theme’s .info.yml file:

base theme: false

That would mean your theme would use Drupal 8’s core templates, CSS, and JS directly, rather than passing through Stable or Classy first. That’s a risky strategy. If something changes in core, you might need to update your theme accordingly.

Using Classy or Stable as your base theme is a more reliable way to ensure the stability of your theme.

Chaining base themes together

Contrib themes can also choose to use Classy or Stable as their base theme, or no base theme at all. That’s right, a base theme like Zen can have Stable or Classy as its base theme. A sub-theme of Zen would have Zen as its base theme, but would also inherit from Zen’s base theme, whether that’s Classy or Stable. So while you might end up using a contrib theme as a base for your theme, ultimately that contrib theme will likely trace back to one of the two base themes in core, Classy or Stable.

When you evaluate base themes like Zen or Omega or Adaptive Theme, make sure to check their info.yml file for the base theme setting. If you see base theme: false, you may want to be wary as core markup will surely change. If you see Classy or no base theme setting at all, you’ll have an idea what kind of markup (and stability) to expect from that contrib theme.

To pull together what we've learned so far, this graphic created by Morten DK shows how Drupal's core themes interact with contrib themes or themes you might develop:

How Drupal 8 themes relate to each other

This is a good look at how things stand in October 2015, with Drupal 8 RC1 recently released. As more and more people try out Drupal 8 and build themes, we'll continue to develop a better understanding of how best to set up contrib and custom themes.

Learning more

You can learn more about theming in Drupal 8 by checking out John Hannah’s great articles on the subject, Drupal 8 Theming Fundamentals Part I and Part II

I’m definitely looking forward to working with markup in Drupal 8, more so than I ever have before. Hopefully you will too!

What Happened to Hook_Menu in Drupal 8?

In Drupal 7 and earlier versions hook_menu has been the Swiss Army knife of hooks. It does a little bit of everything: page paths, menu callbacks, tabs and local tasks, contextual links, access control, arguments and parameters, form callbacks, and on top of all that it even sets up menu items! In my book it’s probably the most-used hook of all. I don’t know if I’ve ever written a module that didn’t implement hook_menu.

But things have changed In Drupal 8. Hook_menu is gone and now all these tasks are managed separately using a system of YAML files that provide metadata about each item and corresponding PHP classes that provide the underlying logic.

The new system makes lots of sense, but figuring out how to make the switch can be confusing. To make things worse, the API has changed a few times over the long cycle of Drupal 8 development, so there is documentation out in the wild that is now incorrect. This article explains how things work now, and it shouldn't change any more.

I’m going to list some of the situations I ran into while porting a custom module to Drupal 8 and show before and after code examples of what happened to my old hook_menu items.

Custom Pages

One of the simplest uses of hook_menu is to set up a custom page at a given path. You'd use this for a classic "Hello World" module. In Drupal 8, paths are managed using a MODULE.routing.yml file to describe each path (or ‘route’) and a corresponding controller class that extends a base controller, which contains the logic of what happens on that path. Each controller class lives in its own file, where the file is named to match the class name. This controller logic might have lived in a separate MODULE.pages.inc file in Drupal 7.

In Drupal 7 the code might look like this:

function example_menu() { $items = array(); $items['main'] = array( 'title' => 'Main Page', 'page callback' => example_main_page', 'access arguments' => array('access content'), 'type' => MENU_NORMAL_ITEM, 'file' => 'MODULE.pages.inc' ); return $items; } function example_main_page() { return t(‘Something goes here’); }

In Drupal 8 we put the route information into a file called MODULE.routing.yml. Routes have names that don’t necessary have anything to do with their paths. They are just unique identifiers. They should be prefixed with your module name to avoid name clashes. You may see documentation that talks about using _content or _form instead of _controller in this YAML file, but that was later changed. You should now always use _controller to identify the related controller.

example.main_page_controller: path: '/main' defaults: _controller: '\Drupal\example\Controller\MainPageController::mainPage' _title: 'Main Page' requirements: _permission: 'access content'

Note that we now use a preceding slash on paths! In Drupal 7 the path would have been main, and in Drupal 8 it is /main! I keep forgetting that and it is a common source of problems as I make the transition. It’s the first thing to check if your new code isn’t working!

The page callback goes into a controller class. In this example the controller class is named MainPageController.php, and is located at MODULE/src/Controller/MainPageController.php. The file name should match the class name of the controller, and all your module’s controllers should be in that /src/Controller directory. That location is dictated by the PSR-4 standard that Drupal has adopted. Basically, anything that is located in the expected place in the ‘/src’ directory will be autoloaded when needed without using module_load_include() or listing file locations in the .info file, as we had to do in Drupal 7.

The method used inside the controller to manage this route can have any name, mainPage is an arbitrary choice for the method in this example. The method used in the controller file should match the YAML file, where it is described as CLASS_NAME::METHOD. Note that the Contains line in the class @file documentation matches the _controller entry in the YAML file above.

A controller can manage one or more routes, as long as each has a method for its callback and its own entry in the YAML file. For instance, the core nodeController manages four of the routes listed in node.routing.yml.

The controller should always return a render array, not text or HTML, another change from Drupal 7.

Translation is available within the controller as $this->t() instead of t(). This works because ControllerBase has added the StringTranslationTrait. There's a good article about how PHP Traits like translation work in Drupal 8 on Drupalize.Me.

/** * @file * Contains \Drupal\example\Controller\MainPageController. */ namespace Drupal\example\Controller; use Drupal\Core\Controller\ControllerBase; class MainPageController extends ControllerBase { public function mainPage() { return [ '#markup' => $this->t('Something goes here!'), ]; } Paths With Arguments

Some paths need additional arguments or parameters. If my page had a couple extra parameters it would look like this in Drupal 7:

function example_menu() { $items = array(); $items[‘main/%/%’] = array( 'title' => 'Main Page', 'page callback' => 'example_main_page', ‘page arguments’ => array(1, 2), 'access arguments' => array('access content'), 'type' => MENU_NORMAL_ITEM, ); return $items; } function example_main_page($first, $second) { return t(‘Something goes here’); }

In Drupal 8 the YAML file would be adjusted to look like this (adding the parameters to the path):

example.main_page_controller: path: '/main/{first}/{second}' defaults: _controller: '\Drupal\example\Controller\MainPageController::mainPage' _title: 'Main Page’ requirements: _permission: 'access content'

The controller then looks like this (showing the parameters in the function signature)::

/** * @file * Contains \Drupal\example\Controller\MainPageController. */ namespace Drupal\example\Controller; use Drupal\Core\Controller\ControllerBase; class MainPageController extends ControllerBase { public function mainPage($first, $second) { // Do something with $first and $second. return [ '#markup => $this->t('Something goes here!'), ]; } }

Obviously anything in the path could be altered by a user so you’ll want to test for valid values and otherwise ensure that these values are safe to use. I can’t tell if the system does any sanitization of these values or if this is a straight pass-through of whatever is in the url, so I’d probably assume that I need to type hint and sanitize these values as necessary for my code to work.

Paths With Optional Arguments

The above code will work correctly only for that specific path, with both parameters. Neither the path /main, nor /main/first will work, only /main/first/second. If you want the parameters to be optional, so /main, /main/first, and /main/first/second are all valid paths, you need to make some changes to the YAML file.

By adding the arguments to the defaults section you are telling the controller to treat the base path as the main route and the two additional parameters as path alternatives. You are also setting the default value for the parameters. The empty value says they are optional, or you could give them a fixed default value to be used if they are not present in the url.

example.main_page_controller: path: '/main/{first}/{second}' defaults: _controller: '\Drupal\example\Controller\MainPageController::mainPage' _title: 'Main Page' first: '' second: '' requirements: _permission: 'access content' Restricting Parameters

Once you set up parameters you probably should also provide information about what values will be allowed for them. You can do this by adding some more information to the YAML file. The example below indicates that $first can only contain the values ‘Y’ or ‘N’, and $second must be a number. Any parameters that don’t match these rules will return a 404. Basically the code is expecting to evaluate a regular expression to determine if the path is valid.

See Symfony documentation for lots more information about configuring routes and route requirements.

example.main_page_controller: path: '/main/{first}/{second}' defaults: _controller: '\Drupal\example\Controller\MainPageController::mainPage' _title: 'Main Page' first: '' second: '' requirements: _permission: 'access content' first: Y|N second: \d+ Entity Parameters

As in Drupal 7, when creating a route that has an entity id you can set it up so the system will automatically pass the entity object to the callback instead of just the id. This is called ‘upcasting’. In Drupal 7 we did this by using %node instead of %. In Drupal 8 you just need to use the name of the entity type as the parameter name, for instance {node} or {user}.

example.main_page_controller: path: '/node/{node}' defaults: _controller: '\Drupal\example\Controller\MainPageController::mainPage' _title: 'Node Page' requirements: _permission: 'access content'

This upcasting only happens if you have type-hinted the entity object in your controller parameter. Otherwise it will simply be the value of the parameter.

JSON Callbacks

All the above code will create HTML at the specified path. Your render array will be converted to HTML automatically by the system. But what if you wanted that path to display JSON instead? I had trouble finding any documentation about how to do that. There is some old documentation that indicates you need to add _format: json to the YAML file in the requirements section, but that is not required unless you want to provide alternate formats at the same path.

Create the array of values you want to return and then return it as a JsonResponse object. Be sure to add ”use Symfony\Component\HttpFoundation\JsonResponse” at the top of your class so it will be available.

/** * @file * Contains \Drupal\example\Controller\MainPageController. */ namespace Drupal\example\Controller; use Drupal\Core\Controller\ControllerBase; use Symfony\Component\HttpFoundation\JsonResponse; class MainPageController extends ControllerBase { public function mainPage() { $return = array(); // Create key/value array. return new JsonResponse($return); } } Access Control

Hook_menu() also manages access control. Access control is now handled by the MODULE.routing.yml file. There are various ways to control access:

Allow access by anyone to this path:

example.main_page_controller: path: '/main' requirements: _access: 'TRUE'

Limit access to users with ‘access content’ permission:

example.main_page_controller: path: '/main' requirements: _permission: 'access content'

Limit access to users with the ‘admin’ role:

example.main_page_controller: path: '/main' requirements: _role: 'admin'

Limit access to users who have ‘edit’ permission on an entity (when the entity is provided in the path):

example.main_page_controller: path: '/node/{node}' requirements: _entity_access: 'node.edit'

See Drupal.org documentation for more details about setting up access control in your MODULE.routing.yml file.

Hook_Menu_Alter

So what if a route already exists (created by core or some other module) and you want to alter something about it? In Drupal 7 that is done with hook_menu_alter, but that hook is also removed in Drupal 8. It’s a little more complicated now. The simplest example in core I could find was in the Node module, which is altering a route created by the System module.

A class file at MODULE/src/Routing/CLASSNAME.php extends RouteSubscriberBase and looks like the following. It finds the route it wants to alter using the alterRoutes() method and changes it as necessary. You can see that the values that are being altered map to lines in the original MODULE.routing.yml file for this entry.

/** * @file * Contains \Drupal\node\Routing\RouteSubscriber. */ namespace Drupal\node\Routing; use Drupal\Core\Routing\RouteSubscriberBase; use Symfony\Component\Routing\RouteCollection; /** * Listens to the dynamic route events. */ class RouteSubscriber extends RouteSubscriberBase { /** * {@inheritdoc} */ protected function alterRoutes(RouteCollection $collection) { // As nodes are the primary type of content, the node listing should be // easily available. In order to do that, override admin/content to show // a node listing instead of the path's child links. $route = $collection->get('system.admin_content'); if ($route) { $route->setDefaults(array( '_title' => 'Content', '_entity_list' => 'node', )); $route->setRequirements(array( '_permission' => 'access content overview', )); } } }

To wire up the menu_alter there is also a MODULE.services.yml file with an entry that points to the class that does the work:

services: node.route_subscriber: class: Drupal\node\Routing\RouteSubscriber tags: - { name: event_subscriber }

Many core modules put their RouteSubscriber class in a different location: MODULE/src/EventSubscriber/CLASSNAME.php instead of MODULE/src/Routing/CLASSNAME.php. I haven’t been able to figure out why you would use one location over the other.

Altering routes and creating dynamic routes are complicated topics that are really beyond the scope of this article. There are more complex examples in the Field UI and Views modules in core.

And More!

And these are still only some of the things that are done in hook_menu in Drupal 7 that need to be transformed to Drupal 8. Hook_menu is also used for creating menu items, local tasks (tabs), contextual links, and form callbacks. I’ll dive into the Drupal 8 versions of some of those in a later article.

More information about this topic:

Better, then Bigger: Cultivating the Drupal Community

Each year at the largest Drupal conferences in the world, Dries Buytaert, the creator and project lead of Drupal, presents keynotes about the current “State of Drupal.” These events are well known in the Drupal community as “Driesnotes” (Dries, obviously influenced by Steve Jobs, has quoted Jobs in his keynotes and has even ended multiple Driesnotes with “one more thing,” much like a Stevenote). Frequently, Dries will clarify ideas from his keynotes on his personal blog, which was the case in a post from last year titled “Scaling Open Source communities.” While his title suggests a focus on open source software, his more immediate ambition is scaling Drupal. Indeed, Dries conflates “Drupal” with “Open Source” in his article, concluding, “we can scale Drupal development to new heights and with that, increase Open Source’s impact on the world.” Dries would like to grow Open Source (he likes to capitalize these words) by growing Drupal.

It was certainly not the first discussion about scaling the Drupal community, but when Dries first made his case for “scaling” in Amsterdam in 2014, many seasoned Drupalers immediately realized this was not a typical Driesnote. Dries referenced a variety of economic theories, covering topics such as “public goods,” the “free rider problem,” “self-interest theory,” “collective action theory,” “selective benefits,” and “privileged groups.” He was not talking about the average number of times Drupal had been downloaded each day or charting the number of contributed modules, as he often did in previous “states of Drupal” talks. Dries was engaging in analysis. He warned the audience that he had been reading “lots of economic papers,” admitted that “there’s an academic hidden inside me,” and pleaded, “please don’t fall asleep.” That such a talk required these disclaimers revealed the level of patience our community typically has had for academically-oriented analysis. Dries proceeded in his keynote (but not his blog post) to cite peer-reviewed articles from the economist Paul Samuelson and the American ecologist Garrett Hardin, and he extrapolated ideas from economist Mancur Olson’s well-known 1965 book, The Logic of Collective Action. Rather than just see Dries presenting, the audience witnessed Dr. Buytaert historicizing. The British journalist Will Self once remarked, “Visionaries, notoriously, are quite free from ratiocination and devoid of insight.” With his new ideas based on economic theories, Dries contested the stereotype. The reaction from the community was generally positive, with his talk garnering such accolades as “historic” and “the best Dries keynote ever.”

This Driesnote signaled a more nuanced critique from an entrepreneur more accustomed to discussing books about Drupal than books from academic presses. More important, since Dries started promoting his ideas about economic theory, some of what he suggested has become reality. It probably comes as little surprise that this “benevolent dictator” can get things done. On his blog and in his talks he suggested various changes to Drupal.org, such as improved organizational profile pages featuring more statistics and adding corporate attributions to individual commit credits in Drupal code (a topic he had blogged about previously). He offered other concrete suggestions that might very well still be in the works, including new advertising opportunities on Drupal.org in exchange for fixing bugs, the opportunity for organizations to get better visibility on the job board, and the ability to sort the marketplace page by contributions rather than just alphabetically. All of his suggested improvements were technical in nature, and ostensibly designed to benefit organizations.

To his great credit, Dries maintains an openness to other ideas. In his Amsterdam keynote, Dries said, “these are not final solutions. These are just ideas, and I hope they will be starting points for discussion” (34:53). He said, “this is just me brainstorming” (43:49), and that we should keep working at building our community, “even if it takes us years to get it right” (47:27). Accordingly, here I try to adopt a constructivist approach that adds to – rather than subtracts from – what Dries suggested. I am here to assemble, not debunk. I bring the same attitude that I aspire to when dealing with any of my colleagues, which is to assume positive motivation – I assume that Dries, like my co-workers, has good intentions (others of Dries’s critics seem to forget this). Like Dries, I care deeply about the Drupal community and I would like to understand more about the problems we face, what Drupal means, and how various changes might affect our community dynamics. In the remainder of this article, I will spend most of my effort dissecting Dries’s suggestions, the logic behind them, and how they compare to the theories of the economic theorists he cites. Finally, I will offer a few of my own suggestions. I believe that we will be most successful not merely by convincing more people to work with us through technological manipulations, but instead by focusing on improving interactions within the community and a goal of cultivating social solidarity. In other words, I will argue that instead of using technology to grow our community, we should focus our efforts on adjusting our culture in order to improve our technology.

What Is the Problem?

Before we can discuss solutions, we should consider the problems that need solving. Dries mentions generalized goals of attracting “more contributors” to the Drupal project in order to “try more things and do things better and faster,” without interrogating what “better” means or why “faster” should be a goal. His solutions seem to suggest that we should lure organizations to get more involved by hiring Drupal core developers, although Dries admits that “hiring Drupal talent is hard.” That Dries does not make explicit the benefits of growing the community beyond increasing our capacity to do things “better and faster” indicates that he understands the problem to be obvious. But is the problem actually that straightforward? Does bigger mean better? Should we consider goals beyond growing the community?

Evgeny Morozov, a rigorous thinker with a combative style, would label Dries’s approach “solutionism.” In To Save Everything, Click Here: The Folly of Technological Solutionism, Morozov writes, “Solutionism presumes rather than investigates the problems that it is trying to solve” (6). Morozov is frustrated by the prevalence of solutionism in technology debates and he dislikes any debate that presupposes the inherent worth of technologies such as “the Internet” (nowadays, Morozov always puts “the Internet” in scare quotes) or Open Source. I agree, and for our purposes, we should not assume that scaling open source, or Drupal, is a venture of unquestionable worth. Do we grow Drupal for social reasons? Are we politically motivated? Is this activism? Is this a philosophical debate? Or should we all just assume economic motivations? Whatever the impetus, I feel that when we talk about growing Drupal, we should not approach this activity as one with absolute value.

One potential benefit might be to increase business. Perhaps Dries feels it unnecessary to explain his motives. Dries is not just a developer, he’s also a successful entrepreneur. Dries discusses his ideas about “scaling” on his blog, which is also the place where he posts his annual “retrospectives” about his Drupal company, Acquia. In other words, Dries uses his blog not just to share personal information and news related to the Drupal project, he also uses his blog for business. So it seems quite probable that he wants to do more than grow the community, and that his goal is also to grow his company. Dries has fully committed himself to Drupal, and as the value of the Drupal software increases, so does the value of his Drupal company. One can hardly fault someone who has to answer to investors and who seeks to take his company public.

Another possibility is that Dries needs to defend his company. Dries is keenly aware that Acquia contributes disproportionately more to the Drupal project than any other company, and he understandably seeks to change this situation. Indeed, multiple times during his presentation Dries discusses the ratio of contributors. Dries says that it is “all about this ratio” (26:29 minutes into his talk) and that changing the ratio “will fundamentally change the dynamics of the community” (26:40). I agree with the latter part of his suggestion in that growing the community beyond Acquia will ease the “exploitation” of Acquia. While “exploitation” may seem a bit strong in this context, I borrow this word from one of Dries’s primary informants, Mancur Olson, who uses it repeatedly in The Logic of Collective Action. Olson believes there exists a “systematic tendency for ‘exploitation’ of the great by the small” (29). So applying Olson’s idea to Dries’s subject, we could understand why Acquia – run by the founder of Drupal, offering Drupal services, and employing more Drupal contributors than any other organization – has to carry the most weight. We should not feel too bad, however, because while it may be that Acquia contributes disproportionately to Drupal, it is also true that Acquia benefits disproportionately as Drupal gets better. Arguably, Dries and his company have the most to gain when others participate in Drupal.

While Acquia grows with Drupal, there are certainly many others in the Drupal community that stand to benefit as well, especially the many other Drupal “agencies” (including Lullabot, where I work) as well as Acquia’s many partners. Dries writes, “my company Acquia currently employs the most full-time contributors to Drupal but does not receive any exclusive benefits in terms of monetizing Drupal. While Acquia does accrue some value from hiring the Drupal contributors that it does, this is something any company can do.” Certainly another part of Dries’s project is to entice Drupal agencies to contribute. But doesn’t this happen already? Don’t agencies understand that the prospect of scaling Drupal will lead to more clients and that it is in their best interest to contribute to Drupal? This topic of individuals contributing to groups is, in fact, one of the main subjects in Olson’s book, with his main point being that as groups get larger, rational individuals are less likely to participate. Olson’s thesis is that in large groups, “rational, self-interested individuals will not act to achieve their common or group interests” (2). Consequently, it is not difficult to understand why Dries was drawn to Olson’s theories – Olson’s study offers multiple perspectives on why individuals do not contribute to large groups, which Dries can use to help understand why all of these Drupal agencies do not fund as many Drupal core developers as Acquia. Even better, it offers multiple ideas about how to entice these agencies to help make Drupal “better and faster.”

While it would seem that Dries is focused primarily on growing Acquia and other Drupal businesses, he would also like to attract individuals. He later clarified his goal in a comment of a curious blog post, maintaining that he “proposed ways to increase the social capital of both individuals and organizations.” His most immediate goal is not, in fact, to “scale Open Source.” Rather, he seeks to encourage individuals and organizations to contribute to Drupal. And from Olson, Dries learns more methods for coercion, another term that Olson uses frequently in his book. Olson believes that members of a group will not “act to advance their common or group objectives unless there is coercion to force them to do so, or unless some separate incentive, distinct from the achievement of the common or group interest, is offered to the members of the group individually on the condition that they help bear the costs or burdens involved in the achievement of the group objectives” (2). Olson talks at length about various types of incentives – social, selective, economic, etc. – that would make participation in a group more rational.

It can be quite tricky to grok the motivations of the organizations and individuals that contribute to the Drupal project. Olson focuses primarily on individuals who are rational and self-interested. Olson’s subjects are individuals that “rationally seek to maximize their personal welfare” (2). In a similar manner, Dries believes “modern economics suggest that both individuals and organizations tend to act in their own self-interest,” even as he admits that contributions to Drupal are “often described as altruistic.” Dries’s discussion of “self-interest” hints at the difficulties in characterizing the motivations for participating in our community, and the need for subtlety. Especially with Drupal agencies, it can be difficult to generalize motivations. For example, I was recently talking with a senior member of the Drupal community, and a former Lullabot employee, who described Lullabot as a “lifestyle company” that seemingly puts the needs of its employees ahead of profit, and that is extremely selective when evaluating potential projects. His description of Lullabot feels apt, and by no means exclusive to Lullabot. Four Kitchens takes a similar approach with its employees by striving to cultivate a “culture of empowerment.” Think Shout goes a step further, having recently become a B Corp, which is a type of for-profit company that is required to make a positive impact on society and the environment. Or consider Enjoy Creativity, a nonprofit organization – required to act for the public good – that builds Drupal sites for churches and ministries. These kinds of Drupal agencies seem motivated by goals that are different from – if not in conflict with – traditionally capitalist goals where “the common good” is, as Ayn Rand put it, “merely a secondary consequence.” We might conclude that sometimes we just help others for no rational reason, and that, as Nietzsche famously observed, “in everything one thing is impossible: rationality.” Or we might adopt a more optimistic view, as Dries did in The Next Web, and conclude that “capitalism is starting to become more collaborative rather than centered around individual ownership.”

So it seems there exists a wide variety of potential problems that need solving, although few of them feel urgent. First, Acquia has too much control. One of our goals should be to ensure that Drupal is understood as institutionally independent and that no single company dictates its future. I agree with Dries in that we should work to change the ratio of developers contributing code to Drupal. Second, we should ensure that Drupal continues to welcome a wide variety of individuals and organizations, both those that have the resources to contribute to core and those that do not. Drupal must not be construed as something only for business. After all, we do not know if Drupal will survive without individual contributors. Finally, we should strive to adapt to change and continue to make decisions that are understood as welcoming as well as benefiting a broader community. It is fine to desire more individual and organization participation, but not if that means alienating significant groups within the community. In other words, rather than asking if a change will grow the Drupal project, we should ask if it will improve the Drupal project.

Drupal: Public Good or Collective Good?

Perhaps even more confounding than articulating Drupal’s problems is the task of determining what Drupal is. Previously I explored the “cultural construction of Drupal” and various narratives about Drupal in our community. Dries offers yet another narrative when he states clearly his belief that “Open Source projects are public goods.” He arrives at this conclusion because he feels that open source meets the two relevant criteria of “non-excludability” (“it is impossible to prevent anyone from consuming that good”) and “non-rivalry” (“consumption of this good by anyone does not reduce the benefits available to others”). Again, this is Dries borrowing from economic theory, and on the surface this seems like a useful way of thinking about Drupal, as well as free software.

Dries’s use of the term “public good” is not problematic in that a large body of research uses this term. However, returning to Olson, it seems unlikely that he would call Drupal a “public good” and that he would have instead used the term “collective good” or “common good.” Olson characterizes public goods as government goods. I arrived at this conclusion not only because Olson based his research on Paul Samuelson’s work – whose essay mentioned “collective,” not “public,” goods – or because the title of Olson’s book is the Logic of Collective Action, but also because Olson made statements like this in his book: “The common or collective benefits provided by governments are usually called ‘public goods’ by economists” (14, emphasis added). Olson was actually quite specific about this distinction between “public” and “collective” goods: “A state is first of all an organization that provides public goods for its members, the citizens; and other types of organizations similarly provide collective goods for their members” (15). Even so, Dries very clearly compared Drupal to other public goods that eventually became the purview of the government – on one slide he placed Drupal alongside roads, schools, parks, streetlights, and defense. He was clear that each of these goods went from “invention” to “product” to “utility,” and that each was controlled by “volunteers,” then “business,” then “government.”

While Dries certainly was not suggesting that the government take over control of Drupal, it seems a curious choice to compare Drupal to government projects. It makes for an interesting thought experiment to consider what happens when we understand Drupal as a public good, controlled by the government. Olson’s study, after all, concerns groups (representing individuals) that work for common interests. So to begin, it would make sense that the Drupal Association – a nonprofit “dedicated to helping the open-source Drupal CMS project flourish” – would be the group that represents we, the Drupal community. Partially akin to one of Olson’s labor unions, the Drupal Association works for a common interest. But what branch of the government would control Drupal? Would this also be the Drupal Association, with Dries as its president, moving under the purview of the government? Or would the government hire the core committers, with Dries still in his role as the benevolent dictator? But enough of that. One could (and should) object that comparing Drupal to the government is obtuse, and that it suggests a kind of economic determinism in which Drupal would become a government utility, which is clearly not anyone’s goal. I would agree, and while this may seem silly to construct a building with the lights on and nobody home, it helps to reveal what we already know – we do not actually want the government involved in Drupal. Many people in our community, including Dries, do not really consider Drupal to be like other government projects. Drupal is our project. We make it what we want and we do not want to (nor can we, really) hand the keys over to the government.

So what happens when we shift the focus to free software as a “collective” good? To be clear, this use of “collective” does not signify “the collective life of humanity” (as Philip Gilbert Hamerton once put it), but rather a group of individuals acting together. Conceiving of Drupal as a “collective” rather than a “public” can be helpful for a variety of purposes. For one, it helps to explain why Holly Ross, the executive director of the Drupal Association, talks openly and thoughtfully about why she is starting to question whether the most appropriate tax classification for the Drupal Association is 501c3 – an organization that exists for the public good – or if it should more appropriately be classified as a 501c6, a trade organization whose purpose is to grow the businesses that support it. While I was quite taken aback when she admitted this to me, I can understand the thesis. It seems quite likely that our community is moving away from the notion of Drupal as something for the public and instead something for our collective. The internal deliberations of the Drupal Association are yet another indication that our group is gradually becoming more business focused.

In the end, it does not especially matter if Drupal is a public good or a collective good if our focus is on improving the Drupal project. Our group, like the large organizations that Olson analyzes, is growing not just in members and contributors, but also in complexity of problems. We have a wide variety of ways to understand our community and its corresponding problems. A growing percentage of our membership is both self-interested and economically motivated, while other factions lean toward the selfless or the seemingly irrational. How one understands our community, and the problems that need solving, greatly informs how we go about finding solutions.

The Trouble with Technical Fixes

Dries likes to fix problems with technology because, like countless entrepreneurs before him, Dries has great faith in technology. He writes, “We truly live in miraculous times. Open Source is at the core of the largest organizations in the world. Open Source is changing lives in emerging countries. Open Source has changed the tide of governments around the world.” Talk of “miraculous times” is a bold assertion. It’s also an example of an attitude that Morozov, that pugnacious and insightful technology critic, describes in his book, To Save Everything, Click Here, as “epochalism”, or “to believe one is living in truly exceptional times” (36). The problem with this attitude, Morozov claims, is that it leads to unhealthy beliefs about technology. What Dries claims for Open Source is quite similar to what others had envisioned for the telegraph, radio, telephone, television, personal computers, and countless other technologies, which Morozov takes up in his book, The Net Delusion. Morozov cites a bevy of ideas with eerily familiar conjecture, some of which are worth noting here. For example, in 1858, a New Englander editorial proclaimed: “The telegraph binds together by a vital cord all the nations of the earth” (276). In 1921 the president of GE predicted that radio would be “a means for general and perpetual peace on earth” (278). And just a few years later, the New York Times critic Orrin Dunlap would foresee that “Television will usher in a new era of friendly intercourse between the nations of the earth” (280). Fast forward to 2014 and we read Dries making similar prognostications about open source changing organizations and governments. This belief in technology entices us into using it for new purposes.

Dries’s choice of a technical solution is confusing. In addition to works by Samuelson and Olson, Dries cites in his keynote a well-known article by Garrett Hardin titled “Tragedy of the Commons” (20:45). Dries is vague about how he understands this article (he accidentally calls it a “book”), which makes it all the more curious why he would mention it. The epigraph of the article reads, “The population problem has no technical solution; it requires a fundamental extension in morality” (emphasis added). What is more, Hardin’s first sentence contains the following quotes from Weisner and York: “It is our considered professional judgment that this dilemma has no technical solution. If the great powers continue to look for solutions in the area of science and technology only, the result will be to worsen the situation” (1243). Hardin was bullish in his suspicion of “technical solutions,” reiterating his position four years later in the preface to his 1972 book, Exploring New Ethics for Survival: “For too long have we supposed that technology would solve the ‘population problem.’ It won’t.” Like Morozov, Hardin is suspect of technical fixes to complex problems. Since Hardin’s essay focused on “a class of human problems” that he described as “no technical solution problems,” perhaps there was another aspect that Dries found helpful.

Hardin, who contends “it takes courage to assert that a desired technical solution is not possible” (1243), had agonized over how to convey his conclusions. Hardin also believes that we cannot succeed by appealing to conscience or by making people feel guilty. Hardin, like Olson, speaks of “coercion” to counteract the effects of self-interest. Hardin recommends that we cannot appeal to a person’s “sense of responsibility” but rather that in order to make changes we instead need a “mutually agreed upon coercion,” and that it may require infringing on personal liberties. He talks of the need to take away freedoms for the common good. Thirty years later, in 1998, Hardin would describe the ideas he presented in his earlier essay as his “first attempt at interdisciplinary analysis.” He felt that he was trying to solve a problem so large – the human overpopulation problem – that he could not employ simple, technological fixes, and that it would be necessary to draw on conclusions derived from multiple disciplines. People, not computers, would have to work together.

Moreover, there are pitfalls with technological fixes beyond what Hardin construes (and again, I draw inspiration from Morozov and others). For example, introducing technological fixes can irritate existing social conflicts. Organizations that have long flourished in the Drupal community might be embarrassed by the new profile pages and might be less inclined to contribute, not more. Technological fixes can also distract, or act as mechanisms for denying the existence of deeper social problems – higher listings on the marketplace page, for example, will not distract individuals and organizations that are upset by Acquia’s sales techniques or who have concerns about its influence on Drupal Association webinars. When technological fixes do not work, they can have the effect of making us think that we just need a different technological fix. Dries seems to express just this attitude when he writes, “There are plenty of technical challenges ahead of us that we need to work on, fun ideas that we should experiment with, and more.” If these are intellectually challenging problems that require serious discussions, and not just “fun ideas,” we will never get to the point of solving our problems.

Perhaps the most troublesome trait of technological fixes is when they close down thoughtful contributions by people with knowledge about addressing social and political problems. Dries broaches the topic of “social capital” in his Amsterdam keynote (28:24), saying, “this is where we are good.” But he follows that up, suggesting that “altruism” and “social capital” are not scalable (31:37) and that these are not solutions for Drupal (31:55). Why close down discussion of these topics and abandon ideals that have served the community so well? What would happen if, rather than discarding these modes of investigation, we dug deeper to find alternative answers? What if the solution to scaling Drupal lies not with technology? What if, rather than use technology to change our community and culture we reverse our efforts and instead focus on making adjustments to our culture in order to improve our technology? Or perhaps we should only consider technological fixes that support broader efforts to improve the Drupal project rather than simply grow it? As the German philosopher Martin Heidegger put it, “the essence of technology is by no means anything technological.”

Sprouting Social Solidarity

When I suggest we need to look beyond technical solutions, this is not necessarily contra Dries. In another, much shorter blog post on “Open Source and social capital” – posted less than a month before his post about “scaling Open Source” – Dries concluded, “social capital is a big deal; it is worth understanding, worth talking about, and worth investing in. It is key to achieving personal success, business success and even happiness.” Plus, Dries has written about “fostering inclusivity and diversity” on his blog. Like Dries, I do not believe that there is only one way to grow the Drupal community. We can use technology to support our broader goals, so discarding all technological fixes is not my objective. Rather, I am suggesting an approach to cultivating our community that mirrors how we make changes to Drupal code – we carefully consider how each change will improve the overall project, never assuming that more automatically means better.

What is more compelling to me than technological fixes is to examine how Drupal and cultures around the globe shape each other, and how we can create more situations where more individuals make the choice to start participating in our community. This mode of investigation requires a multidisciplinary approach, a broader understanding not just of economic transactions, but also human interactions. I agree with Lars Udéhn’s assessment that “Olson’s theory of collective action has proved inadequate and must be replaced by a theory assuming mixed motivations” (239). The last time I checked, Drupal’s unofficial slogan is not “come for the code, stay for the economy” – it’s about community, and that is where I believe we should concentrate our efforts.

While I found Dries’s turn to analysis refreshing, I also question that his informants offer the most helpful of ideas. Hardin changed his mind many times over his career, a fact he readily admits, so it would seem reasonable to explore his later ideas. Samuelson’s article, with its thick prose and mathematical formulas, feels quite unrelated to Drupal. It seems reasonable that someone like Dries, with all his responsibilities, should not be compelled to trace the history of theories of rationalism and social action from Aristotle to Descartes to Kant to Max Weber and beyond – especially not in a single Driesnote. If only we did not have those pesky clients to help and 529s to fund, we could explore more of these ideas. All of this is but a reminder that Dries’s sources are certainly not the last words on these subjects.

Not to pick on Olson, but it also does not seem as though he considered the full force of social solidarity to Marx’s thinking about motivation. In his Economic and Philosophical Manuscripts, Marx writes of workers who get together to further their shared goal, “but at the same time, they acquire a new need – the need for society – and what appears as a means had become an end.” For Marx, building relationships was another form of production, and social solidarity was a key component for bringing about change (for more detailed critiques of Olson’s interpretation of Marx, see, for example, Gomberg or Booth below). Likewise, social solidarity is a significant force in the Drupal community. In my local Drupal community we not only have a monthly “user group” meeting, but every month we also have a “jam session” (coder meetup), community “lab hours,” and a social meetup at a bar. Many individuals in our community help organize the Twin Cities DrupalCamp, attend the nearby DrupalCorn or DrupalCamp Midwest, and travel to the annual North American DrupalCon (DrupalCons, organized by the Drupal Association, are the largest Drupal conferences in the world). The people we interact with become important to our lives – not just collaborators, but friends. “It is not the consciousness of men that determines their existence,” Marx wrote in A Contribution to the Critique of Political Economy, “but their social existence that determines their consciousness.”

We, as a community, would benefit from questioning our own unexamined beliefs, no matter what discomfort it may cause. We should continue to ask if successful programs like D8 Accelerate – a project that funds Drupal core development through grants – truly benefit our community, or if they might foster a collective motivated primarily by money. The way Drupal production is organized affects our understanding of it, and how we choose to coerce individuals matters. While many would prefer economic incentives and hard science over humanities, some in our community are marginalized and brushed aside by such priorities. Perhaps we will determine that it is in our best interest to ensure that our community exists more for the public than for our collective. It could be that programs like D8 Accelerate negatively affect solidarity.

If IRC and issue queues online beget lively debates at conferences and code sprints in person, we should continue to examine each of those interactions. For instance, I agree with Larry Garfield when he writes, “The new contributor first commit is one of #DrupalCon’s most important rituals.” At the end of a week-long conference, the community code sprint occurs on the final day. During this sprint, veteran Drupalers train new contributors about the peculiarities of contributing code to the Drupal code base. Near the end of the day, one or more lucky individuals are picked to go in front of everyone else where Dries commits that person’s contribution. Personally, it was this event that got me hooked on the Drupal community. This symbolic act welcomes people to our community, demonstrates their worth, and gives future contributors some extra motivation as they work toward finding problems to solve.

Moreover, we should promote a wide variety of events, encouraging more meetups, social events, and quasi-productive gatherings where code and conversation flow freely. The Drupal Association has already made steps in the right direction when they announced the results of their survey and their resulting “new approach to community at DrupalCon.” The community theme in this announcement was comprehensive: “Community Keynote,” “Community Kickoff,” “Community BoFs,” “Community Training,” and “Community Sprints.” One could argue that the Drupal Association is bringing these activities back, and that previously they did not require the “community” prefix.

Finally, I hope to see more thoughtful writing from our community about our community. The complexity of our community makes this a difficult task for an outsider. In addition to recommendations about “how to configure a View” and “how to make a page load faster” on Planet Drupal, many of us would like to know how other local Drupal communities work. What has been successful? How do they grow their membership? What does it mean to grow membership? The problem is not that we never discuss these issues, it is that we tend not to interrogate these issues more thoroughly “in print.” Drupal Watchdog is a step in the right direction, with its slightly longer form articles that allow the community to share their ideas in a more considered manner than a traditional blog post. While sharing ideas is nice, it can be even more helpful to share our ideas after they have been improved by an editor. While there are many issues of Drupal Watchdog that contain content that I find less engaging, I am glad that it allows for a wider range of voices.

All too often we incorrectly describe Drupal as a means to an end, detached from a political agenda. We forget that organizations use Drupal not only as a tool, or even because of the community. Some organizations, such as the Free Software Foundation, clearly choose their software, including Drupal, for philosophical reasons first. Or consider the American Booksellers Association (ABA), an organization engaged in political and trade-related efforts geared toward helping independently owned bookstores. The ABA’s hundreds of Drupal websites represent just one component of their larger political project. Like so many nonprofits, the ABA has a staff of passionate individuals dedicated to the cause, and their conception of Drupal must not conflict with their ideals. Consequently, I would like to see more posts on Planet Drupal that test the boundaries of the guidelines, which discourage “posts that don’t provide valuable, actionable content.” It would be nice to see more thoughtful articles that discuss political agendas and activities, and then describe how Drupal supports those activities. Countless people are inspired to use Drupal for reasons that have nothing to do with technology, and we should consider encouraging more of these stories.

While I have many other ideas that I am tempted to suggest here, those ideas are more properly topics for another article. That said, I think we can certainly benefit from studying other free software communities. When I was sitting in the audience for the DriesNote at DrupalCon Los Angeles in May, I suggested on Twitter that it “sounds like @Dries gets lots of inspiration from proprietary products (Pinterest, Pandora), rather than from other free software.” Dries later saw my tweet and clicked the “Favorite” button. I think we would benefit not just from discussing other free software projects, but also interrogating the thinking about them. The kind of scholarship that I have found most illuminating is not that of economists, but rather work like Gabriella Coleman’s anthropological studies of the Debian community and groups associated with Anonymous, as well as Christopher Kelty’s ethnographic research into free software. There is a great deal to be gained by considering our ideas about Drupal in light of what we know about the Linux community, the Fedora project, OpenStack, and other large free software communities, while acknowledging that the Drupal community is complex and that there are no easy answers or solutions.

The distinguished literary theorist Terry Eagleton has remarked, “most people are too preoccupied with keeping themselves afloat to bother with visions of the future. Social disruption, understandably enough, is not something most men and women are eager to embrace” (194). I understand why many in our community would not be quick to embrace any sort of radical change, but I also think it’s important that we talk about these issues. We cannot offload all of our problem solving to technology. To change what we think, we must change what we do. Making the case that the Drupal project should focus on its community and culture might seem less exciting the innovative technical solutions, but I hope to have highlighted just a few of the approaches to understanding our community that could prove beneficial, and that we should be careful as we consider which of them to adopt. Dries, in his recent turn to historicizing, is on the right track, and I hope the conversation continues.

Works Cited

Booth, Douglas E. “Collective Action, Marx’s Class Theory, and the Union Movement.” Journal of Economic Issues 12 (1978): 163-185.

Coleman, Gabriella. Coding Freedom: The Ethics and Aesthetics of Hacking. Princeton: Princeton University Press, 2012.

Coleman, Gabriella. Hacker, Hoaxer, Whistleblower, Spy: The Many Faces of Anonymous. New York: Verso, 2014.

Eagleton, Terry. Why Marx Was Right. New Haven: Yale University Press, 2011.

Gomberg, Paul. “Marxism and Rationality.” American Philosophical Quarterly 26 (1989): 53-62.

Hamerton, Philip Gilbert. The Intellectual Life. New York: Macmillan, 1875.

Hardin, Garrett. Exploring New Ethics for Survival: The Voyage of the Spaceship Beagle. New York: Viking Press, 1972.

Hardin, Garrett. “Extensions of ‘The Tragedy of the Commons.’” Science 280 (1998): 682-683.

Hardin, Garrett. “The Tragedy of the Commons.” Science 162 (1968): 1243-1248.

Heidegger, Martin. The Question Concerning Technology and Other Essays. New York: Garland, 1977.

Kelty, Christopher. Two Bits: The Cultural Significance of Free Software. Durham: Duke University Press, 2008.

Morozov, Evgeny. The Net Delusion: The Dark Side of Internet Freedom. New York: PublicAffairs, 2011.

Morozov, Evgeny. To Save Everything, Click Here: The Folly of Technological Solutionism. New York: Public Affairs, 2013.

Nietzsche, Friedrich. The Portable Nietzsche. Translated by Walter Kaufmann. New York: Penguin Books, 1977.

Olson, Mancur. The Logic of Collective Action: Public Goods and the Theory of Groups. Cambridge, Mass.: Harvard University Press, 1971.

Rand, Ayn. Capitalism, the Unknown Ideal. New York: New American Library, 1966.

Samuelson, Paul. “The Pure Theory of Public Expenditure.” The Review of Economics and Statistics 36 (1954): 387-389.

Self, Will. How the Dead Live. New York: Grove Press, 2000.

Udéhn, Lars. “Twenty-Five Years with ‘The Logic of Collective Action.’” Acta Sociologica 36 (1993): 239-261.

Pages