Upgrade Your Drupal Skills

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

See Advanced Courses NAH, I know Enough
Jan 24 2016
Jan 24
[embedded content]

Drupal Console is software which allows you to alter your Drupal installation through the command line. According to the official website, “The Drupal Console is a CLI tool to generate boilerplate code, interact and debug Drupal 8.” Unlike Drush, Drupal Console is specifically for Drupal 8, the latest major release.

Although Drupal Console and Drush share many capabilities such as clearing the cache, generating one-time login links, or un/installing modules/themes, one distinct functionality that comes out of the box with Drupal Console is that it can generate boilerplate code for modules, themes, controllers, forms, blocks, and much more.

Installing Drupal Console

Setting up Drupal Console is just as easy as executing the following commands found on the homepage of the website:

# Run this in your terminal to get the latest project version: curl https://drupalconsole.com/installer -L -o drupal.phar # Or if you don't have curl: php -r "readfile('https://drupalconsole.com/installer');" > drupal.phar # Accessing from anywhere on your system: mv drupal.phar /usr/local/bin/drupal # Apply executable permissions on the downloaded file: chmod +x /usr/local/bin/drupal # Run this in your terminal to get the latest project version:curl https://drupalconsole.com/installer -L -o drupal.phar# Or if you don't have curl:php -r "readfile('https://drupalconsole.com/installer');" > drupal.phar# Accessing from anywhere on your system:mv drupal.phar /usr/local/bin/drupal# Apply executable permissions on the downloaded file:chmod +x /usr/local/bin/drupal

To check if Drupal Console is working, execute drupal list. You should see a list of commands:

$ drupal list Drupal Console version 0.10.5 Usage: command [options] [arguments] Options: ... Available commands: ... module ... multisite ... site site:debug List all known local and remote sites. site:install Install a Drupal project site:new Create a new Drupal project theme ... yaml ... $ drupal listDrupal Console version 0.10.5command [options] [arguments]Available commands:site:debug List all known local and remote sites.site:install Install a Drupal projectsite:new Create a new Drupal project

Downloading Drupal

Let’s start from the beginning. We can actually get any version of Drupal by running drupal site:new:

$ drupal site:new Enter the directory name when downloading Drupal: > drupal Getting releases for Drupal Select a core release: [0 ] 8.0.2 [1 ] 8.0.1 [2 ] 8.0.0 [3 ] 8.0.0-rc4 [4 ] 8.0.0-rc3 [5 ] 8.0.0-rc2 [6 ] 8.0.0-rc1 [7 ] 8.0.0-beta16 [8 ] 8.0.0-beta15 [9 ] 8.0.0-beta14 [10] 8.0.0-beta13 [11] 8.0.0-beta12 [12] 8.0.0-beta11 [13] 8.0.0-beta10 [14] 8.0.0-beta9 > 0 Downloading drupal 8.0.2 [OK] Drupal 8.0.2 was downloaded in directory /home/ubuntu/workspace/drupal $ drupal site:newEnter the directory name when downloading Drupal:Getting releases for DrupalSelect a core release:[0 ] 8.0.2[1 ] 8.0.1[2 ] 8.0.0[3 ] 8.0.0-rc4[4 ] 8.0.0-rc3[5 ] 8.0.0-rc2[6 ] 8.0.0-rc1[7 ] 8.0.0-beta16[8 ] 8.0.0-beta15[9 ] 8.0.0-beta14[10] 8.0.0-beta13[11] 8.0.0-beta12[12] 8.0.0-beta11[13] 8.0.0-beta10[14] 8.0.0-beta9Downloading drupal 8.0.2[OK] Drupal 8.0.2 was downloaded in directory /home/ubuntu/workspace/drupal

Installing Drupal

Now, you might be thinking to go visit the running website to install Drupal. Wrong! Well, right you can do that. But, it is also possible via Drupal Console by simply running drupal site:install inside the drupal directory:

$ drupal site:install Select Drupal profile to be installed: [0] Minimal [1] Standard > 1 Select language for your Drupal installation [English]: > Drupal Database type: [0] MySQL, MariaDB, Percona Server, or equivalent [1] SQLite [2] PostgreSQL > 0 Database Host [127.0.0.1]: > Database Name: > drupal Database User: > {YOUR_DATABASE_USERNAME} Database Pass [ ]: > {YOUR_DATABASE_PASSWORD} Database Port [3306]: > Database Prefix [ ]: > Provide your Drupal site name [Drupal 8 Site Install]: > Drupal Console is Awesome! Provide your Drupal site mail [[email protected]]: > {YOUR_SITE_MAIL} Provide your Drupal administrator account name [admin]: > {YOUR_USER_NAME} Provide your Drupal administrator account mail [[email protected]]: > {YOUR_USER_MAIL} Provide your Drupal administrator account password: > {YOUR_USER_PASSWORD} Starting Drupal 8 install process [OK] Your Drupal 8 installation was completed successfully $ drupal site:installSelect Drupal profile to be installed:[0] Minimal[1] StandardSelect language for your Drupal installation [English]:Drupal Database type:[0] MySQL, MariaDB, Percona Server, or equivalent[1] SQLite[2] PostgreSQLDatabase Host [127.0.0.1]:Database Name:Database User:> {YOUR_DATABASE_USERNAME}Database Pass [ ]:> {YOUR_DATABASE_PASSWORD}Database Port [3306]:Database Prefix [ ]:Provide your Drupal site name [Drupal 8 Site Install]:> Drupal Console is Awesome!Provide your Drupal site mail [admin@example.com]:> {YOUR_SITE_MAIL}Provide your Drupal administrator account name [admin]:> {YOUR_USER_NAME}Provide your Drupal administrator account mail [admin@example.com]:> {YOUR_USER_MAIL}Provide your Drupal administrator account password:> {YOUR_USER_PASSWORD}Starting Drupal 8 install process[OK] Your Drupal 8 installation was completed successfully

Our new Drupal site should now be ready to be worked on:
Drupal Home

Activating Maintenance Mode

It is best to set a website in production to maintenance mode when being worked on. Be sure to log in first, so you can view the development of the site. Then you can turn on maintenance mode with drupal site:maintenance on:

$ drupal site:maintenance on Operating in maintenance mode on Rebuilding cache(s), wait a moment please. [OK] Done clearing cache(s). $ drupal site:maintenance onOperating in maintenance mode onRebuilding cache(s), wait a moment please.[OK] Done clearing cache(s).

Now, this is what regular users will see when visiting the website:

Drupal Maintenance

Creating a Hello World Module

Defining Module Parameters

Drupal Generate Module Directory Structure
First let’s generate code to define the module such as the .info.yml and the composer.json files by running drupal generate:module:

$ drupal generate:module // Welcome to the Drupal module generator Enter the new module name: > Hello World Enter the module machine name [hello_world]: > Enter the module Path [/modules/custom]: > Enter module description [My Awesome Module]: > Say Hello World Enter package name [Custom]: > Enter Drupal Core version [8.x]: > Do you want to generate a .module file (yes/no) [no]: > Define module as feature (yes/no) [no]: > Do you want to add a composer.json file to your module (yes/no) [yes]: > Would you like to add module dependencies (yes/no) [no]: > Do you confirm generation? (yes/no) [yes]: > Generated or updated files Site path: /home/ubuntu/workspace/drupal 1 - modules/custom/hello_world/hello_world.info.yml 2 - modules/custom/hello_world/composer.json $ drupal generate:module// Welcome to the Drupal module generatorEnter the new module name:> Hello WorldEnter the module machine name [hello_world]:Enter the module Path [/modules/custom]:Enter module description [My Awesome Module]:> Say Hello WorldEnter package name [Custom]:Enter Drupal Core version [8.x]:Do you want to generate a .module file (yes/no) [no]:Define module as feature (yes/no) [no]:Do you want to add a composer.json file to your module (yes/no) [yes]:Would you like to add module dependencies (yes/no) [no]:Do you confirm generation? (yes/no) [yes]:Generated or updated filesSite path: /home/ubuntu/workspace/drupal1 - modules/custom/hello_world/hello_world.info.yml2 - modules/custom/hello_world/composer.json

Installing the Module

Again, we can just enable to module using the command line using drupal module:install {PLUGIN_MACHINE_NAME}:

$ drupal module:install hello_world [OK] The following module(s) were installed successfully: hello_world Rebuilding cache(s), wait a moment please. [OK] Done clearing cache(s). $ drupal module:install hello_world[OK] The following module(s) were installed successfully: hello_worldRebuilding cache(s), wait a moment please.[OK] Done clearing cache(s).

Generating the Controller

Now, we need to generate a controller that will show the “Hello World” page. Do this using drupal generate:controller:

$ drupal generate:controller // Welcome to the Drupal Controller generator Enter the module name [hello_world]: > Enter the Controller class name [DefaultController]: > HelloWorldController Enter the Controller method title (leave empty and press enter when done) [ ]: > Hello World Enter the action method name [hello]: > helloWorld Enter the route path [hello_world/hello/{name}]: > /hello/world Enter the Controller method title (leave empty and press enter when done) [ ]: > Do you want to generate a unit test class (yes/no) [yes]: > Do you want to load services from the container (yes/no) [no]: > Do you confirm generation? (yes/no) [yes]: > Generated or updated files Site path: /home/ubuntu/workspace/drupal 1 - modules/custom/hello_world/src/Controller/HelloWorldController.php 2 - modules/custom/hello_world/hello_world.routing.yml 3 - modules/custom/hello_world/Tests/Controller/HelloWorldControllerTest.php Rebuilding routes, wait a moment please [OK] Done rebuilding route(s). $ drupal generate:controller// Welcome to the Drupal Controller generatorEnter the module name [hello_world]:Enter the Controller class name [DefaultController]:> HelloWorldControllerEnter the Controller method title (leave empty and press enter when done) [ ]:> Hello WorldEnter the action method name [hello]:> helloWorldEnter the route path [hello_world/hello/{name}]:> /hello/worldEnter the Controller method title (leave empty and press enter when done) [ ]:Do you want to generate a unit test class (yes/no) [yes]:Do you want to load services from the container (yes/no) [no]:Do you confirm generation? (yes/no) [yes]:Generated or updated filesSite path: /home/ubuntu/workspace/drupal1 - modules/custom/hello_world/src/Controller/HelloWorldController.php2 - modules/custom/hello_world/hello_world.routing.yml3 - modules/custom/hello_world/Tests/Controller/HelloWorldControllerTest.phpRebuilding routes, wait a moment please[OK] Done rebuilding route(s).

Drupal Hello World Directory Structure

This will automatically generate our controller file, routing file, and even our Test file.

Finished Module

Now, if we visit http://www.mydrupalwebsite.com/hello/world, we should see:

Drupal Hello World 1

This way of generating modules is much quicker than normal. You can compare it to normally creating a Hello World module.

Creating a Block

We can also make a block to go along with this module and have it hold a configuration value. This can be done by running drupal generate:plugin:block:

$ drupal generate:plugin:block // Welcome to the Drupal Plugin Block generator Enter the module name [hello_world]: > Plugin class name [DefaultBlock]: > Plugin label [Default block]: > Plugin id [default_block]: > Theme region to render Plugin Block [ ]: > Do you want to load services from the container (yes/no) [no]: > You can add input fields to create special configurations in the block. This is optional, press enter to continue Do you want to generate a form structure? (yes/no) [yes]: > Type [ ]: > textfield Input label: > Content Input machine name [content]: > Maximum amount of characters [64]: > Width of the textfield (in characters) [64]: > Description [ ]: > Content to Display Default value [ ]: > Weight for input item [0]: > Type [ ]: > Do you confirm generation? (yes/no) [yes]: > Generated or updated files Site path: /home/ubuntu/workspace/drupal 1 - modules/custom/hello_world/src/Plugin/Block/DefaultBlock.php Rebuilding cache(s), wait a moment please. [OK] Done clearing cache(s). $ drupal generate:plugin:block// Welcome to the Drupal Plugin Block generatorEnter the module name [hello_world]:Plugin class name [DefaultBlock]:Plugin label [Default block]:Plugin id [default_block]:Theme region to render Plugin Block [ ]:Do you want to load services from the container (yes/no) [no]:You can add input fields to create special configurations in the block.This is optional, press enter to continueDo you want to generate a form structure? (yes/no) [yes]:> textfieldInput label:Input machine name [content]:Maximum amount of characters [64]:Width of the textfield (in characters) [64]:Description [ ]:> Content to DisplayDefault value [ ]:Weight for input item [0]:Do you confirm generation? (yes/no) [yes]:Generated or updated filesSite path: /home/ubuntu/workspace/drupal1 - modules/custom/hello_world/src/Plugin/Block/DefaultBlock.phpRebuilding cache(s), wait a moment please.[OK] Done clearing cache(s).

Drupal Plugin Block Directory Structure

If we now go to Admin > Structure > Block layout > Sidebar second > Place Block, we can see our newly generated block there and add it to the sidebar:

Drupal Place Block
Drupal Configure Block
Drupal Default Block

Generating Random Nodes

If we are just testing our website for development and need some content, nodes can be easily generated using drupal create:nodes:

$ drupal create:nodes // Welcome to the Drupal nodes generator Select content type(s) to be used on node creation: [0] Article [1] Basic page > 0 Enter how many nodes would you like to generate [10]: > Enter the maximum number of words in titles [5]: > How far back in time should the nodes be dated?: [0] N | Now [1] H | 1 hour ago [2] D | 1 day ago [3] W | 1 week ago [4] M | 1 month ago [5] Y | 1 year ago > 5 --------- -------------- ---------------------------- --------------------- Node Id Content type Title Created Time --------- -------------- ---------------------------- --------------------- 1 Article Capto Eros Haero Uxor 2016-01-18 03:23:09 2 Article Nunc Voco 2015-02-08 11:23:37 3 Article Abdo Eu Pala Plaga 2015-10-16 02:26:27 4 Article Commoveo Erat Iustum 2015-09-15 05:52:22 5 Article Olim 2015-08-13 10:50:27 6 Article Comis Damnum Ille Vulpes 2015-12-11 03:19:55 7 Article Gravis Probo 2015-04-16 07:38:44 8 Article Nobis Quidem Torqueo Zelus 2015-10-25 06:47:05 9 Article Commoveo Duis Metuo Quidne 2015-05-30 10:21:16 10 Article Illum Mos Obruo Tamen 2015-04-22 01:47:57 --------- -------------- ---------------------------- --------------------- [OK] Created 10 nodes successfully $ drupal create:nodes// Welcome to the Drupal nodes generatorSelect content type(s) to be used on node creation:[0] Article[1] Basic pageEnter how many nodes would you like to generate [10]:Enter the maximum number of words in titles [5]:How far back in time should the nodes be dated?:[0] N | Now[1] H | 1 hour ago[2] D | 1 day ago[3] W | 1 week ago[4] M | 1 month ago[5] Y | 1 year ago--------- -------------- ---------------------------- ---------------------Node Id Content type Title Created Time--------- -------------- ---------------------------- ---------------------1 Article Capto Eros Haero Uxor 2016-01-18 03:23:092 Article Nunc Voco 2015-02-08 11:23:373 Article Abdo Eu Pala Plaga 2015-10-16 02:26:274 Article Commoveo Erat Iustum 2015-09-15 05:52:225 Article Olim 2015-08-13 10:50:276 Article Comis Damnum Ille Vulpes 2015-12-11 03:19:557 Article Gravis Probo 2015-04-16 07:38:448 Article Nobis Quidem Torqueo Zelus 2015-10-25 06:47:059 Article Commoveo Duis Metuo Quidne 2015-05-30 10:21:1610 Article Illum Mos Obruo Tamen 2015-04-22 01:47:57--------- -------------- ---------------------------- ---------------------[OK] Created 10 nodes successfully

Now, you can see some of these articles posted to the front page with images:

Drupal Front Page

Generating a Theme

Apart from generating modules and content, we can also generate themes. Let’s generate a basic theme that extends the classy base theme by running drupal generate:theme:

$ drupal generate:theme // Welcome to the Drupal theme generator Enter the new theme name []: > New Theme Enter the module machine name [new_theme]: > Enter the theme Path [/themes/custom]: > Enter theme description [My Awesome theme]: > Plain Theme Enter package name [Other]: > Enter Drupal Core version [8.x]: > Base theme (i.e. classy, stable) [bartik]: > classy Enter the global styling library name [global-styling]: > Do you want to generate the theme regions (yes/no) [yes]: > Enter region name [Content]: > Enter region machine name [content]: > Do you want add another region (yes/no) [yes]: > no Do you want to generate the theme breakpoints (yes/no) [yes]: > Enter breakpoint name [narrow]: > Enter breakpoint label [narrow]: > Enter breakpoint media query [all and (min-width: 560px) and (max-width: 850px)]: > Enter breakpoint weight [1]: > Enter breakpoint multipliers [1x]: > Do you want to add another breakpoint (yes/no) [yes]: > no Do you confirm generation? (yes/no) [yes]: > Generated or updated files Site path: /home/ubuntu/workspace/drupal 1 - themes/custom/new_theme/new_theme.info.yml 2 - themes/custom/new_theme/new_theme.theme 3 - themes/custom/new_theme/new_theme.breakpoints.yml $ drupal generate:theme// Welcome to the Drupal theme generatorEnter the new theme name []:> New ThemeEnter the module machine name [new_theme]:Enter the theme Path [/themes/custom]:Enter theme description [My Awesome theme]:> Plain ThemeEnter package name [Other]:Enter Drupal Core version [8.x]:Base theme (i.e. classy, stable) [bartik]:Enter the global styling library name [global-styling]:Do you want to generate the theme regions (yes/no) [yes]:Enter region name [Content]:Enter region machine name [content]:Do you want add another region (yes/no) [yes]:Do you want to generate the theme breakpoints (yes/no) [yes]:Enter breakpoint name [narrow]:Enter breakpoint label [narrow]:Enter breakpoint media query [all and (min-width: 560px) and (max-width: 850px)]:Enter breakpoint weight [1]:Enter breakpoint multipliers [1x]:Do you want to add another breakpoint (yes/no) [yes]:Do you confirm generation? (yes/no) [yes]:Generated or updated filesSite path: /home/ubuntu/workspace/drupal1 - themes/custom/new_theme/new_theme.info.yml2 - themes/custom/new_theme/new_theme.theme3 - themes/custom/new_theme/new_theme.breakpoints.yml

Drupal Theme Directory Structure

Three new files should have been generated in the themes folder. You should now be able to install the theme by running drupal theme:install {THEME_MACHINE_NAME}:

$ drupal theme:install new_theme The New Theme theme has been installed successfully Rebuilding cache(s), wait a moment please. [OK] Done clearing cache(s). $ drupal theme:install new_themeThe New Theme theme has been installed successfullyRebuilding cache(s), wait a moment please.[OK] Done clearing cache(s).

The theme still needs to be set as default before we can see it in action. However, it will just be a plain style-less theme because it just extends classy:

Drupal New Theme

Deactivating Maintenance Mode

As we are done with our development, stop maintenance mode by running drupal site:maintenance off:

$ drupal site:maintenance off Operating in maintenance mode off Rebuilding cache(s), wait a moment please. [OK] Done clearing cache(s). $ drupal site:maintenance offOperating in maintenance mode offRebuilding cache(s), wait a moment please.[OK] Done clearing cache(s).

This post just covered the surface of what all Drupal Console can do. It can do so much more such as generating Forms, Permissions, and even most of the features from Drush. Remember to explore all the commands by running drupal list. It is a tool every Drupal developer should be taking advantage of. You can learn more of the commands and shortcuts on the Drupal Console Documentation.

Share this:

Author: Akshay Kalose

A teenager, who is interested in Computer Science, Information Technology, Programming, Web Designing, Engineering and Physical Sciences.

Jan 09 2016
Jan 09
[embedded content]

Drupal 8 came out with many new features and updates at the end of 2015. As Drupal 8 is object oriented and enforces PSR-4 standards, the way you make modules has significantly changed. However, this change makes modules much more organized to fit today’s coding practices. I will be demonstrating how to create a simple “Hello World!” module in Drupal 8.

Getting Started

I will be assuming you already have a working Drupal 8 website.

Creating The Module Directory

Drupal Module Directory Structure 1Drupal 8 has a much cleaner folder structure than before. All the modules will go in the module folder. Create a hello_world folder here. This will serve as the machine name for the module and will contain all the necessary files for our module to function.

Defining Parameters

Module Information

First, parameters such as title and description need to be set. These keys will be defined in the file hello_world.info.yml. Unlike Drupal 7, Drupal 8 uses YAML (YAML Ain’t Markup Language) for these types of files. Create this file and enter these definitions:

name: Hello World type: module description: Say Hello World package: Custom core: 8.x name: Hello Worldtype: moduledescription: Say Hello Worldpackage: Custom
  • name: Hello World
    • This is the title of our module shown on the extend page.
  • type: module
    • This is to tell Drupal what we are making is a module.
  • description: Say Hello World
    • This is a description shown alongside the title on the extend page.
  • package: Custom
    • This is what category our module will be listed under on the extend page.
  • core: 8.x
    • This tells Drupal our module is compatible with Drupal 8.x core.

You will now be able to see the module listed under the Custom package category:

Drupal Module List

Let’s enable this module:

Drupal Module Enabled

Module Routes

Next, we will need to tell Drupal where the module can be accessed. This is done by creating a route for our module. Create the file hello_world.routing.yml and enter the following parameters:

hello_world: path: /hello/world defaults: _controller: Drupal\hello_world\Controller\HelloWorldController::hello requirements: _permission: 'access content' hello_world:    path: /hello/world    defaults:        _controller: Drupal\hello_world\Controller\HelloWorldController::hello    requirements:        _permission: 'access content'
  • path: /hello/world
    • This tells Drupal what path will be used to access our module.
  • _controller: Drupal\hello_world\Controller\HelloWorldController::hello
    • This is the method Drupal will call to process a request to our path.
  • _permission: 'access content'
    • This is to ensure only users who can access content will be able to see our Hello World page.

Quotes are not required around the values, however, you can include them to be safe.

Drupal Module Directory Structure 2

Your current directory structure should now look like this:

Coding the Module

After defining all the parameters for the module, we still need to add functionality to our module. We need to write code that will live in a “Controller”. Since Drupal 8 follows PSR-4 standards, there is a specific directory structure to follow. All code will exist inside the src folder. After creating this, make a Controller folder. This is where the PHP Class will be made. Here, create HelloWorldController.php and define the as so:

<?php class HelloWorldController { } class HelloWorldController {

Any Drupal 8 class will also need to be namespaced. This is just to make sure the right classes are referenced from outside if some classes have the same name and it makes it more organized. Add this above the class definition:

<?php namespace Drupal\hello_world\Controller; class HelloWorldController { } namespace Drupal\hello_world\Controller;class HelloWorldController {

Remember in the routing file we told Drupal to call the hello method in the class Drupal\hello_world\Controller\HelloWorldController. Let’s define this in our class:

<?php namespace Drupal\hello_world\Controller; class HelloWorldController { public function hello() { } } namespace Drupal\hello_world\Controller;class HelloWorldController {    public function hello() {

Since Drupal 8 is based on the Symfony Framework, a new Symfony\Component\HttpFoundation\Response object can be returned. However, since this is Drupal, you can also return a render array which Drupal will process and automatically add theming. For our simple page, we can return an array with keys #title and #markup:

<?php namespace Drupal\hello_world\Controller; class HelloWorldController { public function hello() { return array( '#title' => 'Hello World!', '#markup' => 'Here is some content.', ); } } namespace Drupal\hello_world\Controller;class HelloWorldController {    public function hello() {        return array(                '#title' => 'Hello World!',                '#markup' => 'Here is some content.',            );

Finishing Up

Before we visit our custom page, we need to clear the cache, specifically the router cache. Otherwise, you will see a 404 Not Found page. You can clear the cache via Drupal Console:

Drupal Router Rebuild

Or through the Admin Control Panel:

Drupal Clear All Caches

Now, if we visit http://www.mydrupalwebsite.com/hello/world, we can see our custom page:

Drupal Hello World

The Full Module

Drupal Module Directory Structure FinalHere is the final directory structure and code of our Hello World module:

*/

That’s it! Creating Drupal modules is as easy as that! Now that you
know how to define module parameters and set up your own Controller to handle specific route requests, you can move on to create more complex Drupal modules using other components such as Forms and implementing Services.

Share this:

Author: Akshay Kalose

A teenager, who is interested in Computer Science, Information Technology, Programming, Web Designing, Engineering and Physical Sciences.

Dec 26 2015
Dec 26

It has been a long time coming. With over 200 new features developed during the last 5 years by more than 2,000 contributors, the most anticipated version of Drupal was finally announced November 19th, 2015. This new release of Drupal contains spectacular features worth upgrading for than any previous release. The improvements evident in Drupal 8 justify that this is one of the best and largest updates in Drupal history.

User Interface and Experience

User

Posts and Pages

Drupal 8 makes use of a library called CKEditor in the admin panel. This library allows you to edit new posts and pages in a rich WYSIWYG editor unlike before. Also, thanks to “Quick Edit”, content is now able to be changed right in the front-end when you are viewing the post.

Mobile-First

As more and more websites are being accessed from mobile phones, it is necessary that websites are first made to support mobile and then desktop. This is exactly what Drupal 8 is doing. Drupal 8 is responsive both in the front-end and admin panel. Images and the site design will rearrange depending on device width. All themes including the defaults, Bartik and Seven, work seamlessly on mobile devices, tablets, and desktops.

Multilingual

Internationalization is now built into Drupal. This means that no contributed modules are needed for translation. All the modules are included in a fresh install of Drupal 8. Everything is translatable such as Content, Blocks, Menus, Views, Comments, Feeds and more. You can choose which language to use on your Drupal website on the first page of the installation process and the translations will be automatically downloaded.

Developers

Code

Front-end

Drupal 8 has switched its templating engine to Twig from PHPTemplate which has a more friendly syntax for designers. HTML5 Forms are also included which means mobile users can be displayed a variety of keyboards for different types of input. Some of the new fields include Date, Email, Link, Reference, and Telephone.

Object Oriented PHP

Drupal 8 has adopted the latest best practices for PHP. It uses PHP 5.4+, Classes and Interfaces, Namespaces, Traits, Dependency Injection, and conforms to the PSR-4 standards for file paths to autoload classes. Drupal 8 is built on top of several proven Symfony 2 modules and utilizes several other libraries such as PHPUnit, Composer, and jQuery. To create modules, themes, blocks and more you will now need to use classes and new info files in YAML.

Configuration Management

There is now a central location to store and retrieve saved information and configurations. This will improve organization and simplify creating modules for Drupal 8. These configurations can be easily exported and imported into another Drupal 8 website.

Anyone running Drupal 6 or 7 is encouraged to update to Drupal 8, especially if you are running Drupal 6 because this will no longer be supported. Since no more updates will be provided, your website will be vulnerable to security risks in the future. Regardless of security risks, these spectacular features should be enough to convince you to upgrade to Drupal 8!

For more information about Drupal 8, Click Here. This post was created during Google Code-in 2015, an open source competition for high school students.

Share this:

Author: Akshay Kalose

A teenager, who is interested in Computer Science, Information Technology, Programming, Web Designing, Engineering and Physical Sciences.

Jan 17 2015
Jan 17

Drupal Form Ajax Example

Why reload the whole page, when you can just update certain parts of the DOM? Ajax allows you to do just this, to dynamically update content. Just one of the many great uses of Ajax is Form Validation. In this example, we will see how to implement this.

We will be making a simple form which will contain a text field that will validate if the username entered exists, and a button that will replace the text field value with a random existing username.

Building The Form

First, we need to define our two form elements:

$form['user_name'] = array( '#type' => 'textfield', '#title' => 'Username', '#description' => 'Please enter in a username', ); $form['random_user'] = array( '#type' => 'button', '#value' => 'Random Username', ); $form['user_name'] = array(  '#type' => 'textfield',  '#title' => 'Username',  '#description' => 'Please enter in a username',$form['random_user'] = array(  '#type' => 'button',  '#value' => 'Random Username',

Next, to start using Ajax in Drupal, all you need to specify is the “callback”, or function to call, when the “event”, or trigger, is fired on that certain form element, in an array under the “#ajax” key:

$form['user_name'] = array( '#type' => 'textfield', '#title' => 'Username', '#description' => 'Please enter in a username', '#ajax' => array( // Function to call when event on form element triggered. 'callback' => 'Drupal\ajax_example\Form\AjaxExampleForm::usernameValidateCallback', // Javascript event to trigger Ajax. Currently for: 'onchange'. 'event' => 'change', ); $form['user_name'] = array(  '#type' => 'textfield',  '#title' => 'Username',  '#description' => 'Please enter in a username',  '#ajax' => array(    // Function to call when event on form element triggered.    'callback' => 'Drupal\ajax_example\Form\AjaxExampleForm::usernameValidateCallback',    // Javascript event to trigger Ajax. Currently for: 'onchange'.    'event' => 'change',

In the “callback”, include the full namespaced class and function you want to call. The event can be any Javascript event without the “on”. A list of Javascript events can be found here.

Once you have added these two keys, you can add extra options such as “effect”, and “progress”. More options can be found on the Ajax API. Here are the finished elements:

$form['user_name'] = array( '#type' => 'textfield', '#title' => 'Username', '#description' => 'Please enter in a username', '#ajax' => array( // Function to call when event on form element triggered. 'callback' => 'Drupal\ajax_example\Form\AjaxExampleForm::usernameValidateCallback', // Effect when replacing content. Options: 'none' (default), 'slide', 'fade'. 'effect' => 'fade', // Javascript event to trigger Ajax. Currently for: 'onchange'. 'event' => 'change', 'progress' => array( // Graphic shown to indicate ajax. Options: 'throbber' (default), 'bar'. 'type' => 'throbber', // Message to show along progress graphic. Default: 'Please wait...'. 'message' => NULL, ), ), ); $form['random_user'] = array( '#type' => 'button', '#value' => 'Random Username', '#ajax' => array( 'callback' => 'Drupal\ajax_example\Form\AjaxExampleForm::randomUsernameCallback', 'event' => 'click', 'progress' => array( 'type' => 'throbber', 'message' => 'Getting Random Username', ), ), ); $form['user_name'] = array(  '#type' => 'textfield',  '#title' => 'Username',  '#description' => 'Please enter in a username',  '#ajax' => array(    // Function to call when event on form element triggered.    'callback' => 'Drupal\ajax_example\Form\AjaxExampleForm::usernameValidateCallback',    // Effect when replacing content. Options: 'none' (default), 'slide', 'fade'.    'effect' => 'fade',    // Javascript event to trigger Ajax. Currently for: 'onchange'.    'event' => 'change',    'progress' => array(      // Graphic shown to indicate ajax. Options: 'throbber' (default), 'bar'.      'type' => 'throbber',      // Message to show along progress graphic. Default: 'Please wait...'.      'message' => NULL,    ),$form['random_user'] = array(  '#type' => 'button',  '#value' => 'Random Username',  '#ajax' => array(    'callback' => 'Drupal\ajax_example\Form\AjaxExampleForm::randomUsernameCallback',    'event' => 'click',    'progress' => array(      'type' => 'throbber',      'message' => 'Getting Random Username',    ),

Creating The Callbacks

After creating our form elements, it is time to create the callback functions which will return the response of what to update on the page.

These callbacks will return an instance of \Drupal\Core\Ajax\AjaxResponse. Each AjaxResponse instance will contain jQuery commands that will execute on the form. You can use the “addCommand()” method on AjaxResponse to add commands that implement \Drupal\Core\Ajax\CommandInterface.

Some commands such as CssCommand and ChangedCommand did not work. Thankfully, there is InvokeCommand which allows you to run any jQuery command. You can construct it with a jQuery selector, method, and arguments:

public InvokeCommand::__construct($selector, $method, array $arguments = array()) public InvokeCommand::__construct($selector, $method, array $arguments = array())

Here are the two callbacks for our form:

public function usernameValidateCallback(array &$form, FormStateInterface $form_state) { // Instantiate an AjaxResponse Object to return. $ajax_response = new AjaxResponse(); // Check if Username exists and is not Anonymous User (''). if (user_load_by_name($form_state->getValue('user_name')) && $form_state->getValue('user_name') != false) { $text = 'User Found'; $color = 'green'; } else { $text = 'No User Found'; $color = 'red'; } // Add a command to execute on form, jQuery .html() replaces content between tags. // In this case, we replace the desription with wheter the username was found or not. $ajax_response->addCommand(new HtmlCommand('#edit-user-name--description', $text)); // CssCommand did not work. //$ajax_response->addCommand(new CssCommand('#edit-user-name--description', array('color', $color))); // Add a command, InvokeCommand, which allows for custom jQuery commands. // In this case, we alter the color of the description. $ajax_response->addCommand(new InvokeCommand('#edit-user-name--description', 'css', array('color', $color))); // Return the AjaxResponse Object. return $ajax_response; } public function randomUsernameCallback(array &$form, FormStateInterface $form_state) { // Get all User Entities. $all_users = entity_load_multiple('user'); // Remove Anonymous User. array_shift($all_users); // Pick Random User. $random_user = $all_users[array_rand($all_users)]; // Instantiate an AjaxResponse Object to return. $ajax_response = new AjaxResponse(); // ValCommand does not exist, so we can use InvokeCommand. $ajax_response->addCommand(new InvokeCommand('#edit-user-name', 'val' , array($random_user->get('name')->getString()))); // ChangedCommand did not work. //$ajax_response->addCommand(new ChangedCommand('#edit-user-name', '#edit-user-name')); // We can still invoke the change command on #edit-user-name so it triggers Ajax on that element to validate username. $ajax_response->addCommand(new InvokeCommand('#edit-user-name', 'change')); // Return the AjaxResponse Object. return $ajax_response; } public function usernameValidateCallback(array &$form, FormStateInterface $form_state) {  // Instantiate an AjaxResponse Object to return.  $ajax_response = new AjaxResponse();  // Check if Username exists and is not Anonymous User ('').   if (user_load_by_name($form_state->getValue('user_name')) && $form_state->getValue('user_name') != false) {    $text = 'User Found';    $color = 'green';  } else {    $text = 'No User Found';    $color = 'red';  // Add a command to execute on form, jQuery .html() replaces content between tags.  // In this case, we replace the desription with wheter the username was found or not.  $ajax_response->addCommand(new HtmlCommand('#edit-user-name--description', $text));  // CssCommand did not work.  //$ajax_response->addCommand(new CssCommand('#edit-user-name--description', array('color', $color)));  // Add a command, InvokeCommand, which allows for custom jQuery commands.  // In this case, we alter the color of the description.  $ajax_response->addCommand(new InvokeCommand('#edit-user-name--description', 'css', array('color', $color)));  // Return the AjaxResponse Object.  return $ajax_response;public function randomUsernameCallback(array &$form, FormStateInterface $form_state) {  // Get all User Entities.  $all_users = entity_load_multiple('user');  // Remove Anonymous User.  array_shift($all_users);  // Pick Random User.  $random_user = $all_users[array_rand($all_users)];  // Instantiate an AjaxResponse Object to return.  $ajax_response = new AjaxResponse();  // ValCommand does not exist, so we can use InvokeCommand.  $ajax_response->addCommand(new InvokeCommand('#edit-user-name', 'val' , array($random_user->get('name')->getString())));  // ChangedCommand did not work.  //$ajax_response->addCommand(new ChangedCommand('#edit-user-name', '#edit-user-name'));  // We can still invoke the change command on #edit-user-name so it triggers Ajax on that element to validate username.  $ajax_response->addCommand(new InvokeCommand('#edit-user-name', 'change'));  // Return the AjaxResponse Object.  return $ajax_response;

Finished Form

Here is our finished Ajax Example Form:

Drupal Form Ajax Example

This blog post was created for Google Code-In 2014 to learn about a Drupal Core System.

Full Module Code

*/

Share this:

Author: Akshay Kalose

A teenager, who is interested in Computer Science, Information Technology, Programming, Web Designing, Engineering and Physical Sciences.

Jan 09 2015
Jan 09
[embedded content]

RDF UI is a module for Drupal 8 created by Sachini Aparna Herath for her Google Summer of Code 2014 project. RDF stands for Resource Description Framework; it provides a standardized model for data interchange. This module enables you to easily create mappings of Schema.org Things to Drupal Content Types and Fields. RDF UI will embed these specified mappings in the HTML as RDFa once your content is published. This blog post was made for Google Code-In 2014 to test and review RDF UI.

Installation

RDF UI can be easily installed using the Drush command line tool for Drupal. You may want to select installing the development version, as that will be the closest to running with the latest Drupal 8 development version:

drush dl rdfui --select drush dl rdfui --select

Upgrading

This section is about my experience upgrading RDF UI to the latest Drupal 8. This module was created over the summer, when GSoC takes place. As a result, it wasn’t compatible with the latest Drupal 8 development release, Beta 4.

I started upgrading from the stable version, and by the time I realized there was a development version, I had already done much of the upgrading, so I integrated the changes from that branch into my upgrade while still keeping most of mine similar to how it was before.

Some of the things I had to upgrade were: moving the attached CSS to a library, re-factoring the Form State from an array to an object, and accounting for the fact that Drupal\views_ui\OverviewBase was merged into Drupal\views_ui\DisplayOverviewBase. I also have included minor improvements here and there in the code.

One problem I encountered while using the sub module was that the default Schema.org/Text Data Type was text. However in the latest Drupal 8, this is the formatted text, and the plain text was needed, so this was fixed by changing the default from “text” to “string“:

diff --git a/rdf_builder/src/Form/ContentBuilderForm.php b/rdf_builder/src/Form/ContentBuilderForm.php index 6d2001a..c3cc50c 100644 --- a/rdf_builder/src/Form/ContentBuilderForm.php +++ b/rdf_builder/src/Form/ContentBuilderForm.php @@ -64,8 +64,8 @@ class ContentBuilderForm extends FormBase { public function __construct() { $this->converter = new SchemaOrgConverter(); $this->datatype_field_mappings = array( - 'http://schema.org/Text' => 'text', - 'http://schema.org/PostalAddress' => 'text_long', + 'http://schema.org/Text' => 'string', + 'http://schema.org/PostalAddress' => 'string_long', 'http://schema.org/Number' => 'integer', 'http://schema.org/MediaObject' => 'file', 'http://schema.org/AudioObject' => 'file', @@ -470,6 +468,6 @@ class ContentBuilderForm extends FormBase { return $this->datatype_field_mappings[$datatype]; } } - return 'text'; + return 'string'; } } diff --git a/rdf_builder/src/Form/ContentBuilderForm.php b/rdf_builder/src/Form/ContentBuilderForm.phpindex 6d2001a..c3cc50c 100644--- a/rdf_builder/src/Form/ContentBuilderForm.php+++ b/rdf_builder/src/Form/ContentBuilderForm.php@@ -64,8 +64,8 @@ class ContentBuilderForm extends FormBase {   public function __construct() {     $this->converter = new SchemaOrgConverter();     $this->datatype_field_mappings = array(-      'http://schema.org/Text' => 'text',-      'http://schema.org/PostalAddress' => 'text_long',+      'http://schema.org/Text' => 'string',+      'http://schema.org/PostalAddress' => 'string_long',       'http://schema.org/Number' => 'integer',       'http://schema.org/MediaObject' => 'file',       'http://schema.org/AudioObject' => 'file',@@ -470,6 +468,6 @@ class ContentBuilderForm extends FormBase {         return $this->datatype_field_mappings[$datatype];       }     }-    return 'text';+    return 'string';

The full upgrade patch can be viewed at this Issue on RDF UI.

Usage

As said on the project page, integration of Schema.org mappings in Content Types is seamless. In the “Add Content Type” form you can choose which Schema.org Type this Content Type will be:RDF UI Add Content Type

Once you fill this out and reach the “Manage Fields” page, you need to create you new fields. You can then click the “RDF Mappings” tab to assign these fields their Schema.org property:

RDF UI Manage Fields

Now you are ready to go ahead and create your content. Once created and published, the node should show the fields and the html should contain the Schema.org Type in the article tag and Schema.org Properties in the field-items divisions:

RDF UI Awesome Event

RDF UI Builder!

RDF UI also comes with a very helpful sub module named: RDF UI Builder. Want to shorten up all the steps above in creating Schema.org mapped Content Types? This sub module comes in handy for that very purpose.

Once this module is enabled, you can find the new “+ Add Schema.org Content Type” button next to the original “+ Add content type” button:

RDF UI Add Schema.org Content Type

After selecting which Schema.org Type you want to use, you are redirected to the next page where the only thing you need to do is select which fields you want, and they will automatically be created and mapped for you!

RDF UI Choose Fields RDF UI Content Type Event Created

That’s it, now you can go off and create content for that type.

Conclusion

Sachini Aparna Herath, with the help of her mentors Stéphane Corlosquet and Kevin Oleary, has created a great module for Drupal 8. RDF UI fits in with the rest of Drupal, and can be used to quickly create content types or fields and assign them Schema.org Types and Properties. This can help any site owner to provide “semantic rich data” on their web pages.

One improvement I can suggest is that the http://schema.org/Date Type should default as date only in Drupal. As of right now both http://schema.org/DateTime and http://schema.org/Date convert into Drupal datetime. This may be because Drupal does not have Date and DateTime options in the drop down. If this is the case, this improvement would be for Drupal Core to move selecting DateTime or only Date to the main select menu before selecting Date and then choosing for a new field.

From a Google Code-In perspective, this task had many obstacles which I had to go through to upgrade the module to be working with the latest Drupal 8 version, Beta 4, and I am glad as I keep learning more with the more problems I face. It is also the first time I have created a change record, because one of the errors received had not been included in the list of changes.

Share this:

Author: Akshay Kalose

A teenager, who is interested in Computer Science, Information Technology, Programming, Web Designing, Engineering and Physical Sciences.

Dec 31 2014
Dec 31
[embedded content]

As your website grows, there will be a point when there are more people accessing your web server than possible for a single server to handle. This is when load balancing will become a critical step in your Drupal setup. Load balancing increases reliability of your application in case a web server goes down and spreads the load across multiple web servers. In this tutorial, we are going to use HAProxy as a Layer 4 Load Balancer for our Drupal website. We will have a proxy server, two web servers, and one database server, all running on Ubuntu 14.04 LTS (Trusty Tahr) 32-bit.

Load Balancing Diagram 2

The public will be able to access the proxy server; this server will then use an algorithm to redirect the user to a web server which will access the database server if needed and respond with generated web page or content requested.

In a production environment, there would be separate physical servers for each proxy, web, and database. However, for the sake of simplicity and availability, I will be using virtual machines.

Setting Up Vagrant

Vagrant is an easy tool you can use to quickly “create and configure lightweight, reproducible, and portable development environments.” Along with this, you will need to install VirtualBox to emulate these virtual machines.

Go ahead and create a folder for our Vagrant environment. For example, I will create mine at: C:\Users\{user-name}\Documents\Vagrant\drupal. Note: this directory will not contain any Drupal files.

Open up command prompt and run this command to download the Ubuntu 14.04 32-bit box:

vagrant box add ubuntu/trusty32 vagrant box add ubuntu/trusty32

This will download the pre-configured box for VirtualBox which will reduce the time required to create our later virtual machines.

In this directory, lets initialize Vagrant. To do this, run:

vagrant init ubuntu/trusty32 --minimal vagrant init ubuntu/trusty32 --minimal

Load Balancing Vagrant Init

This will create a configuration file called “Vagrantfile” in that directory. This file will define basic settings for our virtual machines such as box, hostname, and public/private IP. Throughout this tutorial, we will keep adding configurations for the different servers we are going to set up.

Setting Up Web Server 1

Lets get started with this Load Balancing Setup, but starting with both of our web servers. These web servers will be the actual computers processing the web page, while the proxy server will just forward the requests.

To start, lets edit our Vagrantfile to look like this:

Vagrant.configure(2) do |config| # Sets up all virtual machines using this box. config.vm.box = "ubuntu/trusty32" # Agent forwarding over SSH connections is enabled config.ssh.forward_agent = true config.vm.define "web1" do |web1| # Sets hostname of server. config.vm.hostname = "web1" # Allows access to server from host machine. # 127.0.0.1:8884 (Your computer) ---> 192.168.50.4:80 (Server) config.vm.network :forwarded_port, guest: 80, host: 8884 # Sets IP for private network with other servers. config.vm.network "private_network", ip: "192.168.50.4", # Set name of the internal network. virtualbox__intnet: "intnet" end end Vagrant.configure(2) do |config|  # Sets up all virtual machines using this box.  config.vm.box = "ubuntu/trusty32"  # Agent forwarding over SSH connections is enabled  config.ssh.forward_agent = true  config.vm.define "web1" do |web1|      # Sets hostname of server.      config.vm.hostname = "web1"   # Allows access to server from host machine.   # 127.0.0.1:8884 (Your computer) ---> 192.168.50.4:80 (Server)   config.vm.network :forwarded_port, guest: 80, host: 8884   # Sets IP for private network with other servers.      config.vm.network "private_network", ip: "192.168.50.4",   # Set name of the internal network.      virtualbox__intnet: "intnet"

We will set the option to set up all our virtual machines as Ubuntu 14.04. We will give this specific virtual machine, a host name of web1. Forwarding ports allows a machine only connected to a private network, and not a public network to be accessed by the host computer. So for example, we set up the port 8884 to redirect to port 80 on the guest. As a result, if we visit 127.0.0.1:8884 on our host computer, it will be forwarded to port 80 of that virtual machine. We have set the private IP as 192.168.50.4. This is the type of IP that web1 and all other servers to be set up will use to communicate with each other. We have set the internal network name to “intnet” which we will need to keep constant throughout all servers.

Once you have saved this, go ahead and start up this server using this command:

vagrant up web1 vagrant up web1

This will import the Ubuntu box, and configure the server according to our settings in Vagrantfile. This will take longer the first time since it has to get everything set up.

Load Balancing Vagrant Up Web1

Once the virtual machine has booted up you can ssh into it using:

vagrant ssh web1 vagrant ssh web1

Load Balancing Vagrant SSH Web1

Installing Dependencies

Then lets update the package list and install the required software:

sudo apt-get update sudo apt-get install nginx php5-fpm php5-mysql php5-gd sudo apt-get updatesudo apt-get install nginx php5-fpm php5-mysql php5-gd

Before we start with downloading and running Drupal, we need to first configure PHP and Nginx.

Configuring PHP

Lets start with editing the configuration of php5-fpm:

sudo nano /etc/php5/fpm/php.ini sudo nano /etc/php5/fpm/php.ini

In this file, find the option cgi.fix_pathinfo. By default, this value is set to 1. However, this is a security risk since you can access a file by typing in something that is close to it. Uncomment and change the value to 0:

cgi.fix_pathinfo=0 cgi.fix_pathinfo=0

Now you can save and exit out of this file since we are done with it.

Next, lets edit pool.d/www.conf:

sudo nano /etc/php5/fpm/pool.d/www.conf sudo nano /etc/php5/fpm/pool.d/www.conf

Here, if not set to this value already, change the listen value to the php5-fpm unix domain socket:

listen = /var/run/php5-fpm.sock listen = /var/run/php5-fpm.sock

Save and exit. Now lets restart the php5-fpm process for these changes to take effect:

sudo service php5-fpm restart sudo service php5-fpm restart

Configuring Nginx

It is time to configure our web server, Nginx. To start, lets make a copy of the default site config in the available sites. Then, lets edit it:

sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/drupal sudo nano /etc/nginx/sites-available/drupal sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/drupalsudo nano /etc/nginx/sites-available/drupal

Now make changes accordingly so your drupal virtual hosts file looks like this in the server block(Note: only replace corresponding lines with lines below, do not simply copy and paste the code):

server { # Listen for requests in port 80. listen 80; # Comment out IPv6 line. #listen [::]:80 default_server ipv6only=on; # Root of where our Drupal files will be located. root /var/www/drupal; # Order in which to look for index files. index index.php index.html index.htm; location / { # First attempt to serve request as file, then # as directory, then fall back to displaying a 404. try_files $uri $uri/ =404; # Uncomment to enable naxsi on this location # include /etc/nginx/naxsi.rules } # redirect server not found pages to /404.html error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } location ~ \.php$ { fastcgi_split_path_info ^(.+\.php)(/.+)$; # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini try_files $uri =404; # With php5-cgi alone: #fastcgi_pass 127.0.0.1:9000; # With php5-fpm: fastcgi_pass unix:/var/run/php5-fpm.sock; fastcgi_index index.php; include fastcgi_params; } } # Listen for requests in port 80. listen 80; # Comment out IPv6 line. #listen [::]:80 default_server ipv6only=on; # Root of where our Drupal files will be located. root /var/www/drupal; # Order in which to look for index files. index index.php index.html index.htm; location / { # First attempt to serve request as file, then # as directory, then fall back to displaying a 404. try_files $uri $uri/ =404; # Uncomment to enable naxsi on this location # include /etc/nginx/naxsi.rules # redirect server not found pages to /404.html error_page 404 /404.html; # redirect server error pages to the static page /50x.html error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; location ~ \.php$ { fastcgi_split_path_info ^(.+\.php)(/.+)$; # NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini try_files $uri =404; # With php5-cgi alone: #fastcgi_pass 127.0.0.1:9000; # With php5-fpm: fastcgi_pass unix:/var/run/php5-fpm.sock; fastcgi_index index.php; include fastcgi_params;

This will listen to all requests coming from port 80. We will also comment out the IPv6 line. Change the root to where you will have your Drupal files. I have set my root to /var/www/drupal. Uncomment the 404 and 50x error lines. Comment out the line under “# With php5-cgi alone” since we have installed php5-fpm. Add the line above “# With php5-cgi alone” in the example to immediately return a 404 if an exact match is not found.

Once you have completed this, lets remove the default virtual host from the enabled sites, and make a symbolic link of drupal to the enabled sites from the available sites:

sudo rm /etc/nginx/sites-enabled/default sudo ln -s /etc/nginx/sites-available/drupal /etc/nginx/sites-enabled/ sudo rm /etc/nginx/sites-enabled/defaultsudo ln -s /etc/nginx/sites-available/drupal /etc/nginx/sites-enabled/

Now that we have configured everything, lets restart the nginx service:

sudo service nginx restart sudo service nginx restart

Preparing Drupal

Lets go into our home directory and download the latest version of Drupal. I will be downloading 8.0.0-beta4. You can find the latest Drupal releases here.

cd ~ wget http://ftp.drupal.org/files/projects/drupal-8.0.0-beta4.tar.gz wget http://ftp.drupal.org/files/projects/drupal-8.0.0-beta4.tar.gz

Once this is finished downloading, we can extract the contents:

tar xzvf drupal-8.0.0-beta4.tar.gz tar xzvf drupal-8.0.0-beta4.tar.gz

After the contents are extracted, we can now create the directory we defined as root in nginx. Lets create this folder and copy all the Drupal files to there:

sudo mkdir -p /var/www/drupal sudo cp -r ~/drupal-8.0.0-beta4/* /var/www/drupal sudo mkdir -p /var/www/drupalsudo cp -r ~/drupal-8.0.0-beta4/* /var/www/drupal

Setting Permissions

Lets set the owner of all these files to our user account, in this case “vagrant”, and change the group to “www-data”, the web server user:

sudo chown -R vagrant:www-data /var/www/drupal sudo chown -R vagrant:www-data /var/www/drupal

Now lets also add our user to the www-data group, and give that group permission to read and write to all files:

sudo usermod -a -G www-data vagrant sudo chmod -R g+rw /var/www/drupal sudo usermod -a -G www-data vagrantsudo chmod -R g+rw /var/www/drupal

You should now be able to access the Drupal installation page by visiting 127.0.0.1:8884 which we set up in the port forwarding setting in the Vagrantfile. However, don’t install Drupal just yet! We still need to sync up both web servers.

Setting Up Web Server 2

There are two ways you can go about creating web server 2. You can either repeat the process of setting up web server 1 or you can create a clone of web server 1 and have vagrant set up the replica. I will be going with the latter.

First, lets create a replica box. To do this, halt your first webserver:

vagrant halt web1 vagrant halt web1

Then, lets create a package of that virtual machine:

vagrant package web1 vagrant package web1

This command will package everything in web1 into your Vagrant directory as package.box.

Load Balancing Vagrant Package Web1

Now, in your Vagrantfile, go ahead and add the configuration for web2:

config.vm.define "web2" do |web2| # Custom box name config.vm.box = "drupal-nginx-php" # Location of packaged box config.vm.box_url = "file:///C:/Users/{user-name}/Documents/Vagrant/drupal/package.box" config.vm.hostname = "web2" config.vm.network :forwarded_port, guest: 80, host: 8886 config.vm.network "private_network", ip: "192.168.50.5", virtualbox__intnet: "intnet" end   config.vm.define "web2" do |web2|   # Custom box name      config.vm.box = "drupal-nginx-php"   # Location of packaged box   config.vm.box_url = "file:///C:/Users/{user-name}/Documents/Vagrant/drupal/package.box"      config.vm.hostname = "web2"   config.vm.network :forwarded_port, guest: 80, host: 8886      config.vm.network "private_network", ip: "192.168.50.5",      virtualbox__intnet: "intnet"

You can give the box we just packaged a name like “drupal-nginx-php”. Set “config.vm.box_url” as the location of the package.box. We will be giving this machine a hostname of web2, port forwarding HTTP IP of 8886, and a private network IP of 192.168.50.5.

Now, lets start both of our servers again:

vagrant up web1 web2 vagrant up web1 web2

Synchronizing Drupal Between Web Servers

We need to synchronize the Drupal folder between both servers so if an image or module gets uploaded on one server, it will not be broken if another user connecting to the other servers is looking for that content.

There are many solutions to syncing file on two different servers such as GlusterFS, and NFS. While using GlusterFS, the core directory in Drupal always replied with a Input/output error. As a result, I will be using Unison to sync files across web1 and web2. As an alternative you can also use a script I wrote that utilize inotifywait and scp to sync files.

The benefit of using inotifywait with scp is that it will only sync whenever there is a file modified, created, deleted, or moved. However, this option is unidirectional, so you will need to run it on both servers. On the other hand, Unison is bidirectional and can run on one server only.

Syncing with Unison

Start by installing unison on both machines:

sudo apt-get install unison sudo apt-get install unison

Once installed, run the command which will create the folder “~/.unison”:

Now, on web1, lets make a copy the default profile and edit our drupal profile:

cp ~/.unison/default.prf ~/.unison/drupal.prf nano ~/.unison/drupal.prf cp ~/.unison/default.prf ~/.unison/drupal.prfnano ~/.unison/drupal.prf

Make your drupal profile for Unison look like this:

# Folders to sync root = /var/www/drupal root = ssh://[email protected]//var/www/drupal # batch mode: ask no questions at all batch = true # automatically accept default (nonconflicting) actions auto = true # synchronize modification times times = true # synchronize owner owner = true # synchronize group attributes group = true # Folders to syncroot = /var/www/drupal# batch mode: ask no questions at allbatch = true# automatically accept default (nonconflicting) actionsauto = true# synchronize modification timestimes = true# synchronize ownerowner = true# synchronize group attributesgroup = true

Save and exit. You can find more settings in the Unison User Manual.

You only need one server running Unison since it syncs bidirectionally, so you only need to create this profile file on one machine. The root variables in this file will tell unison which folders to sync. Since the second folder to sync is on another server, we give it a absolute folder via ssh.

To sync both files you can run:

unison drupal unison drupal

The first time you run this, you might get a prompt asking you whether you want to add web2’s ECDSA key fingerprint to your known hosts. Type “yes”. It will also for the first time create archives of all the files which will take some time depending on the amount of files you have. For Drupal 8, it should take a minute or less.

Load Balancing Unison Drupal

However, there is a problem. This will only sync once and exit. We want unison to keep syncing the files. For this, we can use the -repeat flag.

Still there is an issue if we just run this from our SSH client. First, once we start Unison with repeat, it will start, but we cannot run any other command without stopping Unison. Second, we need to keep our terminal open at all times. If we close our client, a HUP or hangup will be sent to the server, and Unison will be stopped.

The Solution? We can use screen. We can start unison in screen and detach from it. Even if we exit our client, we can go back into the screen and see output from Unison.

If you do not already have screen installed:

sudo apt-get install screen sudo apt-get install screen

Next you can start a new screen by typing:

It will look like your screen was cleared. Now lets starts unison with repeat mode set to watch, or whenever a file is updated:

unison drupal -repeat watch unison drupal -repeat watch

Load Balancing Unison Drupal Repeat Watch

If repeat by watch does not work, you can set a value for how many seconds it should wait to sync again:

unison drupal -repeat 1 unison drupal -repeat 1

Load Balancing Unison Drupal Repeat 1

The above will sync the folders every second. You should keep it low when configuring Drupal, however once you have set up Drupal and don’t need constant syncing, you can increase the amount of time between each sync, set a cron job, or manually run the command whenever you update one server.

You can disconnect from the screen by keying Ctrl + A + D. This will keep the process running. If you ever want to reattach to the screen, get the screen id from the list:

screen -list screen -list

Then, use the beginning characters of the screen to reattach:

screen -r {id} screen -r {id}

Note: If you only have one screen open, using -r will automatically reattach you to that screen.

Syncing with drupal_sync (inotifywait + scp)

If you want to use my Bash script you will first need to install inotify-tools:

sudo apt-get install inotify-tools sudo apt-get install inotify-tools

Once you have done this, transfer this file over to your home (~) directory through SFTP or another method:

#!/bin/bash SYNC_DIR="/var/www/drupal" REMOTE_USER="vagrant" REMOTE_HOST="192.168.50.5" LAST_MODIFY_TIME=0 do_start() { inotifywait -mr $SYNC_DIR --timefmt '%Y%m%d%H%M%S' --format '%T %e %w%f' -e create,modify,attrib,move,delete | while read time event file; do case $event in CREATE|MODIFY|MOVE_TO|CREATE,ISDIR) if [ $event = MODIFY ]; then if [ $LAST_MODIFY_TIME -ne 0 ]; then if [ $(($time - $LAST_MODIFY_TIME)) -lt 5 ]; then continue; fi; fi LAST_MODIFY_TIME=$time; if [ "$(ssh [email protected]$REMOTE_HOST 'cat $file' | diff - $file)" >/dev/null ]; then continue fi fi if [ $event = CREATE,ISDIR ]; then exists=$(ssh [email protected]$REMOTE_HOST "[ -d $file ] && echo 1 || echo 2") if [ $exists = 1 ]; then continue fi fi scp -rp $file [email protected]$REMOTE_HOST:$file ;; ATTRIB|ATTRIB,ISDIR) perm=$(stat -c "%U %G" $file) permr=$(ssh [email protected]$REMOTE_HOST "stat -c '%U %G' $file") if [ "$perm" != "$permr" ]; then echo $perm | while read owner group; do ssh [email protected]$REMOTE_HOST "chown $owner:$group $file"; done fi ;; MOVE_FROM|DELETE|DELETE,ISDIR) exists=$(ssh [email protected]$REMOTE_HOST "test -e $file && echo 1 || echo 0") if [ $exists = 1 ]; then ssh [email protected]$REMOTE_HOST "rm -r $file" fi ;; esac done & echo "Drupal Sync Started" } do_stop() { if [ -z "$(pgrep inotifywait)" ]; then echo "Drupal Sync Process Never Started" else pkill inotifywait echo "Drupal Sync Process Killed" fi } show_status() { if [ -z "$(pgrep inotifywait)" ]; then echo "Drupal Sync is Not Running" else echo "Drupal Sync is Running" fi } case "$1" in start) do_start ;; stop) do_stop ;; status) show_status ;; restart|force-reload) do_stop do_start ;; *) echo "Usage: drupal_sync {start|stop|status|restart|force-reload}" >&2 exit 3 ;; esac #!/bin/bashSYNC_DIR="/var/www/drupal"REMOTE_USER="vagrant"REMOTE_HOST="192.168.50.5"LAST_MODIFY_TIME=0do_start() inotifywait -mr $SYNC_DIR --timefmt '%Y%m%d%H%M%S' --format '%T %e %w%f' -e create,modify,attrib,move,delete | while read time event file; do case $event in CREATE|MODIFY|MOVE_TO|CREATE,ISDIR) if [ $event = MODIFY ]; then if [ $LAST_MODIFY_TIME -ne 0 ]; then if [ $(($time - $LAST_MODIFY_TIME)) -lt 5 ]; then continue; fi; LAST_MODIFY_TIME=$time; if [ "$(ssh [email protected]$REMOTE_HOST 'cat $file' | diff - $file)" >/dev/null ]; then if [ $event = CREATE,ISDIR ]; then exists=$(ssh $REMOTE_USER@$REMOTE_HOST "[ -d $file ] && echo 1 || echo 2") if [ $exists = 1 ]; then scp -rp $file $REMOTE_USER@$REMOTE_HOST:$file ATTRIB|ATTRIB,ISDIR) perm=$(stat -c "%U %G" $file) permr=$(ssh $REMOTE_USER@$REMOTE_HOST "stat -c '%U %G' $file") if [ "$perm" != "$permr" ]; then echo $perm | while read owner group; do ssh $REMOTE_USER@$REMOTE_HOST "chown $owner:$group $file"; done MOVE_FROM|DELETE|DELETE,ISDIR) exists=$(ssh $REMOTE_USER@$REMOTE_HOST "test -e $file && echo 1 || echo 0") if [ $exists = 1 ]; then ssh $REMOTE_USER@$REMOTE_HOST "rm -r $file" echo "Drupal Sync Started" if [ -z "$(pgrep inotifywait)" ]; then echo "Drupal Sync Process Never Started" pkill inotifywait echo "Drupal Sync Process Killed"show_status() if [ -z "$(pgrep inotifywait)" ]; then echo "Drupal Sync is Not Running" echo "Drupal Sync is Running"case "$1" in  start)  status) show_status  restart|force-reload) echo "Usage: drupal_sync {start|stop|status|restart|force-reload}" >&2

Edit the variables at the top of the script to your server settings.

Before we can run this script, we need to add permissions to execute this file:

chmod +x ~/drupal_sync chmod +x ~/drupal_sync

Now transfer it over to your other server with scp:

scp ~/drupal_sync [email protected]:~/drupal_sync scp ~/drupal_sync vagrant@192.168.50.5:~/drupal_sync

Note: you will also need to edit the variabled at the top of the script on the other server, web2, so that the “$REMOTE_HOST” is set to the private IP of web1.

We can run the file, however every time it will prompt you for the other server’s ssh password. To allow automatic access we need to make a pair of SSH keys:

ssh-keygen -t rsa ssh-keygen -t rsa

Do not give a name, or pass-phrase, just press enter.

Once you have generated a pair of RSA keys, we need to add the public key to the authorized keys of the other server:

cat ~/.ssh/id_rsa.pub | ssh [email protected] 'cat >> ~/.ssh/authorized_keys' cat ~/.ssh/id_rsa.pub | ssh vagrant@192.168.50.5 'cat >> ~/.ssh/authorized_keys'

Now, after you complete this command by typing in the password of [email protected] again, the next time you ssh you should automatically be connected:

ssh [email protected] ssh vagrant@192.168.50.5

If you are taken to a new SSH screen, congratulations it has worked, to exit back into your other server:

Now repeat this process of generating RSA keys from web2 to web1, so web2 can access web1 without being prompted for a password.

Once both of your servers can SSH to each other automatically, you can start the drupal sync script on both servers:

~/drupal_sync start ~/drupal_sync start

Setting Up The Database Server

Now that we have successfully set up and synced both of our web servers, web1 and web2, it is time to set up the database server that is going to be accessed by both.

Start by adding the virtual machine settings in Vagrantfile:

config.vm.define "db" do |db| config.vm.hostname = "db" config.vm.network "private_network", ip: "192.168.50.6", virtualbox__intnet: "intnet" end   config.vm.define "db" do |db|      config.vm.hostname = "db"      config.vm.network "private_network", ip: "192.168.50.6",      virtualbox__intnet: "intnet"

We will give this server a host name of db and a private IP of 192.168.50.6.

Lets get the database server up and running:

vagrant up db vagrant up db

Installing MySQL

Once the virtual machine has booted, lets ssh into the server, update package list, and install MySQL server:

vagrant ssh db sudo apt-get update sudo apt-get install mysql-server vagrant ssh dbsudo apt-get updatesudo apt-get install mysql-server

While this is installing, it will ask you to make and confirm your root user password, make this complex!

Load Balancing Database Root Password

Once this has finished install, we need to run the actual MySQL installation:

sudo mysql_install_db sudo mysql_install_db

By default, many things will be included in this install for testing. To be more secure, run this command:

sudo mysql_secure_installation sudo mysql_secure_installation

Load Balancing Database Secure Installation

Configuring MySQL

As of right now, MySQL will only accept connections coming from the same computer, so we need to allow for it to accept connections from outside by enabling remote access.

Lets edit the MySQL configuration file:

sudo nano /etc/mysql/my.cnf sudo nano /etc/mysql/my.cnf

Under the section [mysqld] change the value of bind-address from localhost to the private IP of the db server.

bind-address = 192.168.50.6 bind-address        = 192.168.50.6

When you are finished, save and close out of the editor. Next, lets restart the MySQL server for the changes to take effect:

sudo service mysql restart sudo service mysql restart

Creating MySQL Database & Users

Now that we have MySQL listening to a private IP that our web servers can access, we need to create MySQL users for our web servers.

Log in to MySQL as the root user with the password we set earlier:

mysql -u root -p mysql -u root -p

First, lets create our drupal database:

CREATE DATABASE drupal CHARACTER SET utf8 COLLATE utf8_general_ci; CREATE DATABASE drupal CHARACTER SET utf8 COLLATE utf8_general_ci;

Now lets create users for both web servers:

CREATE USER 'drupal_user'@'192.168.50.4' IDENTIFIED BY 'password'; CREATE USER 'drupal_user'@'192.168.50.5' IDENTIFIED BY 'password'; CREATE USER 'drupal_user'@'192.168.50.4' IDENTIFIED BY 'password';CREATE USER 'drupal_user'@'192.168.50.5' IDENTIFIED BY 'password';

The IP following the name of your user should be the private IP’s of your web servers. I have set the password as “password” for both my users, however you should change it to something much more secure.

The last thing we need to do is grant privileges to these users at their respective IP address. For Drupal, you need to give these privileges to your users:

GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, CREATE TEMPORARY TABLES ON drupal.* TO 'drupal_user'@'192.168.50.4'; GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, CREATE TEMPORARY TABLES ON drupal.* TO 'drupal_user'@'102.168.50.5'; GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, CREATE TEMPORARY TABLES ON drupal.* TO 'drupal_user'@'192.168.50.4';GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER, CREATE TEMPORARY TABLES ON drupal.* TO 'drupal_user'@'102.168.50.5';

Once done, flush privileges and exit:

FLUSH PRIVILEGES; exit FLUSH PRIVILEGES;

Our database server, db, is now configured and ready to be used by web1 and web2.

Setting Up The Proxy / Load Balancing Server

The only server left to set up is the load balancing server which acts as a proxy to both web servers. This may be the last to be set up, but certainly the most critical in this setup.

Lets add the server config to our Vagrantfile:

config.vm.define "balancer" do |balancer| config.vm.hostname = "balancer" config.vm.network :forwarded_port, guest: 80, host: 9090 # We want to be assinged a public IP config.vm.network "public_network" config.vm.network "private_network", ip: "192.168.50.7", virtualbox__intnet: "intnet" end   config.vm.define "balancer" do |balancer|      config.vm.hostname = "balancer"      config.vm.network :forwarded_port, guest: 80, host: 9090      # We want to be assinged a public IP      config.vm.network "public_network"      config.vm.network "private_network", ip: "192.168.50.7",      virtualbox__intnet: "intnet"

This server will have a host name of balancer. We have also declared a private IP of 192.168.50.6 and forward port 80 to port 9090 in our local machine. Once important different between the rest of the other server and this server is the fact we set in the configuration that we want to be connected to the public network. This will automatically assign and public IP to the server. We can then access this server at that IP from our host machine.

Lets start up the balancer server:

vagrant up balancer vagrant up balancer

Since we have set this server to connect to the public network, it might ask you to choose an adapter. Try the first one, if it doesn’t work, halt and up balacer again, then choose the other option/s. For me, my Wi-Fi Adapter worked.

Finding the Public IP

Once the server has booted up, notice the order of your network adapters, it should look something like this:

==> balancer: Preparing network interfaces based on configuration... balancer: Adapter 1: nat balancer: Adapter 2: bridged balancer: Adapter 3: intnet ==> balancer: Preparing network interfaces based on configuration...    balancer: Adapter 1: nat    balancer: Adapter 2: bridged    balancer: Adapter 3: intnet

Find where the “bridged” adapter is. Mine is set as bridged for Adapter 2.

Next, lets ssh into the balancer server.

vagrant ssh balancer vagrant ssh balancer

To get our IP configuration, lets run this command:

Load Balancing ifconnfig

Since my second adapter was the bridged network, I will look at the second link: eth1 for my IP address. It will be printed after “inet addr”. My public IP for the balancer is 192.168.1.110. This will be the IP we will use to connect to the balancer/proxy in our browser.

Installing HAProxy

Now, lets update package list, and install haproxy:

sudo apt-get update sudo apt-get install haproxy sudo apt-get updatesudo apt-get install haproxy

Configuring HAProxy

Lets make it so that HAProxy will start on every boot of the server:

sudo nano /etc/default/haproxy sudo nano /etc/default/haproxy

In this file, change the value of “ENABLED” to 1.

Save this file and exit.

Now, it’s time to set up the main HAProxy configuration:

sudo nano /etc/haproxy/haproxy.cfg sudo nano /etc/haproxy/haproxy.cfg

We will let the global settings stay the same. Under “defaults”, change the mode and option from “http” to “tcp”:

defaults ... mode tcp option tcplog ... mode    tcp option  tcplog

Replacing “http” with “tcp” will tell HAProxy that we are going to be using a Layer 4 Load Balancing.

Next, we need to make a frontend, the balancer, with the address to listen, and which backend to point to. The frontend is the server the public can access, and the backend are servers that cannot be directly accessed, like web1 and web2. Add the www frontend:

frontend www bind 192.168.1.110:80 default_backend drupal-backend frontend www bind 192.168.1.110:80 default_backend drupal-backend

You want to bind the public IP with port 80. So, whenever someone requests at port 80 to that public IP, it will use the default backend, “drupal-backend”. Now, lets define “drupal-backend”:

backend drupal-backend balance roundrobin mode tcp server webserver1 192.168.50.4:80 check server webserver2 192.168.50.5:80 check backend drupal-backend balance roundrobin server webserver1 192.168.50.4:80 check server webserver2 192.168.50.5:80 check

Here, we have defined that we want to use the round robin algorithm to chose a server for the request. There are many algorithms you can choose from. Here are a few:

  • roundrobin – “Each server is used in turns, according to their weights.”
  • leastconn – “The server with the lowest number of connections receives the connection.”
  • source – “The source IP address is hashed and divided by the total weight of the running servers to designate which server will receive the request. This ensures that the same client IP address will always reach the same server as long as no server goes down or up.”
  • uri – “This algorithm hashes either the left part of the URI (before the question mark) or the whole URI (if the “whole” parameter is present) and divides the hash value by the total weight of the running servers. … This ensures that the same URI will always be directed to the same server as long as no server goes up or down.”

You can find more algorithms in the HAProxy Configuration Manual.

Again, the mode as “tcp” will specify we want Layer 4 and not Layer 7 Load Balancing. We also define our two servers with names “webserver1” and “webserver2”. The check is added at the end to check the health of the server before sending the request there. If one server fails, the other server will be sent all the requests.

We are done configuring HAProxy, so lets save and exit.

HAProxy Logging

If you want to enable logging, you need to edit /etc/rsyslog.conf:

sudo nano /etc/rsyslog.conf sudo nano /etc/rsyslog.conf

Find and uncomment the first two lines, then add the third line:

$ModLoad imudp $UDPServerRun 514 $UDPServerAddress 127.0.0.1 $ModLoad imudp$UDPServerRun 514$UDPServerAddress 127.0.0.1

Logging will now be enabled, and HAProxy logs can be viewed at: /var/log/haproxy.log once started.

When you are done editing this file, save and exit. Now, restart both rsyslog and haproxy services:

sudo service rsyslog restart sudo service haproxy restart sudo service rsyslog restartsudo service haproxy restart

Installing Drupal

We are finally at the last step of getting Drupal working on a Load Balanced setup. It is time to install Drupal.

You should now be able to see the Drupal installation page when you visit the public IP of your load balancing server from your host machine.

Load Balancing Drupal Installation

Once you click “Save and continue” on the Profile selection page, settings.php and services.yml should appear in /var/www/drupal/sites/default/.

Load Balancing Drupal Sites Default Permissions

By default, Drupal will not be able to edit these as necessary because it does not have permission to write to these files as it’s user www-data. Since we set the group permissions to www-data, we need to give the group write permissions on these files:

sudo chmod g+w /var/www/drupal/sites/default/settings.php sudo chmod g+w /var/www/drupal/sites/default/services.yml sudo chmod g+w /var/www/drupal/sites/default/settings.phpsudo chmod g+w /var/www/drupal/sites/default/services.yml

Once done with the process, it is recommended you remove write permissions to keep your site secure:

sudo chmod g-w /var/www/drupal/sites/default/settings.php sudo chmod g-w /var/www/drupal/sites/default/services.yml sudo chmod g-w /var/www/drupal/sites/default/settings.phpsudo chmod g-w /var/www/drupal/sites/default/services.yml

You can now continue with the installation. Once the database options come up, you need to specify the private IP, database, user, and password we made during the MySQL Database server setup.

Load Balancing Drupal Database Configuration

Again, follow the configuration of Drupal, and you should soon come to your new Drupal install on a Layer 4 Load Balanced Setup using HAProxy.

Load Balancing Drupal Home Page

Conclusion

There you go, we now have a fully functional Layer 4 Load Balacing Setup using 4 servers running Drupal. As your traffic increases, this setup will reduce load on your servers by spreading your users out to different servers. It can also be fail-safe. If one day you wake up to find one of your servers down, throughout the night all your traffic was being sent to the other available server/s, so your users wouldn’t have experienced any downtime with your website.

To test if the load balancing is working and/or with your algorithm of choice, you can view the logs we enabled during the HAProxy server setup at /var/log/haproxy.log.

This blog post was made for Google Code-In 2014. This task has by far been the most challenging, and I have truly expanded my knowledge on Linux servers and Unix commands.

Troubleshooting

If you try running the command vagrant, and you get a response that says the command could not be found, try appending the directory of where vagrant.exe exists to the “PATH” variable in your System/User Environment Variables.

If you are having trouble when running vagrant ssh, this is because there is no ssh command on Windows by default. Luckily, if you have git, you can add the {git-directory}/bin to your System/User Evironment Variables to get access to many commands such as ssh, and ls on Windows. if you do no have git installed, you can use PuTTY to SSH to your server at 127.0.0.1:2200, or the assigned forwarded port for SSH.

If you get 50x errors while visiting your HTTP forwarded ports on web1 or web2, and have this in your nginx logs: “*1 connect() to unix:/var/run/php5-fpm.sock failed (13: Permission denied)”, you need to edit /etc/php5/fpm/pool.d/www.conf and uncomment these lines:

listen.owner = www-data listen.group = www-data listen.mode = 0660 listen.owner = www-datalisten.group = www-datalisten.mode = 0660

If you get PHP Fatat errors such as: “Unexpected [, expecting )”, this is because you have installed a version of php5-fpm below PHP 5.4. This error means your PHP version does not support short-hand array notation in PHP. Solution is to find a PPA to update your PHP version to 5.4+ or update to Ubuntu 14.04 which should have PHP 5.4+ with php5-fpm.

Sources

Share this:

Author: Akshay Kalose

A teenager, who is interested in Computer Science, Information Technology, Programming, Web Designing, Engineering and Physical Sciences.

Dec 25 2014
Dec 25
[embedded content]

FluxPocket is a module created by Umar Ahmad for the FluxKraft distribution of Drupal. FluxKraft is an easy tool you can use for self-hosted, social automation. However, it does not support Pocket by default.  As a result, Umar Ahmad made the FluxKraft module as part of his Google Summer of Code 2014 project. This blog post was made for Google Code-In 2014 to test and review FluxPocket.

Installation

The installation process is very simple. The module can be cloned and enabled with no errors. The instructions are clearly written in the USAGE.md file. The most complicated part of the installation can be attributed to running commands:

drush composer-json-rebuild drush composer-manager update drush composer-json-rebuilddrush composer-manager update

FluxPocket Installation

If the second command’s output is “Killed“, you can restart your Apache/PHP server to fix this and finish installing all dependencies.

Configuration

All Pocket integration is nicely fitted in with the other services available in FluxKraft such as Twitter, Facebook, Flickr, and DropBox. You can easily follow FluxKraft or FluxPocket instructions to install and configure Pocket service such as creating a new Pocket app, and enabling access to the app from your Pocket account. However, one problem is after adding the service account under Configuration -> Web services -> Service accounts -> Add account, the pocket account needs to be added again by clicking Service accounts (at top right of screen) -> Add account. Other than this, configuration is very smooth.

FluxPocket Configuration

Usage

Adding new rules to FluxKraft with FluxPocket is seamless. Many events and actions are detailed in the GitHub repo, and all seem to work great! Examples of events are: if new URL is added to Pocket, or if URL is added to favorites in Pocket. Examples of actions are: Add URL to Pocket, Archive URL in Pocket, Add/Replace/Remove tags of a URL in Pocket.

FluxPocket Create New Article

FluxPocket Pocket Article

Conclusion

Lastly, it can be said that the FluxPocket module is a solid addition to the FluxKraft distribution of Drupal. Events and actions that are provided by this module work great in integrating Pocket with the other services available by default.

Share this:

Author: Akshay Kalose

A teenager, who is interested in Computer Science, Information Technology, Programming, Web Designing, Engineering and Physical Sciences.

Dec 24 2014
Dec 24
[embedded content]

Drupal faced one of its biggest security vulnerabilities recently. It was so bad, it was dubbed “Drupalgeddon”. It affected every single site that was running Drupal 7.31 (latest at the time) or below, as you can read in this Security Advisory.

The exploit could be executed via SQL Injection. The problem was in the expandArguments() method in abstract DatabaseConnection class (\drupal7\includes\database\database.inc) extending PDO. In this method, under the nested foreach loop iterating over the $data array as $i => $value, the $i variable is never sanitized as it is assumed to be a incrementing integer. However, this is not actually the case when posting inputs with the name attribute as arrays, such as:

<input type="text" name="pass[pass1]" value="Password 1"> <input type="text" name="pass[pass2]" value="Password 2">

The above will be posted as:

$_POST = array( ... 'pass' => array( 'pass1' => 'Password 1', 'pass2' => 'Password 2', ), ... ); $_POST = array(    ...    'pass' => array(        'pass1' => 'Password 1',        'pass2' => 'Password 2',     ),    ...

In this case, you will actually be iterating over the pass array with $i being ‘pass1‘ and ‘pass2‘. All other inputs are sanitized, except for these. This is where SQL could have been injected to exploit any website running Drupal.

Drupalgeddon SQL Injection

Drupalgeddon Injected SQL

Drupalgeddon Hacked

This vulnerability could be easily fixed with wrapping the $data array with the array_values() function, although you could have also updated to Drupal 7.32. This function makes any associative array into a regular array with incrementing keys.

From:

foreach ($data as $i => $value) { // This assumes that there are no other placeholders that use the same // name. For example, if the array placeholder is defined as :example // and there is already an :example_2 placeholder, this will generate // a duplicate key. We do not account for that as the calling code // is already broken if that happens. $new_keys[$key . '_' . $i] = $value; } foreach ($data as $i => $value) {  // This assumes that there are no other placeholders that use the same  // name.  For example, if the array placeholder is defined as :example  // and there is already an :example_2 placeholder, this will generate  // a duplicate key.  We do not account for that as the calling code  // is already broken if that happens.  $new_keys[$key . '_' . $i] = $value;

To:

foreach (array_values($data) as $i => $value) { // This assumes that there are no other placeholders that use the same // name. For example, if the array placeholder is defined as :example // and there is already an :example_2 placeholder, this will generate // a duplicate key. We do not account for that as the calling code // is already broken if that happens. $new_keys[$key . '_' . $i] = $value; } foreach (array_values($data) as $i => $value) {  // This assumes that there are no other placeholders that use the same  // name.  For example, if the array placeholder is defined as :example  // and there is already an :example_2 placeholder, this will generate  // a duplicate key.  We do not account for that as the calling code  // is already broken if that happens.  $new_keys[$key . '_' . $i] = $value;

Drupalgeddon was such a major issue because it affected every single version of Drupal 7 before the 7.32 security update addressing the issue. It’s security risk was rated at 25/25 by the Drupal Security Team. It was also such as easy way to inject SQL. In addition, hours after posting the Security Advisory, there was a Public Service Announcement revealing backdoors were implemented, and would remain even after upgrading to 7.32 providing continuous access to your site from hackers.

This post was created with a video for Google Code-In 2014 to explain Drupalgeddon, and why it was such a major issue.

Share this:

Author: Akshay Kalose

A teenager, who is interested in Computer Science, Information Technology, Programming, Web Designing, Engineering and Physical Sciences.

Dec 22 2014
Dec 22
[embedded content]

Entity Embed is a module created for Drupal 8 by Chandan Singh for his Google Summer of Code 2014 project. It allows you to embed any type of entity such as Content, Comment, and Role. This blog post was made for Google Code-In 2014 to test and review Entity Embed.

Installation

The module was easily cloned into /modules/contrib/ from the GitHub Repository. From there, the module was activated. An error occurred while installing, which resulted in a blank white screen. Although, I was notified by Chandan Singh that this is a known error, and in spite of the error, the module should work fine. After a refresh, the module seemed to be installed, and no error was seen. The install process was easy, however the error might worry a user.

Configuration

Configuring the module was fairly straightforward. An Embed Button needs to be created for each entity type wished to be used in CKEditor. Once the buttons have been created, they can be added to the CKEditor interface in “Text formats and editors”.

entity-embed-ckeditor-config

As it is right now, it is beneficial for those who will use this module to embed only certain entity types since there is one button for a single entity type, however for people who want to embed many entity types it will be difficult to distinguish between the buttons since they all have the same icon. Although an icon/image can be uploaded for the buttons, they might not know how to make the icons.

Usage

Using Entity Embed in CKEditor while creating a new article or page was very simple. After clicking the “E” button, typing in the title of the entity, and configuring, the entity could be seen in the WYSIWYG editor.

Entity Embed Edit Content

One improvement for user interface is to keep the title in the box and load the ID/UUID in the background instead of showing the ID/UUID in the textbox. Also, some “Display as:” values show the same content in the editor. For example, both “Author” and “Label” show the title of the entity. Issue Reported Here. “Display as:” problem relates to another module.

Entity Embed Embedded First Post

Conclusion

Overall, Entity Embed is a great plugin! It can easily be used to embed any type of entity into the WYSIWYG CKEditor in Drupal 8. With some improvements here and there Entity Embed can be perfected!

Share this:

Author: Akshay Kalose

A teenager, who is interested in Computer Science, Information Technology, Programming, Web Designing, Engineering and Physical Sciences.

Dec 13 2014
Dec 13
[embedded content]

There is an incredibly easy way to create multiple Drupal sites. You don’t need to duplicate the code. You can have multiple sites, with different content, users, and themes since each site can use a different database. They will all run on a single codebase and will be able to share modules. This is one of Drupal’s greatest features called Multi-site. It was first implemented in Drupal 4, and it is here now in Drupal 8.

Getting Prepared

To get started, go ahead and download Drupal.

Then, upload and extract all the files into a new folder for Drupal. For example: /public_html/drupal/

I will be creating two sites, one for dogs, and the other for cats.

These multiple sites can be accessed either from subdomains or subdirectories. If you are going to use subdomains, go ahead and create them. However, if you are going to use subdirectories, hold off until later, since you will have to remove it anyways.

I want the dogs website to be accessed by a subdomain and the cats website by a subdirectory, so I will create dogs.kalose.net

Now create a separate database for each site. Also, create a user or two to access these databases.

I will name my databases user_dogs and user_cats which will be accessed by user user_drupal who will have privileges: ALTER, CREATE, CREATE TEMPORARY TABLES, DELETE, DROP, INDEX, INSERT, SELECT, AND UPDATE.

The Technical Part

Coding

Navigate into the directory: /drupal/sites/

We need to tell Drupal we are going to have multiple sites. To do this, create a file called sites.php. You can find extra examples and documentation of how to set it up in the example.sites.php file.

This file will contain an $sites array containing all the rules. For each index, the key will be the formatted address the website will be accessed at, and the value will be what folder to look in.

Format of each index:

'..' => 'directory'.

My sites.php:

<?php $sites = array( // http://dogs.kalose.net/ 'dogs.kalose.net' => 'dogs', // http://www.kalose.net/cats/ 'www.kalose.net.cats' => 'cats', ); /* $sites = array( // http://www.drupal.org:8080/mysite/test/ '8080.www.drupal.org.mysite.test' => 'example.com', ); */ $sites = array( // http://dogs.kalose.net/ 'dogs.kalose.net' => 'dogs', // http://www.kalose.net/cats/ 'www.kalose.net.cats' => 'cats',$sites = array( // http://www.drupal.org:8080/mysite/test/ '8080.www.drupal.org.mysite.test' => 'example.com',

Since we said dogs.kalose.net was going to use the /dogs/ folder and www.kalose.net/cats/ was going to use the /cats/ folder, these folders need to be created: /drupal/sites/dogs/ and /drupal/sites/cats/

SSH / Shell / Terminal

Connect to your webserver via SSH. If you’re on Windows, you can use PuTTY.

The /drupal/sites/default/ site will have default files we need to copy into each of our sites. Copy these files using the cp command in the sites directory:

cp default/default.settings.php dogs/settings.php cp default/default.settings.php cats/settings.php cp default/default.services.yml dogs/services.yml cp default/default.services.yml cats/services.yml cp default/default.settings.php dogs/settings.phpcp default/default.settings.php cats/settings.phpcp default/default.services.yml dogs/services.ymlcp default/default.services.yml cats/services.yml

Now we have successfully set up Drupal for multi-site. Although, if you visit dogs.kalose.net or www.kalose.net/cats/ it will be blank. Why? How does the web server know to point these sites to the /drupal/ directory? This is why we need to make symbolic links from /dogs/ and /cats/ to /drupal/

If a subdirectory is created when you create a subdomain, delete that directory.

rmdir dogs rmdir dogs

Now time to create the symbolic links. This is why we did’t have to create the /cats/ subdirectory. The symbolic link will automatically point /dogs/ and /cats/ to /drupal/ like they are the same folder.

ln -s drupal dogs ln -s drupal cats ln -s drupal dogsln -s drupal cats

* You may notice that these folders/links have all permissions (chmod 777). You don’t need to worry about this because these permissions won’t be used, instead the permissions of /drupal/ or the file/folder you are linking to will be used.

Finishing Up

All that is left to do now is to install Drupal. I can visit dogs.kalose.net and www.kalose.net/cats/ and set them up using databases user_dogs and user_cats. Using Multi-site, these sites can be completely different except for the fact that they share the same underlying code.

Share this:

Author: Akshay Kalose

A teenager, who is interested in Computer Science, Information Technology, Programming, Web Designing, Engineering and Physical Sciences.

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web