Upgrade Your Drupal Skills

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

See Advanced Courses NAH, I know Enough
Nov 08 2018
Nov 08

This is a public update on the work of the Governance Task Force.

Drupal is one of the most successful open source projects in the world. Governance is fundamental to the project's success.

The community and the code has been built from the ground up. And as the code has grown, so has the community.

When communities are first emerging it's easy to bring newcomers along, but over time the community begins to mature, change, and then needs to adapt. Challenges and opportunities emerge as evolution occurs, and our community needs to navigate them strategically.

A Governance Task Force has been meeting weekly since May to put together the strategic proposal we now share with you. We've synthesized ideas, discussions, and experiences from people we've interviewed, and we've revisited the themes that emerged from the community listening project run by Whitney Hess and by previous governance discussions.

This Drupal Governance Task Force 2018 Proposal serves two purposes.

Firstly, it's clear that for community evolution to occur there needs to be broad agreement and buy-in. People are comfortable jumping in and building a new module, but community change and action is hard. People talked to us openly about the unclear processes and barriers holding back community progress.

We heard strong perceptions that support from Dries or the Drupal Association is needed before initiatives could be created or scaled; real or otherwise, this is affecting community progress and action. Speaking to people from the Drupal Association, the Community Working Group and other initiative leaders, they also feel limitations. But to change their terms of reference and priorities they also need to have a community directive.

The community is stronger and more influential than we sometimes assume  --- when we are speaking together.

That's why at the heart of this proposal is a new community governance structure.

The second purpose of the proposal is to create a starting point --- a framework. We’ve been practical, highlighting a range of actions that form a backbone for community evolution. It’s not a defined roadmap, and it’s not a list of every idea we had or heard. We welcome the discussion, debate and idea generation that this document will spark. We want to hear your solutions on how to get change done, and what you would like to contribute.

We strived to make practical recommendations with the potential to make progress, lower barriers, and help our community to continue to evolve with time.

Throughout this process we have heard people say they believe change is necessary. Change is necessary for the longevity of Drupal the project and the code. Change is necessary to create a new generation of Drupallers — the people we want to help build ambitious things and to have the chance to build a career within our community.

It is hard to not feel the project is at a crossroads. We’ve climbed the mountain of Drupal 8, we sit at the peak and look to the valley below.

Where we go next, and who we take with us, is up to you.

We hope this proposal helps.

David, Ela, Stella, Lyndsey, Rachel, Hussain, and Adam

File attachments:  Drupal-Governance-Task-Force-Proposal-2018.pdf
Nov 06 2018
Nov 06
Jody's desk

Hardware

After a long run on MacBook Pros, I switched to an LG Gram laptop running Debian this year. It’s faster, lighter, and less expensive. 

If your development workflow now depends on Docker containers running Linux, the performance benefits you’ll get with a native Linux OS are huge. I wish I could go back in time and ditch Mac earlier.

Containers

For almost ten years I was doing local development in Linux virtual machines, but in the past year, I’ve moved to containers as these tools have matured. The change has also come with us doing less of our own hosting. My Zivtech engineering team has always held the philosophy that you need your local environment to match the production environment as closely as possible. 

But in order to work on many different projects and accomplish this in a virtual machine, we had to standardize our production environments by doing our own hosting. A project that ran on a different stack or just different versions could require us to run a separate virtual machine, slowing down our work. 

As the Drupal hosting ecosystem has matured (Pantheon, Platform.sh, Acquia, etc.), doing our own hosting began to make less sense. As we diversified our production environments more, container-based local development became more attractive, allowing us to have a more light-weight individualized stack for each project.

I’ve been happy using the Lando project, a Docker-based local web development system. It integrates well with Pantheon hosting, automatically making my local environment very close to the Pantheon environments and making it simple to refresh my local database from a Pantheon environment. 

Once I fully embraced containers and switched to a Linux host machine, I was in Docker paradise. Note: you do not need a new machine to free yourself from OSX. You can run Linux on your Mac hardware, and if you don’t want to cut the cord you could try a double boot.

Philadelphia City Hall outside Jody's office
A cool office view (like mine of Philly’s City Hall) is essential for development mojo

Editor

In terms of editors/IDEs I’m still using Sublime Text and vim, as I have for many years. I like Sublime for its performance, especially its ability to quickly search projects with 100,000 files. I search entire projects constantly. It’s an approach that has always served me well. 

I also recommend using a large font size. I’m at 14px. With a larger font size, I make fewer mistakes and read more easily. I’m not sure why most programmers use dark backgrounds and small fonts when it’s obvious that this decreases readability. I’m guessing it’s an ego thing.

Browser

In browser news, I’m back to Chrome after a time on Firefox, mainly because the LastPass plugin in Firefox didn’t let me copy passwords. But I have plenty of LastPass problems in any browser. When working on multiple projects with multiple people, a password manager is essential, but LastPass’s overall crappiness makes me miserable.

Wired: Linux, git, Docker, Lando
Tired: OSX, Virtual machines, small fonts
Undesired: LastPass, egos

Terminal

I typically only run the browser, the text editor, and the terminal, a few windows of each. In the terminal, I’m up to 16px font size. Recommend! A lot of the work I do in the terminal is running git commands. I also work in the MySQL CLI a good deal. I don’t run a lot of custom configuration in my shell – I like to keep it pretty vanilla so that when I work on various production servers I’m right at home.

Terminal screenshot

Git

I get a lot of value out of my git mastery. If you’re using git but don’t feel like a master, I recommend investing time into that. With basic git skills you can quickly uncover the history of code to better understand it, never lose any work in progress, and safely deploy exactly what you want to.

Once I mastered git I started finding all kinds of other uses for it. For example, I was recently working on a project in which I was scraping a thousand pages in order to migrate them to a new CMS. At the beginning of the project, I scraped the pages and stored them in JSON files, which I added to git.  At the end of the project, I re-scraped the pages and used git to tell me which pages had been updated and to show me which words had changed. 

On another project, I cut a daily import process from hours to seconds by using git to determine what had changed in a large inventory file. On a third, I used multiple remotes with Jenkins jobs to create a network of sites that run a shared codebase while allowing individual variations. Git is a good friend to have.

Hope you found something useful in my setup. Have any suggestions on taking it to the next level?
 

Nov 05 2018
Nov 05

By Milos Bovan on 05 November 2018

Last year, Drupal Association has decided to take a break to consolidate and not organize an official DrupalCon Europe in 2018. Twelve community members stepped in and developed a plan to organize a DrupalCon replacement − named Drupal Europe. The final result was outstanding.

More than 1000 Drupalers from all over the world gathered in Darmstadt, Germany from 10th to 14th September 2018 to attend the yearly biggest European Drupal event. The new event brought a new concept. It featured 10 amazing tracks that guaranteed high-quality content for all of the Drupal target groups − developers, marketers and agencies. Additionally, it created more room for contribution and collaboration outside session slots.

Official Group Photo Drupal Europe Darmstadt 2018

We supported the event by giving three talks in two different tracks.

Miro Dietiker - Connecting media solutions beyond Drupal

On Tuesday, Miro Dietiker, founder of MD Systems, gave an interesting talk about integrating Digital Asset Management systems with Drupal. He compared the benefits of existing solutions and provided a practical guide what kind of solutions you could use to fulfill your media management needs.

[embedded content]

On Wednesday, Miloš Bovan held a session about Paragraphs module and its best practices. The full room of people proves that people love and use Paragraphs a lot. The talk focused on answering frequently asked questions when it comes to working with Paragraphs. Those covered some of the new features that are not well known as well as ideas on how to improve the editorial experience easily.

Miloš Bovan - Enrich your Paragraphs workflow with features you didn’t know about John Gustavo Choque Condori - Drupal PKM: A personal knowledge management Drupal distro

[embedded content]

Social activities

Besides sessions, contributions and coding we enjoyed attending social events as well. On Tuesday, the organizers brought participants to the Bavarian Beer Garden to taste traditional German bratwurst and beers. The place was located in the park and there was no chance to miss the great event. 


On Wednesday, we joined our fellow Drupalers from the Swiss community for a dinner. It is always nice to catch up with local people in an international event such as Drupal Europe.

As usual, the event came to an end with traditional and yet always exciting Trivia Night.

What’s next?

During Drupal Europe, the project founder, Dries Buytaert has announced that Drupal Association signed an agreement with Kuoni, an international company specialized in events organizations. This results in bringing the official DrupalCon Europe event back in 2019 and it is going to happen in the capital of The Netherlands − Amsterdam.

We hope to see you all there!

Nov 02 2018
Nov 02

You Can’t Put a Price Tag on Visibility, Creditability, and Collegiality

Kaleem Clarkson“pink pig” by Fabian Blank on Unsplash

Organizing a DrupalCamp takes a lot of commitment from volunteers, so when someone gets motivated to help organize these events, the financial risks can be quite alarming and sometimes overwhelming. But forget all that mess, you are a Drupal enthusiast and have drummed up the courage to volunteer with the organization of your local DrupalCamp. During your first meeting, you find out that there are no free college or community spaces in the area and the estimated price tag is $25,000. Holy Batman that is a lot of money!

Naturally, you start thinking about how we are going to cover that price tag, so you immediately ask, “how many people usually attend?” Well unless you are one of the big 5, (BADCamp, NYCCamp, Drupal GovCon, MidCamp or FloridaCamp) we average between 100 and 200 people. Then you ask, “how much can we charge?” You are then told that we cannot charge more than $50 because camps are supposed to be affordable for the local community and that has been the culture of most DrupalCamps.

Are you interested in attending the first online DrupalCamp Organizers Meeting, on Friday, November 9th at 4:00pm (EST)? RSVP Here.

Why Don’t We Treat DrupalCamps Like It’s the Enterprise Solution?

Drupal is the Enterprise solution. Drupal has forgotten about the hobbyist and is only concerned about large-scale projects. Drupal developers and companies make more per hour than Wordpress developers. These are all things I have heard from people within the community. So if any of these statements are valid, why are all the camps priced like it is 2002 and we are all sitting around in a circle singing Kumbaya? In 2016 for DrupalCamp Atlanta, we couldn’t make the numbers work, so we decided to raise the price of the camp from $45 to $65 (early bird) and $85 (regular rate). This was a long drawn out and heated debate that took nearly all of our 2 hours allotted for our google hangout. At the end of the day, one of our board members who is also a Diamond sponsor said,

Courtesy of Amaziee.io Labs

If a camp roughly costs $25,000 and you can only charge 150 people $50, how in the world are DrupalCamps produced? The simple answer, sponsors, sponsors, and more sponsors. Most camps solely rely on the sponsors to cover the costs. One camp, in particular, BADCamp has roughly 2,000 attendees and the registration is FREE. That’s right, the camp is completely free and did I forget to mention that it’s in San Francisco? Based on the BADCamp model and due to the fact the diamond sponsorship for DrupalCon Nashville was $50,000, getting 10 companies to sponsor your camp at $2,500 will be no sweat. Oh and don’t forget Drupal is the enterprise solution, right?

With all of your newfound confidence in obtaining sponsorships, you start contacting some of the larger Drupal shops in your area and after a week nothing. You reach out again maybe by phone this time and actually speak to someone but they are not committing because they want some more information as to why they should sponsor the camp such as, what other perks can you throw in for the sponsorship, are we guaranteed presentation slots, and do you provide the participant list. Of course, the worst response is the dreaded no, we cannot sponsor your conference because we have already met our sponsorship budget for the year.

At this point, you feel defeated and confused as to why organizations are not chomping at the bit to fork over $2,500 to be the sponsor. Yep, that’s right, twenty-five hundred, not $25,000 to be the highest level, sponsor. Mind you many Drupal shops charge anywhere between $150 — $250 an hour. So that means donating 10–17 hours of your organizations time to support a Drupal event in your local community. Yes, you understand that there are a lot of DrupalCamps contacting the same companies for sponsorship so you ask yourself, what has changed from years past?

Are you interested in attending the first online DrupalCamp Organizers Meeting, on Friday, November 9th at 4:00 pm (EST)? RSVP Here.

What Do Companies Expect to Gain From DrupalCamp Sponsorships?

At DrupalCon Nashville, I got an awesome opportunity to participate in a session around organizing DrupalCamps. It was really interesting to hear about how other organizers produce their camp and what were some of the biggest pain points.

Group Photo — DrupalCon 2018 Nashville by Susanne Coates

During this session, we were talking about a centralized sponsorship program for all DrupalCamps (that I personally disagree with and will save that discussion for another blog post) and an individual asked the question,

Needless to say, they caught me completely off guard, so I paused then replied,

“DrupalCamp Atlanta has between 150–200 people, most of them from other Drupal shops, so what is it that you are expecting to get out of the sponsorship that would make it worth it to you? Why do you sponsor any DrupalCamps?”

Have Drupal Companies Outgrown the Need to Sponsor DrupalCamps?

On the plane ride back to the ATL it got me thinking, why does an organization sponsor DrupalCamps? What is the return on their investment? I started reminiscing of the very first DrupalCamp that I attended in 2008 and all the rage at that time (and still is), was inbound marketing and how using a content strategy and or conference presentations can establish your company as thought leaders in the field, therefore, clients will find your information useful and approach you when its time to hire for services. Maybe this is why so many camps received a ton of presentation submissions and why it was easy to find sponsors, but that was over 10 years ago now and some of those same companies have now been established as leaders in the field. Could it be, that established companies no longer need the visibility of DrupalCamps?

The Drupal community thrives when Drupal shops become bigger and take on those huge projects because it results in contributions back to the code, therefore, making our project more competitive. But an unintended consequence of these Drupal shops becoming larger is that there is a lot more pressure on them to raise funding thus they need to spend more resources on obtaining clients outside of the Drupal community. Acquia, the company built by the founder of Drupal, Dries Buytaert, have made it clear that they are pulling back on their local camp sponsorships and have even created their own conference called Acquia Engage that showcases their enterprise clients. Now from a business perspective, I totally understand why they would create this event as it provides a much higher return on their investment but it results in competing with other camps (ahem, this year’s DrupalCamp Atlanta), but more importantly the sponsorship dollars all of us depend on are now being redirected to other initiatives.

Are you interested in attending the first online DrupalCamp Organizers Meeting, on Friday, November 9th at 4:00 pm (EST)? RSVP Here.

Why Should Established Companies Sponsor a DrupalCamp?

The reality of the situation is that sponsoring these DrupalCamps are most likely not going to land your next big client that pays your company a $500,000 contract. So what are true reasons to sponsor a DrupalCamp:

  • Visibility
    When sponsoring these DrupalCamps most of us organizers do a pretty good job of tweeting thanks to the company and if the organization has presenters we usually promote the sessions as well. In addition, most camps print logos on the website, merchandise, and name after parties. Yes, its only a little bit but the internet is forever and the more you are mentioned the better off you are. But you are from a well established Drupal shop so you don’t need any more visibility.
  • Credibility
    Even the companies who are have been established need their staff to be credible. There will always be some amount of turnover and when that happens your clients still want to know if this person is talented. And if your company is new, being associated with Drupal in your local community does provide your company a sense of credibility.
  • Collegiality
    I saved the best for last. Collegiality is highly overlooked when looking at sponsoring camps. Most companies have a referral program for new hires and when the time comes for you to hire, people tend to refer their friends and their professional acquaintances. There is no better place to meet and interact with other Drupalist than a DrupalCamp. What about employee engagement? In a recent focus group I participated in with a Drupal shop, many of the staff wanted more opportunities for professional development. These local camps are affordable and can allow staff to attend multiple events in a year when you have small budgets.

I must end by saying, that there are so many great Drupal companies that I have had the pleasure to work with and if it were not for the Acquia’s of the world Drupal wouldn’t exist. I understand that CEO’s are responsible for their employees and their families so I don’t want to underestimate the pressures that come with making payroll and having a client pipeline. The purpose of this post was to explain how it feels as a volunteer who is doing something for the community and the frustrations that sometimes come with it.

Oct 29 2018
Oct 29

At this year's BADCamp, our Senior Web Architect Nick Lewis led a session on Gatsby and the JAMstack. The JAMStack is a web development architecture based on client-side JavaScript, reusable APIs, and prebuilt Markup. Gatsby is one of the leading JAMstack based static page generators, and this session primarily covers how to integrate it with Drupal. 

Our team has been developing a "Gatsby Drupal Kit" over the past few months to help jump start Gatsby-Drupal integrations. This kit is designed to work with a minimal Drupal install as a jumping off point, and give a structure that can be extended to much larger, more complicated sites.

This session will leave you with: 

1. A base Drupal 8 site that is connected with Gatsby.  

2. Best practices for making Gatsby work for real sites in production.

3. Sane patterns for translating Drupal's structure into Gatsby components, templates, and pages.

This is not an advanced session for those already familiar with React and Gatsby. Recommended prerequisites are a basic knowledge of npm package management, git, CSS, Drupal, web services, and Javascript. Watch the full session below. 

Oct 27 2018
Oct 27

If the community is a top priority then resources for organizing DrupalCamps must also be a top priority.

Kaleem Clarkson“Together We Create graffiti wall decor” by "My Life Through A Lens" on Unsplash

Community, community and more community. One of the common themes we hear when it comes to evaluating Drupal against other content management systems (CMS), is that the community is made up of over 100,000 highly skilled and passionate developers who contribute code. And in many of these application evaluations, it’s the community, not the software that leads to Drupal winning the bid. We have also heard Dries Buytaert speak about the importance of the community at various DrupalCons and he is quoted on Drupal.org’s getting involved page:

My First Encounter with the Drupal Community

With this emphasis on community, I tried to think back to how and when I first interacted with the community. Like so many others, my first introduction to Drupal was at a local Meetup. I remember going to this office building in Atlanta and the room was packed with people, plenty of pizza, soda and, of course, laptops. It was a nice relaxed atmosphere where we introduced ourselves and got a chance to know each other a little bit. Then the lights dimmed, the projector turned on and the presentations kicked off, highlighting some new content strategy or a new module that can help layout your content. After that first meetup, I felt energized because until that point, I had never spoken with someone in person about Drupal and it was the first time that I was introduced to Drupal professionals and companies.

Are you interested in attending the first online DrupalCamp Organizers Meeting, on Friday, November 9th at 4:00pm (EST)? RSVP Here.

DrupalCamps Play An Integral Role in Fostering Community

After attending a few meetups, I joined the email list and I received an email announcing DrupalCamp Atlanta was going to be held at Georgia Tech and the call for proposals was now open for session submissions.

2013 DrupalCamp Atlanta photo by Mediacurrent

I purchased a ticket for a mere $30 and added it to my Google calendar. On the day of the event, I remember walking in the front door and being blown away by the professionalism of the conference as there were sponsor booths, giveaways, and four concurrent sessions throughout the day. But it wasn’t until I was inside the auditorium during the opening session and saw the 200 or so people pile in that made me realize this Drupal community thing I heard about was for real. Over the next couple of years, I decided that I would attend other camps instead of DrupalCon because the camps were more affordable and less intimidating. My first camp outside of Atlanta was Design4Drupal in Boston, DrupalCamp Charlotte, DrupalCamp Florida and BADCamp were all camps I went to before attending a DrupalCon. All of these camps were top notch but what I really loved is that each camp had their own identity and culture. It’s exactly what I think a community should be and for the very first time, I felt that I was a part of the Drupal community.

Why Establish the DrupalCamp Organizers Council?

As provided in my previous examples, one of the advantages of Drupal comes from the great community and DrupalCamps are an important aspect in fostering this community. Running any event can be challenging, but to pull off a respectable DrupalCamp you have consider so many things such as the website, credit card processing, food, accepting and rejecting sessions, finding a keynote speaker, the afterparty, pre-conference trainings, oh and did I mention the website? You get my drift, it's a lot of work. Many of these tasks just roll off my tongue from past experience so ask yourself;

  • Where can I share my knowledge with other people who organize camps?
  • What if there was some way that all of us DrupalCamp organizers could come together and implement services that make organizing camps easier?
  • How could we provide camp organizers with resources to produce great camps?

During the #AskDries session at DrupalCon Nashville (listen for yourself), Midwest DrupalCamp Organizer Avi Schwab asked Dries the following question;

“... giving the limited funding the Drupal Association has, where should we go in trying to support our smaller local community events?” — Avi Schwab

Dries then responded with:

“That’s a great question. I actually think its a great idea what they (WordCamp) do. Because these camps are a lot of work. ...I think having some sort of central service or lack of a better term, that helps local camp organizers, I think is a fantastic idea, because we could do a lot of things, like have a camp website out of the box, ... we could have all sorts of best practices out of the box .” — Dries Buytaert

DrupalCamp Slack Community was the first time that I was provided a link to a spreadsheet that had the camp history dating back to 2006 and people were adding their target camp dates even if they were just in the planning stages. As a camp organizer I felt connected, I felt empowered to make better decisions and most of all I could just ask everyone, hey, how are you doing this?

Are you interested in attending the first online DrupalCamp Organizers meeting, on Friday, November 9th at 4:00pm (EST)? RSVP Here.

Earlier this year I volunteered for the Drupal Diversity and Inclusion Initiative (DDI) and was inspired when I heard Tara King on the DrupalEasy podcast, talk about how she just created the ddi-contrib channel on the Drupal slack and started hosting meetings. All jazzed up and motivated by that podcast, I reached out to over 20 different camp organizers from various countries and asked them if they would be interested in being on something like this? And if not, would they feel represented if this council existed?

Here are some quotes from Camp Organizers:

“I think a DrupalCamp Organizers Council is a great idea. I would be interested in being a part of such a working group. Just now I’m restraining myself from pouring ideas forth, so I definitely think I’m interested in being a part.”

“I am interested in seeing something that gathers resources from the vast experiences of current/past organizers and provides support to camps.”

“I definitely would appreciate having such a council and taking part. I’ve now helped organize DrupalCamp four times, and this was the first year we were looped into the slack channels for the organizers.”

“I really like the idea — what do we need to do to get this started?”

What are the Next Steps?

Based on the positive feedback and the spike in interest from other camp organizers I have decided to take the plunge and establish our first meeting of DrupalCamp Organizers on Friday, November 9th at 4:00pm (EST). This will be an online Zoom video call to encourage people to use their cameras so we can actually get to know one another.

The agenda is simple:

  • Introductions from all callers, and one thing they would like to see from the council.
  • Brainstorm the list of items the council should be advocating for.
  • Identify procedures for electing people to the Council: ways to nominate, eligibility criteria, Drupal event organizer experience required etc.
  • Outline of a quick strategic plan.
Oct 26 2018
Oct 26

After almost one year, and that $1.6M for a single item we had a couple more (big) sales that are worth talking about.

If you expect this to be a pat on the shoulder kind of post, where I’m talking about some hyped tech stack, sprinkled with the words “you can just”, and “simply” - while describing some incredible success, I can assure you it is not that.

It is, however, also not a “we have completely failed” self reflection.

Like every good story, it’s somewhere in the middle.

The Exciting World of Stamps

Many years ago, when Brice and me founded Gizra, we decided “No gambling, and no porn.” It’s our “Do no evil” equivalent. Along all the life of Gizra we always had at least one entrepreneurial project going on in different fields and areas. On all of them we just lost money. I’m not saying it necessarily as a bad thing - one needs to know how to lose money; but obviously it would be hard to tell it as a good thing.

Even in the beginning days, we knew something that we know also now - as a service provider there’s a very clear glass ceiling. Take the number of developers you have, multiple by your hourly rate, number of working hours, and that’s your your optimal revenue. Reduce at least 15% (and probably way more, unless you are very minded about efficiency) and now you have a realistic revenue. Building websites is a tough market, and it’s never getting easy - but it pays the salaries and all things considered, I think it’s worth it.

While we are blessed with some really fancy clients, and we are already established in the international Drupal & Elm market, we wanted to have a product. I tend to joke that I already know all the pain points of being a service provider, so it’s about time I know also the ones of having a product.

Five years ago Yoav entered our door with the idea of CircuitAuction - a system for auction houses (the “going once… going twice…” type). Yoav was born to a family of stamps collectors and was also a Drupaler. He knew building the system he dreamed of was above his pay grade, so he contacted us.

Boy, did we suck. Not just Gizra. Also Yoav. There was a really good division between us in terms of suckiness. If you think I’m harsh with myself, picture yourself five years ago, and tell yourself what you think of past you.

I won’t go much into the history. Suffice to say that my believe that only on the third rewrite of any application do you start getting it right, was finally put to the test (and proved itself right). Also, important to note that at some point we turned from service provides to partners and now CircuitAuction is owned by Brice, Yoav, and myself. This part will be important when we reach the “Choose your partners right” section.

So the first official sale along with the third version of CircuitAuction happened in Germany at March 2017. I’ve never had a more stressful time at work than the weeks before, and along the sale. I was completely exhausted. If you ever heard me preaching about work life balance, you would probably understand how it took me by surprise the fact that I’ve worked for 16 hours a day, weekdays and weekends, for six weeks straight.

I don’t regret doing so. Or more precisely, I would probably really regret it if we would have failed. But we were equipped with a lot of passion to nail it. But still, when I think of those pre-sale weeks I cringe.

Stamp Collections & Auction Houses 101

Some people, very few (and unfortunately for you the reader, you are probably not one of them) are very, very (very) rich. They are rich to the point that for them, buying a stamp in thousands or hundreds of euros is just not a big deal.

Some people, very few (and unfortunately for you the reader, you are probably not one of them), have stamp collections or just a couple of valuable stamps that they want to sell.

This is where the auction house comes in. They are not the ones that own the stamps. No, an auction house’s reputation is determined by the the two rolodexes they have: the one with the collectors, and the one with the sellers. Privacy and confidentiality, along with honesty, are obviously among the most important traits for the auction house.

So, you might think “They just need to sell a few stamps. How hard can that be?”

Well, there are probably harder things in life, but our path led us here, so this is what we’re dealing with. The thing is that along those five days of a “live sale” there are about 7,000 items (stamps, collections, postcards etc’) that beforehand need to be categorized, arranged, curated and pass an extensive and rigorous workflow (if you would buy these 4 stamp for 74,000 euro, you’d expect it to be carefully handled, right?).

Screenshot of the live auction webapp, built with Elm. A stamp is being sold in real time for lots of Euros!

Now mind you that handling stamps is quite different from coins and they are both completely different from paintings. For the unprofessional eye those are “just” auctions, but when dealing with such expensive items, and such specific niches, each one has different needs and jargon.

We Went Too Far. Maybe.

  • Big stamps sales are a few million euros; but those of coins are of hundreds.
  • The logic for stamp auctions is usually more complex than that of coins.
  • Heinrich Koehler, our current biggest client and one of the most prestige stamps auction houses in the world has an even crazier logic. Emphasis on the crazier. Being such a central auction house, every case that would normally be considered as edge case, manifests itself on every sale.

So, we went with a “poor” vertical (may we all be as poor as this vertical), and with a very complex system. There are a few reasons for that, although only time would tell if was a good bet:

Yoav, our partner, has a lot of personal connections in this market - he literally played as kid or had weekend barbecues with many of the existing players. The auction houses by nature are relying heavily on those relations, so having a foothold in this niche market is an incredible advantage

Grabbing the big player was really hard. Heinrich Koehler requires a lot of care, and enormous amount of development. But once we got there, we have one hell of a bragging right.

There’s also an obvious one that is often not mentioned - we didn’t know better. Only until very late in the process, we never asked those questions, as we were too distracted with chasing the opportunities that were popping.

But the above derails from probably the biggest mistake we did along the years: not building the right thing.

If you are in the tech industry, I would bet you have seen this in one form or another. The manifestation of it is the dreaded “In the end we don’t need it” sentence floating in the air, and a team of developers and project managers face-palming. Developers are cynical for a reason. They have seen this one too many times.

I think that developing something that is only 90% correct is much worse than not developing it at all. When you don’t have a car, you don’t go out of town for a trip. When you do, but it constantly breaks or doesn’t really get you to the point you wanted, you also don’t get to hike, only you are super frustrated at the expense of the misbehaving car, and at the fact that it’s, well, not working.

We were able to prevent that from happening to many of our clients, but fell to the same trap. We assumed some features were needed. We thought we should build it in a certain way. But we didn’t know. We didn’t always have a real use case, and we ended rewriting parts over and over again.

The biggest change, and what has put us on the right path, was when we stopped developing on assumptions, and moved one line of code at a time, only when it was backed up with real use cases. Sounds trivial? It is. Unfortunately, also doing the opposite - “develop by gut feeling” is trivial. I find that it requires more discipline staying on the right path.

Luckily, at some point we have found a superb liaison.

The Liaison, The Partners, and the Art of War

Tobias (Tobi) Huylmans, our liaison, is a person that really influenced for the better and helped shape the product to what it is.

He’s a key person in Heinrich Koehler dealing with just about any aspect of their business. From getting the stamps, describing them, expediting them (i.e. being the professional that gives the seal of approval that the item is genuine), teaching the team how to work with technology, getting every possible complaint from every person nearby, opening issues for us on GitHub, getting filled with pure rage when a feature is not working, getting excited when a feature is working, being the auctioneer at the sale, helping accounting with the bookkeeping, and last, but not least, being a husband and a father.

There are quite a few significant things I’ve learned working with him. The most important is - have someone close the team, that really knows what they are talking about, when it comes to the problem area. That is, I don’t think that his solutions are always the best ones, but he definitely understands the underlying problem.

It’s probably ridiculous how obvious this above resolution is, and yet I suspect we are not the only ones in the world who didn’t grasp it fully. If I’d have to make it an actionable item for any new entrepreneur I’d call it “Don’t start anything unless you have an expert in the field, that is in a daily contact with you.”

Every field has a certain amount of logic, that only when you immerse yourself in it do you really get it. For me personally it took almost four months of daily work to “get it”, when it came to how bids should be allowed to be placed. Your brain might tell you it’s a click of a button, but my code with 40+ different exceptions that can be thrown along a single request is saying differently.

We wouldn’t have gotten there without Tobi. It’s obvious that I have enormous respect for him, but at the same time he can drive me crazy.

I need a calm atmosphere in order to be productive. However, Tobi is all over the place. I can’t blame him - you’ve just read how many things he’s dealing with. But at times of pressure he’s sometimes expecting FOR THINGS TO BE FIXED IMMEDIATELY!!!
You probably get my point. I’m appreciative for all his input, but I need it to be filtered. Luckily me and my partners’ personalities are on slightly different spectrums that are (usually) complimenting each other:

I can code well in short sprints, where the scope is limited. I’m slightly obsessed with clean code and automatic testing, but I can’t hold it for super long periods.

Brice is hardly ever getting stressed and can manage huge scopes. He’s more of a “if it works don’t fix it”, while I have a tendency to want to polish existing (ugly) code when I come across it. His “Pragmatic” level is set all the way to maximum. So while I don’t always agree with his technical decisions, one way or another, the end result is a beast of a system that allows managing huge collections of items, with their history and along with their accounting, invoicing and much more. In short, he delivers.

Yoav’s knows the ins and outs of the auction field. On top of that his patience is only slightly higher than a Hindu cow. One can imagine the amount of pressure he has undergone in those first sales when things were not as smooth as they should have been. I surely would have cracked.

This mix of personalities isn’t something we’re hiding. In fact it’s what allows us to manage this battle field called auctions sales. Sometimes the client needs a good old tender loving care, with a “Yes, we will have it”; sometimes they need to hear a “No, we will not get to it on time” with a calm voice; and sometimes they need to see me about to start foaming from the mouth when I feel our efforts are not appreciated.

Our Stack

Our Elm & Drupal stack is probably quite unique. After almost 4 years with this stack I’m feeling very strongly about the following universal truth:

Elm is absolutely awesome. We would not have had such a stable product with JS in such a short time. I’m not saying others could not do it in JS. I’m saying we couldn’t, and I wouldn’t have wanted to bother to try it. In a way I feel that I have reached a point where I see people writing apps in JS, and can’t understand why they are interacting with that language directly. If there is one technical tip I’d give someone looking into front end and feeling burned by JS is “try Elm.”

Drupal is also really great. But it’s built on a language without a proper type system and a friendly compiler. On any other day I’d tell you how now days I think that’s a really a bad idea. However, I won’t do it today, because we have one big advantage by using Drupal - we master it. This cannot be underestimated: even though we have re-written CircuitAuction “just” three times, in fact we have built with Drupal many (many) other websites and web applications and learned almost everything that can be thought. I am personally very eager to getting Haskell officially into our stack, but business oriented me doesn’t allow it yet. I’m not saying Haskell isn’t right. I’m just saying that for us it’s still hard to justify it over Drupal. Mastery takes many years, and is worth a lot of hours and dollars. I still choose to believe that we’ll get there.

On Investments, Cash Flow, and Marketing

We have a lot of more work ahead of us. I’m not saying it in that extra cheerful and motivated tone one hears in cheesy movies about startups. No, I’m seeing it in the “Shit! We have a lot of more work ahead of us.” tone.
Ok, maybe a bit cheerful, and maybe a bit motivated - but I’m trying to make a point here.

For the first time in our Gizra life we have received a small investment ($0.5M). It’s worth noting that we sought a small investment. One of the advantages of building a product only after we’ve established a steady income is that we can invest some of our revenues in our entrepreneurial projects. But still, we are in our early days, and there is just about only a single way to measure if we’ll be successful or not: will we have many clients.

We now have some money to buy us a few months without worrying about cash flow, but we know the only way to keep telling the CircuitAuction story is by selling. Marketing was done before, but now we’re really stepping on it, in Germany, UK, US and Israel. I’m personally quite optimistic, and I’m really looking forward to the upcoming months, to see for real if our team is as good as I think and hope, and be able to simply say “We deliver.”

Oct 24 2018
Oct 24

Today I found another instance where I decided to use Illuminate Collections within my Drupal 8 code; whilst I was debugging an issue where a Drupal Commerce promotion was incorrectly being applied to an order.

No adjustments were showing in the Drupal UI for that order, so after some initial investigation and finding that $order->getAdjustments() was empty, I determined that I would need to get the adjustments from each order item within the order.

If the order were an array, this is how it would be structured in this situation:

$order = [
  'id' => 1,
  'items' => [
    [
      'id' => 1,
      'adjustments' => [
        ['name' => 'Adjustment 1'],
        ['name' => 'Adjustment 2'],
        ['name' => 'Adjustment 3'],
      ]
    ],
    [
      'id' => 2,
      'adjustments' => [
        ['name' => 'Adjustment 4'],
      ]
    ],
    [
      'id' => 3,
      'adjustments' => [
        ['name' => 'Adjustment 5'],
        ['name' => 'Adjustment 6'],
      ]
    ],
  ],
];

Getting the order items

I started by using $order->getItems() to load the order’s items, converted them into a Collection, and used the Collection’s pipe() method and the dump() function provided by the Devel module to output the order items.

collect($order->getItems())
  ->pipe(function (Collection $collection) {
    dump($collection);
  });

Get the order item adjustments

Now we have a Collection of order items, for each item we need to get it’s adjustments. We can do this with map(), then call getAdjustments() on the order item.

This would return a Collection of arrays, with each array containing it’s own adjustments, so we can use flatten() to collapse all the adjustments into one single-dimensional array.

collect($order->getItems())
  ->map(function (OrderItem $order_item) {
    return $order_item->getAdjustments();
  })
  ->flatten(1);

There are a couple of refactors that we can do here though:

  • Use flatMap() to combine the flatten() and map() methods.
  • Use higher order messages to delegate straight to the getAdjustments() method on the order, rather than having to create a closure and call the method within it.
collect($order->getItems())
  ->flatMap->getAdjustments();

Filtering

In this scenario, each order item had three adjustments - the correct promotion, the incorrect one and the standard VAT addition. I wasn’t concerned about the VAT adjustment for debugging, so I used filter() to remove it based on the result of the adjustment’s getSourceId() method.

collect($order->getItems())
  ->flatMap->getAdjustments()
  ->filter(function (Adjustment $adjustment) {
    return $adjustment->getSourceId() != 'vat';
  });

Conclusion

Now I have just the relevant adjustments, I want to be able to load each one to load it and check it’s conditions. To do this, I need just the source IDs.

Again, I can use a higher order message to directly call getSourceId() on the adjustment and return it’s value to map().

collect($order->getItems())
  ->flatMap->getAdjustments()
  ->filter(function (Adjustment $adjustment) {
    return $adjustment->getSourceId() != 'vat';
  })
  ->map->getSourceId();

This returns a Collection containing just the relevant promotion IDs being applied to the order that I can use for debugging.

Now just to find out why the incorrect promotion was applying!

Oct 21 2018
Oct 21

Wouldn’t it be nice if you could add any block you want to your paragraphs?

Kaleem Clarkson

In years past, layout for Drupal has been in the hands of front-end developers, but over time various modules were developed that provided site-builders the ability to adjust the layout. An improvement yes, but there still wasn’t a clear cut option that empowered content editors to alter the layout during the editorial process.

Look out! Here comes the Paragraphs Module. This module has been taking the Drupal community over by storm because it allows content editors to add pre-designed components which gives each page the option to have different layouts. One of the limitations of the Paragraphs module, is that each paragraph can only be used once, and only for the current node you are editing. This means that you can’t re-use a common paragraph such as a call to action block, email signup or contact us form, so you end up finding yourself duplicating a lot of work if you want the same block on numerous pages. While the Drupal community has been working to help solve this problem by allowing the re-use of paragraphs, there are still going to be plenty of situations where you want to insert custom blocks, views, or system blocks such as the site logo or login block.

How do you allow your site editors to add re-used blocks into their content during the editorial process?

There are plenty of awesome articles out there that explains how to use paragraphs so I won’t get into that. To follow along with my steps be sure to have downloaded and enabled both the Paragraphs and the Block Field modules.

Steps to Add Blocks to Paragraphs

  1. Download and Enable the Paragraphs and Block Field modules.
  2. Create a paragraph type called Block Reference (or whatever name you want)
  3. Add a new field, by selecting the Block (plugin) field type from the dropdown and save it.
  4. Go to manage display and make the label hidden.
    I always forget this step and then I scratch my head when I see the Block Ref field label above my views title.
  5. Now go to back to your content type that has the paragraph reference field and ensure the Block Reference paragraph type is correctly enabled.
    The content type with the paragraph reference field was not covered in this tutorial.
  6. When adding or editing your content with a paragraph reference field. Add the Block Reference paragraph type. Select the name of the block that you would like to reference from the dropdown hit save on the content and watch the magic happen.

In conclusion, it does feel a little scary giving content editors this much freedom so it will be imperative that all views and custom blocks have descriptive names so that editors can clearly identify what blocks to reference. Overall I feel like this is a good solution for referencing existing blocks that can save a lot of time and really unleashes the power of the paragraphs module. The Drupal community continues to amaze me!

Oct 19 2018
Oct 19
Deirdre Habershaw

Today, more than 80% of people’s interactions with government take place online. Whether it’s starting a business or filing for unemployment, too many of these experiences are slow, confusing, or frustrating. That’s why, one year ago, the Commonwealth of Massachusetts created Digital Services in the Executive Office of Technology and Security Services. Digital Services is at the forefront of the state’s digital transformation. Its mission is to leverage the best technology and information available to make people’s interactions with state government fast, easy, and wicked awesome. There’s a lot of work to do, but we’re making quick progress.

In 2017, Digital Services launched the new Mass.gov. In 2018, the team rolled out the first-ever statewide web analytics platform to use data and verbatim user feedback to guide ongoing product development. Now our researchers and designers are hard at work creating a modern design system that can be reused across the state’s websites and conducting the end-to-end research projects to create user journey maps to improve service design.

If you want to work in a fast-paced agile environment, with a good work life balance, solving hard problems, working with cutting-edge technology, and making a difference in people’s lives, you should join Massachusetts Digital Services.

We are currently recruiting for a Technical Architect if you are interested submit your resume here

Check out more about hiring at the Executive Office of Technology and Security Services and submit your resume in order to be informed on roles as they become available.

Oct 19 2018
Oct 19

I have a card component with a title, image, text, and link. How come all my card variants are inheriting all the values from the default one? Short answer, you don't. It's a feature, not a bug.

Where this really becomes frustrating is when you have a pattern that lists a number of items in an array. In that case, all variants will have (at least) that many items, even though you may want fewer.

For illustration:

list.twig has something like this:

 {% for list_item in list_items %} 
  {{ list_item }} 
{% endfor %} 

Then list.yml has something like this:

 list_items: 
  - join(): 
    - include(): pattern: content-teaser 
  - join(): 
    - include(): pattern: content-teaser 
  - join(): 
    - include(): pattern: content-teaser
  - join(): 
    - include(): pattern: content-teaser 
  - join(): 
    - include(): pattern: content-teaser 
  - join(): 
-- loads of more teasers for the main listing page 

Now you want to create a variant of list such as list~related-articles, but with only 2 items. You'd expect this would work

 list_items: 
  - join(): 
    - include(): pattern: content-teaser 
  - join(): 
    - include(): pattern: content-teaser 

But, no. This will still render as many items as were in the parent component. That's the beauty (a feature, not a bug) of PatternLab's inheritance system. To stop it you need to do something like this:

 list_items: 
  - join(): 
    - include(): pattern: content-teaser 
  - join(): 
    - include(): pattern: content-teaser 
    - - - and so on, so each extra one is basically set to 'false' 

When we do this with a component such as a card, we might also want to have variants such as card~no-image, card~no-text, etc. In this case, we'd have a card.yml like so:

​card_title: 'My Card Title' 
card_image: '' 
card_text: 'The text of the card will go here'
​

However, if we create variants, each of the items in card will be inherited to the variant. You'll notice this if you try to create one super mega component for all variants of a hero component for example (hero title, pre-title, sub-title, image, alignment, cta buttons, etc).

In this case, what I do is create a default component card.yml or hero.yml and give it only values for items that will more than likely be in all variants (basically whatever you are going to mark as a required field in Drupal (or whatever CMS you are using)), then set all others to 'false' in the component. Now when I create variants I only need to override the specifics for that variant, since everything else that is being inherited is already set to false. I also create a 'Kitchen Sink' version of the component which shows every item in action but DO NOT create this as the default/reference component.

My default card.yml might look like this:

card_title: 'My Card Title' 
card_image: false 
card_text: false 

Now my variants can look as simple as:

card~with-image.yml

​card_image: ''

And card~long-title will be just one line:

 card_title: 'This is a long title on a card just to illustrate what happens when it wraps to more than one line' 

And that is why this is a feature, not a bug - it allows us to write variants very simply and quickly. Is there a better way of doing this? I'm not aware of one. If you are, drop it in the comments. Thanks.

Oct 15 2018
Oct 15

There are a couple of modules out there to ajaxify the add to cart form of Drupal Commerce, but I'll introduce to one that on top offers a real decoupled solution. Additionally I'll show how you can achieve a simpler version of that with only a few lines of custom JS code.

Commerce Cart Flyout

The Commerce Cart Flyout module is my clear recommendation when it comes to what of the existing Drupal contrib modules to use, when you want to have an ajaxified add to cart form. It provides a sidebar which will "flyout", when either the cart block is clicked or the add to cart button was pressed. The flyout allows to view the cart's contents, change quantities or remove order items entirely. The block includes an item counter as well.

The module is based on Commerce Cart API and provides a progressively decoupled cart experience. Both modules are maintained by Commerce co-maintainer Matt Glaman, who works for Commerce Guys - the company responsible for developing and maintaining Drupal Commerce, which is another pro argument in choosing this approach. Another plus is that Cart Flyout is highly customizable, offering nine different Twig templatetes for the output of different parts of the module. The JS logic is cleanly structured and built on top of Backbone to offer a wonderful separation between models and views. Thanks to Drupal's library management, you could even override single JS parts of this module with ease.

Custom coding

I really like Cart Flyout very much and also use it in certain projects. But these days I've decided to rather use custom coding to achieve an ajaxified add to cart experience. And here I'm showing you how and why.

Why I haven't used Cart Flyout in that project

Well, in the end, it was the sum of a couple of small factors that led me into this direction. First one was that the project was built entirely without an ajaxified add to cart logic. The site wasn't live at that moment, but development work was already finished by about 99.9%. So we already had a custom block showing cart and wishlist icons with item counters inside. Cart Flyout needs its own cart block, so I'd have to entirely swap my block, fully customize the Twig template of the new block and find a way to include the wishlist icon as well. I'd have to swap out one JS model of the Flyout module as well, including a quite small change. And the main driver behind the decision was that we are using Commerce Wishlist as well. As the flyout module entirely swaps the add to cart form, we would have had to hook into that one and re-add the wishlist functionality. Not a huge impact, but also requiring some changes, was the fact that we are currently using Commerce Add To Cart Link module for all product teaser views (overview pages, search results, etc)

So the sum of all these factors and the curiosity about playing around by myself with the Cart API were the reason, why I've decided in favour of a custom solution in that project. And that shouldn't be a very tough task to achieve. Given the fact, that this is developed for a specific project, where you know your existing markup, CSS selectors and the new markup you want to create, you can leave the overhead of making everything customizable via config and templates behind you and ease the path.

I also didn't need the flyout sidebar containing the full cart info. Instead I just wanted a simple modal overlay showing the information that product XY was added to cart. As we are using Zurb Foundation as our frontend framework, we wanted to use Foundation's Reveal component for showing this modal.

Of course I was also building up on the great Commerce Cart API module, as well as I've created the wishlist counterpart Commerce Wishlist API to achieve all my goals.

First, I've defined the MYTHEME/ajax_add_to_cart JS library in my theme, which looks like this (and despite the name handles both the add links and forms for both cart and wishlist). Please note that this code also involves updating of a item counter that is part of the navigation bar (updateCart and updateWishlist) - this could also be skipped for even more minimal solutions.


(function ($, Drupal, drupalSettings) {
  Drupal.behaviors.ajaxAddToCart = {
    getCsrfToken: function(callback) {
      $.get(Drupal.url('rest/session/token'))
          .done(function (data) {
            callback(data);
          });
    },

    addToCart: function (csrfToken, purchasedEntityType, purchasedEntityId, qty) {
      $('body').append('');
      $.ajax({
        url: Drupal.url('cart/add?_format=json'),
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-CSRF-Token': csrfToken
        },
        data: JSON.stringify([{
          purchased_entity_type: purchasedEntityType,
          purchased_entity_id: purchasedEntityId,
          quantity: qty
        }]),
        success: function(data) {
          var orderItem = data[0];
          var $overlay = $('#add-to-cart-overlay');
          $overlay.find('.purchased-entity').text(orderItem.title);
          $overlay.foundation('open');
          Drupal.behaviors.ajaxAddToCart.updateCart();
          $('.add-to-cart-ajax-throbber').remove();
        }
      });
    },

    updateCart: function() {
      var $cartCount = $('.store-action--cart .store-action__link__count');
      if ($cartCount.length) {
        $.ajax({
          url: Drupal.url('cart?_format=json'),
          method: 'GET',
          headers: {
            'Content-Type': 'application/json',
          },
          success: function(data) {
            var cart = data[0];
            var count = cart.order_items.reduce(function (previousValue, currentValue) {
              return previousValue + parseInt(currentValue.quantity);
            }, 0);
            $cartCount.text(count);
          }
        });
      }
    },

    addToWishlist: function (csrfToken, purchasableEntityType, purchasableEntityId, qty) {
      $('body').append('');
      $.ajax({
        url: Drupal.url('wishlist/add?_format=json'),
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-CSRF-Token': csrfToken
        },
        data: JSON.stringify([{
          purchasable_entity_type: purchasableEntityType,
          purchasable_entity_id: purchasableEntityId,
          quantity: qty
        }]),
        success: function(data) {
          var wishlistItem = data[0];
          var $overlay = $('#add-to-wishlist-overlay');
          $overlay.find('.purchasable-entity').text(wishlistItem.title);
          $overlay.foundation('open');
          Drupal.behaviors.ajaxAddToCart.updateWishlist();
          $('.add-to-wishlist-ajax-throbber').remove();
        }
      });
    },

    updateWishlist: function() {
      var $wishlistCount = $('.store-action--wishlist .store-action__link__count');
      if ($wishlistCount.length) {
        $.ajax({
          url: Drupal.url('wishlist?_format=json'),
          method: 'GET',
          headers: {
            'Content-Type': 'application/json',
          },
          success: function(data) {
            var wishlist = data[0];
            var count = wishlist.wishlist_items.reduce(function (previousValue, currentValue) {
              return previousValue + parseInt(currentValue.quantity);
            }, 0);
            $wishlistCount.text(count);
          }
        });
      }
    },

    attach: function attach(context) {
      $(context).find('.add-to-cart-link').once('add-to-cart-link-init').each(function () {
        $(this).on('click', function (e) {
          e.preventDefault();
          var variationId = $(this).data('variation');
          Drupal.behaviors.ajaxAddToCart.getCsrfToken(function (csrfToken) {
            Drupal.behaviors.ajaxAddToCart.addToCart(csrfToken, 'commerce_product_variation', variationId, 1);
          });
        });
      });

      $(context).find('form.add-to-cart-form').once('add-to-cart-form-init').each(function () {
        $(this).on('click', '.form-submit', function(e) {
          var isWishlistButton = $(this).hasClass('add-to-wishlist-button');
          $(this).parents('form').data('button-clicked', isWishlistButton ? 'wishlist' : 'cart');
        });
        $(this).on('submit', function (e) {
          e.preventDefault();
          var buttonClicked = $(this).data('button-clicked');
          var purchasedEntityType = $(this).data('purchased-entity-type');
          var purchasedEntityId = $(this).data('purchased-entity-id');
          var qty = $(this).find('input[name="quantity[0][value]"]').val();
          Drupal.behaviors.ajaxAddToCart.getCsrfToken(function (csrfToken) {
            if (buttonClicked === 'wishlist') {
              Drupal.behaviors.ajaxAddToCart.addToWishlist(csrfToken, purchasedEntityType, purchasedEntityId, qty);
            }
            else {
              Drupal.behaviors.ajaxAddToCart.addToCart(csrfToken, purchasedEntityType, purchasedEntityId, qty);
            }
          });
        });
      });

      $(context).find('.add-to-wishlist-link').once('add-to-wishlist-link-init').each(function () {
        $(this).on('click', function (e) {
          e.preventDefault();
          var variationId = $(this).data('variation');
          Drupal.behaviors.ajaxAddToCart.getCsrfToken(function (csrfToken) {
            Drupal.behaviors.ajaxAddToCart.addToWishlist(csrfToken, 'commerce_product_variation', variationId, 1);
          });
        });
      });
    }
  };

})(jQuery, Drupal, drupalSettings);

Then I've added this library to the add to cart form, as well as to the add to cart links. Also I've added a class to the

tag for easier targeting and set the data-purchased-entity-type and data-purchased-entity-id attributes.
/**
 * Implements hook_form_BASE_FORM_ID_alter() for commerce_order_item_add_to_cart_form.
 */
function MYTHEME_form_commerce_order_item_add_to_cart_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  $form['#attached']['library'][] = 'MYTHEME/ajax_add_to_cart';
  // Add custom class to form.
  $form['#attributes']['class'][] = 'add-to-cart-form';
  /** @var \Drupal\commerce_cart\Form\AddToCartForm $form_object */
  $form_object = $form_state->getFormObject();
  /** @var \Drupal\commerce_order\Entity\OrderItemInterface $order_item */
  $order_item = $form_object->getEntity();
  $purchased_entity = $order_item->getPurchasedEntity();
  $form['#attributes']['data-purchased-entity-type'] = $purchased_entity->getEntityTypeId();
  // The order item's purchased entity reference won't be up to date, when a
  // different product variation was selected and updated via Ajax. The form
  // state stores it to 'selected_variation' then. Our product set entity on the
  // other side, does not store such a value, but is the purchasable entity on
  // its own. So we can safely take the one from the order item.
  $purchased_entity_id = $purchased_entity instanceof ProductVariationInterface ? $form_state->get('selected_variation') : $purchased_entity->id();
  $form['#attributes']['data-purchased-entity-id'] = $purchased_entity_id;
}

/**
 * Implements template_preprocess_commerce_add_to_cart_link().
 */
function MYTHEME_preprocess_commerce_add_to_cart_link(array &$variables) {
  $variables['#attached']['library'][] = 'MYTHEME/ajax_add_to_cart';
}

And finally, I've simply added the add to cart and add to wishlist to the page template, which will be hidden, until it get's the command to show up via JS. So I've added these lines to the very end of the page.html.twig (of course I've could also have chosen the html.html.twig file) (sorry that this code example is looking bad in the blog post - it got distracted somehow by CKEditor - you can also view it on a Gist, I'll mentioning in the end of the post):



  

wurde Ihrem Warenkorb hinzugefügt.

           

wurde Ihrer Merkliste hinzugefügt.

         

That's it :) I've summarized the above code in a Github Gist snippet for easier viewing.

Oct 10 2018
Oct 10

Authors are eager to learn, and a content-focused community is forming. But there’s still work to do.

Julia GutierrezVideo showing highlights of speakers, presenters, and attendees interacting at ConCon 2018.

When you spend most of your time focused on how to serve constituents on digital channels, it can be good to simply get some face time with peers. It’s an interesting paradox of the work we do alongside our partners at organizations across the state. Getting in a room and discussing content strategy is always productive.

That was one of the main reasons behind organizing the first ever Massachusetts Content Conference (ConCon). More than 100 attendees from 35 organizations came together for a day of learning and networking at District Hall in Boston. There were 15 sessions on everything from how to use Mayflower — the Commonwealth’s design system — to what it takes to create an awesome service.

Graphic showing more than 100 attendees from 50 organizations attended 15 sessions from 14 presenters at ConCon 2018.

ConCon is and will always be about our authors, and we’re encouraged by the feedback we’ve received from them so far. Of the attendees who responded to a survey, 93% said they learned about new tools or techniques to help them create better content. More so, 96% said they would return to the next ConCon. The average grade attendees gave to the first ever ConCon on a scale of 1 to 10 — with 1 being the worst and 10 the best — was 8.3.

Our authors were engaged and ready to share their experiences, which made for an educational environment, for their peers as well as our own team at Digital Services. In fact, it was an eye opening experience, and we took a lot away from the event. Here are some of our team’s reflections on what they learned about our authors and our content needs moving forward.

“The way we show feedback and scores per page is great but it doesn’t help authors prioritize their efforts to get the biggest gain for their constituents. We’re working hard to increase visibility of this data in Drupal.”

— Joe Galluccio

Katie Rahhal, Content Strategist
“I learned we’re moving in the right direction with our analysis and Mass.gov feedback tools. In the breakout sessions, I heard over and over that our content authors really like the ones we have and they want more. More ways to review their feedback, more tools to improve their content quality, and they’re open to learning new ways to improve their content.”

Christine Bath, Designer
“It was so interesting and helpful to see how our authors use and respond to user feedback on Mass.gov. It gives us a lot of ideas for how we can make it easier to get user feedback to our authors in more actionable ways. We want to make it easy to share constituent feedback within agencies to power changes on Mass.gov.”

Embedded tweet from @MassGovDigital highlighting a lesson on good design practices from ConCon 2018.

Joe Galluccio, Product Manager
“I learned how important it is for our authors to get performance data integrated into the Drupal authoring experience. The way we show feedback and scores per page is great but it doesn’t help authors prioritize their efforts to get the biggest gain for their constituents. We’re working hard to increase visibility of this data in Drupal.”

Bryan Hirsch, Deputy Chief Digital Officer
“Having Dana Chisnell, co-founder of the Center for Civic Design, present her work on mapping and improving the journey of American voters was the perfect lesson at the perfect time. The page-level analytics dashboards are a good foundation we want to build on. In the next year, we’re going to research, test, and build Mass.gov journey analytics dashboards. We’re also spending this year working with partner organizations on mapping end-to-end user journeys for different services. Dana’s experience on how to map a journey, identify challenges, and then improve the process was relevant to everyone in the room. It was eye-opening, enlightening, and exciting. There are a lot of opportunities to improve the lives of our constituents.”

Want to know how we created our page-level data dashboards? Read Custom dashboards: Surfacing data where Mass.gov authors need it

Embedded tweet from @epubpupil highlighting her positive thoughts on Dana Chisnell’s keynote presentation on mapping and improving the journey of American voters.

“It’s great to see there’s a Mayflower community forming among stakeholders in different roles across state government. ”

— Minghua Sun

Sienna Svob, Developer and Data Analyst
“We need to work harder to build a Mayflower community that will support the diversity of print, web, and applications across the Commonwealth. Agencies are willing and excited to use Mayflower and we need to harness this and involve them more to make it a better product.”

Minghua Sun, Mayflower Product Owner
“I’m super excited to see that so many of the content authors came to the Mayflower breakout session. They were not only interested in using the Mayflower Design System to create a single face of government but also raised constructive questions and were willing to collaborate on making it better! After the conference we followed up with more information and invited them to the Mayflower public Slack channel. It’s great to see there’s a Mayflower community forming among stakeholders in different roles across state government. ”

Sam Mathius, Digital Communications Strategist
“It was great to see how many of our authors rely on digital newsletters to connect with constituents, which came up during a breakout session on the topic. Most of them feel like they need some help integrating them into their overall content strategy, and they were particularly excited about using tools and software to help them collect better data. In fact, attendees from some organizations mentioned how they’ve used newsletter data to uncover seasonal trends that help them inform the rest of their content strategy. I think that use case got the analytics gears turning for a lot of folks, which is exciting.”

“I’d like to see us create more opportunities for authors to get together in informal sessions. They’re such a diverse group, but they share a desire to get it right.”

— Fiona Molloy

Shannon Desmond, Content Strategist
“I learned that the Mass.gov authors are energetic about the new content types that have been implemented over the past 8 months and are even more eager to learn about the new enhancements to the content management system (CMS) that continue to roll out. Furthermore, as a lifelong Massachusetts resident and a dedicated member of the Mass.gov team, it was enlightening to see how passionate the authors are about translating government language and regulations for constituents in a way that can be easily and quickly understood by the constituents of the State.”

Fiona Molloy, Content Strategist
“Talking to people who came to ConCon and sitting in on various sessions, it really struck me how eager our content authors are to learn — whether from us here at Digital Services or from each other. I’d like to see us create more opportunities for authors to get together in informal sessions. They’re such a diverse group, but they share a desire to get it right and that’s really encouraging as we work together to build a better Mass.gov.”

Embedded tweet from @MassGovDigital highlighting a session from ConCon 2018 in which content authors offered tips for using authoring tools on Mass.gov.

Adam Cogbill, Content Strategist
“I was reminded that one of the biggest challenges that government content authors face is communicating lots of complex information. We need to make sure we understand our audience’s relationships to our content, both through data about their online behavior and through user testing.”

Greg Derosiers, Content Strategist
“I learned we need to do a better job of offering help and support. There were a number of authors in attendance that didn’t know about readily-available resources that we had assumed people just weren’t interested in. We need to re-evaluate how we’re marketing these services and make sure everyone knows what’s available.”

Embedded tweet from @MassGovDigital highlighting the start of ConCon 2018.

Thinking about hosting your own content conference? Reach out to us! We’d love to share lessons and collaborate with others in the civic tech community.

Oct 09 2018
Oct 09

It was recently announced that 2020 will be the year Drupal 9 is officially released into the wild. The exact date hasn’t been set, but we can now look forward to the 9.0 release that year. The announcement also gave us an official End of Life date of November 2021 for Drupal 7 AND Drupal 8. So, what does this mean if you’re currently running or developing a site on one of those versions? In this post, I’ll explain.

What this means for Drupal 8?

Drupal 8 is built around a concept of continuous innovation. What this means is that new features and backwards-compatible changes are continuously added. When an old system or code is depreciated, instead of removing it, it stays in the codebase. This ensures that custom code and contributed modules will continue to work and have time to update. Eventually, there will be an excess amount of depreciated code and dependencies and there will be a need to remove it. That is one of the reasons for the release of Drupal 9. All that old stuff gets removed and we start fresh with the latest and greatest technology.

The great thing about Drupal 8 is that by the time Drupal 9 is released all of the modules and custom code in your site should be up-to-date. Therefor, updating from 8 to 9 is no different than from 8.5 to 8.6. Clean and painless!

And that’s the point. This method of building and releasing versions will continue for the foreseeable future which is why we like to say that a migration to the latest Drupal will be the last migration you ever need.

What this means for Drupal 7?

Unfortunately, Drupal 7 is a different story. When Drupal 7 reaches end of life in November of 2021, it will no longer be supported by the community at large. There are plans to release a Drupal 7 version that uses the latest version of PHP. There is also a paid support program planned (similar to Drupal 6 LTS) that will allow people and organizations unable or unwilling to migrate to continue to keep their sites secure. But really, your best course of action is to plan for a migration to Drupal 8 by 2020. This keeps your site current and guarantees it’s security moving forward.

The codebase between 7 and 8 is entirely different so a migration to Drupal 8 is a pretty big undertaking. You could call it replatforming. Drupal 8 does however include a built in data migration tool that will make the move easier. You might still need some help though depending on your site requirements and edge cases. Plus, data is one thing, but you would also need to move your theme, too. The silver lining is that migrating presents an opportunity to freshen up the look of your site and increase site speed with the latest software. For more information on what is involved in a migration, check out this post.

Like I mentioned earlier in this post, a migration to Drupal 8 may likely be the last migration you ever need since subsequent major version updates (i.e. from 8 to 9) should be very quick and easy. Once you’ve made that initial investment migrating to Drupal 8, you can rest assured that you won't have to go through that process again, possibly forever.

Migration experts

Acro Media is a Drupal agency specialized in eCommerce. We help build and maintain successful eCommerce websites as well as the underlying Drupal Commerce platform. We are also heavily involved in the development of Drupal’s migration tools. If you want to discuss what a migration might look like for your business, talk to us! We’re happy to help.

Contact us to discuss your migration!

Oct 04 2018
Oct 04

Variety of content and the need for empathy drive our effort to simplify language across Mass.gov

EOTSS Digital Services

Nearly 7 million people live in Massachusetts, and millions more visit the state each year. These people come from different backgrounds and interact with the Commonwealth for various reasons.

Graphic showing more than 3 million visitors go to Mass.gov each month.

We need to write for everyone while empathizing with each individual. That’s why we write at a 6th grade reading level. Let’s dig into the reasons why.

The Commonwealth has a high literacy rate and a world-renowned education network. From elementary school to college and beyond, you can get a great education here.

We’re proud of our education environment, but it doesn’t affect our readability standards. Navigating the Women, Infants, and Children (WIC) Nutrition Program might be challenging for everyone.

People searching for nutrition services are doing so out of necessity. They’re probably frustrated, worried, and scared. That affects how people read and retain information.

Learn about our content strategy. Read the 2017 content team review.

This is the case for many other scenarios. Government services can be complicated to navigate. Our job is to simplify language. We get rid of the white noise and focus on essential details.

You don’t browse Mass.gov in your free time. It’s a resource you use when you have to. Think of it as a speedboat, not a cruise ship. They’ll both get you across the water, just at different speeds.

Graphic showing desktop visitors to Mass.gov look at more pages and have longer sessions than mobile and tablet visitors.

Mass.gov visitors on mobile devices spend less time on the site and read fewer pages. The 44% share of mobile and tablet traffic will only increase over time. These visitors need information boiled down to essential details. Simplifying language is key here.

A 6th-grade reading level doesn’t work all the time. We noticed this when we conducted power-user testing. Lawyers, accountants, and other groups who frequently use Mass.gov were involved in the tests.

These groups want jargon and industry language. It taught us that readability is relative.

We use the Flesch-Kincaid model to determine reading level in our dashboards. It accounts for factors like sentence length and the number of syllables in words.

This is a good foundation to ensure we consistently hit the mark. However, time is the most important tool we have. The more content we write, the better we’ll get.

Writing is a skill refined over time, and adjusting writing styles isn’t simple. Even so, we’re making progress. In fact, this post is written at a 6th grade reading level.

Oct 02 2018
Oct 02

The Media module made its way into Drupal core for the Drupal 8.4 release a while back. It gives Drupal users a standardized way for managing local media resources, including image, audio, video, and document files. We wanted to add using this module into our Drupal Commerce demo site to give an example of how this module could potentially be used in a Commerce setting.

In this Tech Talk video, I’ll quickly show you how we updated our digital download Commerce product example to use the Media module, giving us the flexibility to add audio samples to the product page and access to the full download after purchase.

[embedded content]

Background

The product I wanted to update is the Epic Mix Tape by Urban Hipster digital download example product. This is a fake album featuring all of your favourites by artists you’ve never heard before. The idea is to showcase that you can add digital products to a Drupal Commerce based online store, not just physical products.

Originally we were using just a standard file field that, when checkout was completed, gave the customer access to download the file. This was done before the Media module made its way into core. Now that the Media module is in core, we figured it’s time to update it.

Setting up an Album media type

When the Media module is installed you get some new admin menu items. The first is a section called Media Types (under Structure) where you can configure your media entities like any other Drupal content entity. Here I created an ‘Album’ media type with two unlimited file fields, one for sample audio tracks and one for the full audio tracks. This is the basis for creating my downloadable albums.

The second admin menu is under Content. Here you get a new Media tab which is where you can add, edit and remove any media items. Since I already created the Album media type I can now add the Epic Mix Tape album files here. This completes the media side of the updated digital download product. All I need to do now is update the product configuration to use it.

Completing the digital download product configuration

Now that the media type has been added and I’ve uploaded an album, I need to set up a way to use it. It’s pretty easy to do. First, for the digital download Product Type, I add an entity reference field to give a way for selecting the album media entity to use for the product samples.

I then do the same thing for the Product Variation Type. This one, however, will be used to give access to the full files after purchase.

Finally, some template updates. The Drupal Commerce demo site has some pretty custom template files for the products. In the template, I access the media entity directly and loop through the items, printing each audio sample and track title onto the product page. I do the same thing for the checkout complete page but print out the full tracks instead.

Depending on your templates and display settings, you can get similar results without manually accessing the files in the template file, however I wanted to print out the file description with the audio player right on the page. Showing the description unfortunately is something you don’t have the option of doing using the standard audio display widget.

And that’s it! Check out the Urban Hipster Drupal Commerce demo site below to see it in action.

Demo Drupal Commerce today! View our demo site.

Sep 29 2018
hw
Sep 29

Another month, another Drupal meetup in Bangalore. This month’s meetup was held at Athenahealth office on Lavelle Road in Bangalore. Since the last month’s meetup was scheduled a week early, there was more than usual gap since the last meetup. This time, we had a full schedule and exciting sessions planned. For all this, we were in a beautiful room on the 17th floor overlooking the gorgeous cityscape of Bangalore (as you can see in some of the photos below). This was thanks to Athenahealth, our gracious host for this meetup.

Drupal Meetup

We started at around 10:30 AM with introductions of everyone present in the room. We had about 30 attendees in total, out of which about 7-8 were first time attendees. After introductions, Taher started the day by talking about updates to Drupal, mainly Drupal 8.6, 8.6.1, and talking about Drupal 9. He also mentioned upcoming events and mainly talked about important dates for DrupalCon Seattle 2019 including session submission deadlines.

We begin today's #Drupal #meetup with @devtaher covering what's new in Drupal. pic.twitter.com/NoITmOOtPI

— hussainweb (@hussainweb) September 29, 2018

Sessions

The first session of the day was about Drupal 8 Plugins and Plugin API by Manoj Kumar of Athenahealth. Manoj described common confusions between plugins and services in Drupal 8 and when to use each of them. He talked about the plugin API itself, specifically plugin discovery and factories. He walked us through a demo of how to create our own plugin type and creating plugins of that type. This included a very lively and engaged discussion about plugins.

First session of the day about #Drupal plugins by @manojapare at @drupal_bug #meetup. pic.twitter.com/RMGrcb1Lp5

— hussainweb (@hussainweb) September 29, 2018

This was followed by a lightning session on using Alexa with Drupal by Rakshith of Axelerant. Rakshith described how a service like Alexa integrates with Drupal, demonstrated the Alexa developer console and described concepts like intents. He also demonstrated tying this together with Drupal where Alexa could respond to user queries like “Read article” or getting a list of articles.

Rakshith talks about using Alexa with Drupal at the @drupal_bug #meetup today. pic.twitter.com/tWc1maQlN7

— hussainweb (@hussainweb) September 29, 2018

Break

This was followed by a few announcements and a break, with refreshments courtesy of Athenahealth.

Thank you @athenahealth for hosting us for this month's #Drupal #meetup and providing a beautiful venue and refreshments. pic.twitter.com/C0p6elSWBH

— hussainweb (@hussainweb) September 29, 2018

And more sessions

After the break, we resumed sessions with an introduction to the paragraphs module by Parvateesam Konapala of TCS. Parvateesam started by explaining what Paragraphs module provides and some of the modules which extend paragraphs’ functionalities. He also gave a demo in which he created paragraph types, adding them to content types, and how to use paragraphs in your site building workflow.

Back from a break and we have @paru_523 giving an introduction to paragraphs module in #Drupal. pic.twitter.com/vOO7Wmw2iJ

— hussainweb (@hussainweb) September 29, 2018

The last session of the day was on using Tome by Malabya Tewari of Specbee. Malabya started by summarising static site generators and how are they different from the conventional approach. He talked where static site generators might be used and their benefits. After talking about a few other systems, Malabya started talking about Tome and how it works. He demonstrated a workflow of a static website

The last session of the day on Tome (static site generator using #Drupal) by @malavya88 at @drupal_bug #meetup. pic.twitter.com/lyC0MNEkNC

— hussainweb (@hussainweb) September 29, 2018

End of the day

We had a quick questions and answers round where people brought up various issues they are facing and we collaboratively discussed on solving them. This was quickly followed by closing announcements and of course, crediting all the speakers and organisers on our meetup planning issue. We also discussed a bit about the format of our meetups and what we can do to improve. After a great discussion on couple of topics on what else we could do, the meetup ended at 2 PM with a group photo. There are more photos below.

Drupal Meetup Bangalore - September 2018

Sep 28 2018
Sep 28

Pairing Composer template for Drupal Projects with Lando gives you a fully working Drupal environment with barely any setup.

Lando is an open-source, cross-platform local development environment. It uses Docker to build containers for well-known frameworks and services written in simple recipes. If you haven’t started using Lando for your local development, we highly recommend it. It is easier, faster, and relatively pain-free compared to MAMP, WAMP, VirtualBox VMs, Vagrant or building your own Docker infrastructure.

Prerequisites

You’ll need to have Composer and Lando installed:

Setting up Composer Template Drupal Project

If you want to find details about what you are getting when you install the drupal-project you can view the repo. Otherwise, if you’d rather simply set up a Drupal template site, run the following command.

composer create-project drupal-composer/drupal-project:8.x-dev [your-project] --stability dev --no-interaction

Once that is done running, cd into the newly created directory. You’ll find that you now have a more than basic Drupal installation.

Getting the site setup on Lando

Next, run lando init, which prompts you with 3 simple questions:

? What recipe do you want to use? > drupal8
? Where is your webroot relative to the init destination? > web
? What do you want to call this app? > [your-project]

Once that is done provisioning, run lando start—which downloads and spins up the necessary containers. Providing you with a set of URLs that you can use to visit your site:

https://localhost:32807
http://localhost:32808
http://[your-project].lndo.site:8000
https://[your-project].lndo.site

Setup Drupal

Visit any of the URLs to initialize the Drupal installation flow. Run lando info to get the database detail:

Database: drupal8
Username: drupal8
Password: drupal8
Host: database

Working with your new Site

One of the useful benefits of using Lando is that your toolchain does not need to be installed on your local machine, it can be installed in the Docker container that Lando uses. Meaning you can use commands provided by Lando without having to install other packages. The commands that come with Lando include lando drush, lando drupal, and lando composer. Execute these commands in your command prompt as usual, though they'll execute from within the container.

Once you commit your lando.yml file others can use the same Lando configuration on their machines. Having this shared configuration makes it easy to share and set up local environments that have the same configuration.

Sep 25 2018
Sep 25

The Urban Hipster Drupal Commerce demo site was built to showcase what Drupal 8 and Commerce related modules can do. While the main focus has been Commerce, recently I started enhancing the content side of the site, mainly the blog. After all, Drupal is a content publishing platform at its core, so why not show how content and commerce can work on the same platform together. In the ecommerce world, that’s actually a pretty big deal!

In this Tech Talk video, I’ll show you how the Drupal core Comments module is used for blog commenting and product reviews. I also go into detail on how you can configure a role based publishing workflow using core’s Workflows and Content Moderation modules.

[embedded content]

Comments and reviews

All of the blog posts and products on the demo site use the core Comments module for customer feedback. This allows any level of user (anonymous, authenticated, etc.) to add comments or reviews to these content items. The configuration and permissions for the Comments module controls whether or not the comments need to be approved by an administrator before they appear on the site. When logged in, an administrator who has permissions to manage the comments can use both the frontend interface as well as a backend interface for deleting, approving, editing and finally replying to the comments.

Like any content entity in Drupal, comments are fieldable. This means that you can configure fields to allow for additional functionality for your comments. It’s not covered in this video, but it’s worth mentioning that this is how I was able to get a 5 star review system easily integrated into the product comments.

Content moderation workflows

Drupal core also has a couple modules for letting you define a process for adding specific types of content to your site. The Urban Hipster blog is now setup to be an example for this. 

The first aspect to configure is the workflow. Workflows is where you determine what content will make use of the workflow, the “states” that the content will transition through, and finally the transitions that can happen at any given state. These things all need to be configured first before moving on to permissions.

The second aspect is assigning role based permissions to use the workflow. Permissions for workflows are found in the usual permissions backend page where all other permissions are set. Each workflow transition has a permission attached to it and so you just simply check the role that can perform each transition. You can create new roles if you need to.

View the live example

As mentioned, the Urban Hipster Drupal Commerce is an example of what can be done. Try it out yourself and see what you think. Here are some username/password combinations that will let you check out the workflows in action. The site refreshes every night so you don’t need to worry about breaking anything.

Role based workflow logins:

  • Blog author: blogauthor/blogauthor
  • Blog reviewer: blogreviewer/blogreviewer
  • Blog publisher: blogpublisher/blogpublisher

Administrator login (for viewing the configuration):

  • Administrator: demoadmin/demoadmin
Demo Drupal Commerce today! View our demo site.
Sep 25 2018
Sep 25

With phone in hand, laptop in bag and earbuds in place, the typical user quickly scans multiple sites. If your site takes too long to load, your visitor is gone. If your site isn’t mobile friendly, you’ve lost precious traffic. That’s why it’s essential to build well organized, mobile ready sites.

But how do you get good results?

  • Understand whom you’re building for
  • Employ the right frameworks
  • Organize your codebase
  • Make your life a lot easier with a CSS preprocessor

Let’s look at each of these points.

Design For Mobile

When you look at usage statistics, the trend is clear. This chart shows how mobile device usage has increased each year. 
 

Mobile device usage graphSource

A vast array of mobile devices accomplish a variety of tasks while running tons of applications. This plethora of device options means that you need to account for a wide assortment of display sizes in the design process.

As a front end developer, it’s vital to consider all possible end users when creating a web experience. Keeping so many display sizes in mind can be a challenge, and responsive design methodologies are useful to tackle that problem.

Frameworks that Work

Bootstrap, Zurb, and Jeet are among the frameworks that developers use to give websites a responsive layout. The concept of responsive web design provides for optimal viewing and interaction across many devices. Media queries are rules that developers write to adapt designs to specific screen widths or height.

Writing these from scratch can be time consuming and repetitive, so frameworks prepackage media queries using common screen size rules. They are worth a try even just as a starting point in a project.

Organizing A Large Code Base

Depending on the size of a web project, just the front end code can be difficult to organize. Creating an organizational standard that all developers on a team should follow can be a challenge. Here at Zivtech, we are moving toward the atomic design methodology pioneered by Brad Frost. Taking cues from chemistry, this design paradigm suggests that developers organize code into 5 categories:

  1. Atoms
  2. Molecules
  3. Organisms
  4. Templates
  5. Pages

Basic HTML tags like inputs, labels, and buttons would be considered atoms. Styling atoms can be done in one or more appropriate files. A search form, for example, is considered a molecule composed of a label atom, input atom, and button atom. The search form is styled around its atomic components, which can be tied in as partials or includes. The search form molecule is placed in the context of the header organism, which also contains the logo atom and the primary navigation molecule.

Now Add CSS Preprocessors

Although atomic design structure is a great start to organizing code, CSS preprocessors such as Sass are useful tools to streamline the development process. One cool feature of Sass is that it allows developers to define variables so that repetitive code can be defined once and reused throughout.

Here’s an example. If a project uses a specific shade of mint blue (#37FDFC), it can be defined in a Sass file as $mint-blue = #37FDFC. When styling, instead of typing the hex code every time, you can simply use $mint-blue. It makes the code easier to read and understand for the team. 

Let’s say the client rebrands and wants that blue changed to a slightly lighter shade (#97FFFF). Instead of manually finding all the areas where $mint-blue is referenced on multiples pages of code, a developer can easily revise the variable to equal the new shade ($mint-blue = #97FFFF; ). This change now automatically reflects everywhere $mint-blue was used.

Another useful feature of Sass is the ability to nest style rules. Traditionally, with plain CSS, a developer would have to repetitively type the parent selector multiple times to target each child component. With Sass, you can confidently nest styles within a parent tag, as shown below. The two examples here are equivalent, but when you use Sass, it’s a kind of shorthand that automates the process.

Traditional CSS

Sass

Although there are a lot of challenges organizing code and designing for a wide variety of screen sizes, keep in mind that there are excellent tools available to automate the development process, gracefully solve all your front end problems and keep your site traffic healthy.

This post was originally published on July 1, 2016 and has been updated for accuracy.

Sep 22 2018
Sep 22

Regular Structured Communications

There can be a tendency to presume everyone knows what they are doing and should be allowed to work on their own initiative. This is a tendency I agree with, but it’s also of utmost importance to make sure others on the team also know what each person is working on.

Without regular, structured communications (at least weekly, if not daily, meetings), it’s quite possible for one person to work on a component (say a “tile” component) while another person works on another component (say a “card” component) and both of these components to be the exact same item. Except now we have two versions of the same component, just different names for them.

Without regular, structured communications, each team member is working in a silo, not aware of the whole of the project, and being responsible for only their section. This makes it harder to integrate each section of the site into each other. It will also mean it takes more time for the diligent developer to understand each part of the project if they wish to. Regular meetings save time.

One of the great things about regular, structured communications is that they lend themselves very well to team building. Let your team know you are very proud of how much has been achieved this sprint. Congratulate someone for solving a particular tricky issue. Allow a space for people to vent any issues they might be having.

Fewer Developers, More Focussed

It’s a truism and a paradox, but the more developers we have on a project, the longer the project is going to take to complete. Every new developer takes time to get up to speed with the codebase and slows down the momentum of the other team members. This causes projects to go over-time and over-budget.

This is not to say we should have only one developer per project, but I would suggest keeping teams as small as possible. A small number of really focussed developers will work much faster (having such an intimate knowledge of the codebase, feature set, sprint goals, etc.) than a large team of developers working on many small features. A large team lends itself too easily to people changing someone else’s code not expecting there to be any ramifications, to people “fixing” up another’s code because it doesn’t meet coding standards or has a security issue – work that shouldn’t have to be redone.

Use a Design System/Pattern Library

Pattern library tools have become very popular in the past few years. They allow everyone to see an inventory of the different components the team has available to work with when building web pages.

The tool I have chosen to standardise on is PatternLab. There are many others.

They also allow us to show our clients what the designs will look like on real devices, rather than images of what they might look like in InVision or some other tool like that. For years, yes literally years, I’ve been talking at conferences extolling the virtues of using a ‘Design in the Browser’ approach to front-end development and against the practice of using static design tools (Photoshop/Sketch) to deliver designs to clients. By all means, use these tools to deliver the designs to your team, but let the team code up the designs to deliver to your client.

Clients should be sent URLs, not PDFs.

One Component Per Design Feature

Once we are all agreed that we are going to use a pattern library tool, we can then start coding up the features of the front-end. Often components have variations of themselves. An example could be a “Card” component which has an image at the top, followed by a headline; with a variation which has the headline on top followed by the image. In this case we might create a card component and also a card—reversed component.

Sometimes teams can fall into the trap of thinking that “everything” is a variation of a core component. So, for example, we might have a component which has an image on the left, a headline and some teaser text on the right and/or vice-versa. Surely we can just create a component variation such as card—columns and card—columns-reversed? Yes, we can. But then when we see another component that has the same layout, except the image is a different size, there is no teaser text, we have tags under the headline, etc., we are then going to need to make another variation of card— and yet another variation of card— and yet another variation of card— and so on. Technically this is possible, but in practice you are setting yourself up for a maintenance nightmare.

The reason each of these components might look like variations on a core component is because they designers are following brand guidelines and using the same font, same sizing and rhythm rules, same colour scheme, etc. When you think about it like that, everything should look similar – it’s all supposed to fit together, but that doesn’t mean everything is a variation of a core component.

If we want to create all the variations of display types based off one core component, we are going to have a large core file, with a lot of logic and clauses and “if this then that” statements, and an equally large CSS file to take consideration of all the variations. This makes it harder to read the code, harder for new developers to get on-boarded to the project, and harder to maintain.

My suggestion is to create one component per display type. Based on the examples above, we would have a card component and a columns component. My files in PatternLab might look like this:

/card
– card.twig
– card.js
– card.scss
– card.yml
– card~reversed.yml
– card~
no-teaser-text.yml
– card~
no-teaser--text-reversed.yml
– card.md

/columns
– columns.twig
– columns.js
– columns.scss
– columns.yml
– columns~reversed.yml
– columns~
no-teaser-text.yml
– columns~
no-teaser-text-reversed.yml
– columns.md

This means that all the functionality and styling for the card component is within the card directory. It’s a small core file, with only variations for the image to be top/bottom or for there to be teaser text/no teaser text – very obvious variations of the card. The core file should be easy to read (one CSS class will create the variations), and it’s specific enough to know exactly what this component does.

We can set up a regression test for each component very easily to catch any curveballs that might arise later. None should arise if we use correct BEM naming conventions so that all styling for .card is prefixed with .card and all styling for .columns is prefixed with .columns, like so:

.card { css for card container }
.card__image { css for the image in the card }
.card__headline { css for the headline in the card}

.columns { css for columns container }
.columns__image { css for the image in the columns }
.columns__headline { css for the headline in the columns}

Focus on Features, Not Breakpoints

The Unix philosophy says “do one thing, do it well”. Taking this as a guiding principle, we should focus on developing one feature and completing it (the “card” component, for example) before moving to the next one. Sometimes I talk with teams and they tell me they focus on the desktop first or on the mobile screen views first and try to get the whole website looking correct at one breakpoint before moving on to the next one. While much of the code might be fine for each breakpoint, going over the code to catch all the issues for other breakpoints is going to take longer than completing each feature and having them signed off.

Remember, signed-off features can have regression tests written against each breakpoint. It also means that if the site is going to go over time, it’s possible to launch with a smaller set of (completed) features, rather than a feature-complete website, the front-end of which does not meet all the brand guidelines.

Automate Code Quality

Following coding standards can take time. You write your code, you check back over it, you fix up the indentation and comments style. And – you still miss something. The simple fix? Using code sniffers and/or linters and/or combing tools to automate this for you.

I have CSSComb set up in all my projects, so every time I save a CSS or SCSS file, it’s automatically formatted to follow the (Drupal) coding standards. I use prettier for my JS files, so again, each time I save my file, it’s automatically formatted to meet our coding standards. I have php_codesniffer installed and set to (Drupal) coding standards, so it notifies me each time I have an indentation wrong or something like that.

Why is this important? This is important when working as part of a team to make sure everyone’s code looks the same. It makes it much easier to read code from other developers when it is formatted the same as your code. It’s even more important when you edit a file that someone else has worked on and then try to commit those changes to git. How many of the changes were changes you made (that actually affect the code) and how many were just the csscomb or prettier auto-formatting your code? You want your git commits to be meaningful, so you only want the commit to announce the changes you have made.

What I do now on any file I need to work on is save it the moment I open it, so if another developer doesn’t have automated code checkers built-in, the file will format itself. Then I commit this save with the git commit message “running csscomb” or “running prettier”. After this, I make my changes and my commits are then meaningful – I can see the actual code I changed. This takes more time that it would if everyone just installed code formatting tools from the outset, and is something I think we should insist on for every project.

Front-end in Tandem with Site Building

The chances are that the designs are going to be sent to the client as Sketch files or PDFs or similar and not as PatternLab URLs (despite my best efforts to encourage this for years). Or maybe another agency won the contract to design the website and our agency won the contract to build/maintain it. If this is the case, by the time they get to the front-end developers, they are signed-off – a fait accompli.

Let’s turn this into a positive. As we are creating our front-end components (one component per design pattern, please!), we should also be building out the CMS functionality that this component will use. So, if we have a listing page showing teasers of events in a card display, then we should build out the fields (in Drupal or whatever CMS we use) that support this, and then create the configuration and templates needed to map 1-to-1 from Drupal to PatternLab.

This allows us to “dog food” our own work very early on. We can create our front-end and backend in tandem and make sure that both work seamlessly. We can ask our clients to sign-off on the front-end and CMS features at the same time; and we can then set up regression testing for the front-end and functional testing (with Behat or something else) for the backend. As the project progresses, we will have a featured-complete, fully-tested website and can move from feature to feature (or new feature to new feature during future phases of work) with less fear of things breaking when we make changes.

Regression Test

Here’s a joke: a css selector walks into a bar; a table in another bar across town falls over.

Writing front-end code can often be like playing Whack-A-Mole. It doesn’t have to be.

Regression testing is basically taking a screenshot of your website that you can use as a “reference” (this is what it is supposed to look like). Then, when you make a change, you can take automated screenshots of the site again to make sure that the only things changed were things you expected to change. The regression testing tool I use is BackstopJS.

An example: you have a header, a main content area, and a footer. You set up your reference shots when these are all signed off by the client. In a future sprint you are asked to change the main content area from 1220px wide to 1440px wide on desktops. You make the change, tell your regression testing tool to run the tests and then you get back a report showing you that the main content area is now wider than the header and the footer – as it should be. You see the header and footer were not affected so you can deploy this new item. Now, think about doing this for every component across your website! The regression testing suite will run all the tests in a matter of minutes (while you can be happily working on another feature – multitasking for the win!), whereas it could take you an hour to check everything and you might miss something.

Like automating code quality, automate regression testing: automate the boring things.

Set Time Limit for Being Blocked

If a developer is blocked on something and can’t figure out how to complete the task, that is time wasted. Developers need to know they can ask for advice and they need to know whom they should ask – probably the gatekeeper (see next section).

My general rule of thumb for this is: if I feel I am blocked and just looking at the screen for 15 minutes, I need to ask for help. If I haven’t an idea what to do to unblock myself and I’ve thought hard about it for 15 minutes, that’s enough of the company’s money spent on it.

Ask for help sooner rather than later so you can be as productive as possible.

Nominate Code Gatekeeper

If you need to have a large team of developers, you need to have a code gatekeeper: someone whose job it is to know what components are developed, what ones are in progress, and what is in the backlog.

The gatekeeper also needs to know who developed what component and in what manner, so any components that are re-usable are re-used and one-off components are kept to a minimum. It is the gatekeeper’s job to ensure that no component is developed more than once, just using different names (“card” and “tile” as we saw above).

When someone is blocked or unsure of something, they ask the gatekeeper for advice and guidance so they can continue working as soon as possible. As well as that, the gatekeeper can also step in and try to figure out a solution (or even a proof-of-concept for a solution) while the developer works on something else. When the gatekeeper has an approach figured out they can tell the developer about it and then the developer can get back to that feature as soon as possible. The gatekeeper is a pair of fresh eyes.

The gatekeeper might be the lead developer on the project or it could be a separate role. The gatekeeper should write as little code as possible for the actual production version of the site, and, rather, should focus on the high-level overview of the project and working on solutions to unblock developers.

Conclusion

That’s it. That’s my thoughts on putting together and running a front-end (or teams) that can effectively work on large, enterprise websites. If you have a small team or are a freelancer, I think what I have said above also holds true.

Any thoughts or comments, leave them below. Thanks.

Sep 21 2018
DA
Sep 21

In this post we will share our experience in installing a Drupal 8 application on an Amazon EC2 server with latest Ubuntu 18.04 LTS.

Installing Drupal with composer greatly simplify system maintenance and further update.

AWS image

First you will need to create EC2 instance with proper AMI from Amazon Web Service. You can find the AMI from the locator.

We will not cover in detail this part, as we assume this is already covered by many other blogs and tutorials.

We pick latest 18.04 LTS version of Ubuntu to comply with requirements of Drupal 8 with PHP 7.2.

Composer

Once your server is running, the next step is to install composer.

Once again, we will not go too much into details as composer installation is also widely covered.

In our case we followed similar procedure as the one described here.

Drupal

For actual installatin of Drupal with composer, there is a guide at drupal.org with 3 options. We picked the option A.

The repository is a composer template for Drupal projects with pretty good usage guide. The latest version will install Drupal 8.6.1.

We run the command:

git clone https://github.com/drupal-composer/drupal-project.git

(note: the code will be copied within the folder "MyAppName" within your current folder location. For instance if you are in /var/www, the application will be in /var/www/MyAppName)

At this point we edited the composer.json file to match our desired folder configuration. You need to edit manually the installer path here before installing the application or if you prefer, keep the default paths.


"installer-paths": {
  "web/core": ["type:drupal-core"],
  "web/libraries/{$name}": ["type:drupal-library"],
  "web/modules/contrib/{$name}": ["type:drupal-module"],
  "web/profiles/contrib/{$name}": ["type:drupal-profile"],
  "web/themes/contrib/{$name}": ["type:drupal-theme"],
  "drush/Commands/{$name}": ["type:drupal-drush"]
},

To edit, run command:

Sudo nano MyAppName/composer.json

and edit "installer-paths". In our case we changed to:

Once your have the desired folder configuration, you can run actual installation command:

composer -vvv install

(note: -vvv option is optional)

This will install Drupal site.

Custom application

One of the purpose of using composer installation is to merge other composer files and install custom plugins and applications.

To be able to merge composer files, you need to install the composer-merge-plugin first with command:

composer require wikimedia/composer-merge-plugin

then run:

composer update --lock

You can now add additional plugins with their specific composer installer. In our case, we install our own application EK Management tools suite with the following command:

composer require arreasystem/ek:"dev-8.x-dev"

This will install custom application.

You can merge composer.json paths as "extra" option in main composer.json:

Sudo nano MyAppName/composer.json

For instance add the custom plugins paths:    


"extra": {
     "merge-plugin": {
         "include": [
            "modules/contrib/ek/ek_admin/composer.json"
         ],
         "recurse": true,
         "replace": false,
         "merge-extra": false
     },

And run:

composer update --lock

This will for instance install following libraries:

You may encounter error with composer when updating with an out of memory error. This will happen with low specification EC2 instances. To solve this problem, add swap memory on Ubuntu server.

Create swap file:

sudo dd if=/dev/zero of=/swapfile bs=2M count=2048
(this will create a 4M memory swap);

Enable file:

sudo chmod 600 /swapfile

Allocate:

sudo mkswap /swapfile

Start:

sudo swapon /swapfile

With this installation, you will just have to run

composer update

We hope this short post has been useful. Feel free to add comment or questions.

Sep 20 2018
Sep 20

Responsive images in PatternLab get a bit of a bad rap sometimes, because they are tricky to have in PL and Drupal. Here's my "easy way" of achieving it.

This came up today in the DrupalTwig Slack (join it). A user wanted to know how to use responsive images with the Emulsify Drupal theme. I don't use the Emulsify theme (yet - I will soon), though Four Kitchens, the geniuses who created it, have responsive images built in. Recently I created my own - simple and rudimentary, but it works a treat.

I first create a "Responsive Image" pattern. In this I have two files - responsive-image.twig and responsive-image.yml. Here's the contents:

responsive-image.twig:

responsive-image.yml:

image_src_sets:
  join():
    - 'https://placeimg.com/500/500/nature 500w, '
    - 'https://placeimg.com/1000/750/nature 1000w, '
    - 'https://placeimg.com/1440/475/nature 1440w'

image_sizes: '(max-width: 600px) 100vw, (max-width: 960px) 100vw'

image_src: ''https://placeimg.com/1440/475/nature'

To use it in another component, I just call a variable and set that variable in the YML file.

For example, to call the hero image as a responsive image in my event component, I'll print this: {{ hero_image }}. Then in my corresponding event.yml file, I'll define the hero_image item like so:

hero_image:
  join():
    - include():
        pattern: 'basic-elements-responsive-image'
        with:
          image_src_sets:
            join():
              - 'https://placeimg.com/600/600/tech 500w, '
              - 'https://placeimg.com/1200/360/nature 1000w'
         image_src: 'https://placeimg.com/1200/360/nature'

The {{ image_src }} variable is needed to provide a default fallback for browsers that don't support srcset or sizes, such as IE9/10/11.

Then in my Drupal template I just swap my image field variable for the responsive image one, like this:

{% if node.field_hero_image.value %}
  {% set hero_image: content.field_hero_image %}
{% endif %}
{% include ... usual path to component stuff ... %}

Drupal then renders the image field using whatever settings I have given it in Drupal - presumably responsive image ones.

This post might not help you if you are using Emulsify, but it might help others who stumble upon it.

Sep 19 2018
Sep 19

Expo just got nominated for the Swedish design award. Before Expo was nominated for two publishing awards, best magazine and best magazine website. The winners will be announced 7th of November 2018 in Stocholm where Ramsalt will be present. While we we wait, we have decided to share with you the secrets behind building Expo.se on Drupal. 

Expo screenshotShort about the client 

Expo is a Swedish anti-racist magazine started in 1995 by Stieg Larsson, also known as the author of the Millennium series books, where the inspiration comes from Expo. Expo magazine is issued by the non-profit Expo Foundation. The magazine contains investigative journalism focused on nationalist, racist, anti-democratic, anti-semitic, and far-right movements and organisations. The people responsible for Expo make no connections with specific organisations or political parties, but work together with individuals and organisations that share Expo's platform. The magazine is headquartered in Stockholm.

Why Drupal was chosen?

Drupal was chosen for many reasons and out-ranked other solutions because it fulfilled many of the requirements from Expo. The strong capabilities for large amount of data, content-authoring options and the high level of community support were a few key elements where Drupal perfectly met the needs. Additionally, Drupal provided reliable content management and moderation features, as well as an editor and user-focused interface. Especially when it came to the wiki functions.

Other reasons include

  • Feature flexibility
  • Scalability
  • No licensing fees
  • Maintainability

Goals, requirements and outcome

Previous site pain points
Multiple Subsites
The previous version of the site was spread over a number of subsites/subdomains which the client was eager to unify into one categorised Drupal site.
Complex, custom database structure & content editing
The database of the previous site was overly complex and custom built which made the site very hard to maintain. On top of that the content editing forms were very long and confusing.

Non-responsive theme
Expo has a huge readership but without a mobile friendly version of the site the full potential of their reach was not being met.

Understanding the pain points of the previous site was essential in making sure we met the needs of the client.

Ramsalt Media
At Ramsalt we’ve developed our own distribution of Drupal called Ramsalt Media which is specialised for online publishers including newspapers & magazines. Ramsalt Media includes all the tools are publisher needs including article scheduling, WYSIWYG, media management and frontpage management. We were able to use Ramsalt Media as a platform to build on top for the rest of the site needs which went outside that of our standard clients. At the time of project start in 2017, Ramsalt Media was only stable for Drupal 7 so initial development has been done in Drupal 7, but Ramsalt Media has since 2018 been available in Drupal 8 buildt with Thunder.

Key development steps
Content Migration
Expo produces both a physical magazine and regular online articles so all the old articles from the custom database had to be migrated into Drupal along with their related media assets. As mentioned above, the database had a very complex structure & naming convention, once information from the previous developer was provided we were able to start mapping the migration for the some 7000+ articles & 2500+ media assets. The migrate module was the main contrib module we used for the migration.

We also made use of rules autotag module which allowed us to read article titles & body content and auto tag articles on migration based on the topics they covered. The topics were all predefined as taxonomy terms, if an exact match was found in the title or body then the article was tagged with the given taxonomy term. This gave the editorials a helpful kick start in categorising all the articles within Drupal as taxonomy term landing pages (see: https://expo.se/tag/hatets-politik-2017 ) where deemed an important new piece of functionality on the site.



Just double click on any text and you jump into edit mode, with our module QuickerEdit

Improved Publishing Tools
As previously mentioned the site was built on top of our own distribution called Ramsalt Media. Ramsalt Media pulls together some of the best contrib modules out there to make article & content publishing easier and more streamlined.

Responsive Theming
We worked closely with Expo’s own design team led by Daniela Juvall throughout development. The goal was a simple, clean design with bold typography which was inline with Expo’s physical magazine style. The magazine has been nominated for a number of design awards in Sweden.
As mentioned the previous site was not responsive so it was essential the new site was optimised for all devices.
The theme was custom built for Drupal 7 using sass.

The New Wiki
Another major part of the development involved creating a new Wiki section to expo.se to house information on all the topics covered by Expo. This included information about right-wing political leaders and groups as well as their symbols and related articles. This section was built as a number of different content types which were then bridged together using taxonomy and entity reference fields. This approached allowed wiki nodes to be associated to a single taxonomy term which could in turn be associated with other content on the site. Binding the wiki together is a central search page which was built using the search API, facets & panels.

[embedded content] Long Reads
Long read or long form articles have become more and more popular with our publishing clients in recent years. They allow for a richer and longer form of article to be published.
Expo wanted long read functionality to be able to post longer articles from the physical magazine in a richer and more eye catching manner. We used the paragraphs module for this and created a number of custom paragraph types to store the different types of content needed to make up a long read. This included text paragraphs, call to actions, images and videos.
We also created a “layout” paragraph type which allowed all paragraph types to be laid out in a set responsive left/right column layout. On top of paragraphs we custom built a floating “table of content” (optional) and read indicator bar.
We found paragraphs to be the best option for this job although the node edit form is an area where we are hoping to improve in the future as it can become confusing on very long articles.

Site Building
The rest of the site was built out using custom content types & fields, views, entity collection, panels/page manager and views content panes. This allowed for a rapid & streamlined approach to site building. Using views content panes meant similar displays could reuse a single view using pane settings to vary the display as needed.

Key modules 

Scheduler: allows editors automatically set articles & content to publish (and unpublish) at a set date & time.

Entity Collection: allows editors to control the frontpage articles list in a custom order and apply custom styles to each article in the context of the frontpage. This along with views & panels allowed us to build a custom frontpage and inject custom blocks with the frontpage article flow.
Entity collection was used throughout the site to give a unified approach to all content lists/blocks you see throughout the site.

Media: allows editors to upload media to a central library allowing easy reuse of media throughout the site.

Fields: of course the fields module & it’s contrib modules are used to provide custom field types allowing editors to add galleries, videos, article sources, bylines and more.

Sep 18 2018
Sep 18

A slick new feature was recently added to Drupal 8 starting with the 8.5 release  — out of the box off-canvas dialog support.

Off-canvas dialogs are those which slide out from off page. They push over existing content in order to make space for themselves while keeping the existing content unobstructed, unlike a traditional dialog popup. These dialogs are often used for menus on smaller screens. Most Drupal 8 users are familiar with Admin Toolbar's use of an off-canvas style menu tray, which is automatically enabled on smaller screens.

Admin toolbar off-canvas

Drupal founder Dries posted a tutorial and I finally got a chance to try it myself.

In my case, I was creating a form for reviewers to submit reviews of long and complicated application submissions. Reviewers needed to be able to easily access the entire application while entering their review. A form at the bottom of the screen would have meant too much scrolling, and a traditional popup would have blocked much of the content they needed to see. Therefore, an off-canvas style dialog was the perfect solution. 

Build your own

With the latest updates to Drupal core, you can now easily add your own off-canvas dialogs.

Create a page for Your off-canvas content 

The built in off-canvas integration is designed to load Drupal pages into the dialog window (and only pages as far as I can tell). So you will need either an existing page, such as a node edit form, or you'll need to create your custom own page through Drupal's routing system, which will contain your custom form or other content. In my case, I created a custom page with a custom form.

Create a Link

Once you have a page that you would like to render inside the dialog, you'll need to create a link to that page. This will function as the triggering element to load the dialog.

In my case, I wanted to render the review form dialog from the application full node display itself. I created an "extra field" using hook_entity_extra_field_info(), built the link in hook_ENTITY_TYPE_view(), and then configured the new link field using the Manage Display tab for my application entity. 

/*
 * Implements hook_entity_extra_field_info().
 */
function custom_entity_extra_field_info() {
  $extra['node']['application']['display']['review_form_link'] = array(
    'label' => t('Review Application'),
    'description' => t('Displays a link to the review form.'),
    'weight' => 0,
  );
  return $extra;
}

/**
 * Implements hook_ENTITY_TYPE_view().
 */
function custom_node_view(array &$build, Drupal\Core\Entity\EntityInterface $entity, Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode) {
  if ($display->getComponent('review_form_link')) {
    $build['review_link'] = array(
      '#title' => t('Review Application'),
      '#type' => 'link',
      '#url' => Url::fromRoute('custom.review_form', ['application' => $entity->id()]),
    );
  }
}

Add off-canvas to the link

Next you just need to set the link to open using off-canvas instead of as a new page.

There are four attributes to add to your link array in order to do this:

      '#attributes' => array(
        'class' => 'use-ajax',
        'data-dialog-renderer' => 'off_canvas',
        'data-dialog-type' => 'dialog',
        'data-dialog-options' => '{"width":"30%"}'
      ),
      '#attached' => [
        'library' => [
          'core/drupal.dialog.ajax',
        ],
      ],

The first three attributes are required to get your dialog working and the last is recommended, as it will let you control the size of the dialog.

Additionally, you'll need to attach the Drupal ajax dialog library. Before I added the library to my implementation, I was running into an issue where some user roles could access the dialog and others could not. It turned out this was because the library was being loaded for roles with access to the Admin Toolbar.

The rendered link will end up looking like:

Review Application

And that's it! Off-canvas dialog is done and ready for action.

off-canvas-demo-gif
Sep 12 2018
Sep 12

Creating great website content and presenting it well is the best way to improve Google rankings and engage more visitors. While you need to create the content, Drupal 8 is one of the elite Content Management Systems (CMS) available to organize and show it off well. Drupal’s ability to organize your marketing content is one of the reasons it has always been great for SEO.

A winning SEO strategy requires relevant, timely, and well-organized content. Website content provides the relevant substance for your keywords and includes links to related content. Fresh content takes precedence over older, stale content in Google search results. When your website content is well-organized, visitors and search engines will find it easier to navigate and Google will reward your site with even higher rankings.

6 strategies and actions for better content management with Drupal 8

With that in mind, here are a few tips to create great content and organize it well in Drupal 8.

1. Create great content

Write content that you would love to read. This holds your audience’s interest and gets them talking about--and linking to--your website. Remember to write for your audience not for the search engine. Search engine algorithms are smarter than ever and consider much broader concepts than just keywords. The search engine will evaluate your entire page for the best search match.

With Drupal, you can turn great ideas into organized text, graphics and videos that are valuable to your users. Drupal’s built in Taxonomy system allows you to categorize and tag content so that it is easy for your audience to find what matches their interest. Another building system, Views, gets your content out of Drupal and onto the page without needing a query. You create views and filters that meet specific needs.

natural seo keywords2. Use keywords naturally

While the latest Google search algorithms may lessen the need for exact key phrases, don’t give them up just yet. You should still optimize your content with keyphrases, just don’t overuse them or use them unnaturally. Repeating keywords without any valid reason will turn off visitors and search engines. Even so, make sure your keyword shows up once or twice in the document.

To find related words that will help your ranking, try typing your keyword into Google like this: ~yourkeyword. This is an approximate or synonym search. It finds terms that are related to the one you’re searching for. Pay attention to the bolded items in the search results and you’ll see keywords that Google considers approximate and relevant. Write additional content around those terms and Google will reward your efforts.

In Drupal 8, the Real Time SEO module can help you optimize your content around keyphrases. This module evaluates your text and compares it to SEO best practices.

When you create a new node, you’ll see a section called Real Time SEO for Drupal. Here you can identify the focus keyword. The module shows you what your listing will look like in Google and allows you to edit it. You can also edit the title tag, URL path and meta description. Then the module evaluates your content and makes recommendations on what to change to improve SEO. It will give you a list of items to fix as you create content. Having this module will keep your SEO strategy on track.

3. Use internal links

Properly formed and placed links are a powerful strategy for any SEO campaign. Quality links can improve navigation and increase website traffic. Links to other sites and link to pages within your own website are also useful in helping visitors to view more relevant content.

Ensuring that your internal links stay accurate is made easier using the Linkit module for Drupal 8. This module provides an autocomplete field in your content editor making it easy to create links and ensures that the links are well formed, up to date, and accurate. The Linkit module replaces the built in linking mechanism in your WYSIWYG editor. Once the module is installed, all you need to do is select the text that you wish to link and click the link button.

To get even more value from links, you can install the D8 Editor Advanced Link module. Now when you add a link in your text, there will be an “Advanced” dropdown box. Here, you can fill in the appropriate fields such as title, class, id, target and rel for each link in CKEditor. SEO is improved because there is more text information about each link on your page.

4. Organize your content

Organized content is useful content. Organize your content around categories, then create sections on subtopics that support the main idea. Drupal 8 has built-in modules to help you do this already, which is another reason that Drupal is the best CMS for SEO. By keeping similar ideas together, your website will be more useful to your visitors. It’s also easy to see where to focus your writing efforts to fill in content categories.

For the main content of your site, you should create a taxonomy category with a handful of keyword-rich terms that all of your content will fit under. If you have great categories and loads of content in those categories, it’s easy to add a few relevant keywords to the category pages. If your content is spread out over too many categories, or it’s not organized at all, it’s almost impossible to get all those ideas onto one page.

Organized paths and URLS matter too. No content should be more than three clicks from the home page. The closer the node is to the home page, the more important Google thinks it is and the higher that individual page will rank.

Using Drupal, it’s easy to create paths that give your site a nice, clean hierarchical structure, making it easier for visitors to find their way around. Using the Pathauto module you can automate SEO-friendly URLs. Putting good words in the URL is great for SEO so this module is essential. Drupal URL paths with Pathauto operate in patterns. Instead of creating a path to every single piece of content, it’s better to specify a pattern for groups of content. Drupal will follow the pattern so that you have consistency across your website.

breadcrumb examples on a cat website

The organization and labeling of your whole website is important. Taxonomy (for example, blog categories and tags) should be well-structured and hierarchical. Google loves hierarchy because it helps them understand your content. Visitors love hierarchy because it helps them figure out your site’s organization structure.

Another way to show your site’s hierarchy is through breadcrumbs. Breadcrumbs are top-of-page navigation elements that show visitors where they are on a website. Drupal 8 will create breadcrumbs automatically for you when you add the Easy Breadcrumb module. This module builds the breadcrumbs from the Drupal path. Each “/” becomes a part of the breadcrumb. The first breadcrumb comes from the Easy Breadcrumb configuration page while the title of the node becomes the last breadcrumb. Breadcrumbs help SEO by showing the hierarchy of your content to Google too.

Navigation is also improved when you provide an html sitemap. The Sitemap module for Drupal 8 provides an html sitemap which is a “cheat sheet” that makes it easier for visitors and search engines to navigate your website. Your visitors can use the sitemap to find the content they are looking for. This increases their time on site and reduces the bounce rate.

6. Keep your content compliant

Poorly written HTML might confuse the Googlebot or even your visitors’ browsers. When your HTML follows web standards such as the ones created by the World Wide Web Consortium (W3C), you may find that better SEO follows. Thankfully, checking your website’s HTML is easy with the W3C Validator module. This Drupal 8 module checks the markup validity of your website. Once you run the module, you will receive a list of errors for each page which you can turn over to your web developer. While you don’t need to obsess over every error, fixing the big mistakes can make a difference for your SEO.

Improve your Drupal 8 content management

Using the tips above will go a long way toward improving your Drupal 8 website for more website traffic and quality leads. If you need step-by-step guidance to improving Drupal 8 content management and SEO, take a look at Drupal 8 SEO. This book is the definitive guide to optimizing your content and boosting your Google rankings and it covers all of these tips in step-by-step detail..

Volacci is the expert when it comes to specialized Drupal SEO. Our Drupal marketing services will help you gain better, faster results for your website marketing strategy. Whether you are migrating from another CMS, upgrading from older Drupal platforms, or optimizing your current Drupal 8 website, we can help.

Give us a call at 512.989.2945 or get in touch on-line.

Sep 11 2018
Sep 11

There is a lot of talk out there right now about “decoupled” or “headless” open source eCommerce platforms. I won’t get into why that is in this article, but I will show you how easy it is to enable a full REST API for your Drupal 8 and Drupal Commerce platform in JSON format. It’s literally the enabling of a module… that’s it! Let’s take a look.

[embedded content]

In this Acro Media Tech Talk video, you’ll learn a little bit about the module used to expose the API, where to find documentation, and see an additional module that enhances the experience working with the API. Using our Drupal Commerce demo site as an example, you’ll see where you can view and modify the site resources as well as how to view the data for each resources in JSON format.

The data structure of Drupal is well suited to the JSON API which makes Drupal an excellent choice as a backend content-creation area for a decoupled application. This video will get you started, but what you ultimately do with that data is up to you!

Demo Drupal Commerce today! View our demo site.

Additional details:

Sep 10 2018
Sep 10

As ever, we have brought our bags of knowledge with us to share out our goodies. On Tuesday, Mark Conroy will co-present on the new installation profile and theme for Drupal core - Umami. This is part of the "Out of the Box" initiative of which Mark is co-maintainer. Mark will be accompanied by two other leads from that initiative - Eliot Ward and Keith Jay. Later on Tuesday, the three will co-host a BoF to set the parameters for what they want to achieve during the weeks' code sprints.

Also on Tuesday, Stella Power will co-present a session on Drupal governance. Maybe you already heard about the Governance Task Force. This is a chartered group that was formed to make a proposal on community governance in Drupal. The session will share what the task force is doing, how to get involved, and the current progress of the Task Force.

Not to be outdone, David Thorne will - again on Tuesday (we've a busy Tuesday!) - give a presentation on the Islandora CLAW distribution for Drupal. This talk will introduce CLAW's key concepts to educational establishments, many of whom may already use Drupal for websites, as well as Fedora Commons for their digital asset collections.

Wednesday is the day we'll all relax after our presentations on Tuesday, and look forward towards Thursday where we'll host the usual (pretty crazy) Drupal Trivia Night Quiz, along with an army of volunteer runners, judges, and the ever-dapper host - Anthony Lindsay.

On Friday, we'll crack open the laptops and contribute as much code as we possibly can to the Drupal project, before flying home Friday night and Saturday morning.

Oh, and - we're hiring!

Sep 08 2018
Sep 08

Yes, I know, there's more than one way to integrate PatternLab with Drupal. Here's how I create a card component and map it to Drupal.

Here's the task - create a card component with fields for:

  1. Card Image
  2. Card Title
  3. Card Body
  4. Card Link URL
  5. Card Style (sets a colour for a border-top on the card)
  6. Card Size (sets the width of the card)

In PatternLab, here's what our Twig file might look like (with explanations after it):

{%
set classes = [
  "card",
  card_image ? 'card--has-image',
  card_style ? 'card--' ~ card_style,
  card_size ? 'card--' ~ card_size,
]
%}

{% if card_link_url %}
  {% set element = 'a' %}
  {% else %}
    {% set element = 'div' %}
{% endif %}

<{{element}}{{ attributes.addClass(classes) }}{% if card_link_url %} href="https://mark.ie/blog/creating-a-card-component-in-patternlab-and-mapping-to-drupal-the-right-way/{{ card_link_url}}"{% endif %}>

  {% if card_image %}
    

{{ card_image }}

{% endif %} {% if card_title %}

{{ card_title }}

{% endif %} {% if card_text %}

{{ card_text }}

{% endif %} {% block content_variable %} {# This allows the cache_context to bubble up for us, without having to individually list every field in {{ content|without('field_name', 'field_other_field', 'field_etc') }} #} {% set catch_cache = content|render %} {% endblock %}

The classes array at the top allows us to set variations for our card, depending on values chosen by the editor. So, if there is an image, we add a class of .card--has-image; if a style is chosen, we add a class of that style, for example: .card--medium (I create options for small, medium, large, and full - with 'small' being the default - corresponding on large screens to a width within their container of 33%, 50% 66% and 100% respectively).

Next, we set our {{ element }}. This allows us to have the card wrapped in an a tag or a div tag. We check to see if the link field has been filled in and, if so, we use the a element, but if not, we use the div element instead. This will render HTML like one of the following:


  CARD STUFF GOES HERE

CARD STUFF GOES HERE

Following this, we check if there is an image and, if so, we render our image div. Checking first allows us to have nice bem-style classes, but also means we don't end up rendering emtpy divs. Although, when it comes to Drupal, what's another div!

We then do the same for the title and body.

The funny looking part at the end about cache was inspired by an article about Drupal block cache bubbling by PreviousNext. The specific code came from this Drupal.org issue. The PreviousNext article says to render the {{ content }} variable with our fields set to 'without', because without the {{ content }} variable rendering, caching is not working properly (I don't know enough about caching to explain more). However, on a content type with loads of fields, it's very cumbersome to add every field in with {{ content|without('field_image', 'field_tags', 'field_other', etc) }}. Instead, I put that {{ catch_cache = content|render }} at the bottom of each of my content patterns - node, block, paragraphs, etc, then don't need to add it later in Drupal.

The SCSS for this looks like this:

// Theming individual cards

.card {
  width: 100%;
  margin-bottom: $base-line-height;
  border-top: 0.5rem solid $c-primary;
  background-color: $c-grey--lighter;
}

a.card {
  text-decoration: none;
  &:focus,
  &:hover {
    background-color: $c-primary;
    background-image: linear-gradient($c-primary, darken($c-primary, 15%));
  }
}

.card--has-image {
  background-color: $c-white;
}

.card--small {
  @include breakpoint($bp--medium) {
    width: calc(33% - 2rem);
  }
}
.card--medium {
  @include breakpoint($bp--medium) {
    width: calc(50% - 2rem);
  }
}
.card--large {
  @include breakpoint($bp--medium) {
    width: calc(66% - 2rem);
  }
}
.card--full {
  @include breakpoint($bp--medium) {
    width: calc(100% - 2rem);
  }
}

.card--primary {
  border-top-color: $c-primary;
}
.card--secondary {
  border-top-color: $c-secondary;
}
.card--tertiary {
  border-top-color: $c-tertiary;
}
.card--quaternary {
  border-top-color: $c-quaternary;
}
.card--quinary {
  border-top-color: $c-quinary;
}
a.card--primary {
  &:focus,
  &:hover {
    background-color: $c-primary;
    background-image: linear-gradient($c-primary, darken($c-primary, 15%));
  }
}
a.card--secondary {
  &:focus,
  &:hover {
    background-color: $c-secondary;
    background-image: linear-gradient($c-secondary, darken($c-secondary, 15%));
    .card__text {
      color: $c-white;
    }
  }
}
a.card--tertiary {
  &:focus,
  &:hover {
    background-color: $c-tertiary;
    background-image: linear-gradient($c-tertiary, darken($c-tertiary, 15%));
    .card__title,
    .card__text {
      color: $c-white;
    }
  }
}
a.card--quaternary {
  &:focus,
  &:hover {
    background-color: $c-quaternary;
    background-image: linear-gradient($c-quaternary, darken($c-quaternary, 15%));
    .card__title,
    .card__text {
      color: $c-white;
    }
  }
}
a.card--quinary {
  &:focus,
  &:hover {
    background-color: $c-quinary;
    background-image: linear-gradient($c-quinary, darken($c-quinary, 15%));
    .card__text {
      color: $c-white;
    }
  }
}

.card__content {
  padding: $base-line-height;
}

.card__title {
  color: $c-grey--darker;
  font-family: $ff--alternate;
}

.card__image img {
  width: 100%;
  height: auto;
}

.card__text {
  color: $c-grey--dark;
  p:last-of-type {
    margin-bottom: 0;
  }
}

We can do the site building very easily with the paragraphs module. Create a paragraph of type card, add the fields

  1. Card Image - media image
  2. Card Title - text (plain)
  3. Card Body - text (long, formatted)
  4. Card Link URL - link
  5. Card Style (sets a colour for a border-top on the card) - text (list)
  6. Card Size (sets the width of the card) - text (list)

Then, in our paragraph--card.html.twig file, we write the following code:

{% if paragraph.field_p_card_style.value %}
  {% set card_style = paragraph.field_p_card_style.value %}
{% endif %}

{% if paragraph.field_p_card_size.value %}
  {% set card_size = paragraph.field_p_card_size.value %}
{% endif %}

{% if paragraph.field_p_card_link.value %}
  {% set card_link_url = content.field_p_card_link.0['#url'] %}
{% endif %}

{% if paragraph.field_p_card_image.value %}
  {% set card_image = content.field_p_card_image %}
{% endif %}

{% if paragraph.field_p_card_title.value %}
  {% set card_title = content.field_p_card_title %}
{% endif %}

{% if paragraph.field_p_card_text.value %}
  {% set card_text = content.field_p_card_text %}
{% endif %}

{% include "@building-blocks/card-layout/_card.twig" %}

What the above does is checks if the card paragraph has values in its fields and then sets variables if it does. This means we don't render empty divs.

You will also notice that I render each field's full content for image, title, and body. This is to keep all the Drupal goodness we have in the attributes object - for accessibility and to make sure things like contextual links/quick edit still work.

You will often see the same template written like this:

{% include "@building-blocks/card-layout/_card.twig"
  with {
    card_style = paragraph.field_p_card_style.value,
    card_size = paragraph.field_p_card_size.value,
    card_link_url = content.field_p_card_link.0['#url'],
    card_image = content.field_p_card_image,
    card_title = content.field_p_card_title,
    card_text = content.field_p_card_text
  }
%}

I find doing that leads to fields such as {{ content.field_image }} always returning true because of Drupal's rendering system. So, even if we don't have an image, we'll still have an image div, whereas doing an explicit check before we {% include %} our variable seems much safer.

That's it - PatternLab + Drupal integrated beautifully (I think) the "right" way (according to me - you might differ).

===

You can see a sample of this in action on this site's PatternLab.
Note - I call cards in my PatternLab 'tiles' and make them part of a wrapper component called 'Tiled Layout', which allows me lots of flexibility for cool layouts with little effort.

Sep 05 2018
Sep 05
The making of Acro Media’s website content creation framework


It’s common place for brands to create guides so that there is a constant standard to follow when working with the brand’s identity. These are generally called Style Guides. We have one ourselves that we use when designing internal documents and printed layouts. This is great when it comes to branding, but how do you go about maintaining a level of consistency for something larger, such as a website? We recently underwent a fundamental shift in the way we create our website content, and with it, the Live Website Component Guide was born.

The Content Type Crux

For those familiar with Drupal, creating something called a Content Type is a common way to go about setting up a type of content - think your standard web page, blog post, frequently asked questions, rich media slider, etc. That content type can then be used to generate the pages of your website.

This works well enough if your site doesn’t need to change, but our marketing team is constantly looking to adapt to new trends, change content layouts, and A/B test. A website should ideally be dynamic and quick to change, however, changes ended up taking a lot of time because they need to first be designed and then built. The standard Drupal content type is rigid and the layout is fixed, so if you want to change the layout, the change is going to cascade to any page that uses that Content Type. Because of this, we often needed to create a whole new Content Type, template and styling. Something that should be quick ends up taking weeks because our process just wasn’t efficient. And furthermore, the more code we introduced, the more difficult the site was to maintain.

Eureka! Paragraphs

In February of 2017, we had a “eureka” moment while attending the Pacific Northwest Drupal Summit in Vancouver, BC, Canada. Here we learned about a new (to us) content creation module called Paragraphs. I know many people have been using this module for a while now, but it was new to us and we immediately saw the potential. As stated on the Paragraphs module’s Drupal.org project page:

“Paragraphs is the new way of content creation! It allows you — Site Builders — to make things cleaner so that you can give more editing power to your end-users.”

And it DOES do that! Instead of thinking in ‘pages’ we can now think in ‘components’. The graphic below illustrates this. On the left, a representation of a standard Drupal page layout using a Content Type. On the right, the same page broken out into individual Paragraphs components.

Content Type vs Paragraphs

Drupal paragraph vs content type example

What this module allowed us to do is to remove the rigid structure of the Content Type and instead build out a set of individual, standalone components that can then be inserted into a page wherever we want. If we want to add a new component to a page, we just select the component from a list and place it on the page. To remove one, we just click a remove button. To change the order, all we need to do is drag and drop the component where we want it. If there is a component that we need, but doesn’t yet exist, we can now create just that one component. It makes things fast!

The content creation aspect is incredible dynamic and easy to use. The best part of all is that once the components are built, the only need for a developer is to create new components later on. The actual content creation can easily be done by anyone with a very small bit of training.

From a design point of view, our designers can now piece a layout together knowing exactly what components are available to them. They know that if we’re missing a component, they can come up with something new and it’s not going to take weeks to implement. Plus, we already loosely follow the Atomic Design methodology by Brad Frost, so the whole concept was easy for them to grasp and got them excited. In fact, our Creative Director jumped on this concept and we now include a full set of content creation Paragraphs components in every new project that we build.

live-component-guide_02
An example of how easily we can generate page layouts using component driven design.

Things get very easy from a code maintenance point of view too! We created each of our components to have a standalone template and styling. This means that things stay consistent throughout the site no matter how a page has been setup. If we need to make a visual change to a component, we make it once and the change cascades throughout the site. The code base is small and logical. Anyone new to the project can jump in and get up to speed quickly.

Our Live Website Component Guide

So, if you’ve read this far, I bet you want to see it in action? You’re in luck! I recorded a quick video that shows you how it works using our corporate website’s Live Website Component Guide. You can watch the video below or view the page in all its glory.

UPDATE: Part 2 of this post is now available showing a Drupal 8 live component guide. 

[embedded content]

Contact us and learn more about our custom ecommerce solutions

Sep 02 2018
Sep 02
Drupal Europe

In only 8 days Drupal Europe will be happening from September 10 to 14 in Darmstadt, Germany. Are you coming?

Throughout the last 12 months a lot of volunteers worked really hard to make this event happen. Starting with our decision and commitment at DrupalCon Vienna to organize Drupal Europe, followed by an extensive search for locations, numerous volunteers have been busy for a year. Reaching out to sponsors, structuring the program, organizing the Open Web Lounge, planning the venue spaces, answering all your emails, writing visa invitation letters, launching trainings, reviewing sessions and putting together the big schedule.

How it started in the community keynote photo by Amazee Labs

Drupal Europe hosts 162 hours of sessions, 9 in-depth workshops, 3 training courses, contribution every day but the biggest value of all is meeting everyone. This conference brings together CEOs, project managers, marketing professionals, and developers alike. It is both a technology conference and a family reunion for the Drupal community and that is why we organized it.

Drupal Europe is a unique possibility to meet your (international) colleagues and talk about what drives, connects and challenges our community. There is only one open source community where “you come for the code and stay for the community” is so deeply rooted. And Drupal Europe is also a great place to connect with other open source technologies. WordPress, Rocket.Chat, Typo3, Mautic, you name it! You may be surprised that there are more that connect us than what separates us.

Have a look at the diverse and interesting program.

Besides the sessions and BoFs we also plan our other traditional activities.

On Thursday evening we organise the exciting Trivia Night where you can win eternal fame with your team.

Contribution opportunities are open all week. On Monday and especially Friday, mentors will be around to help you get started contributing. Contribution is for everyone, all skill and energy levels are invited.

New this year at Drupal Europe is the first international Splash Awards! All golden and silver winners from local Splash Awards will compete for the European awards.

All together we think there are plenty of reasons why you should come to Darmstadt and participate at Drupal Europe.

To make our offer even better, if you buy a ticket before end of the late ticket deadline (today or tomorrow), you enter a raffle for a free hotel room for Sept 10–13 at Intercity Hotel Darmstadt! Use FLS-LPNLGS5DS84E4 to also get 100 EUR off the ticket price.

The hotel room raffle closes and online ticket sales will stop at end of Monday. You will only have a chance to buy a ticket onsite at Drupal Europe afterwards.

Grab this last chance to join us at Drupal Europe, book your travels and have a safe trip getting here.

See you in Darmstadt!

Image Darmstadium venue in Darmstadt, Germany
Aug 27 2018
Aug 27

Expo just got nominated for two prestigious publishing awards in Sweden, best magazine and best magazine website. The winners will be announced 7th of November 2018 in Stocholm where Ramsalt Lab will be present. We are very excited for this news and have decided to share with you the secrets behind building Expo.se on Drupal. So stay tuned for more the following days. 

Short about the Expo 

Expo is a Swedish anti-racist magazine started in 1995 by Stieg Larsson, also known as the author of the Millennium novel series, where the inspiration comes from Expo. Expo magazine is issued by the non-profit Expo Foundation. The magazine contains investigative journalism focused on nationalist, racist, anti-democratic, anti-semitic, and far-right movements and organisations. Expo became widely known in Sweden after 1996 following a string of threats and attacks directed against companies printing and selling the magazine, and organisations supporting it. The magazine is headquartered in Stockholm. More about Expo on Wikipedia

Have a look at the website on Expo.se

Aug 27 2018
Aug 27

This post is part 5 in the series “Hashing out a docker workflow”. I have resurrected this series from over a year ago, but if you want to checkout the previous posts, you can find the first post here. Although the beginning of this blog series pre-dates Docker Machine, Docker for Mac, or Docker for Window’s. The Docker concepts still apply, just not using it with Vagrant any more. Instead, check out the Docker Toolbox. There isn’t a need to use Vagrant any longer.

We are going to take the Drupal image that I created from my last post “Creating a deployable Docker image with Jenkins” and deploy it. You can find the image that we created last time up on Docker Hub, that is where we pushed the image last time. You have several options on how to deploy Docker images to production, whether that be manually, using a service like AWS ECS, or OpenShift, etc… Today, I’m going to walk you through a deployment process using Kubernetes also known as simply k8s.

Why use Kubernetes?

There are an abundance of options out there to deploy Docker containers to the cloud easily. Most of the options provide a nice UI with a form wizard that will take you through deploying your containers. So why use k8s? The biggest advantage in my opinion is that Kubernetes is agnostic of the cloud that you are deploying on. This means if/when you decide you no longer want to host your application on AWS, or whatever cloud you happen to be on, and instead want to move to Google Cloud or Azure, you can pick up your entire cluster configuration and move it very easily to another cloud provider.

Obviously there is the trade-off of needing to learn yet another technology (Kubernetes) to get your app deployed, but you also won’t have the vendor lock-in when it is time to move your application to a different cloud. Some of the other benefits to mention about K8s is the large community, all the add-ons, and the ability to have all of your cluster/deployment configuration in code. I don't want to turn this post into the benefits of Kubernetes over others, so lets jump into some hands-on and start setting things up.

Setup a local cluster.

Instead of spinning up servers in a cloud provider and paying for the cost of those servers while we explore k8s, we are going to setup a cluster locally and configure Kubernetes without paying a dime out of our pocket. Setting up a local cluster is super simple with a tool called Minikube. Head over to the Kubernetes website and get that installed. Once you have Minikube installed, boot it up by typing minkube start. You should see something similar to what is shown below:

$ minikube start
Starting local Kubernetes v1.10.0 cluster...
Starting VM...
Downloading Minikube ISO
 160.27 MB / 160.27 MB [============================================] 100.00% 0s
Getting VM IP address...
Moving files into cluster...
Downloading kubeadm v1.10.0
Downloading kubelet v1.10.0
Finished Downloading kubelet v1.10.0
Finished Downloading kubeadm v1.10.0
Setting up certs...
Connecting to cluster...
Setting up kubeconfig...
Starting cluster components...
Kubectl is now configured to use the cluster.
Loading cached images from config file.

This command setup a virtual machine on your computer, likely using Virtualbox. If you want to double check, pop open the Virtualbox UI to see a new VM created there. This virtual machine has loaded on it all the necessary components to run a Kubernetes cluster. In K8s speak, each virtual machine is called a node. If you want to log in to the node to explore a bit, type minikube ssh. Below I have ssh'd into the machine and ran docker ps. You’ll notice that this vm has quite a few Docker containers running to make this cluster.

 $ minikube ssh
                         _             _
            _         _ ( )           ( )
  ___ ___  (_)  ___  (_)| |/')  _   _ | |_      __
/' _ ` _ `\| |/' _ `\| || , <  ( ) ( )| '_`\  /'__`\
| ( ) ( ) || || ( ) || || |\`\ | (_) || |_) )(  ___/
(_) (_) (_)(_)(_) (_)(_)(_) (_)`\___/'(_,__/'`\____)

$ docker ps
CONTAINER ID        IMAGE                                      COMMAND                  CREATED             STATUS              PORTS               NAMES
aa766ccc69e2        k8s.gcr.io/k8s-dns-sidecar-amd64           "/sidecar --v=2 --lo…"   5 minutes ago       Up 5 minutes                            k8s_sidecar_kube-dns-86f4d74b45-kb2tz_kube-system_3a21f134-a637-11e8-894d-0800273ca679_0
6dc978b31b0d        k8s.gcr.io/k8s-dns-dnsmasq-nanny-amd64     "/dnsmasq-nanny -v=2…"   5 minutes ago       Up 5 minutes                            k8s_dnsmasq_kube-dns-86f4d74b45-kb2tz_kube-system_3a21f134-a637-11e8-894d-0800273ca679_0
0c08805e8068        k8s.gcr.io/kubernetes-dashboard-amd64      "/dashboard --insecu…"   5 minutes ago       Up 5 minutes                            k8s_kubernetes-dashboard_kubernetes-dashboard-5498ccf677-hvt4f_kube-system_3abef591-a637-11e8-894d-0800273ca679_0
f5d725b1c96a        gcr.io/k8s-minikube/storage-provisioner    "/storage-provisioner"   6 minutes ago       Up 6 minutes                            k8s_storage-provisioner_storage-provisioner_kube-system_3acd2f39-a637-11e8-894d-0800273ca679_0
3bab9f953f14        k8s.gcr.io/k8s-dns-kube-dns-amd64          "/kube-dns --domain=…"   6 minutes ago       Up 6 minutes                            k8s_kubedns_kube-dns-86f4d74b45-kb2tz_kube-system_3a21f134-a637-11e8-894d-0800273ca679_0
9b8306dbaab7        k8s.gcr.io/kube-proxy-amd64                "/usr/local/bin/kube…"   6 minutes ago       Up 6 minutes                            k8s_kube-proxy_kube-proxy-dwhn6_kube-system_3a0fa9b2-a637-11e8-894d-0800273ca679_0
5446ddd71cf5        k8s.gcr.io/pause-amd64:3.1                 "/pause"                 7 minutes ago       Up 7 minutes                            k8s_POD_storage-provisioner_kube-system_3acd2f39-a637-11e8-894d-0800273ca679_0
17907c340c66        k8s.gcr.io/pause-amd64:3.1                 "/pause"                 7 minutes ago       Up 7 minutes                            k8s_POD_kubernetes-dashboard-5498ccf677-hvt4f_kube-system_3abef591-a637-11e8-894d-0800273ca679_0
71ed3f405944        k8s.gcr.io/pause-amd64:3.1                 "/pause"                 7 minutes ago       Up 7 minutes                            k8s_POD_kube-dns-86f4d74b45-kb2tz_kube-system_3a21f134-a637-11e8-894d-0800273ca679_0
daf1cac5a9a5        k8s.gcr.io/pause-amd64:3.1                 "/pause"                 7 minutes ago       Up 7 minutes                            k8s_POD_kube-proxy-dwhn6_kube-system_3a0fa9b2-a637-11e8-894d-0800273ca679_0
9d00a680eac4        k8s.gcr.io/kube-scheduler-amd64            "kube-scheduler --ad…"   7 minutes ago       Up 7 minutes                            k8s_kube-scheduler_kube-scheduler-minikube_kube-system_31cf0ccbee286239d451edb6fb511513_0
4d545d0f4298        k8s.gcr.io/kube-apiserver-amd64            "kube-apiserver --ad…"   7 minutes ago       Up 7 minutes                            k8s_kube-apiserver_kube-apiserver-minikube_kube-system_2057c3a47cba59c001b9ca29375936fb_0
66589606f12d        k8s.gcr.io/kube-controller-manager-amd64   "kube-controller-man…"   8 minutes ago       Up 8 minutes                            k8s_kube-controller-manager_kube-controller-manager-minikube_kube-system_ee3fd35687a14a83a0373a2bd98be6c5_0
1054b57bf3bf        k8s.gcr.io/etcd-amd64                      "etcd --data-dir=/da…"   8 minutes ago       Up 8 minutes                            k8s_etcd_etcd-minikube_kube-system_a5f05205ed5e6b681272a52d0c8d887b_0
bb5a121078e8        k8s.gcr.io/kube-addon-manager              "/opt/kube-addons.sh"    9 minutes ago       Up 9 minutes                            k8s_kube-addon-manager_kube-addon-manager-minikube_kube-system_3afaf06535cc3b85be93c31632b765da_0
04e262a1f675        k8s.gcr.io/pause-amd64:3.1                 "/pause"                 9 minutes ago       Up 9 minutes                            k8s_POD_kube-apiserver-minikube_kube-system_2057c3a47cba59c001b9ca29375936fb_0
25a86a334555        k8s.gcr.io/pause-amd64:3.1                 "/pause"                 9 minutes ago       Up 9 minutes                            k8s_POD_kube-scheduler-minikube_kube-system_31cf0ccbee286239d451edb6fb511513_0
e1f0bd797091        k8s.gcr.io/pause-amd64:3.1                 "/pause"                 9 minutes ago       Up 9 minutes                            k8s_POD_kube-controller-manager-minikube_kube-system_ee3fd35687a14a83a0373a2bd98be6c5_0
0db163f8c68d        k8s.gcr.io/pause-amd64:3.1                 "/pause"                 9 minutes ago       Up 9 minutes                            k8s_POD_etcd-minikube_kube-system_a5f05205ed5e6b681272a52d0c8d887b_0
4badf1309a58        k8s.gcr.io/pause-amd64:3.1                 "/pause"                 9 minutes ago       Up 9 minutes                            k8s_POD_kube-addon-manager-minikube_kube-system_3afaf06535cc3b85be93c31632b765da_0

When you’re done snooping around the inside the node, log out of the session by typing Ctrl+D. This should take you back to a session on your local machine.

Interacting with the cluster

Kubernetes is managed via a REST API, however you will find yourself interacting with the cluster mainly with a CLI tool called kubectl. With kubectl, we will issue it commands and the tool will generate the necessary Create, Read, Update, and Delete requests for us, and execute those requests against the API. It’s time to install the CLI tool, go checkout the docs here to install on your OS.

Once you have the command line tool installed, it should be automatically configured to interface with the cluster that you just setup with minikube. To verify, run a command to see all of the nodes in the cluster kubectl get nodes.

$ kubectl get nodes
NAME       STATUS    ROLES     AGE       VERSION
minikube   Ready     master    6m        v1.10.0

We have one node in the cluster! Lets deploy our app using the Docker image that we created last time.

Writing Config Files

With the kubectl cli tool, you can define all of your Kubernetes objects directly, but I like to create config files that I can commit in a repository and mange changes as we expand the cluster. For this deployment, I’ll take you through creating 3 different K8s objects. We will explicitly create a Deployment object, which will implicitly create a Pod object, and we will create a Service object. For details on what these 3 objects are, check out the Kubernetes docs.

In a nutshell, a Pod is a wrapper around a Docker container, a Service is a way to expose a Pod, or several Pods, on a specific port to the outside world. Pods are only accessible inside the Kubernetes cluster, the only way to access any services in a Pod is to expose the Pod with a Service. A Deployment is an object that manages Pod’s, and ensures that Pod’s are healthy and are up. If you configure a deployment to have 2 replicas, then the deployment will ensure 2 Pods are always up, and if one crashes, Kubernetes will spin up another Pod to match the Deployment definition.

deployment.yml

Head over to the API reference and grab the example config file https://v1-10.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.10/#deployment-v1-apps. We will modify the config file from the docs to our needs. Change the template to look like below (I changed the image, app, and name properties in the yml below):

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  
  name: deployment-example
spec:
  
  replicas: 3
  template:
    metadata:
      labels:
        
        
        app: drupal
    spec:
      containers:
      - name: drupal
        
        image: tomfriedhof/docker_blog_post

Now it’s time to feed that config file into the Kubernetes API, we will use the CLI tool for this:

$ kubectl create -f deployment.yml

You can check the status of that deployment by asking the k8s for all Pod and Deployment objects:

$ kubectl get deploy,po

Once everything is up and running you should see something like this:

 $ kubectl get deploy,po
NAME                        DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deploy/deployment-example   3         3         3            3           3m

NAME                                    READY     STATUS    RESTARTS   AGE
po/deployment-example-fc5d69475-dfkx2   1/1       Running   0          3m
po/deployment-example-fc5d69475-t5w2j   1/1       Running   0          3m
po/deployment-example-fc5d69475-xw9m6   1/1       Running   0          3m

service.yml

We have no way of accessing any of those Pods in the deployment. We need to expose the Pods using a Kubernetes Service. To do this, grab the example file from the docs again and change it to the following: https://v1-10.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.10/#service-v1-core

kind: Service
apiVersion: v1
metadata:
  
  name: service-example
spec:
  ports:
    
    - name: http
      port: 80
      targetPort: 80
  selector:
    
    
    app: drupal
  
  
  
  type: LoadBalancer

Create this service object using the CLI tool again:

$ kubectl create -f service.yml

You can now ask Kubernetes to show you all 3 objects that you created by typing the following:

$ kubectl get deploy,po,svc
NAME                        DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deploy/deployment-example   3         3         3            3           7m

NAME                                    READY     STATUS    RESTARTS   AGE
po/deployment-example-fc5d69475-dfkx2   1/1       Running   0          7m
po/deployment-example-fc5d69475-t5w2j   1/1       Running   0          7m
po/deployment-example-fc5d69475-xw9m6   1/1       Running   0          7m

NAME                  TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
svc/kubernetes        ClusterIP      10.96.0.1       <none>        443/TCP        1h
svc/service-example   LoadBalancer   10.96.176.233   <pending>     80:31337/TCP   13s

You can see under the services at the bottom that port 31337 was mapped to port 80 on the Pods. Now if we hit any node in the cluster, in our case it's just the one VM, on port 31337 we should see the Drupal app that we built from the Docker image we created in the last post. Since we are using Minikube, there is a command to open a browser on the specific port of the service, type minikube service :

$ minikube service service-example

This should open up a browser window and you should see the Installation screen for Drupal. You have successfully deployed the Docker image that we created to a production-like environment.

What is next?

We have just barely scratched the surface of what is possible with Kubernetes. I showed you the bare minimum to get a Docker image deployed on Kubernetes. The next step is to deploy your cluster to an actual cloud provider. For further reading on how to do that, definitely check-out the KOPS project.

If you have any questions, feel free to leave a comment below. If you want to see a demo of everything that I wrote about on the ActiveLAMP YouTube channel, let us know in the comments as well.

Aug 23 2018
Aug 23

RESTful API endpoints in Drupal can allow you to expose the content of Drupal 7 website to other external resources. These endpoints can be used for fetching data with an AJAX call or simply a web service.

For making complex RESTful web services in Drupal 7, Services module is the best option to go for but if you looking for a simple endpoint in Drupal 7, which outputs JSON response, then going with a custom solution will be the best bet.

Create API endpoint with JSON response in Drupal 7

To create a simple API endpoint in Drupal to serve a JSON put you need to create a custom callback

/**
 * Implements hook_menu().
 */
function mymodule_menu() {
  $items['api/mymodule'] = array(
    'access callback' => true,
    'page callback' => 'mymodule_json',
    'delivery callback' => 'drupal_json_output',
  );

  return $items;
}

A noticeable point in this callback is the "delivery callback" argument which runs the output through drupal_json_output function.

/**
 * Callback function to return the data.
 */
function mymodule_json() {
  $data = ['cat', 'dog', 'mouse', 'elephant'];

  return $data;
}

Now if you navigate to the callback url 'api/mymodule' to see the output then you will see the following result

[
 "cat",
 "dog",
 "mouse",
 "elephant",
]

Now you can see that this output is exposed as a JSON and can be consumed by other platforms or an AJAX callback to consume data.

The same approach can be used to expose a node or a user as a JSON output.

Aug 23 2018
Aug 23
Drupal EuropeImage by LuckyStep @shutterstock
  • to revolutionize publishing, with a new rewarding model in an environment which can build trust and allows community governance.
  • to reshape open source communities, with a better engagement and rewarding system.
  • to free digital identity, thus killing the need of middlemen at the protocol layer.

Blockchain is an universal tool and can be applied in many different areas.

Communities, like the Drupal Community, can find new ways to flourish. Even larger and risky projects can be financed in new ways, with ICO (Initial Coin Offer). Taco Potze (Co-Founder Open Social) has a 10 year Drupal background and is an expert on Communities. He is working on blockchain technology to build a better engagement and rewarding systems for communities. Wouldn’t that be really nice for us?

See also Taco’s session: ICOs, a revolutionary way to raise money for your company

Publishing and its classic monetization model is challenged. Intermediates are about to disrupt the relationship between authors and publishers and their readers. This is based on a troublesome business model, with massive tracking and profile building, to turn our engagement in advertisement money. At the same time poor content and fake news has become a threat to our society. Gagik Yeghiazarian (CEO, Co-Founder Publiq) is looking for new ways to address these problems, with a non profit, distributed media platform based on blockchain.

See also Gagik’s session: Blockchain Distributed Media — A Future for good publishing

The Internet is broken and blockchain can fix it. The biggest promise with blockchain is to make middlemen obsolete, by creating trusted identities in an open protocol. This is to break the monopoly of the middlemen and to retain a free web. We recognize aribnb, amazon, ebay, netflix, itunes as middlemen. We understand, when we by or book, they get their share. With Google, Facebook and YouTube there are some other huge monopoly middlemen, they get their share based on our attention and personal data. They know how to transfer our attention into dollars, by selling it to advertisers. Ingo Rübe (CEO Bot Lab) is working on a protocol, which will allow people to gain control of their digital identity. It will be called KILT Protocol. (Ingo is well known in the Drupal Community and a Member of Drupal’s Advisory Board. As a former CTO of Burda he was the Initiator of the Drupal Thunder Distribution)

Our Panel will be moderated by Audra Martin Merrick, a board member of Drupal Association.

signed
Drupal Europe
Your Track Chairs

Aug 22 2018
hw
Aug 22

This month’s Drupal meetup was held at 91Springboard in JP Nagar. We held this meetup early instead of our usual last Saturday of the month due to a long weekend.

Drupal meetup

It was a lazy rainy Saturday morning and most of the people made it on time. We started the meetup at 10:30 AM as planned. We started with introductions and a catch-up on news and upcoming events in the Drupal world.

Starting off today's #Drupal meetup with @devtaher @drupal_bug @BangaloreDrupal pic.twitter.com/MF8LoIGGjh

— hussainweb (@hussainweb) August 18, 2018

Umami

Our first lightning talk was by Malabya on Umami, which is an effort for Drupal’s out of the box initiative. Malabya described why Umami was necessary and what are the problems it solves. The slides are available here.

.@malavya88 talks about why first impressions are important and how #Umami helps #Drupal do a better job there. @drupal_bug pic.twitter.com/NPI5rUnMRC

— hussainweb (@hussainweb) August 18, 2018

BLT

This was followed by Srikanth and Tejasvi giving an introduction to BLT. They introduced why something like BLT is required for developing Drupal sites in a moderately large team. They also described the structure of a BLT based setup briefly and answered questions related to BLT.

Introduction to BLT by @srikantmatihali at #Drupal meet-up in Bangalore. @drupal_bug pic.twitter.com/KAaqnR8vu0

— hussainweb (@hussainweb) August 18, 2018

Tejasvi talks about what a BLT setup looks like and the code structure at @drupal_bug meetup. pic.twitter.com/nO96l7xdGt

— hussainweb (@hussainweb) August 18, 2018

Bring your questions

We tried something new this meetup based on feedback. Many people brought in various questions they face when using Drupal. Some of the questions we addressed were:

  • What is config split? How do we use it?
  • Are there any best practices for Drupal multi-site?
  • Issues with layout plugin module in early versions of Drupal 8 and how to upgrade
  • Using paragraphs
  • and more which I can’t remember now…

QnA session at @BangaloreDrupal meetup. @hussainweb talking about Config Split module. #Drupal pic.twitter.com/SqyMpOrVbP

— Malabya (@malavya88) August 18, 2018

Webpack with Drupal themes

We had a short break after the session after which we started the last session of the day on using Webpack with Drupal themes by myself. I started the topic with a discussion on modern JavaScript including newer ES6 syntax and specifically, writing modular JavaScript. I then introduced the template I have put up to use Webpack along with Drupal’s bootstrap theme’s SASS starterkit.

.@hussainweb is talking about javascript and webpack @BangaloreDrupal #meetup pic.twitter.com/QKcUpiWdQi

— Taher Jodhpurwala (@devtaher) August 18, 2018

We ended the day with crediting all the speakers and organisers of the meetup on the issue we have for our meetup. This was followed by our group photo and closing.

Photos

All photos from the meetup are below.

Drupal Meetup Bangalore - August 2018

Aug 21 2018
Aug 21

I’ve been experimenting with moving some code to Drupal 8, and I’m quite intrigued by a different way that I’ve tried to structure it - using event subscribers, building on some of the takeaways from Drupal Dev Days.

Here is how this module is currently structured:

Note that there is no opdavies_blog.module file, and rather than calling actions from within a hook like opdavies_blog_entity_update(), each action becomes it’s own event subscriber class.

This means that there are no long hook_entity_update functions, and instead there are descriptive, readable event subscriber class names, simpler action code that is responsibile only for performing one task, and you’re able to inject and autowire dependencies into the event subscriber classes as services - making it easier and cleaner to use dependency injection, and simpler write tests to mock dependencies when needed.

The additional events are provided by the Hook Event Dispatcher module.

Code

opdavies_blog.services.yml:

services:
  Drupal\opdavies_blog\EventSubscriber\PostToMedium:
    autowire: true
    tags:
      - { name: event_subscriber }

  Drupal\opdavies_blog\EventSubscriber\SendTweet:
    autowire: true
    tags:
      - { name: event_subscriber }

Adding autowire: true is not required for the event subscriber to work. I’m using it to automatically inject any dependencies into the class rather than specifying them separately as arguments.

src/EventSubscriber/SendTweet.php:

namespace Drupal\opdavies_blog\EventSubscriber;

use Drupal\hook_event_dispatcher\Event\Entity\EntityUpdateEvent;
use Drupal\hook_event_dispatcher\HookEventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class SendTweet implements EventSubscriberInterface {

  ...

  public static function getSubscribedEvents() {
    return [
      HookEventDispatcherInterface::ENTITY_UPDATE => 'sendTweet',
    ];
  }

  public function sendTweet(EntityUpdateEvent $event) {
    // Perform checks and send the tweet.
  }

}
Aug 20 2018
Aug 20

Helping content creators make data-driven decisions with custom data dashboards

Greg Desrosiers

Our analytics dashboards help Mass.gov content authors make data-driven decisions to improve their content. All content has a purpose, and these tools help make sure each page on Mass.gov fulfills its purpose.

Before the dashboards were developed, performance data was scattered among multiple tools and databases, including Google Analytics, Siteimprove, and Superset. These required additional logins, permissions, and advanced understanding of how to interpret what you were seeing. Our dashboards take all of this data and compile it into something that’s focused and easy to understand.

We made the decision to embed dashboards directly into our content management system (CMS), so authors can simply click a tab when they’re editing content.

GIF showing how a content author navigates to the analytics dashboard in the Mass.gov CMS.

The content performance team spent more than 8 months diving into web data and analytics to develop and test data-driven indicators. Over the testing period, we looked at a dozen different indicators, from pageviews and exit rates to scroll-depth and reading grade levels. We tested as many potential indicators as we could to see what was most useful. Fortunately, our data team helped us content folks through the process and provided valuable insight.

Love data? Check out our 2017 data and machine learning recap.

We chose a sample set of more than 100 of the most visited pages on Mass.gov. We made predictions about what certain indicators said about performance, and then made content changes to see how it impacted data related to each indicator.

We reached out to 5 partner agencies to help us validate the indicators we thought would be effective. These partners worked to implement our suggestions and we monitored how these changes affected the indicators. This led us to discover the nuances of creating a custom, yet scalable, scoring system.

Line chart showing test results validating user feedback data as a performance indicator.

For example, we learned that a number of indicators we were testing behaved differently depending on the type of page we were analyzing. It’s easy to tell if somebody completed the desired action on a transactional page by tracking their click to an off-site application. It’s much more difficult to know if a user got the information they were looking for when there’s no action to take. This is why we’re planning to continually explore, iterate on, and test indicators until we find the right recipe.

Using the strategies developed with our partners, we watched, and over time, saw the metrics move. At that point, we knew we had a formula that would work.

We rolled indicators up into 4 simple categories:

  • Findability — Is it easy for users to find a page?
  • Outcomes — If the page is transactional, are users taking the intended action? If the page is focused on directing users to other pages, are they following the right links?
  • Content quality — Does the page have any broken links? Is the content written at an appropriate reading level?
  • User satisfaction — How many people didn’t find what they were looking for?
Screenshot of dashboard results as they appear in the Mass.gov CMS.

Each category receives a score on a scale of 0–4. These scores are then averaged to produce an overall score. Scoring a 4 means a page is checking all the boxes and performing as expected, while a 0 means there are some improvements to be made to increase the page’s overall performance.

All dashboards include general recommendations on how authors can improve pages by category. If these suggestions aren’t enough to produce the boost they were looking for, authors can meet with a content strategist from Digital Services to dive deeper into their content and create a more nuanced strategy.

GIF showing how a user navigates to the “Improve Your Content” tab in a Mass.gov analytics dashboard.

We realize we can’t totally measure everything through quantitative data, so these scores aren’t the be-all, end-all when it comes to measuring content performance. We’re a long way off from automating the work a good editor or content strategist can do.

Also, it’s important to note these dashboards are still in the beta phase. We’re fortunate to work with partner organizations who understand the bumps in the proverbial development road. There are bugs to work out and usability enhancements to make. As we learn more, we’ll continue to refine them. We plan to add dashboards to more content types each quarter, eventually offering a dashboard and specific recommendations for the 20+ content types in our CMS.

Aug 14 2018
Aug 14

Drupal Europe: Publishing + Media Special Focus

Drupal Europe

What industries come to mind when you hear blockchain? Banking? Trading? Healthcare? How about publishing? At Drupal Europe publishers will gain insights into the potential blockchain technology offers and learn how they can benefit. Meet Gagik Yeghiazarian, founder of the nonprofit foundation Publiq, and learn how he wants to fight fake news and build a censorship-resistant platform — using blockchain.

The publishing world is changing. Publishers no longer solely control media distribution. Big players like Facebook and Google are middlemen between the publishers and their readers, and technology built to entice publishers — Google’s AMP (Accelerated Mobile Pages) and Facebook Instant Articles — has strengthened social platforms as distribution channels. Additionally, publishers have lost money making classifieds business as employment and real estate markets create their own platforms and portals to reach the audience.

Photo by Ian Schneider on Unsplash

As a result of these developments, publishers are losing direct relationships with their readers as well as critical advertising which traditionally supported the editorial and operational costs. The platforms act as middlemen, using the content of the publishers for collecting data and selling them to advertisers. The publishers are left out in the cold.

Critically, publishers are also facing a crisis of confidence. As social platforms are used to spread fake news and poor content, mistrust in journalism grows.

The nonprofit foundation Publiq wants to face these challenges with a blockchain-powered infrastructure. It aims at removing unnecessary intermediaries from the equation and helping to create an independent, censorship-free environment. Gagik Yeghiazarian, CEO and Co-Founder of Publiq, is convinced: “Blockchain infrastructure allows content creators, readers and other participants to build a trusted relationship.”

You can learn more about Publiq and its blockchain infrastructure at Drupal Europe in Darmstadt: Gagik Yeghiazarian’s session “Blockchain Distributed Media — A Future for good publishing” will give you a glimpse into this new technology and a real-world application of it.

While you’re at Drupal Europe, be sure to check out the exciting blockchain panel discussion where Gagik, Ingo Rübe of Botlabs, and Taco Potze of Open Social, will share insights and use cases for blockchain technology. Don’t miss this!

Drupal Europe
Publishing & Media — Track Chairs

Aug 13 2018
Aug 13

Drupal Europe: Publishing + Media Special Focus

Drupal Europe

Drupal Europe offers up a plethora of cases and solutions to help you with your DAM integration.

Multichannel publishing by Oleksiy Mark on Shutterstock

With so much to organize and store, publishers typically use Digital Asset Management Systems (DAM) to manage their assets. Add multiple channels to the mix and you have big operational hurdles. Thanks to the Media Initiative, Drupal now has a well-defined ecosystem for media management and its architecture is designed to play well with all kinds of media, media management systems, and web services that support them. The system is highly adaptable — the media management documentation outlines 15 modules shaping Drupal’s new ecosystem for media assets.

The Drupal Europe program offers several sessions to help you learn more about solutions building on this foundation. Case studies of demanding media management projects around the publishing industry include:

Drupal Europe
Publishing & Media — Track Chairs

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