Web Wash: Hide Block if no Results are Returned using Views in Drupal

Planet Drupal - Tue, 2021/05/18 - 12:31pm

Creating a block using views is pretty straightforward. You could create a block to display a list of published articles or ones that have been promoted to the front page. Then you can add that block into any theme region.

But you may encounter a situation where you no longer have any articles which are published and then you end up with an empty block.

Views comes with a feature that allows you to hide a block if no results are returned and this is what will be covered in this tutorial.


Golems GABB: Use heatmap analysis to heat up your e-commerce conversions

Planet Drupal - Tue, 2021/05/18 - 12:25pm
Use heatmap analysis to heat up your e-commerce conversions Editor Tue, 05/18/2021 - 13:25

For anyone who wants to boost their e-commerce store sales, it’s incredibly useful to know all steps of user interaction with the website. However, without the right analytics tools, this may look like playing guessing games. For example, remember the “hot and cold” game where the chosen player has to figure out some mystery action and the crowd shouts “hot!” or “cold!” depending on the player’s right or wrong guesses?

No guessing games are needed if you are using a hot digital technique like heatmaps! With heatmap analysis, you always know for sure what your customers are doing on your website, and this information is visualized in the most convenient way.

Discover more about what heatmaps are, what data they provide, what insights you can get from heatmap analysis to improve your e-commerce conversions, and what heatmap analysis tools are out there.


Morpht: Makers, takers and altruism

Planet Drupal - Tue, 2021/05/18 - 11:54am
A look at the concepts of makers and takers and how an understanding of altruism can help open source projects such as Drupal adapt to the future.

PreviousNext: Introducing the Filter Format Audit module

Planet Drupal - Tue, 2021/05/18 - 12:14am

Securing filter formats is one of the most important tasks when setting up a new site.

But sometimes when you inherit a site you find it wasn't done securely, or perhaps over time the format has gotten a bit lax and you want to make changes.

The Filter Format Audit module makes this task easy.


by lee.rowlands / 18 May 2021

When you've got a lot of content, knowing whether the changes you want to make to your filter formats are going to cause any regressions in existing content has always been a bit of a 'try it and see approach'.

Now with the Filter Format Audit module, you can take out the guesswork and make sure your filter formats are secure enough to prevent attacks like Cross-site scripting, but permissive enough that you don't end up breaking existing content.


Filter Format Audit lets you analyse your content to find where an input filter may be stripping out tags or attributes.

Use cases include:

  • When you've changed your filter formats over time and need to identify existing content that is being impacted by the changes
  • You've migrated content from another system and need to identify when tags/attributes are being stripped
  • You had a lax filter format, and now need to make it secure
  1. Download and install the module like normal
  2. Visit admin/content/filter-format-audit and hit 'run analysis'
  3. Let it do its thing ⏳
  4. Review the results and edit your formats/content as required
  5. Rinse and repeat from step 2 onwards until you're happy ♻️
How it works

The module works by querying all fields that contain formatted text (e.g Text (Formatted), Text (Formatted, With Summary) where the value uses a filter-format that has the HTML Filter turned on.

It then applies the format to each field item via a batch callback

It keeps track of which tags and attributes were stripped and stores the result, as well as a link to the piece of content in a new Analysis Result content entity. Once analysis is done, the report shows a list of items where tags or attributes were stripped.


The module also provides some handy summary views of stripped tags and attributes, sorted by count. This is ideal for finding the low-hanging fruit.

The analysis report provides handy 'edit content' links to allow you to quickly edit content that needs updating. This is smart enough to know context of the edit. So for example if the text content lives in a paragraph, or an inline block via Layout Builder, the edit link takes you to the host entity. It can even work out if the content is in a paragraph, which is on an inline block, which is placed in Layout Builder. All of this is done cleanly via Entity-type handlers, so if your custom entity-type has unique requirements you should be able to add support.

Future steps

At present the module uses the currently configured filter-format for each piece of content, but we have plans to add support for simulating a format change.

See the issue queue for our future plans, and feel free to open an issue if you encounter any problems or have ideas for features.

Thank you to the NSW Department of Customer Service for sponsoring this work.

Tagged Filter format, XSS, Drupal Security

Jacob Rockowitz: To all the backers of the Webform module's Open Collective, THANK YOU!

Planet Drupal - Mon, 2021/05/17 - 5:43pm

In January 2019, the Webform module for Drupal joined Open Collective. In a recent blog post, I shared my thoughts around sustainability, why I felt starting an Open Collective was important, and my hopes on how to spend the collected fund. For DrupalCon Seattle, we were able to raise money to pay for a Webform logo and tee-shirts, but I've struggled to decide how to spend the funds. I felt guilty about using funds to pay for my time, mainly because my day job was partially sponsoring/supporting my contribution to the Drupal community.

After paying for logo design and tee-shirts, I decided to help support the Drupal Association in their time of need and donate $4000 of the Webform module's Open Collective funds to the DrupalCares campaign.

Many things have changed during the pandemic. As many people know, my day job is no longer using Drupal. This change led me to question if and how I should continue to maintain the Webform module. My struggle with this question made my commitment to Drupal wane and I started to burn out, which led me to write about my predicament. The Drupal community was extremely empathetic, both publicly and privately, to my situation. With every blog post and change to the Webform module's project page, issue queue, and UI, more people started to...Read More


clemens-tolboom pushed to feature/add-log-info in clemens-tolboom/geodot-plugin

On github - Fri, 2021/05/14 - 9:33pm
clemens-tolboom pushed to feature/add-log-info in clemens-tolboom/geodot-plugin May 14, 2021 2 commits to feature/add-log-info

Mediacurrent: Adding OOP to Your Drupal 7 Refactor

Planet Drupal - Fri, 2021/05/14 - 3:00pm

When you are used to working in Drupal 8 and beyond, having to make changes to custom Drupal 7 code can be unpleasant. If you’re lucky, the code was well written and maintained and you can make your changes without feeling like you’re adding to a house of cards. Too often, though, the code is in a state where it could really use refactoring.

One of the many nice changes Drupal 8 brought about was changing most of the code over to object-oriented programming, or OOP. There are many benefits to this and one of them is that it organizes most of the code into classes. In Drupal 7, all that code was dumped into the .module files because that’s how things were done back then. But it doesn’t need to be that way.

Why Add OOP?

Drupal 7 may not have the OOP framework that Drupal 8 built, and you still need the hooks, but there are benefits to adding OOP to your refactor:

  • Upgrades - It gets your custom code closer to Drupal 8/9. If there is any thought of an upgrade in the site’s future, having the custom modules as close to Drupal 8/9 as possible will make that upgrade easier.
  • Clean Code - It makes your Drupal 7 code cleaner and easier to maintain. Even if an upgrade is a long way off, having clean and organized code is much easier to maintain and reduces the chance of knocking down that house of cards when you need to make a change.
  • Easier Refactoring - It makes refactoring easier because you can work on a copy of the code that is moved to a new place rather than trying to do everything in place. As you move and refactor each function, you can easily see what code is refactored and what is still the old code.
  • Testing - It makes it easier to test your refactored code. There’s more on that in the “Testing the refactoring” section below. If you are following along with this blog post and making changes to your module as you go, you’ll want to read that section before you start.
An Example Module

For the purpose of examples, let’s say we are refactoring a custom module called XYZ Events. Our site is for company XYZ and this module is where we stored all the custom functionality related to our events.

This module has custom menu items, blocks, and a bunch of miscellaneous helper functions all sitting in the .module file and we want to clean that up.

I’ll reference this fictional module to provide examples along the way.

Make class loading simple

To make using classes much easier, start with a very handy contributed module: X Autoload. X Autoload lets you make use of the automagic class loading that Drupal 8 has just by putting your classes in the proper directories and setting the namespace. With that in place, you are ready to start moving your code into classes.

Whenever you add a new class, be sure to clear the cache before you try to use it.

Services for all the miscellaneous functions

While hooks and a few other things need to stay in the .module file, chances are there are a lot of miscellaneous functions that can be organized into one or more service classes. These won’t be true D8/9 services with the service.yml and all of that but they serve the same purpose. In theory, you could write your own service container if you wanted to push this even further but even just regular classes help.

Make the events service:

  • Add the directory “src” to the root of the xyz_events directory.
  • In there, add EventsService.php. It’s not required to have “Service” in the name but it helps make the purpose clear.
  • The basic outline of the class looks like this:
<?php namespace Drupal\xyz_events; /** * Utility functions related to events. */ class EventsService { }

Move the code:

For each of the non-hook, non-callback functions in the .module file (and .inc files), copy it into the appropriate class. Some functions might make more sense in a site-wide utility class if they aren’t directly related to the module’s “theme”. Once it’s in its new home, do the cleanup and refactoring:

  • Change the function names to camel case and remove the module name (ie: xyz_events_get_event_list() becomes getEventList())
  • Add the public, protected, or private designation to the front. Since these used to be in a .module file, most of them are likely to be public. But, if any functions are only used by other functions that are now in the same class, those could be changed to protected or private.
  • Now is a good time to clean up any coding standards issues. I like to use Drupal 8/9’s standards if I know I’m on a PHP version that supports it such as using short array syntax. This gets it as close to D8/9 as possible.
  • Do whatever refactoring is needed to make the code easier to follow, improve performance, fix bugs, etc.

Update calls:

Using grep, check your whole site to find out all the places where that function was being called. For any place it’s being called that isn’t already in the class, change the call from the old function to the new method:

  • Add $events_service = new EventsService(); if that isn’t already in scope.
  • Change xyz_events_get_event_list() to $events_service->getEventList()

If the call is within the class already, change it to use “$this” instead of the service reference variable:

  • xyz_events_get_event_list() to $this->getEventList()

You now have all the miscellaneous custom code in a service (or multiple services if it makes sense to divvy them up). When moving to Drupal 8/9, all that’s needed is to update the class so that it’s a proper service container service and then change the calls to go through the container rather than using “new SomeClass()”.

Block classes

Drupal 8 introduced blocks as classes which is much cleaner than the old style that used multiple hooks. If you have multiple custom blocks each with a chunk of code, hook_block_view() can get quite long and hard to follow. While the hooks themselves are still needed, the actual code can be split off into classes. hook_block_info() stays the same but hook_block_view() becomes much simpler. 

  • If you haven’t already, add a directory “src” at the root of the module directory.
  • In “src” add a directory structure “Plugin/Block”.
  • For each block in hook_block_view():
    • Add a file in “Block” that is BlockNameBlock.php. Like services, the “Block” at the end isn’t required but makes it clearer what the class does. For our example module, we end up with UpcomingEventsBlock.php and FeaturedEventsBlock.php.
    • Take all the code for generating that block out of the hook and put it in the class.
    • Replace the content in the hook with a call to the class.
  • If your blocks have a lot of similar functionality, you can take advantage of inheritance and move the common functionality into a base block. In our case, since both blocks are listing events, we add EventListingBlockBase.php.

In the .module file we have:

/** * Implements hook_block_info(). */ function xyz_events_block_info() { $blocks = []; $blocks['upcoming'] = [ 'info' => t('Show upcoming events'), ]; $blocks['featured'] = [ 'info' => t('Show featured events'), ]; return $blocks; } /** * Implements hook_block_view(). */ function xyz_events_block_view($delta = '') { $block = []; switch ($delta) { case 'upcoming': $block_object = new UpcomingEventsBlock(); $block = $block_object->build(); break; case 'featured': $block_object = new FeaturedEventsBlock(); $block = $block_object->build(); break; } return $block; }

And then our blocks:


<?php namespace Drupal\xyz_events\Plugin\Block; use Drupal\xyz_events\EventsService; /** * Provides a base class for the event listing blocks. */ abstract class EventListingBlockBase { /** * The events service. * * @var \Drupal\xyz_events\EventsService */ protected $EventsService; /** * EventListingBlockBase constructor. */ public function __construct() { $this->EventsService = new EventsService(); } /** * Builds the content for the block. */ abstract public function build(); /** * Format the content into the array needed for the block. * * @param string $title * The block title. * @param array $items * The complete list of items. * @param string $empty * The text to print if there are no items. * @param string $theme_hook * The theme hook for the block content. * * @return array * The block content array. */ protected function formatContent(string $title, array $items, string $empty, string $theme_hook) { // Only keep the empty text if there are no items. $empty = (count($items) == 0) ? $empty : ''; $variables = [ 'items' => $items, 'empty' => $empty, ]; $content = [ 'subject' => $title, 'content' => theme($theme_hook, $variables), ]; return $content; } }

UpcomingEventsBlock.php and FeaturedEventsBlock.php both use the following code, just altering the “upcoming” to “featured” as appropriate.

<?php namespace Drupal\xyz_events\Plugin\Block; /** * Provides the content for the UpcomingEventsBlock block. */ class UpcomingEventsBlock extends EventListingBlockBase { /** * {@inheritdoc} */ public function build() { // Block title. $title = t('Upcoming Events'); // Get the list of events. $items = $this->EventsService->getEventList('upcoming') // What it should print if there aren't any. $empty = t('There are no upcoming events.'); // The theme hook to use to format the contents of the block. $theme_hook = 'xyz_events_upcoming_events'; return $this->formatContent($title, $items, $empty, $theme_hook); } }

Now all the content for building each block is encapsulated in the class for that block. When moving to Drupal 8/9, add the block annotation that it uses to identify blocks and remove the block-related hooks from the .module file.

If your blocks need configuration, this can be taken a step further by adding the form code and save code as methods on the block class and then referencing those from hook_block_configure() and hook_block_save().

Menu items to controllers

While hook_menu itself usually doesn’t get too overwhelming due to the actual code for the menu items being in separate functions, it does contribute to the .module file bloat. It’s also a lot nicer to have the menu items be in individual controllers like in Drupal 8/9.

To make this happen:

  • If you haven’t already, add a “src” directory at the root of your module.
  • Add a “Controller” directory under that.
  • For each menu item, add a file in there that is SomeController.php. Like services, “Controller” isn’t required but it makes it clearer. Another option is to use “Page” if the item corresponds to a viewable page rather than an API callback. For our example module, we end up with “UpcomingEventsController.php” and “FeaturedEventsController.php”.
  • As with blocks, a base controller can be used if the controllers have similar code.
  • Replace the hook code with a reference to the class (explained below).

There are two ways that the hook_menu() code can reference your class. Using a static function on the class and calling it directly or using a wrapper function to call the object.

Static method:
  • In hook_menu:
    'page callback' => 'UpcomingEventsController::build',
  • The build method on the class needs to be static.
Wrapper method:
  • In hook_menu:
    'page callback' => 'xyz_events_controller_callback',
    'page arguments' => ['controller_class' => 'UpcomingEventsCoursesPage'],
  • function xyz_events_controller_callback() needs to be in the .module file. (see below)
  • The build method on the class does not need to be static as we are instantiating an object.

In the .module file:

/** * Implements hook_menu(). */ function xyz_events_menu() { $items = []; $items['events/upcoming'] = [ 'title' => 'Upcoming events', 'page callback' => 'xyz_events_controller_callback', 'page arguments' => ['controller_class' => 'UpcomingEventsController'], 'type' => MENU_NORMAL_ITEM, ]; $items['events/featured'] = [ 'title' => 'Featured events', 'page callback' => 'xyz_events_controller_callback', 'page arguments' => ['controller_class' => 'FeaturedEventsController'], 'type' => MENU_NORMAL_ITEM, ]; return $items; } /** * Menu callback that wraps the controllers. */ function xyz_events_controller_callback($controller_class) { $controller_class = "\\Drupal\\xyz_events\\Controller\\$controller_class"; $controller = new $controller_class(); return $controller->build(); }

The classes:


<?php namespace Drupal\xyz_events\Controller; use Drupal\xyz_events\EventsService; /** * Provides a base class for the event listing pages. */ abstract class EventListingControllerBase { /** * The events service. * * @var \Drupal\xyz_events\EventsService */ protected $eventsService; /** * UserInProgressProgramsBlock constructor. */ public function __construct() { $this->eventsService = new EventsService(); } /** * Builds the content for the page. */ abstract public function build(); }

UpcomingEventsController.php and FeaturedEventsController.php have the same code with “upcoming” changed to “featured” as needed.

<?php namespace Drupal\xyz_events\Controller; /** * Provides the content for the UpcomingEventsController controller. */ class UpcomingEventsController extends EventListingControllerBase { /** * {@inheritdoc} */ public function build(): array { // Get the list of events. $items = $this->eventsService->getEventList('upcoming'); $content = theme('xyz_events_upcoming_events', ['items' => $items]); return ['#markup' => $content]; } } The rest of the hooks

While Drupal 7 relies on a lot of hooks and these need to be in a .module or .inc file so they can be found, there’s nothing requiring the actual code for the hooks to live in the functions. Like we did with blocks and menu items, the hook functions can serve as a wrapper around a call to a class where the actual code lives.

Testing the refactoring

While writing actual tests is the best way to test, that isn’t always possible with time and budget considerations. Still, you want to be sure your refactored code gets you the same results as the old code. Moving functions into classes while refactoring helps with that.

  • Add an include file to the root of the module. Ex: xyz_events_replaced_functions.inc
  • At the top of the .module file, add include_once 'xyz_events_replaced_functions.inc';
  • As you move functions into the classes, copy them into this file instead of deleting them from the .module file.

This keeps all the old functions active at the same time as the new ones which lets you test them in parallel within the same site.

Add this to the top of the module file:

/** * Implements hook_menu(). */ function xyz_events_menu() { // Adds a testing page for dev use. $items['admin/code-test'] = array( 'access arguments' => array('administer content'), 'description' => 'Place to test code.', 'page callback' => xyz_events_test_code', 'title' => 'Code testing', ); return $items; } /** * Place to put test code that is called from admin/code-test. */ function xyz_events_test_code() { print("Test completed."); }

Within xyz_events_test_code() you can do something like this:

Within xyz_events_test_code() you can do something like this: $events_service = new EventsService(); $old_list = xyz_events_get_event_list(); $new_list = $events_service->getEventList();

Set a breakpoint at the top of the function and then visit /admin/code-test on the site. You can then step through and compare what you get running the original function vs running your refactored function and make sure the result is either the same or has any differences that you intended with the refactor.

Once you have finished and are ready to commit your refactor, delete the include file, the include line, and the testing code.

Wrapping up

At this point, your .module file should be much smaller, consisting of just hooks and things like callback functions that can’t be moved into a class. Your directory structure should look a lot like a Drupal 8/9 module with all the code wrapped up into classes. There will still be work needed to move to Drupal 8/9 in dealing with the API changes but the actual structure of the code and where things are found shouldn’t need much changing. And maintaining the Drupal 7 code until that time should be a much more pleasant experience.

Further reference

This blog post came from actual client work but that work was inspired by others. These two were my biggest sources for reference:

I didn't get into forms above but here's an article that covers them: https://roomify.us/blog/object-oriented-forms-in-drupal-7/


The Russian Lullaby: Books/ 31 Days of Drupal Migrations

Planet Drupal - Fri, 2021/05/14 - 2:00am

Perhaps of all the Drupal available APIs, the Migrate API is one of the areas of knowledge most hidden from the average Drupal user. It’s even possible that as you read this post, you know for the first time that there is a whole set of Drupal modules both in core and contributed, and fully dedicated to the design and execution of migration processes within Drupal. You can move data from an old Drupal to the most recent version but also from others databases, files, systems, frameworks, CMS, DXPs… and all these options are aggregated around the Drupal Migrate API, a set of …


mark.ie: Supporting IE11 while still using Modern CSS

Planet Drupal - Thu, 2021/05/13 - 5:00pm

Drupal currently supports IE11, and will do until Drupal 10 in about a year's time. Here's my proposal for how to support IE11 in a distribution without holding back from modern CSS.


mark.ie: LocalGov Drupal: Creating Sensible Defaults in your Theme

Planet Drupal - Thu, 2021/05/13 - 2:01pm

We often end up with a lot of templates when theming, but we should also remember to create "sensible defaults", so before you start theming specifically your site doesn't look broken.


Matt Glaman: 🥳 phpstan-drupal 0.12.10 out!

Planet Drupal - Thu, 2021/05/13 - 6:39am

I just released phpstan-drupal 0.12.10, which includes a major bug fix and major feature.


DrupalCon News: Message from the Program Team

Planet Drupal - Thu, 2021/05/13 - 12:00am

Open Collaborative Connections - Join us to experience the open web with a collaborative culture and a connection to DrupalCamps across Europe. Learn about topic areas for DrupalCon, how content will be selected, and how you can get involved.


Tag1 Consulting: On 20 Years of Drupal - an interview with Emma Jane Hogbin

Planet Drupal - Wed, 2021/05/12 - 4:00pm

Many of Tag1’s team members have worked in various other companies and organizations during the 20 years Drupal has existed. In those many years, we have worked for and collaborated with other Drupal users and developers all over the world. This Tag1 Team Talk highlights one of those people. In this special edition of our 20 years of Drupal series, we are pleased to welcome Emma Jane Hogbin. Emma Jane was a very early user of Drupal and has written or contributed to documentation on Drupal.org and in her multiple books on Drupal and related subjects. Tag1's Managing Director Michael Meyers, hosts Emma Jane in this chat about her history with Drupal, and why it’s community, not code. For a transcript of this video, see Transcript: Emma Jane Hogbin - part 2. Click here for a list of other interviews in this series. --- Photo by Debby Hudson on Unsplash

Read more lynette@tag1co… Wed, 05/12/2021 - 07:00

Web Wash: Generate Open Graph Meta Tags for Facebook using Metatag in Drupal

Planet Drupal - Wed, 2021/05/12 - 8:55am

When someone shares a Facebook post with a link to your website, Facebook lets you control how your website content appears to others by parsing your Open Graph (OG) markup.

By doing some minimal configuration changes on your Drupal site using the Metatag Module and the Metatag: Open Graph sub module, you can define what specific content can be shown on Facebook regardless of whether it’s shared from the desktop or mobile web or a mobile app.

If you want to learn more about the OG tags. Click here for a detailed list and explanations of the Facebook OG tags.

In this tutorial, we are going to show you how to configure a content type to dynamically populate the Facebook OG tags using the Metagtag module and Metatag Open Graph sub module.


BADCamp News: What's next at San Francisco Drupal User's Group?

Planet Drupal - Tue, 2021/05/11 - 4:58pm
What's next at San Francisco Drupal User's Group? Tue, 05/11/2021 - 12:00 volkswagenchick Tue, 05/11/2021 - 07:58 While BADCamp is taking a hiatus in 2021, the San Francisco Drupal community is still committed to providing the high-quality content we all know and love twice a month through SFDUG. We typically meet the second and fourth Thursdays of the month at alternating times to accommodate a wider audience who might be visiting us from other time zones.  Drupal Planet

mark.ie: LocalGov Drupal: Refactoring Code - Video Paragraph Type

Planet Drupal - Tue, 2021/05/11 - 4:51pm

Here's an example of where "less is more" sometimes. I reckon we can make the video paragraph type in LocalGov Drupal simpler by just removing the template.


Bounteous.com: Acquia Site Studio: Introducing Visual Page Builder, New Features, and Bug Fixes

Planet Drupal - Tue, 2021/05/11 - 4:43pm
A brief article about what's new with Site Studio, version 6.5.

Acro Media: Automated Bookkeeping With Quickbooks Drupal Integration | Acro Media

Planet Drupal - Tue, 2021/05/11 - 4:00pm

Workflow automation is key in saving time, and money, in all aspects of a business. Accounting is one department that benefits from automation and our Quickbooks integration for Drupal can help remove manual data entry and free staff up to focus on other business objectives.

Automating accounting workflows with Quickbooks integration for Drupal

We all know as business owners that time is money. The more time we can save, the more time we have to work on other tasks for our business.

This is common sense, and is something every business owner should be looking to improve on every single day.

  • How can you save time?
  • What can you automate?
  • What type of processes can you create to become more efficient?
  • What systems and softwares can you use to become more organized?

These are things that you need to be looking at to not only save time for you and your staff, but so you can get things done right the first time. Having solid processes, automation and efficiency is how you create a smooth running business, that's why we decided to help out.

How Acro Media is contributing to workflow automation

" src="https://no-cache.hubspot.com/cta/default/1659229/41108856-e4f4-4f5a-a41f-56f3d09a8c3d.png" align="right">While it’s hard for us to know what specific needs, systems and processes your business would need in order to save time, we have been looking for a solution to help the majority of our clients.

One category we know every business could use some help on, and by help I mean automate, is accounting and bookkeeping. This can be a huge time suck.

Manually inputting product orders, customers, refunds, credit memos, and invoicing takes time. If you use Quickbooks and don’t have any type of automation or integration setup with your Drupal site, these tasks can eat up a lot of manpower every month. Manpower that could be used to hunt new accounts, research better products or services and in general find ways to make you business better, instead of doing menial tasks.

Not to mention, if you are doing all of these tasks manually, there is always a chance for human error, and we know, all of humans make mistakes.(Frantically searches for typos, again.)

This is why Acro Media has made an effort to develop a module that can integrate and connect your Drupal site straight to your Quickbooks account. This module eliminates any chance of human error and will save you a HUGE amount of time every month as all your data from your Drupal site will be automatically pushed into your Quickbooks account.

If your business is a little unique or you require functionality that our module doesn't currently have, we can build it for you. Gotta love open source!

To get a better visual idea of what our new Quickbooks module can do, have a look at the infographic below.

How much time can you expect to save?

While there is no way for us to determine how much time you can save each month, we have put together a rough formula to see how this module can help you and your team save countless hours every single month.

We calculated the median time spent manually creating the following bookkeeping essentials:

  • Creation of an invoice or sales receipt: 2.5 min/order
  • Manual credit memo or credit note: 1 min/order
  • Payment processing or annotation: 1 min/order

Time saved = [amount of orders each month] X 4.5min

So, let’s turn that formula into a good old grade-school word/math problem:

You have a healthy ecommerce business, and your online store alone is generating 2,500 sales a month.

2,500 x 4.5 min is equal to 11,250 minutes a month. Or, 187.5 hours a month.

That means you are freeing up 1 full-time staff member and 1 part-time staff member every month by having your ecommerce site integrated and talking directly to Quickbooks. Not to mention the reduction in human error, the better data you will get out of your sales reports and the time your staff can now dedicate to anything other than manual data entry!

What’s next?

If you're looking for ways to save time in your business and focus on what matters most, reach out to our team and let’s see if this module is a right fit for your business.

Talk soon, and remember... time is of the essence.

" src="https://no-cache.hubspot.com/cta/default/1659229/aae032bc-41c6-4af4-accd-d5cad8789268.png" align="middle">

Editor’s Note: This article was originally published on August 24, 2016, and has been updated for freshness, accuracy and comprehensiveness.