Blog: The importance of the right technology stack in a digital strategy

Planet Drupal - Fri, 2021/07/23 - 9:50am

In this article, we discuss the importance of having the right technology stack and key considerations for building it.


DevCollaborative - Planet Drupal: How to Add an RSS Feed to a Drupal Website

Planet Drupal - Thu, 2021/07/22 - 9:07pm
I recently went through the process of setting up an RSS Feed for our blog here at DevCollaborative so that our articles can be syndicated to Planet Drupal. It had a surprising amount of gotchas and the documentation was dated and sparse. So I thought I’d help remedy that by sharing what I learned.

Droptica: How to Perform a Drupal Security Audit? Overview of the Modules and Libraries

Planet Drupal - Thu, 2021/07/22 - 7:04pm

A security audit is the process of identifying security threats that can lead to unauthorised access to content, data leaks, bypassing the security, and other dangers. In the first part of the series on conducting a security audit, we'll focus on the overview of the Drupal module versions that we use at Droptica for this purpose, as well as on PHP and JavaScript libraries.

Drupal security audit

At Droptica, we make every effort to ensure that the solutions we provide are as safe as possible. We use the tools provided by the Drupal community, such as the Security Review module, to optimize the process of detecting the most popular security errors. We also use the Security Kit to make the project we're working on more resistant to attacks. You can learn more about the functionality of these modules in the linked posts, and the information on their operation will be useful in the following parts, in which we'll talk about the Drupal configuration review and code analysis.

Checking the versions of the installed Drupal modules

Updating modules and libraries is the simplest activity that we can perform to improve the security of our application. Drupal provides a view listing all the modules, which additionally indicates whether a given module is up-to-date, and if it isn’t – whether the update contains security fixes.

To check if the modules are up-to-date, go to /admin/modules/update

In the screenshot above, you can see that some of the modules need updating. Of course, in such cases we always recommend that you update all possible modules. If any of the modules contain a security fix, the update is required to ensure a high level of security for the application.

In the case of Drupal, the information about whether a given module has a security flaw is made available to the public when the author of the module releases its patched version. Module authors usually try to hide which code has been changed to patch a security flaw, but this always means that the attacker just needs more time to find a way to cause the bug and exploit it. Time is important, so you should keep track of security updates regularly, not only during a Drupal security audit. As we mentioned earlier, this is one of the simplest steps we can take to ensure a higher level of security for our application.

Patches review

When updating the Drupal modules, you should also check if a patch has been applied to a given module. If so, we proceed as follows:

  1. We check whether the patch was created by the community and if it concerns a specific issue on If so, we look for the issue that the patch is from. It's possible that the patch has been applied to one of the newer versions of the module. In such a case, we recommend updating the module and removing the patch with the information that the code that fixes the bug or adds a given functionality has been applied to the official, newer version of the module. If the patch hasn’t yet been applied to the newer version of the module, we still recommend updating and testing if the latest version of the patch serves its purpose.
  2. If the patch wasn’t created by the Drupal community, but is the result of working on the project, we still recommend updating the module. In this case, however, ensuring the correct operation of the patch lies with the people responsible for the custom code of the project. After updating, you should check whether the patch works as intended. If not, we recommend introducing appropriate fixes to the patch which will ensure its correct operation on the latest version of the module.
PHP libraries review

The next step will be reviewing the used PHP libraries. To list them, we can use the composer show command or the local-php-security-checker package. We recommend the latter solution because it significantly speeds up the process.

Result of the composer show command.

If you choose to install the local-php-security-checker package, follow the guidelines in the file.

Result of the scan using local-php-security-checker.

There's also the little-known Drupal Composer Security Checker module that uses the security-checker package. Currently, this module doesn't fulfill its task and the security-checker package itself isn't actively developed (since January 2021), therefore we'll focus on the local-php-security-checker package itself. If you find a security risk, our recommendation will be to update the library, of course – as in any case. An audit of the PHP libraries should be carried out regularly, the same as in the case of the Drupal modules.

JavaScript libraries review

The next step will be to check if the used JavaScript libraries are up-to-date and if they contain security fixes. To do this, you should review the library directory and the used package.json files.

In the case of the library directory, you need to check the version manually. In the case of package.json, we use the npm-audit command.

Result of the npm-audit command

The npm-audit command will list all known vulnerabilities, determine the threat level, package, dependencies, package path, and show a link with information about the vulnerability.

If you find a vulnerability, as always we recommend the update. JS library scans should be performed routinely, more often than a comprehensive security audit.

Improving the Drupal security - further steps

In this part of the Drupal security audit cycle, we've learned how to check whether the used versions of the modules and libraries are up-to-date and don't contain known security bugs. We also understand how to proceed if there is a patch available for a module – both when the patch comes from the Drupal community and when it was prepared by the developer working on the application.

Acquiring the knowledge provided in this post is the easiest way to improve the security of your application. Checking the versions of the used solutions is the first step that we perform during a security audit - our Drupal support team recommends periodic checking for updates. In the event that an update containing security fixes is released, we recommend that you perform an update as soon as possible.

In the next part of this series of articles, we'll get to learn more about the Drupal configuration aimed at increasing the security of our application. We'll also learn how to reduce the number of attack vectors and we'll find out more about the modules that'll help us with this.

Categories: How to host your Google Fonts locally with Drupal

Planet Drupal - Thu, 2021/07/22 - 1:15pm

Google fonts are a great way to give your Drupal website a unique look. But it’s even better to host those Google fonts locally. In this tutorial I will show you how.


Envato Tuts+: New Course: Code a Custom Drupal Module

Planet Drupal - Thu, 2021/07/22 - 9:39am
What You'll Be Creating

If you want an easy way to create engaging, content-driven websites for you and your customers, you should give Drupal 8 a try. And Drupal modules allow you to take things a step further and create highly customized functionality for your site. 

In our new course, Code a Custom Drupal Module, Envato Tuts+ instructor Derek Jensen will get you up and running with modules in no time. You'll build a simple calculator module, and along the way you'll learn about creating routes, controllers, parameters, and more.

You can take our new course straight away with a subscription to Envato Elements. For a single low monthly fee, you get access not only to this course, but also to our growing library of over 1,000 video courses and industry-leading eBooks on Envato Tuts+. 

Plus you now get unlimited downloads from the huge Envato Elements library of 200,000+ photos and 26,000+ design assets and templates. Create with unique fonts, photos, graphics and templates, and deliver better projects faster.

Looking for a shortcut? Try downloading some of the ready-made Drupal themes on Envato Market.


Envato Tuts+: New Code eBooks Available for Subscribers

Planet Drupal - Thu, 2021/07/22 - 9:39am

Do you want to learn more about asynchronous Android programming? How about the Ionic framework, or JavaScript design patterns? Our latest batch of eBooks will teach you all you need to know about these topics and more.

Our Latest Selection of eBooks

This month we’ve made eight new eBooks available for Envato Tuts+ subscribers to download. Here’s a summary of those books and what you can learn from them.

  • Xamarin: Cross-Platform Mobile Application Development

    Developing a mobile application for just one platform is becoming a thing of the past. Companies expect their apps to be supported on iOS, Android and Windows Phone, while leveraging the best native features on all three platforms. Xamarin's tools help ease this problem by giving developers a single toolset to target all three platforms. The main goal of this book is to equip you with knowledge to successfully analyze, develop, and manage Xamarin cross-platform projects using the most efficient, robust, and scalable implementation patterns.

  • Drupal 8 Theming With Twig

    Drupal 8 is an open-source content management system and powerful framework that helps deliver great websites to individuals and organizations, including non-profits, commercial, and government around the globe. Starting from the bottom up, in this eBook you will learn to install, set up, and configure Drupal 8. You'll get a walk-through of a real-world project to create a Twig theme from concept to completion while adopting best practices to implement CSS frameworks and JavaScript libraries. You will see just how quick and easy it is to create beautiful, responsive Drupal 8 websites while avoiding the common mistakes that many front-end developers make.

  • Asynchronous Android Programming: Second Edition

    Asynchronous programming has acquired immense importance in Android programming, especially when we want to make use of the number of independent processing units (cores) available on the most recent Android devices. With this guide in your hands, you’ll be able to bring the power of asynchronous programming to your own projects, and make your Android apps more powerful than ever before!

  • Ionic Framework by Example

    With Ionic, mobile development has never been so simple, so elegant and obvious. By helping developers to harness AngularJS and HTML5 for mobile development, it’s the perfect framework for anyone obsessed with performance, and anyone that understands just how important a great user experience really is. This book shows you how to get started with Ionic framework immediately. But it doesn’t just give you instructions and then expect you to follow them. Instead it demonstrates what Ionic is capable of through three practical projects you can follow and build yourself.

  • Sass and Compass Designer's Cookbook

    Sass and Compass Designer's Cookbook helps you to get most out of CSS3 and harness its benefits to create engaging and receptive applications. This book will help you develop faster and reduce the maintenance time for your web development projects by using Sass and Compass. You will learn how to use with CSS frameworks such as Bootstrap and Foundation and understand how to use other libraries of pre-built mixins. You will also learn setting up a development environment with Gulp.

  • Android Sensor Programming by Example

    Android phones available in today’s market have a wide variety of powerful and highly precise sensors. This book will give you the skills required to use sensors in your Android applications. It will walk you through all the fundamentals of sensors and will provide a thorough understanding of the Android Sensor Framework. By the end of the book, you will be well versed in the use of Android sensors and programming to build interactive applications.

  • Mastering Yii

    The successor of Yii Framework 1.1, Yii2 is a complete rewrite of Yii Framework, one of the most popular PHP 5 frameworks for making modern web applications. This book has been written to enhance your skills and knowledge with Yii Framework 2. Starting with configuration and how to initialize new projects, you’ll learn how to configure, manage, and use every aspect of Yii2 from Gii, DAO, Query Builder, Active Record, and migrations, to asset manager. With this book by your side, you’ll have all the skills you need to quickly create rich modern web and console applications with Yii2.

  • Mastering JavaScript Design Patterns: Second Edition

    In this book, you will explore how design patterns can help you improve and organize your JavaScript code. You’ll get to grips with creational, structural and behavioral patterns as you discover how to put them to work in different scenarios. Then, you'll get a deeper look at patterns used in functional programming, as well as model view patterns and patterns to build web applications. By the end of the book, you'll be saved of a lot of trial and error and developmental headaches, and you will be on the road to becoming a JavaScript expert.

Start Learning With a Yearly Subscription

Subscribe to Envato Tuts+ for access to our library of hundreds of eBooks. With a Yearly subscription, you can download up to five eBooks per month, while the Yearly Pro subscription gives you unlimited access.

You can also build on your newfound knowledge by using some of the fantastic code scripts and plugins on Envato Market.


Envato Tuts+: Drupal 8: Properly Injecting Dependencies Using DI

Planet Drupal - Thu, 2021/07/22 - 9:39am

As I am sure you know by now, dependency injection (DI) and the Symfony service container are important new development features of Drupal 8. However, even though they are starting to be better understood in the Drupal development community, there is still some lack of clarity about how exactly to inject services into Drupal 8 classes.

Many examples talk about services, but most cover only the static way of loading them:

$service = \Drupal::service('service_name');

This is understandable as the proper injection approach is more verbose, and if you know it already, rather boilerplate. However, the static approach in real life should only be used in two cases:

  • in the .module file (outside of a class context)
  • those rare occasions within a class context where the class is being loaded without service container awareness

Other than that, injecting services is the best practice as it ensures decoupled code and eases testing.

In Drupal 8 there are some specificities about dependency injection that you will not be able to understand solely from a pure Symfony approach. So in this article we are going to look at some examples of proper constructor injection in Drupal 8. To this end, but also to cover all the basics, we will look at three types of examples, in order of complexity:

  • injecting services into another of your own services
  • injecting services into non-service classes
  • injecting services into plugin classes

Going forward, the assumption is that you know already what DI is, what purpose it serves and how the service container supports it. If not, I recommend checking out this article first.


Injecting services into your own service is very easy. Since you are the one defining the service, all you have to do is pass it as an argument to the service you want to inject. Imagine the following service definitions:

services: demo.demo_service: class: Drupal\demo\DemoService demo.another_demo_service: class: Drupal\demo\AnotherDemoService arguments: ['@demo.demo_service']

Here we define two services where the second one takes the first one as a constructor argument. So all we have to do now in the AnotherDemoService class is store it as a local variable:

class AnotherDemoService { /** * @var \Drupal\demo\DemoService */ private $demoService; public function __construct(DemoService $demoService) { $this->demoService = $demoService; } // The rest of your methods }

And that is pretty much it. It's also important to mention that this approach is exactly the same as in Symfony, so no change here.

Non-Service Classes

Now let's take a look at classes that we often interact with but that are not our own services. To understand how this injection takes place, you need to understand how the classes are resolved and how they are instantiated. But we will see that in practice soon.


Controller classes are mostly used for mapping routing paths to business logic. They are supposed to stay thin and delegate heavier business logic to services. Many extend the ControllerBase class and get some helper methods to retrieve common services from the container. However, these are returned statically.

When a controller object is being created (ControllerResolver::createController), the ClassResolver is used to get an instance of the controller class definition. The resolver is container aware and returns an instance of the controller if the container already has it. Conversely, it instantiates a new one and returns that. 

And here is where our injection takes place: if the class being resolved implements the ContainerAwareInterface, the instantiation takes place by using the static create() method on that class which receives the entire container. And our ControllerBase class also implements the ContainerAwareInterface.

So let's take a look at an example controller which properly injects services using this approach (instead of requesting them statically):

/** * Defines a controller to list blocks. */ class BlockListController extends EntityListController { /** * The theme handler. * * @var \Drupal\Core\Extension\ThemeHandlerInterface */ protected $themeHandler; /** * Constructs the BlockListController. * * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler * The theme handler. */ public function __construct(ThemeHandlerInterface $theme_handler) { $this->themeHandler = $theme_handler; } /** * {@inheritdoc} */ public static function create(ContainerInterface $container) { return new static( $container->get('theme_handler') ); } }

The EntityListController class doesn't do anything for our purposes here, so just imagine that BlockListController directly extends the ControllerBase class, which in turn implements the ContainerInjectionInterface.

As we said, when this controller is instantiated, the static create() method is called. Its purpose is to instantiate this class and pass whatever parameters it wants to the class constructor. And since the container is passed to create(), it can choose which services to request and pass along to the constructor. 

Then, the constructor simply has to receive the services and store them locally. Do keep in mind that it's bad practice to inject the entire container into your class, and you should always limit the services you inject to the ones you need. And if you need too many, you are likely doing something wrong.

We used this controller example to go a bit deeper into the Drupal dependency injection approach and understand how constructor injection works. There are also setter injection possibilities by making classes container aware, but we won't cover that here. Let's instead look at other examples of classes you may interact with and in which you should inject services.


Forms are another great example of classes where you need to inject services. Usually you either extend the FormBase or ConfigFormBase classes which already implement the ContainerInjectionInterface. In this case, if you override the create() and constructor methods, you can inject whatever you want. If you don't want to extend these classes, all you have to do is implement this interface yourself and follow the same steps we saw above with the controller.

As an example, let's take a look at the SiteInformationForm which extends the ConfigFormBase and see how it injects services on top of the config.factory its parent needs:

class SiteInformationForm extends ConfigFormBase { ... public function __construct(ConfigFactoryInterface $config_factory, AliasManagerInterface $alias_manager, PathValidatorInterface $path_validator, RequestContext $request_context) { parent::__construct($config_factory); $this->aliasManager = $alias_manager; $this->pathValidator = $path_validator; $this->requestContext = $request_context; } /** * {@inheritdoc} */ public static function create(ContainerInterface $container) { return new static( $container->get('config.factory'), $container->get('path.alias_manager'), $container->get('path.validator'), $container->get('router.request_context') ); } ... }

As before, the create() method is used for the instantiation, which passes to the constructor the service required by the parent class as well as some extra ones it needs on top.

And this is pretty much how the basic constructor injection works in Drupal 8. It's available in almost all class contexts, save for a few in which the instantiation part was not yet solved in this manner (e.g. FieldType plugins). Additionally, there is an important subsystem which has some differences but is crucially important to understand: plugins.


The plugin system is a very important Drupal 8 component that powers a lot of functionality. So let's see how dependency injection works with plugin classes.

The most important difference in how injection is handled with plugins is the interface plugin classes need to implement: ContainerFactoryPluginInterface. The reason is that plugins are not resolved but are managed by a plugin manager. So when this manager needs to instantiate one of its plugins, it will do so using a factory. And usually, this factory is the ContainerFactory (or a similar variation of it). 

So if we look at ContainerFactory::createInstance(), we see that aside from the container being passed to the usual create() method, the $configuration, $plugin_id, and $plugin_definition variables are passed as well (which are the three basic parameters each plugin comes with).

So let's see two examples of such plugins that inject services. First, the core UserLoginBlock plugin (@Block):

class UserLoginBlock extends BlockBase implements ContainerFactoryPluginInterface { ... public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteMatchInterface $route_match) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->routeMatch = $route_match; } /** * {@inheritdoc} */ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { return new static( $configuration, $plugin_id, $plugin_definition, $container->get('current_route_match') ); } ... }

As you can see, it implements the ContainerFactoryPluginInterface and the create() method receives those three extra parameters. These are then passed in the right order to the class constructor, and from the container a service is requested and passed as well. This is the most basic, yet commonly used, example of injecting services into plugin classes.

Another interesting example is the FileWidget plugin (@FieldWidget):

class FileWidget extends WidgetBase implements ContainerFactoryPluginInterface { /** * {@inheritdoc} */ public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, ElementInfoManagerInterface $element_info) { parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings); $this->elementInfo = $element_info; } /** * {@inheritdoc} */ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { return new static($plugin_id, $plugin_definition, $configuration['field_definition'], $configuration['settings'], $configuration['third_party_settings'], $container->get('element_info')); } ... }

As you can see, the create() method receives the same parameters, but the class constructor expects extra ones that are specific to this plugin type. This is not a problem. They can usually be found inside the $configuration array of that particular plugin and passed from there.

So these are the main differences when it comes to injecting services into plugin classes. There's a different interface to implement and some extra parameters in the create() method.


As we've seen in this article, there are a number of ways we can get our hands on services in Drupal 8. Sometimes we have to statically request them. However, most of the time we shouldn't. And we've seen some typical examples of when and how we should inject them into our classes instead. We've also seen the two main interfaces the classes need to implement in order to be instantiated with the container and be ready for injection, as well as the difference between them.

If you are working in a class context and you are unsure of how to inject services, start looking at other classes of that type. If they are plugins, check if any of the parents implement the ContainerFactoryPluginInterface. If not, do it yourself for your class and make sure the constructor receives what it expects. Also check out the plugin manager class responsible and see what factory it uses. 

In other cases, such as with TypedData classes like the FieldType, take a look at other examples in core. If you see others using statically loaded services, it's most likely not yet ready for injection so you'll have to do the same. But keep an eye out, because this might change in the future.


Envato Tuts+: What Is Drupal?

Planet Drupal - Thu, 2021/07/22 - 9:39am

Drupal is a popular open-source content management system written in PHP. Having been created in the early 2000s by a Belgian student, it now powers some of the most prominent websites on the web (,, etc.). It is often regarded as a competitor of CMSs such as WordPress and Joomla.

One of the most important components of the Drupal project is its community of supporters (contributors, developers, evangelists, business owners, etc.). Prominent within this community stands the Drupal Association, responsible for "fostering and supporting the Drupal software project, the community and its growth".

A giant leap from its predecessor, the 8th major release of the Drupal project has just hit the shelves. It brought about a serious modernisation of its code, practices and mentality. Many regard this shift as a real move away from the traditional notion of a CMS to more of a Content Management Framework (CMF) that provides a great platform for building complex applications.

In this article, I'm going to answer some of the more frequent questions people have about Drupal when starting up for the first time or considering doing so:

  • Is it right for me? Who is it aimed at?
  • How can it be installed, and where can I host it?
  • How can I start working with it as a developer?
  • What options do I have for extending functionality or styling it?
Who Is Drupal Aimed At?

Since the beginning of the project, Drupal has evolved from being mainly a tool for building smaller sites to one that can now power enterprise-level platforms. Especially with Drupal 8, site builders and developers can easily scale up from small websites to large platforms with many integrations. For example, the adoption of Composer allows you not only to bring external libraries into a Drupal project, but also to use Drupal as part of a bigger project of applications and libraries. It's safe to say that Drupal is flexible enough to meet the needs of a wide range of projects.

When it comes to development, Drupal has always had a relatively closed community—not because people are unfriendly, quite the contrary, but mainly because of the code typically being written in a Drupal way (resulting in what sometimes is referred to as Drupalisms). This has meant a learning curve for any developer starting up, but also less interest from developers of other PHP projects to jump in and contribute.

This is no longer the case. Especially with the release of Drupal 8, the community now promotes a new mentality of code reusability and best practice across different open-source projects. Active participation in the PHP Framework Interoperability Group is part of this effort, and using a number of popular Symfony components in Drupal 8 core is a testament to this commitment. 

With this move, the Drupal community has gotten richer by welcoming many developers from other communities and projects, and it is sure to grow even further. So if you are a Laravel developer, looking at Drupal code will no longer be so daunting.

How Can I Install Drupal, and Where Can I Host It?

Traditionally, Drupal has had a relatively easy installation process, particularly for people who at least knew their way around a Linux environment. The project simply needs to be dropped into a folder your web server can run (which needs to be using PHP and have a MySQL or MariaDB database). Then pointing your browser to the /install.php file and following the steps takes care of the rest. The most important screen you'll see is the one in which you select a specific database to use.

In terms of requirements, the LAMP stack (Linux, Apache, MySQL and PHP) environment has always been a favourite for Drupal to run in. However, it is in no way restricted to it. Solutions exist for installing it straight on Windows or Mac (e.g. using the Acquia Dev Desktop) but also on a Linux system that runs other web servers.

The easiest approach, if you go with your own setup, is to use a LAMP server for hosting. For a bit more performance you can replace Apache with Nginx, but you'll then have to take care of some specific configuration that otherwise is handled in the .htaccess file Drupal ships with.

However, if you don't want the hassle of maintaining your own hosting server, there are three main providers of specialised Drupal managed hosting: Acquia, Pantheon, and These also provide a workflow for easy updates and development flow. Past that, you are looking at fully managed hosting with a Drupal development company.

How Can I Get Started Developing for It?

Developing Drupal websites has typically been the kind of thing you either liked a lot or didn't like at all. This is because when you were first introduced to Drupal, you encountered very many specificities that you didn't see in other projects. So if those tickled your fancy, you loved it forever.

With getting off this island in Drupal 8, this is no longer the case as much. You still have plenty of Drupalisms left that you can love or hate, but you now also have external components like Symfony or Guzzle and, most importantly, a more modern way of writing code in general (OOP, design patterns, reusable components, etc.). So your PHP skills from building websites with Zend will come in handy.

A good way of getting into Drupal development is to follow some online video courses. There are a couple of resources that are excellent for this purpose, most notably If, however, video is not your favourite medium, there are also many written tutorials and guides available to get you started. Check out the following links for some of the first steps you can take:

Since Drupal 8 is brand new, you'll find significantly more learning content for Drupal 7. Nevertheless, the focus in the community has been shifting recently towards Drupal 8, so you can expect more and more of these resources to crop up. And if you have no experience with any version of Drupal, it's best to focus exclusively on Drupal 8 as the changes between the two are big and perhaps you'd be facing unnecessary challenges.

How Can I Extend Drupal?

The main extension point of a core Drupal installation is its module system. 

Modules are used to encapsulate bigger chunks of reusable functionality that can/should work on different sites. Aside from the core modules, there are a large number of contributed ones, available for installation. 

Granted, most are still only for Drupal 6 and 7, but the community is catching up also for the newest version. This problem is also mitigated by the incorporation in Drupal 8 of a few popular contributed modules as well as extending the scope of what core can do out of the box (compared to Drupal 7). 

Lastly, custom modules (the ones that you write yourself) are the primary way you can add any functionality that you want and that is not available via a contributed module.

Installing modules can allow you to plug in various pieces of functionality, but you should not treat this as a green light for adding too many. It's always better to stick to the ones you actually need, and don't be afraid to be critical in this respect. You can also work on finding a good balance between contributed code and the custom one you write yourself. 

Additionally, since we are talking about open-source software, you should always evaluate the modules you install. The following indicators are good examples to pay attention to: number of downloads and usage, commit frequency, maintainer engagement, state of the issue queue.

And do keep security in mind as well. It's highly recommended you keep both Drupal core and any contributed modules up to date as this will significantly help you keep your site and server secure (though it doesn't ensure it).

What About Styling?

The styling layer of a Drupal site is handled (in large part) by its theme. Themes are similar to modules in that they are an extension point, but they have different responsibilities. They contain the styles, front-end libraries and in most cases template files that are used to output data.

There has been great progress in Drupal 8 compared to the previous version: the popular Twig engine has been adopted for templating, theming has been limited to Twig template files, debugging and overriding templates has been made much easier, etc. Similar to the advances in back-end development experience (DX), the theming layer has been made more appealing to the non-Drupal crowd. Front-end developers can now easily work with Drupal themes without having to understand the ins and outs of the back end.

Drupal core comes with a number of themes that can provide you with examples but also which you can extend from. There are also contributed themes similar to how there are modules. Popular front-end frameworks such as Bootstrap or Zurb Foundation have mature Drupal theme implementations for Drupal 7, which are also readying for Drupal 8. These work very well as base themes but also for quickly scaffolding a website and making it look decent.

Paid themes are also available to try out. Usually they are very cheap and quick to set up. The problem with them is that they are worth exactly as much as you pay for them and usually have gaping holes in their flexibility. As a beginner, these themes can seem like a great way to set up a site, and they very well may be. However, as you progress, you'll learn to avoid them and build your own, based on external designs or even plain HTML/CSS/JS templates.


Drupal is a powerful tool for building websites and platforms of any kind. With each new major release, Drupal has shown a commitment to better itself, become more robust and flexible, and embrace outside communities as well.


Envato Tuts+: Using and Extending the Drupal 8 Mail API: Part 2

Planet Drupal - Thu, 2021/07/22 - 9:39am

In the previous article we looked at how we can send emails programatically in Drupal 8. We also saw how other modules can alter these outgoing mails. Today, we are going to look at how we can use the Mail API to extend this default behaviour. The purpose is to use an external service as a means for email delivery. 

For this, we will use Mandrill, although the focus of the article will not be its API or how to work with it, but rather the Drupal side of things. And remember, the working module can be found in this Git repository.

As we've seen in the previous article, sending an email in Drupal 8 happens by requesting the mail manager, passing some parameters to its mail() method, and setting up a template inside a hook_mail() implementation. What the mail manager does internally is load up the appropriate mail plugin, construct the email, and then delegate to the mail() method of whatever plugin was loaded.

But who does it actually delegate to?

Plugin Selection

An important thing to understand before writing our own plugin is the selection process of the mail manager for loading plugins. In other words, how do we know which plugin it will load, and how can we make it load our own?

The system.mail.interface configuration array holds all the answers. It contains the ids of the available plugins, keyed by the context they are used in. By default, all we have inside this configuration is default => phpmail. This means that the plugin with the id phpmail (the PHPMail class) is used as fallback for all contexts that are not otherwise specified, i.e. the default.

If we want to write our own plugin, we need to add another element into that array with the plugin id as its value. The key for this value can be one of two things: the machine name of our module (to load the plugin whenever our module sends emails) or a combination of module name and email template key (to load the plugin whenever our module sends an email using that specific key). 

An example of the latter construct is d8mail_node_insert, where d8mail is our module name we started building in the previous article, and node_insert is the email template key we defined.

So now that we know how the mail plugin selection happens, we need to make sure this config array contains the necessary information so that emails sent with our d8mail module use the new plugin we will build. We can do this inside a hook_install() implementation that gets triggered only once when the module gets installed:


/** * Implements hook_install(). */ function d8mail_install() { $config = \Drupal::configFactory()->getEditable('system.mail'); $mail_plugins = $config->get('interface'); if (in_array('d8mail', array_keys($mail_plugins))) { return; } $mail_plugins['d8mail'] = 'mandrill_mail'; $config->set('interface', $mail_plugins)->save(); }

Not super complicated what happens above. We load the editable config object representing the system.mail configuration, and add a new element to the interface array: d8mail => mandrill_mail. We will soon create a mail plugin with the id of mandrill_mail which will be used for all emails sent by the d8mail module. And that's it.

But before we move on, we need to make sure this change is reverted when the module is uninstalled. For this, we can use the counterpart hook_uninstall() that gets called when a module gets uninstalled (there is no more module disabling in Drupal 8).

Inside the same file:

/** * Implements hook_uninstall(). */ function d8mail_uninstall() { $config = \Drupal::configFactory()->getEditable('system.mail'); $mail_plugins = $config->get('interface'); if ( ! in_array('d8mail', array_keys($mail_plugins))) { return; } unset($mail_plugins['d8mail']); $config->set('interface', $mail_plugins)->save(); }

With the hook_uninstall() implementation we do the opposite of before: we remove our plugin id if it is set.

The install/uninstall scenario is just one way to go. You can also create an administration form that allows users to select the plugin they want and under which context. But you still need to make sure that when you disable the module defining a particular plugin, the configuration will no longer keep a reference to that plugin. Otherwise the mail manager may try to use a non-existent class and throw all kinds of errors.


As I mentioned before, we will work with the Mandrill API in order to illustrate our task. So let's load up Mandrill's PHP Library and make it available in our environment. There are three steps we need to do for this.

First, we need to actually get the library inside Drupal. At the time of writing, this basically means adding the "mandrill/mandrill": "1.0.*" dependency to the root composer.json file and running composer install. Keep in mind, though, that this will also clear the Drupal installation from inside the core/ folder and download the latest stable release instead. Then, you'll need to edit the root index.php file and change the path to the autoloader as per these instructions. Hopefully this last action won't be necessary soon, and I encourage you to follow the discussions around the future of Composer in Drupal 8 for managing external libraries.

Second, we need to get an API key from Mandrill. Luckily, this we can easily generate from their administration pages. Once we have that, we can store it inside a new file created on our server, at either location:

~/.mandrill.key /etc/mandrill.key

We can also pass the key as a constructor parameter to the main Mandrill class, but this way we won't have to hardcode it in our code. 

Thirdly, we need to create a service so that we can use dependency injection for passing the Mandrill class into our plugin:

services: d8mail.mandrill: class: Mandrill

Depending on how you have loaded the Mandrill class into your application, you'll need to change the value after class. By using the composer.json approach, this will suffice.

The Mail Plugin

It's finally time to create our plugin. In Drupal 8, plugin classes go inside the src/Plugin folder of our module. Depending on their type, however, they are placed further down within other directories (in our case Mail). Let's write our class that will depend on the Mandrill API library to send emails:


<?php namespace Drupal\d8mail\Plugin\Mail; use Drupal\Core\Mail\MailFormatHelper; use Drupal\Core\Mail\MailInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Mandrill; use Mandrill_Error; /** * Defines the Mandrill mail backend. * * @Mail( * id = "mandrill_mail", * label = @Translation("Mandrill mailer"), * description = @Translation("Sends an email using Mandrill.") * ) */ class MandrillMail implements MailInterface, ContainerFactoryPluginInterface { /** * @var Mandrill */ private $mandrill; /** * @param Mandrill $mandrill */ public function __construct(Mandrill $mandrill) { $this->mandrill = $mandrill; } /** * {@inheritdoc} */ public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { return new static( $container->get('d8mail.mandrill') ); } /** * {@inheritdoc} */ public function format(array $message) { // Join the body array into one string. $message['body'] = implode("\n\n", $message['body']); // Convert any HTML to plain-text. $message['body'] = MailFormatHelper::htmlToText($message['body']); // Wrap the mail body for sending. $message['body'] = MailFormatHelper::wrapMail($message['body']); return $message; } /** * {@inheritdoc} */ public function mail(array $message) { try { $vars = [ 'html' => $message['body'], 'subject' => $message['subject'], 'from_email' => $message['from'], 'to' => array( array('email' => $message['to']) ), ]; $result = $this->mandrill->messages->send($vars); if ($result[0]['status'] !== 'sent') { return false; } return $result; } catch (Mandrill_Error $e) { return false; } } }

There are a couple of things to note before getting into what the class does.

First, the annotations above the class. This is just the most common plugin discovery mechanism for Drupal 8. The id key matches the value we added to the system.mail.interface configuration array earlier, while the rest are basic plugin definition elements.

Second, the implementation of the ContainerFactoryPluginInterface interface by which we define the create() method. The latter is part of the dependency injection process by which we can load up the Mandrill service we defined in the services.yml file earlier. This makes testing much easier and it's considered best practice.

As I mentioned, the mail plugins need to implement the MailInterface interface which enforces the existence of the format() and mail() methods. In our case, the first does exactly the same thing as the PHPMail plugin: a bit of processing of the message body. So you can add your own logic here if you want. The latter method, on the other hand, is responsible for sending the mail out, in our case, using the Mandrill API itself.

As the Mandrill documentation instructs, we construct an email message inside the $vars array using values passed from the mail manager through the $message parameter. These will be already filtered through hook_mail(), hook_mail_alter() and the plugin's own format() method. All that's left is to actually send the email. I won't go into the details of using the Mandrill API as you can consult the documentation for all the options you can use.

After sending the email and getting back from Mandrill a sent status, we return the entire response array, which contains some more information. This array then gets added by the mail manager to its own return array keyed as result. If Mandrill has a problem, rejects the email or throws an exception, we return false. This will make the mail manager handle this situation by logging the incident and printing a status message.

And that is pretty much it. We can clear the cache and try creating another article node. This time, the notification email should be sent by Mandrill instead of PHP's mail(). With this in place, though, the hook_mail_alter() implementation has become superfluous as there are no headers we are actually sending through to Mandrill (and the text is HTML already). And for that matter quite a lot of the work of the mail manager is not used, as we are not passing that on to Mandrill. But this is just meant to illustrate the process of how you can go about setting this up. The details of the implementation remain up to you and your needs.


And there we have it. We have implemented our own mail plugin to be used by the d8module we started in the previous article. And due to the extensible nature of Drupal 8, it didn't even take too much effort. 

What's left for you to do is to perfect the mail sending logic and adapt it to your circumstances. This can mean further integration between Mandrill and your Drupal instance, constructing nice templates or what have you. Additionally, an important remaining task is writing automated tests for this functionality. And Drupal 8 offers quite the toolkit for that as well.


Envato Tuts+: Using and Extending the Drupal 8 Mail API: Part 1

Planet Drupal - Thu, 2021/07/22 - 9:39am

In this two part series we are going to explore the Mail API in Drupal 8. In doing so, we are going to cover two main aspects: how to use it programatically for sending emails and how to extend it for using an external service like Mandrill.

To demonstrate this, in the first part we will create a custom email template that gets used for sending emails to the current user when s/he saves a new Article node. Additionally, we will see how others can alter that template in order to allow for HTML rendering of the email body instead of the default plain text.

In the second part we are going to look at extending the mail system and integrating an external API for email delivery. For this, we will use Mandrill and its PHP library that provides a good foundation for interacting with its API.

All the work we go through can be found in this Git repository as part of a custom Drupal 8 module that we will start writing here. So feel free to check that out if you want to follow along. Let's get started.

The first prerequisite of this module is its .info file:

name: Drupal 8 Mailer description: 'Demonstrates the use of the Mail API in Drupal 8.' core: 8.x type: module

With this out of the way, we can already enable the module on our site if we want.

How Do We Send an Email?

There are two main steps needed to send an email programatically with Drupal 8. We first need to implement hook_mail() in order to define one or more email templates. The second step is to use the mail manager to send emails using one of these templates.

Although called a hook, hook_mail() is not a typical hook but more of a regular function that generally gets called only by the same module that implements it. In other words, when you send an email programatically, you need to specify the module name implementing hook_mail() and the template id you want to use and that is defined by this hook. But we'll see that in a minute. First, how do we implement it?


/** * Implements hook_mail(). */ function d8mail_mail($key, &$message, $params) { $options = array( 'langcode' => $message['langcode'], ); switch ($key) { case 'node_insert': $message['from'] = \Drupal::config('')->get('mail'); $message['subject'] = t('Node created: @title', array('@title' => $params['node_title']), $options); $message['body'][] = SafeMarkup::checkPlain($params['message']); break; } }

This is a very simple implementation that defines one template identified as node_insert (the $key). The other two function arguments are:

  • $message: passed by reference, and inside which we add as much boilerplate about our email as we need 
  • $params: an array of extra data that needs to go in the email and that is passed from the mail manager when we try to send the email

As you can see, we are building up the $message array with values we want this email to include in all the calls. We are setting a default from value that is retrieved from the configuration system and that represents the main site email address. We set a boilerplate email subject that lets the recipient know a new node was created, followed by the name of the node (which will be passed in through the $params array). The subject is also translatable into the language that gets passed from the caller. 

Lastly, we run the message body through the string sanitiser because the text may contain HTML and it might get truncated if we don't encode the HTML elements. And since we are using the SafeMarkup class, we need to use it at the top:

use Drupal\Component\Utility\SafeMarkup;

Additionally, the message body is an array that will later be imploded into a string. And obviously there are many other parameters we can set, such as headers, but this will suffice for this example.

And that's all for the hook_mail() implementation. Now let's turn to the code which gets run every time a new node is created, hook_entity_insert():

/** * Implements hook_entity_insert(). */ function d8mail_entity_insert(Drupal\Core\Entity\EntityInterface $entity) { if ($entity->getEntityTypeId() !== 'node' || ($entity->getEntityTypeId() === 'node' && $entity->bundle() !== 'article')) { return; } $mailManager = \Drupal::service('plugin.manager.mail'); $module = 'd8mail'; $key = 'node_insert'; $to = \Drupal::currentUser()->getEmail(); $params['message'] = $entity->get('body')->value; $params['node_title'] = $entity->label(); $langcode = \Drupal::currentUser()->getPreferredLangcode(); $send = true; $result = $mailManager->mail($module, $key, $to, $langcode, $params, NULL, $send); if ($result['result'] !== true) { $message = t('There was a problem sending your email notification to @email for creating node @id.', array('@email' => $to, '@id' => $entity->id())); drupal_set_message($message, 'error'); \Drupal::logger('d8mail')->error($message); return; } $message = t('An email notification has been sent to @email for creating node @id.', array('@email' => $to, '@id' => $entity->id())); drupal_set_message($message); \Drupal::logger('d8mail')->notice($message); }

This hook gets triggered after every node save, and all we have to do is make sure we are targeting the correct node and include our logic.

After checking that the node entity is of the type article, we load the Drupal mail manager service and start setting some values for the email. We need the following information:

  • the module name that implements hook_mail() and defines our template (what I mentioned above)
  • the template id (the $key)
  • the recipient email address (the one found on the current user account)
  • the language ($langcode) which goes inside the $params array and which will be used to translate the subject message
  • the node title that will get added to the email subject
  • the email body, which in our case will be the value of the node's body field
  • the boolean value indicating whether the email should be actually sent

We then pass all these values to the mail() method of the mail manager. The latter is responsible for building the email (calling the right hook_mail() implementation being one aspect of this) and finally delegating the actual delivery to the responsible plugin. By default, this will be PHPMail, which uses the default mail() function that comes with PHP.

If the mail manager is successful in sending the email (actual delivery is not taken into account but rather a successful PHP action), the mail() method will return an array containing a result key with whatever the mail plugin returns. Checking for that value, we can learn whether the email action was successful and inform the user that we have notified them of their action. Otherwise, we print and log an error message.

And that's about it. Clearing the cache and creating an article node should land an email in your inbox. If you are not getting anything and there are no error signs on your screen, make sure you check your server logs and mail queue to verify that emails are being sent out.

Before moving on, I would like to make a quick note regarding this hook implementation. In this example, I placed all the logic inside it directly. Additionally, I used an early return at the top, which essentially means no other logic can be added but the one specific to the article nodes. In real applications I recommend refactoring the mailing logic into a separate function or class and deferring to that. Moreover, you should not use early returns inside hook implementations but instead call other functions if the conditions are met. 

How Do We Alter an Email?

Once all of this is in place, we have another tool at our disposal that allows us to alter such an existing setup: hook_mail_alter(). This hook is called from within the mail manager before the responsible mail plugin sends the email. The purpose is to allow other modules to perform final alterations to an existent email being sent out.

Although this can be used by other modules as well, we will illustrate an example implementation from within the same module we've been working with. To this end, we will alter the email by changing one of its default headers in order to transform it from plain text to HTML. And this is how we can do this:

/** * Implements hook_mail_alter(). */ function d8mail_mail_alter(&$message) { switch ($message['key']) { case 'node_insert': $message['headers']['Content-Type'] = 'text/html; charset=UTF-8; format=flowed; delsp=yes'; break; } }

As you can see, this is a simple alteration of the Content-Type header that transforms the email into HTML. This way plain text HTML entities will be parsed as HTML by mail clients. And using the switch case, we make sure this only happens for the email template we defined earlier.

One thing to note here is that the alter hook gets called after the relevant hook_mail() implementation. So after this, the only processing that happens on the email is done inside the format() method of the mail plugin (enforced by its interface).


And that is pretty much all there is to sending emails programatically using Drupal 8. We've seen the steps required to programatically set up email templates that get hydrated by the mail manager whenever we want it. We've also mentioned the default mail delivery plugin which is used to send emails in Drupal 8. And lastly, we've seen how other modules can now alter our email by adding new headers, changing the subject, concatenating values to the mail body, etc.

In the next article we are going to look at replacing the default PHPMail plugin with our own custom implementation. We will set up a mailer that uses Mandrill with the help of its PHP library. The goal is to allow our own module to use this mailer while the rest of the application continues to use the default PHPMailer.


Envato Tuts+: Create a Facebook Recent Activity Drupal Module

Planet Drupal - Thu, 2021/07/22 - 9:39am

Enhancing Drupal's built-in functionality with new modules is one of the features that attracted a lot of developers to the platform and made it extremely popular. This tutorial will demonstrate how to create a Drupal module, using techniques recommended by Drupal gurus.

Before getting our hands dirty, let's take a look at what are we going to learn.


In order to fully understand the information presented in this tutorial, you need to have the following knowledge:

  • basic Drupal administration including installing Drupal, enabling a module, adding some content
  • basic PHP knowledge
Drupal Hooks

The Drupal codebase is modular and consists of two parts:

  • the core modules (the core)
  • the contributed modules (the modules)

The core provides all the basic Drupal functionalities, and the modules add more features to the base installation. Interaction between modules and the core is done via "hooks".

According to Drupal's documentation:

"A hook is a PHP function that is named foo_bar(), where foo is the name of the module (whose filename is thus foo.module) and bar is the name of the hook."

So, the hook is a function with a special name that is called by the Drupal system in order to allow modules to include their functionality to the core. The file containing these functions also has a special name which allows the core to find all the installed modules.

The Drupal API provides the developer with a large number of hooks with which to alter almost the whole functionality of the core. For a Drupal developer, the sky is the limit for creating a site based on this powerful CMS.

Separating the basic functionality from the auxiliary features enables an increased flexibility on performing administrative tasks such as upgrading Drupal to a newer version. Since the core is somehow independent on modules, this operation can be done just by overriding the core.

What We're Building Today

We need a realistic goal for implementing our module, and I think Facebook integration is the perfect idea. We will allow the user to "like" our articles by adding a Facebook 'Like' button to each of them. The second task of our module will be to show, in the sidebar, which articles were liked by the users.

Obtaining the Data

Since the main focus of this article is on how to implement a Drupal module, we will use the simplest method to retrieve the data from Facebook: Social Plugins. Social Plugins allow users to add Facebook elements to your site with only a single line of code, which can be taken out with copy/paste from the Facebook site.

Our final product will have an output which should look like so:

Step 1: Setup

Drupal searches for contributed modules in the sites/all/modules folder of your Drupal installation. In case an administrator decides to have multiple sites driven by one Drupal installation, the path to the contributed module will look like sites/subdomain/modules.

First, let's choose a name for our module. Since its task will be to add Facebook functionality to our site, I assume "facebook" will be a proper name for it. We can start preparing the required directory structure for developing our module.

Inside sites/all create a subfolder, named modules. We will store our contributed modules in a subfolder named custom. The Facebook module will reside in the facebook subdirectory of the custom directory. The final structure will look like so:

Step 2: Inform Drupal about our Module

Drupal presents the user with a list of core and contributed modules based on the content of sites/all/modules. For each module present, Drupal searches for a file named This file should contain information about the specific module and Drupal displays this information to the user.

Let's create the info file for our module. Create a file, named in the sites/all/modules/custom/facebook folder, and add the following code to it:

; the module's user friendly name, will be displayed in the modules list name = Facebook Recent Activity ; the module's description, will be displayed in the second column in the modules list description = Retrieves and displays in a block the recent activity data from Facebook. ; the module's package, will be the name of the module's group on the modules list page package = Nettuts+ Drupal Module Tutorial ; the Drupal core package core = 7.x ; the files array indicating which files are part of the module files[] = facebook.module

The code above reveals the required information to be placed in an info file. Notice that we've referenced the facebook.module in the files array. This file will contain our module's code. For the moment, go ahead and create an empty file in our folder.

The .info file is a standard .ini file; therefore, the lines starting with ";" are comments.


Now, let's check what we've done so far. Visit your website and select Modules from the upper main menu. The modules list should be displayed, and at the bottom of it, you will find a new module group, named Nettuts+ Tutorial Module containing our module. Check out how the information you've placed in the info file is displayed here.

Step 3: Add the Like Button

We need to add the code provided by Facebook to the Drupal code that processes the node. This can be done by implementing hook_node_view.

As we will find in a moment, implementing hook_node_view requires using the theme function.

A theme function is a Drupal API function that is used to allow desginers to theme the modules as desired. The fact that we will use the function also means that we will have to implement hook_theme too.

Retrieving the Code from Facebook

The Facebook code that displays the 'Like' button on the pages can be obtained here. You can customize the look of the button using the controls on the page. Pressing the Get Code button displays the required XFBML code.

Implement hook_node_view

The hook_node_view returns the renderable view of the nodes (articles or pages for instance). Using this hook, we can add custom code to the one generated by Drupal by default. The implementation of this hook looks like so:

/** * Implements hook_node_view(). * * Returns the renderable view of our nodes (pages or articles). * We want to moddify the code so that we add the like button * to our pages. */ function facebook_node_view($node, $view_mode, $langcode) { $node->content['facebook'] = array( '#markup' => theme('facebook_add_like_button'), ); }

The name of the function is facebook_node_view, so you can deduce that it is composed from the name of our module and the name of the hook.

The parameters are:

  • $node -- contains the node data that will be rendered lately
  • $view_mode -- specifies how the node is displayed (can be full mode, teaser etc.)
  • $langcode -- to control the language code for rendering

We modify only the $node parameter and the result will be that our 'Like' button will appear also when a node teaser is displayed. I leave it as an exercise for you to modify the code to display the Like button only on full pages.

The code adds a new key to the $node->content array, which says that the markup for the facebook module will be rendered by the facebook_add_like_button function.

Implement hook_theme

This hook should be implemented to register the implementation of the theme function.

/** * Implements hook_theme(). * * Just to let Drupal know about our theme function. */ function facebook_theme() { return array( 'facebook_add_like_button' => array('variables' => NULL), ); }

The code returns an array containing the name and parameteres of the function. In our case, we don't require any parameters, so the array of variables is set to NULL.

Implement our theme Function

Finally, we've reached the moment when we can add the code taken from Facebook to our module. This can be done like so:

/** * Function to add the desired code to our page. */ function theme_facebook_add_like_button() { $output = '&lt;div id="fb-root"&gt;&lt;/div&gt;&lt;script src=""&gt;&lt;/script&gt;&lt;fb:like href="" send="true" width="450" show_faces="true" font=""&gt;&lt;/fb:like&gt;'; return $output; }

Note that the function's name is composed from the name registered by theme hook prefixed with theme_. The function returns a string containing the code taken from Facebook.


We can now check to see if everything is okay up to this point. You can activate the module by clicking on Modules from the upper menu, scrolling down to the Facebook module and activating the enable check box in front of our module.

Click on Save configuration button to enable the module. If you do not have any articles added to your site, add some now and check out the spiffy buttons that have been added.

Your posts should look like below:

Step 4: Create the Sidebar Block

Creating the sidebar block consists of two actions:

First, we have to let Drupal know about the existence of a new block and make it appear in the list of available blocks. Second, we have to write the code that displays information in the block.

This assumes implementation of the hooks: the hook_block_info, which will list our block in the block list, and hook_block_view, which contains the necessary code to display the Facebook recent activity inside the block.

Implementing hook_block_info /** * Implements hook_block_info(). * * Using this hook we declare to Drupal that our module * provides one block identified as facebook */ function facebook_block_info() { $blocks['facebook'] = array( 'info' => t('Facebook Recent Activity'), // leave the other properties of the block default ); return $blocks; }

The block_info hook tweaks the $blocks array by adding a new key, named info to it, which contains the text that will be available on the blocks page near our Facebook module.

Implementing hook_block_view

The first thing to do is take the Facebook code, available here. We need to configure the default options: set the width to 170 and the header to false (uncheck the Show header checkbox).

We need a 170px wide block, since this is the standard Drupal block width and we will set up our own text for the header -- therefore, we don't need Facebook's title.

Let's check the code for hook_block_view:

/** * Implements hook_block_view(). * * Returns the renderable view of our block. It takes * the configured values of facebook recent activity * social plugin */ function facebook_block_view($delta = '') { switch($delta) { case 'facebook' : $block['subject'] = t('Facebook recent activity'); $block['content'] = '&lt;script src="">&lt;/script&gt;&lt;fb:activity site="" width="170" height="500" header="false" font="" border_color="#fff" recommendations="false"&gt;&lt;/fb:activity&gt;'; } return $block; }

The block view hook receives a single parameter, which is an indication of which block is rendered. We check to see if the block belongs to our module and, if yes, we add two new items to the $block array:

  • a subject string, which will be the title of the block
  • a content string, which, in our case, is taken from Facebook.

We're almost ready. Enable the module, then navigate to Structure in the main menu, then choose Blocks and scroll down to the disabled list of blocks, and set our Facebook recent activity block to be displayed in the second sidebar (which will appear on right on the default theme).

Next, press the like button for some of the articles previously created. These will appear listed in the sidebar in our freshly created block.

That's it! We're done. The result should look like so:

Wrapping Up

I hope this tutorial convinced you that creating a Drupal module isn't really as hard as you might think. Of course, this tutorial only scratches the surface of what Drupal modules can accomplish. Nonetheless, this should be an excellent starting point!

Thank you so much for reading and let me know if you have any questions!


Envato Tuts+: What's New in Drupal 7

Planet Drupal - Thu, 2021/07/22 - 9:39am

Drupal is one of the most popular content management systems (CMS) out there. To mark the new year, Drupal 7, the next major version of Drupal, is being released! In this article, I'll walk you through some of the most exciting new features.

New Themes

The old themes have been replaced with powerful, new ones.

If you've worked with Drupal 6, you may have noticed the default "Garland" theme looks a bit outdated by now. Furthermore, using Garland for site administration and content editing is, frankly, not very intuitive.

Drupal 7 changes all that! The old themes have been discarded and replaced with a powerful theme trio:

  • Bartik - The attractive new default theme your users will see
  • Seven - The new administrative theme. If you've worked with Drupal 6, you will love this new administrative theme (more about that in a following section).
  • Stark - A blank theme that helps theme developers (aka the themers) understand Drupal's default HTML and CSS

As always, these themes can be replaced by a theme you download and install from or by a custom theme of your own making!

Revamped Admin Interface

One of the most intrinsic functions of any CMS, be it Wordpress, Joomla, or Drupal, is to provide an easy way for end-users to update content. Drupal 6 has some very good administrative themes, such as Rubik, but Drupal 7 makes creating, updating, and editing content far simpler. Take a look at the following short video to get a feel for the new administrative interface:

A video demonstration of the Drupal 7 Administrative Interface Improved Theming Layer

Meaningful HTML is not a strong suit of Drupal 6, but Drupal 7 delivers big-time.

Another important features of any CMS is the ability to take full control over the look and feel of the site you're building. Drupal 6 has a fantastic theming layer, but it does have a few quirks that are ironed out in Drupal 7. As a note, template files in Drupal end with the .tpl.php extension, which is often pronounced "tipple-fip" for brevity.

If you've worked with Drupal 6 themes, perhaps the biggest change you'll notice is the introduction of html.tpl.php, which is used to display the basic html structure of a single Drupal page, including DOCTYPE, head, html, and body. In Drupal 6, page.tpl.php used to include these elements, but is now used specifically to display the content of a single page. This change should free themers from declaring DOCTYPES, head, etc. in multiple files, thus making maintenance and changes simpler.

Unsemantic class names have been renamed. For example, the class block-blog-0 has been renamed block-blog-recent, and block-profile-0 has become block-profile-author-information. While this may seem minor, meaningful and semantic classnames can greatly speed up theme development and make debugging CSS issues clearer.

There's far too much to cover in one small section, from hidden regions to new PHP functions. If you're interested in learning more about changes to the theme layer, check out the following links:

jQuery Updates

For the front-end developers out there, this is a big one. Unfortunately, Drupal 6 still ships with jQuery 1.2.6, and upgrading isn't simple. Luckily, Drupal 7 ships with jQuery 1.4.4, which is significantly faster than jQuery 1.2.6, and provides developers with access to fantastic features such as .delegate() and $.proxy().

Drupal 7 ships with jQuery 1.4.4

In addition to updating jQuery, Drupal 7 will also ship with jQueryUI 1.8. jQueryUI is a smart addition which should help standardize many UI components, such as tabs, drag & drop events, or accordions. There are loads of Drupal modules which try to fulfill these tasks in Drupal 6. Therefore, standardizing around one UI library in Drupal 7 should make front-end development and maintenance easier.

Drupal 7 Ships with CCK

CCK is the Drupal equivalent of WordPress' custom post types

For those unfamiliar with Drupal, CCK stands for Content Construction Kit, and it is one of the coolest features of Drupal. While CCK used to be an add-on module, it is now included with Drupal 7 by default.

Essentially, CCK allows you to quickly create new content types, such as an article, blog post, or even music album. You can easily add fields to your content type using the administrative interface. For example, you could add Album Name, Tracks, Producer and release year to a music album content type. Once the content type is created with the appropriate fields, content contributors can start entering in content while you work on the technical parts of the site! If that explanation didn't get you excited about content types, check out this quick video:

A video demonstrating the Content Construction Kit: RDF Support

Drupal 7 is the first major CMS to implement RDF.

Have you heard of the Semantic Web, otherwise known as the Giant Global Graph? According to Wikipedia, the semantic web is a group of methods and technologies to allow machines to understand the meaning - or 'semantics' - of information on the World Wide Web. In practice, the semantic web should vastly improve search engines, mashups, and data mining.

But what technology is used to implement the semantic web on our sites? That technology is called RDF. Drupal 7 is the first major CMS to implement RDF.

If you haven't heard of RDF yet, and remain unconvinced of its usefulness, I would highly recommend you watch the following video from DrupalCon to get an idea for what RDF can do for your site: The story of RDF in Drupal 7 and what it means for the Web at large.


This article has covered many of the most exciting features of Drupal 7, but there's even more! For those interested in Drupal module development, Fields are being overhauled and should make the creation of modules even simpler. Installation profiles have become easier to create and maintain. What are you favorite features of Drupal 7? Tell us in the comments!

Download Drupal 7.


Envato Tuts+: Top 10 Most Usable Content Management Systems

Planet Drupal - Thu, 2021/07/22 - 9:39am

There are plenty of options when it comes to picking a content management system for a development project. Depending on how advanced you need the CMS to be, what language it's built in, and who is going to be using it, it can be a nightmare trying to find the "perfect" CMS for a project.

However, some CMSs have a slight edge over the rest of the competition because of the usability of the software. Some are just easier to install, use and extend, thanks to some thoughtful planning by the lead developers. 

We have a number of themes and resources to support these top content management systems. If you're looking for Wordpress Themes, Drupal Themes, or Joomla Themes we have you covered on Envato Market

We support a number of additional popular CMS systems. And a gallery of Wordpress Plugins, Drupal Plugins, Joomla plugins and more. Visit our ThemeForest or CodeCanyon marketplaces to browse through a ton of professional options.  

Here are 10 of the most usable CMSs on the web to use in your next project, so you can choose the one that fits your needs best.

1. WordPress

What is there left to say about WordPress that hasn't already been said? The PHP blogging platform is far and away the most popular CMS for blogging, and probably the most popular CMS overall. It's a great platform for beginners, thanks to their excellent documentation

and super-quick installation wizard. Five minutes to a running CMS is pretty good. Not to mention the fact that the newest versions auto-update the core and plugins from within the backend, without having to download a single file.

For those users not familiar with HTML or other markup language, a WYSIWYG editor is provided straight out of the box. The backend layout is streamlined and intuitive, and a new user should be able to easily find their way around the administration section. Wordpres also comes with built-in image and multimedia uploading support.

For developers, the theming language is fairly simple and straightforward, as well the Plugin API.

The WordPress Community is a faithful and zealous bunch. Wordpress probably has the widest base of plugins and themes to choose from. We have thousands of professional Wordpress Themes and Wordpress Plugins available for sale on Envato Market, with a full suite of styles and options to choose from. 

A great part about the Wordpress community is the amount of help and documentation online you can find on nearly every aspect of customizing WordPress. If you can dream it, chances are it's already been done with WordPress and documented somewhere.

If you need help with anything from installing a theme to optimizing the speed of your WordPress site, you can find plenty of experienced WordPress developers to help you on Envato Studio.

2. Drupal

Drupal is another CMS that has a very large, active community. Instead of focusing on blogging as a platform, Drupal is more of a pure CMS. A plain installation comes with a ton of optional modules that can add lots of interesting features like forums, user blogs, OpenID, profiles and more. It's trivial to create a site with social features with a simple install of Drupal. In fact, with a few 3rd party modules you can create some interesting site clones with little effort.

One of Drupal's most popular features is the Taxonomy module, a feature that allows for multiple levels and types of categories for content types. And you can find plenty of professional Drupal Themes, which are ready to be customized and worked with. You can also grab Drupal Plugins.

Drupal also has a very active community powering it, and has excellent support for plugins and other general questions. 

You can also hire a developer to complete a range of tasks for your Drupal site for a reasonable fixed fee.

3. Joomla!

Joomla is a very advanced CMS in terms of functionality. That said, getting started with Joomla is fairly easy, thanks to Joomla's installer. Joomla's installer is meant to work on common shared hosting packages, and is a very straightforward considering how configurable the software is.

Joomla is very similar to Drupal in that it's a complete CMS, and might be a bit much for a simple portfolio site. It comes with an attractive administration interface, complete with intuitive drop-down menus and other features. The CMS also has great support for access control protocols like LDAP, OpenID and even

The Joomla site hosts more than 3,200 extensions, so you know the developer community behind the popular CMS is alive and kicking. Like Wordpress, you can add just about any needed functionality with an extension. However, the Joomla theme and extension community relies more on paid resources, so if you're looking for customizations, be ready to pull out your wallet. You can also grab Joomla plugins, or hire Joomla developers to help you get your store set up right.

4. ExpressionEngine

ExpressionEngine (EE) is an elegant, flexible CMS solution for any type of project. Designed to be extensible and easy to modify, EE sets itself apart in how clean and intuitive their user administration area is. It takes only a matter of minutes to understand the layout of the backend and to start creating content or modify the look. It's fantastic for creating websites for less-than-savvy clients that need to use the backend without getting confused.

ExpressionEngine is packed with helpful features like the ability to have multiple sites with one installation of software. For designers, EE has a powerful templating engine that has custom global variables, custom SQL queries and a built in versioning system. Template caching, query caching and tag caching keep the site running quickly too.

One of my favorite features of EE that is the global search and replace functionality. Anyone who's ever managed a site or blog knows how useful it is to change lots of data without having to manually search and open each page or post to modify it.

ExpressionEngine is quite different than other previously-mentioned CMS in that it's paid software. The personal license costs $99.95, and the commercial license costs $249.99. You can also get help with ExpressionEngine on Envato Studio.

5. TextPattern

Textpattern is a popular choice for designers because of its simple elegance. Textpattern isn't a CMS that throws in every feature it can think of. The code base is svelte and minimal. The main goal of Textpattern is to provide an excellent CMS that creates well-structured, standards-compliant pages. Instead of providing a WYSIWYG editor, Textpattern uses textile markup in the textareas to create HTML elements within the pages. The pages that are generated are extremely lightweight and fast-loading.

Even though Textpattern is deliberately simple in design, the backend is surprisingly easy to use and intuitive. New users should be able to find their way around the administration section easily.

While Textpattern may be very minimal at the core level, you can always extend the functionality by 3rd party extensions, mods or plugins. Textpattern has an active developer community with lots of help and resources at their site.

6. Radiant CMS

The content management systems that we've listed so far are all PHP programs. PHP is the most popular language for web development, but that doesn't mean we should overlook other popular web languages like Ruby. Radiant CMS is a fast, minimal CMS that might be compared to Textpattern. Radiant is built on the popular Ruby framework Rails, and the developers behind Radiant have done their best to make the software as simple and elegant as possible, with just the right amount of functionality. Like Textpattern, Radiant doesn't come with a WYSIWYG editor and relies on Textile markup to create rich HTML. Radiant also has it's own templating language Radius which is very similar to HTML for intuitive template creation.

7. Cushy CMS

Cushy CMS is a different type of CMS altogether. Sure, it has all the basic functionality of a regular content management system, but it doesn't rely on a specific language. In fact, the CMS is a hosted solution. There are no downloads or future upgrades to worry about.

How Cushy works is it takes FTP info and uploads content on to the server, which in turn the developer or the designer can modify the layout, as well as the posting fields in the backend, just by changing the style classes of the styles. Very, very simple.

Cushy CMS is free for anyone, even for professional use. There is an option to upgrade to a pro account to use your own logo and color scheme, as well as other fine-grain customizations in the way Cushy CMS functions.

8. SilverStripe

SilverStripe is another PHP CMS that behaves much like Wordpress, except has many more configurable options and is tailored towards content management, and not blogging. SilverStripe is unique because it was built upon its very own PHP framework Saphire. It also provides its own templating language to help with the design process.

SilverStripe also has some interesting features built in to the base, like content version control and native SEO support. What's really unique with SilverStripe is that developers and designers can customize the administration area for their clients, if need be. While the development community isn't as large as other projects there are some modules, themes and widgets to add functionality. Also, you'll want to modify the theme for each site, as SilverStripe doesn't provide much in terms of style, to give the designer more freedom.

9. Alfresco

Alfresco is a JSP is a beefy enterprise content management solution that is surprisingly easy to install. A really useful feature of Alfresco is the ability to drop files into folders and turn them into web documents. Alfresco might be a little bit more work than some of the other CMS and isn't as beginner-friendly, it certainly is quite usable given the massive power of the system. The administration backend is clean and well-designed.

While Alfresco might not be a great choice for most simple sites, it's an excellent choice for enterprise needs.

10. TYPOlight

TYPOlight seems to have the perfect balance of features built into the CMS. In terms of functionality, TYPOlight ranks with Drupal and ExpressionEngine, and even offers some unique bundled modules like newsletters and calendars. Developers can save time with the built-in CSS generator, and there are plenty of resources for learning more about the CMS.

If there is a downside to TYPOlight, it's that it has so many features and configurable options. Even though the backend is thoughtfully organized, there are still a lot of options to consider. But if you're wanting to build a site with advanced functionality and little extra programming, TYPOlight could be a great fit.


Envato Tuts+: Intro to Drupal: Build a Simple CMS

Planet Drupal - Thu, 2021/07/22 - 9:39am

Drupal's popularity has lately been rising. It's a great platform for setting up content management systems and community driven sites. Here, I'll give a general overview of Drupal and build a simple site for a fake client. We'll begin with outlining the client's needs, installing and configuring a few modules, creating user roles and assigning permissions, and finally we'll add in some content. We won't go into theming, as it's a bit out of the scope of this article.

1. A fake client

Let's start off with a fake client.

SmartMarks is a small marketing consulting firm, with 4 employees. Each employee would like their own blog. The site will need a few pages in addition to the blogs:

  • Home
  • About
  • Contact
  • Links
  • Blogs

Shannon, the business owner, wants full control over the site. The rest of the employees (Bill, Jean, and Terry) should only be able to write blog entries, but Bill may publish links.

The contact us form will accept the user's name, phone, email, and a short message. Submissions of the contact form should be sent only to Shannon.

Sounds pretty simple, huh? Well with Drupal, a lot of this core functionality is already built in. We'll use as much of the core functionality as we can, and we'll add in a few other modules to make building this site a breeze!

2. Install some stuff

First, start out by installing Drupal. I'll be developing this one on my local machine, but you can install it anywhere you wish. For this tutorial, I'll be working with Drupal 6.x.

To install Drupal, simply download ( and unpack it, create your database, and visit http://localhost/ (or your own dev URL). Installation should be relatively simple for ya.

You'll need to create a config file. You can copy /webroot/sites/default/default.settings.php to /webroot/sites/default/settings.php. Be sure to make it writable to the server. Also, leave a copy of sites/default/default.settings.php where it is; Drupal will use it during installation.

After your config file is created, you can go ahead and install Drupal.

On the next screen, you'll setup the first account for the site. This is the main administrator, or super user. This user will have permission to do anything and everything on the site.

And you can go ahead and specify a few server settings. If your server is configured for mod_rewrite URL rewriting, then you can go ahead and enable Clean URLs now. This will change your URLs from something like /?q=node/3 to just /node/3.

After successful installation, you can visit the site and login as the superuser.

3. Get some modules

For this site, we'll be using a few contributed modules. We'll have to download those and activate them before we can use them.

All modules that you'll install will be placed in the directory /webroot/sites/all/modules. If the modules directory doesn't exist there, just make a new one and name it modules.

Make sure to download the modules compatible with the version of Drupal that you're using. I'm using Drupal 6.x.

4. Admin Menu

This module is a must have for working with Drupal. It's not totally necessary, but it will save you loads of time.

Download it over at and place it in /webroot/sites/all/modules

PathAuto & Token

Next, go grab a copy of PathAuto and Token. PathAuto is a module that will have Drupal automatically generate nice URLs. PathAuto requires Token to work.

Meta Tags (Nodewords)

Originally titled NodeWords, the Meta Tags module allows users to specify common meta tags, like meta keywords and meta description.

Get a copy of this module over at

CCK (Content Construction Kit)

CCK allows you to easily create new content types, without ever having to write any code! We'll use this for the company's external links section.

Get CCK at


The views module allows you to configure custom views for displaying content. They're very useful when you have complex content types and categories. Here we'll use Views to display Links.

Get the Views module at

Install some modules

After you've downloaded and unpacked the above modules into /webroot/sites/all/modules, you can go ahead and install them.

Visit http://localhost/admin/build/modules to turn some of them on.

For this site, we'll need to install the following. Simply check the boxes and click "Save configuration".

  • Administration - Administration Menu
  • Core - Blog
  • Core - Contact
  • Core - Path
  • Content - Content
  • Content - Text
  • Other - Meta tags
  • Other - Pathauto
  • Other - Token
  • Views - Views
  • Views - Views UI
5. Content Types

Before we work with users and roles, we'll create our Links content type. Each Link will need a title, URL, and short description.

What's a node?

Almost every piece of content in Drupal is stored as a single node. All nodes have a title and an optional description. By creating content types, you can add fields to the content type to extend the node.

In our case, each Link will need one additional field that's not provided by default, the URL.

Create a Link content type

We'll create a content type called Link. We'll then add a field to the content type called URL.

Visit http://localhost/admin/content/types/add

In the name field, enter the human-readable name. In the type field, enter a unique name for the type. The system will use this name internally. You can make it up, but generally it'll look like a variable name, lowercase and underscored. Also enter a short description of the content type.

Next we'll modify slightly this content type from the general node. In the "Submission form settings" group, instead of "Body", we'll title the body field "Short Description".

Next, we'll edit the "Workflow settings." Allow the link to be published by default, and disable automatic promotion to the front page.

And finally, disable comments on the Links.

Save the content type. If you visit the "Create Content" page, you'll now see the new content type, Link.

Additional fields with CCK

So we've got our base Link content type set up. But we need to add an additional field to each Link: URL. Visit http://localhost/content/types and "Manage fields" for content type Link.

Add a field titled "url", and name it "field_url". Choose text data and text field.

Save it. Another page will come up, with some more options. The defaults are ok for this, so just contine by clicking "Save field settings". After this, the Link content type should appear like this:


Now let's set up a view for our new content type, Links.

Views can become quite complex, but for our example, we'll keep it very simple. We'll make a page view that displays Links. Plain and simple

Visit http://localhost/admin/build/views to get started. Click the tab "Add" to create a new view.

Name the view "Links" and choose type node.

The next few pages can grow quite complex, but be paitent. A bit of practice will get you more comfortable with views.

Firstly, we'll want our Links view to be a full page. So add a page display.

We'll have to make some settings next. Change the name and title of the view to Links. Set the "Row Style" to node, and choose to display teaser and links.

Make sure you're clicking "Update Default Display" every time.

Set the Path to "links". This will be the URL path and our page view will show up at http://localhost/links.

Then set a menu for the view. Choose "Normal menu entry", title it Links, and put it into Primary Links. (More on menus a bit later).

The Basic Settings area should be similar to this by now:

And finally for the view, we'll need to setup a filter. The filter will allow us to restrict the view to only display nodes of type "link".

Add a filter by using the "+" button at the top of the Filters box.

Scroll down until you find the filter titled "Node: Type". Check it's box, then add it as a filter.

Choose a node type of "Link".

At this point, our whole view should look very simliar to the following.

Save the view. We'll come back to it later.

7. Users, Roles, & Permissions

Next we'll set up some user roles and permissions, and then we'll create some real users. Refer to the site requirements above to refresh on what our users need to be able to do.

User settings

Only SmartMarks staff will be able to have accounts. Public registration will not be necessary for this site. So we need to restrict regsitration at http://localhost/admin/user/settings and disallow public registration.


We'll need a couple of roles. Since Shannon wants full control, we'll need an Admin role. Since Bill can modify certain things that others can't we'll setup a Manager role. And finally, the rest of SmartMarks' employees will need to belong to an Employee role.

Visit http://localhost/admin/user/roles to get started.

Create a role titled Admin.

Repeat the process to create two more roles, Manager and Employee. We should have something like this now.


Next, we'll define permissions for each of the roles. Visit http://localhost/admin/user/permissions to set up permissions. Set them up like so.

You may be wondering why we didn't give Manager too many permissions. This is because we'll set Bill to also be part of the Employee role, so Manager simply needs to be able to add and edit links. All of the permissions associated with Employee will be granted to Bill.

Now that we've got roles and permissions going, we can create our sites' users. User Role Shannon Admin Bill Employee, Manager Jean Employee Terry Employee

Go ahead and create these users at http://localhost/user/user/create, assigning roles to each of them. We should end up with something like this on http://localhost/admin/user/user:

8. Creating Content... Finally!

And finally we can start creating content. We're well over halfway done at this point.

Create pages

First off, let's begin with the home page. Visit http://localhost/content/add to create a new Page.

Enter the page title and some sample content for the home page. Set up a menu item for this page. You may also enter some meta tag info if you'd like.

Leave the URL alias setting alone. We'll let Pathauto handle it, and we'll set that up shortly.

Save the page and create another for the About page.

If you now visit the main page, you'll notice that we've got a menu already going. These items come from the pages we just made and from the view we made for Links earlier.

Create some Links

Next, we'll create a few links. Visit the create content page again, but this time choose Link. Create a few links.

After we've created a few links, we can visit the view for Links. Visit http://localhost/links to see our links. Here's what I've got. Remember this is coming from the view we made earlier.

Contact form

Drupal's built-in Contact module is totally sufficient for SmartMarks. We'll just need to set it up. Visit http://localhost/admin/build/contact and click "Add Category" to begin.

Add a category for "General Enquiries", enter Shannon's email as the only recipient, and set "Selected" to yes. This will cause this category to be the default for the contact form. (You could setup multiple categories to handle contact submissions for areas such as Sales, Support, etc.)

You may now view your contact form at http://localhost/contact

Create some blog entries

Last of the content, we'll make a few sample blog entries. We could log out, and then log back in as each user individually, creating a blog entry under each. Or, since you're already logged in as superuser, you can create a few entries and change the author to each user.

Visit http://localhost/node/add/blog and create a sample entry.

Under the authoring info, enter shannon. This entry will become Shannon's first blog entry.

Repeat that to create a blog entry for the other users (bill, terry, jean). Then visit http://localhost/blog to see the user blogs.

9. Finishing touches

We still have a few things to tidy up before we're done. We need to setup pathauto to handle automatic URL aliases, finish our menu, check out each user account, and then we'll add a few blocks to demonstrate a little about blocks.


Let's start with menus. We've already created a few menu items beneath the Primary Links menu. We did this when we created the view for Links and when we created each static page.

Visit http://localhost/admin/build/menu. Here you'll see several menus available. Choose Primary Links.

Choose "Add item". We'll create an item for the Contact form.

Note that the path is relative to the site root. So don't enter "/contact"; just enter "contact".

Repeat the above to create another menu item for user blogs, using a path of "blog". Then visit the tab "List items" to view all the menu items within the Primary Links menu. Now we can reorder the items using the drag and drop handles on the left.

Make sure to save!!!

Now our primary links in the header should be complete and sorted.


Next up, we'll setup pathauto to handle our nice URLs. Visit http://localhost/admin/build/path.

Before we configure paths, let's remove any existing URL aliases. You can do this by visiting the tab "Delete Aliases", then just go ahead and choose all aliases and delete them.

Now we'll setup the automatic aliases under the tab "Automated alias settings".

Open up "Blog path settings" and check the box to have the system "Bulk generate aliases".

Now open up the "Node path settings." Here we'll set up a few rules to handle paths for different node types. Use the replacement patterns (this is where the Token module comes into play) to set up appropriate paths. Make sure your URL alias scheme will produce only unique URLs! And be sure to have it "Bulk generate aliases."

Then save. Then view "List" again. You should see new aliases made for all of our existing content.

A note about URL aliases: Don't forget to delete aliases if you change your URL scheme and regenerate aliases. Deleting and recreating aliases may seem a bit scary, but the more you do it, the more confident you'll become in your URL scheme.


Shannon just informed us that she wants the site to highlight the company's recent office move. This is a good opportunity to go over blocks.

A block in Drupal is simply a 'chunk' of content, be it a list of nodes, some static HTML, a few images, or whatever. We'll set up a simple block to hold the notice about the office move.

Visit http://localhost/admin/build/block. Add a new block by using the tab "Add block".

After adding a block, you'll have to assign it to a region for display. Assign it to the left sidebar on http://localhost/admin/build/block, and don't forget to save!!!

You may also sort them with the drag handles, like menu items.

You should now see the new block displayed in the left sidebar after save.

Check user accounts

Now we're almost there. Just want to login as the users to make sure they've got the right permissions and that they can access the links to allow them to get stuff done.

Log out of the system and log back in as Shannon. Shannon is our administrator, so let's make sure she's able to create/edit all content. After logging in as Shannon, we should see a link to create content.

Go through and login as each user. Just take a look to make sure each one has the permissions and links available to get stuff done. If they don't, try going back to administer user permissions, and verify that they've been granted the correct permissions. Or also make sure you've correctly assigned roles.

Set the home page

We also need to tell Drupal to use our home page as the default home page. You can do this under http://localhost/admin/settings/site-information.

Before you change this data, though, we need to grab the node id of our home page. Visit our welcome page at http://localhost/welcome-smartmarks. Click or mouse-over the "Edit" tab; we just need the node's ID.

Our home page has a node id of 1, so we'll use that for the default home page. Visit http://localhost/admin/settings/site-information and enter some data. At the bottom, you'll see a field for the default home page. Enter "node/1". Note that we're using the node id becuase of pathauto. If we were to change the URL alias of the home page, then we won't have to change its node id, since it will remain the same. Drupal will automatically print out the correct URL alias.

10. Summary

This overview only scratches the surface of what Drupal can do. Hopefully I've given you a good overview of how to get started with the system in building this very simple CMS.


Drupal supports multiple themes, and each user can even chose their own theme.

Building a custom theme is out of the scope of this tutorial. But you can download and install some contributed themes. A good starting place is over at Theme Garden. Download and unpack themes into /webroot/sites/all/themes, then enable and configure them at http://localhost/admin/build/themes. Note that whenever you activate a new theme, you'll have to visit the blocks page to assign blocks to the theme's regions.

If you're ready to start building a theme, you might want to check out the Theme Guide.

Good luck!

Good luck in your Drupal ventures, and feel free to ask questions! Also check out for more information and helpful articles.


Morpht: Layout Paragraphs: A new way to manage Paragraphs

Planet Drupal - Thu, 2021/07/22 - 9:39am
This is a presentation about a relatively new module called Layout Paragraphs. However, it really should be subtitled as "A brief history of laying things out in Drupal". 

Promet Source: A Comprehensive Guide to Drupal Migration Resources

Planet Drupal - Wed, 2021/07/21 - 6:37pm
  Current stats on the high percentage of Drupal websites that are still on version 7, with no apparent migration plan in sight, are sparking concern and curiosity throughout the Drupal sphere. What exactly is standing in the way of decisions to shed this outdated version and start reaping the benefits of better security and a far superior CMS? 

Security advisories: Drupal core - Critical - Drupal core - Critical - Third-party libraries - SA-CORE-2021-004

Planet Drupal - Wed, 2021/07/21 - 5:59pm
Project: Drupal coreDate: 2021-July-21Security risk: Critical 15∕25 AC:Complex/A:User/CI:All/II:All/E:Theoretical/TD:UncommonVulnerability: Drupal core - Critical - Third-party librariesCVE IDs: CVE-2021-32610Description: 

The Drupal project uses the pear Archive_Tar library, which has released a security update that impacts Drupal.

The vulnerability is mitigated by the fact that Drupal core's use of the Archive_Tar library is not vulnerable, as it does not permit symlinks.

Exploitation may be possible if contrib or custom code uses the library to extract tar archives (for example .tar, .tar.gz, .bz2, or .tlz) which come from a potentially untrusted source.

This advisory is not covered by Drupal Steward.


Install the latest version:

Versions of Drupal 8 prior to 8.9.x and versions of Drupal 9 prior to 9.1.x are end-of-life and do not receive security coverage.

Reported By: Fixed By: 
Categories: Drupal 9 Beginners guide for web designers

Planet Drupal - Wed, 2021/07/21 - 3:28pm

Are you a web designer looking into learning Drupal? Maybe you’ve been working with Wordpress for a while and are looking for a CMS that can build more than a simple website with a few pages. Then I got you covered, because in this blogpost I will outline all the different aspects and terminology involved with Drupal.


Drupal Diversity & Inclusion: Call for speakers is open for DDI Camp! We want to hear from YOU!

Planet Drupal - Tue, 2021/07/20 - 9:38pm
Call for speakers is open for DDI Camp! We want to hear from YOU! Alex Laughnan Tue, 07/20/2021 - 12:38

Redfin Solutions: Migrate Drupal WYSIWYG to Paragraphs

Planet Drupal - Tue, 2021/07/20 - 4:47pm
How to build an automated migration that can intelligently divide ambiguous WYSIWYG content into your specific destination paragraphs.