Author

Upgrade Your Drupal Skills

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

See Advanced Courses NAH, I know Enough
Dec 20 2017
Dec 20

When it comes to Drupal 8 theming layer, there is a lot Drupal 8 offers. Few concepts that come to mind while thinking of Drupal 8 theme layer include Renderable Array, Cacheabilty, Cache Context, Cache Tags, Twig and Preprocessors. Some of these are improvements of old concepts, while others are new introduction in Drupal 8. In this post, I'll share my experience on how to best utilise these concepts for a robust and performant frontend with Drupal 8.

To get best out of this post, you should be comfortable with:

  1. Drupal 8 #cache
  2. Twig Debug
  3. Preprocessor functions

We will focus on the following concepts:

Renderable array caching, walk through Drupal 8 caching power and Cache contexts

Drupal 8 comes with a lot of changes and almost all of us are even familiar with these changes. So, now it’s high time we start exploiting these to get best out of Drupal 8. It’s very important to understand how Drupal 8 caching works. Go through drupal.org documentation besides implementing the same once thoroughly on a vanilla Drupal instance. At the end have an answer to all these questions:

  • What is a renderable array?
  • Understand that every renderable array is cacheable.
  • One Renderable array can consist of other renderable arrays (nesting):
    So it’s like, a page is a renderable array which actually consists of other renderable arrays: region (renderable) arrays and in a region, we can have block (renderable array). The main point to understand here is hierarchy and nesting and the caching among this.
    Reference: https://www.drupal.org/docs/8/api/render-api/cacheability-of-render-arrays
  • Making use of cache context:
    A general mistake I have seen many of us perform is using
    
    #cache = max-age => -1
    
    

    There come few scenarios where we cannot use direct caching but that particular array can be cached on per user/URL basis. Especially for those cases, we have cache contexts. It’s very easy concept and you would love once you start using this, there are several other bases on which you can decide cache-ability of a renderable array.
    Reference: https://www.drupal.org/docs/8/api/cache-api/cache-contexts
     

Selection of template files/preprocessor functions and placement of the same.

With great flexibility, comes responsibility. Most of the time, we come across scenarios where we need to do tweaks to field output based on certain requirements. Drupal provides us handful of ways to do so, an important point here is to know and analyze what will be the best way to achieve expected results for the case. So, generally, we follow the following rule for customizations:

Tweak data -> Use Preprocess level alter
Tweak Markup -> Use Twig level customizations

Hence, when we have to do some custom work where we have to alter data conditionally, we make use of preprocessor functions. Consider overriding display of date field value based on certain conditions. In that case, a general approach we may take is to write hook_preprocess_field. But we need to consider that this preprocessor will run for all fields, which will definitely affect the performance. In this case, writing a specific preprocessor for date field makes more sense.

One more important thing regarding hook_preprocess_HOOK:

Consider a scenario, where we are using node.html.twig for node and then we have to do some alteration for a specific content type teaser view.

drupal8-twig

In that case creating specific twig and then using the specific preprocessor (node__content_type__teaser) for alteration is an extra step, instead, we can directly use the preprocessor hook_node__content_type__teaser without writing the specific twig file. So, for a generic twig, we can make use of specific hooks as suggested by twig debug too which definitely gives better performance.

Regarding placement of these preprocessors, it is completely based on the project requirement and usage. We place them in the module, in case the functionality to be provided should work on a modular approach. Placement of Preprocessors will work both in module and theme, while for twig files placing them in the module may need hook_theme_registery_alter based on the twig file we are overriding.

Use of Drupal Attributes

It’s not recommended to hardcode drupal attributes (HTML attributes like id, class) in twig files and the reason for this is: there are several modules which perform alteration of drupal attributes and make use of Drupal attributes and will no longer work if we hard code this. One example I can think of is Schema.org module which provides RDF mapping through attributes only.

Moreover, I would always suggest doing styling based on default classes provided by Drupal. Drupal by default provides appropriate classes both generic and specific and id for better styling. It is our duty, to make better use of them by understanding Drupal way. It also helps in saving project time and cost especially when we are following best practices and Drupal way of doing things. Drupal Core and Contrib are the best examples of how to proceed further on this. Also, following a proper structure makes possible use of styling written in Drupal core itself.

Logic in template/twig files - Yes/No?

In Drupal 7 To speed up the output process, it’s always recommended to avoid writing logic in template files. Instead, as discussed earlier consider writing preprocessors for this purpose. But with Drupal 8 adopting Twig Theme Engine things are different now. Doing tradeoff among the twig and preprocess (PHP) is mainly centric over performance concerns. We basically need our site to be faster. Drupal 7 PHP Engine used PHP theme engine and hence we recommended avoiding logic in template files, but in D8 with twig into effect, we have a faster theme engine with several twig filters, functions to be used. So, here we categorize our logic in 2 ways based on the type of work to be accomplished: soft logic and hard logic.

Soft Logic: Consider a scenario where we need to display comma separated values. Now, in this case, instead of writing a preprocessor for altering data we should use available twig filter "safe_join". It is definitely faster than the traditional way of using PHP preprocessor.

Hard Logic: In the above case, let's say if the value is taxonomy term and we need to print all the parent taxonomy term too. Then we need to load parent terms and we can then join them for final display, then this preprocessing should go in preprocessor only.
 

Frontend / Backend Collaboration

One more important thing I’ve observed is, it is very important to have a good collaboration between frontend and backend developers during the project. Sometimes as a frontend developer we come across weird requirements, in that case, it is very important to sit together to understand requirements and get the things done in the Drupal way to achieve best out of Drupal.

Also, go through this official guide to understand best practices for Drupal theming: https://www.drupal.org/docs/8/theming/twig/twig-best-practices-preprocess-functions-and-templates

Dec 19 2017
Dec 19

When it comes to Drupal 8 theming layer, there is a lot Drupal 8 offers. Few concepts that come to mind while thinking of Drupal 8 theme layer include Renderable Array, Cacheabilty, Cache Context, Cache Tags, Twig and Preprocessors. Some of these are improvements of old concepts, while others are new introduction in Drupal 8. In this post, I'll share my experience on how to best utilise these concepts for a robust and performant frontend with Drupal 8.

To get best out of this post, you should be comfortable with:

  1. Drupal 8 #cache
  2. Twig Debug
  3. Preprocessor functions

We will focus on the following concepts:

Renderable array caching, walk through Drupal 8 caching power and Cache contexts

Drupal 8 comes with a lot of changes and almost all of us are even familiar with these changes. So, now it’s high time we start exploiting these to get best out of Drupal 8. It’s very important to understand how Drupal 8 caching works. Go through drupal.org documentation besides implementing the same once thoroughly on a vanilla Drupal instance. At the end have an answer to all these questions:

  • What is a renderable array?
  • Understand that every renderable array is cacheable.
  • One Renderable array can consist of other renderable arrays (nesting):
    So it’s like, a page is a renderable array which actually consists of other renderable arrays: region (renderable) arrays and in a region, we can have block (renderable array). The main point to understand here is hierarchy and nesting and the caching among this.
    Reference: https://www.drupal.org/docs/8/api/render-api/cacheability-of-render-arrays
  • Making use of cache context:
    A general mistake I have seen many of us perform is using

#cache = max-age => -1

There come few scenarios where we cannot use direct caching but that particular array can be cached on per user/URL basis. Especially for those cases, we have cache contexts. It’s very easy concept and you would love once you start using this, there are several other bases on which you can decide cache-ability of a renderable array.
Reference: https://www.drupal.org/docs/8/api/cache-api/cache-contexts
 

Selection of template files/preprocessor functions and placement of the same.

With great flexibility, comes responsibility. Most of the time, we come across scenarios where we need to do tweaks to field output based on certain requirements. Drupal provides us handful of ways to do so, an important point here is to know and analyze what will be the best way to achieve expected results for the case. So, generally, we follow the following rule for customizations:

Tweak data -> Use Preprocess level alter
Tweak Markup -> Use Twig level customizations

Hence, when we have to do some custom work where we have to alter data conditionally, we make use of preprocessor functions. Consider overriding display of date field value based on certain conditions. In that case, a general approach we may take is to write hook_preprocess_field. But we need to consider that this preprocessor will run for all fields, which will definitely affect the performance. In this case, writing a specific preprocessor for date field makes more sense.

One more important thing regarding hook_preprocess_HOOK:

Consider a scenario, where we are using node.html.twig for node and then we have to do some alteration for a specific content type teaser view.

drupal8-twig

In that case creating specific twig and then using the specific preprocessor (node__content_type__teaser) for alteration is an extra step, instead, we can directly use the preprocessor hook_node__content_type__teaser without writing the specific twig file. So, for a generic twig, we can make use of specific hooks as suggested by twig debug too which definitely gives better performance.

Regarding placement of these preprocessors, it is completely based on the project requirement and usage. We place them in the module, in case the functionality to be provided should work on a modular approach. Placement of Preprocessors will work both in module and theme, while for twig files placing them in the module may need hook_theme_registery_alter based on the twig file we are overriding.

Use of Drupal Attributes

It’s not recommended to hardcode drupal attributes (HTML attributes like id, class) in twig files and the reason for this is: there are several modules which perform alteration of drupal attributes and make use of Drupal attributes and will no longer work if we hard code this. One example I can think of is Schema.org module which provides RDF mapping through attributes only.

Moreover, I would always suggest doing styling based on default classes provided by Drupal. Drupal by default provides appropriate classes both generic and specific and id for better styling. It is our duty, to make better use of them by understanding Drupal way. It also helps in saving project time and cost especially when we are following best practices and Drupal way of doing things. Drupal Core and Contrib are the best examples of how to proceed further on this. Also, following a proper structure makes possible use of styling written in Drupal core itself.

Logic in template/twig files - Yes/No?

In Drupal 7 To speed up the output process, it’s always recommended to avoid writing logic in template files. Instead, as discussed earlier consider writing preprocessors for this purpose. But with Drupal 8 adopting Twig Theme Engine things are different now. Doing tradeoff among the twig and preprocess (PHP) is mainly centric over performance concerns. We basically need our site to be faster. Drupal 7 PHP Engine used PHP theme engine and hence we recommended avoiding logic in template files, but in D8 with twig into effect, we have a faster theme engine with several twig filters, functions to be used. So, here we categorize our logic in 2 ways based on the type of work to be accomplished: soft logic and hard logic.

Soft Logic: Consider a scenario where we need to display comma separated values. Now, in this case, instead of writing a preprocessor for altering data we should use available twig filter "safe_join". It is definitely faster than the traditional way of using PHP preprocessor.

Hard Logic: In the above case, let's say if the value is taxonomy term and we need to print all the parent taxonomy term too. Then we need to load parent terms and we can then join them for final display, then this preprocessing should go in preprocessor only.
 

Frontend / Backend Collaboration

One more important thing I’ve observed is, it is very important to have a good collaboration between frontend and backend developers during the project. Sometimes as a frontend developer we come across weird requirements, in that case, it is very important to sit together to understand requirements and get the things done in the Drupal way to achieve best out of Drupal.

Also, go through this official guide to understand best practices for Drupal theming: https://www.drupal.org/docs/8/theming/twig/twig-best-practices-preprocess-functions-and-templates

Dec 12 2017
Dec 12

The Rise of Assistants

In last couple of years we have seen the rise of assistants, AI is enabling our lives more and more and with help of devices like Google Home and Amazon Echo, its now entering our living rooms and changing how we interact with technology. Though Assistants have been around for couple of years through android google home app, the UX is changing rapidly with home devices where now we are experiencing Conversational UI i.e. being able to talk to devices, no more typing/searching, you can now converse with your device and book a cab or play your favourite music. Though the verdict on home devices like Echo and Google home is pending, the underlying technology i.e. AI based assistants are here to stay.

In this post, we will explore Google Assistant Developer framework and how we can integrate it with Drupal.

Google Assistant Overview


Google Assistant works with help of Apps that define actions which in turn invokes operations to be performed on our product and services. These apps are registered with Actions on Google, which basically is a platform comprising of Apps and hence connecting different products and services via Apps. Unlike traditional mobile or desktop apps, users interact with Assistant apps through a conversation, natural-sounding back and forth exchanges (voice or text) and not traditional Click and Touch paradigms. 

The first step in the flow is understanding use requests through actions, so lets learn more about it. 

How Action on Google works with the Assistant?

It is very important to understand how actually actions on Google work with the assistant to have an overview of the workflow. From the development perspective, it's crucial we understand the whole of the Google Assistant and Google Action model in total, so that extending the same becomes easier.

 

Actions on Google

 

It all starts with User requesting an action, followed by Google Assistant invoking best corresponding APP using Actions on Google. Now, it's the duty of Actions on Google to contact APP by sending a request. The app must be prepared to handle the request, perform the corresponding action and send a valid response to the Actions on Google which is then passed to Google Assistant. Google Assistant renders the response in its UI and displays it to the user and conversation begins.

Lets build our own action, following tools are required:

  • Ngrok - Local web server supporting HTTPS. 
  • Editor - Sublime/PHPStorm
  • Google Pixel 2 - Just kidding! Although you can order 1 for me :p
  • Bit of patience and 100% attention

STEP1: BUILD YOUR ACTION APP

Very first step now is building our Actions on Google APP. Google provides 3 ways to accomplish this:

  1. With Templates
  2. With Dialogflow
  3. With Actions SDK

Main purpose of this app would be matching user request with an action. For now, we would be going with Dialogflow (for beginner convenience). To develop with Dialogflow, we first need to create an Actions on Google developer project and a Dialogflow agent. Having a project allows us to access the developer console to manage and distribute our app.

  1. Go to the Actions on Google Developer Console.
  2. Click on Add Project, enter YourAppName for the project name, and click Create Project.
  3. In the Overview screen, click BUILD on the Dialogflow card and then CREATE ACTIONS ON Dialogflow to start building actions.
  4. The Dialogflow console appears with information automatically populated in an agent. Click Save to save the agent.

Post saving an agent, we start improving/developing our agent. We can consider this step as training of our newly created Agent via some training data set. These structured training data sets referred here are intents. An individual Intent comprises of query patterns that a user may ask to perform an action, events and actions associated with this particular intent which together define a purpose user want to fulfill. So, every task user wants Assistant to perform is actually mapped with an intent. Events and Actions can be considered as a definitive representation of the actual associated event and task that needs to be performed which will be used by our products and services to understand what the end user is asking for.

So, here we define all the intents that define our app. Let's start with creating an intent to do cache rebuild.

  1. Create a new intent with name CACHE-REBUILD.
  2. We need to add query patterns we can think of, that user might say to invoke this intent. (Query Patterns may content parameters too, we will cover this later.)
  3. Add event cache-rebuild.
  4. Save the intent.
Intent Google Actions

For now, this is enough to just understand the flow, we will focus on entities and other aspects later. To verify if the intent you have created gets invoked if user says “do cache rebuild”, use “Try it now” present in the right side of the Dialogflow window.

STEP2: BUILD FULFILLMENT

After we are done with defining action in dialogflow, we now need to prepare our product (Drupal App) to fulfill the user request. So, basically after understanding user request and matching that with an intent and action Actions on Google is now going to invoke our Drupal App in one or the other way . This is accomplished using WEBHOOKS. So, Google is now going to send a post request with all the details. Under Fulfillment tab, we configure our webhook. We need to ensure that our web service fulfills webhook requirements.

According to this, the web service must use HTTPS and the URL must be publicly accessible and hence we need to install NGROK. Ngrok exposes local web server to the internet.

NGROK

After having a publicly accessible URL, we just need to add this URL under fulfillment tab. As this URL will receive post request and processing will be done thereafter, so we need to add that URL where we are gonna handle requests just like endpoints. (It may be like http://yourlocalsite.ngrok.io/google-assistant-request)

add webhook url

Now, we need to build corresponding fulfillment to process the intent.

OK! It seems simple we just need to create a custom module with a route and a controller to handle the request. Indeed it is simple, only important point is understanding the flow which we understood above.

So, why are we waiting? Let’s start.

Create a custom module and a routing file:

droogle.default_controller_handleRequest:
 path: '/google-assistant-request'
 defaults:
   _controller: '\Drupal\droogle\Controller\DefaultController::handleRequest'
   _title: 'Handle Request'
 requirements:
   _access: TRUE

Now, let’s add the corresponding controller

<?php

namespace Drupal\droogle\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RequestStack;

/**
* Class DefaultController.
*/
class DefaultController extends ControllerBase {

 /**
  * Symfony\Component\HttpFoundation\RequestStack definition.
  *
  * @var \Symfony\Component\HttpFoundation\RequestStack
  */
 protected $requestStack;

 /**
  * The logger factory.
  *
  * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
  */
 protected $loggerFactory;

 /**
  * Constructs a new DefaultController object.
  */
 public function __construct(RequestStack $request_stack, LoggerChannelFactoryInterface $loggerFactory) {
   $this->requestStack = $request_stack;
   $this->loggerFactory = $loggerFactory;
 }

 /**
  * {@inheritdoc}
  */
 public static function create(ContainerInterface $container) {
   return new static(
     $container->get('request_stack'),
     $container->get('logger.factory')
   );
 }

 /**
  * Handlerequest.
  *
  * @return mixed
  *   Return Hello string.
  */
 public function handleRequest() {
   $this->loggerFactory->get('droogle')->info('droogle triggered');
   $this->processRequest();
   $data = [
     'speech' => 'Cache Rebuild Completed for the Site',
     'displayText' => 'Cache Rebuild Completed',
     'data' => '',
     'contextOut' => [],
     'source' => 'uniworld',
   ];
   return JsonResponse::create($data, 200);
 }

 protected function processRequest() {
   $params = $this->requestStack->getCurrentRequest();
   // Here we will process the request to get intent

   // and fulfill the action.
 }
}

Done! We are ready with a request handler to process the request that will be made by Google Assistant.

 

STEP3: DEPLOY FULFILLMENT AND TESTING THE APP

Part of the deployment has already been done, as we are developing on our local only. Now, we need to enable our custom module. Post that let's get back to dialogflow and establish the connection with app to test this. Earlier we had configured fulfillment URL details, ensure we have enabled webhook for all domains.

deployment

 

Let’s get back to intent that we build and enable webhook there too and save the intent.

intent enable webhook

Now, to test this we need to integrate it any of the device or live/sandbox app. Under Integrations tab, google provides several options for this too. Enable for Web Demo and open the URL in new tab, to test this:

Integration Web Demo

Speak up and test your newly build APP and let Google Assistant do its work.

So, as seen in the screenshot, there can be 2 type of responses. First, where our server is not able to handle request properly and the second one where Drupal server sends a valid JSON response.

GREAT! Connection is now established, you can now add intents in Google Action APP and correspondingly handle that intent and action at Drupal End. This is just a taste, conversational UX and Assistant technology will definitely impact how we interact with technology and we believe Drupal has a great role to play as a robust backend.

Dec 06 2017
Dec 06

The Rise of Assistants

In last couple of years we have seen the rise of assistants, AI is enabling our lives more and more and with help of devices like Google Home and Amazon Echo, its now entering our living rooms and changing how we interact with technology. Though Assistants have been around for couple of years through android google home app, the UX is changing rapidly with home devices where now we are experiencing Conversational UI i.e. being able to talk to devices, no more typing/searching, you can now converse with your device and book a cab or play your favourite music. Though the verdict on home devices like Echo and Google home is pending, the underlying technology i.e. AI based assistants are here to stay.

In this post, we will explore Google Assistant Developer framework and how we can integrate it with Drupal.

Google Assistant Overview


Google Assistant works with help of Apps that define actions which in turn invokes operations to be performed on our product and services. These apps are registered with Actions on Google, which basically is a platform comprising of Apps and hence connecting different products and services via Apps. Unlike traditional mobile or desktop apps, users interact with Assistant apps through a conversation, natural-sounding back and forth exchanges (voice or text) and not traditional Click and Touch paradigms. 

The first step in the flow is understanding use requests through actions, so lets learn more about it. 

How Action on Google works with the Assistant?

It is very important to understand how actually actions on Google work with the assistant to have an overview of the workflow. From the development perspective, it's crucial we understand the whole of the Google Assistant and Google Action model in total, so that extending the same becomes easier.

 

Actions on Google

 

It all starts with User requesting an action, followed by Google Assistant invoking best corresponding APP using Actions on Google. Now, it's the duty of Actions on Google to contact APP by sending a request. The app must be prepared to handle the request, perform the corresponding action and send a valid response to the Actions on Google which is then passed to Google Assistant. Google Assistant renders the response in its UI and displays it to the user and conversation begins.

Lets build our own action, following tools are required:

  • Ngrok - Local web server supporting HTTPS. 
  • Editor - Sublime/PHPStorm
  • Google Pixel 2 - Just kidding! Although you can order 1 for me :p
  • Bit of patience and 100% attention

STEP1: BUILD YOUR ACTION APP

Very first step now is building our Actions on Google APP. Google provides 3 ways to accomplish this:

  1. With Templates
  2. With Dialogflow
  3. With Actions SDK

Main purpose of this app would be matching user request with an action. For now, we would be going with Dialogflow (for beginner convenience). To develop with Dialogflow, we first need to create an Actions on Google developer project and a Dialogflow agent. Having a project allows us to access the developer console to manage and distribute our app.

  1. Go to the Actions on Google Developer Console.
  2. Click on Add Project, enter YourAppName for the project name, and click Create Project.
  3. In the Overview screen, click BUILD on the Dialogflow card and then CREATE ACTIONS ON Dialogflow to start building actions.
  4. The Dialogflow console appears with information automatically populated in an agent. Click Save to save the agent.

Post saving an agent, we start improving/developing our agent. We can consider this step as training of our newly created Agent via some training data set. These structured training data sets referred here are intents. An individual Intent comprises of query patterns that a user may ask to perform an action, events and actions associated with this particular intent which together define a purpose user want to fulfill. So, every task user wants Assistant to perform is actually mapped with an intent. Events and Actions can be considered as a definitive representation of the actual associated event and task that needs to be performed which will be used by our products and services to understand what the end user is asking for.

So, here we define all the intents that define our app. Let's start with creating an intent to do cache rebuild.

  1. Create a new intent with name CACHE-REBUILD.
  2. We need to add query patterns we can think of, that user might say to invoke this intent. (Query Patterns may content parameters too, we will cover this later.)
  3. Add event cache-rebuild.
  4. Save the intent.
Intent Google Actions

For now, this is enough to just understand the flow, we will focus on entities and other aspects later. To verify if the intent you have created gets invoked if user says “do cache rebuild”, use “Try it now” present in the right side of the Dialogflow window.

STEP2: BUILD FULFILLMENT

After we are done with defining action in dialogflow, we now need to prepare our product (Drupal App) to fulfill the user request. So, basically after understanding user request and matching that with an intent and action Actions on Google is now going to invoke our Drupal App in one or the other way . This is accomplished using WEBHOOKS. So, Google is now going to send a post request with all the details. Under Fulfillment tab, we configure our webhook. We need to ensure that our web service fulfills webhook requirements.

According to this, the web service must use HTTPS and the URL must be publicly accessible and hence we need to install NGROK. Ngrok exposes local web server to the internet.

NGROK

After having a publicly accessible URL, we just need to add this URL under fulfillment tab. As this URL will receive post request and processing will be done thereafter, so we need to add that URL where we are gonna handle requests just like endpoints. (It may be like http://yourlocalsite.ngrok.io/google-assistant-request)

add webhook url

Now, we need to build corresponding fulfillment to process the intent.

OK! It seems simple we just need to create a custom module with a route and a controller to handle the request. Indeed it is simple, only important point is understanding the flow which we understood above.

So, why are we waiting? Let’s start.

Create a custom module and a routing file:

droogle.default_controller_handleRequest:
 path: '/google-assistant-request'
 defaults:
   _controller: '\Drupal\droogle\Controller\DefaultController::handleRequest'
   _title: 'Handle Request'
 requirements:
   _access: TRUE

Now, let’s add the corresponding controller

<?php

namespace Drupal\droogle\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RequestStack;

/**
* Class DefaultController.
*/
class DefaultController extends ControllerBase {

 /**
  * Symfony\Component\HttpFoundation\RequestStack definition.
  *
  * @var \Symfony\Component\HttpFoundation\RequestStack
  */
 protected $requestStack;

 /**
  * The logger factory.
  *
  * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
  */
 protected $loggerFactory;

 /**
  * Constructs a new DefaultController object.
  */
 public function __construct(RequestStack $request_stack, LoggerChannelFactoryInterface $loggerFactory) {
   $this->requestStack = $request_stack;
   $this->loggerFactory = $loggerFactory;
 }

 /**
  * {@inheritdoc}
  */
 public static function create(ContainerInterface $container) {
   return new static(
     $container->get('request_stack'),
     $container->get('logger.factory')
   );
 }

 /**
  * Handlerequest.
  *
  * @return mixed
  *   Return Hello string.
  */
 public function handleRequest() {
   $this->loggerFactory->get('droogle')->info('droogle triggered');
   $this->processRequest();
   $data = [
     'speech' => 'Cache Rebuild Completed for the Site',
     'displayText' => 'Cache Rebuild Completed',
     'data' => '',
     'contextOut' => [],
     'source' => 'uniworld',
   ];
   return JsonResponse::create($data, 200);
 }

 protected function processRequest() {
   $params = $this->requestStack->getCurrentRequest();
   // Here we will process the request to get intent

   // and fulfill the action.
 }
}

Done! We are ready with a request handler to process the request that will be made by Google Assistant.

 

STEP3: DEPLOY FULFILLMENT AND TESTING THE APP

Part of the deployment has already been done, as we are developing on our local only. Now, we need to enable our custom module. Post that let's get back to dialogflow and establish the connection with app to test this. Earlier we had configured fulfillment URL details, ensure we have enabled webhook for all domains.

deployment

 

Let’s get back to intent that we build and enable webhook there too and save the intent.

intent enable webhook

Now, to test this we need to integrate it any of the device or live/sandbox app. Under Integrations tab, google provides several options for this too. Enable for Web Demo and open the URL in new tab, to test this:

Integration Web Demo

Speak up and test your newly build APP and let Google Assistant do its work.

So, as seen in the screenshot, there can be 2 type of responses. First, where our server is not able to handle request properly and the second one where Drupal server sends a valid JSON response.

GREAT! Connection is now established, you can now add intents in Google Action APP and correspondingly handle that intent and action at Drupal End. This is just a taste, conversational UX and Assistant technology will definitely impact how we interact with technology and we believe Drupal has a great role to play as a robust backend.

Dec 06 2017
Dec 06
In last couple of years we have seen the rise of assistants, AI is enabling our lives more and more and with help of devices like Google Home and Amazon Echo, its now entering our living rooms and changing how we interact with technology.
Nov 24 2016
Nov 24

Autocomplete on textfields like tags / user & node reference helps improve the UX and interactivity for your site visitors, In this blog post I'd like to cover how to implement autocomplete functionality in Drupal 8, including implementing a custom callback

Step 1: Assign autocomplete properties to textfield

As per Drupal Change records, #autocomplete_path has been replaced by #autocomplete_route_name and #autocomplete_parameters for autocomplete fields ( More details -- https://www.drupal.org/node/2070985).

The very first step is to assign appropriate properties to the textfield:

  1. '#autocomplete_route_name':
    for passing route name of callback URL to be used by autocomplete Javascript Library.
  2. '#autocomplete_route_parameters':
    for passing array of arguments to be passed to autocomplete handler.
$form['name'] = array(
    '#type' => 'textfield',
    '#autocomplete_route_name' => 'my_module.autocomplete',
    '#autocomplete_route_parameters' => array('field_name' => 'name', 'count' => 10),
);

Thats all! for adding an #autocomplete callback to a textfield. 

However, there might be cases where the routes provided by core might not suffice as we might different response in JSON or additional data. Lets take a look at how to write a autocomplete callback, we will be using using my_module.autocomplete route and will pass arguments: 'name' as field_name and 10 as count.

Step 2: Define autocomplete route

Now, add the 'my_module.autocomplete' route in my_module.routing.yml file as:

my_module.autocomplete:
  path: '/my-module-autocomplete/{field_name}/{count}'
  defaults:
    _controller: '\Drupal\my_module\Controller\AutocompleteController::handleAutocomplete'
    _format: json
  requirements:
    _access: 'TRUE'

While Passing parameters to controller, use the same names in curly braces, which were used while defining the autocomplete_route_parameters. Defining _format as json is a good practise.

Step 3: Add Controller and return JSON response

Finally, we need to generate the JSON response for our field element. So, proceeding further we would be creating AutoCompleteController class file at my_module > src > Controller > AutocompleteController.php.

<?php

namespace Drupal\my_module\Controller;

use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Component\Utility\Tags;
use Drupal\Component\Utility\Unicode;

/**
 * Defines a route controller for entity autocomplete form elements.
 */
class AutocompleteController extends ControllerBase {

  /**
   * Handler for autocomplete request.
   */
  public function handleAutocomplete(Request $request, $field_name, $count) {
    $results = [];

    // Get the typed string from the URL, if it exists.
    if ($input = $request->query->get('q')) {
      $typed_string = Tags::explode($input);
      $typed_string = Unicode::strtolower(array_pop($typed_string));
      // @todo: Apply logic for generating results based on typed_string and other
      // arguments passed.
      for ($i = 0; $i < $count; $i++) {
        $results[] = [
          'value' => $field_name . '_' . $i . '(' . $i . ')',
          'label' => $field_name . ' ' . $i,
        ];
      }
    }

    return new JsonResponse($results);
  }

}

We would be extending ControllerBase class and would then define our handler method, which will return results. Parameters for the handler would be Request object and arguments (field_name and count) passed in routing.yml file. From the Request object, we would be getting the typed string from the URL. Besides, we do have other route parameters (field_name and Count) on the basis of which we can generate the results array. 

An important point to be noticed here is, we need the results array to have data in 'value' and 'label' key-value pair as we have done above. Then finally we would be generating JsonResponse by creating new JsonResponse object and passing $results.

That's all we need to make autocomplete field working. Rebuild the cache and load the form page to see results.

Nov 24 2016
Nov 24

Autocomplete on textfields like tags / user & node reference helps improve the UX and interactivity for your site visitors, In this blog post I'd like to cover how to implement autocomplete functionality in Drupal 8, including implementing a custom callback

Step 1: Assign autocomplete properties to textfield

As per Drupal Change records, #autocomplete_path has been replaced by #autocomplete_route_name and #autocomplete_parameters for autocomplete fields ( More details -- https://www.drupal.org/node/2070985).

The very first step is to assign appropriate properties to the textfield:

  1. '#autocomplete_route_name':
    for passing route name of callback URL to be used by autocomplete Javascript Library.
  2. '#autocomplete_route_parameters':
    for passing array of arguments to be passed to autocomplete handler.
$form['name'] = array(
    '#type' => 'textfield',
    '#autocomplete_route_name' => 'my_module.autocomplete',
    '#autocomplete_route_parameters' => array('field_name' => 'name', 'count' => 10),
);

Thats all! for adding an #autocomplete callback to a textfield. 

However, there might be cases where the routes provided by core might not suffice as we might different response in JSON or additional data. Lets take a look at how to write a autocomplete callback, we will be using using my_module.autocomplete route and will pass arguments: 'name' as field_name and 10 as count.

Step 2: Define autocomplete route

Now, add the 'my_module.autocomplete' route in my_module.routing.yml file as:

my_module.autocomplete:
  path: '/my-module-autocomplete/{field_name}/{count}'
  defaults:
    _controller: '\Drupal\my_module\Controller\AutocompleteController::handleAutocomplete'
    _format: json
  requirements:
    _access: 'TRUE'

While Passing parameters to controller, use the same names in curly braces, which were used while defining the autocomplete_route_parameters. Defining _format as json is a good practise.

Step 3: Add Controller and return JSON response

Finally, we need to generate the JSON response for our field element. So, proceeding further we would be creating AutoCompleteController class file at my_module > src > Controller > AutocompleteController.php.

<?php

namespace Drupal\my_module\Controller;

use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Component\Utility\Tags;
use Drupal\Component\Utility\Unicode;

/**
 * Defines a route controller for entity autocomplete form elements.
 */
class AutocompleteController extends ControllerBase {

  /**
   * Handler for autocomplete request.
   */
  public function handleAutocomplete(Request $request, $field_name, $count) {
    $results = [];

    // Get the typed string from the URL, if it exists.
    if ($input = $request->query->get('q')) {
      $typed_string = Tags::explode($input);
      $typed_string = Unicode::strtolower(array_pop($typed_string));
      // @todo: Apply logic for generating results based on typed_string and other
      // arguments passed.
      for ($i = 0; $i < $count; $i++) {
        $results[] = [
          'value' => $field_name . '_' . $i . '(' . $i . ')',
          'label' => $field_name . ' ' . $i,
        ];
      }
    }

    return new JsonResponse($results);
  }

}

We would be extending ControllerBase class and would then define our handler method, which will return results. Parameters for the handler would be Request object and arguments (field_name and count) passed in routing.yml file. From the Request object, we would be getting the typed string from the URL. Besides, we do have other route parameters (field_name and Count) on the basis of which we can generate the results array. 

An important point to be noticed here is, we need the results array to have data in 'value' and 'label' key-value pair as we have done above. Then finally we would be generating JsonResponse by creating new JsonResponse object and passing $results.

That's all we need to make autocomplete field working. Rebuild the cache and load the form page to see results.

Nov 10 2016
Nov 10

Drupal sites with events functionality, often have to allow their users to export events in their personal calendars. On a recent Drupal 8 project we were asked to integrate 3rd party service Add to Calendar to their events and having found no formal integration of the widget with Drupal we developed and contributed this module. The widget provided by Add to calendar supports export of Dates / events to iCalender, Google Calendar, Outlook, Outlook Online and Yahoo Calendar.

add-to-calendar-blue

 

Why use Add To Calendar Service?

  • Add to Calendar Module provides a widget to export events.
  • With Add to Calendar Module, you can create event button on a page and allow guests to add this event to their calendar.

How Does Add to Calendar Module Works?

Add to Calendar Module provides third party field formatter settings for DateTime fields. Module internally uses services provided by http://addtocalendar.com to load free add to calendar button for event page on website and email. Clicking on this button, the event is exported to the corresponding website with proper information in the next tab where a user can add the event to their calendar. Besides, it provides a handful of configuration for a really flexible experience, Allowing you to use your datetime format along with Add to Calendar button.

Using Add to Calendar

  1. Download and enable Add to Calendar module (https://www.drupal.org/project/addtocalendar)

  2. Adding Add to Calendar button to any datetime field would require enabling “Show Add to Calendar” checkbox present at format configurations on Manage Display page of the desired content type.

add-to-calendar-manage-display

 

  1. Following configurations are available:

Option Description Style Three basic styles are available: Basic, Blue and Glow Orange Display Text Text for the display button. Event Details Module provides you three options here. You may opt for static data, tokenized value or any field value, specific to the current entity. Privacy Use public for free access to event information while private if the event is closed to public access. Security Level To specify whether button link should use http or https Calendars to show Select Calendars to be enabled for the display.

4. Save the settings and visit content display page.

Developer Support

Devs have the option to add "Add to Calendar" button anywhere on the website by following below steps:

1. Include base library ('addtocalendar/base') for add to calendar basic functionality. Optionally, You may also one of the following style libraries for styling the display button:

  • 'addtocalendar/blue'
  • 'addtocalendar/glow_orange'
$variables['#attached']['library'][] = 'addtocalendar/base';

2. Place event data on the page as:

<span class="addtocalendar atc-style-blue">
<var class="atc_event">
<var class="atc_date_start">2016-05-04 12:00:00</var>
<var class="atc_date_end">2016-05-04 18:00:00</var>
<var class="atc_timezone">Europe/London</var>
<var class="atc_title">Star Wars Day Party</var>
<var class="atc_description">May the force be with you</var>
<var class="atc_location">Tatooine</var>
<var class="atc_organizer">Luke Skywalker</var>
<var class="atc_organizer_email">[email protected]</var>
</var>
</span>

For further customization of this custom button visit: http://addtocalendar.com/ Event Data Options section.

3. This would create "Add to Calendar" button for your website.

 

Nov 08 2016
Nov 08

Drupal sites with events functionality, often have to allow their users to export events in their personal calendars. On a recent Drupal 8 project we were asked to integrate 3rd party service Add to Calendar to their events and having found no formal integration of the widget with Drupal we developed and contributed this module. The widget provided by Add to calendar supports export of Dates / events to iCalender, Google Calendar, Outlook, Outlook Online and Yahoo Calendar.

add-to-calendar-blue

 

Why use Add To Calendar Service?

  • Add to Calendar Module provides a widget to export events.
  • With Add to Calendar Module, you can create event button on a page and allow guests to add this event to their calendar.

How Does Add to Calendar Module Works?

Add to Calendar Module provides third party field formatter settings for DateTime fields. Module internally uses services provided by http://addtocalendar.com to load free add to calendar button for event page on website and email. Clicking on this button, the event is exported to the corresponding website with proper information in the next tab where a user can add the event to their calendar. Besides, it provides a handful of configuration for a really flexible experience, Allowing you to use your datetime format along with Add to Calendar button.

Using Add to Calendar

  1. Download and enable Add to Calendar module (https://www.drupal.org/project/addtocalendar)

  2. Adding Add to Calendar button to any datetime field would require enabling “Show Add to Calendar” checkbox present at format configurations on Manage Display page of the desired content type.

add-to-calendar-manage-display

 

  1. Following configurations are available:

Option Description Style Three basic styles are available: Basic, Blue and Glow Orange Display Text Text for the display button. Event Details Module provides you three options here. You may opt for static data, tokenized value or any field value, specific to the current entity. Privacy Use public for free access to event information while private if the event is closed to public access. Security Level To specify whether button link should use http or https Calendars to show Select Calendars to be enabled for the display.

4. Save the settings and visit content display page.

Developer Support

Devs have the option to add "Add to Calendar" button anywhere on the website by following below steps:

1. Include base library ('addtocalendar/base') for add to calendar basic functionality. Optionally, You may also one of the following style libraries for styling the display button:

  • 'addtocalendar/blue'
  • 'addtocalendar/glow_orange'
$variables['#attached']['library'][] = 'addtocalendar/base';

2. Place event data on the page as:

<span class="addtocalendar atc-style-blue">
<var class="atc_event">
<var class="atc_date_start">2016-05-04 12:00:00</var>
<var class="atc_date_end">2016-05-04 18:00:00</var>
<var class="atc_timezone">Europe/London</var>
<var class="atc_title">Star Wars Day Party</var>
<var class="atc_description">May the force be with you</var>
<var class="atc_location">Tatooine</var>
<var class="atc_organizer">Luke Skywalker</var>
<var class="atc_organizer_email">[email protected]</var>
</var>
</span>

For further customization of this custom button visit: http://addtocalendar.com/ Event Data Options section.

3. This would create "Add to Calendar" button for your website.

 

Oct 28 2016
Oct 28

CSSgram module supplements Drupal Image styling experience by making Instagram like filters available to your Drupal 8 site images, we do this with help of CSSgram library. 

Beauty of this module is, it simply uses css to beautify your image.

cssgram-filters-sample

Few CSSGram sample filters applied to an image.

How CSSGram Module works?

CSSGram module uses CSSGram Library for adding filter effects via CSS to the image fields. Module extends Field Formatter Settings to add image filter for that particular field. CSSGram extends field formatter settings and hence these filters can be applied on top of the existing available image formatters and image presets. Allowing you to use your desired image preset along with CSSGram filters.
 

Using CSSGram

  1. Download and enable CSSGram module (https://www.drupal.org/project/cssgram)

  2. Visit Manage Display of content type and for the desired image field, click on the setting link under format column.

  3. Select Filter option lets us choose from the available image filters. Just select the desired image filter and hit update button.

third-party-settings-cssgram
  1. Save the settings and visit the content display page.

Developer Support

Devs have the option to use these filters anywhere on the site by just attaching the ‘cssgram/cssgram’ library and then applying any of the available css filter class to the wrapper element.


function mymodule_preprocess_field(&$variables) {
    // Add desired css class.
    $variables['attributes']['class'] = 'kelvin';
    // Attach cssgram library.
    $variables['#attached']['library'][] = 'cssgram/cssgram';
}
Oct 25 2016
Oct 25

CSSgram module supplements Drupal Image styling experience by making Instagram like filters available to your Drupal 8 site images, we do this with help of CSSgram library. 

Beauty of this module is, it simply uses css to beautify your image.

cssgram-filters-sample

Few CSSGram sample filters applied to an image.

How CSSGram Module works?

CSSGram module uses CSSGram Library for adding filter effects via CSS to the image fields. Module extends Field Formatter Settings to add image filter for that particular field. CSSGram extends field formatter settings and hence these filters can be applied on top of the existing available image formatters and image presets. Allowing you to use your desired image preset along with CSSGram filters.
 

Using CSSGram

  1. Download and enable CSSGram module (https://www.drupal.org/project/cssgram)

  2. Visit Manage Display of content type and for the desired image field, click on the setting link under format column.

  3. Select Filter option lets us choose from the available image filters. Just select the desired image filter and hit update button.

third-party-settings-cssgram
  1. Save the settings and visit the content display page.

Developer Support

Devs have the option to use these filters anywhere on the site by just attaching the ‘cssgram/cssgram’ library and then applying any of the available css filter class to the wrapper element.


function mymodule_preprocess_field(&$variables) {
    // Add desired css class.
    $variables['attributes']['class'] = 'kelvin';
    // Attach cssgram library.
    $variables['#attached']['library'][] = 'cssgram/cssgram';
}

Related Articles

Views Attach Library in Drupal 8

This module is designed to attach JS and CSS library in views, by merely mentioning the library name. It provides a simple UI option in the views where a user has to simply mention the libraries name.

Gatsby Incremental build for self-hosted Drupal environments

Incremental build functionality is now a part of the Gatsby core. Let's understand why the Gatsby incremental build is required for self-hosted environments and how to implement it.

Drupal Contribution - Page Specific Classes

This Drupal 8 module enables a user to add different classes to the body tag of any desired page. It supports all pages which can be created via Node, Views, or a Custom Route.

Looking for a Drupal partner?

We are drupal 8 ready

Oct 25 2016
Oct 25

Drupal has always had excellent support for human-friendly URL’s and SEO in general, from early on we have had the luxury of modules like pathauto offering options to update URL for specific entities, token support and bulk update of URLs. Though bulk update works for most cases, there are some situations where we have to update URLs programmatically, some of the use cases are:

  • Multisite setup -- When your setup has many sites, its inconvenient to use bulk update UI to update aliases for each of your sites. Programmatically updating the aliases is a good choice and can be executed via the update hooks.
  • Conditional Update -- When you wish to update aliases based on certain conditions.

 

In Drupal 8 Pathauto services.yml file we can see that there is a service named ‘pathauto.generator’ which is what we would need. The class for corresponding to this service, PathautoGenerator provides updateEntityAlias method which is what we would be using here:

public function updateEntityAlias(EntityInterface $entity, $op, array $options = array())


 

EntityInterface $entity: So, we need to pass the entity (node, taxonomy_term or user) here.
String $op: Operation to be performed on the entity (‘update’, ‘insert’ or ‘bulkupdate’).
$options: An optional array of additional options.

Now all we need is to loop the entities through this function, these entities may be the user, taxonomy_term or node. We will be using entityQuery and entityTypeManager to load entities, similar to the code below

  $entities = [];
  // Load All nodes.
  $result = \Drupal::entityQuery('node')->execute();
  $entity_storage = \Drupal::entityTypeManager()->getStorage('node');
  $entities = array_merge($entities, $entity_storage->loadMultiple($result));

  // Load All taxonomy terms.
  $result = \Drupal::entityQuery('taxonomy_term')->execute();
  $entity_storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
  $entities = array_merge($entities, $entity_storage->loadMultiple($result));

  // Load All Users.
  $result = \Drupal::entityQuery('user')->execute();
  $entity_storage = \Drupal::entityTypeManager()->getStorage('user');
  $entities = array_merge($entities, $entity_storage->loadMultiple($result));

  // Update URL aliases.
  foreach ($entities as $entity) {
    \Drupal::service('pathauto.generator')->updateEntityAlias($entity, 'update');
  }

This works fine and could be used on a small site but considering the real world scenarios we generally perform this type of one-time operation in hook_update_N() and for larger sites, we may have memory issues, which we can resolve by involving batch API to run the updates in the batch. 

Using Batch API in hook_update_n()

As the update process in itself a batch process, so we can’t just use batch_set() to execute our batch process for URL alias update, we need to break the task into smaller chunks and use the $sandbox variable which is passed by reference to update function to track the progress.  

The whole process could be broken down into 4 steps:

  • Collect number of nodes / entities we would be updating the URL for.
  • Use the $sandbox to store information needed to track progress.
  • Process nodes in groups of a certain number (say 20, in our case ).
  • And finally setting the finished status based on progress.
function mymodule_update_8100(&$sandbox) {
  $entities = [];
  $entities['node'] = \Drupal::entityQuery('node')->execute();
  $entities['user'] = \Drupal::entityQuery('user')->execute();
  $entities['taxonomy_term'] = \Drupal::entityQuery('taxonomy_term')->execute();
  $result = [];

  foreach ($entities as $type => $entity_list) {
    foreach ($entity_list as $entity_id) {
      $result[] = [
        'entity_type' => $type,
        'id' => $entity_id,
      ];
    }
  }

  // Use the sandbox to store the information needed to track progression.
  if (!isset($sandbox['current']))
  {
    // The count of entities visited so far.
    $sandbox['current'] = 0;
    // Total entities that must be visited.
    $sandbox['max'] = count($result);
    // A place to store messages during the run.
  }

  // Process entities by groups of 20.
  // When a group is processed, the batch update engine determines
  // whether it should continue processing in the same request or provide
  // progress feedback to the user and wait for the next request.
  $limit = 20;
  $result = array_slice($result, $sandbox['current'], $limit);

  foreach ($result as $row) {
    $entity_storage = \Drupal::entityTypeManager()->getStorage($row['entity_type']);
    $entity = $entity_storage->load($row['id']);

    // Update Entity URL alias.
    \Drupal::service('pathauto.generator')->updateEntityAlias($entity, 'update');

    // Update our progress information.
    $sandbox['current']++;
  }

  $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['current'] / $sandbox['max']);

  if ($sandbox['#finished'] >= 1) {
    return t('The batch URL Alias update is finished.');
  }

}

The process of loading the entities will differ in case we are just updating a single entity type say nodes. In that case, we can use loadMultiple to load all the entities at once per single batch operation. That’s a kind of trade-off we have to do according to our requirements. The crucial part is using sandbox variable and splitting the job into chunks for batch processing.

Oct 23 2016
Oct 23

CSSgram module supplements Drupal Image styling experience by making Instagram like filters available to your Drupal 8 site images, we do this with help of CSSgram library. 

Beauty of this module is, it simply uses css to beautify your image.

cssgram-filters-sample

Few CSSGram sample filters applied to an image.

How CSSGram Module works?

CSSGram module uses CSSGram Library for adding filter effects via CSS to the image fields. Module extends Field Formatter Settings to add image filter for that particular field. CSSGram extends field formatter settings and hence these filters can be applied on top of the existing available image formatters and image presets. Allowing you to use your desired image preset along with CSSGram filters.
 

Using CSSGram

  1. Download and enable CSSGram module (https://www.drupal.org/project/cssgram)

  2. Visit Manage Display of content type and for the desired image field, click on the setting link under format column.

  3. Select Filter option lets us choose from the available image filters. Just select the desired image filter and hit update button.

third-party-settings-cssgram
  1. Save the settings and visit the content display page.

Developer Support

Devs have the option to use these filters anywhere on the site by just attaching the ‘cssgram/cssgram’ library and then applying any of the available css filter class to the wrapper element.


function mymodule_preprocess_field(&$variables) {
    // Add desired css class.
    $variables['attributes']['class'] = 'kelvin';
    // Attach cssgram library.
    $variables['#attached']['library'][] = 'cssgram/cssgram';
}
Mar 29 2016
Mar 29

There are scenarios when outside of a view we need to fetch results of any particular view. This is a very specific case when we just want the records compiled by Drupal Views. In that case obviously views api or views hooks are of no use, as we are not looking for event driven activities. It’s just the results needs to be fetched using views because of the complexity of the criterion on which these results are computed.

We won’t go for this approach when we want simple results like all the nodes of a specific content type sorted alphabetically. In that case, simply db_select would be better choice again depending on various project specific factors. In general, we can’t actually tag any approach as the best or optimal for general purpose as these are scenario specifics.

In our case, the scenario is we have a very complex view having good amount of filters or simply i would say the corresponding sql query is complex. Now in that case outside of the view we have two options to get results:

  1. Function views_get_view_result()
  2. Executing corresponding SQL Query

Approach 1: Function views_get_view_result():

// To get the result of a view.
views_get_view_result($name, $display_id = NULL)  // views.module

This looks promising, provided by views module. Accepts views name and display_id as parameter and simply fetches you the result. What we wanted is accomplished.

Limitation: In case of pagination, probably we want to get all the results and this approach fails there. We cannot fetch all the results if the corresponding view limits results to specific number per page. So, what should we do next!


Let’s look at our second approach, would that be useful in our case?

Approach 2: Executing corresponding SQL Query:

We can directly execute static sql query, which is a good solution for this scenario. But when we want to enjoy further flexibilities of this approach, say we want to change the query a bit then what? It is possible with this approach but the method is not recommendable. Infact best way to proceed further using this approach is to convert static query into dynamic drupal query which is a tedious process. So, how to achieve this?

Final Solution:

Looking at how the views work, we got a very promising structured way of solving all our related problems. Views provide several methods for views object which can be used to get all the results with/without customization of a particular view. Let’s see how:

$view = views_get_view(‘example_view’);
$view->build($display_id);
$view->query->limit = 0;
$view->execute();
$results = $view->result;

So, we finally have all the results of a view. As far as the above implementation is concerned, we basically are fetching a view, then building a specific display of that view and after customization we are finally executing it to get the results. In simple language, we are creating a similar temporary instance to get the desired results.


Hope this helps you (smile).  

Dec 16 2015
Dec 16

Web Distributed Authoring and Versioning (WebDAV) is an extension of the Hypertext Transfer Protocol (HTTP) that allows for remote Web content authoring operations. There are various clients available to perform such operations but sometimes you need to access webdav and perform operations programmatically. We had such requirement, and we chose to use curl as our HTTP client.

For intro to curl commands on webdav, follow this link. In this post will see how a much more complex scenario where curl has to be authorised by 2 Factor authentication to do those operations.

Two Factor Authentication: Two-factor authentication (also known as 2FA or 2-Step Verification) is a pattern of authentication in which users are asked to authenticate to use a service by following two steps -- typically password and OTP ( One time login ) delivered by SMS but could also be other set of combinations.

Implementation of 2FA in webdav context typically includes authentication of client-server transactions using certificates. So, we need a brief understanding of the required certificates. For 2FA Client is provided with three things:

  1. crt file (Let's say cert.crt)
  2. key file (cert.key)
  3. passphrase (passphrase)

The crt and key files represent both parts of a certificate, key being the private key to the certificate and crt being the signed certificate. Assuming you have these three things, our very first step is to generate valid pem/p12 file for using it with curl command to access webdav server. A PEM file contains both certficate and private key. Depending on our server's operating system we either have to use pem or p12 file format. A p12 file is generated from pem file only.

For linux based servers, we use .pem file.
For OS X Mac based servers, we use .p12 file.

Generation of pem/p12 file:

Generation of valid pem/p12 file is a series of sequential steps and requires all the above things. Firstly put both files in a directory and in the terminal moce to that directory. Now change the permission of .key file.

chmod 600 cert.key

Now we need to enclose passphrase to key file. So following command would be used and when terminal prompts for old/new passphrase provide the passphrase you are having each time.

 ssh-keygen -p -f cert.key

Now we have to generate either pem file:

cat cert.crt cert.key > cert.pem

In case our server is OS X (mac) based, we also need to generate .p12 file:

openssl pkcs12 -export -in cert.pem -inkey cert.key -out cert.p12

So now we are finally ready to use curl command for 2fa while sending requests.

Curl Command for 2FA:

For linux environment, we need both pem and crt file:

curl --cert cert.pem:'passphrase' --cacert cert.crt --user 'user:pass' -T '/path/to/file.txt' 'https://example.com/test/" 

For OS X environment, we need only p12 file:

curl --cert cert.p12:'passphrase' --user 'user:pass' -T '/path/to/file.txt' 'https://example.com/test/"

In this way we can use curl commands for two factor authentication on Webdav Servers.

Dec 16 2015
Dec 16

Curl is a command line tool for doing all sorts of URL manipulations and transfers, but this particular post will focus on how to use curl for managing (read/ delete/ rename/ upload) files on Webdav Server. While the second part of the post will cover implementation of Two Factor Authentication for the same. I'll assume that you know how to execute 'curl' command using terminal and invoke 'curl --help' or 'curl --manual' to get basic information about it.

Assume we have following Data:

Webdav URL: http://example.com/webdav
Username: user
Password: pass

Note: It is recommended to read article completely (both management and curl options) before implementation.

  • Reading Files/Folders on Webdav Server:

Reading files or folders on Webdav Server is simply implementation of Curl Get request. So, the command would be:

curl 'http://example.com/webdav'

Also Optionally, you can get response headers from the webdav server, based on which you can get to know about completion of the operation. Click here

  • Deleting Files/Folders on Webdav Server:

For deleting an existing file or folder on Webdav Server we need to send DELETE request followed by path to the folder/file of the webdav server. Let's say we need to delete test folder on webdav server, so our command would be:

curl -X DELETE 'http://example.com/webdav/test'

Similarly for deleting file say test.txt:

curl -X DELETE 'http://example.com/webdav/test.txt'

On few Webdav Servers, it is possible that curl -X DELETE command is not able to delete folder because of index.* file in that folder. In that case try renaming index file first.

  • Renaming File on Webdav Server:

Rename operation can be performed using -X MOVE request. Let's say we wanna rename old.txt to new.txt:

curl -X MOVE --header 'Destination:http://example.org/new.txt' 'http://example.com/old.txt'
  • Creating new foder on Webdav Server:

To create new directory on webdav server use -X MKCOL request:

curl -X MKCOL 'http://example.com/new_folder'
  • Uploading File on Webdav Server:

For uploading file on webdav server use PUT request. Let's say src file is /path/to/local/file.txt and we want to upload it in test folder on webdav server, so our command would be:

curl -T '/path/to/local/file.txt' 'http://example.com/test/'

Curl provides various options which makes our life easier. In this article we will focus on few necessary options required for basic webdav access.

  • Username/Password

To specify username and password for webdav, use '--user' or use alias '-u':

curl --user 'user:pass' 'http://example.com'
  • HTTP Authentication

Authentication is the ability to tell the server your username and password so that it can verify that you're allowed to do the request you're doing. Commonly used Authentications in case of webdav are : Basic and Digest.

Usage:

curl --user 'user:pass' 'http://example.com' --basic

curl --user 'user:pass' 'http://example.com' --digest

It is always better to let curl decide the authentication method to be used and hence use --anyauth:

curl --user 'user:pass' 'http://example.com' --anyauth
  • Get Response Code

We definitely need a way to know if our request performed desired operation successfully on Remote Webdav Server or not. In that case, there are various options provided by curl which could be useful. One of them is to get response code and hence analyze the behaviour.

curl --user 'user:pass' -X DELETE 'http://example.com/test' -sw '%{http_code}'

This command will output only the response code sent in response header by Webdav Server. Let's say the output is: 200, this means operation completed successfully. So, different http codes correspond to different meaning. For detailed information follow this link.

To use curl command for Two factor Authentication (2FA) on Webdav follow this post.

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