Upgrade Your Drupal Skills

We trained 1,000+ Drupal Developers over the last decade.

See Advanced Courses NAH, I know Enough
Sep 29 2016
Sep 29

We knew Ireland.ie (built by Annertech on Drupal) was a special website. The design is beautiful thanks to the amazing work of BigO Media, the content, media, and experience is second to none thanks to the the team in the Ireland.ie office at the Department of Arts, Heritage and the Gaeltacht. The implementation is without flaw (if we say so ourselves!).

Last night at the Realex Web Awards 2016 ireland.ie was nominated in two categories: "Best Arts and Culture" website and "Most Beautiful Website in Ireland". It won both. Were we happy? We were ecstatic but that was increased moments later when we won the Grand Prix, the overall winner for "Best Website in Ireland" 2016. Congratulations ireland.ie - winner of three awards!

As well as the above awards we (Annertech) won one more - WEB AGENCY OF THE YEAR 2016. That was just amazing!

Sep 26 2016
Sep 26

We're all very helpful people in the Drupal community and so help should easily be available. But sometimes you get caught out and can't find people nearby - you get lost, you lose your phone, you're in an area of town and haven't a clue how to get back to your home, you are locked out of your AirBnB, you've gone to kiss the Blarney Stone not realising it was 350km away!

If you need help with anything while in Dublin, please get in contact with us. We have lots of local knowledge (and a team of 15 people here for DrupalCon) willing to help you. You can contact us via:

email: [email protected]
email: [email protected]
phone: +353 (0)1 524 0312
twitter: @annertech
 

We'll be keeping a good monitor on all of the above and will do whatever we can to help anyone that needs it.

Sep 22 2016
Sep 22

DrupalCon Dublin is just around the corner (since I live in Ireland, I mean that literally!). DRUPALCON: HEAR ME ROAR! (or at least speak, along with some other Annertechies). At DrupalCon we'll be speaking on a number of topics (interesting aside: we're the only Irish agency with any speakers at this year's DrupalCon). Here's a quick roundup of our talks and why you won't want to miss them:

Speaker(s): Alan Burke and Aisling Furlong

Where: Liffey Meeting 2 | 10:45

Why come to this: You will learn what large organisations look for when selecting a CMS, how you can pitch Drupal as the CMS of choice, what competition Drupal has and how it fares against it. You do want insights from one of Ireland's largest multinationals don't you?

Speaker(s): Mark Conroy

Where: Wicklow Hall 2B | 14:15

Why come to this: Come get an overview of how to use Drupal as a decoupled system, how to use Ionic Framework (and AngularJS) to take the decoupled data to make a hybrid webapp that can run on iOS, Android, or the native web. Sorry Windows phone.

Speaker(s): Alan Burke

Where: Liffey Meeting 3 | 12:00

Why come to this: Learn from our insights gained building ireland.ie, where we built a co-lingual rather than multi-lingual website. In a co-lingual website all languages present are given equal importance. We made sure this was true on the frontend and the backend.

Speaker(s): Andrew Macpherson

Where: Liffey Meeting 4 | 10:45

Why come to this: Andrew, Drupal core maintainer for accessibility, is one of Drupal's most recent core maintainers. In this core conversation he will discuss the present state of accessibility of Drupal, where it might be improved, and how we can do so. Andrew is the only core maintainer working for an Irish agency.

Speaker(s): Anthony Lindsay

Where: Wicklow Meeting 1 | 10:45

Why come to this: Anthony, Annertech's lead support engineer, works from home (like the rest of us here at Annertech). Come listen to Anthony tell in his own inimitable style why remote working makes him happy.

That's our official speaking slots. Don't miss them, and don't forget to say hello to us at Booth 901.

Sep 21 2016
Sep 21

Without doubt, the greatest resource for information about Drupal frontend issues is the DrupalTwig Slack. There are over 300 of the world's most well known frontend Drupal developers - Wim Leers, MortenDK, Mark Drummond, David Hernandez, and more - offering and receiving help all day every day. It's very rare that someone asks a question and doesn't receive a reply with a solution within a few minutes, or at the very least a conversation about why the issue might be hard to solve.

That said, as a free Slack we do not have the ability to search through more than 10,000 posts at a time. With that in mind, I've decided to start compiling things I've learned or have helped others to learn on the channel so we can have a record of them. So here goes:

1. How to get the value of a link field in Twig in Drupal 8

Get the value of a link field in Drupal Twig

This is definitely the question that pops up most often. You'd be forgiven for thinking you could get it by printing {{ content.field_link }} but the field_link is an array, so you need to get the correct item from the array. It's simple when you finally crack it: {{ content.field.0['#url'] }}. Thanks to mdrummond for sorting this out. You can also find info about it on Stack Overflow.

2. How to create/alter global variables

Create/Alter global variables in Drupal 8

This was a question I had when one of our developers wanted to have a destination to the current URL in the path after a form was submitted. It's handy because it creates a variable that can be printed in any template. You can do this using the MYMODULE_template_preprocess_default_variables_alter(&$variables) hook (thanks to mikeker for pointing this out to me). The code:

You can then print {{ current_path }} in any template. Which brings us to item 3.

3. Themes cannot alter global variables

Themes cannot alter global variables

I could not get the above code to work when placing it in my THEME.theme file. As it turns out, global variables can only be run from a module, not a theme. There is an issue filed to fix this. Again, thanks to mikeker for clarifying this.

4. Use Preprocess to float image/text in paragraph bundles

Use Preprocess to float image/text in paragraph bundles

If you have a paragraph bundle for image and text and want to be able to let the editors choose to float the images left or right, this comes in handy. You can also manipulate it to allow editors to choose heading types from a select list, or the width of a bounding area for example.

In the above example, we have a select list field that the editor uses to say if they want the image left or right. In other paragraph bundles we have a heading text and heading level field, so editors can easily edit the heading style if they wish. Jim Birch shows a method of doing this purely in Twig in this gist.

5. How specific can you get with preprocess functions?

how specific can you get with preprocess functions

It turns out that hook_preprocess_field() is not as specific as you can get. As Chris Shattuck shows you can preprocess inside the entity type inside the bundle. So, mytheme_preprocess_field__node__body__page will preprocess the body field inside nodes of type page.

6. Preprocess functions only affect the render array that is sent into that render array’s template, they don’t affect the render array itself.

preprocess functions only affect the render array that is sent into that render array’s template, they don’t affect the render array itself.

Preprocess only affects the render array that it is called on. For example, if you change a field setting in hook_preprocess_field, that setting will not be changed in a node template. Yes, I'm still trying to figure this one out as well.

7. How to add template suggestions for custom blocks

How to add template suggestions for custom blocks

I presumed, like with custom content types, there would automatically be template suggestions for custom block types. But no. You can add them with the following code (amend to your liking):

The above code is an edited version of Jeff Burnz's solution from Drupal.org.

8. How to add bundle class to blocks

add bundle class to blocks

As with the above, I presumed custom blocks would have a class for the block type for theming. They don't. A few of us put our heads together and quickly came up with the above, after refining the thoughts of Greg Boggs.

add bundle class to blocks

Here's the code:

I haven't checked yet to see if that only works if the block has a body field. I suspect so.

9. Views exposed form block is not part of views-view.html.twig

Views exposed form block is not part of views-view.html.twig

If you have a view with some exposed filters that are in a block, the exposed filters cannot be printed via views-view.html.twig (without doing some preprocess work first).

That's just a very small selection of some of the things I've learned and/or helped solve in the DrupalTwig Slack. You should join us to help solve more (or get answers if you are stuck).

I'm guessing there are some better ways of doing some of these things. If you know them, please leave a comment.

Sep 20 2016
Sep 20

This day next week, as part of the Drupal Ireland Association, we will be delighted to welcome you to Dublin at the DrupalCon Welcome Party. It's on a boat, which is going to be deadly ("deadly" in this context means great, "lethal" would mean dodgy/dangerous!). The boat is just across the road from the convention centre, so that will be savage ("savage" of course means lots of fun). However, the ceiling inside the boat is only about 2 metres high, that's going to be a bit cat (let's just say "cat" doesn't mean brilliant).

So what is this hoolie (usually a loud party with lots of traditional instruments and improptu performances, but not in this case) all about? It's about the Drupal Ireland Association giving a céad míle fáilte (one hundred thousand welcomes) to the world when they arrive in Baile Átha Cliath (Dublin). It's about us showing gach duine (everyone) that we are a welcoming community of Drupalists in Ireland. That we are open to new people coming on board and helping out with what we do - we'd love to hear from Irish people using Drupal whom are not members of Drupal Ireland yet and also people from other tech scenes here.

The party is on the MV Cill Airne boat (Cill Airne is the Irish for Killarney - you won't be disappointed if you go there - and means the "church of the sloes"). It's starts at tea time (18:00) and goes on until serving time finishes (23:30 on a Tuesday). Come join mise (pronounced misha, means "me"), all the rest of the Annertech team, many others from the Drupal Ireland Association, and hundreds (sorry we can't fit thousands onto the boat) of other Drupalists at the DrupalCon Welcome Party to have a savage, deadly night of craic (fun).

Sep 13 2016
Sep 13

Annertech will be descending upon DrupalCon with (nearly) our full team of "Annertechies". So much so that there will be more Annertechies in attendance than all other people from Irish agencies combined. With that kind of showing, we thought we'd introduce ourselves and let you get to know us.

Stella Power

Stella is Ireland's Drupal wunderkind. Founder of the Drupal Ireland Association, member of the Drupal security team (the only person from an Irish agency with that accolade), and managing director of Annertech, Stella is the kind of person that doesn't come along often enough. Stella has spoken at many European DrupalCons; this year she was track chair for the business track and is also the local liaison for DrupalCon.

Alan Burke

Alan is also a director of Annertech. When not keeping the invoices raised and paid, he's focussing on being a top-notch frontend developer. In this vein, his main passion is for website performance. Alan has spoken at a lot of DrupalCons over the years; this year he will be speaking about developing a co-lingual website for ireland.ie and why a multinational organisation might choose Drupal.

Dermot Frost

Poor Dermot has a tough life. He won't be able to make it to DrupalCon as he'll be preparing for a conference the following week in Boston. When not jet-setting, Dermot spends a lot of time building and maintaining server infrastructures.

Anthony Lindsay

Anthony is our lead support engineer. He makes sure that all our existing clients are happy. For Annertech, support often means on-going development of new features and enhancements to existing ones. If you have an existing website and would like us to support it, come talk to Anthony at Booth 901. This year he will give a presentation at DrupalCon about how remote working makes him happy.

Mike King

Mike keeps us all in check. He's our project manager. He makes sure we know what the client wants and deliver it to them on time, on budget, and with smiles on our faces. He was track chair for DrupalCon this year on the project management track.

Mark Conroy

Mark - me! - is a lead frontend developer with Annertech. He's very interested in maintainable code, design in the browser, component-based frontend, and how those three can be brought together. You can find him (too) often in the DrupalTwig slack. He'll be presenting at DrupalCon about getting started creating mobile/hybrid apps using Drupal as a backend and Ionic Framework as a frontend. Mark is presently chairperson of the Drupal Ireland Association.

Tommy Lynge Jørgensen

Tommy is one of our lead backend developers. He knows a lot about solr, and migrations, and backing up data. He comes from Denmark, lives in Ireland, and is the reason we have cake at Booth 901. Come for the code, stay for the cake!

Andrew Macpherson

Andrew is another lead backend developer in Annertech. He is the only core maintainer working for an Irish agency, having recently been made a core maintainer for accessibility. So, if you want an accessible Drupal website built by an Irish agency, get in contact with us. He will conduct a core conversation on the future of Drupal accessibility at DrupalCon.

Gavin Hughes

Gavin is one of our support engineers. Whilst Anthony makes sure that all our clients are happy, Gavin is the one beavering away in the background actually doing the work!!! (Sorry Anthony!) When not debugging issues and developing new features, he's probably kite surfing somewhere off the west coast of Ireland.

Bren Vaughan

Bren joined us recently as a project manager. Having trained as a developer, he is slowly recovering from his past life. He's also slowly recovering from participating in Iron Man competitions and other feats of endurance unknown to the rest of Annertech. He's got so much recovery to do, he won't make it to DrupalCon this year.

Ricardo Flores Galán

Ricardo, from Spain, is our in-house designer and UX expert. He checks the fine details of designs for consistency, brand adherence, vertical rhythm, and more. Oh, and his wife gave birth to a beautiful baby boy yesterday. CONGRATULATIONS. Ricardo is presently secretary of the Drupal Ireland Association.

Rob McCreary

Rob lives in Northern Ireland. He joined Annertech almost a year ago and has been doing some fantastic frontend work for us. Previously having worked in the non-profit sector he is a great compliment to many of the types of clients Annertech has historically serviced.

Adrien Sirjacques

Adrien is one of our backend developers, and has worked on a number of projects to add new features to existing sites and also some greenfields work. Though from France, he's very active in the Irish Drupal community and can be found each month at our Drupal Ireland meetup in Dublin.

Tom Bamford

Tom is English. He lives in France where he attends to the meadow, country walks, sunshine (which apparently is rare in Normandy) and frontend development. We're lucky to have him on board given his vast knowledge of Drupal, JavaScript, SASS and other complimentary items.

Karen Leech

Karen is also English. She also lives in France, and attends to the same things as Tom. Except, instead of frontend development she is a site builder and QA Analyst. You know when you miss out on a client request by just a tiny item? Karen is our backup to notice that and makes sure nothing gets to UAT without passing her exacting standards.

So that's it. A quick roundup of the Annertechies you'll (likely) meet at DrupalCon. We'll be at Booth 901 and here's some reasons why you should come talk to us.

Sep 08 2016
Sep 08

DrupalCon is big. It's got hundreds of sessions. A similar amount of BoFs. Approximately 2,000 attendees. Social events left, right, and centre. It's not hard to get confused, miss things that you promised not to, and leave thinking "damn, I could have done that better". At Annertech, we're Ireland's most seasoned DrupalCon attendees. Here's our guide to making the most of it.

Create a Schedule

You can add any session to your own personal schedule on the DrupalCon website. You can do this right now. Do so. With so many great sessions running concurrently, it's very hard to work out on the spot what to go to next. You can find your personal schedule here.

Attend BoFs

At my first DrupalCon (in Prague) I went to the all day media sprint on Friday. I can't explain how much I learned that day - amazing. It was then I realised that I had missed out (and it was too late) on great opportunities by only going to sessions during the week. BoFs are where you get down and dirty with the innards of Drupal and related technologies and theories - accessibility, Drupal for Museums, Open Data, etc were just some of the ones I attended in Barcelona last year.

What's a BoF? It's a "birds of a feather" meeting. Basically, people with a common interest book a room and sit around discussing it. It's usually someone with A LOT of knowledge about a topic that does so. The format is very informal and friendly, just like a tutorial in college, with usually less than 20 people in attendance. This year I'll end up at about 50% BoFs and 50% sessions (or less).

Go Easy on the Alcohol

Yes, we know, you're in Ireland and Irish people like to drink and party. That's true, but you're going to be here for a week. Please don't go overboard on Monday and be wiped out for the rest of the week (not least becuase my session is on Tuesday and I'd like to see you there!).

During the week there will be lots of social events. You are welcome to all of them. But do not feel pressured to drink alcohol or to buy drinks for others. Be respectful of yourself and others and when leaving venues please do so quietly - there are lots of people trying to sleep.

Attend the Keynotes

If you go easy on the alcohol, this one is easier to achieve. The keynotes are where you'll learn about the state of Drupal and the plans for the immediate future of it from Dries. This will be followed by a Q&A with Dries, moderated by a local volunteer, where you get to tweet questions to him.

The other two keynotes will be hugely relevant talks from very respected individuals - Emer Coleman and Eduardo Garcia.

Contribute to Drupal

There will be loads of opportunities to contribute to Drupal by sprinting and/or mentoring. There will be extended sprints each weekend before and after DrupalCon. The conference hotel will have a 24 hour sprint room. Contributing is how Drupal gets built. Please contribute.

Take Time Out

With all the talking and sessions and BoFs and keynotes and contributing, it's okay if your brain is feeling a little over-worked. We have a beautiful city in Dublin. Take some time out, go for a walk. Visit some our recommended things to see and do in Dublin. Talk to some locals. Enjoy yourself (DrupalCon is about more than just work).

And if all that fails to help you get the most out of DrupalCon, well, you could just go on the DrupalCon diet!

Sep 06 2016
Sep 06

(This article, co-authored with Dries Buytaert, the founder and project lead of Drupal, was cross-posted on drupal.org, lullabot.com, and buytaert.net.)

There exist millions of Open Source projects today, but many of them aren't sustainable. Scaling Open Source projects in a sustainable manner is difficult. A prime example is OpenSSL, which plays a critical role in securing the internet. Despite its importance, the entire OpenSSL development team is relatively small, consisting of 11 people, 10 of whom are volunteers. In 2014, security researchers discovered an important security bug that exposed millions of websites. Like OpenSSL, most Open Source projects fail to scale their resources. Notable exceptions are the Linux kernel, Debian, Apache, Drupal, and WordPress, which have foundations, multiple corporate sponsors and many contributors that help these projects scale.

We (Dries Buytaert is the founder and project lead of Drupal and co-founder and Chief Technology Officer of Acquia and Matthew Tift is a Senior Developer at Lullabot and Drupal 8 configuration system co-maintainer) believe that the Drupal community has a shared responsibility to build Drupal and that those who get more from Drupal should consider giving more. We examined commit data to help understand who develops Drupal, how much of that work is sponsored, and where that sponsorship comes from. We will illustrate that the Drupal community is far ahead in understanding how to sustain and scale the project. We will show that the Drupal project is a healthy project with a diverse community of contributors. Nevertheless, in Drupal's spirit of always striving to do better, we will also highlight areas where our community can and should do better.

Who is working on Drupal?

In the spring of 2015, after proposing ideas about giving credit and discussing various approaches at length, Drupal.org added the ability for people to attribute their work to an organization or customer in the Drupal.org issue queues. Maintainers of Drupal themes and modules can award issues credits to people who help resolve issues with code, comments, design, and more.

example issue credit on drupal.orgA screenshot of an issue comment on Drupal.org. You can see that jamadar worked on this patch as a volunteer, but also as part of his day job working for TATA Consultancy Services on behalf of their customer, Pfizer.

Drupal.org's credit system captures all the issue activity on Drupal.org. This is primarily code contributions, but also includes some (but not all) of the work on design, translations, documentation, etc. It is important to note that contributing in the issues on Drupal.org is not the only way to contribute. There are other activities — for instance, sponsoring events, promoting Drupal, providing help and mentoring — important to the long-term health of the Drupal project. These activities are not currently captured by the credit system. Additionally, we acknowledge that parts of Drupal are developed on GitHub and that credits might get lost when those contributions are moved to Drupal.org. For the purposes of this post, however, we looked only at the issue contributions captured by the credit system on Drupal.org.

What we learned is that in the 12-month period from July 1, 2015 to June 30, 2016 there were 32,711 issue credits — both to Drupal core as well as all the contributed themes and modules — attributed to 5,196 different individual contributors and 659 different organizations.

Despite the large number of individual contributors, a relatively small number do the majority of the work. Approximately 51% of the contributors involved got just one credit. The top 30 contributors (or top 0.5% contributors) account for over 21% of the total credits, indicating that these individuals put an incredible amount of time and effort in developing Drupal and its contributed modules:

How much of the work is sponsored?

As mentioned above, from July 1, 2015 to June 30, 2016, 659 organizations contributed code to Drupal.org. Drupal is used by more than one million websites. The vast majority of the organizations behind these Drupal websites never participate in the development of Drupal; they use the software as it is and do not feel the need to help drive its development.

Technically, Drupal started out as a 100% volunteer-driven project. But nowadays, the data suggests that the majority of the code on Drupal.org is sponsored by organizations in Drupal's ecosystem. For example, of the 32,711 commit credits we studied, 69% of the credited work is “sponsored.”

We then looked at the distribution of how many of the credits are given to volunteers versus given to individuals doing "sponsored work" (i.e. contributing as part of their paid job):

contributions by top x

Looking at the top 100 contributors, for example, 23% of their credits are the result of contributing as volunteers and 56% of their credits are attributed to a corporate sponsor. The remainder, roughly 21% of the credits, are not attributed. Attribution is optional so this means it could either be volunteer-driven, sponsored, or both.

As can be seen on the graph, the ratio of volunteer versus sponsored don't meaningfully change as we look beyond the top 100 — the only thing that changes is that more credits that are not attributed. This might be explained by the fact that occasional contributors might not be aware of or understand the credit system, or could not be bothered with setting up organizational profiles for their employer or customers.

As shown in jamadar's screenshot above, a credit can be marked as volunteer and sponsored at the same time. This could be the case when someone does the minimum required work to satisfy the customer's need, but uses his or her spare time to add extra functionality. We can also look at the amount of code credits that are exclusively volunteer credits. Of the 7,874 credits that marked volunteer, 43% of them (3,376 credits) only had the volunteer box checked and 57% of them (4,498) were also partially sponsored. These 3,376 credits are one of our best metrics to measure volunteer-only contributions. This suggests that only 10% of the 32,711 commit credits we examined were contributed exclusively by volunteers. This number is a stark contrast to the 12,888 credits that were “purely sponsored,” and that account for 39% of the total credits. In other words, there were roughly four times as many “purely sponsored” credits as there were “purely volunteer” credits.

When we looked at the 5,196 users, rather than credits, we found somewhat different results. A similar percentage of all users had exclusively volunteer credits: 14% (741 users). But the percentage of users with exclusively sponsored credits is only 50% higher: 21% (1077 users). Thus, when we look at the data this way, we find that users who only do sponsored work tend to contribute quite a bit more than users who only do volunteer work.

None of these methodologies are perfect, but they all point to a conclusion that most of the work on Drupal is sponsored. At the same time, the data shows that volunteer contribution remains very important to Drupal. We believe there is a healthy ratio between sponsored and volunteer contributions.

Who is sponsoring the work?

Because we established that most of the work on Drupal is sponsored, we know it is important to track and study what organizations contribute to Drupal. Despite 659 different organizations contributing to Drupal, approximately 50% of them got 4 credits or less. The top 30 organizations (roughly top 5%) account for about 29% of the total credits, which suggests that the top 30 companies play a crucial role in the health of the Drupal project. The graph below shows the top 30 organizations and the number of credits they received between July 1, 2015 and June 30, 2016:

contributions by the top 30 organizations

While not immediately obvious from the graph above, different types of companies are active in Drupal's ecosystem and we propose the following categorization below to discuss our ecosystem.

Category Description Traditional Drupal businesses Small-to-medium-sized professional services companies that make money primarily using Drupal. They typically employ less than 100 employees, and because they specialize in Drupal, many of these professional services companies contribute frequently and are a huge part of our community. Examples are Lullabot (shown on graph) or Chapter Three (shown on graph). Digital marketing agencies Larger full-service agencies that have marketing led practices using a variety of tools, typically including Drupal, Adobe Experience Manager, Sitecore, WordPress, etc. They are typically larger, with the larger agencies employing thousands of people. Examples are Sapient (shown on graph) or AKQA. System integrators Larger companies that specialize in bringing together different technologies into one solution. Example system agencies are Accenture, TATA Consultancy Services, Capgemini or CI&T. Technology and infrastructure companies Examples are Acquia (shown on graph), Lingotek (shown on graph), BlackMesh, RackSpace, Pantheon or Platform.sh. End-users Examples are Pfizer (shown on graph), Examiner.com (shown on graph) or NBC Universal.

Most of the top 30 sponsors are traditional Drupal companies. Sapient (120 credits) is the only digital marketing agency showing up in the top 30. No system integrator shows up in the top 30. The first system integrator is CI&T, which ranked 31st with 102 credits. As far as system integrators are concerned CI&T is a smaller player with between 1,000 and 5,000 employees. Other system integrators with credits are Capgemini (43 credits), Globant (26 credits), and TATA Consultancy Services (7 credits). We didn't see any code contributions from Accenture, Wipro or IBM Global Services. We expect these will come as most of them are building out Drupal practices. For example, we know that IBM Global Services already has over 100 people doing Drupal work.

contributions by organization type

When we look beyond the top 30 sponsors, we see that roughly 82% of the code contribution on Drupal.org comes from the traditional Drupal businesses. About 13% of the contributions comes from infrastructure and software companies, though that category is mostly dominated by one company, Acquia. This means that the technology and infrastructure companies, digital marketing agencies, system integrators and end-users are not meaningfully contributing code to Drupal.org today. In an ideal world, the pie chart above would be sliced in equal sized parts.

How can we explain that unbalance? We believe the two biggest reasons are: (1) Drupal's strategic importance and (2) the level of maturity with Drupal and Open Source. Various of the traditional Drupal agencies have been involved with Drupal for 10 years and almost entirely depend on on Drupal. Given both their expertise and dependence on Drupal, they are most likely to look after Drupal's development and well-being. These organizations are typically recognized as Drupal experts and sought out by organizations that want to build a Drupal website. Contrast this with most of the digital marketing agencies and system integrators who have the size to work with a diversified portfolio of content management platforms, and are just getting started with Drupal and Open Source. They deliver digital marketing solutions and aren't necessarily sought out for their Drupal expertise. As their Drupal practices grow in size and importance, this could change, and when it does, we expect them to contribute more. Right now many of the digital marketing agencies and system integrators have little or no experience with Open Source so it is important that we motivate them to contribute and then teach them how to contribute.

There are two main business reasons for organizations to contribute: (1) it improves their ability to sell and win deals and (2) it improves their ability to hire. Companies that contribute to Drupal tend to promote their contributions in RFPs and sales pitches to win more deals. Contributing to Drupal also results in being recognized as a great place to work for Drupal experts.

We also should note that many organizations in the Drupal community contribute for reasons that would not seem to be explicitly economically motivated. More than 100 credits were sponsored by colleges or universities, such as the University of Waterloo (45 credits). More than 50 credits came from community groups, such as the Drupal Bangalore Community and the Drupal Ukraine Community. Other nonprofits and government organization that appeared in our data include the Drupal Association (166), National Virtual Library of India (25 credits), Center for Research Libraries (20), and Welsh Government (9 credits).

Infrastructure and software companies

Infrastructure and software companies play a different role in our community. These companies are less reliant on professional services (building Drupal websites) and primarily make money from selling subscription based products.

Acquia, Pantheon and Platform.sh are venture-backed Platform-as-a-Service companies born out of the Drupal community. Rackspace and AWS are public companies hosting thousands of Drupal sites each. Lingotek offers cloud-based translation management software for Drupal.

graph of contributions by technology companies

The graph above suggests that Pantheon and Platform.sh have barely contributed code on Drupal.org during the past year. (Platform.sh only became an independent company 6 months ago after they split off from CommerceGuys.) The chart also does not reflect sponsored code contributions on GitHub (such as drush), Drupal event sponsorship, and the wide variety of value that these companies add to Drupal and other Open Source communities.

Consequently, these data show that the Drupal community needs to do a better job of enticing infrastructure and software companies to contribute code to Drupal.org. The Drupal community has a long tradition of encouraging organizations to share code on Drupal.org rather than keep it behind firewalls. While the spirit of the Drupal project cannot be reduced to any single ideology — not every organization can or will share their code — we would like to see organizations continue to prioritize collaboration over individual ownership. Our aim is not to criticize those who do not contribute, but rather to help foster an environment worthy of contribution.

End users

We saw two end-users in the top 30 corporate sponsors: Pfizer (158 credits) and Examiner.com (132 credits). Other notable end-users that are actively giving back are Workday (52 credits), NBC Universal (40 credits), the University of Waterloo (45 credits) and CARD.com (33 credits). The end users that tend to contribute to Drupal use Drupal for a key part of their business and often have an internal team of Drupal developers.

Given that there are hundreds of thousands of Drupal end-users, we would like to see more end-users in the top 30 sponsors. We recognize that a lot of digital agencies don't want, or are not legally allowed, to attribute their customers. We hope that will change as Open Source continues to get more and more adopted.

Given the vast amount of Drupal users, we believe encouraging end-users to contribute could be a big opportunity. Being credited on Drupal.org gives them visibility in the Drupal community and recognizes them as a great place for Open Source developers to work.

The uneasy alliance with corporate contributions

As mentioned above, when community-driven Open Source projects grow, there becomes a bigger need for organizations to help drive its development. It almost always creates an uneasy alliance between volunteers and corporations.

This theory played out in the Linux community well before it played out in the Drupal community. The Linux project is 25 years old now has seen a steady increase in the number of corporate contributors for roughly 20 years. While Linux companies like Red Hat and SUSE rank highly on the contribution list, so do non-Linux-centric companies such as Samsung, Intel, Oracle and Google. The major theme in this story is that all of these corporate contributors were using Linux as an integral part of their business.

The 659 organizations that contribute to Drupal (which includes corporations), is roughly three times the number of organizations that sponsor development of the Linux kernel, “one of the largest cooperative software projects ever attempted.” In fairness, Linux has a different ecosystem than Drupal. The Linux business ecosystem has various large organizations (Red Hat, Google, Intel, IBM and SUSE) for whom Linux is very strategic. As a result, many of them employ dozens of full-time Linux contributors and invest millions of dollars in Linux each year.

In the Drupal community, Acquia has had people dedicated full-time to Drupal starting nine years ago when it hired Gábor Hojtsy to contribute to Drupal core full-time. Today, Acquia has about 10 developers contributing to Drupal full-time. They work on core, contributed modules, security, user experience, performance, best practices, and more. Their work has benefited untold numbers of people around the world, most of whom are not Acquia customers.

In response to Acquia’s high level of participation in the Drupal project, as well as to the number of Acquia employees that hold leadership positions, some members of the Drupal community have suggested that Acquia wields its influence and power to control the future of Drupal for its own commercial benefit. But neither of us believe that Acquia should contribute less. Instead, we would like to see more companies provide more leadership to Drupal and meaningfully contribute on Drupal.org.

Who is sponsoring the top 30 contributors?

Rank Username Issues Volunteer Sponsored Not specified Sponsors 1 dawehner 560 84.1% 77.7% 9.5% Drupal Association (182), Chapter Three (179), Tag1 Consulting (160), Cando (6), Acquia (4), Comm-press (1) 2 DamienMcKenna 448 6.9% 76.3% 19.4% Mediacurrent (342) 3 alexpott 409 0.2% 97.8% 2.2% Chapter Three (400) 4 Berdir 383 0.0% 95.3% 4.7% MD Systems (365), Acquia (9) 5 Wim Leers 382 31.7% 98.2% 1.8% Acquia (375) 6 jhodgdon 381 5.2% 3.4% 91.3% Drupal Association (13), Poplar ProductivityWare (13) 7 joelpittet 294 23.8% 1.4% 76.2% Drupal Association (4) 8 heykarthikwithu 293 99.3% 100.0% 0.0% Valuebound (293), Drupal Bangalore Community (3) 9 mglaman 292 9.6% 96.9% 0.7% Commerce Guys (257), Bluehorn Digital (14), Gaggle.net, Inc. (12), LivePerson, Inc (11), Bluespark (5), DPCI (3), Thinkbean, LLC (3), Digital Bridge Solutions (2), Matsmart (1) 10 drunken monkey 248 75.4% 55.6% 2.0% Acquia (72), StudentFirst (44), epiqo (12), Vizala (9), Sunlime IT Services GmbH (1) 11 Sam152 237 75.9% 89.5% 10.1% PreviousNext (210), Code Drop (2) 12 borisson_ 207 62.8% 36.2% 15.9% Acquia (67), Intracto digital agency (8) 13 benjy 206 0.0% 98.1% 1.9% PreviousNext (168), Code Drop (34) 14 edurenye 184 0.0% 100.0% 0.0% MD Systems (184) 15 catch 180 3.3% 44.4% 54.4% Third and Grove (44), Tag1 Consulting (36), Drupal Association (4) 16 slashrsm 179 12.8% 96.6% 2.8% Examiner.com (89), MD Systems (84), Acquia (18), Studio Matris (1) 17 phenaproxima 177 0.0% 94.4% 5.6% Acquia (167) 18 mbovan 174 7.5% 100.0% 0.0% MD Systems (118), ACTO Team (43), Google Summer of Code (13) 19 tim.plunkett 168 14.3% 89.9% 10.1% Acquia (151) 20 rakesh.gectcr 163 100.0% 100.0% 0.0% Valuebound (138), National Virtual Library of India (NVLI) (25) 21 martin107 163 4.9% 0.0% 95.1%   22 dsnopek 152 0.7% 0.0% 99.3%   23 mikeryan 150 0.0% 89.3% 10.7% Acquia (112), Virtuoso Performance (22), Drupalize.Me (4), North Studio (4) 24 jhedstrom 149 0.0% 83.2% 16.8% Phase2 (124), Workday, Inc. (36), Memorial Sloan Kettering Cancer Center (4) 25 xjm 147 0.0% 81.0% 19.0% Acquia (119) 26 hussainweb 147 2.0% 98.6% 1.4% Axelerant (145) 27 stefan.r 146 0.7% 0.7% 98.6% Drupal Association (1) 28 bojanz 145 2.1% 83.4% 15.2% Commerce Guys (121), Bluespark (2) 29 penyaskito 141 6.4% 95.0% 3.5% Lingotek (129), Cocomore AG (5) 30 larowlan 135 34.1% 63.0% 16.3% PreviousNext (85), Department of Justice & Regulation, Victoria (14), amaysim Australia Ltd. (1), University of Adelaide (1)

We observe that the top 30 contributors are sponsored by 45 organizations. This kind of diversity is aligned with our desire not to see Drupal controlled by a single organization. The top 30 contributors and the 45 organizations are from many different parts in the world and work with customers large or small. We could still benefit from more diversity, though. The top 30 lacks digital marketing agencies, large system integrators and end-users — all of whom could contribute meaningfully to making Drupal for them and others.

Evolving the credit system

The credit system gives us quantifiable data about where our community's contributions come from, but that data is not perfect. Here are a few suggested improvements:

  1. We need to find ways to recognize non-code contributions as well as code contributions outside of Drupal.org (i.e. on GitHub). Lots of people and organizations spend hundreds of hours putting together local events, writing documentation, translating Drupal, mentoring new contributors, and more — and none of that gets captured by the credit system.
  2. We'd benefit by finding a way to account for the complexity and quality of contributions; one person might have worked several weeks for just one credit, while another person might have gotten a credit for 30 minutes of work. We could, for example, consider the issue credit data in conjunction with Git commit data regarding insertions, deletions, and files changed.
  3. We could try to leverage the credit system to encourage more companies, especially those that do not contribute today, to participate in large-scale initiatives. Dries presented some ideas two years ago in his DrupalCon Amsterdam keynote and Matthew has suggested other ideas, but we are open to more suggestions on how we might bring more contributors into the fold using the credit system.
  4. We could segment out organization profiles between end users and different kinds of service providers. Doing so would make it easier to see who the top contributors are in each segment and perhaps foster more healthy competition among peers. In turn, the community could learn about the peculiar motivations within each segment.

Like Drupal the software, the credit system on Drupal.org is a tool that can evolve, but that ultimately will only be useful when the community uses it, understands its shortcomings, and suggests constructive improvements. In highlighting the organizations that sponsor work on Drupal.org, we hope to provoke responses that help evolve the credit system into something that incentivizes business to sponsor more work and that allows more people the opportunity to participate in our community, learn from others, teach newcomers, and make positive contributions. We view Drupal as a productive force for change and we wish to use the credit system to highlight (at least some of) the work of our diverse community of volunteers, companies, nonprofits, governments, schools, universities, individuals, and other groups.

Conclusion

Our data shows that Drupal is a vibrant and diverse community, with thousands of contributors, that is constantly evolving and improving the software. While here we have examined issue credits mostly through the lens of sponsorship, in future analyses we plan to consider the same issue credits in conjunction with other publicly-disclosed Drupal user data, such as gender identification, geography, seasonal participation, mentorship, and event attendance.

Our analysis of the Drupal.org credit data concludes that most of the contributions to Drupal are sponsored. At the same time, the data shows that volunteer contribution remains very important to Drupal.

As a community, we need to understand that a healthy Open Source ecosystem is a diverse ecosystem that includes more than traditional Drupal agencies. The traditional Drupal agencies and Acquia contribute the most but we don't see a lot of contribution from the larger digital marketing agencies, system integrators, technology companies, or end-users of Drupal — we believe that might come as these organizations build out their Drupal practices and Drupal becomes more strategic for them.

To grow and sustain Drupal, we should support those that contribute to Drupal, and find ways to get those that are not contributing, involved in our community. We invite you to help us figure out how we can continue to strengthen our ecosystem.

We hope to repeat this work in 1 or 2 years' time so we can track our evolution. Special thanks to Tim Lehnen (Drupal Association) for providing us the credit system data and supporting us during our research.

Sep 06 2016
Sep 06

Annertech DrupalCon Dublin Sponsors BadgeAs Ireland's leading Drupal devlopment agency, we were thrilled that DrupalCon was coming to Dublin. So much so, we were the first Irish agency to add our name to the sponsorhip list (not to mention being the only Irish agency with people giving presenations at it). For the week of DrupalCon we'll be manning Booth 901. We think you'll be glad you talked to us, here's why.

You have a project idea

You want to develop a website or other web application - cms, ecommerce store, crm, etc - and want to make sure that it is built by the best team possible in the shortest amount of time. Even though the project might be complex and the deadline short, you want to make sure you will be dealing with a team that is reliable and comes up with solutions, not problems.

You want someone to support your current website

You have a website. For whatever reason - you have no support contract at present, your current support agency is not up to scratch - you want to move support for this to a new agency. Supporting websites built by other agencies and developers is one of our strong points. We are constantly contracted to take over work started/completed by others. This usually involves moving your website to our first-in-class hosting platform, performance tuning it, making it more secure, and adding new features over time to get the website into the shape you envisioned for it.

You are a very reliable agency and need to partner with another very reliable agency

We have some fantastic partnerships with other agencies where we divide the work according to our strengths and availability. Need some developers to help out on the front-end? We can probably help you. Don't have experience integrating with SugarCRM or other third party system? We can certainly help you. Finished building a website and don't want to be the future maintainer? See our answer above!

You are a great Drupal developer

Like all good agencies, we are always open to discussions with people who are capable and have great ideas. Tell us what yours is; who knows, it might align with ours. Be careful though, we don't hire interns or junior developers: we expect people to be at least mid-level before coming on board with us.

We didn't mention all the things above that might make us sound arrogant, such as contributing more code to Drupal than all other Irish agencies combined, being Ireland's largest Drupal agency, having the only Irish-based member on the Drupal security team ... and lots more. If you want to hear those juicy details, well, that's just one more reason to come talk to us at Booth 901.

P. S. We'll have cake. You want cake don't you?

Sep 01 2016
Sep 01

Annertech DrupalCon Dublin Sponsors BadgeDrupalCon is coming to Dublin next month. Damn, we're exicted. You are too, we know. Actually, apparently there's a small few people still trying to make up their minds on whether to come to DrupalCon Dublin or not.

Sitting on the fence? Don't. Get off the fence, get to Dublin. Here's some reasons why (feel free to add more in the comments).

1) It'll be the best DrupalCon ever

Us Europeans know that the DrupalCon Europe is the best DrupalCon experience. Heck, Drupal was created by a European after all. So, with that disucussion out of the way, us Irish know (not just believe) that DrupalCon Dublin will be the best Europe has ever had.

This year there are more sessions, more speakers, more tracks than you can handle. More sponsors, more stands, more BoFs than is healthy. More sprints, more ... you get the idea. DrupalCon Dublin is going to be epic.

2) Meet and Greet the Biggest names in the Drupalverse

You know the way you get loads of free code to use however you want when you install Drupal? You know the way you get even more free stuff when you use contributed modules and themes? Well, many of the people who make that happen will be in Dublin.

When you come to DrupalCon Dublin you will get to meet your Drupal heroes. Make sure you thank them for all the great work they do and, cumulatively, the awesome product that we as the Drupal community (that includes you!) have all helped to build.

3) Get hired, Find talent, Make Connections

There's a DrupalCon meme - "I came back from DrupalCon and quit my job". The next item then is usually "and set up my own company". Well done to all who do this for their bravery, ambition, and self-belief.

Another great thing about DrupalCon is that loooooaaads of companies there are hiring. If you are looking for a job, you'll be hard pressed to leave DrupalCon without one. On the flip side, if you are looking to hire great Drupal talent, you could be no where better in the world than Dublin for DrupalCon.

If you are not looking for a job and are not looking to hire anyone, don't miss out on the great opportunity that DrupalCon affords for you to make some life-lasting connections with like-minded people and agencies. Want to scale? This is how.

4) Contribute to Drupal

Drupal is nothing without contributions. That's how free and open source software works. DrupalCon Dublin gives you many, many opportunities to contribute - to core, to contrib, to documentation, to your own personal projects.

If you are stuck with an issue, I bet someone in a sprint room will have a solution for you. If you have an idea you'd like to see in core, chat with a core contributor and get the ball rolling. If you've never contributed before, the mentored sprints for first-time contributors will make you feel very welcome.

And, as always, Drupal needs more mentors. (Dear mentors - If you're reading this, I'm happy to sign up this year!).

5) Have the craic

People who are not from Ireland always laugh at our word 'craic' pronounced 'crack'. Which is probably a very good thing. Because in Ireland to 'have the craic' means to have great fun.

At DrupalCon Dublin expect to have a week's load of craic between the discussions, sessions, BoFs, welcome party, other parties, hallway conversations and more.

Céad míle fáilte!

Presumably you haven't gotten to the end of this list because you've gone to purchase a DrupalCon ticket. That's great. If you have gotten to the end of this list, we're sorry we haven't convinced you. How about I get you a pint of Guinness when you get here?

Sep 01 2016
Sep 01

Annertech DrupalCon Dublin Sponsors BadgeComing to DrupalCon Dublin but planning to travel around Ireland a bit? Previously we blogged about our top recommendations to see and do in Dublin, but I know some of you are planning to stay for longer and so here are our recommendations for places to visit outside Dublin.

Day Trips from Dublin

1. Newgrange

Source: Stella Power

Newgrange is a stone age or neolithic monument located just north of Dublin in an area known as the Boyne Valley in County Meath. It was built about 5,200 years ago (c 3200 BC), making it older than both Stonehenge and the Great Pyramids of Giza! It is primarily referred to as a passage tomb, but that might be an over-simplification as to its significance in the lives of those who constructed it. It most likely held religious, spiritual or ceremonial importance due to its alignment with the rising sun and the flooding of the inner chamber with light on the winter solstice. The Boyne Valley contains over 35 other neolithic monuments all built along the River Boyne, such as Knowth and Dowth (also open to the public), and together are known as Brú na Bóinne and are a UNESCO World Heritage site. 

You can also visit the Hill of Tara, seat of the Irish High Kings. The oldest visible monument is the passage tomb "Dumha na nGiall"(meaning ‘the mound of the hostages'), but more interesting is the Lia Fáil (Stone of Destiny), a standing stone located within an area known as the Forrad or "The Royal Seat" - this was the inauguration stone of the Kings of Tara. 

2. Glendalough

Located in the Wicklow Mountains is Glendalough meaning the "valley of two lakes". It is a beautiful spot with stunning scenery and lots of walks and trails. There is also a world famous early Christian monastic site here, founded in the 6th century by St. Kevin. There are a number of monastic buildings and ruins here, the most impressive of which is the Round Tower standing at 30m high. There's also some Celtic crosses at this site too.

3. Powerscourt Estate and Gardens

Powerscourt Estate is a large country estate encompassing an 18th century mansion, landscaped gardens, a hotel, Ireland's highest waterfall and two golf courses! Powerscourt House has recently been voted one of the top ten houses and mansions worldwide. Part of the ground floor of the house is open to the public, but it also contains some shops (including Avoca handweavers) and Tara's Palace Museum of Childhood which features dolls, miniatures, historic toys and Tara's Palace dollhouse. The gardens have also been ranked in the top three in the world, with over 250 different types of trees, an Italian garden, a Japanese garden and much more besides. 

Further afield

1. Cliffs of Moher and the Burren

The Cliffs of Moher are one of Ireland's most visited natural attractions. Located on the west coast of Ireland in County Clare, not far from the village of Doolin, they stretch for 8km in length and range between 120m and 214m high.  A new Cliffs of Moher Visitor centre was built into a hillside approaching the cliffs and is intended to be environmentally sensitive in its use of renewable energy systems including geothermal heating and cooling, solar panels, and grey water recycling.

The Cliffs of Moher are also located in the rather unusual landscape of the Burren National Park. The word "Burren" comes from an Irish word "Boíreann" meaning a rocky place. In 1651 a Cromwellian Army Officer named Edward Ludlow commented on the Burren saying "there is not water enough to drown a man, wood enough to hang one, nor earth enough to bury them." This is all very apt given it's barren limestone landscape. The Burren contains a number of rare flora, some of which are only to be found here, and has been declared as a Special Area of Conservation. There are also some blow holes to be found where the sea has eroded away at the rock, and you can also visit the extensive caverns of the Ailwee Caves

Note, there are (long) day trips available from Dublin to the Cliffs of Moher if you wish.

2. Ring of Kerry and Dingle Peninsula

The Ring of Kerry is a long circular tourist route in the south west of Ireland in County Kerry. It essentially takes you on the loop of the Iveragh peninsula, and takes in many beautiful spots including Killarney National Park, the lakes of Killarney, Derrynane Abbey, and many beaches, stone forts and other places of interest.

The next peninsula to the north is that of Dingle, and is one of my favourite spots in Ireland. The views from the coastal road are spectacular and breath-taking. However, be warned that the road can be twisty and narrow in places (not helped by tourists stopping to take photos), so I'd recommend taking the clockwise route around the peninsula lest you meet a coach tour bus coming the other way! 

The Dingle peninsula is a Gaeltacht (Irish-speaking) area and you'll notice many of the road signs switch to being in Irish. Dingle (or An Daingean) itself is a busy tourist spot with a decent aquarium for kids, day trips out to see Fungi the dolphin and a pub for every week in the year. Other interesting spots on the peninsula are the Gallarus Oratory and Louis Mulcahy's pottery studio where you can try your hand at spinning a pot on a potter's wheel.

3. Giant's Causeway

The Giant's Causeway is located in Northern Ireland in County Antrim (so be sure to bring some Sterling if travelling from Dublin to it). It is the result of an ancient volcanic eruption which formed (mostly) hexagonal basalt columns as the liquid lava cooled and contracted. Declared a UNESCO World Heritage site in 1986, it is one of Northern Ireland's most popular tourist attractions.

Not far from the Giant's Causeway is the Carrick-a-Rede rope bridge. It crosses a 20m wide chasm over a 23m drop down to the sea below, so if you're afraid of heights, then this isn't for you! Also, nearby is the Bushmills Distillery which do excellent tours and a free whiskey tasting at the end.

Again, it is possible to do a day trip from Dublin to the Giant's Causeway if you wish.

Source: Sean MacEntee Source: Ben Askins Source: Sean MacEntee Source: Barbara Walsh Source: Ty

Aug 24 2016
Aug 24

The TWG coding standards committee is announcing two coding standards changes for final discussion. These appear to have reached a point close enough to consensus for final completion. The new process for proposing and ratifying changes is documented on the coding standards project page.

Official coding standards updates now ratified:

Issues awaiting core approval:

Issues that just need a little TLC (you can help!):

These proposals will be re-evaluated during the next coding standards meeting currently scheduled for August 30th. At that point the discussion may be extended, or if clear consensus has been reached one or more policies may be dismissed or ratified and moved to the next step in the process.

Aug 08 2016
Aug 08

Coming to DrupalCon Dublin? Planning to take some extra time to explore the city? Here's a list of our recommended things to do and see while here - other than Drupal of course!

1. Guinness Storehouse

Photo of Guinness Storehouse gateSource: Beatrice Tiberi

Home to the black-stuff, the Guinness Storehouse is a must-see in the Dublin tourism scene, and was recently recognised as the best tourist attraction in Europe in the World Travel Awards. A former Guinness fermentation plant, it was renovated and redesigned in the shape of a giant pint! On each of the seven floors you get to see how Guinness is made, before stopping off at the Gravity Bar on the top floor for 360 views of the city and a pint of Guinness.

2. Kilmainham Gaol

Photo of inside of Kilmainham GaolSource: Sean Munson

If you're in any way interested in Irish history, particularly the story behind our independence, then you have to visit Kilmainham Gaol. The leaders of the 1916 Easter Rising were executed here, while many other Irish nationalists were imprisoned here too. With this year being the 100 year anniversary of the Rising, there is a special "1916 Portraits and Lives" exhibition being held.

3. Trinity College Dublin & the Book of Kells

Photo of script from the Book of KellsSource: manuscript_nerd

Located in the heart of Dublin city, is the beautiful campus of Trinity College Dublin. The college was founded in 1592 by Queen Elizabeth and contains many historic buildings. The Book of Kells is also located here, and it and the Long Room library are both well worth a visit.

The Book of Kells is a beautifully illustrated manuscript dating back to around 800 AD and contains the Latin texts of the four Gospels, painstakingly written and illustrated by Irish monks. After visiting the Book of Kells, you can then visit the Long Room, the magnificent library housing all of Trinity's oldest books. The library was actually the inspiration for the Jedi Archives featured in the “Star Wars Episode II: Attack of the Clones” movie.

4. Little Museum of Dublin

Photo of the Little Museum of Dublin signSource: William Murphy

The Little Museum of Dublin is another cool place to visit. One of the newer museums in Dublin, it occupies a Georgian townhouse on St. Stephen's Green. Did you know that the DrupalCon Dublin logo features a Georgian door? 

The Little Museum has over 400 artefacts comprising art, photography, letters and advertising from the 20th century, offering a unique insight into Dublin over the last century. It's a small museum and won't take too long to wander around, but well worth the visit all the same.

5. National Museum of Ireland at Collins Barracks

Photo from Collin's BarracksSource: William Murphy

Collins Barracks was an army base for nearly 300 years before being renovated and is now part of the National Museum of Ireland - Decorative Arts & History. It showcases Ireland's economic, social, political and military progress through the ages and is itself a historical place of interest. It also played a role in the 1916 Easter Rising, as a base for British troops tackling rebel positions in the GPO and other locations around Dublin.

Other places of interest

If none of those take your interest, why not check out some of the following:

Aug 05 2016
Aug 05

Every Drupal developer knows the following error message (maybe some by heart): The content has been modified by another user, changes cannot be saved. In Drupal 8 the message is even a bit longer: The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved. While this inbuilt mechanism is very useful to preserve data integrity, the only way to get rid of the message is to reload the form and then redo the changes you want to make. This can be (or should I say 'is') very frustrating for users, especially when they have no idea why this is happening. In an environment where multiple users modify the same content, there are solutions like the Content locking module to get overcome this nagging problem. But what if your content changes a lot by backend calls ?

On a big project I'm currently working on, Musescore.com (D6 to D8), members can upload their scores to the website. On save, the file is send to Amazon where it will be processed so you can play and listen to the music in your browser. Depending on the length of a score, the processing might take a couple of minutes before it's available. In the meantime, you can edit the score because the user might want to update the title, body content, or add some new tags. While the edit form is open, the backend might be pinging back to our application notifying the score is now ready for playing and will update field values, thus saving the node. At this very moment, the changed time has been updated to the future, so when the user wants to save new values, Drupal will complain. This is just a simple example, in reality, the backend workers might be pinging a couple of times back on several occasions doing various operations and updating field values. And ironically, the user doesn't even have any permission to update one or more of these properties on the form itself. If you have ever uploaded a video to YouTube, you know that while your video is processing you can happily update your content and tags without any problem at all. That's what we want here too.

In Drupal 8, validating an entity is now decoupled from form validation. More information can be found on the Entity Validation API handbook and how they integrate with Symfony. Now, the validation plugin responsible for that message lives in EntityChangedConstraint and EntityChangedConstraintValidator. Since they are plugins, we can easily swap out the class and depending on our needs only add the violation when we really want to. What we also want is to preserve values of fields that might have been updated by a previous operation, in our case a backend call pinging back to tell us that the score is now ready for playing. Are you ready ? Here goes!

Step 1. Swap the class

All plugin managers in Core (any plugin manager should do that!) allow you to alter the definitions, so let's change the class to our own custom class.

/**
 * Implements hook_validation_constraint_alter().
 */
function project_validation_constraint_alter(array &$definitions) {
  if (isset(
$definitions['EntityChanged'])) {
   
$definitions['EntityChanged']['class'] = 'Drupal\project\Plugin\Validation\Constraint\CustomEntityChangedConstraint';
  }
}
?>

For the actual class itself, you can copy the original one, but without the annotation. The constraint plugin manager doesn't need to know about an additional new one (unless you want it to of course).

namespace Drupal\project\Plugin\Validation\Constraint;

use </span>Symfony\Component\Validator\Constraint;

/**
 * Custom implementation of the validation constraint for the entity changed timestamp.
 */
class CustomEntityChangedConstraint extends Constraint {
  public
$message = 'The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved. In case you still see this, then you are really unlucky this time!';
}
?>

Step 2: alter the node form

We want to be able to know that a validation of an entity is happening when an actual form is submitted. For this, we're adding a hidden field which stores a token based on the node id which we can then use later.

/**
 * Implements hook_form_BASE_FORM_ID_alter() for \Drupal\node\NodeForm.
 */
function project_form_node_form_alter(&$form, &$form_state) {
 
/** @var \Drupal\Node\NodeInterface $node */
 
$node = $form_state->getFormObject()->getEntity();
  if (!
$node->isNew() && $node->bundle() == 'your_bundle' && $node->getOwnerId() == \Drupal::currentUser()->id()) {
   
$form['web_submission'] = [
     
'#type' => 'hidden',
     
'#value' => \Drupal::csrfToken()->get($node->id()),
    ];
  }
}
?>

Step 3: Validating the entity and storing an id for later

We're getting to the tricky part. Not adding a violation is easy, but the entity that comes inside the constraint can't be changed. The reason is that ContentEntityForm rebuilts the entity when it comes in the submission phase, which means that if you would make any changes to the entity during validation, they would be lost. And it's a good idea anyway as other constraints might add violations which are necessary. To come around that, our constraint, in case the changed time is in the past, will verify if there is a valid token and call a function to store the id of the node in a static variable which can be picked up later.

namespace Drupal\project\Plugin\Validation\Constraint;

use </span>Symfony\Component\Validator\Constraint;
use
Symfony\Component\Validator\ConstraintValidator;

/**
 * Validates the EntityChanged constraint.
 */
class CustomEntityChangedConstraintValidator extends ConstraintValidator {

  </span>/**
   * {@inheritdoc}
   */
 
public function validate($entity, Constraint $constraint) {
    if (isset(
$entity)) {
     
/** @var \Drupal\Core\Entity\EntityInterface $entity */
     
if (!$entity->isNew()) {
       
$saved_entity = \Drupal::entityManager()->getStorage($entity->getEntityTypeId())->loadUnchanged($entity->id());
       
// A change to any other translation must add a violation to the current
        // translation because there might be untranslatable shared fields.
       
if ($saved_entity && $saved_entity->getChangedTimeAcrossTranslations() > $entity->getChangedTimeAcrossTranslations()) {
         
$add_violation = TRUE;
          if (
$entity->getEntityTypeId() == 'node' && $entity->bundle() == 'your_bundle' &&
           
$this->isValidWebsubmission($entity->id())) {
           
$add_violation = FALSE;

            </span>// Store this id.
           
project_preserve_values_from_original_entity($entity->id(), TRUE);
          }

          </span>// Add the violation if necessary.
         
if ($add_violation) {
           
$this->context->addViolation($constraint->message);
          }
        }
      }
    }
  }

  </span>/**
   * Validate the web submission.
   *
   * @param $value
   *   The value.
   *
   * @see project_form_node_form_alter().
   *
   * @return bool
   */
 
public function isValidWebsubmission($value) {
    if (!empty(
\Drupal::request()->get('web_submission'))) {
      return
\Drupal::csrfToken()->validate(\Drupal::request()->get('web_submission'), $value);
    }

    return </span>FALSE;
  }

}

/**
 * Function which holds a static array with ids of entities which need to
 * preserve values from the original entity.
 *
 * @param $id
 *   The entity id.
 * @param bool $set
 *   Whether to store the id or not.
 *
 * @return bool
 *   TRUE if id is set in the $ids array or not.
 */
function project_preserve_values_from_original_entity($id, $set = FALSE) {
  static
$ids = [];

  if (</span>$set && !isset($ids[$id])) {
   
$ids[$id] = TRUE;
  }

  return isset(</span>$ids[$id]) ? TRUE : FALSE;
}
?>

Step 4: copy over values from the original entity

So we now passed validation, even if the submitted changed timestamp is in the past of the last saved version of this node. Now we need to copy over values that might have been changed by another process that we want to preserve. In hook_node_presave() we can call project_preserve_values_from_original_entity() to ask if this entity is eligible for this operation. If so, we can just do our thing and happily copy those values, while keeping the fields that the user has changed in tact.

/**
 * Implements hook_ENTITY_TYPE_presave().
 */
function project_node_presave(NodeInterface $node) {
  if (!
$node->isNew() && isset($node->original) && $node->bundle() == 'your_bundle' && project_preserve_values_from_original_entity($node->id())) {
   
$node->set('your_field', $node->original->get('your_field')->value);
   
// Do many more copies here.
 
}
}
?>

A happy user!

Not only the user is happy: backends can update whenever they want and customer support does not have to explain anymore where this annoying user facing message is coming from.

Jul 30 2016
Jul 30

On a recent project, we had to create multiple sitemaps for each of the domains that we have setup on the site. We came across some problems that we had to resolve because of the nature of our pURL setup.

##Goals##

  • We want all of the front pages from each subdomain to be added to the sitemap and we are able to set the rules for them on the XMLSitemap settings page.

  • We want to make sure that the URLs that we are adding to the other pages no longer show up in the main domain's sitemap.

##Problems## 1) Only On The Primary Domain

The XML sitemap module only creates one sitemap based on the primary domain.

2) Prefixes not Distinguished

Our URLs for nodes are setup so that nodes can be prefixed with our subdomain (pURL modifier) and XMLSitemap doesn't see our prefixes as being different sites. At this point, all nodes are added to every single domain's sitemap.

3) URL Formats

Our URLs are not in the correct format when being added to the sitemap. Our URLs should look like http://subdomain.domain.org/*, however, because we are prefixing them, they show up as http://domain.org/subdomain/*. We want our URLs to look like they are from the right sub-domain and not all coming from the base domain.

##Solution##

We were able to add the ability to create sitemaps for each of the 15 domains by adding the XMLSitemap domain module. The XLMSitemap domain module allows us to define a domain for each sitemap, generate a sitemap and serve it on the correct domain.

We added xmlsitemap-dont-write-empty-element-in-xml-sitemap-file-2545050-3.patch to prevent empty elements from being added to the sitemap.

Then we used a xmlsitemap_element_alter inside of our own custom module that looks something like this:




function hook_xmlsitemap_element_alter(array &$element, array $link, $sitemap) {
  $domain = $sitemap->uri['options']['base_url'];
  $url_parts = explode('//', $domain);
  $parts = explode('.', $url_parts[1]);
  $subdomain = array_shift($parts);

  $current_parts = explode('/', $link['loc']);
  $current_prefix = array_shift($current_parts);

  $modifiers = _get_core_modifiers();

  
  if (in_array($subdomain, array_keys($modifiers))) {
    
    
    if ($current_prefix != $subdomain && $current_prefix != '') {
      
      $element = array();
        return $element;
      }
    else {
      
      $pattern = $current_prefix . '/';
      $element['loc'] = $domain . str_replace($pattern, '', $link['loc']);
    }
  }
  else {
    
    
    if (in_array($current_prefix, array_keys($modifiers))) {
      $element = array();
      return $element;
    }
  }
}


function _get_core_modifiers() {
  if (!$cache = cache_get('subdomains')) {
    $result = db_query("SELECT id, value FROM {purl} WHERE provider = 'og_purl_provider'")->fetchAllAssoc('value');
    cache_set('subdomains', $result, 'cache', time() + 86400);
    return $result;
  }
  else {
    return $cache->data;
  }
?>

If you have any questions, suggestions, feel free to drop a comment below!

Jul 25 2016
Jul 25

Yesterday all the accepted sessions for DrupalCon Dublin were announced, and we are delighted to report that 5 of our 8 session proposals were accepted! With Acquia being the only company receiving more acceptances, we are extremely proud of our achievement.

Testament to our high standing in the Drupal community, we are the only Irish company speaking at DrupalCon Dublin. Our accepted sessions this year span a number of different tracks, namely Business, Horizons, Site Building, Being Human and Core Conversations, and cover topics from accessibility to remote working to building mobile apps with the Ionic framework. Congratulations to all our speakers!

Here's a quick run down of each session.

Building a co-lingual website - lessons learned from ireland.ie

Speaker: Alan Burke
Track: Site Building

2016 marks the centenary of the 1916 rising in Dublin, a pivotal year in Irish history, and is marked with a series of high-profile events commemorating the rising. ireland.ie is the official state website for the 1916 commemoration and runs on Drupal 7.

While English is the main language in Ireland, Irish is the first official language. A decision was taken to present both languages side by side wherever possible for the 1916 commemorations - including on the website. This session will focus on the unusual co-lingual [2 languages side-by-side] approach, and how Drupal made it possible. 

Choosing Drupal - insider advice from an Irish multinational

Speaker: Alan Burke & Aisling Furlong from Glanbia
Track: Business

Struggling to sell Drupal to clients? Ever wondered what goes into the decision making process when choosing a CMS?
In 2014, Glanbia selected Drupal as the CMS of choice for marketing sites. This session will outline the decision-making process used, and what Drupal agencies can learn when pitching Drupal. This is a joint session proposal between Annertech and Glanbia.

Bridging the PhoneGap: Getting Started Creating Hybrid Mobile Apps with Drupal and Ionic Framework

Speaker: Mark Conroy
Track: Horizons

With the advent of hybrid mobile apps, you can continue being a Drupal frontend developer and also build apps without needing to learn new technologies. The mobile web is quickly catching up with native apps. The mobile web is free, and open, and available to all of us right now and doesn't bind us to proprietary systems. With the many advances being made in this area, we can create great mobile experiences for users.

Future Directions for Drupal Accessibility

Speaker: Andrew Macpherson
Track: Core Conversations

Drupal has made great advances in accessibility over several major releases, and nowadays ranks as a leading implementation of web accessibility standards.  This session will encourage contributors to look ahead at future challenges and opportunities for accessibility during the faster 8.x (and 9.x) release cycle. 

Happiness is... remote working

Speaker: Anthony Lindsay
Track: Being Human

Many Drupal agencies have remote workers. Some are entirely distributed. Whilst remote working is beneficial to all concerned in so many ways, it does come with its own challenges. This talk will cover the journey I took when I moved from a typical 9-5 office job and joined Annertech, which is an entirely distributed Drupal agency. It will highlight the challenges I found: the good, the bad, the funny and the downright surprising, and offer as examples, my experiences for staying happy and healthy in what has the potential to be an isolating environment. 

Congratulations to Alan, Anthony, Andrew and Mark on their great achievement. We look forward to seeing these and all the other great sessions at DrupalCon Dublin in September. Hope to see you there!

Jul 23 2016
Jul 23

Ever since Andrew joined Annertech, he's been a champion of accessible web design and has ensured that accessibility has remained a key focus area in everything we do. That combined with his dedication to open source and contributing back to the community, meant that we were not surprised when he was asked if he'd be interested in becoming a Drupal core accessibility maintainer.

Andrew is truly passionate about accessibility and has increased the knowledge and awareness of issues encountered by people with disabilities for all members of our team. We can not think of a better candidate for a new Drupal core accessibility maintainer.

His response when asked to be a Drupal Core maintainer?

I was really stoked when Mike asked if I'd consider becoming a core maintainer. I have barely stopped bouncing around my home.

Congratulations from everyone in Annertech Andrew!

Jul 14 2016
Jul 14

Back in December, Tom Friedhof shared how we set up our Drupal 8 development and build process utilizing Docker. It has been working well in the several months we have used it and worked within its framework. Within the time-span however, we experienced a few issues here and there which led me to come up with an alternative process which keeps the good things we like and getting rid of/resolving the issues we encountered.

First, I'll list some improvements that we'd like to see:

  1. Solve file-syncing issues

    One issue that I keep running into when working with our development process is that the file-syncing stops working when the host machine powers off in the interim. Even though Vagrant's rsync-auto can still detect changes on the host file-system and initiates an rsync to propel files up into the containers via a mounted volume, the changes do not really appear within the containers themselves. I had a tough time debugging this issue, and the only resolution in sight was to do a vagrant reload -- it's a time-consuming process as it rebuilds every image and running them again. Having to do this every morning when I turn on my laptop at work was no fun.

  2. Performant access to Drupal's root

    Previously, we had to mount Drupal's document root to our host machine using sshfs to explore in it, but it's not exactly performant. For example, performing a grep or ag to search within files contents under Drupal 8's core takes ~10 seconds or more. Colleagues using PhpStorm report that mounting the Drupal root unto the host system brings the IDE to a crawl while it indexes the files.

  3. Levarage Docker Compose

    Docker Compose is a great tool for managing the life-cycle of Docker containers, especially if you are running multiple applications. I felt that it comes with useful features that we were missing out because we were just using Vagrant's built-in Docker provider. Also with the expectation that Docker for Mac Beta will become stable in the not-so-distant future, I'd like the switch to a native Docker development environment as smooth as possible. For me, introducing Docker Compose into the equation is the logical first-step.

    dlite just got into my attention quite recently which could fulfill the role of Docker for Mac before its stable release, but haven't gotten the chance to try it yet.

  4. Use Composer as the first-class package manager

    Our previous build primarily uses Drush to build the Drupal 8 site and download dependencies and relegating the resolution of some Composer dependencies to Composer Manager. Drush worked really well for us in the past and there is no pressing reason why we should abandon it, but considering that Composer Manager is deprecated for Drupal 8.x and that there is already a Composer project for Drupal sites, I thought it would be a good idea to be more proactive and rethink the way we have been doing Drupal builds and adopt the de-facto way of putting together a PHP application. At the moment, Composer is where it's at.

  5. Faster and more efficient builds

    Our previous build utilizes a Jenkins server (also ran as a container) to perform the necessary steps to deploy changes to Pantheon. Since we were mostly deploying from our local machines anyway, I always thought that perhaps running the build steps via docker run ... would probably suffice (and it doesn't incur the overhead of a running Jenkins instance). Ultimately, we decided to explore Platform.sh as our deployment target, so basing our build in Composer became almost imperative as Drupal 8 support (via Drush) on Platform.sh is still in beta.

With these in mind, I'd like to share our new development environment & build process.

1. File & directory structure

Here is a high-level tree-view of the file structure of the project:

/<project_root>
├── Vagrantfile
├── Makefile
├── .platform/ 
│   └── routes.yaml
├── bin/ 
│   ├── drupal*
│   ├── drush*
│   └── sync-host*
├── docker-compose.yml 
├── environment 
├── src/ 
│   ├── .gitignore
│   ├── .platform.app.yaml 
│   ├── Dockerfile
│   ├── LICENSE
│   ├── bin/ 
│   │   ├── drupal-portal*
│   │   └── drush-portal*
│   ├── composer.json
│   ├── composer.lock
│   ├── custom/
│   ├── phpunit.xml.dist
│   ├── scripts/
│   ├── vendor/
│   └── web/ 
└── zsh/ 
    ├── zshrc
    ├── async.zsh
    └── pure.zsh

2. The Vagrantfile


Vagrant.configure("2") do |config|

  config.vm.box = "debian/jessie64"
  config.vm.network "private_network", ip: "192.168.100.47"

  config.vm.hostname = 'activelamp.dev'

  config.vm.provider :virtualbox do |vb|
    vb.name = "activelamp.com"
    vb.memory = 2048
  end

  config.ssh.forward_agent = true

  config.vm.provision "shell",
    inline: "apt-get install -y zsh && sudo chsh -s /usr/bin/zsh vagrant",
    run: "once"

  config.vm.provision "shell",
    inline: "[ -e /home/vagrant/.zshrc ] && echo '' || ln -s /vagrant/zsh/zshrc /home/vagrant/.zshrc",
    run: "once"

  config.vm.provision "shell",
    inline: "[ -e /usr/local/share/zsh/site-functions/prompt_pure_setup ] && echo '' || ln -s /vagrant/zsh/pure.zsh /usr/local/share/zsh/site-functions/prompt_pure_setup",
    run: "once"

  config.vm.provision "shell",
    inline: "[ -e /usr/local/share/zsh/site-functions/async ] && echo '' || ln -s /vagrant/zsh/async.zsh /usr/local/share/zsh/site-functions/async",
    run: "once"

  if ENV['GITHUB_OAUTH_TOKEN']
    config.vm.provision "shell",
      inline: "sudo sed -i '/^GITHUB_OAUTH_TOKEN=/d' /etc/environment  && sudo bash -c 'echo GITHUB_OAUTH_TOKEN=#{ENV['GITHUB_OAUTH_TOKEN']} >> /etc/environment'"
  end

  
  config.vm.provision :docker

  config.vm.provision :docker_compose, yml: "/vagrant/docker-compose.yml", run: "always", compose_version: "1.7.1"

  config.vm.synced_folder ".", "/vagrant", type: "nfs"
  config.vm.synced_folder "./src", "/mnt/code", type: "rsync", rsync__exclude: [".git/", "src/vendor"]
end

Compare this new manifest to the old one and you will notice that we reduce Vagrant's involvement in defining and managing Docker containers. We are simply using this virtual machine as the Docker host, using the vagrant-docker-compose plugin to provision it with the Docker Compose executable and having it (re)build the images during provisiong stage and (re)start the containers on vagrant up.

We are also setting up Vagrant to sync file changes on src/ to /mnt/code/ in the VM via rsync. This directory in the VM will be mounted into the container as you'll see later.

We are also setting up zsh as the login shell for the vagrant user for an improved experience when operating within the virtual machine.

3. The Drupal 8 Build

For now let's zoom in to where the main action happens: the Drupal 8 installation. Let's remove Docker from our thoughts for now and focus on how the Drupal 8 build works.

The src/ directory cotains all files that constitute a Drupal 8 Composer project:


/src/
├── composer.json
├── composer.lock
├── phpunit.xml.dist
├── scripts/
│   └── composer/
├── vendor/ # Composer dependencies
│   └── ...
└── web/ # Web root
    ├── .htaccess
    ├── autoload.php
    ├── core/ # Drupal 8 Core
    ├── drush/
    ├── index.php
    ├── modules/
    ├── profiles/
    ├── robots.txt
    ├── sites/
    │   ├── default/
    │   │   ├── .env
    │   │   ├── config/ # Configuration export files
    │   │   │   ├── system.site.yml
    │   │   │   └── ...
    │   │   ├── default.services.yml
    │   │   ├── default.settings.php
    │   │   ├── files/
    │   │   │   └── ...
    │   │   ├── services.yml
    │   │   ├── settings.local.php.dist
    │   │   ├── settings.php
    │   │   └── settings.platform.php
    │   └── development.services.yml
    ├── themes/
    ├── update.php
    └── web.config

The first step of the build is simply executing composer install within src/. Doing so will download all dependencies defined in composer.lock and scaffold files and folders necessary for the Drupal installation to work. You can head over to the Drupal 8 Composer project repository and look through the code to see in depth how the scaffolding works.

3.1 Defining Composer dependencies from custom installation profiles & modules

Since we cannot use the Composer Manager module anymore, we need a different way of letting Composer know that we may have other dependencies defined in other areas in the project. For this let's look at composer.json:

{
    ...
    "require": {
        ...
        "wikimedia/composer-merge-plugin": "^1.3",
        "activelamp/sync_uuids": "dev-8.x-1.x"
    },
    "extra": {
        ...
        "merge-plugin": {
          "include": [
            "web/profiles/activelamp_com/composer.json",
            "web/profiles/activelamp_com/modules/custom/*/composer.json"
          ]
        }
    }
}

We are requiring the wikimedia/composer-merge-plugin and configuring it in the extra section to also read the installation profile's composer.json and one's that are in custom modules within it.

We can define the contrib modules that we need for our site from within the installation profile.

src/web/profiles/activelamp_com/composer.json:

{
  "name": "activelamp/activelamp-com-profile",
  "require": {
    "drupal/admin_toolbar": "^8.1",
    "drupal/ds": "^8.2",
    "drupal/page_manager": "^8.1@alpha",
    "drupal/panels": "~8.0",
    "drupal/pathauto": "~8.0",
    "drupal/redirect": "~8.0",
    "drupal/coffee": "~8.0"
  }
}

As we create custom modules for the site, any Composer dependencies in them will be picked up everytime we run composer update. This replicates what Composer Manager allowed us to do in Drupal 7. Note however that unlike Composer Manager, Composer does not care if a module is enabled or not -- it will always read its Composer dependencies and resolve them.

3.2 Drupal configuration

3.2.1 Settings file

Let's peek at what's inside src/web/settings.php:




$settings['container_yamls'][] = __DIR__ . '/services.yml';

$config_directories[CONFIG_SYNC_DIRECTORY] = __DIR__ . '/config';


include __DIR__ . "/settings.platform.php";

$update_free_access = FALSE;
$drupal_hash_salt = '';

$local_settings = __DIR__ . '/settings.local.php';

if (file_exists($local_settings)) {
  require_once($local_settings);
}

$settings['install_profile'] = 'activelamp_com';
$settings['hash_salt'] = $drupal_hash_salt;

Next, let's look at settings.platform.php:



if (!getenv('PLATFORM_ENVIRONMENT')) {
    return;
}

$relationships = json_decode(base64_decode(getenv('PLATFORM_RELATIONSHIPS')), true);

$database_creds = $relationships['database'][0];

$databases['default']['default'] = [
    'database' => $database_creds['path'],
    'username' => $database_creds['username'],
    'password' => $database_creds['password'],
    'host' => $database_creds['host'],
    'port' => $database_creds['port'],
    'driver' => 'mysql',
    'prefix' => '',
    'collation' => 'utf8mb4_general_ci',
];

We return early from this file if PLATFORM_ENVIRONMENT is not set. Otherwise, we'll parse the PLATFORM_RELATIONSHIPS data and extract the database credentials from it.

For our development environment however, we'll do something different in settings.local.php.dist:



$databases['default']['default'] = array(
    'database' => getenv('MYSQL_DATABASE'),
    'username' => getenv('MYSQL_USER'),
    'password' => getenv('MYSQL_PASSWORD'),
    'host' => getenv('DRUPAL_MYSQL_HOST'),
    'driver' => 'mysql',
    'port' => 3306,
    'prefix' => '',
);

We are pulling the database values from the environment, as this is how we'll pass data in a Docker run-time. We also append .dist to the file-name because we don't actually want settings.local.php in version control (otherwise, it will mess up the configuration in non-development environments). We will simply rename this file as part of the development workflow. More on this later.

3.2.2 Staged configuration

src/web/sites/default/config/ contains YAML files that constitute the desired Drupal 8 configuration. These files will be used to seed a fresh Drupal 8 installation with configuration specific for the site. As we develop features, we will continually export the configuration entities and place them into this folder so that they are also versioned via Git.

Configuration entities in Drupal 8 are assigned a universally unique ID (a.k.a UUID). Because of this, configuration files are typically only meant to be imported into the same (or a clone of the) Drupal site they were imported from. The proper approach is usually getting hold of a database dump of the Drupal site and use that to seed a Drupal 8 installation which you plan to import the configuration files into. To streamline the process during development, we wrote the drush command sync-uuids that updates the UUIDs of the active configuration entities of a non-clone site (i.e. a freshly installed Drupal instance) to match those found in the staged configuration. We packaged it as Composer package named activelamp/sync_uuids.

The complete steps for the Drupal 8 build is the following:

$ cd src
$ composer install
$ [ -f web/sites/default/settings.local.php ] && : || cp web/sites/default/settings.local.php.dist web/sites/default/settings.local.php
$ drush site-install activelamp_com --account-pass=default-pass -y
$ drush pm-enable config sync_uuids -y
$ drush sync-uuids -y
$ drush config-import -y

These build steps will result a fresh Drupal 8 installation based on the activelamp_com installation profile and will have the proper configuration entities from web/sites/default/config. This will be similar to any site that is built from the same code-base minus any of the actual content. Sometimes that is all that you need.

Now let's look at the development workflow utilizing Docker. Let's start with the src/Dockerfile:



FROM php:7.0-apache

RUN apt-get update && apt-get install -y \
  vim \
  git \
  unzip \
  wget \
  curl \
  libmcrypt-dev \
  libgd2-dev \
  libgd2-xpm-dev \
  libcurl4-openssl-dev \
  mysql-client

ENV PHP_TIMEZONE America/Los_Angeles


RUN docker-php-ext-install -j$(nproc) iconv mcrypt \
&& docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
 && docker-php-ext-install -j$(nproc) gd pdo_mysql curl mbstring opcache


RUN curl -sS https://getcomposer.org/installer | php
RUN mv composer.phar /usr/local/bin/composer
RUN echo 'export PATH="$PATH:/root/.composer/vendor/bin"' >> $HOME/.bashrc


RUN composer global require drush/drush:8.1.2 drupal/console:0.11.3
RUN $HOME/.composer/vendor/bin/drupal init
RUN echo source '$HOME/.console/console.rc' >> $HOME/.bashrc


RUN echo "date.timezone = \"$PHP_TIMEZONE\"" > /usr/local/etc/php/conf.d/timezone.ini
ARG github_oauth_token

RUN [ -n $github_oauth_token ] && composer config -g github-oauth.github.com $github_oauth_token || echo ''

RUN [ -e /etc/apache2/sites-enabled/000-default.conf ] && sed -i -e "s/\/var\/www\/html/\/var\/www\/web/" /etc/apache2/sites-enabled/000-default.conf || sed -i -e "s/\/var\/www\/html/\/var\/www\/web/" /etc/apache2/apache2.conf


COPY bin/drush-portal /usr/bin/drush-portal
COPY bin/drupal-portal /usr/bin/drupal-portal

COPY . /var/www/
WORKDIR /var/www/

RUN composer --working-dir=/var/www install

The majority of the Dockerfile should be self-explanatory. The important bits are the provisioning of a GitHub OAuth token & adding of the {drupal,drush}-portal executables which are essential for the bin/{drush,drupal} pass-through scripts.

Provisioning a GitHub OAuth token

Sometimes it is necessary to configure Composer to use an OAuth token to authenticate on GitHub's API when resolving dependencies. These tokens must remain private and should not be committed into version control. We declare that our Docker build will take github_oauth_token as a build argument. If present, it will configure Composer to authenticate using it to get around API rate limits. More on this later.

DrupalConsole and Drush pass-through scripts

Our previous build involved opening up an SSH port on the container running Drupal so that we can execute Drush commands remotely. However, we should already be able to run Drush commands inside the container without having SSH access by utilizing docker run. However the commands can get too lengthy. In fact, they will be extra lengthy because we also need to execute this from within the Vagrant machine using vagrant ssh.

Here are a bunch of scripts that makes it easier to execute drush and drupal commands from the host machine:

Here are the contents of bin/drush and bin/drupal:

#!/usr/bin/env bash
cmd="docker-compose -f /vagrant/docker-compose.yml  run --no-deps --rm server drupal-portal $@"
vagrant ssh -c "$cmd"
#!/usr/bin/env bash
cmd="docker-compose -f /vagrant/docker-compose.yml  run --no-deps --rm server drush-portal $@"
vagrant ssh -c "$cmd"

This allow us to do bin/drush to run Drush commands and bin/drupal ... to run DrupalConsole commands, and the arguments will be pass over to the executables in the container.

Here are the contents of src/bin/drupal-portal and src/bin/drush-portal:

#!/usr/bin/env bash
/root/.composer/vendor/bin/drupal --root=/var/www/web $@
#!/usr/bin/env bash
/root/.composer/vendor/bin/drush --root=/var/www/web $@

The above scripts are added to the container and is essential to making sure drush and drupal commands are applied to the correct directory.

In order for this to work, we actually have to remove Drush and DrupalConsole from the project's composer.json file. This is easily done via the composer remove command.

The docker-compose.yml file

To tie everything together, we have this Compose file:

version: '2'
services:
  server:
    build:
      context: ./src
      args:
        github_oauth_token: ${GITHUB_OAUTH_TOKEN}
    volumes:
      - /mnt/code:/var/www
      - composer-cache:/root/.composer/cache
    env_file: environment
    links:
      - mysql:mysql
    ports:
      - 80:80
  mysql:
    image: 'mysql:5.7.9'
    env_file: environment
    volumes:
      - database:/var/lib/mysql

volumes:
  database: {}
  composer-cache: {}

There are four things of note:

  1. github_oauth_token: ${GITHUB_OAUTH_TOKEN}

    This tells Docker Compose to use the environment variable GITHUB_OAUTH_TOKEN as the github_oauth_token build argument. This, if not empty, will effectively provision the Composer with an OAuth token. If you go back to the Vagrantfile, you will see that this environment variable is set in the virtual machine (because docker-compose is run under it) by appending it to the /etc/environment file. All it needs is that the environment variable is present in the host environment (OS X) during the provisioning step.

    For example, it can be provisioned via: GITHUB_OAUTH_TOKEN= vagrant provision

  2. composer-cache:/root/.composer/cache

    This tells Docker to mount a volume on /root/.composer/cache so that we can persist the contents of this directory between restarts. This will ensure that composer install and composer update is fast and would not require re-downloading packages from the web every time we run. This will drastically imrpove the build speeds.

  3. database:/var/lib/mysql

    This will tell Docker to persist the MySQL data between builds as well. This is so that we don't end up with an empty database whenever we restart the containers.

  4. env_file: environment

    This let us define all environment variables in a single file, for example:

    MYSQL_USER=activelamp
    MYSQL_ROOT_PASSWORD=root
    MYSQL_PASSWORD=some-secret-passphrase
    MYSQL_DATABASE=activelamp
    DRUPAL_MYSQL_HOST=mysql

    We just configure each service to read environment variables from the same file as they both need these values.

We employ rsync to sync files from the host machine to the VM since it offers by far the fastest file I/O compared to the built-in alternatives in Vagrant + VirtualBox. In the Vagrantfile we specified that we sync src/ to /mnt/code/ in the VM. Following this we configured Docker Compose to mount this directory into the server container. This means that any file changes we make on OS X will get synced up to /mnt/code, and ultimately into /var/www/web in the container. However, this only covers changes that originate from the host machine.

To sync changes that originates from the container -- files that were scaffolded by drupal generate:*, Composer dependencies, and Drupal 8 core itself -- we'll use the fact that our project root is also available at /vagrant as a mount in the VM. We can use rsync to sync files the other way -- rsyncing from /mnt/code to /vagrant/src will bring file changes back up to the host machine.

Here is a script I wrote that does an rsync but will ask for confirmation before doing so to avoid overwriting potentially uncommitted work:

#!/usr/bin/env bash

echo "Dry-run..."

args=$@

diffs="$(vagrant ssh -- rsync --dry-run --itemize-changes $args | grep '^[>

We are keeping this generic and not bake in the paths because we might want to sync arbitrary files to arbitrary destinations.

We can use this script like so:

$ bin/sync-host --recursive --progress --verbose --exclude=".git/" --delete-after /mnt/code/ /vagrant/src/

If the rsync will result in file changes on the host machine, it will bring up a summary of the changes and will ask if you want to proceed or not.

Makefile

We are using make as our task-runner just like in the previous build. This is really useful for encapsulating operations that are common in our workflow:



sync-host:
	bin/sync-host --recursive --progress --verbose --delete-after --exclude='.git/' /mnt/code/ /vagrant/src/

sync:
	vagrant rsync-auto

sync-once:
	vagrant rsync

docker-rebuild:
	vagrant ssh -- docker-compose -f /vagrant/docker-compose.yml build

docker-restart:
	vagrant ssh -- docker-compose -f /vagrant/docker-compose.yml up -d

composer-install:
	vagrant ssh -- docker-compose -f /vagrant/docker-compose.yml run --no-deps --rm server composer --working-dir=/var/www install

composer-update:
	vagrant ssh -- docker-compose -f /vagrant/docker-compose.yml run --no-deps --rm server composer --working-dir=/var/www update --no-interaction



lock-file:
	@vagrant ssh -- cat /mnt/code/composer.lock

install-drupal: composer-install
	vagrant ssh -- '[ -f /mnt/code/web/sites/default/settings.local.php ] && echo '' || cp /mnt/code/web/sites/default/settings.local.php.dist /mnt/code/web/sites/default/settings.local.php'
	-bin/drush si activelamp_com --account-pass=secret -y
	-bin/drush en config sync_uuids -y
	bin/drush sync-uuids -y
	[ $(ls -l src/web/sites/default/config/*.yml | wc -l) -gt 0  ] && bin/drush cim -y || echo "Config is empty. Skipping import..."

init: install-drupal
	yes | bin/sync-host --recursive --progress --verbose --delete-after --exclude='.git/' /mnt/code/ /vagrant/src/

platform-ssh:
	ssh ></span>@ssh.us.platform.sh

The Drupal 8 build steps are simply translated to use bin/drush and the actual paths within the virtual machine in the install-drupal task. After cloning the repository for the first time, a developer should just be able to execute make init, sit back with a cup of coffee and wait until the task is complete.

Try it out yourself!

I wrote the docker-drupal-8 Yeoman generator so that you can easily give this a spin. Feel free to use it to look around and see it in action, or even to start off your Drupal 8 sites in the future:

$ npm install -g yo generator-docker-drupal-8
$ mkdir myd8
$ cd myd8
$ yo docker-drupal-8

Just follow through the instructions, and once complete, run vagrant up && make docker-restart && make init to get it up & running.

If you have any questions, suggestions, anything, feel free to drop a comment below!

Jul 01 2016
Jul 01

With three months left to go before DrupalCon Dublin, event planning is in full swing. Like all Irish Drupal events, Annertech are actively involved in preparations for DrupalCon. Our managing director, Stella Power, has taken on the role of local team lead as well as Business Track chair, and I myself am the Project Management track chair. There's less than a week now to get your sessions submitted for DrupalCon, so it's time to get writing!

Business Track

This year the business track is focusing on how agencies can grow their business and how to do so sustainably. We're looking for speakers who are willing to share their experiences and insights into growing their businesses, what worked, what didn't, and why.

Suggested topics include:

Marketing Drupal

Competition in the marketplace is growing, both from a growing number of agencies offering similar services, but also from alternative solutions being offered. We are looking for sessions on how you generate new leads for your business, and how you market and postiion Drupal and your agency as being the best solution available.

Going for Growth

Growing your business is all very well, but how do you manage this growth and ensure you have the necessary structures in place to ensure this growth is sustainable. We are seeking strategies which offer sustainable approaches to growth for the small and large Drupal agency alike.

Business Innovation and Diversification

Perhaps the solution to sustainable business growth is to innovate and diversify your service offering. Maybe you need to consider extending into additional or alternative markets or verticals. But when is the time right to do this? And what are your strategies to approaching this?

Drupal 8

With the release of Drupal 8, there are a number challenges, but also opportunities for agencies working with Drupal. What has been the impact of Drupal 8 on your business, on demand for your services, on your sales or marketing processes and on your team? How has it affected the estimation and delivery of your projects? How do you convince clients to upgrade to Drupal 8?

These are just some of the ideas we have for the Business Track this year. We want to hear from you, about your experiences and insights into the above topic areas. Got an idea for another session? Great, then let us know and submit it!

Project Management Track

Whatever the size of the project, no matter how many people are working on it, it will need to be managed. Some have dedicated project managers, others share the task within the team, whatever way you spin it it's got to be done, so why not make sure we can learn how to do it in the most appropriate way.

The focus is about sharing your experiences, good or bad, as long as you learned something which is relevant to others, you're welcome to submit a session, here are some topics which we're actively looking for:

  • Tips and Tricks: How did you improve your processes or learn how not to do something?
  • Becoming agile: How did you become more agile?
  • Getting the numbers right: Estimations, budgeting and reporting. How do you keep on top of things?
  • Freelance Project Management: Are you, or have you ever been, a freelance Project Manager? Tell us your story.
  • Managing an Open Source Project: Ever been involved in managing an open source project? How did you get by? Would you do it again?

As always, if you're unsure whether your session proposal is appropriate, reach out to myself or Stella and we will help you where we can.

Countdown to session submission closing starts now, are you ready?

Jul 01 2016
Jul 01

TL;DR

We were already prepared for a scenario like the Aberdeen Cloud breakdown, owing to our disaster recovery plan. Fortunately we didn't have to set it in motion. Each night we have a simple script which takes off-site backups of all of our hosted sites. We've made the source code available on github, so hopefully this will help others prepare for the likes of the Aberdeen Cloud implosion, and perhaps we can share ideas on how to improve each other's disaster recovery plans.

Our experience with the Aberdeen Cloud incident

We have a large number of sites hosted by various cloud services. Since autumn 2013, we'd mainly used Aberdeen Cloud, and in autumn 2015 we started to explore other options to see what else the market had to offer. Platform.sh was the one that we decided to give a serious test for new clients.

Soon after that, Aberdeen Cloud began to seem a bit flaky. Longer response times on support tickets. Solr services started to fail, along with random outages of various other services. We accepted that for a while, but after having lost and regained SSH access to all of our sites (including git and rsync), we eventually decided that enough was enough and we couldn't put our trust in them anymore.

We had to migrate everything to alternative hosting platforms. Given the similar price points and the fact that they seem well funded and offer excellent support, we decided on Platform.sh. And so began Project Exodus. 

Over the next three weeks we migrated over 20 sites to Platform.sh in a staged approach. I wouldn't say that it was a straight-forward process. Lots of clients had specific quirks to their setup. For example, some needed a PHPBB forum, others had FTP access for uploading files, some integrated with external systems that required firewall changes, etc, while others had custom .htaccess redirection rules that needed to be rewritten for Nginx. However, we were very lucky and had completed Project Exodus nearly a month before Aberdeen Cloud finally came tumbling down.

So what if you were not as fortunate as us, and still have sites whose assets are no longer accessible? Stuck in cyberspace, or maybe just plain deleted?

Well, I'm not sure there is a lot you can do. Maybe read Code Engima's blog post about the different things they've tried. However, to put it bluntly, it sucks! Enough said about the matter.

Now it's time to ensure you're never caught out again.

But what can we do to prevent this from happening again?

All companies probably have their own way to deal with this kind of scenario, but I'll tell you about how Annertech deals with backups and recoveries.

We have two sets of backups. A backup from each day, for each production/live/master environment, which is hosted by the cloud service. Then there is an off-site backup (again, daily) used for disaster recovery. The latter one is the important one in this scenario.

The idea is that even if Amazon (which hosts Aberdeen Cloud services) pulled the plug, we would still have access to our clients' data.

We have a server, hosted by a different company, that: 

  1. Pulls down a copy of the database, from every cloud-hosted site, every night, and saves it for four days, before it deletes it again.
  2. Runs `rsync` on every cloud-hosted site, to get an up-to-date version of the files folder, every night.

Sounds simple? It is. All it requires is that your hosting partner supports running Drush on your remote sites and you're good. If you run Drupal sites, and your hosting partner doesn't support running Drush on the remote sites, find somebody else who does. It's that important!

Regarding the code for the sites; we keep our source code repositories on dedicated git services. And, more than likely, we'll also have a copy or two on developers machines.

I'd like to show you the two backup scripts that I made, one for Aberdeen Cloud and one for Platform.sh.

The code is meant to work in our setup only, and is not (yet) generic enough to just work out of the box elsewhere. The release of the scripts is meant to give you a leg up and some inspiration. This is by no means the final end point for these scripts - we are continually evaluating and improving our system, and I look forward to hearing what ideas you have on where we could take it from here too.

The entire repository of code can be "stolen" from github.

When you have a disaster recovery plan you also need to make sure that it actually works. You can do this by downloading the latest backup from each of your sites once a month, installing and then testing them. I've tested a site, where the DB file was corrupt, but only for that site, so make sure that you test all of them. The setup of test sites can also be automated by a script so you don't have to setup 10, 50, or 300 sites and test each manually. Scripts are your friends. Make good use of them and have them do all the hard work.

Now, if you really want to push this further, you should implement a "Smoke test" in all of your installation profiles, so that you can trigger that to see if the site is alive; or perhaps tie it in with a Jenkins server.

If something is unclear, feel free to put a comment below. If you feel like this could be improved, feel free to contribute with a pull request. We are all ears.

Jun 15 2016
Jun 15

Adding media (for most people that means adding images) in Drupal has been an issue for a long time. Adding reusable media (upload an image once, use it on any page on your website) has been even trickier.

With the advent of Drupal 8 and the sterling work done by the media team, adding reusable media (in a very user friendly manner) is now a reality. This tutorial shows you how:

Step 1 - Download and Install these Modules:

  • Media Entity (the base media module that other media modules can extend from)
  • Media Entity Image (or one of the other media entity type modules - Media Entity Instagram, Media Entity Twitter, Media Entity YouTube, etc)
  • Entity Browser (so you can browse through your existing media library and other entites to reuse them on your page)
  • Inline Entity Form (thanks to Drupal Commerce, this allows you to create new media items and/or select from your existing library)

Step 2 - Create Media Bundles

Navigate to /admin/structure/media and add a media bundle - for example "Image":

Drupal 8 Add media bundle interface

You will be asked to define a field to use as the image field, you can't do that yet as you have not created any fields.

Save the page, and then use the UI to add a new field to your image media bundle - at a minimum you will need an image field.

Drupal 8 media manage fields interface

Click the edit button to edit the image media bundle and check that the image field is now set as the field that the image will be sourced from.

Drupal 8 media file source field interface

You now have a media bundle for images. It's a similar process for creating other media types.

Step 3 - Add a Media Field to a Content Type

Navigate to the "Manage Fields" page of one of your content types and add a new field. Choose field of type "Other" under "Reference" (yes, it could be a little more intuitive!).

Drupal 8 media field type

On the following page, choose the type of entity that this field will reference/use - in our case "Media".

Drupal 8 Media choose entity reference type

On the following page, scroll to the end and select the types of media bundles that can be referenced from this field. In our case, we only have image at the moment. Save the page.

Drupal 8 Media select media bundles to reference - image

You now have a field that allows you to reference uploaded images using the entity reference widget. This means the image needs to have already been added to your site (at /media/add/) and that you know the name you gave to the image when adding it. Your node edit form will have a field like this:

Drupal 8 Media reference image entity on node edit form

Whilst this works, it doesn't allow us to add new images from here. Enter the wonderful Inline Entity Form module.

Step 4 - Create New Media/Use Existing Media - inline

Next, go to the "Manage Form Display" page on your content type. Find the reusable image field and change the widget from 'autocomplete' to 'Inline Entity Form - complex' (don't worry, it's not complex!).

Drupal 8 Media inline entity form

Click the settings cog on the right and set the inline entity form to be able to both add new media and use existing media:

Drupal 8 Media inline entity form settings

Now, when you are on your node edit page you will see an interface like this for your reusable image field:

Drupal 8 Media reusable media interface

"Add new media" will open up the form that is found at /media/add and let you add the media inline here.

Drupal 8 Media inline entity form form

After filling out this form, be sure to click on the "Create media" button. You will then see this:

Drupal 8 Media uploaded image interface

If you had chosen "Add existing media" from a few screenshots above, you will be given the same autocomplete interface that you saw earlier. This is because the Inline Entity Form is not yet using the "Entity Browser" module to search for your media.

Step 5 - Entity Browser Setup

To make this a little easier, I also installed the Entity Browser sub-modules "Entity Browser Test Modules". Then, navigate to /admin/config/content/entity_browser and choose "Add entity browser". In my case, I gave it a name of "Image Browser". I left all the settings as default until I got to the last screen "widgets".

I added a widget of type "Upload" and gave it a human readable name of "Add New Image" and added a widget of type "View" and gave it a human readable name of "Reuse Existing Image" (then chose the "test" files browser view as the view that I would use to find my files).

Drupal 8 entity browser widgets

A caveat - I couldn't get this to work with the Inline Entity Form (it's possible in code - see the "hook_inline_entity_form_reference_form_alter()" in "entity_browser_entity_form.module"). However, there is a UI being built for it which should be ready soon, so I decided to wait for that.

In the meantime, on the content type "Manage Form Display" page, if you set your reusable image field to use "Entity Browser" you can see what it will be like when it's working fully. This (an example of the browser using an iframe:

Drupal 8 entity browser using iframe

And that's about it, a quick roundup of how to add reusable media to your Drupal 8 website - it's almost the holy grail and once we have entity browser working with the inline entity form, it will be the holy grail.

Thanks and well done to all on the media team (of which I have been only a very peripheral part).

Jun 15 2016
Jun 15

The web development community can have a long list of requirements, languages, frameworks, constructs and tools that most companies or bosses want you to know.

This list may not include everything you need to know including PHP, HTML, CSS, responsive web development principles, and Drupalisms. Here is the list of some of the important skills, concepts, and tools that we think you should know as a beginner Drupal developer.

####1. Version Control

Every developer should have some experience with version control and versioning. Version control is an essential part of the Drupal community. Versioning allows for Drupal projects to be easily managed, maintained and contributed in a uniform manner. Version control will also most likely be used in-house to manage each client project as well.

####2. Command Line Interface (CLI)

It isn't necessary to be a CLI Ninja, however being able to work comfortably using a CLI is very important. One of the advantages to using a CLI is the ability to be more productive. You can quickly automate repetitive tasks, perform tasks without jumping from application to application, and the ability to use tools like Drush to perform tasks that would normally require you to navigate 3 or more mouse clicks to accomplish.

####3. Package Managers

Using package managers is important to the installation of Drupal. Whether it is installing Sass or Bootstrap from node or Drush from composer, it is important to know how package managers work and exactly what you are running before running commands on your computer.

####4. Contributing Back

An important part of the Drupal community is contributing back to projects and core. When you find an issue, such as something that just doesn't seem to work correctly, or you would like to implement a functionality to Drupal, you should think about giving back to the community. If you find an issue on an existing project or core, check to see if there is an existing ticket on that project. If there isn't, you can create one, and if you can debug it and resolve the issue you can contribute a patch to that issue. If you don't know exactly how to debug the issue you can have an open conversation with other developers and maintainers to help resolve the issue. Contributing and interacting in the community moves Drupal forward.

####5. CSS Preprocessors

Within the last couple of years, there has been a movement to CSS preprocessors to add a programmatic feel to CSS2 and CSS3. There are some that are against preprocessors because it adds a little more overhead to a project. Whether you use them or not, you may have a client or framework that uses one that you might need to be familiar with how to use a preprocessor.

####6. A Framework

Within the Drupal community, there is often talk of headless Drupal. We have seen some interesting ideas come from the adopters of headless Drupal. Headless Drupal setups usually use a framework for the front-end. It may be Angular, Angular 2, Backbone, Ember or something different, however, most of the frameworks have two things in common, they are often written in Javascript and almost always make use of templating.

####7. Templating

It is important to know the principles of templating so that you can easily pick up and learn new frameworks. Whether it is Mustache, Twig, Jade, or the templating syntax from within Angular, there are similarities between the syntax and the principles can be applied to each of the languages that will allow you to quickly step from one to the next with a smaller learning curve.

####8. Basic Debugging

Debugging a problem correctly can save you valuable time by getting you directly to the cause of an issue instead of looking over each line of code one by one. It is essential to know how to do basic debugging when working with Drupal. Sometimes the error messages can give you enough information, other times it is necessary to step into Devel or XDebug and step through the project to find the exact location where the code is not working correctly so that you can start to solve the problem.

####9. Unit Testing / Code Testing

Testing your own code is important. When it comes to code testing you have many options, from TTD and BDD you can write unit tests to cover your classes, linting to make sure you are writing "good", standardized code. Linting can be helpful for writing code that others can easily navigate and sets up some best practices for you to follow.

####10. A CMS

When starting with Drupal, it might be good to have familiarity with a CMS platform before jumping in. There are some advantages to knowing the constructs of other CMS platforms and being familiar with how to work within a platform. However, when working with Drupal it is important to think about the way Drupal works and not be stuck in the way other CMS platforms accomplish goals.

####Conclusion As a web developer, it is important to know many concepts and technologies. Many companies will not require you to know everything, do everything and be a jack-of-all-trades. In technology, there are so many new tools, frameworks, and languages coming out daily that it is impossible to stay on top of them all. It is far better to get a good base understanding of core web concepts that can be applied to multiple languages, tools, and technologies and then specialize.

Did I miss something you feel is important? Is there something you would like to have seen on the list? Leave a comment below.

Jun 07 2016
Jun 07

Continuing from Evan's blog post on building pages with Paragraphs and writing custom blocks of content as fields, I will walk you through how to create a custom field-formatter in Drupal 8 by example.

A field-formatter is the last piece of code to go with the field-type and the field-widget that Evan wrote about in the previous blog post. While the field-type tells Drupal about what data comprises a field, the field-formatter is responsible for telling Drupal how to display the data stored in the field.

To recap, we defined a hashtag_search field type in the previous blog post whose instances will be composed of two items: the hashtag to search for, and the number of items to display. We want to convert this data into a list of the most recent n tweets with the specified hashtag.

A field-formatter is a Drupal plugin, just like its respective field-type and field-widget. They live in /src/Plugin/Field/FieldFormatter/ and are namespaced appropriately: Drupal\\Plugin\Field\FieldFormatter.




namespace Drupal\my_module\Plugin\Field\FieldFormatter;


use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
use Drupal\Core\Form\FormStateInterface;


class HashtagFormatter extends FormatterBase
{

    public function viewElements(FieldItemListInterface $items, $langcode)
    {
        return array();
    }
}

We tell Drupal important details about our new field-formatter using a @FieldFormatter class annotation. We declare its unique id; a human-readable, translatable label; and a list of field_types that it supports.

The most important method in a field-formatter is the viewElements method. It's responsibility is returning a render array based on field data being passed as $items\Core\Field\FieldItemListInterface>.

Let's look at the code:




use Drupal\my_module\Twitter\TwitterClient;
use Drupal\my_module\Twitter\TweetFormatter;

...

    
    protected $twitter;

    
    protected $formatter;

    ...

    public function viewElements(FieldItemListInterface $items, $langcode)
    {
        $element = [];

        
        foreach ($items as $delta => $item) {

            try {

                
                $results = $this->twitter->search($item->hashtag_search, $item->count);

                
                
                
                $statuses = array_map(function ($s) {
                    $s['formatted_text'] = $this->formatter->toHtml($s['text'], $s['entities']);
                    return $s;
                }, $results['statuses']);

                
                if (!empty($statuses)) {
                    $element[$delta]['header'] = [
                        '#markup' => '

#'</span> . $item->hashtag_search . '

'
]; } foreach ($statuses as $status) { $element[$delta]['status'][] = [ '#theme' => 'my_module_status', '#status' => $status ]; } } catch (\Exception $e) { $this->logger->error('[:exception]: %message', [ ':exception' => get_class($e), '%message' => $e->getMessage(), ]); continue; } } $element['#attached']['library'][] = 'my_module/twitter_intents'; return $element; } ...

See https://github.com/bezhermoso/tweet-to-html-php for how TweetFormatter works. Also, you can find the source-code for the basic Twitter HTTP client here: https://gist.github.com/bezhermoso/5a04e03cedbc77f6662c03d774f784c5

Custom theme renderer

As shown above, each individual tweets are using the my_module_status render theme. We'll define it in the my_module.module file:




function my_module_theme($existing, $type, $theme, $path) {
  $theme = [];
  $theme['my_module_status'] = array(
    'variables' => array(
      'status' => NULL
    ),
    'template' => 'twitter-status',
    'render element' => 'element',
    'path' => $path . '/templates'
  );

  return $theme;
}

With this, we are telling Drupal to use the template file modules/my_module/templates/twitter-status.twig.html for any render array using my_module_status as its theme.

Render caching

Drupal 8 does a good job caching content: typically any field formatter is only called once and the resulting collective render arrays are cached for subsequent page loads until the Drupal cache is cleared. We don't really want our Twitter block to be cached for that long. Since it is always great practice to keep caching enabled, we can define how caching is to be applied to our Twitter blocks. This is done by adding cache definitions in the render array before we return it:



      public function viewElements(...)
      {

        ...

        $element['#attached']['library'][] = 'my_module/twitter_intents';
        
        $element['#cache']['max-age'] = 60 * 5;

        return $element;
      }

Here we are telling Drupal to keep the render array in cache for 5 minutes. Drupal will still cache the rest of the page's elements how they want to be cached, but will call our field formatter again -- which pulls fresh data from Twitter -- if 5 minutes has passed since the last time it was called.

Jun 04 2016
Jun 04

Tom Friedhof

Senior Software Engineer

Tom has been designing and developing for the web since 2002 and got involved with Drupal in 2006. Previously he worked as a systems administrator for a large mortgage bank, managing servers and workstations, which is where he discovered his passion for automation and scripting. On his free time he enjoys camping with his wife and three kids.

Jun 03 2016
Jun 03

DrupalCamp is coming to the Twin Cities again this summer!

For those who don’t know, DrupalCamp is the name for a regional conference– a smaller version of the national DrupalCons. TwinCities DrupalCamp (aka TCDC) is a great opportunity for anyone who works with Drupal to learn, network and grow professionally. We encourage all our regional clients to attend!

Sponsoring

As we have every year since the initial 2011 TwinCities DrupaCamp, we are proud sponsors of the conference. Electric Citizen has been a Platinum sponsor for the past 5 years, and always happy to help this great local conference through sponsorships, design and volunteering.

Dan and Tim at the Electric Citizen table 2015

Organizing and Volunteering

Our wonderful project manager Keri is once again cat-herding the conference organizers, helping keep us on track, on time, and on-budget. While many great people help out every year to make TCDC a success, we couldn’t do it without Keri’s guiding hand. Big shout-out to all the volunteers and organizers!

Speaking

3 of us will be giving sessions at this year’s conference, so another good reason to attend. I will be giving a session on “How Long Do Websites Last”, examining the lifespan of a website, how sites can die, and ways to keep your site alive.

Our technical director Tim will be speaking on “Drupal vs Wordpress,” an age-old debate if you were to Google it, but from the perspective of how similar they’ve become, where the differ, and how to choose. No hate, just love for 2 well-known open source projects.

Finally, Keri will be teaming up with a colleague from another firm to discuss "Putting content at the center of your project." We’d like to help clients have a better plan at the beginning of a project for content planning, content strategy, content creation. Let’s not make content a secondary concern!

Are you coming?

Make sure you visit http://2016.tcdrupal.org today to register. There’s another day of FREE training sessions, 2 days of sessions, and fantastic social events. It’s a great time, you should be there. Save the date, June 23rd-26th. Coming up soon!

Jun 03 2016
Jun 03

On a recent project we had to create a section that is basically a Twitter search for a hashtag. It needed to be usuable in different sections of the layout and work the same. Also, we were using the Paragraphs module and came up with a pretty nifty (we think) solution of creating a custom field that solved this particular problem for us. I will walk you through how to create a custom field/widget/formatter for Drupal 8. There are Drupal console commands for generating boilerplate code for this... which I will list before going through each of the methods for the components.

Field Type creation

The first thing to do is create a custom field. In a custom module (here as "my_module") either run drupal:generate:fieldtype or create a file called HashTagSearchItem.php in src/Plugin/Field/FieldType. The basic structure for the class will be:



namespace Drupal\my_module\Plugin\Field\FieldType;

use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\TypedData\DataDefinition;


class HashtagSearchItem extends FieldItemBase {



}

Next, implement a few methods that will tell Drupal how our field will be structured. Provide a default field settings for the field that will be the count for the amount of tweets to pull. This will return of default settings keyed by the setting's name.



  
  public static function defaultFieldSettings() {
    return [
      'count' => 6
    ] + parent::defaultFieldSettings();
  }

Then provide the field item's properties. In this case there will be an input for hashtag and a count. Each property will be keyed by the property name and be a DataDefinition defining what the properties will hold.


  
  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
    $properties = [];
    $properties['hashtag_search'] = DataDefinition::create('string')
      ->setLabel(t('The hashtag to search for.'));
    $properties['count'] = DataDefinition::create('integer')
      ->setLabel(t('The count of twitter items to pull.'));
    return $properties;
  }

Then provide a schema for the field. This will be the properties that we have created above.


  
  public static function schema(FieldStorageDefinitionInterface $field_definition) {
    return [
      'columns' => [
        'hashtag_search' => [
          'type' => 'varchar',
          'length' => 32,
        ],
        'count' => [
          'type' => 'int',
          'default' => 6
        ]
      ]
    ];
  }

Field widget creation

Next create the widget for the field, which is the actual form element and it's settings. Either drupal:generate:fieldwidget or create a file in src/Plugin/Field/FieldWidget/ called HashtagSearchWidget.php. This is the class' skeleton:



namespace Drupal\my_module\Plugin\Field\FieldWidget;

use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;



class HashtagSearchWidget extends WidgetBase {
  
}

Then implement several methods. Provide a default count of tweets to pull for new fields and the settings form for the field item:


  
  public static function defaultSettings() {
    return [
      'default_count' => 6,
    ] + parent::defaultSettings();
  }

  
  public function settingsForm(array $form, FormStateInterface $form_state) {
    $elements = [];
    $elements['default_count'] = [
      '#type' => 'number',
      '#title' => $this->t('Default count'),
      '#default_value' => $this->getSetting('default_count'),
      '#empty_value' => '',
      '#min' => 1
    ];

    return $elements;
  }

  
  public function settingsSummary() {
    $summary = [];
    $summary[] = t('Default count: !count', array('!count' => $this->getSetting('default_count')));

    return $summary;
  }

Then create the actual form element. Add the hashtag textfield and count number field and wrap it in a fieldset for a better experience:


  
  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
    $item = $items[$delta];

    $element['hashtag_search'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Hashtag'),
      '#required' => FALSE,
      '#size' => 60,
      '#default_value' => (!$item->isEmpty()) ? $item->hashtag_search : NULL,
    ];

    $element['count'] = [
      '#type' => 'number',
      '#title' => $this->t('Pull count'),
      '#default_value' => $this->getSetting('default_count'),
      '#size' => 2
    ];

    $element += [
      '#type' => 'fieldset',
    ];

    return $element;
  }

In part 2, Bez will show you how to pull the tweets and create a field formatter for the display of the tweets. You can read that post here!

May 27 2016
May 27

In what has become an annual tradition, the Electric Citizen team traveled cross country for a Drupal-related conference.

DrupalCon North America is an yearly conference of the Drupal community, where over 3,000 designers, developers, project managers, marketers, and more gather to network, give presentations, learn, and have fun.

For myself and Tim, it was our 6th consecutive DrupalCon and Keri wasn’t far behind (Adam was the newbie). We each went to the conference to learn, learn, learn. Also to improve what we offer at Electric Citizen and grow as a business, for ourselves and for our clients. We all had our own highlights from the conference which we’d like to share:

Dan’s Highlights

Accessibility: We’re on a mission to continually improve our understand of accessibility and the web, and several talks at DrupalCon helped us out.

Content Strategy: as we as an agency increase our work in strategic planning, learning all we can about how others are carrying out content strategy, content audits and other best practices.

Managing Support: continually improving the support services we offer our clients is another high priority this year, and I got several tips on how to keep getting better (including ramping up our support team!)

Dan in front of DrupalCon entrance to exhibit hall

Tim’ Highlights

Progressive decoupling: learning new techniques for bringing the latest in front-end development scripting over to Drupal, such as Angular.js.

Next Level Git: picking up tips and reminders of how much more we could do as a team with version control

Drupal 8: getting up to speed with everything that's new and improved with Drupal 8. The latest version of Drupal is slowly becoming part of our new builds.

Scene from the Drupal 6 "funeral"

Adam’s Highlights

Component-based design: how to better utilize “atomic design” and PatternLab in our design and theming process, so we are designing around reusable components and systems instead of simply “web pages”

Keri’s Highlights

Wireframes to Widgets: we would like to continue improving our design and theming process and making it easier for a clients and developers to visualize and interact with websites as they are being designed. She picked up some ideas and techniques for better prototyping alongside Drupal. 

Drupacon trivia night at World War II museum

Garden District New Orleans

Tim enjoying some crawfish

May 14 2016
May 14

Tom Friedhof

Senior Software Engineer

Tom has been designing and developing for the web since 2002 and got involved with Drupal in 2006. Previously he worked as a systems administrator for a large mortgage bank, managing servers and workstations, which is where he discovered his passion for automation and scripting. On his free time he enjoys camping with his wife and three kids.

May 10 2016
May 10

I've been theming with Drupal since version 6 back in 2009. In the intervening years, I've seen a lot of changes of how theming and front-end development is approached and have tried to keep up with the latest trends and adhere to best practices. Drupal 8 takes theming to a whole new level; it feels elegant, freeing, uniform, cohesive, and logical. So there's a lot to get excited about here.

In Drupal 8, there's templates for just about anything and with Drupal 8's new theme hooks, you can really narrow in on crafting a custom template for your specific use case. This makes getting at and altering markup far more accessible than it was in Drupal 6 and 7.

My preferred method for Drupal 8 theming is using Classy as my base theme, creating a sub-theme off of that and then overriding the parts that are necessary. An override is as simple as copying one of Classy's 100+ templates into your Classy based sub-theme. Sometimes, an out of the box template is not enough so you can create your own theme hook in that case.

In this post, I'll help you get started with creating Drupal 8 custom theme hook suggestions and templates. I also refer to various tools that I use, you can read more about them in my article, Drupal 8 Development: 5 Things to Add to Your Theming Toolbox.

Overview

The goal for this tutorial is to create a theme hook and subsequent template for a group of block regions in our site. The regions are called postscript_one, postscript_two, and postscript_three. The Drupal API function we will use for this is hook_theme_suggestions_HOOK_alter where "HOOK" is the name of the theme hook that we see in our Twig debug area. So essentially, we'll grab this theme hook and make a template suggestion for an array of these regions.

Getting Started

To start off, I'll place a block in one of my postscript regions and then examine the placed block with Web Inspector. Here is what I see when we do that:


From the above, we see the theme hook name, region as well as a two template suggestions. What I am after here is a common region template for any of the the three postscript regions that I mentioned earlier. Time to fire up Devel Kint and see what we can learn by inspecting the region array. For this, I will initiate my theme hook function in my theme's .theme file. Of note is hibiscus, my Classy sub-theme theme name and the HOOK, "region".

/\*\*

- Implements hook_theme_suggestions_HOOK_alter().
  \*/
  function hibiscus_theme_suggestions_region_alter(&$suggestions, $vars, $hook) {

}

Now I'll enable kint() as kint($vars);:

function hibiscus_theme_suggestions_region_alter(&$suggestions, $vars, $hook) {
  kint($vars);
}

After I run drupal cache:rebuild all using Drupal Console, I'll now see kint print out the region array on my page. As you can see from the below image.

Kint search in action

Construct the theme hook

Using Kint search, we can search for and copy the path for the region name definition, $var['elements']['#region'] It is from this that we will construct our array of regions and subsequent theme hook. First I define the region name variable from the above:

$region = $vars['elements']['#region'];

Now I'll define an array of my three regions:

$region_postscript = array(
'postscript_first',
'postscript_second',
'postscript_third',
);

Next I construct an if statement to make the actual theme suggestion:

if (in_array($region, $region_postscript)) {
$suggestions[] = 'region' . '\_\_' . 'postscript';
}

What this does is say, if it's any of the three postscript regions, create a theme hook that's common to all these regions.

Putting it all together

Putting the entire function all together, we now have:

/\*\*

- Implements hook_theme_suggestions_HOOK_alter().
  \*/
  function hibiscus_theme_suggestions_region_alter(&$suggestions, $vars, $hook) {

// Define a variable for the #region name.
$region = $vars['elements']['#region'];

// Define an array of our postscript regions.
$region_postscript = array(
'postscript_first',
'postscript_second',
'postscript_third',
);

// If we are in any of these regions, create a theme hook suggestion.
if (in_array($region, $region_postscript)) {
$suggestions[] = 'region' . '\_\_' . 'postscript';
}
}

Now that I have written my theme hook, I can clear cache and reload the page to see my new theme hook suggestion in action.


May 07 2016
May 07

Drupal 8 has greatly improved editor experience out-of-the-box. It comes shipped with CKEditor for WYSIWYG editing. Although, D8 ships with a custom build of CKEditor and it may not have the plugins that you would like to have or that your client wants to have. I will show you how to add new plugins into the CKEditor that comes with Drupal 8.

Adding plugins with a button

First, create a bare-bones custom module called editor_experience. Files will be added here that will tell Drupal that there is a new CKEditor plugin. Find a plugin to actually install... for the first example I will use bootstrap buttons ckeditor plugin. Place the downloaded plugin inside libraries directory at the root of the Drupal installation; or use a make file to place it there. Also make sure you have the libraries module installed drupal module:download libraries.

Create a file inside of the editor_experience module inside of src/Plugin/CKEditorPlugin called BtButton.php. Add the name space and the two use statements shown below.





namespace Drupal\editor_experience\Plugin\CKEditorPlugin;

use Drupal\ckeditor\CKEditorPluginBase;
use Drupal\editor\Entity\Editor;


class BtButton extends CKEditorPluginBase {

  ... 

}

The annotation @CKEditorPlugin tells Drupal there is a plugin for CKEditor to load. For the id, use the name of the plugin as defined in the plugin.js file that came with the btbutton download. Now we add several methods to our BtButton class.

First method will return false since it is not part of the internal CKEditor build.





public function isInternal() {
  return FALSE;
}

Next method will get the plugin's javascript file.





public function getFile() {
  return libraries_get_path('btbutton') . '/plugin.js';
}

Let Drupal know where your button is. Be sure that the key is set to the name of the plugin. In this case btbutton.





  public function getButtons() {
    return [
      'btbutton' => [
        'label' => t('Bootstrap Buttons'),
        'image' => libraries_get_path('btbutton') . '/icons/btbutton.png'
      ]
    ];
  }

Also implement getConfig() and return an empty array since this plugin has no configurations.

Then go to admin/config/content/formats/manage/basic_html or whatever format you have that uses the CKEditor and pull the Bootstrap button icon down into the toolbar.

Now the button is available for use on the CKEditor!

Adding plugins without a button (CKEditor font)

Some plugins do not come with a button png that allows users to drag the tool into the configuration, so what then?

In order to get a plugin into Drupal that does not have a button, the implementation of getButtons() is a little different. For example to add the Font/Font size dropdowns use image_alternative like below:





 public function getButtons() {
   return [
     'Font' => [
       'label' => t('Font'),
       'image_alternative' => [
         '#type' => 'inline_template',
         '#template' => '{{ font }}',
         '#context' => [
           'font' => t('Font'),
         ],
       ],
     ],
     'FontSize' => [
       'label' => t('Font Size'),
       'image_alternative' => [
         '#type' => 'inline_template',
         '#template' => '{{ font }}',
         '#context' => [
           'font' => t('Font Size'),
         ],
       ],
     ],
   ];
}

Then pull in the dropdown the same way the Bootstrap button plugin was added! Have any questions? Comment below or tweet us @activelamp.

Apr 14 2016
Apr 14

Drupal 8 performance: the Supercache module

Post date: 

Thu, 04/14/2016 - 00:00

Difficulty: 

Piece of Cake

The Supercache module is the result of an attempt to improve Drupal 8 efficiency when dealing with cache tag management and other design issues with several caching components that make it a pain to deal with Drupal 8 based applications that change a lot. 

An out of the box Drupal 8 install will issue about 2,100 database statements for a simple task such as performing a log in and creating two articles.

With a little setup and the Supercache module I was able to bring that down to 240 statements.

Here is a video proof that these numbers are real. The statement count is being measured real time thanks to the awesome SQL Server Profiler tool.

[embedded content]

The impact of the Supercache module - that was for a while a core patch - was benchmarked and proved to reduce wall times by about 25% and database queries by as much as 50% after things change (doing a cache write).

How does the Supercache module do this?

  • Drupal's cache system got heavier in Drupal 8 at the expense of making it more precise thanks to cache tags. But there are situations where you simply do not need all that bloatage. The Supercache module introduces a new and sleeker cache layer (of course without cache tags). A simple cache tag stored in Drupal's cache system takes up 196 bytes. The new caching system only uses 12 bytes. This does not seam like a big deal after all, it's just a few bytes difference. But it translates to being able to store 65,000 cache tags in 1MB of APCu/Wincache instead of just 5,000, But that is not the only advantage of this cache layer:
    • Reduced storage size, up to x12 less for small cache items.
    • Levarage native functionalities provided by most storage backends such as touch, counter, increment, etc.
    • Faster processing due to lack of cache tags and other extras.
    • Scalar types are stored natively so you can batch operate on the cache items themselves if the storage backend allows you to do so (database, couchbase or mongo)
  • Drupal 8 introduced the very useful ChainedFastBackend (that you can easily use in Drupal 7). But the current implementation of that backend has some design flaws - such as invalidating the whole fast backend when doing a cache write or not using cache tags in the fast backend. Supercache replaces the ChainedFastBackend implementation with one that solves those two issues improving hit rates in the fast backend on systems with lots of writes.
  • Replaces the default cache tag invalidator services (that works directly on the database) for one that leverages a similar concept to the ChainedFastBackend.
  • Introduces the ability for the key value storage to work similarly to the ChainedFastBackend.

To properly leverage what the Supercache module has to offer you should setup support for a centralized caching backend such as Couchbase

By: root Thursday, April 14, 2016 - 00:00

Apr 09 2016
Apr 09

Tom Friedhof

Senior Software Engineer

Tom has been designing and developing for the web since 2002 and got involved with Drupal in 2006. Previously he worked as a systems administrator for a large mortgage bank, managing servers and workstations, which is where he discovered his passion for automation and scripting. On his free time he enjoys camping with his wife and three kids.

Apr 04 2016
Apr 04

Tom Friedhof

Senior Software Engineer

Tom has been designing and developing for the web since 2002 and got involved with Drupal in 2006. Previously he worked as a systems administrator for a large mortgage bank, managing servers and workstations, which is where he discovered his passion for automation and scripting. On his free time he enjoys camping with his wife and three kids.

Mar 29 2016
Mar 29

You're working on a client's existing site, and they ask for some new functionality. Your first thoughts are: "Yay, some work!" Quickly following that you think: "What do they want? How am I going to build it?" The often unasked question, however, is "How can I integrate this new functionality with what they already have, without making maintenance a nightmare, or even worse, impacting existing functionality?"

This is a tough ask, as the new feature has the potential to reach across multiple pages, content types and styles.

We can build it!

For simpler features, it's easier to confidently introduce the new parts. For example, if your client wants a blog, you know that you can create a blog content type, give it an image field and a taxonomy reference field for tagging, and you're almost done. Build a couple of views displays and wrap the lot up in two features. One hook_update_N() function later and you have a scripted deployment to add your new blog articles, listing, tags and menu items in a single  drush updb command. Easy, right?

But even with this simple example, what about theming?

The example assumes that there's no particular theming requirements for the blog articles and listing, and that the theme will handle all of the elements gracefully. But if changes are required, how do you prevent inadvertent CSS bleed from new style rules over into existing content types. Equally, if new styles are introduced, should they apply elsewhere on the site? Should, in fact, any new blog-related CSS live in the module layer, with the definitions of all the functionality, keeping all the related code together? Or should we dive into the theme and add styles that would become redundant if the blog were ever deprecated?

These questions deserve consideration, and I fear that they are often ignored, with a blasé "of course CSS goes in the theme!" attitude.

There is evil there that does not sleep.

Let's talk about Webform.

The Webform module is amazing. It is really powerful and allows site builders and site managers/editors to quickly craft complex forms for capturing and storing data, reporting on that data and even allows limited workflow functionality.

But for many business cases, the out of the box functionality simply is not enough. They want to be able to take payments and to sell things - tickets, time, etc. They want to be able to prepopulate fields with values and modify form elements based upon other field values.

Now we need to start really thinking.

We can modify form elements with hook_form_alter() - that's fine. But how do we limit it to webforms? OK, we can use the Form ID - it has the form 'webform_client_form_NID' so we can look for that. But each webform has a different numerical ID, so how can we limit our customizations to just a certain form, or a certain subset of forms? This is where we can get messy and we need to remember that this functionality is for life (well, the life of the project), not just for Christmas. However we do this, it needs to be maintainable.

Possible solutions.

If we want to identify a single specific webform, we can use its Node ID, which is an obvious choice. We can put that NID into a variable and into settings.php, which avoids hard-coding the NID into the functionality. The drawback of this method is that NIDs can change between environments, so your donation form might have a different NID on your development environment than it does on the live environment. This, at the very least, complicates deployments.

If we want to modify a subset of webforms, maybe we can use a content type. For example, Webform can be attached to any content type, so we can easily create a 'donation form' content type and make our modifications to any webform of type 'donation form'. In this way we can manage separate functional modifications to other forms, e.g. event registration or lead generation. This too works, up to the point where the site owner wants to combine both your donation/payment functionality and your event registration functionality into one form. How does one rationalise and implement such a request? And how do you then limit this combined functionality to specific forms? Are we back to square one with node IDs set in settings.php, and its attendant maintenance nightmare?

There has to be a better way.

I quite like to use fields to bring configurability to new functionality. A couple of fields in a vertical tab field group lends a nice air of a proper settings form to your node edit page. Once you interrogate your node for the values of these settings fields, you can then control your custom functionality at a per node level, which is a really neat thing to be able to do from both a site admin and site maintenance point of view.

The problem here is that you've two separate items that are tightly coupled: your nifty new module/code and field configuration updates to any content type that you want to make use of it. You then need to bear in mind that you need to maintain the Features for any such content type. It would probably be worth bringing this logic to its natural next step, creating a Feature for all the Field Bases of your new fields, guaranteeing that they exist and are consistent with the machine names expected by your custom code (and more easily allowing field bases to be shared across content types/features without conflicts).

Somewhere, over the rainbow

Here's my version of the Holy Grail: imagine a system where you could indicate on a settings page, which custom functionality should apply to which content types, and settings controls would then magically appear on the relevant node edit forms... As of this writing I have not seen a working model of such a system, but I can dream.

Maintenance of your site should never be far from your thoughts when introducing new features/functionality. As Support Lead for Annertech, I have to deal with a lot of legacy code, and therefore think about these things a lot. If you'd like to chat about support for your Drupal website, or ongoing development in a sustainable, maintainable way, give us a shout.

Mar 29 2016
Mar 29

(From left: Last Call Media’s Alan Wolf, Kelly Albrecht, and Brianna Doxzen with conference organizer Sven Aas and Keynote speaker Dave Cameron)

Recently, I had the honor of attending the 2016 HighEdWeb New England regional conference. It was a fantastic group of higher education web professionals from across New England, the surrounding regions and a long plane ride (or two) beyond. 

All Drupal All the Time

At Last Call we’re pretty invested in the Drupal community. And when I say pretty invested, I mean we’re completely invested. We live and breathe Drupal and open source everyday. We go to conferences all over the world, sharing our own expertise and learning from others in the open source community. This event was our first in the HighEdWeb Association community and what a fabulous introduction it was!

We Like to Build Stuff

We are made up of insatiably curious and driven problem solvers. We love to tinker. When we have spare time, or are between tasks and need to clear our minds with a fun activity, we design cool gifs, add neat features to our own site like “making it snow,” or — in HeWebNE’s case— build a Twitter Train.

We were honored to be able to sponsor HeWebNE as their Gold Sponsor to support Dave Cameron’s moving keynote as well as accessibility (sign language interpreters and live captioning) to support Svetlana Kouznetsova’s session “Communication Access for Deaf Students & Employees“. I mention this because Gold Sponsorship also came with the added benefit of a slightly larger exhibitor space. When we were informed of the increased real estate our first thought went to: room for activities

Ahem, I mean, the Twitter Train.

Now, this was not the debut of our Twitter Train. In fact, HeWebNE marked it coming out of retirement. Previously the Twitter Train has been seen at The United Nations in NYC in participation of NYCCamp, a regional Drupal event.

What’s a Twitter Train, Anyway?

Previously we’d written a program in Python for a Raspberry Pi to hook to a toy train and have it run around the track when triggered by a follow on our Twitter, @LastCallMedia. This was cool, but it meant that participants could only interact with the train once, and where’s the fun in that? For this event we added mentions, so you could follow us on Twitter and mention us as much as you wanted to make the train “choo-choo” as the program triggered the sound effect through an attached speaker. This was immensely more satisfying, or at least it was when we first set it up in the office as our developers (and I) tweeted to get it to move non-stop for about an hour… because trains.

The train in action GIF(The train in action)

What If the Internet Didn’t Exist : The Horror

Internet is crucial, that goes without saying really, but in this case we needed the Raspberry Pi to be connected to internet to receive the Twitter trigger to run the program. When we got to the conference we were unable to connect as there were issues within the facilities. Despite the herculean efforts of the conference organizers, the ethernet ports, and then with our Raspberry Pi’s Wi-Fi dongle, the train wasn’t choo-ing.

A Reroute to Show The World

So it seemed as if the Twitter train was something of a fail. 

Why am I blogging to the world about what some might see as a failure? Because people came together to help. Not just the wonderful organizers, but random attendees, too! We each had a different piece of the puzzle, from knowledge of laying toy train tracks to finding a computer with a working ethernet port. Eventually, Kelly saved the day by setting up a DHCP server on his laptop to give the Raspberry Pi an IP address and to shell into it directly. It was a beautiful coming together of people that spoke so strongly to this particular higher ed community.

We’re Stronger Together

The collaboration and willingness to share is exactly why we do what we do. It’s why we love the open source community and now it’s why we love the HighEdWeb Association community, too. By lunch time the Twitter Train was racing around the track, powered by a mini Twitter storm.

The high ed web community as viewed from above(The wonderful HighEdWeb New England Community as seen from above!)

Thank you to everyone that made the Twitter Train and HighEdWeb New England possible. We can’t wait to see you next year!

Mar 01 2016
Mar 01

This was the user story: "As a site admin, I want to be able to add different types of content chunks on any page with customisable backgrounds, so that we have control over the style and layout of our website". Yes, it's a pretty big user story, but bear with me.

When working with a large government client recently we made special efforts to ensure that all the content on the website was developed in a structured manner and to develop mechanisms for what Karen McGrane calls 'chunks' of data instead of a 'blob' in the body field. We had been trialling the Paragraphs module on a number of sites (being cautious because of some issues that it's older relation Field Collection had with multilingual and revisions when used with Workbench Moderation). We gave Paragraphs the full treatment on this site, creating paragraph bundles for section headings, text blocks, image (with optional caption and float left/right capabilities), slideshow, call to action, and more.

Drupal, adding structured content

We then created a paragraph bundle called 'Structured Content Block' which had two fields: a background colour field and a multivalue embedded paragraphs field of all the above named paragraphs, a concept we called 'The Russian Doll'. This meant that the client could create any type of content within their pages and reorder it at will.

Drupal, adding structured content items

Another requirement was the ability to create landing pages on the fly, with some default content on every landing page, but all other aspects of the page customisable, while retaining a content model of one single source of truth. To achieve this, we used Panels and Panelizer. This meant we could create a content type called "Landing Page" and set some default views that would be placed on all landing pages. These were further restricted by only showing nodes in the views that were also tagged with the same taxonomy terms that were found on the landing page. So, a landing page of Type: A, and Topic B, and Sub-topic C would only show news items that had the same taxonomy terms selected. Next we created a panels plugin with lots of regions to allow the client maximum flexibility when it came to how the pages would be laid out. This screenshot shows about half of those regions.

Drupal, custom panelizer page

A further requirement was that all panes needed to be customisable in terms of background colours and the content that could go into them. So, they needed to be able to accept the standard things that panels can - existing nodes, views content panes, blocks and other widgets. However, the client also wanted to be able to add ad hoc (though reusable) chunks on these landing pages. To achieve this we used Fieldable Panel Panes, and 'The Russian Doll' approach from above. Fielded panels panes had two fields - background style and embedded paragraphs. This allowed the client to create panes with a background colour and then create paragraphs inside these of different content chunk types. The panel panes are re-usable, so if you create one for one landing page, you can then use it again in another one. Here's the set up for a fielded panel pane (which we called "Landing Page Item"):

Drupal, adding fieldable panel pane

This was the user story: "As a site admin, I want to be able to add different types of content chunks on any page with customisable backgrounds, so that we have control over the style and layout of our website". Yes, it was a pretty big user story, but we beared with it.

Now, your turn - what's your approach to structured content in Drupal? What's your experience of Paragraphs module? More specifically, have you any thougths on the mixture of paragraphs with panelizer and revisions/workbench moderation (especially on multilingual websites)? Let us know in the comments.

Mar 01 2016
Mar 01

The biggest thing that got me excited with Drupal 8 is the first-class use of services & dependency-injection throughout the entire system. From aspects like routing, templating, managing configuration, querying and persisting data, you name it -- everything is done with services. This is a great thing, because it grants developers a level of flexibility in extending Drupal that is far greater than what Drupal 7 was able to.

I'll walk you through a few strategies of extending existing functionality, leveraging the power of Symfony's DependencyInjection component.

Example 1: Inheritance

Since everything in Drupal 8 can be traced back to a method call on an object within the service container, using inheritance to modify core behavior is a valid strategy.

For example, say we want to add the ability to specify the hostname when generating absolute URLs using Drupal\Core\Url::fromRoute. A quick look at this method will tell you that all the work is actually done by the @url_generator service (a lot of Drupal 8's API is set up like this -- a static method that simply delegates work to a service from the container somehow).

drupal/core/core.services.yml can tell us a lot about what makes up the @url_generator service:

services:
...
url_generator:
class: 'Drupal\Core\Render\MetadataBubblingUrlGenerator'
arguments: ['@url_generator.non_bubbling', '@renderer']
calls: - [setContext, ['@?router.request_context']]

url_generator.non_bubbling:
class: 'Drupal\Core\Routing\UrlGenerator'
arguments: ['@router.route_provider', '@path_processor_manager', '@route_processor_manager', '@request_stack', '%filter_protocols%']
public: false
calls: - [setContext, ['@?router.request_context']]
...

A peek at Drupal\Core\Render\MetadataBubblingUrlGenerator will actually show that it just delegates the core work of constructing a URL to the @url_generator.non_bubbling service.

To add the desired ability to the URL generator, we will have to write a new class that handles the extra logic:



namespace Drupal\foo\Routing;

use Drupal\Core\Routing\UrlGenerator;

class HostOverridingUrlGenerator extends UrlGenerator
{
    public function generateFromRoute(
      $name,
      $parameters = array(),
      $options = array(),
      $collected_bubbleable_metadata = NULL
    ) {

        
        $hasHostOverride = array_key_exists('host', $options) && $options['host'];

        if ($hasHostOverride) {
            
            $originalHost = $this->context->getHost();
            $this->context->setHost((string) $options['host']);
            $options['absolute'] = true;
        }

        $result = parent::generateFromRoute($name, $parameters, $options, $collected_bubbleable_metadata);

        if ($hasHostOverride) {
            
            $this->context->setHost($originalHost);
        }

        return $result;
    }
}

We now have a new type that is capable of overriding the hosts in absolute URLs that are generated. The next step is to tell Drupal use this in favor of the original. We can do that by manipulating the existing definition through a service provider.

Service Providers

Drupal will look for a service provider in your module directory and will hand it the container-builder for it to be manipulated. Telling Drupal to use our new class is not done by editing drupal/core/core.services.yml but by modifying the definition through the service provider:






namespace Drupal\foo;

use Drupal\Core\DependencyInjection\ServiceProviderInterface;
use Drupal\Core\DependencyInjection\ContainerBuilder;

class FooServiceProvider  implements ServiceProviderInterface
{
    public function register(ContainerBuilder $container)
    {
        $urlGenerator = $container->getDefinition('url_generator.non_bubbling');
        $urlGenerator->setClass(__NAMESPACE__ . '\Routing\HostOverridingUrlGenerator');
    }
}

Done! Once the foo module is installed, your service provider will now have the chance to modify the service container as it see fit.

Example 2: Decorators

In a nutshell, a decorator modifies the functionality of an existing object not by extending its type but by wrapping the object and putting logic around the existing functionality.

This distinction between extending the object's type versus wrapping it seems trivial, but in practice it can be very powerful. It means you can change an object's behavior at run-time, with the added benefit of not having to care what the object's type is. An update to Drupal core could change the type (a.k.a. the class) of a service at any point, as long as the substitute object still respect the agreed contract imposed by an interface, then the decorator would still work.

Say another module declared a service named @twitter_feed and we want to cache the result of some expensive method call:



use Drupal\foo\Twitter;

use Drupal\some_twitter_module\Api\TwitterFeedInterface;
use Drupal\Core\Cache\CacheBackendInterface;

class CachingTwitterFeed implements TwitterFeedInterface
{
      public function __construct(TwitterFeedInterface $feed, CacheBackendInterface $cache)
      {
          $this->feed = $feed;
          $this->cache = $cache;
      }

      public function getLatestTweet($handle)
      {

          
          if ($this->cache->has($handle)) {
            return $this->cache->get($handle);
          }

          
          
          $tweet = $this->feed->getLatestTweet($handle);
          $this->cache->set($handle, $tweet, 60 * 60 * 5); 

          return $tweet;
      }

      public function setAuthenticationToken($token)
      {
          
          return $this->feed->setAuthenticationToken($token);
      }
}

To tell Drupal to decorate a service, you can do so in YAML notation:



services:
cached.twitter_feed:
class: 'Drupal\foo\Twitter\CachingTwitterFeed'
decorates: 'twitter_feed'
arguments: ['@twitter_feed.inner', '@cache.twittter_feed']

With this in place, all references and services requiring @twitter_feed will get our instance that does caching instead.

The original @twitter_feed service will be renamed to @twitter_feed.inner by convention.

Decorators vs sub-classes

Decorators are perfect for when you need to add logic around existing ones. One beauty behind decorators is that it doesn't need to know the actual type of the object it tries to change. It only needs to know what methods it responds to i.e. it only cares about the objects interface, and not much else.

Another beautiful thing is that you can effectively modify the object's behavior at run-time:




$feed = new TwitterFeed();
$feed->setAuthenticationToken($token);

if ($isProduction) { 
  $feed = new CachingTwitterFeed($feed, $cache);
}

$feed->getLastTweet(...);

or:



$feed = new TwitterFeed();
$cachedFeed = new CachingTwitterFeed($feed, $cache);

$feed->setAuthenticationToken($token);

$feed->getLastTweet(...) 
$cachedFeed->getLastTweet(...) 

$feed->setAuthenticateToken($newToken); 

Compare that to if you have the caching version as a sub-class, then you'll need to instantiate two objects, and (re)authenticate both.

And lastly, one cool thing about decorators is you can layer them with greater flexibility:



$feed = new TwitterFeed();

$feed = new CachingTwitterFeed($feed, $cache); 
$feed = new LoggingTwitterFeed($feed); 
$feed = new CachingTwitterFeed(new LoggingTwitterFeed($feed), $cache); 

These are contrived examples but I hope you get the gist.

However there are cases where using decorators just wouldn't cut it (for example, if you need to access a protected property or method, which you can't do with decorators). I'd say that if you can accomplish the necessary modifications using only an object's public API, think about achieving it using decorator(s) instead and see if it's advantageous.

The HostOverridingUrlGenerator can actually be written as a decorator, as we can achieve the required operations using the objects public API only -- instead of using $this->context, we can use $this->inner->getContext() instead, etc.

In fact, the @url_generator service, an instance of Drupal\Core\Render\MetadataBubblingUrlGenerator > is a decorator in itself. The host override behavior can be modelled as:

new MetadataBubblingUrlGenerator(new HostOverridingUrlGenerator(new UrlGenerator(...)), ...)

One down-side of using decorators is you will end up with a bunch of boilerplate logic of simply passing parameters to the inner object without doing much else. It will also break if there are any changes to the interface, although this shouldn't happen until a next major version bump.

Composites

You might want to create a new service whose functionality (or part thereof) involves the application of multiple objects that it manages.

For example:




use Drupal\foo\Processor;

class CompositeProcessor implements ProcessorInterface
{
    
    protected $processors = array();

    public function process($value)
    {
        
        foreach ($this->processors as $processor) {
            $value = $processor->process($value);
        }

        return $value;
    }

    public function addProcessor(ProcessorInterface $processor)
    {
        $this->processors[] = $processor;
    }
}

Composite objects like this are quite common, and there are a bunch of them in Drupal 8 as well. Traditionally, an object that wants to be added to the collection must be declared as tagged service. They are then gathered together during a compiler pass and added to the composite object's definition.

In Drupal 8, you don't need to code the compiler pass logic anymore. You can just tag your composite service as a service_collector, like so:




services:
the_processor:
class: 'Drupal\foo\Processor\CompositeProcessor'
tags: - { name: 'service_collector', tag: 'awesome_processor', call: 'addProcessor' }

    bar_processor:
      class: 'Drupal\foo\Processor\BarProcessor'
      arguments: [ '@bar' ]
      tags:
        - { name: 'awesome_processor' }

    foo_processor:
      class: 'Drupal\foo\Processor\FooProcessor'
      arguments: [ '@foo' ]
      tags:
        - { name: 'awesome_processor' }

With this configuration, the service container will make sure that @bar_processor and @foo_processor are injected into the @the_processor service whenever you ask for it. This also allows other modules to hook into your service by tagging their service with awesome_processor, which is great.

Conclusion

These are just a few OOP techniques that the addition of a dependency injection component has opened up to Drupal 8 development. These are things that PHP developers using Symfony2, Laravel, ZF2 (using its own DI component, Zend\Di), and many others have enjoyed in the recent years, and they are now ripe for the taking by the Drupal community.

For more info on Symfony's DependencyInjection component, head to http://symfony.com/doc/2.7/components/dependency_injection/index.html. I urge you to read up on manipulating the container-builder in code as there are a bunch of things you can do in service providers that you can't achieve by using the YAML notation.

If you have any questions, comments, criticisms, or some insights to share, feel free to leave a comment! Happy coding!f you have any questions, comments, criticisms, etc feel free to leave a comment! Happy coding!

Feb 28 2016
Feb 28

This is a technical comparison of the older 2.x branch of the Simple XML sitemap module and an older development version of XML sitemap. XML sitemap has just had its first release and for more on the newer 4.x branch of Simple XML sitemap see this article.

This comparison may be interesting for XML sitemap users moving to Drupal 8 or for users intending to wait for the port. (Do not wait, help out!) Please also be advised that gbyte made Simple XML sitemap, which makes this comparison intrinsically biased.

There are major differences between Simple XML sitemap (simple_sitemap) and XML sitemap (xmlsitemap) and depending on your use case, you might want to choose one or the other.

What sets the modules apart are their complexity, extensibility, performance and feature sets.

Code base

Having been built specifically for D8, simple_sitemap has arguably a cleaner code base adhering to D8 standards i (i.e. use of OOP). In contrast, the xmlsitemap module will have a hard time adjusting to D8 technologies and guidelines, as it carries around a whole lot of legacy code going back as far as Drupal 5.

Performance

What is meant here is the impact of the module on a Drupal 8 system, how quick the sitemap generation process is and how long it takes for a visitor to fetch the sitemap.

Sitemap generation performance

Both the modules feature the batch API which allows to generate huge amounts of links without hitting any PHP limits. The sitemap generation performance differs in that it is a one step process in simple_sitemap as opposed to the two step process of the xmlsitemap module.

While the simple_sitemap's one step process is less error prone and initially generates the sitemap quicker, xmlsitemap's two steps have the advantage of tracking which entities have changed since last generation through implementing an additional database table making sure the subsequent rebuilds are quicker.

Sitemap fetching performance

When it comes to fetching the sitemap, both modules cache them: xmlsitemap creates a physical file, while simple_sitemap caches the sitemap in the database.
Both modules have the ability to set the maximum amount of links included in the sitemap and they generate multiple sitemaps along with an index if this amount is exceeded.

Consequently there should be no noticeable difference between the two modules when it comes to fetching performance.

Overall system impact

Because of its leaner code base and the fact that the code does not get invoked through hooks all to often, simple_sitemap's footprint is smaller.

Supported entities

Both of the modules support all core content entity types like nodes, taxonomy terms, menu links, users, etc. as well as contributed entity types (e.g. commerce products or media) out of the box. This is possible due to the great D8 entity API. Whereas xmlsitemap keeps its bundle settings on a designated page, simple_sitemap incorporates its bundle settings into bundle edit pages, which seems a bit more intuitive. Both modules allow overriding of sitemap settings on a per-entity basis.

XML Sitemap standards

Here the edge goes to simple_sitemap, as it features the newer hreflang XML standard developed by google. In addition simple_sitemap is optionally able to index images attached to an entity resulting in an image sitemap. Adhering to Google's standards is important, as this is the search engine most of us would like to correctly understand and index our site.

Additional functionality

Having been around since 2007, the xmlsitemap module has had lots of time to incorporate various additional functionalities which are not present in simple_sitemap yet. Automatically sending the stiemap to search engines is an example of such a functionality.

Extensibility

Whereas xmlsitemap offers many hooks making it easy to alter the XML output, simple_sitemap's strength lies within its powerful service API allowing to chain tasks like adding custom links and altering configuration. Since version 2.11, simple_sitemap takes advantage of plugins, so new URL generators can be implemented by 3rd party modules. Depending on your needs, you may find one approach superior to the other.

Which one is right for me?

As of now, simple_sitemap is the more stable module having close to none open bug reports. As soon as xmlsitemap runs well in Drupal 8 however, you will have to decide: simple_sitemap for a more performant codebase with the newer sitemap standard and more powerful API, or xmlsitemap for its bigger feature set and a smarter sitemap generation process.

Feb 26 2016
Feb 26

Oxfam Ireland wanted a faster site. It was already pretty performant, especially for a high traffic site, chock-full of features, but with the ever upward march of mobile usage, performance rose to the top of the want list.

Why do you want a fast site?

The reasons are many, and often not even on a site-owner's agenda when planning a site. Here's a few:

  1. Slow pages cost you conversions (AKA money!). Research back in 2006 by Greg Linden showed that every 100 millisecond delay in page rendering time resulted in a 1% loss of sales for Amazon. 
  2. Google prefers faster sites, so it impacts on your search rankings
  3. If you're on a mobile device over a dodgy connection you just aren't likely to hang around waiting for a slow page to load. Face it, you'll get bored and load up Twitter instead.

What counts as fast?

How fast is fast? Well for that matter, what counts as a page load? When is it 'done'? There's lots of tools and lots of metrics. e.g.

  • Actual time to finish loading a page
  • Time to first byte i.e. when the first packet (piece) of information hits your browser.
  • Time to usable - this is where everything on the page is not loaded, but there's enough core content there to keep a user happy while the rest finishes downloading.
  • onLoad event - this is a Javascript event that fires when all the Javascript is loaded and therefore all the bells and whistles should work.
  • Perceived performance - this is more nebulous, but arguably the most important thing to consider. How fast does the site feel to the user? Does the important stuff at the top load quickly and first so they have something to look at? Can you click around whilst the rest loads?

We've done a bit of a deep dive on performance testing here, if you want to know more. 

How we made Oxfam's website faster

Performance is not binary. There are a lot of variables to consider. When speeding up oxfamireland.org, we took a three-pronged approach.

  • server infrastructure.
  • Drupal tuning
  • a home-page redesign.

Each of these avenues of attack improved matters and each one worked in tandem with the other two to achieve measurable, demonstrable speed improvements.

Server Infrastructure

This was arguably the easiest improvement to implement. We moved the site from a dedicated, generic, virtual private server to our high availability, high performance platform. Tuned specifically for running big Drupal sites, our hosting environment comes fully loaded with a Varnish reverse-proxy server and Redis (an advanced key-value store) for lightning-quick server response.

In addition to simply putting the site on our platform we also made a few changes at Apache level, to better serve certain elements of a page, for example, compressing SVG files as they were served.

Drupal Tuning

Within the guts of Drupal we found plenty of small wins. We:

  • disabled some unneeded modules.
  • deleted other modules that had found their way on to the site but remained unused to reduce the amount of PHP that had to be parsed.
  • isolated and solved small, otherwise inconsequential PHP errors and notices to relieve pressure from the database
  • minified HTML, JS and CSS to reduce page weight
  • moved as much JS as possible to the bottom of the page so that it would not block other elements from loading
  • ensured that no modules were setting unnecessary cookies that might interfere with Varnish

Home Page Redesign

Home pages are notoriously heavyweight. Typically, home pages tend to have rich multimedia experiences, laying out a site's wares up front in the hope that a user can easily navigate to any part of the site from there.

Heavy pages take ages to load. Pages with lots of images take longer to load. High resolution, uncompressed images squished into a carousel/slider exacerbate slow loading. Throw in a good helping of Javascript and you're unlikely to see your page load this side of the weekend.

Whilst we wanted to improver overall load time, the real win was to improve the perceived usable state, i.e. the time to having usable content presented on the page and bring that down as low as possible.

With the home page redesign, we had the following aims:

  • Reduce the number of page elements. In terms of designing for speed, less is more. So fewer images, fewer styles, less Javascript, etc. Every added element will have a performance impact.
  • Following on from this, reduce the number of HTTP requests. Every HTTP request involves the overhead of a round trip from client to server and back again. Fewer requests means a faster page.

Drupal allowed us to combine CSS files and Javascript files to minimize their numbers and squish them through minification and compression to reduce their file-size. We looked into using image sprites and better using image-styles in order to optimize both the actual size and file-size of images.

The single biggest change was in getting rid of the hero image slider on the home page. This single element involved multiple large images and simply by removing that, it allowed a single smaller masthead image and text to load quickly and efficiently at the top of the page, so that it is the very first thing a user sees. This gives the perception of a really lightning fast site.

So how did we do?

The combination of all these techniques, each one a small gain, led us to a very nice 15% increase in overall page speed and more importantly, a massive 50% improvement in time to perceived usable state.

In an organisation where performance directly relates to numbers of donations which in turn directly affects the lives of the poorest people in the world, every millisecond counts.

Jan 27 2016
Jan 27

Tom Friedhof

Senior Software Engineer

Tom has been designing and developing for the web since 2002 and got involved with Drupal in 2006. Previously he worked as a systems administrator for a large mortgage bank, managing servers and workstations, which is where he discovered his passion for automation and scripting. On his free time he enjoys camping with his wife and three kids.

Pages

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web