February 24, 2018
Many of my longest friendships were born in the Drupal community. I’ve been attending DrupalCons, Drupal Camps, and other events since DrupalCon Barcelona 2007 and centered most of my professional life around contributing to the project as a developer and a teacher. In 2010 that included serving as a mentor in the Google Summer of Code program for a new contributor who wanted to work on Drupal Commerce’s affiliate module, bojanz.
Bojan Živanović and I got to know each other that summer through many IRC chats and coding sessions. After he completed his project successfully, we met at DrupalCon Copenhagen and celebrated at McDonald's. I sure knew how to treat a friend!
I later convinced him to join Commerce Guys’s development team based in Paris. He served on our client services team before diving head first into Commerce Kickstart 2.x development and then creating a whole suite of modules to support usage based billing for subscription services like Platform.sh.
Around DrupalCon Austin in 2014, it appeared Bojan's mission with Commerce Guys might be complete. However, I saw an opportunity for him to develop further as a leader in our company and community. I was already busy leading client services with our U.S. team and then with acquiring and refocusing Commerce Guys around Drupal Commerce. It made perfect sense to me to appoint him to be project lead for Commerce 2.x.
That decision has served Commerce Guys and Drupal Commerce well over the last several years. Bojan brought renewed vigor to the project and discipline around competitive research and automated test coverage that far exceeded my own. He's also proven to be an able mentor in his own right, helping dozens of contributors and whole agency teams learn Drupal 8 development in general and Commerce 2.x development in particular.
Today is Bojan's birthday, and reflecting on our almost 8 years of friendship has obviously made me sentimental. At our first meeting in Copenhagen, my daughter Éowyn was just taking her first steps and wouldn't ever remember meeting Bojan. Today she sees him regularly during company Zoom calls and his occasional visit to our home in Greenville, SC. He's not just some random person from daddy's work, he's Uncle Bojan and a trusted friend.
We're all wishing you a happy birthday, Bojan, and we're grateful for your years of contribution and leadership in our midst. I'll have to hit the 2.x queue this evening to send you a birthday present disguised as a patch.
July 20, 2017
Last weekend I had the pleasure of attending Drupal Camp Asheville 2017 ('twas my fourth year in a row : ). I absolutely love this event and encourage you to consider putting it on your list of Drupal events to hit next year. The Asheville area is a beautiful (and delicious) place to spend the weekend, but the bigger draw for me is the people involved:
Drupal Camp Asheville is always well organized (props to April Sides et al; seriously, it's in the best of the best small conferences for venue, amenities, and content) and attended by a solid blend of seasoned Drupal users / contributors and newcomers. I live only an hour away, so I get to interact with my Drupal friends from Blue Oak Interactive, New Valley Media, and Kanopi on occasion, but then on Camp weekend I also get to see a regular mix of folks from Mediacurrent, Code Journeymen, Lullabot, Palantir, FFW, CivicActions, end users like NOAA, and more.
This year we got to hear from Adam Bergstein as the keynote speaker. Unfortunately, that "we" didn't include me at first, as I managed to roll up right after Adam spoke ... but his keynote is on YouTube already thanks to Kevin Thull! I encourage you to give it a listen to hear how Adam's experience learning to work against his own "winning strategy" as a developer (that of a honey badger ; ) helped him gain empathy for his fellow team members and find purpose in collaborative problem solving to make the world a better place.
I gave a presentation of Drupal Commerce 2.x focusing on how we've improved the out of the box experience since Commerce 1.x. This was fun to deliver, because we really have added quite a bit more functionality along with a better customer experience in the core of Commerce 2.x itself. These improvements continued all the way up to our first release candidate tagged earlier this month, which included new promotions, coupons, and payment capabilities.
Many folks were surprised by how far along Commerce 2.x is, but now that Bojan has decompressed from the RC1 sprint, I expect we'll start to share more about the new goodies on the Drupal Commerce blog. (If you're so inclined, you can subscribe to our newsletter to get bi-weekly news / updates as well.)
Lastly, I loved just hanging out and catching up with friends at the venue and at the afterparty. I played several rounds of a very fun competitive card game in development by Ken Rickard (follow him to find out when his Kickstarter launches!). I also enjoyed several rounds of pool with other Drupallers in the evening and closed out the night with cocktails at Imperial Life, one of my favorite cocktail bars in Asheville. I treasure these kinds of social interactions with people I otherwise only see as usernames and Twitter handles online.
Can't wait to do it again next year!
July 3, 2015
A friend of mine is a DUI lawyer who uses a Drupal site for content marketing and lead generation, managed by my friends at EverConvert. They quickly identified the weekend as the best time for the website to directly appeal to visitors who found the site after a Friday or Saturday night arrest. In addition to live chat, they decided to alter the site's appearance by publishing content only during these times that contains large, direct calls to action.
Fortunately, Drupal provides the tools to build this with nary a line of code.
Introducing the Rules Scheduler
The primary module you'll use to create something similar is Rules with its Rules Scheduler sub-module. Most purpose built content scheduling modules that I've seen allow you to set absolute dates for pieces of content to be published or unpublished. However, you wouldn't want to have to enter in every single weekend date to get that content published at the right times. Fortunately, Rules Scheduler allows you to schedule arbitrary actions using relative date strings (in addition to any other format supported by strtotime()).
Before we dive into the configuration I proposed to them, you should understand that the Rules module basically adds a GUI based scripting language to the back end of Drupal. In addition to configuring actions to be performed after certain events when a set of conditions are met, you can create Rules components that are essentially subroutines that can be directly invoked by Rules (or code) without being triggered by events.
To setup a Rules Scheduler based content publishing schedule, you have to create two Rules components: one will publish the piece of content and schedule it to be unpublished on a relative date (i.e. "next Monday"), while the other will unpublish the piece of content and schedule it to be published on a relative date (i.e. "next Friday"). Another Rule will need to react to content being created or updated to initiate the publishing schedule.
Using Fields to Avoid "Magic" Behavior
One of the keys to building a maintainable Drupal site (or module) is to ensure that every "automated" action is explicitly enabled. In the early days of Drupal Commerce development, I adopted an approach to some module behaviors where a feature just automatically worked if certain conditions were met (e.g. the representation of attribute fields on Add to Cart forms). "Neat!" I thought, until I realized that it was difficult to document and even more difficult to ensure users knew to look for said documentation. Much better to include explicit user interface components to enable functionality.
In the case of a scheduling system, you wouldn't want to build the site to just automatically enter every piece of content, or even every piece of content of a certain type, into the publishing pattern. Your client, a.k.a. the end user, really expects (and needs) to see an indicator on a form that submitting it will lead to a desired outcome.
For my friend's site, my recommendation was simply to add a Boolean field using a checkbox widget to the relevant content type reading, "Schedule this content to be published on Fridays and unpublished on Mondays." If the site required more publishing patterns than just the weekend pattern, I would've used a List (text) field with radio buttons or a select list identifying the different available publishing patterns.
Building the Scheduling System in Rules
Working with Rules is fairly simple if you can write out long hand what it is you're trying to accomplish. Considering all of our constraints here, we need a set of rules that accomplishes the following:
- When a call to action is created or saved with the scheduling checkbox checked, delete any scheduled tasks related to the content. (This is possible because Rules Scheduler lets you assign an identifier to scheduled rules, so identifying our scheduled tasks with the node ID will allow us to delete the tasks when needed in the future.)
- If the call to action that was just saved isn't published, schedule it to be published next Friday.
- If the call to action that was just saved is published, schedule it to be unpublished next Monday.
- When a call to action is automatically published on Friday, schedule it to be unpublished next Monday.
- When a call to action is automatically unpublished on Monday, schedule it to be published next Friday.
The first three items will be accomplished through a single rule reacting to two events, "After saving new content of type Call to action" and "After updating existing content of type Call to action." The rule will first delete any scheduled task for the piece of content and then it will invoke two rules components that will schedule the appropriate task based on whether or not the call to action was published.
The final two items will be accomplished through rules components that perform the necessary action and then schedule the appropriate task. As mentioned above, we'll use relative time strings ("next monday" and "next friday") and choose task identifiers that include the call to action node IDs ("unpublish-call-to-action-[node:nid]" and [publish-call-to-action-[node:nid]" respectively).
Give it a whirl!
It only took me about 10 minutes to create and test the rules based on the specification above, but if you aren't familiar with the Rules UI, it could take much longer. I believe Rules is worth learning (we built Drupal Commerce around it, after all), but there's something to be said for ready made examples.
I've attached to this post a Features export of the content type and related rules configurations for you to try on your own site. Give Scheduled Calls to Action a whirl and let me know how it works for you in the comments!
(Note: to see the rules configurations and scheduled tasks, enable the Rules UI, Views, and Views UI modules if they aren't already enabled on your site.)
January 31, 2015
When I first wrote Ubercart's Cart module, we knew we were going to support both anonymous and authenticated shopping carts and checkout. The decision came at a time when there wasn't consensus around the impact of forced login on conversions, but we knew we wanted it to be optional if at all possible. Additionally, for authenticated users, we wanted to preserve items in their shopping carts so they would see the same items when logging in from multiple devices or across multiple sessions.
This resulted in a small conflict that we had to figure out how to deal with: users could have items in their authenticated shopping carts but browse the site anonymously, create a new shopping cart, and then log in. What should happen to the items in their authenticated carts vs. the items in their anonymous carts?
There are three basic resolutions: combine the shopping carts together so the user still has a single shopping cart, remove the items from the previous session and leave it up to the customer to find them again if desired, or retain the old shopping cart but ignore it until the customer has completed checkout for the current cart. In Ubercart, I chose to combine the items, but in Drupal Commerce I changed course to retain the old cart but, from the customer's point of view, treat that anonymously created cart as the current cart after login.
We got some push back for this decision, but ultimately I didn't change the default functionality of Drupal Commerce. We just made sure there was an appropriate hook (hook_commerce_cart_order_convert()) so developers could alter this behavior on a site-by-site basis as need be.
From the merchant's standpoint, the thinking behind combining carts goes that you don't want customers to forget they intended to purchase those products in the past. However, from the customer's standpoint, suddenly having additional items in the cart after logging in during the checkout process is quite jarring.
In fact, I've been bitten by this behavior when shopping online at Barnes & Noble. Weeks prior to placing an order, I had put a Wheel of Time novel in my shopping cart but eventually bought the book in store. When I came back to the site to purchase a gift for my wife, I used a login button on the checkout form to quickly reuse my previous addresses and payment details. Unbeknownst to me, the website combined my old shopping cart with my current one such that my "quick checkout" experience made me accidentally order a book I already owned! I then had to spend 30 minutes with customer service canceling the order and placing it afresh just for the book I actually wanted.
That experience confirmed in my mind we made the correct decision not to combine carts automatically. As eCommerce framework developers, we have no clue where a developer might like to integrate login during the checkout process. Best to let them decide if it's safe to do something with those previous cart items instead of silently making the decision for them.
That said, I believe we can improve the experience even further. Right now, Drupal Commerce retains the old shopping cart order, and after the customer completes checkout they'll see the previous shopping cart as their current cart. This can be confusing as well!
My ideal situation would likely be a user interface component on the shopping cart page where customers can see items they had added to their carts in previous sessions, giving them the option to add those products to their current carts. If they decide not to, I don't see any harm in then just deleting those historical carts and moving on.
There's always room for improvement.
Photo credit: alphageek