Upgrade Your Drupal Skills

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

See Advanced Courses NAH, I know Enough
Jun 01 2020
Jun 01

Drupal 8's Layout Builder allows content editors and site builders to easily and quickly create visual layouts for displaying content. Users can customize how content is arranged on a single page, or across types of content, or even create custom landing pages with an easy to use drag-and-drop interface. Our recently contributed module - The Layout Builder Asset Injector module is definitely not a replacement for theming, but it provides site administrators with a quick and easy approach. The Layout Builder Asset Injector module allows site builders to add CSS and JS to the layout builder blocks. 

| Benefits 

  • It allows you to inject CSS and JS within the layout builder blocks without the need to add it in the codebase.

  • Append unique CSS and scripts specific to layout builder blocks

  • It provides content editors with basic knowledge of CSS and JS to style their individual layout builder blocks and add short JS scripts to modify the behaviour.

  • Instead of adding CSS and JS on a page level, this module allows you to inject them on an individual block.

| CSS Injector

CSS Injector allows administrators to inject CSS into the page output based on configurable rules. It's useful for adding simple CSS tweaks without modifying a site's official theme.

| JS  Injector

JS Injector allows administrators to inject JS into the page output based on configurable rules. It's useful for adding simple JS tweaks without modifying a site's official theme.

Note: Block class will be appended automatically to each CSS.

For example when we add a CSS as given below:

h1.node__title {

  background-color:red;

}

It will automatically append the class of the block for which CSS is added once saved.

.block-field-blocknodepagebody h2.node__title{

   background-color:red;

}

 

Layout Builder Asset Injector Drupal module

| How does it work? 

  • Enable the Layout Builder Asset module

  • After you enable the module, enable the layout option from the manage display section as shown below:

Layout Builder Asset Injector Drupal module

Layout Builder Asset Injector Drupal module

Layout Builder Asset Injector Drupal module

To understand Layout Builder in detail refer  https://www.drupal.org/docs/8/core/modules/layout-builder

  • You can add your styling and scripts under the CSS and JS fields respectively

  • To make the styling and scripts specific to a block, add classes under the Classes text field. This class should be unique so as to reflect scripts and styling specific to a block.

  • On adding the CSS and JS and saving the configuration, it will automatically prefix the CSS with a custom class added.

Note: You don't need to add it manually.

Layout Builder Asset Injector Drupal module

Layout Builder Asset Injector Drupal module

 https://www.youtube.com/watch?v=3Ogya_66GDU&feature=youtu.be

| Implementing the Layout Builder asset injector module 

Click here for the steps to install the Layout Builder asset injector module - https://www.drupal.org/docs/8/extending-drupal-8/installing-drupal-8-modules

[embedded content]

| Developer support

Kishor Kolekar: https://www.drupal.org/u/kishor_kolekar

Harshal Pradhan: https://www.drupal.org/u/hash6

Abhishek Mazumdar: https://www.drupal.org/u/abhisekmazumdar

Hardik Patel: https://www.drupal.org/u/hardik_patel_12

Naresh Bavaskar: https://www.drupal.org/u/naresh_bavaskar

| Module download link 

https://www.drupal.org/project/layout_builder_asset

| Alternative module links 

https://www.drupal.org/project/asset_injector

https://www.drupal.org/project/layout_builder_styles

Jun 01 2020
Jun 01

While trying to build a form, I found that it would be useful to have a one-click/touch option to clear text field values instead of selecting all the text and removing it. Even though this appears like a small issue, solving this would make users life easy! This was the driving force behind creating the Clear Field Values module for Drupal 8. The Clear Field Values module provides a way to clear data in the text field values with a single click/touch.

| Clear Field Values module

This module adds a button to clear text field values in one click/touch and provides two options at present, one is a simple cross button and the other is using font awesome icons.

| Benefits 

  • Helps clear the entire input field without having to delete the whole text manually 

  • Clears a pre-suggested input present in the input field, that the user does not require.

| Implementing the Clear Field Values module

Follow these steps to configure the Clear Field: 

  1. Go to /admin/config/clear-field/settings steps

  2. Enable Clear Field by selecting the checkbox 

  3. Next, select the type of Button:

    1. Simple X 

    2. Fontawesome for all generic fields

  4. You can then add the text and tooltip which you need to add along with icon or keep it empty if not required.

  5. Add classes for the text field elements where you need the cross button to be visible.

Step 1 : Move to  Settings page /admin/config/clear-field/settings 

Step 2: Enable Clear Field Checkbox

Select Type of Button

  1. Text along with X for all

Selecting the above option would add X(cross) with text beside the text field

  1. Font Awesome for all fields generic

Enable FontAwesome module https://www.drupal.org/project/fontawesome and you can add the cross button

Default values for the text of the icon and tooltip are Clear Field & Clear field value which can be overridden.

Default values for the Classes are form-text form-email separated by a space.

For a custom-form add custom classes defined under element’s attributes under Classes field.

[embedded content]

| Developer support

This module is supported by 

  1. Harshal Pradhan https://www.drupal.org/u/hash6

  2. Kiran Kadam https://www.drupal.org/u/kirankadam911

| Module download links

https://www.drupal.org/project/clear_field

https://www.drupal.org/project/clear_field/releases/8.x-1.x-dev

Do send in your feedback around this module! We would love to hear from you. 
 

Reference: https://www.geeksforgeeks.org/html-clearing-the-input-field/

May 20 2020
May 20

Drupal has witnessed a healthy growth over these years, with ample scope to expedite this growth. One way to achieve this is via a tool that makes the site creator’s life easy. A CMS platform should invariably be extremely user friendly and easy in maintaining as well as building. The wait is over! Cohesion DX8 is a site builder tool which allows a site maintainer/site configurator to create/maintain the site with low coding and more configuration.

Benefits of Cohesion 

  1. Low code 
  2. Highly configurable
  3. Easy maintainable
  4. Easily modifiable 
  5. Highly scalable
  6. Component-based driven
  7. Less developer dependent

Cohesion

Cohesion DX8 provides a contributed module and theme which needs to be installed in Drupal, quite similar to the way we install other modules and themes. However, to use this service, it is required to purchase a cohesion package that provides us with an API Key, Agency Key, Site ID, and an API Server URL which needs to be inserted in the Account Settings form.

Cohesion DX8

Here are the settings and key features of Cohesion DX8:

  1. Website Settings
  2. Style Settings
  3. Components
  4. Templates

Cohesion DX8 provides an in-site configuration form which takes care of your branding requirements. You can add your own branding elements like fonts, colours, icons, grid settings, SCSS variables, etc. directly onto the site. And there is absolutely no need to make any changes in the theme folder as well as the theme settings. Doesn’t this sound cool! Your basic frontend settings would be handy to you and you can modify it directly from the site without asking for help from a developer.

Website Settings

Cohesion DX8

Style Settings

It’s not over yet, with Cohesion DX8 you can create/modify styles of a site. Cohesion DX8 provides settings to update the base style or to create custom styles. 

Cohesion DX8

And this is how the default preview and configuration pane looks like.

[embedded content]

Now let’s understand what comes under Base styling and Custom styling

Base styling - Styling which is going to be consistent throughout the site, e.g., font-size of body. Font formatting of Headings, Style of a button, style of link, etc.

Custom styling - Styling which will be different for each instance or as a variation of base style. E.g., Big button, Small button, Read More Link, Consistent layout related styles (Padding Top & Bottom Large, Padding Top & Bottom Small), Social icons theming, etc.

Let’s look at the example of CTA link styling.

  • CTA style structure
    • Link styling
    • After pseudo-element styling
    • On hover pseudo-element styling
  • Style properties required for CTA link and those properties are added in the config form through properties button on the top right corner of the styling pane
  • Styling properties required for pseudo-element styling
  • Styling properties required for pseudo-element after hover

COHESION DX8

COHESION DX8

cohesion DX8

Component Builder

Now let’s introduce the coolest feature of the Cohesion Dx8 -  a Component Builder. Cohesion Dx8 follows a complete component-based development approach. So, what is the component-based development approach? 

Cohesion Dx8 provides a list of elements. By using these elements even simple and complex components can be created and be made configurable by creating a component form. The amazing part is that everything can be built by mere drag and drop. Doesn’t it sound super user friendly?

Let’s look at an example of a Hero Component and figure out which elements could be part of this component?  Let’s create a list of elements(atoms).

  • Hero Image
  • Hero Title
  • Hero Description
  • CTA button

Let’s consider the below-displayed hero component that we are trying to achieve.

Cohesion dx8

Based on the Hero component design, the hero component structure would look similar as described in the below image.

Cohesion DX8

A component will be created which will have the above-mentioned elements. But what about styling? This is the real beauty of Cohesion Dx8. It allows us to create a base style as well as a custom style and those can be appended to the component. Doesn’t it sound fascinating?

Here would like to take you back to the styling part of the section where we have styled CTA link. Now, here in the above component CTA link element can directly assign the style which has been created under custom style. And as per image, styling can be appended to CTA Link element.

COHESION DX8

Layout

There are multiple ways to create different layouts. Here, with Cohesion DX8, the layout can be defined by a Layout Category component and also component dropzone element can be provided to add other components. 

Does it mean? Woah! Yes, it means nesting use of the component is also possible. I won’t provide the screenshot for this example right now.

Template

There are four main templates:

  • Master Template
  • Content Template
  • Views Template
  • Menu Template

The template names clearly state the use of each template, but, I am sure you must have some questions related to master templates. 

What will it hold? The thumb rule is, this template should be least modifiable, means, it should not be updated frequently. We can ensure this by using only consistent section/components/regions added to it.

I hope this blog was helpful to understand the basic outline of Cohesion Dx8. Stay tuned! We have an upcoming blog that will talk about the advanced usage of templates. 

Apr 18 2020
Apr 18

What divides us pales in comparison to what unites us.

     - Ted Kennedy

This especially rings true in the time we're living in now.

Because of a much needed country-wide lockdown here in India to prevent the spread of COVID-19 disease, everyone has been locked-out in their homes and have socially distanced themselves form their communities. This, of course, includes the Drupal India community as well. And what better way to bring this community together than an online pan-India contribution weekend organized by Drupal India Association.

First-ever fully-remote pan-India contribution event

Today, on 18th April 2020, Drupal India Association (DIA) conducted it's first online 'Contribution Weekend'. It's the first event of its kind in that it was completely remote and saw participation from Drupalers across India.

We had 116 RSVPs, 22 Mentors, 96 Issues on Contribkanban board.

Thank you, organizers!

Before I talk more about the event, I would like to give a huge shout-out to everyone who was involved in the planning of this amazing event and a special shout-out to Rakhi Mandhania, Surabhi Gokte & Sharmila Kumaran (#WomenInDrupal FTW) for managing this so well. The planning team worked super hard to make this a success. They took time out of their personal and professional lives to have various meetings for issue triaging, communication and marketing for the event, arranging for speakers and much more. Thank you, everyone, who contributed to planning this.

The introductory hour for new/first-time contributors

DIA Contribution Weekend April 2020

We started the event at 8:45 in the morning,(Yes, That's 8:45 in the morning!) with a special session for new and first-time contributors. A big shout-out to all the newcomers and especially the mentors who took time out this early in the morning to volunteer for mentoring them. And boy did we have a lot of mentors! We had a mentor-to-mentee ratio of 5:1! Don't you feel special now mentees!

Main event

The main event started at 10:00 AM with more than 40 people joining in. And more people kept pouring in as the event went by. At a time we had around 70 people in the call!

Drupal India Association

Rakhi, our host, got us started and handed over the call to Mukesh Agarwal. Mukesh is CEO of Innoraft, one of the member organizations of DIA, and introduced us to DIA, its goals and purposes and the role it is going to play in representing the Indian Drupal community to the world. Major goals of DIA can be summarised as:

  1. Becoming an exemplary community leadership organization to the rest of the Drupal world
  2. Developing thought leaders in Drupal who will enhance India’s image in the Drupal world
  3. Becoming a major influencer and enabler in the adoption of Drupal in the Gulf & ASEAN region
  4. Making India an innovation hub for Drupal
  5. Making Impactful Contribution to community & the extended stakeholders of Drupal

Drupal Association needs our help!

Mukesh's introduction was followed by Tanisha Kalia, a representative from the Drupal Association, and she talked about DA and the financial effects of COVID-19 on DA and the larger Drupal community. DA is a very important entity for the whole Drupal community. Among the numerous things they do for the community, a few are:

  • Managing, hosting and conducting DrupalCons
  • Managing the infrastructure of drupal.org

Tanisha mentioned that the majority of DA's funding comes from the revenues of DrupalCon events and the almost inevitable possibility of cancellation/postponement of DrupalCon NA has put DA in a big financial hole. DA needs the community's support. 
Tanisha encouraged community members to either donate to DA directly via #DrupalCares initiative or support DA by purchasing/subscribing to be a member of DA, the cost of which starts from just 15 USD.

As of this moment, DA has reached 25% of its USD 500,000 goal:

DrupalCares Goal DIA Contribution Weekend 2020

As an individual member or an organization, you can help DA sustain the COVID-19 Impact and help reach its goal. Read more here: https://bit.ly/2VyKbmM

Tanisha also talked about the initiative taken by prominent members in the community to encourage donations and financial contributions. Some of these are:

  • From now until April 30, Vanessa and Dries are offering a dollar-for-dollar match for new, individual contributions to the Drupal Association - up to a total match of $100K! More details: https://bit.ly/2KefaPJ
  • Gábor Hojtsy, Acquia, will be donating 9EUR for every module ported to Drupal 9 for a total of up to 900EUR. More details: https://bit.ly/2wS5uaE
  • Jeff Geerling, @geerlingguy, will be donating 2$ for every like on his recent Youtube video for a total of up to 1000USD. Video link: https://youtu.be/fdk7zUwDQdM

A big thanks to all of you! 

Thanks to an amazing community, we had 6 new individual memberships registered before the introduction call finished.

Code-of-Conduct and Tips for the day

Next up Rakhi communicated the Code-of-Conduct to all the contributors and shared the various channels for communication that people could use throughout the day.
We were using channels on DIA slack. There were various channels set up for clear and effective communication:

  1. #new-contributors: For first-timers and newcomers.
  2. #regular-contributors: For experienced contributors.
  3. #issue-reviews-contribution day: For issues ready for review.

This setup was really efficient and was instrumental in keeping people engaged and active throughout the day.

Hussain kicking off the day 

Rakhi then handed over to Hussain Abbas from Axelerant who joined us as guest speaker for the sprint. He started by talking about the power of open source and how open source has in past sustained and in fact grown in face of financial crises. Truly, Open Source is here to stay!
Hussain then proceeded with talking about the path for Drupal core and contributes projects to Drupal 9 and how we can make sure that projects we frequently use and maintain can be made ready for Drupal 9. The Drupal community has created various tools to help us with these:

  1. drupal-check: A PHP CLI tool to get a report of any deprecated code used. This can be integrated into build processes and continuous integration systems.
  2. drupal-rector: A tool for automated deprecation fixes for some common cases.
  3. Upgrade status module: A wrapper module around drupal-check internals that generates a full site report of contributed and custom modules used in the site. This report can be used to assess the overall Drupal 9 compatibility of the modules.
  4. Upgrade rector: A user interface on top of drupal-rector, which also integrated with Upgrade Status.

Read more about these tools in the official Drupal documentation: https://bit.ly/3apZhAA

After Hussain finished his session and just before we were about to wrap up the call, we realized that Rachel Lawson, Community Liason - Drupal Association, has joined us in the call. The presence of the Community Liason from DA also inspired & motivated us to give our best to this contribution weekend.

Let's code!

After all the sessions, it was time for the code contribution now. From the start, one could tell that it was going to be a productive day. Just under an hour we made significant progress and moved a lot of issues to RTBC or Fixed.

Start of the day:

Start of the day

An hour into contribution:

An hour into contribution

Come for the code, Stay for the community!

Highlights of the day were the appreciation that the mentors received from the newcomers and first-time contributors for their help and support. We had a first-time contributor who had been contributing back to the WordPress community in the past as well as trying their hands-on Drupal contribution for the first time. They really loved the experience and appreciated the welcome and support they received from the community. In their own words: "You all (Drupal community) are awesome and doing great work!"

Words of appreciation from a wordpress community member.

With our spirits high we continued the rest of the day with the contribution. And to have the feeling of togetherness in this endeavour, we had the zoom bridge open throughout the day so that people could freely reach out to each other. We even had regular check-ins every couple of hours so that everyone feels connected.

We ended the even at 3:30 pm with a call where everyone shared their experience and appreciation for the community. It turned out to be a very fruitful day of contribution. By the end of it, we were able to move around 50 issues from needs work/active to RTBC/Fixed.Thanks to all amazing mentors for their support to make this happen: 

@azeets@JayKandari@piyuesh23

@yogeshmpawar@AshishVDalvi@joshua1234511

@ankushgautam76@meenakshig489@sonvir249

@abhisekmajumdar@forhemant@dipakmdhrm

@malavya88@nitesh624@durgesh29@subson

@iampratik_dk@vaibhavjain_in@heykarthikwithu

In the end, this would never have been possible without all you contributors who supported the event with writing patches, reviewing/testing them,  showing up to the event motivating all organizers. We hope for your continued participation over the coming Sprints.

My experience

Overall, it was a very productive day! I really enjoyed this new experience of contributing from the comfort of my home and it was also a much-welcomed distraction from the grim time we all have been having because of the current pandemic. I appreciate the opportunity to work with everyone from the community and would love to do it again soon.

Oh yes! That reminds me of something! Guess what? We already have another contribution weekend planned for next month! It's planned for May 16th and I would love to see and work with the community again. Read more about the event here: https://groups.drupal.org/node/535887.

See you all again on 16th next month.

Until then, stay safe!

Apr 10 2020
Apr 10

Drupal 9 is scheduled to be released on June 03, 2020. This is when the final Drupal 8 minor release 8.9 will also be released. Considering the history of previous Drupal major upgrades, Drupal 9 will relatively be smooth. Thanks to the semantic versioning introduced in Drupal 8. The upgrade to Drupal 9 will just be another minor upgrade with deprecated code removed. Drupal 8 has brought a lot of standardization in the Drupal world, thus allowing Drupal as a project to grow incrementally. 

To put it in simple terms, Drupal 9 contains the same code as of 8.9 + deprecated code removed. Here’s a reference image from the Drupal 9 documentation.

Drupal 9

That is if you reduce Drupal 9 from the last Drupal 8 version (8.8.x) the rest is just the deprecated code and dependency upgrades. 

However, upgrading all the underlying dependencies and removing all deprecated API is a challenging task. Contributors around the world are working hard to get this done especially when the world is facing an epidemic. This is a challenging time for the entire world, yet Drupal contributors have shown their love towards Drupal and are helping it advance to the next release.

This article highlights some of the system requirements, the new dependencies and some of the most commonly used APIs which are going to be removed in 9.0.0.

Let us expedite Drupal’s journey to its 9th version!

| Third-party dependency updates

  • Upgraded Symfony from 3 to 4.4

  • Upgraded Twig from 1 to 2

  • Upgraded CKEditor from 4 and 5

  • Upgraded PHPUnit from 6 to 7

  • Upgraded Guzzle from 6.3 to 6.5.2

  • Popper.js updated to version 2.0.6

| System requirement updates

  • Apache, at least version 2.4.7
  • PHP - at least 7.3. (PHP 7.4 also supported)
  • Database: MySQL (5.7.8), MariaDB (10.2.7), SQLite (3.26), PostgreSQL (10)

| Modules being removed in D9

  • Block_place

  • Entityreference

  • Field_layout -> Replaced by Layout Builder.

  • SimpleTest module has been moved to contrib

  • Action renamed to action UI

| New features in Drupal 9

Drupal 9 will have the same new features as of Drupal 8.9. Thus, Drupal 9.0 will not include new features. But Drupal 9.1 will continue to receive new features as it did for the minor D8 releases.

| Breaking APIs

Following is a list of major APIs which were deprecated in Drupal 8 and will be removed in Drupal 9.0.0. That means if your code contains any of these codes, they need to be replaced before upgrading to Drupal 9. This list is not exhaustive and has been curated by scanning Drupal core codebase for deprecated warnings and sorted according to the usage of these APIs in contributed projects as given Drupal 9 Deprecation Status by Acquia.

  • drupal_set_message() removed

change record

drupal_set_message(), drupal_get_message() functions removed.

Recommendation: 

Use messenger service 

Example: 

\Drupal::service('messenger')->addMessage('Hello world');
  • Drupal::entityManager() removed

change record

EntityManager has been split into 11 classes.

Recommendation: 

Services like entity_type.manager, entity_type.repository, entity_display.repository etc to be used instead. See change records for more info.

Example: 

\Drupal::entityTypeManager()->getStorage('node')->load(1);
  • db_*() functions removed

change record

All of the db_* procedural functions part of the Database API layer have been deprecated.

Recommendation: 

Obtain the database connection object and execute operations on it. See change records for more info.

Example: 

$injected_database->query($query, $args, $options);

$injected_database->select($table, $alias, $options);
  • drupal_render() removed

change record

drupal_render() and drupal_render_root()  functions

Recommendation: 

Use renderer service

Example: 

\Drupal::service('renderer')->render($elements,$is_recursive_call);
  • Methods for generating URLs and links deprecated

change record

Drupal::l(), Drupal::url(), Drupal::linkinfo() etc and many methods for generating URL & links are removed

Recommendation: 

See example. 

Example: 

EntityInterface::toLink();

EntityInterface::toUrl();
  • File functions removed

change record

file_unmanaged_copy()
file_unmanaged_prepare()
file_unmanaged_move()
file_unmanaged_delete()
file_unmanaged_delete_recursive()
file_unmanaged_save_data()
file_prepare_directory()
file_destination()
file_create_filename()

Recommendation: 

Use file_system service 

Example: 

\Drupal::service('file_system')->copy($source, $destination, $replace);

\Drupal::service('file_system')->move($source, $destination, $replace);

\Drupal::service('file_system')->delete($path);


  • Loading of entities removed

change record

node_load(), entity_load(), file_load() etc removed.

Recommendation: 

Use entity_type.manager service to get specific entity storage and then use the respective load functions

Example: 

use \Drupal\node\Entity\Node;

$node = Node::load(1);

or

$file = \Drupal::entityTypeManager()->getStorage('file')->load(1);
  • Functions to view entities are deprecated

change record

entity_view(), entity_view_multiple(), comment_view() etc removed.

Recommendation: 

Use entity view builder handler

Example: 

$builder = \Drupal::entityTypeManager()->getViewBuilder('node'); 

$build = $builder->view($node, 'teaser');
  • Date formats API changes

Some of the functions and hooks removed (change record)

format_date()

hook_date_formats()

hook_date_formats_alter()

system_get_date_format()

system_get_date_format() etc.

Recommendation: 

Use date.formatter service 

Example: 

\Drupal::service('date.formatter')->format()
  • Unicode::* methods removed

Following functions removed (change record)

Unicode::strlen()

Unicode::strtoupper()

Unicode::strtolower()

Unicode::substr()

Unicode::strpos()

Recommendation: 

Use mb_* functions
 
Example: 

mb_strlen();
  • Functions retrieving extensions info removed

change record

_system_rebuild_module_data(), system_get_info() etc are removed

Recommendation: 

Use extension.list.module, extension.list.profile and extension.list.theme services

Example: 

\Drupal::service('extension.list.module')->getAllInstalledInfo();

\Drupal::service('extension.list.theme')->getAllInstalledInfo();


  • drupal_get_user_timezone() removed

change record

drupal_get_user_timezone() function removed.

Recommendation: 

Replaced with an event listener which updates the default timezone

Example: 

date_default_timezone_get();
  • SafeMarkup methods are removed

change record

SafeMarkup::checkPlain() SafeMarkup::format() etc removed.

Recommendation: 

See change record for replacements 

Example: 

Html::escape();

| More Changes/Updates

  • Drupal core themes no longer extend Classy. Read more

  • Drupal core themes, Bartik, Claro, Seven, and Umami no longer depend on Stable.

  • New Stable theme for D9, recommended new themes to be built on new D9 stable theme. Old D8 stable to be removed from core and be moved to a contributed project before D10.

  • A new administration theme, Claro (targeted for inclusion in Drupal 9.1)

  • Drupal 9 won't be able to run updates from 8.7.x or earlier databases anymore, it is necessary for all new updates added to the code base to be tested from a Drupal 8.8.x starting point. Read more

  • Changes to how HTML Elements are inserted via AJAX commands. Read more

  • ZendFramework/* packages have been updated to their Laminas equivalents. Read more

  • PhantonJS based testing removed. Read more

  • The jQuery UI asset libraries not in use by Drupal core have been marked deprecated and have been removed from core in Drupal 9.

  • Drupal 9 will continue to depend on CKEditor 4 and jQuery 3.4.

  • Modules to be compatible with Drupal 8 and 9 at the same time and to support semantic versioning for contributed projects

  • jquery.cookie has been replaced with js-cookie version 2. 

| Conclusion

At this point, 9.0.0-beta2 is released, which means the code for 9.0.0 is stable and is ready for testing by end-users. Now is a good time to test upgrade your existing D8 sites to the latest version of 9.  If you have contributed a project in drupal.org, it is also a good time to check your extensions for D9 readiness. There are several tools which can speed up this process of making your extensions compatible with D9.

Have questions about how Drupal 9 will impact your site? We are here to help. Check out our Drupal Services or send us an email at [email protected]

| Important References

If you see any discrepancies in the information provided above, please let us know.

Thanks!

Apr 10 2020
Apr 10

Drupal 9 is scheduled to be released on June 03, 2020. This is when the final Drupal 8 minor release 8.9 will also be released. Considering the history of previous Drupal major upgrades, Drupal 9 will relatively be smooth. Thanks to the semantic versioning introduced in Drupal 8. The upgrade to Drupal 9 will just be another minor upgrade with deprecated code removed. Drupal 8 has brought a lot of standardization in the Drupal world, thus allowing Drupal as a project to grow incrementally. 

To put it in simple terms, Drupal 9 contains the same code as of 8.9 + deprecated code removed. Here’s a reference image from the Drupal 9 documentation.

Drupal 9

That is if you reduce Drupal 9 from the last Drupal 8 version (8.8.x) the rest is just the deprecated code and dependency upgrades. 

However, upgrading all the underlying dependencies and removing all deprecated API is a challenging task. Contributors around the world are working hard to get this done especially when the world is facing an epidemic. This is a challenging time for the entire world, yet Drupal contributors have shown their love towards Drupal and are helping it advance to the next release.

This article highlights some of the system requirements, the new dependencies and some of the most commonly used APIs which are going to be removed in 9.0.0.

Let us expedite Drupal’s journey to its 9th version!

| Third-party dependency updates

  • Upgraded Symfony from 3 to 4.4

  • Upgraded Twig from 1 to 2

  • Upgraded CKEditor from 4 and 5

  • Upgraded PHPUnit from 6 to 7

  • Upgraded Guzzle from 6.3 to 6.5.2

  • Popper.js updated to version 2.0.6

| System requirement updates

  • Apache, at least version 2.4.7
  • PHP - at least 7.3. (PHP 7.4 also supported)
  • Database: MySQL (5.7.8), MariaDB (10.2.7), SQLite (3.26), PostgreSQL (10)

| Modules being removed in D9

  • Block_place

  • Entityreference

  • Field_layout -> Replaced by Layout Builder.

  • SimpleTest module has been moved to contrib

  • Action renamed to action UI

| New features in Drupal 9

Drupal 9 will have the same new features as of Drupal 8.9. Thus, Drupal 9.0 will not include new features. But Drupal 9.1 will continue to receive new features as it did for the minor D8 releases.

| Breaking APIs

Following is a list of major APIs which were deprecated in Drupal 8 and will be removed in Drupal 9.0.0. That means if your code contains any of these codes, they need to be replaced before upgrading to Drupal 9. This list is not exhaustive and has been curated by scanning Drupal core codebase for deprecated warnings and sorted according to the usage of these APIs in contributed projects as given Drupal 9 Deprecation Status by Acquia.

  • drupal_set_message() removed

change record

drupal_set_message(), drupal_get_message() functions removed.

Recommendation: 

Use messenger service 

Example: 

\Drupal::service('messenger')->addMessage('Hello world');
  • Drupal::entityManager() removed

change record

EntityManager has been split into 11 classes.

Recommendation: 

Services like entity_type.manager, entity_type.repository, entity_display.repository etc to be used instead. See change records for more info.

Example: 

\Drupal::entityTypeManager()->getStorage('node')->load(1);
  • db_*() functions removed

change record

All of the db_* procedural functions part of the Database API layer have been deprecated.

Recommendation: 

Obtain the database connection object and execute operations on it. See change records for more info.

Example: 

$injected_database->query($query, $args, $options);

$injected_database->select($table, $alias, $options);
  • drupal_render() removed

change record

drupal_render() and drupal_render_root()  functions

Recommendation: 

Use renderer service

Example: 

\Drupal::service('renderer')->render($elements,$is_recursive_call);
  • Methods for generating URLs and links deprecated

change record

Drupal::l(), Drupal::url(), Drupal::linkinfo() etc and many methods for generating URL & links are removed

Recommendation: 

See example. 

Example: 

EntityInterface::toLink();

EntityInterface::toUrl();
  • File functions removed

change record

file_unmanaged_copy()
file_unmanaged_prepare()
file_unmanaged_move()
file_unmanaged_delete()
file_unmanaged_delete_recursive()
file_unmanaged_save_data()
file_prepare_directory()
file_destination()
file_create_filename()

Recommendation: 

Use file_system service 

Example: 

\Drupal::service('file_system')->copy($source, $destination, $replace);

\Drupal::service('file_system')->move($source, $destination, $replace);

\Drupal::service('file_system')->delete($path);


  • Loading of entities removed

change record

node_load(), entity_load(), file_load() etc removed.

Recommendation: 

Use entity_type.manager service to get specific entity storage and then use the respective load functions

Example: 

use \Drupal\node\Entity\Node;

$node = Node::load(1);

or

$file = \Drupal::entityTypeManager()->getStorage('file')->load(1);
  • Functions to view entities are deprecated

change record

entity_view(), entity_view_multiple(), comment_view() etc removed.

Recommendation: 

Use entity view builder handler

Example: 

$builder = \Drupal::entityTypeManager()->getViewBuilder('node'); 

$build = $builder->view($node, 'teaser');
  • Date formats API changes

Some of the functions and hooks removed (change record)

format_date()

hook_date_formats()

hook_date_formats_alter()

system_get_date_format()

system_get_date_format() etc.

Recommendation: 

Use date.formatter service 

Example: 

\Drupal::service('date.formatter')->format()
  • Unicode::* methods removed

Following functions removed (change record)

Unicode::strlen()

Unicode::strtoupper()

Unicode::strtolower()

Unicode::substr()

Unicode::strpos()

Recommendation: 

Use mb_* functions
 
Example: 

mb_strlen();
  • Functions retrieving extensions info removed

change record

_system_rebuild_module_data(), system_get_info() etc are removed

Recommendation: 

Use extension.list.module, extension.list.profile and extension.list.theme services

Example: 

\Drupal::service('extension.list.module')->getAllInstalledInfo();

\Drupal::service('extension.list.theme')->getAllInstalledInfo();


  • drupal_get_user_timezone() removed

change record

drupal_get_user_timezone() function removed.

Recommendation: 

Replaced with an event listener which updates the default timezone

Example: 

date_default_timezone_get();
  • SafeMarkup methods are removed

change record

SafeMarkup::checkPlain() SafeMarkup::format() etc removed.

Recommendation: 

See change record for replacements 

Example: 

Html::escape();

| More Changes/Updates

  • Drupal core themes no longer extend Classy. Read more

  • Drupal core themes, Bartik, Claro, Seven, and Umami no longer depend on Stable.

  • New Stable theme for D9, recommended new themes to be built on new D9 stable theme. Old D8 stable to be removed from core and be moved to a contributed project before D10.

  • A new administration theme, Claro (targeted for inclusion in Drupal 9.1)

  • Drupal 9 won't be able to run updates from 8.7.x or earlier databases anymore, it is necessary for all new updates added to the code base to be tested from a Drupal 8.8.x starting point. Read more

  • Changes to how HTML Elements are inserted via AJAX commands. Read more

  • ZendFramework/* packages have been updated to their Laminas equivalents. Read more

  • PhantonJS based testing removed. Read more

  • The jQuery UI asset libraries not in use by Drupal core have been marked deprecated and have been removed from core in Drupal 9.

  • Drupal 9 will continue to depend on CKEditor 4 and jQuery 3.4.

  • Modules to be compatible with Drupal 8 and 9 at the same time and to support semantic versioning for contributed projects

  • jquery.cookie has been replaced with js-cookie version 2. 

| Conclusion

At this point, 9.0.0-beta2 is released, which means the code for 9.0.0 is stable and is ready for testing by end-users. Now is a good time to test upgrade your existing D8 sites to the latest version of 9.  If you have contributed a project in drupal.org, it is also a good time to check your extensions for D9 readiness. There are several tools which can speed up this process of making your extensions compatible with D9.

Have questions about how Drupal 9 will impact your site? We are here to help. Check out our Drupal Services or send us an email at [email protected]

| Important References

If you see any discrepancies in the information provided above, please let us know.

Thanks!

Jan 15 2019
Jan 15

Last month, Pune Drupal community conducted a Global Training Day event. It was a successful event and saw huge participation from Pune Drupal community.
Pune GTD reminded us that community is what makes Drupal so special and that interaction between the community members was somewhat missing from our Drupal life.

With the hope that we can change this, we had planned to increase the community participation by bringing back our monthly Drupal meetups.
And we did deliver on our plan! We had our first Drupal meetup of the year on Saurday, 12th Jan at SISCR, Pune.

The day started with a session on 'Docksal for Drupal' by Sharique.

Sharique preseting session on Docksal

The session covered the basics of containers and the difference between virtualization and containarization followed by a detailed demo of setting up a Drupal instance using Docksal and managing various configurations for Docksal.
Overall, this was a great introduction for developers & teams trying to adopt Docksal for their development environment.

Highlight of the day was the next session on the topic of Accessibility presented by Nikita, Ambuj & Sonal.

Nikita started the session by explaning basic concepts of Accessibility and day-to-day programming tips & tricks developers/teams can follow to make their site accessible.

Nikita presenting session on accessibility

It was followed by Ambuj's explanation of QA's perspective on Accessibility and what should be expected by users & testers for an accessible site.

Ambuj explaining QA's perspective on accessibility

Sonal concluded the session by going into the implementation details, specially the easy ways a developer/team can make their site accessible.

Sonal preseting session on accessibility.

We ended the day having by a general BoF on 'How to increase the community participation for Drupal meetups'.
This was a very productive discussion and the community identified the challenges of setting up a quality event and came up with action items for them.

  • Attendees want to see a variety of topics instead of just having them limited to Drupal Backend and Custom Module development. The session on Accessibility was much liked by the community and they'd love to see more such topics.
  • There's a need to streamline and improve the process of communicating Drupal events and their reporting.

We promise to work on the above and would love to see many of you join us in future meetups. Stay tuned to community updates for information on future events and topics!

Links to Session slides/resources:
1. Docksal: https://www.slideshare.net/safknw/local-drupal-development-using-docksal
2. Web Accessibility: https://bit.ly/2Dgw5P9

Nov 16 2018
Nov 16

At the point when Amazon launched Alexa, it changed the method for individuals connecting with the gadgets.Virtual assistants are quick getting to be ordinary in the home through items like Echo, Echo Show, Echo Dot, Echo Look and Amazon Tap.

The Alexa Skills marketplace is rapidly growing, below is the stats according to the voicebot.ai

US Alexa skill totals as of december 2017

 

Why Alexa skills are Important for business?

Voice search are growing day by day and it is the latest trend in the field on digital design. “There are over one billion voice searches per month. (January 2018)” estimates Alpine.AI.

 Alexa is an awesome way to achieve a more extensive customer gathering of people, which is the reason an ever increasing number of brands crosswise over verticals are developing skills to catch and draw in with audience members. Alexa is amazon’s cloud based service available on tens of millions of devices from amazon and third-party manufacturers.

Alexa Skills are so new to the market, organizations have a universe of unbounded potential outcomes before them. Very little has been done yet, so there's a great deal of space to end up the first to provide one of a kind interaction to a particular sort of business.

Why account linking is necessary for business?

Account linking allow user to easily link there accounts with existing account or service account. It also allows your skill to securely authenticate user with services. Once the user granted your skill  access to the external account the skill can perform operations with that account on behalf on the user.  

Account linking lets you access the current users information by which you can provide user a personalized content as per their interests. 

Account linking in the Alexa skill set uses OAuth 2.0. OAuth server return a authorization code instead of access token in the first step which gives couple on security benefits, for example, the ability to verify the client and also the transmission of the access token specifically to the client without going it through the resource owner’s user-agent and conceivably exposing to others, including the resource owner. Here the resource owner is the person who can grant authorization to get to an ensured resources in the resource server.

How Alexa account linking works with Drupal?

Alexa linking with Drupal


Account linking with Alexa skill

For custom skill model, account linking is a preferred choice if your skill needs personalized data from another system. Account linking lets you connect the identity on the user with a user account in different system. 

Lets walk through an example to understand things more clearly, you have created a web-based service which enables user to order things, a custom skill that lets user access your service by voice would be useful. For example  the request,“ Alexa add echo dot to the cart” requires the skill to access your service as a specific service user for adding product to cart and payment information. As the user is already registered on the website we need to get details of  that particular user to perform above actions. Here we can’t use the amazon account of  the user to identify him/her in our system because it’s not necessary that user will be using the same account to login on the both the system i.e. Echo dot and web-based service. In such scenarios account liking helps to identify user.

For custom skill models, the user completes an account linking flow in the Alexa app to log in to your service. Once the user is authenticated, requests and directives sent to your skill include an access token that your system can use to identify the user. Refer  Overview of the Authorization code grant working for a better understanding of its functions. 

Note: For account linking with Alexa the Drupal site must have an SSL certificate.

Steps to Implement Account Linking in Your Skill:

  1. Download and enable Alexa- simple_auth, simple auth extras, oauth2 server modules.

  2. Add new OAuth2 server - It will help authenticate the identity of the user and issue access tokens. The screenshot below provides an example of server configurations.
     

    Step 2
  3. Add new client-  In here the Alexa skill will be your client, the skill uses access token to request information from the oauth2 server.
    Redirect URI will be provided in the account linking section of  the alexa developer console. (Check point 6 screenshot) 
     

    Step 3
  4. Go to permissions page grant the permission to particular role as per requirement.
     

    Step 4
  5. Login to your Alexa developer account and go to the Account linking menu (second last on left sidebar)

  6. Select authorization grant type as “Auth Code Grant”, enter the client id and client secret as per the client created on the Drupal end.
     

    Step 6
  7. Save and build the model and then go to Alexa application. There, enable your skill, and it will open a new tab that redirects the user to the Drupal site for authentication.

Identify a user from Alexa request

Once the user authenticates skill with the server, each time Alexa skill makes a request to the Drupal site, it would consist the access token from which the user can be identified. 

Below is the sample Alexa request object:

[session] => Alexa\Request\Session Object
        (
            [user] => Alexa\Request\User Object
                (
                    [userId] => amzn1.ask.account.abc
                    [accessToken] => 26c2cfe374cc1562ea29115e3749a80cf7e5262e
                )
            [new] => 
            [application] => 
            [sessionId] => amzn1.echo-api.session.447c2c75-cd0d-4e4b-9901-902b3f269090
            [attributes] => Array
                (
                )
        )
Feb 13 2018
Feb 13

We are in an era where we see a lots of third party integrations being done in projects. In Drupal based projects, cookie management is done via Drupal itself to maintain session, whether it be a pure Drupal project or decoupled Drupal project,.

But what when we have a scenario where user’s information is being managed by a third party service and no user information is being saved on Drupal? And when the authentication is done via some other third party services? How can we manage cookie in this case to run our site session and also keep it secure?

One is way is to set and maintain cookie on our own. In this case, our user’s will be anonymous to Drupal. So, we keep session running based on cookies! The user information will be stored in cookie itself, which then can be validated when a request is made to Drupal.

We have a php function to set cookie called setCookie() , which we can use to create and destroy cookie. So, the flow will be that a user login request which is made to website is verified via a third party service and then we call setCookie function which sets the cookie containing user information. But, securing the cookie is must, so how do we do that?

For this, let’s refer to Bakery module to see how it does it. It contains functions for encrypting cookie, setting it and validating it.

To achieve this in Drupal 8, we will write a helper class let’s say “UserCookie.php” and place it in ‘{modulename}/src/Helper/’. Our cookie helper class will contain static methods for setting cookie and validating cookie. Static methods so that we will be able to call them from anywhere.

We will have to encrypt cookie before setting it so we will use openssl_encrypt() php function in following manner:

/**
* Encrypts given cookie data.
*
* @param string $cookieData
*   Serialized Cookie data for encryption.
*
* @return string
*   Encrypted cookie.
*/
private static function encryptCookie($cookieData) {

 // Create a key using a string data.
 $key = openssl_digest(Settings::get('SOME_COOKIE_KEY'), 'sha256');

 // Create an initialization vector to be used for encryption.
 $iv = openssl_random_pseudo_bytes(16);

 // Encrypt cookie data along with initialization vector so that initialization
 // vector can be used for decryption of this cookie.
 $encryptedCookie = openssl_encrypt($iv . $cookieData, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);

 // Add a signature to cookie.
 $signature = hash_hmac('sha256', $encryptedCookie, $key);

 // Encode signature and cookie.
 return base64_encode($signature . $encryptedCookie);
}

  1. String parameter in openssl_digest can be replaced with any string you feel like that can be used as key. You can keep simple keyword too.
  2. Key used should be same while decryption of data.
  3. Same initialization vector will be needed while decrypting the data, so to retrieve it back we append this along with cookie data string.
  4. We also add a signature which is generate used the same key used above. We will verify this key while validating cookie.
  5. Finally, we encode both signature and encrypted cookie data together.

For setting cookie:
 

/**
* Set cookie using user data.
*
* @param string $name
*   Name of cookie to store.
* @param mixed $data
*   Data to store in cookie.
*/
public static function setCookie($name, $data) {
$data = (is_array($data)) ? json_encode($data) : $data;
$data = self::encrypt($data);
 setcookie($name, $cookieData,Settings::get('SOME_DEFAULT_COOKIE_EXPIRE_TIME'), '/');
}

Note: You can keep 'SOME_COOKIE_KEY' and 'SOME_DEFAULT_COOKIE_EXPIRE_TIME' in your settings.php. Settings::get() will fetch that for you.
Tip: You can also append and save expiration time of cookie in encrypted data itself so that you can also verify that at time of decryption. This will stop anyone from extending the session by setting cookie timing manually.

Congrats! We have successfully encrypted the user data and set it into a cookie.

Now let’s see how we can decrypt and validate the same cookie.

To decrypt cookie:

/**
* Decrypts the given cookie data.
*
* @param string $cookieData
*   Encrypted cookie data.
*
* @return bool|mixed
*   False if retrieved signature doesn't matches
*   or data.
*/
public static function decryptCookie($cookieData) {

 // Create a key using a string data used while encryption.
 $key = openssl_digest(Settings::get('SOME_COOKIE_KEY'), 'sha256');

 // Reverse base64 encryption of $cookieData.
 $cookieData = base64_decode($cookieData);

 // Extract signature from cookie data.
 $signature = substr($cookieData, 0, 64);

 // Extract data without signature.
 $encryptedData = substr($cookieData, 64);

 // Signature should match for verification of data.
 if ($signature !== hash_hmac('sha256', $encryptedData, $key)) {
   return FALSE;
 }

 // Extract initialization vector from data appended while encryption.
 $iv = substr($string, 64, 16);

 // Extract main encrypted string data which contains profile details.
 $encrypted = substr($string, 80);

 // Decrypt the data using key and
 // initialization vector extracted above.
 return openssl_decrypt($encrypted, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
}

  1. We generate the same key using same string parameter given while encryption.
  2. Then we reverse base64 encoding as we need extract signature to verify it.
  3. We generate same signature again as we have used the same key which was used to creating signature while encryption. If doesn’t signatures doesn’t matches, validation fails!
  4. Else, we extract initialization vector from the encrypted data and use to decrypt the data return to be utilized.
/**
* Validates cookie.
*
* @param string $cookie
*   Name of cookie.
*
* @return boolean
*   True or False based on cookie validation.
*/
public static function validateCookie($cookie) {
 if (self::decryptCookie($cookieData)) {
   return TRUE;
 }
 return FALSE;
}

We can verify cookie on requests made to website to maintain our session. You can implement function for expiring cookie for simulating user logout. We can also use decrypted user data out of cookie for serving user related pages.

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 23 2017
Nov 23

Why do we need to override Config Entity Types?

  1. By default, Vocabulary list displays all the vocabularies. In case we want to restrict certain roles from viewing certain vocabularies. Overriding that Class(VocabularyListBuilder) function would be the solution to display specific/no/all vocabularies.
     
  2. Let's assume we need to specify vocabulary-path for each vocabulary apart from name, title, description, vid etc. In this case we would need to override the default Vocabulary Form of taxonomy_vocabulary config entity type.
     
  3. Suppose we want to custom access check for views on the basis of role/user/views operation or whatever, we would need to override ViewsAccessControllerhandler of view configEntityType and write our own logic.
     
  4. Another use case can be, if we want to display all the image fields which use image style being deleted, on confirm text message, we again need to override ImageStyleFlushForm class and redefine getconfirmText function.

In short, to customise and meet our dynamic requirements which may not be supported by config entity type definition as a part of @ConfigEntityType annotations in core or contributed modules, we need to override existing config entity types and write some custom code :).

How can we override Config Entity Types?

Entity types use object based annotation unlike array based annotation which is commonly used. Also, Unlike Content Entity Types where every thing is a field, NOTHING is a field for Configuration Entity type.

Every Drupal config entity type is defined as a particular ConfigEntityType Annotation. Entity controller is completely different from the Controller of MVC pattern. To avoid this confusion in terminology Entity Controllers are termed as handlers, each form related to a particular entity type say taxonomy_vocabulary is declared inside handlers with form key. 

In this article, will take an example of adding custom form elements to config entity type forms to explain this.

In case we need to add a custom element to any of these forms, we need to follow these 2 steps:

I) Set a new handler class specific to that form.

  1. Implement hook_entity_type_alter(array &$entity_types).
  2. Set new handler class as : 
    $entity_types[{id}]->setHandlerClass('form',
     ['{form_type}' => 'Drupal\my_module\MyModuleForm',
     '....',
     '....'
     ]);
    

    where, id = configEntityType id,  form_type eg: default, reset, delete etc is whichever form we want to override and MyModuleForm is the Class name of new form we'll define in Step II.
    Here is the sample code of overriding default form of taxonomy vocabulary.

    $entity_types['taxonomy_vocabulary']->setHandlerClass('form',
     ['default' => 'Drupal\my_module\VocabularyForm',
     'reset' => 'Drupal\taxonomy\Form\VocabularyResetForm',
     'delete' => 'Drupal\taxonomy\Form\VocabularyDeleteForm'
     ]);
    

II) Define the class set in Step I.

  1. Extend the actual class of the form we want to add new form elements to. 
    use Drupal\taxonomy\VocabularyForm as VocabularyFormBuilderBase;
    
    {this is optional, we need to do this to keep the new class name same as base class i.e VocabularyForm}.
    class MyModuleForm extends VocabularyFormBuilderBase 
    
    OR simply, 
    class MyModuleForm extends VocabularyForm
    

    This override is important because we need to inherit functions and form elements defined in the parent class i.e VocabularyForm and also add additional feature i.e form element without disturbing the core code. This is purely OOPs concept of inheritance.

  2. We need to override the form function by 
    1. Inheriting the parent elements by parent::form(....) and
    2. Defining the new custom elements as the basic example below:
      $form['third_party_settings']['qed42_textfield'] = array(
       '#type' => 'textfield',
       '#title' => t('QED42 Custom Form Element'),
       '#default_value' => $vocabulary->getThirdPartySetting('my_module', 'qed42_textfield', 'Qed42 textfield default value')
      );  
      

      Config Entities have by default "getThirdPartySetting()" function { Config entities inherit this function if these extend ConfigEntityBase class which implements ConfigEntityInterface interface which in turn extends ThirdPartySettingsInterface interface}. This thirdParty function allows to set and retrieve a value particularly for a module.

    3. Similarly, we can inherit the save function to save the value of newly added form element with Third Party Settings  as : 
      If the form is set as Tree we need to set value as
      $vocabulary->setThirdPartySetting('my_module', 'qed42_textfield', $form_state->getValue('third_party_settings')['qed42_textfield']);
      
      else :
      $vocabulary->setThirdPartySetting('my_module', 'qed42_textfield', $form_state->getValue('qed42_textfield'));
      
      and of course inherit the parent save function. 
       
    4. We can implement the same logic for extending definition of any existing method  AND can also define new functions inside our new Form.

Any Configuration Entity Type (Date format etc) can be overridden similarly, this can be extended to list_builder, access etc.  Apart from overriding, we can also add new flavours of entity controller using the above steps.

Nov 22 2017
Nov 22

Why do we need to override Config Entity Types?

  1. By default, Vocabulary list displays all the vocabularies. In case we want to restrict certain roles from viewing certain vocabularies. Overriding that Class(VocabularyListBuilder) function would be the solution to display specific/no/all vocabularies.

  2. Let's assume we need to specify vocabulary-path for each vocabulary apart from name, title, description, vid etc. In this case we would need to override the default Vocabulary Form of taxonomy_vocabulary config entity type.

  3. Suppose we want to custom access check for views on the basis of role/user/views operation or whatever, we would need to override ViewsAccessControllerhandler of view configEntityType and write our own logic.

  4. Another use case can be, if we want to display all the image fields which use image style being deleted, on confirm text message, we again need to override ImageStyleFlushForm class and redefine getconfirmText function.

In short, to customise and meet our dynamic requirements which may not be supported by config entity type definition as a part of @ConfigEntityType annotations in core or contributed modules, we need to override existing config entity types and write some custom code :).

How can we override Config Entity Types?

Entity types use object based annotation unlike array based annotation which is commonly used. Also, Unlike Content Entity Types where every thing is a field, NOTHING is a field for Configuration Entity type.

Every Drupal config entity type is defined as a particular ConfigEntityType Annotation. Entity controller is completely different from the Controller of MVC pattern. To avoid this confusion in terminology Entity Controllers are termed as handlers, each form related to a particular entity type say taxonomy_vocabulary is declared inside handlers with form key. 

In this article, will take an example of adding custom form elements to config entity type forms to explain this.

In case we need to add a custom element to any of these forms, we need to follow these 2 steps:

I) Set a new handler class specific to that form.

  1. Implement hook_entity_type_alter(array &$entity_types).
  2. Set new handler class as : 
    $entity_types[{id}]->setHandlerClass('form',
     ['{form_type}' => 'Drupal\my_module\MyModuleForm',
     '....',
     '....'
     ]);
    

    where, id = configEntityType id,  form_type eg: default, reset, delete etc is whichever form we want to override and MyModuleForm is the Class name of new form we'll define in Step II.
    Here is the sample code of overriding default form of taxonomy vocabulary.

    $entity_types['taxonomy_vocabulary']->setHandlerClass('form',
     ['default' => 'Drupal\my_module\VocabularyForm',
     'reset' => 'Drupal\taxonomy\Form\VocabularyResetForm',
     'delete' => 'Drupal\taxonomy\Form\VocabularyDeleteForm'
     ]);
    

II) Define the class set in Step I.

  1. Extend the actual class of the form we want to add new form elements to. 
    use Drupal\taxonomy\VocabularyForm as VocabularyFormBuilderBase;
    

    {this is optional, we need to do this to keep the new class name same as base class i.e VocabularyForm}.

    class MyModuleForm extends VocabularyFormBuilderBase 
    

    OR simply, 

    class MyModuleForm extends VocabularyForm
    

    This override is important because we need to inherit functions and form elements defined in the parent class i.e VocabularyForm and also add additional feature i.e form element without disturbing the core code. This is purely OOPs concept of inheritance.

  2. We need to override the form function by 
    1. Inheriting the parent elements by parent::form(....) and
    2. Defining the new custom elements as the basic example below:
      $form['third_party_settings']['qed42_textfield'] = array(
       '#type' => 'textfield',
       '#title' => t('QED42 Custom Form Element'),
       '#default_value' => $vocabulary->getThirdPartySetting('my_module', 'qed42_textfield', 'Qed42 textfield default value')
      );  
      

      Config Entities have by default "getThirdPartySetting()" function { Config entities inherit this function if these extend ConfigEntityBase class which implements ConfigEntityInterface interface which in turn extends ThirdPartySettingsInterface interface}. This thirdParty function allows to set and retrieve a value particularly for a module.

    3. Similarly, we can inherit the save function to save the value of newly added form element with Third Party Settings  as : 

      If the form is set as Tree we need to set value as

      $vocabulary->setThirdPartySetting('my_module', 'qed42_textfield', $form_state->getValue('third_party_settings')['qed42_textfield']);
      

      else :

      $vocabulary->setThirdPartySetting('my_module', 'qed42_textfield', $form_state->getValue('qed42_textfield'));
      

      and of course inherit the parent save function. 

    4. We can implement the same logic for extending definition of any existing method  AND can also define new functions inside our new Form.

Any Configuration Entity Type (Date format etc) can be overridden similarly, this can be extended to list_builder, access etc.  Apart from overriding, we can also add new flavours of entity controller using the above steps.

Sep 30 2017
Sep 30

We are in an era where we see a lots of third party integrations being done in projects. In Drupal based projects, cookie management is done via Drupal itself to maintain session, whether it be a pure Drupal project or decoupled Drupal project,.

But what when we have a scenario where user’s information is being managed by a third party service and no user information is being saved on Drupal? And when the authentication is done via some other third party services? How can we manage cookie in this case to run our site session and also keep it secure?

One is way is to set and maintain cookie on our own. In this case, our user’s will be anonymous to Drupal. So, we keep session running based on cookies! The user information will be stored in cookie itself, which then can be validated when a request is made to Drupal.

We have a php function to set cookie called setCookie() , which we can use to create and destroy cookie. So, the flow will be that a user login request which is made to website is verified via a third party service and then we call setCookie function which sets the cookie containing user information. But, securing the cookie is must, so how do we do that?

For this, let’s refer to Bakery module to see how it does it. It contains functions for encrypting cookie, setting it and validating it.

To achieve this in Drupal 8, we will write a helper class let’s say “UserCookie.php” and place it in ‘{modulename}/src/Helper/’. Our cookie helper class will contain static methods for setting cookie and validating cookie. Static methods so that we will be able to call them from anywhere.

We will have to encrypt cookie before setting it so we will use openssl_encrypt() php function in following manner:

/**
* Encrypts given cookie data.
*
* @param string $cookieData
*   Serialized Cookie data for encryption.
*
* @return string
*   Encrypted cookie.
*/
private static function encryptCookie($cookieData) {

 // Create a key using a string data.
 $key = openssl_digest(Settings::get('SOME_COOKIE_KEY'), 'sha256');

 // Create an initialization vector to be used for encryption.
 $iv = openssl_random_pseudo_bytes(16);

 // Encrypt cookie data along with initialization vector so that initialization
 // vector can be used for decryption of this cookie.
 $encryptedCookie = openssl_encrypt($iv . $cookieData, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);

 // Add a signature to cookie.
 $signature = hash_hmac('sha256', $encryptedCookie, $key);

 // Encode signature and cookie.
 return base64_encode($signature . $encryptedCookie);
}

  1. String parameter in openssl_digest can be replaced with any string you feel like that can be used as key. You can keep simple keyword too.
  2. Key used should be same while decryption of data.
  3. Same initialization vector will be needed while decrypting the data, so to retrieve it back we append this along with cookie data string.
  4. We also add a signature which is generate used the same key used above. We will verify this key while validating cookie.
  5. Finally, we encode both signature and encrypted cookie data together.

For setting cookie:
 

/**
* Set cookie using user data.
*
* @param string $name
*   Name of cookie to store.
* @param mixed $data
*   Data to store in cookie.
*/
public static function setCookie($name, $data) {
$data = (is_array($data)) ? json_encode($data) : $data;
$data = self::encrypt($data);
 setcookie($name, $cookieData,Settings::get('SOME_DEFAULT_COOKIE_EXPIRE_TIME'), '/');
}

Note: You can keep 'SOME_COOKIE_KEY' and 'SOME_DEFAULT_COOKIE_EXPIRE_TIME' in your settings.php. Settings::get() will fetch that for you.
Tip: You can also append and save expiration time of cookie in encrypted data itself so that you can also verify that at time of decryption. This will stop anyone from extending the session by setting cookie timing manually.

Congrats! We have successfully encrypted the user data and set it into a cookie.

Now let’s see how we can decrypt and validate the same cookie.

To decrypt cookie:

/**
* Decrypts the given cookie data.
*
* @param string $cookieData
*   Encrypted cookie data.
*
* @return bool|mixed
*   False if retrieved signature doesn't matches
*   or data.
*/
public static function decryptCookie($cookieData) {

 // Create a key using a string data used while encryption.
 $key = openssl_digest(Settings::get('SOME_COOKIE_KEY'), 'sha256');

 // Reverse base64 encryption of $cookieData.
 $cookieData = base64_decode($cookieData);

 // Extract signature from cookie data.
 $signature = substr($cookieData, 0, 64);

 // Extract data without signature.
 $encryptedData = substr($cookieData, 64);

 // Signature should match for verification of data.
 if ($signature !== hash_hmac('sha256', $encryptedData, $key)) {
   return FALSE;
 }

 // Extract initialization vector from data appended while encryption.
 $iv = substr($string, 64, 16);

 // Extract main encrypted string data which contains profile details.
 $encrypted = substr($string, 80);

 // Decrypt the data using key and
 // initialization vector extracted above.
 return openssl_decrypt($encrypted, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
}

  1. We generate the same key using same string parameter given while encryption.
  2. Then we reverse base64 encoding as we need extract signature to verify it.
  3. We generate same signature again as we have used the same key which was used to creating signature while encryption. If doesn’t signatures doesn’t matches, validation fails!
  4. Else, we extract initialization vector from the encrypted data and use to decrypt the data return to be utilized.
/**
* Validates cookie.
*
* @param string $cookie
*   Name of cookie.
*
* @return boolean
*   True or False based on cookie validation.
*/
public static function validateCookie($cookie) {
 if (self::decryptCookie($cookieData)) {
   return TRUE;
 }
 return FALSE;
}

We can verify cookie on requests made to website to maintain our session. You can implement function for expiring cookie for simulating user logout. We can also use decrypted user data out of cookie for serving user related pages.

Sep 28 2017
Sep 28

Disclaimer: This is a temporary workaround which worked for me, not a permanent fix and may not work for everyone

Many of us have already tried to enjoy the flavor of new OS by Apple, macOS High Sierra. And doing that might have given you nightmares, if you were using vagrant based project.

If anyone is facing missing files or file changes not getting detected issue inside Vagrant, this might be because of compatibility issues with Apple’s latest APFS (apple file system) and Vagrant’s synced folder type: nfs.

I experienced this a couple of weeks ago, when I upgraded to beta version of masOS High Sierra. I was not able to see few modules (like views, taxonomy) and files present inside vagrant (guest) even though these were actually present in my mac machine (host). This is an already reported issue.

After struggling for next few days and nights, I found the quickest possible solution for this problem which worked for me.

Please follow the steps mentioned below:

  1. Install the vagrant gatling rsync plugin.
    vagrant plugin install vagrant-gatling-rsync
    
  2. Do changes in VagrantFile as mentioned here : https://github.com/smerrill/vagrant-gatling-rsync

    Add the following part only:

    # Configure the window for gatling to coalesce writes.
     if Vagrant.has_plugin?("vagrant-gatling-rsync")
       config.gatling.latency = 2.5
       config.gatling.time_format = "%H:%M:%S"
     end
    
     # Automatically sync when machines with rsync folders come up.
     config.gatling.rsync_on_startup = true
    
    
    Add this code in Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| function of your VagrantFile. You can also refer to VagrantFile line number: 108 & 114 given in https://github.com/navneet0693/high-sierra-vagrant-problem.
     
  3. Important: Change vagrant_synced_folders type from `type: nfs` to `type: rsync` in box/config.yml. Sync folder is mostly your project repo, which shared b/w host (mac) and guest (vagrant box).
     
    vagrant_synced_folders:
      # The first synced folder will be used for the default Drupal installation, if
      # build_makefile: is 'true'.
      - local_path: ..
        destination: /var/www/demo-drupal
        type: rsync
        create: true
    
    
  4. Then you will have provision your Vagrant again.
    vagrant reload --provision
    
Nov 28 2016
Nov 28

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:
{
 "_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:
{
 "_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:

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

 

DELETE

http:

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.

Nov 28 2016
Nov 28

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:
{
 "_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:
{
 "_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:

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

 

DELETE

http:

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.

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.

 

Nov 04 2016
Nov 04

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="https://www.qed42.com/blog/polymer-zoomove/../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
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 27 2016
Oct 27

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="http://www.qed42.com/blog/sqlite-data-factory-ionic/lib/ngCordova/dist/ng-cordova.js"></script>
<script src="http://www.qed42.com/blog/sqlite-data-factory-ionic/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!

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';
}
Oct 20 2016
Oct 20

Android Permission Provisioning has changed recently, If an app is using Android SDK API level 22 or below, users are asked for all the permissions in bulk at the time of installation i.e. Without granting permission user can not install the app. Now with Android 6's security patch called Run Time Permission (API level 23 and above) the app while in use can request for specific permission when the need arise ( similar to iOS ) e.g. you will be asked for the location's permission when you actually try to access the location of the device.

To work with runtime permissions on Ionic, you would need to install Cordova diagnostic plugin which gives you the function to fetch the status of the native api's exposed to the app. If a certain permission is mandatory for you app you can prompt the user to grant access to proceed. Further you have specific functions for granting permissions.

Install the plugin

cordova plugin add cordova.plugins.diagnostic

To avail the features of Run Time Permission, you have to build the app with Android platform 6.

Check you current Android platform version

ionic platform

If the version of your Android's Platform is below 5, you will need to update it to 5 or above.

Remove android platform:

ionic platform remove android

Install Android platform version 5 or above:

ionic platform add [email protected]

In config.xml, set the target of sdk version to 23

<preference name="android-targetSdkVersion" value="23" />

So far we are all set to ask user's for permission on the fly. We will have to call a functions to fetch the status of particular permission for the app. On the basis of the status we will ask the user to grant permissions or ignore it.

Add the following function in your app.js in $ionicPlatform.ready() function.

This will make $rootScope.checkPermission() global and you can call it whenever you wish to check if the user has given the permission to fetch device's location.

$rootScope.checkPermission = function() {
  setLocationPermission = function() {
    cordova.plugins.diagnostic.requestLocationAuthorization(function(status) {
      switch (status) {
        case cordova.plugins.diagnostic.permissionStatus.NOT_REQUESTED:
          break;
        case cordova.plugins.diagnostic.permissionStatus.DENIED:
          break;
        case cordova.plugins.diagnostic.permissionStatus.GRANTED:
          break;
        case cordova.plugins.diagnostic.permissionStatus.GRANTED_WHEN_IN_USE:
          break;
      }
    }, function(error) {}, cordova.plugins.diagnostic.locationAuthorizationMode.ALWAYS);
  };
  cordova.plugins.diagnostic.getPermissionAuthorizationStatus(function(status) {
    switch (status) {
      case cordova.plugins.diagnostic.runtimePermissionStatus.GRANTED:
        break;
      case cordova.plugins.diagnostic.runtimePermissionStatus.NOT_REQUESTED:
        setLocationPermission();
        break;
      case cordova.plugins.diagnostic.runtimePermissionStatus.DENIED:
        setLocationPermission();
        break;
      case cordova.plugins.diagnostic.runtimePermissionStatus.DENIED_ALWAYS:
        setLocationPermission();
        break;
    }
  }, function(error) {}, cordova.plugins.diagnostic.runtimePermission.ACCESS_COARSE_LOCATION);
};
Run time location permission

Here is a link of the code snippet.

Of course, you can choose to skip all this and stick to sdk target version 22, but you will miss out the new cool feature of Android 6 and amazing user experience. 

Oct 17 2016
Oct 17

One of the popular Growth hacking technique for e-commerce and SaaS businesses is Referrals, which is to leverage your user's network to get new users by offering incentives / discounts. On a recent e-commerce project we had the requirement to create a complete referral system but couldn't find a module that came close to fulfilling the requirements, hence we developed and contributed Commerce Referral Discount. This module allows us to provide a discount credit to an existing user for referring new users. Discounts can be configured for both the referring user and the new user who joins as part of the referral. Lets see a typical flow:

  • User A invites user B to signup on website using a unique invite URL (http://yoursite.com/invite/username).

  • User B visits the site using this URL, and is taken to the registration form to create a new account.

  • User B gets some discount amount (say $5) which he could use on his first purchase.

referral discount on order

 

  • After user B makes his first purchase, user A gets a discount amount (say $10), which could be used in the next purchase.

  • Both discount amounts are configurable from the admin backend.

Module Configuration:

  • To configure the discount amounts browse to /admin/config/referral-discount
discount amount configuration

 

referral discount type

 

Commerce Referral Discount module provide "Invite/Referral Link" block, which contains unique refer/invite link for authenticated users to share across their friends. 


The module also integrates with views:

  1. It provides a view type 'Commerce Referral Discount' which can be used to list down all the discounts and other data which it stores in the 'commerce_referral_discount' table in database.
  2. It also provides relationship to the user entity, so you can also include the data of Referrer user and Invited user.

 

Sep 26 2016
Sep 26

Drupal caching layer has become more advanced with the advent of cache tags & contexts in Drupal 8. In Drupal 7, the core din't offer many options to cache content for authenticated users. Reverse proxies like Varnish, Nginx etc. could only benefit the anonymous users.  Good news is Drupal 8 handle many painful caching scenarios ground up and have given developers / site builders array of options, making Drupal 8 first class option for all sort of performance requirements.  

Lets look at the criteria for an un-cacheable content:

  • Content that would have a very high cardinality e.g, a block that needs to display user info
  • Blocks that need to display content with a very high invalidation rate. e.g, a block displaying current timestamp

In both of the cases above, we would have a mix of dynamic & static content. For simplicity consider a block rendering current timestamp like "The current timestamp is 1421318815". This rendered content consists of 2 parts:

  • static/cacheable: "The current timestamp is"
  • Dynamic: 1421318815

Drupal 8 rendering & caching system provides us with a way to cache static part of the block, leaving out the dynamic one to be uncached. It does so by using the concept of lazy builders. Lazy builders as the name suggests is very similar what a lazy loader does in Javascript. Lazy builders are replaced with unique placeholders to be processed later once the processing is complete for cached content. So, at any point in rendering, we can have n cached content + m placeholders. Once the processing for cached content is complete, Drupal 8 uses its render strategy(single flush) to process all the placeholders & replace them with actual content(fetching dynamic data). The placeholders can also be leveraged by the experimental module bigpipe to render the cached data & present it to the user, while keep processing placeholders in the background. These placeholders as processed, the result is injected into the HTML DOM via embedded AJAX requests.

Lets see how lazy builders actually work in Drupal 8. Taking the above example, I've create a simple module called as timestamp_generator. This module is responsible for providing a block that renders the text "The current timestamp is {{current_timestamp}}".

<?php
/**
* @file
* Contains \Drupal\timestamp_generator\Plugin\Block\Timestamp.
*/
namespace Drupal\timestamp_generator\Plugin\Block;
use Drupal\Core\Block\BlockBase;
/**
* Provides a 'Timestamp' block.
*
* @Block(
* id = "timestamp",
* admin_label = @Translation("Timestamp"),
* )
*/
class Timestamp extends BlockBase {
/**
* [email protected]}
*/
public function build() {
$build = [];
$user = \Drupal::currentUser()->getAccount();
$build['timestamp'] = array(
'#lazy_builder' => ['timestamp_generator.generator:generateUserTimestamp', array()],
'#create_placeholder' => TRUE
);
$build['#markup'] = $this->t('The current timestamp is');
$build['#cache']['contexts'][] = 'languages';
return $build;
}
}

In the block plugin above, lets focus on the build array:

$build['timestamp'] = array(
'#lazy_builder' => ['timestamp_generator.generator:generateUserTimestamp', array()],
'#create_placeholder' => TRUE
);
$build['#markup'] = $this->t('The current timestamp is');

All we need to define a lazy builder is, add an index #lazy_builder to our render array.

#lazy_builder: The lazy builder argument must be an array of callback function & argument this callback function needs. In our case, we have created a service that can generate the current timestamp. Also, since it doesn't need any arguments, the second argument is an empty array.

#create_placeholder: This argument when set to TRUE, makes sure a placeholder is generated & placed while processing this render element.

#markup: This is the cacheable part of the block plugin. Since, the content is translatable, we have added a language cache context here. We can add any cache tag depending on the content being rendered here as well using $build['#cache']['tags'] = ['...'];

Lets take a quick look at our service implementation:

services:
timestamp_generator.generator:
class: Drupal\timestamp_generator\UserTimestampGenerator
arguments: []
<?php
/**
* @file
* Contains \Drupal\timestamp_generator\UserTimestampGenerator.
*/
namespace Drupal\timestamp_generator;
/**
* Class UserTimestampGenerator.
*
* @package Drupal\timestamp_generator
*/
class UserTimestampGenerator {
/**
*
*/
public function generateUserTimestamp() {
return array(
'#markup' => time()
);
}
}

As we can see above the data returned from the service callback function is just the timestamp, which is the dynamic part of block content.

Lets see how Drupal renders it with its default single flush strategy. So, the content of the block before placeholder processing would look like as follows:

<div id="block-timestamp" class="contextual-region block block-timestamp-generator block-timestamp">
<h2>Timestamp</h2>
<div data-contextual-id="block:block=timestamp:langcode=en" class="contextual" role="form">
<button class="trigger focusable visually-hidden" type="button" aria-pressed="false">Open Timestamp configuration options</button>
<ul class="contextual-links" hidden=""><li class="block-configure"><a href="http://www.qed42.com/admin/structure/block/manage/timestamp?destination=node">Configure block</a></li></ul>
</div>
<div class="content">The current time stamp is
<drupal-render-placeholder callback="timestamp_generator.generator:generateUserTimestamp" arguments="" token="d12233422"></drupal-render-placeholder>
</div>
</div>

Once the placeholders are processed, it would change to:

<div id="block-timestamp" class="contextual-region block block-timestamp-generator block-timestamp">
<h2>Timestamp</h2>
<div data-contextual-id="block:block=timestamp:langcode=en" class="contextual" role="form">
<button class="trigger focusable visually-hidden" type="button" aria-pressed="false">Open Timestamp configuration options</button>
<ul class="contextual-links" hidden=""><li class="block-configure"><a href="http://www.qed42.com/admin/structure/block/manage/timestamp?destination=node">Configure block</a></li></ul>
</div>
<div class="content">The current time stamp is 1421319204
</div>
</div>

The placeholder processing in Drupal 8 happens inside via Drupal\Core\Render\Placeholder\ChainedPlaceholderStrategy::processPlaceholders. Drupal 8 core also provides with an interface for defining any custom placeholder processing strategy as well Drupal\Core\Render\Placeholder\PlaceholderStrategyInterface. Bigpipe module implements this interface to provide its own placeholder processing strategy & is able to present users with cached content without waiting for the processing for dynamic ones.

With bigpipe enabled, the block would render something like the one shown in the gif below:

Lazy builder with Bigpipe

As you can see in this example, as soon as the cached part of the timestamp block "The current timestamp is" is ready, its presented to the end users without waiting for the timestamp value. Current timestamp loads when the lazy builders kick in. Lazy builders are not limited to blocks but can work with any render array element in Drupal 8, this means any piece of content being rendered can leverage lazy builders.

N.B. -- We use Bigpipe in the demo above to make the difference visble.

Sep 19 2016
Sep 19

If you have been building Ionic / Cordova apps, you might have noticed your app breaking with public release of iOS 10 launched recently. Common symptom of apps suffering from this issue is that the app continues to work as expected on Android, uptill iOS 9 but doesn't launch / load on iOS 10.

This is because of the newly introduced Content Security policy.

Content Security policy is used to prevent cross-site scripting (XSS). It is now supported by all modern browsers.

Browser Support

CSP provides a standard method for website owners to declare approved origins of content that browsers should be allowed to load on that website.

CSP comprises of multiple directives each separated by a ‘;’

If your Ionic / Cordova app does not load data from remote content / stuck on splash screen on iOS 10, this could be the issue you must be facing and the fix is to add the below meta tag in your index.html

<meta http-equiv="Content-Security-Policy" content="default-src gap://ready file://* *; script-src 'self' 'unsafe-inline' 'unsafe-eval' *; style-src 'self' 'unsafe-inline' *”>

Below is a brief description of the attributes: 

  • default-src :  default policy for CSP.
  • gap://ready file://* : is required to allow the for loading remote content in our app iOS 10.
  • script-src : Defines the valid sources of Javascript to be loaded.
  • unsafe-inline: Allows use of inline source elements such as style attribute, onclick, or script tag bodies.
  • unsafe-eval : Allows unsafe dynamic code evaluation such as JavaScript eval().
  • self : Allows loading resources from the same origin (same scheme, host and port).
  • style-src : Defines valid sources of stylesheets.

Disclaimer: Please use it in line with your security considerations and evaluations.

Jun 26 2016
Jun 26

Drupal India Community have been talking about Pan India code sprint and with effort and cooperation of regional communities we were finally able to organise a combined sprint, we had participation from Mumbai, Jaipur, Delhi and Pune. This is an account of Pune sprint which happened at QED42 Office in Viman Nagar. We had a total attendance of 10 Drupalers out of which 2 were first time sprinters ( Congratulations Dhruvesh and Shreyal on attending your first sprint :) ).

The focus of the sprint was porting modules from D7 to D8 and trying to reach stable releases of some of the modules that were started in previous sprints. One of those modules was auto_entitylabel the issues were triaged prior to the code sprint, so we had less trouble getting around the issues & fixing them up. EOD, we were able to get a basic version of the module, which included integration with tokens.

Auto Entity Label Porting to Drupal 8

Ajit mentored Dhruvesh on autologout tasks and Dhruvesh contributed a fix to an issue in D8 version of the module & then backported it to Drupal 7 version as well.

Vishal mentoring shreyal

Sprint also included some code review work around heap_analytics module, which Nitesh  ported to Drupal 8 (https://github.com/nitesh11/heap_analytics). 

Ajit, Nitesh & Prashant sprinting

Overall, it was a productive sprint & we plan to continue the same on Last Saturday of every Month. Keep an eye on auto_entitylabel, jquery_carousel, heap_analytics if you are interested to use them in Drupal 8, couple of sprints and help from community we should be able to release stable versions of these modules :) we specifically need help on testing of these modules and reporting issues. 

May 31 2016
May 31
A summary of the going-ons at the PDG Meet-up for the month of May.
May 27 2016
May 27

The flavour of this month has been the Drupalcon New Orleans and we decided to keep the momentum going for this PDG meet-up held at the QED42 office.

The first session was given by Rakhi Mandhania on her experience at DrupalCon both as an attendee as well as a Keynote speaker for the Higher Ed Summit. She explained how everyone is concerned with the migration of a large number of websites to Drupal 8 and the lack of rich Drupal talent. DrupalCAP initiative was hailed as a solution to the jarring lack of Drupal literate work force and appreciated all around. 

Rakhi's session

The second session was by Piyuesh Kumar on service workers, the same session both he and Saket kumar presented at New Orleans. He explained that functionalities such as, rich offline experiences, periodic background syncs, push notifications that traditionally require a native application are coming to the web and service workers provides the technical foundation all these features will rely on.

He ended the session with a demo of a working website for DrupalCamp.

Piyuesh

The evening was concluded with us deciding the dates for DrupalCamp Pune 2016, which will tentatively take place sometime in late August.

Watch this space for details, coming shortly!
Good day and see you all soon.

Apr 29 2016
Apr 29

The monthly Pune Drupal Group Meetup for April was hosted by QED42. The second PDG meetup to take place in the month of April, You would assume meeting this often would get tiring for other people but not us! We Drupalers love a good catchup session.

The first session was kicked off by Prashant and Rahul, Interns at QED42 and they spoke on, "Our experience with Drupal." They explained about their journey as new comers to Drupal, from the lenses of both CMS and the community. Their confusion at the beginning, the new tech and softwares they have learned, their experience at Drupalcon Asia and their love for the community. A really enjoyable session peppered with ernest observations and cute cat pictures and a brilliant first time attempt. Bravo boys!

Rahul and Prashant

The second session was taken by Arjun Kumar of QED42 on,"Introduction to CMI." With a brief on CMI and the difference from the features land, he concluded with a demo.

Arjun CMI

After a short discussion on the probable date and location for Pune Drupal Camp we broke off for BOF sessions,with Navneet leading the discussion on Acquia certifications and further discussions on CMI.

BOF

With 20 people in attendence we concluded the PDG april meetup with delicious Pahadi Sandwiches in our tummy. Have a great weekend and see you soon!

Apr 21 2016
Apr 21

The monthly meet-up for March was moved from the last friday of the month, which was the good Friday, to the 1st of April and hoped really hard that people didn't think it was an April fools prank. This PDG meetup was hosted by Rotary International thanks to diligence of Dipak Yadav who works there. It is always fun when the meetup is hosted in different locations because we get to explore different parts of Pune and see new faces.

With 25 members in attendence, the meetup was kicked off by Dipak giving us an informative talk about Rotary International and the work they do.

Introduction of Rotary International

The speaker for the evening was Sushil Hanwate of Axelerant and he spoke on,“ Services and dependency injections in Drupal 8.”

Session

 

The session ended after a short Q&A, we broke off into smaller groups for BOF sessions. Saket headed the BOF for Service workers and the second group discussed about the Drupal 8 Module development.

Once we were done with technical talks, we were served one of the best Kachoris we have tasted :). While we happily munched on the snacks, we decided on the preliminary team members for the upcoming Pune Drupal Camp.

Though the meetups are being held regularly we still need to figure a way of involving newer members into the community and one of the way that is possible is if we get more people volunteering to host the meetups. Kudos to Rotary for hosting us, if you are a Pune based company / group who would like to host the next meetup then please get in touch via comments. 

Our next PDG meetup is scheduled for the 29th of April. Along with a session on,"Experience with Drupal" by Rahul Savaria and Prashant Kumar from QED42, we shall also be planning and discussing further about the upcoming Pune Drupal Camp.
Dont forget to RSVP, See you soon!

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).  

Pages

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