Drupal
  |  28th November, 2016

REST API Explorations in Drupal 8 - Primer

SUMIT MADAN

This article assumes you are familiar with what RESTful is & what do we mean when we use the term REST API. Some of you might have already worked with RESTful Web Services module in D7, it exposes all entity types as web services using REST architecture. Drupal 8 out of the box is RESTful with core support. All entities (provided by core + ones created using Entity API) are RESTful resources.

To explore the RESTful nature of Drupal 8, we will need to enable the following modules:

In Core

  • HAL - Serializes entities using Hypertext Application Language.
  • HTTP Basic Authentication - Provides the HTTP Basic authentication provider.
  • RESTful Web Services - Exposes entities and other resources as RESTful web API
  • Serialization - Provides a service for (de)serializing data to/from formats such as JSON and XML.

Contributed

  • REST UI - Provides a user interface to manage REST resources.

RESTful Resources

Every entity in D8 is a resource, which has an end point. Since, its RESTful, the same end-point is used for CRUD (Create, Read, Update, Delete) operations with different HTTP verbs. Postman is an excellent tool to explore / test RESTful services.  Drupal 8 allows you to selectively choose & enable a REST API. e.g., we can choose to expose only nodes via a REST API & not other entities like users, taxonomy, comments etc.

After enabling REST_UI module we can see list of all RESTful resources at /admin/config/services/rest. In addition to ability to choose the entity one can enable, we can also choose the authentication method per resource & enable specific CRUD operations per resource.

Resource Settings

Let us take a look at what the REST APIs for User entity would be after we save the configuration in the above screenshot.

User

POST

http://domain.com/entity/user?_format=hal_json
{
 "_links": {
   "type": {
     "href": "http://domain.com/rest/type/user/user"
   }
 },
 "name": {
   "value":"testuser"
 },
 "mail":{
   "value":"[email protected]"
 },
 "pass":{
   "value":"testpass"
 },
 "status": {
   "value": 1
 }
}

Header

X-CSRF-Token: Get from http://domain.com/rest/session/token
Content-Type: application/hal+json
Accept: application/hal+json
Authorization: Basic (hashed username and password)

Note: Drupal 8 doesn't allow anonymous user to send a POST on user resource. It is already fixed in 8.3.x branch but for now we can pass the credentials of the user who have permission to create users. If you are interested in taking a deeper look at the issue, you can follow https://www.drupal.org/node/2291055.

Response: You will get a user object with "200 OK" response code

 

PATCH

http://domain.com/user/{uid}?_format=hal_json
{
 "_links": {
   "type": {
     "href": "http://domain.com/rest/type/user/user"
   }
 },
 "pass":[{"existing":"testpass"}],
 "mail":{
   "value":"[email protected]"
 }
}

Note: Now as user have permission to update his own profile so we can pass current user's credentials in authentication header.

Response: You will get "204 No Content" in response code.

 

GET

http://domain.com/user/{uid}?_format=hal_json

Response: You will get a user object with "200 OK" response code.

 

DELETE

http://domain.com/user/{uid}?_format=hal_json

Response: You will get "204 No Content" in response code.

RESTful Views and Authentication

Drupal 8 also allows us to export views as a REST service. It allows you to use all the available authentication mechanism in views itself.

JSON API Module

JSON API module provides a new format called "api_json" which is soon becoming the de-facto standard for Javascript Frontend frameworks, If you plan to use completely de-coupled Drupal with frontend framework like Angular / React / Ember then its worth a look. To read more about JSON API you can visit the site.

Looking for a Drupal partner ?

We are drupal 8 ready

Drupal
  |  24th November, 2016

Implementing #autocomplete in Drupal 8 with Custom Callbacks

PURUSHOTAM RAI

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.

Looking for a Drupal partner ?

We are drupal 8 ready

Drupal
  |  8th November, 2016

New Module - AddToCalendar Drupal Integration

PURUSHOTAM RAI

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)

    • The module has both D8 and a backported D7 versions.

  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.

 

Looking for a Drupal partner ?

We are drupal 8 ready

Drupal
  |  4th November, 2016

Polymer Element for zooming image on mouseover - <polymer-zoomove>

SAKET KUMAR

Zooming-in on images with mouse hover is a common feature used on E-commerce websites to let buyers see details of the products. We had a similar requirement on a polymer project for which we integrated the Zoomove jQuery plugin into polymer to use it as reusable component and now open sourcing it. 

Demo - https://qed42.github.io/polymer-zoomove/

Github project -- https://github.com/qed42/polymer-zoomove 

Using zoomove-polymer Element in your Project

Install this element using bower in your project 

$ bower install zoomove-polymer --save-dev

or you can grab the element  from the github from here - https://github.com/qed42/polymer-zoomove  and place it in the components for referencing.

Now you can add the element in your html page using html imports

<link rel="import" href="../polymer-zoomove.html">

And in your HTML you can now use the tag as follows,

<polymer-zoomove
    image-path="http://lorempixel.com/600/600/animals/"
    image-cover="false">
</polymer-zoomove>
 
<polymer-zoomove
    image-path="http://lorempixel.com/600/600/animals/"
    image-scale="5">
</polymer-zoomove>
 
<polymer-zoomove
    image-path="http://lorempixel.com/600/600/animals/"
    image-cover="true">
</polymer-zoomove>

polymer-zoomove

Available attributes for this elements are:

  1. image-path - The url of the photo to be displayed.
  2. image-scale - Sets the zoom size that should be applied to the image.
  3. image-move - Choose whether the image should move with the mouse move
  4. image-over - With 'over' it is possible to define whether the image may be above
  5. image-cursor - Define the cursor pointer or default

Looking for a Drupal partner ?

We are drupal 8 ready

Digital
  |  31st October, 2016

New Polymer Element for Balanced Photo Galleries - <polymer-bg>

SAKET KUMAR

Balanced Gallery is a jQuery plugin that evenly distributes photos across rows or columns, making the most of the space provided. Photos are scaled based on the size of the 'container' element by default, making Balanced Gallery a great choice for responsive websites. On a recent project we needed a similar photo gallery for Polymer based frontend, hece we created and are open sourcing <polymer-bg> : a polymer element to evenly distribute the photos across rows or columns using balanced gallery plugin.

Demo - https://qed42.github.io/polymer-bg/

Github project -- https://github.com/qed42/polymer-bg ( Test, Use, Fork and let us know of your issues )

Using polymer-bg Element in your Project

Install this using bower

$ bower install polymer-bg --save-dev

Add the element element using html imports

<link rel="import" href="../polymer-bg.html">

Now you can use the tag, pass the images in the <img> tag and use the tag attributes to configure the settings as shown, exhaustive list of attributes below the example:

<polymer-bg bg-background="http://placekitten.com/335/283" ideal-width="300" bg-orientation="vertical">
   <img src="http://placekitten.com/335/283" />
   <img src="http://placekitten.com/325/596" />
   <img src="http://placekitten.com/580/365" />
   <img src="http://placekitten.com/282/581" />
   <img src="http://placekitten.com/503/319" />
   <img src="http://placekitten.com/549/577" />
   <img src="http://placekitten.com/355/493" />
   <img src="http://placekitten.com/500/150" />
   <img src="http://placekitten.com/360/529" />
   <img src="http://placekitten.com/589/361" />
   <img src="http://placekitten.com/452/462" />
   <img src="http://placekitten.com/550/304" />
   <img src="http://placekitten.com/352/204" />
   <img src="http://placekitten.com/400/220" />
</polymer-bg>

 

ballanced gallaries

Available attributes

  1. auto-resize - re-partition and resize the images when the window size changes
  2. bg-background - the css properties of the gallery's containing element
  3. ideal-height - only used for horizontal galleries, defaults to half the containing element's height
  4. ideal-width - only used for vertical galleries, defaults to 1/4 of the containing element's width
  5. maintain-order - keeps images in their original order, setting to 'false' can create a slightly better balance between rows
  6. bg-orientation - 'horizontal' galleries are made of rows and scroll vertically; 'vertical' galleries are made of columns and scroll horizontally
  7. bg-padding - Space in pixels between images

Start your Digital

Transformation journey

Consultancy | Design | Drupal

Digital
  |  27th October, 2016

Factory for Data Operations on SQLite using Ionic

abhay.kumar

One of the pain points in hybrid app development is data persistence & data storage. Though LocalStorage can be used for storing less critical data like cache, devs usually look at SQLite for consistent data storage backend. SQLite works fine for both the platforms (Android & iOS). 

In this post we discuss how to efficiently work with SQLite using a simple factory that can be used for doing simple operations on your SQLite Database. 

SQLite can be accessed natively only, so you need to install the cordova plugin for SQLite.

Download ngCordova dependancies

bower install ngCordova

Include ng-cordova.min.js in your index.html file before cordova.js and after your AngularJS / Ionic file (since ngCordova depends on AngularJS).

<script src="lib/ngCordova/dist/ng-cordova.js"></script>
<script src="cordova.js"></script>

Inject as an Angular dependency

angular.module('myApp', ['ngCordova'])

Install SQLite Cordova Plugin

cordova plugin add https://github.com/litehelpers/Cordova-sqlite-storage.git

Declare a global variable 

var db = null;

So that 'db' is accessible through our the scope the app.

In your app.js :-

if (window.cordova) {
    $rootScope.showHeader = false;
    db = $cordovaSQLite.openDB({ name: 'myapp.db', location: 'default' });
} else {
    db = window.openDatabase("myapp.db", "1.0", "My app", -1);
}

'myapp.db' is the name of your DB. The github page for cordova-sqlite-storage plugin have examples on how to do different operations on the database, below is a simple factory that can make these operations clean and readable:

.factory('DBA', function($cordovaSQLite, $q, $ionicPlatform) {
        var self = this;
        self.query = function(query, parameters) {
            parameters = parameters || [];
            var q = $q.defer();
            $ionicPlatform.ready(function() {
                $cordovaSQLite.execute(db, query, parameters)
                    .then(function(result) {
                        q.resolve(result);
                    }, function(error) {
                        q.reject(error);
                    });
            });
            return q.promise;
        }
        self.getAll = function(result) {
            var output = [];
            for (var i = 0; i < result.rows.length; i++) {
                output.push(result.rows.item(i));
            }
            return output;
        }
        self.getById = function(result) {
            var output = null;
            output = angular.copy(result.rows.item(0));
            return output;
        }
        return self;
    })
    .factory('Data', function($cordovaSQLite, DBA) {
        var self = this;
        self.all = function() {
            return DBA.query("SELECT key, value FROM your_table")
                .then(function(result) {
                    return DBA.getAll(result);
                });
        }
        self.get = function(key) {
            var parameters = [key];
            return DBA.query("SELECT key , value FROM your_table WHERE key = (?)", parameters)
                .then(function(result) {
                    return DBA.getById(result);
                });
        }
        self.add = function(obj) {
            var parameters = [obj.key, obj.name];
            return DBA.query("INSERT INTO your_table (key , value) VALUES (?,?)", parameters);
        }
        self.remove = function(obj) {
            var parameters = [obj.key];
            return DBA.query("DELETE FROM your_table WHERE key = (?)", parameters);
        }
        self.update = function(oldkey, newDataObj) {
            var parameters = [newDataObj.key, newDataObj.value, oldkey];
            return DBA.query("UPDATE your_table SET key = (?), value = (?) WHERE key = (?)", parameters);
        }
        return self;
    })

Disclaimer -- This factory code is for demonstration only, please sanitise and secure your user inputs. Additionally, you can make "your_table" dynamic depending on your use-case.

You may use this factory and do the CRUD operations

Add name in db  :-

var nameObj = {};
nameObj.key = "name ";
nameObj.name = "Abhay Kumar";
Data.add(nameObj);

Get name :-

Data.get("name").then(function(result) {
    userName = result.value;
});

Update name :-

var  nameObj = {};
nameObj.key = "name";
nameObj.value = "QED42";
Data.update("name", nameObj).then(function(result) {
    console.log("Result :", result);
})

Delete name :-

var nameObj = {};
nameObj.key = "name";
Data.remove(nameObj).then(function(result) {
    console.log("Result :", result);
});

Hope you find it useful, Let us know in Comments if you extend this factory or have a better one!

Start your Digital

Transformation journey

Consultancy | Design | Drupal

Drupal
  |  25th October, 2016

New Module - CSSgram recreating Instagram like filters for Drupal 8

PURUSHOTAM RAI

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';
}

 

Looking for a Drupal partner ?

We are drupal 8 ready

Drupal
  |  25th October, 2016

Programmatically updating URL aliases using Batch API in Drupal 8

PURUSHOTAM RAI

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.

Looking for a Drupal partner ?

We are drupal 8 ready