Jun 11 2019
Jun 11

Change is the only constant and yet what one fears the most is change. But it is rightly said about change - “Don’t be afraid of change. You may lose something good, but you may gain something better.” We’ll like to say the same about the fear you hold for changing the current version of your Drupal 6/7 site to Drupal 8. Well, we also know that its more of a confusion than the fear of change, since you’re stuck between the two thoughts - whether to upgrade now to Drupal 8 or wait for Drupal 9. What if we say, we offer you a solution that will hit both the birds with one stone?

An Easy, Inexpensive & Drupal 9 Compatible Migration!

We have been an active Drupal community member since the past 6+ years, 7+ Drupal projects supported, 5000+ successfully delivered international projects and 500+ international Drupal projects - out of which 100+ projects are of Drupal Migration. And hence, we can help you in migrating your current Drupal 6/7 site to Drupal 8 and that too in a way that you will not have to spend a single penny for migrating to Drupal 9 in future. There’s a bunch of rational reasons to back this statement and offer of ours, which we’ll like to share with you:
 

  • Change in Drupal Philosophy
    Previously, every Drupal upgrade was considered to be tedious and more of a technical task as compared to its counterpart CMS platforms. This is because Drupal 8 was created with a philosophy of bridging the gap between the technical developer and a layman-like admin. And taking this philosophy of positive change, Drupal 9 is going to bridge the gap of upgrade issue by introducing compatibility between its older and newer version - making the entire process effortless and inexpensive.
     

  • Upgrade-based Modules
    The compatibility between the older and newer version of Drupal majorly depended upon the modules and themes used while building the older version. Until and unless these modules and themes aren’t upgraded, the migration was a time-taking task and tedious task that required technical assistance. This has been changed with the change in the upgrade path of the content, which makes the migration easier if prepared.
     

  • Drupal Core Deprecating Policy
    Drupal 8 capable of introducing new APIs and features against the old ones. And once these new ones are launched, the old ones automatically get deprecated. Though these old APIs cannot be removed in the minor release of  Drupal 8, it will be removed in the next major version of Drupal 9. Hence, if you migrate to Drupal 8 now, the migration to Drupal 9 can easily be done with just a handful of changes to make it compatible.
     

Looking at the above three major reasons, it must be clear to you that migrating to Drupal 9 from Drupal 8 is far easier as compared to the migration from Drupal 6/7 to Drupal 9. Dries Buytaert, the founder of Drupal, has also shared similar information about planning to be done for Drupal 9. According to him, Drupal 9 is basically built in Drupal 8 instead of a different codebase, altogether. This implies that the new features are added as backward-compatible code and experimental features, which means once the code is stable the old functionality will be deprecated.
 

Dries, in his blog on ‘Plan for Drupal 9’, has quoted contributed module authors as one of the core reasons behind the easy migration from Drupal 8 to Drupal 9. On this, he says that these are the module authors are already well-equipped with the upcoming technologies of Drupal 9 and hence they can priorly work in a manner that is Drupal 9 compatible. AddWeb, being one of these contributing members of the community, can assure you of the easy and inexpensive migration to Drupal 9 as and when it arrives.
 

Why Vouch for Drupal 9?
Now, after grasping all the above information regarding the upcoming major release of Drupal 9, you must be wondering what’s in Drupal 9 to vouch for. Let us throw some light on the same, to be able to bring some clarity for you. Drupal 9 is all about eliminating the use of deprecated modules and APIs. Drupal 8, which runs on the dependency of Symfony 3, will run out from the market by November 2021. And hence, it is highly advisable to upgrade and avail the benefits of all that’s latest!
 

Concluding Words:
As an expert #Drupal-er and active community member, AddWeb is all set to offer you with this amazing opportunity to migrate from your current Drupal 6/7 site to Drupal 8, in a way that the future migration to Drupal 9 will be super easy and inexpensive. Share your details with us in here and let our Drupal Migration Experts get back to you. In case, of any queries or suggestions feel free to get in touch with us!

Apr 29 2019
Apr 29

Drupal has been the choice of the world’s multiple large and top-notch organisations, across various fields. The royal family of the UK, the Greenpeace Greenwire, Oxford University, Warner Music Group, Tesla, Red Cross, and now the Australian Government - everyone is using Drupal. Security and the scope of customisation are two of the major reasons behind the selection of Drupal over other CMS platforms. In fact, the govCMS was also formed using Drupal to resolve the multiple issues faced by Government agencies viz. Security, cost, extraordinaire functionalities, flexibility, smooth process of procurement, et al.
 

As a dedicated Drupal-er for more than 6 years, AddWeb has worked on multiple enterprises and large-sized Drupal projects. And hence, we are cognizant of all the strengths that Drupal contains. This is exactly why we confirm with the Australian Government’s decision of choosing Drupal for creating govCMS.
 

There are multiple reasons that make govCMS an apt choice for Government organisations. govCMS is well-equipped to meet all the requirements of the Government organisation, along with following their guidelines of the web world.
 

Advantages of govCMS:

The Australian Government created the govCMS distribution by combining Drupal Core and a specific set of Drupal modules. So that uniformity is maintained across all the Australian Government’s websites and it the creation of the same also becomes easy. Let us how else does this, govCMS distribution proves to be advantageous:

Cost-Effective

Individual web hosting and creation of the sites demand time and money. The higher the security and quality of these sites, the higher the costing. govCMS saves on both of these factors and simplifies the entire process by choosing a single provider and hosting platform on Acquia Cloud Site Factory PaaS Service. In fact, whenever there’s an increase in resource usage, one can always upgrade the platform, which is eventually beneficial to all the other govCMS sites also.

Government Standards Compliance

The entire govCMS is created in a way that it perfectly complies with the standards of the Federal Government. Hence, this makes the further process quite smooth and sorted. Security being one of the major concerns while creating a website for such Government organisations. And hence, govCMS has complied with their guideline by completing the program process of Information Security Registered Accessors. Plus, every issue that is found and rectified in govCMS will also automatically be implemented to other govCMS sites too.

Software Maintenance

Drupal is one of the largest open-source platform available today and hence it has a large team of 600 expert community members, who work on making govCMS a consistently growing and highly efficient product. This is taken care by Acquia, which also provides 24x7 assistance for govCMS at application and hardware level.   

Security Compliance

One constantly needs to confirm that there are no issues with the govCMS sites, for which a continuous process of testing, bug-fixing and other such process is required to be followed. This is very well taken care of, when it comes to govCMS and hence the security of this platform is kept intact. In fact, an automated testing process has also been set across the entire network by using Behat.

Responsive

In today’s day and age, a website that is not responsive is outdated. Fortunately, govCMS has been created with a base-theme that is responsive by default. This provides the developers in quick-creation of custom themes. This also helps in the creation of a standard look and feel of the Australian Government’s website, which converts into a user-friendly experience.

Accessibility Compliance

Every single Government website needs to be in compliance with the Web Content Accessibility Guideline (WCAG) AA 2.0. And hence, WCAG AA 2.0 has been at the base of creating the base-theme and hence it’s in complete compliance with the Government standards. This also helps in elevating the user-friendliness of the website. govCMS also provides a list of the accessible elements either via the content pages or the WYSIWYG editor.

Data Retention

Backups are a very critical and significant part of any website. So when it is a Government website, the stakes are even higher. The creation of govCMS is done in complete compliance with the National Archives of Australia Standards. This includes about 7 years of data retention on backups, which is a highly beneficial factor that works in the favour of these govCMS-based Australian Government’s websites.  

govCMS is a pool of perfection when it comes to a platform meant for Government - highly secure, affordable and effective! govCMS platform is a pool of perfection when it comes to a platform meant for creating any Government-based website. Because it is highly secure, affordable and effective! AddWeb is glad to have built a website created by using a govCMS platform, which is in complete compliance with WCAG 2.0 and government standards. And we’re all-equipped to work more on other such govCMS-based websites. If there’s anything specific in your mind that you wish to learn about govCMS then free to write to us in here and we’ll be happy to include the relevant topics in our future blogs.

Mar 27 2019
Mar 27

What is govCMS?

The govCMS distribution is supported in Drupal 7 and Drupal 8 version, which has installation profiles for Australian government websites and it is being actively managed on Github (https://github.com/govCMS/govCMS8) and features are maintained from https://www.govcms.gov.au/.
 

To work with govCMS, we have to take care of some factors in order to create a site with the govCMS platform.
 

They have limited number of the module they support and we have to stick to that only and need to find alternate options with the twig and preprocess functions only and they do not allow us to create custom modules as well.
 

Here is the list of modules that they support: https://www.govcms.gov.au/govCMS-d7-modules
 

Unfortunately supported modules for Drupal 8 are less compare to Drupal 7, but we can create support request of community and if it's valid then they can include a module on SaaS platform.
 

Our Experience about working with govCMS

We have worked with govCMS for one of our clients from Australia, who is working with another agency before we met, and already he had started site development with them, and fortunately, he gets to know about the quality of work done so far from the previous agency.

They have added lots of contrib modules and to achieve some functionality they have created custom modules as well, but as the govCMS platform doesn't support such modules we have to flush out all existing implementation and started from the beginning.

All features which are build using custom/contrib modules, we had to find alternatives and get things done only with supported modules and using preprocess functions and twig alters. and along with that, we have to make sure that site is WCAG compliance as it is a government website they must be. So all things we delivered to the client successfully as per the client's expectation with the boundary of govCMS restriction.


 

More details about what is govCMS

Drupal gets big in the Australian Government
 

With almost half of Australian Government departments now running Drupal, and hundreds of more sites now live within various agencies, Drupal has transformed the way government websites are built and managed.
 

Drupal 7: https://www.drupal.org/project/govcms

Drupal 8: https://www.drupal.org/project/govcms8
 

The aim is to provide a single solution for unclassified websites using a common codebase and a shared feature set on a scalable and secure list infrastructure.

govCMS distribution is supported as SaaS by amazeelabs in collaboration with govCMS community and it supports several contributed modules which are available, here is a list of modules which can be used with SaaS https://www.govcms.gov.au/govCMS-d7-modules.
 

Workflows and Ahoy

It is interesting to see that the .ahoy.yml is just a set of command shortcuts, which is similar to the scripts section of a composer.json. Every implementation can be smoothed over by a single Ahoy command, and the underlying implementation can evolve without the developer even noticing.

Speculating, I think the hardest part about adding Ahoy commands will be naming them. Even then, the govCMS team will have the luxury of focussing on the "SaaS govcms 8 on Lagoon" use case, rather than something like BLT which attempts to have commands for "any Drupal anywhere".

Aug 08 2017
Aug 08

Paragraphs

Paragraphs are one of the modules that make site builder and end users more powerful. Paragraph module does this by replacing one big WYSIWYG with predefined paragraph types. Paragraph module utilizes entity reference field to reference paragraph types. Paragraphs are basically entities so users get control over fields and theming of the paragraph.

Bootstrap 

Bootstrap is sleek, intuitive, and powerful mobile first front-end framework for faster and easier web development. Bootstrap has become one of the most popular front-end frameworks and open source projects in the world.

A combination of these two is "Bootstrap Paragraph" module.

"The Bootstrap Paragraphs module provides a suite of content and layout Paragraph bundles made with the Bootstrap front-end framework."

Using bootstrap paragraph module, it is very easy to create features like an accordion, modals, tabs, multi column layouts, and many more things. 

The module has multiple dependencies including paragraphs, Entity reference revisions, contact formatter to name a few. The module also needs Bootstrap framework’s js and CSS added in site theme. Installing this module via composer could be beneficial.

Module installation follows a standard process. The module comes with predefined paragraph bundles which can be accessed from /admin/structure/paragraphs_type. These paragraph bundles are divided into 2 main categories: 

Content bundles

  • Simple HTML
  • Image
  • Blank
  • Contact Form
  • Drupal Block
  • View

Layout bundles

  • Accordion
  • Carousel
  • Columns (Even, up to 6)
  • Columns (3 Uneven)
  • Columns (2 Uneven)
  • Modal
  • Tabs

Each paragraph type comes with 2 options: 

Width and background color

Over 50 background colors are already defined. Empty background classes are also available to customize based on your theme. These classes are :

  • .paragraph--color--primary 
  • .paragraph--color--secondary 
  • .paragraph--color--success 
  • .paragraph--color--info 
  • .paragraph--color--warning 
  • .paragraph--color--danger

Predefined width options include following:

  • Tiny - col-4, offset-4
  • Narrow - col-6, offset-3
  • Medium - col-8, offset-2
  • Wide - col-10, offset-1
  • Full - col-12

To use these paragraph bundles, an entity reference field referencing to paragraph needs to be added to a content type. This field can have unlimited value to allow site builders to add as many paragraphs as they want. Once set you can start creating content. With this module, you can place a piece of content in your own structured page.

Additional Features

  • Paragraphs and fields are saved in the database. This means that whatever changes are made in them are also in the database. 
  • Theme CSS gets precedence over module CSS. You can copy template files in theme from a module and overwrite CSS as you desire.
  • The module can be uninstalled without losing paragraphs and CSS. You need to move all templates and CSS to your theme. Remove “attach_library” calls from template files and call CSS from theme’s library.
  • You can use this module to create reusable paragraph bundles via a module.

To summarize, Bootstrap paragraph module is a suite of Paragraph bundles made with the Bootstrap framework. With this module, you can easily position chunks of content (Paragraph bundles) within a structured layout of your own design.

Hope this blog helped you and explore more about Paragraphs also need assistance for Drupal 8 Services then get in touch with us Feel free to share feedbacks.

Jul 08 2017
Jul 08

Paragraphs is a contributed module which allows creation, administration, and display of customizable content components. In other words, Paragraphs gives you cleaner data structures so you can give more editing power to your end-users. 

This module replaces drupal’s standard body field with paragraph types. This allows any type of content to be added on the page including but not limited to HTML, images, videos etc. All these can be added by users. Users can add as many paragraphs as they like all the while keeping them all responsive. Developers can reuse paragraphs and layouts. 

Paragraphs are basic Drupal entities. This allows site builder control over fields used in paragraphs and their display. This also makes content saved in paragraphs compatible with Search API, views, and services. 

Below is the step by step instructions to work with Paragraphs module:

1 - Download and enable the module
Paragraphs module follows standard process to download and enable.  Module depends on entity reference revision. You can download and enable this module via drush as well.

2 - Add paragraph types
Paragraphs do not come with any predefined paragraph types. Developers can add as many paragraph types they want by going to admin > structure > paragraphs

Introduction to Paragraphs Drupal 8

To add a paragraph, click on “Add paragraph”. Add fields 

Introduction to Paragraphs Drupal 8

Add fields that you want in this paragraph. Go to manage form display and manage display to adjust field display.

3 - Add paragraph field in the content type

Add paragraph field in the content type. 

Introduction to Paragraphs Drupal 8

Allow this field to have multiple values. Select which paragraphs you want available in this field on field settings and save.

4 - Add content

Add content. In paragraph field, you get a list of all the paragraphs you have created and are available for this field. Add as many paragraphs as you need. You can change the order as well.

Introduction to Paragraphs Drupal 8

5 - Theme your paragraphs

Paragraphs can be themed separately. You can use the same template everywhere this paragraph is used.  The file name needs to 

“Paragraph--machine-name-of-your-paragraph.html.twig”

Summary

Paragraph provides various advantages to all users of the site. Site owners get content that is responsive and works on all platforms. Content authors can choose on the fly between predefined paragraph types. They get more structured content creation process. Developers have full control over fields to include and paragraphs and how they should look.

Stay tuned for more on paragraphs!

Hope this helped you to get most out of the system. Feel free to share your reviews or need assistance for Drupal Website Migration then get in touch with us. Pick the best answer for your requirements.

May 12 2017
May 12

Display modes are one of the core functionalities of Drupal 8. They are easy to create and provides great flexibility in terms of theming at entity viewing and editing level. Display modes are available for content, comment, contact messages, custom blocks, taxonomy terms, users. They are also available to views adding more power to views.

Display modes are located at Admin -> Structure -> Display modes -> View mode.

Two types of display modes are available: "view modes" and "form modes."  Both these are example of configuration entities.

View modes allows site builder to request a field to be rendered in a certain way. For example, we can have an article that has doctors field has entity reference. On article full mode display, we can request few details of doctor to be displayed. We can create a new display mode (“Doctor details”) to do so. On Doctor’s full view, we can include a brief summary of articles associated with them. 

To create View mode 

Navigate to Admin -> Structure -> Display modes -> View mode

1. Click on “Add view mode” to add new view mode

2.  Created view mode is available on content types. Navigate to manage display of the content type and enable view mode.

Once enabled, this mode can be used on referenced field.

Once view modes are enabled, we can theme it as per requirements. To get template suggestions, you need to turn on debug property from services.yml. 

You can find theme suggestion in inspect element.

Display modes can also be used based on conditions and we can switch mode based on conditions.

function hook_entity_view_mode_alter(&$view_mode, Drupal\Core\Entity\EntityInterface $entity, $context) {
  // For nodes, change the view mode when it is teaser.
  if ($entity->getEntityTypeId() == 'node' && $view_mode == 'teaser') {
    $view_mode = 'my_custom_view_mode';
  }
}

Hope this helped you to understand Display Modes in Drupal 8, Feel free to drop us a line for any Drupal 8 related work/queries/tasks, or Need any assistance regarding Drupal 8 Web design & Development. Always ready to help :) 

Apr 21 2017
Apr 21

Commerce coupon module adds Commerce coupon facility to Drupal commerce.

Commerce coupon 7.x-2.x is recommended version. This version depends on Commerce Discount. Commerce coupon 7.x-2.x has many architectural improvements and new features which include integration with commerce discount, multiple discounts per coupon, integration with inline conditions. There are no fixed amount coupons or percentage coupons with this version. Instead, we use discounts and use them with coupons. 

Upgrading from Commerce coupon 7.x-1.x to Commerce coupon 7.x-2.x is not directly supported. It is two step process. As per module page on drupal.org,

"Commerce Coupon 7.x-2.x-beta2 has an upgrade path from 1.x to 2.x. It works for upgrading relatively simple coupon 1.x sites; mileage may vary if you have a lot of custom coupon functionality though."

So in order to upgrade to latest commerce coupon, we first need to upgrade to beta2 and then upgrade to latest version. 

Below are the steps to upgrade normal commerce coupon setup without any additional customization.

  • First things first, take a backup.
  • Upgrade to Commerce coupon 7.x-2.x-beta3 following standard upgrade process. You will need to add commerce discounts and inline conditions modules installed before upgrading commerce coupon.
  • Once updates are run, you will see your old coupons converted into new coupon and discount systems. Discounts are created for previous fixed and percentage coupons. 
  • Upgrade to latest commerce coupon version following standard upgrade process.
  • Make sure to give permission to required user roles to "Redeem coupon".

This process assumes that there is no custom coupon functionality is added and no additional modules are used along with commerce coupon. The Success of upgrade process largely depends on how coupon module is used and customization is done on it.  

Below are the steps to upgrade commerce coupon with Commerce Coupon by product reference module installed.

  • Take a backup.
  • Delete product reference fields from coupons.
  • Disable Commerce Coupon by product reference module.
  • Upgrade commerce coupon as outlined in steps 2,3 and 4 in upgrade normal commerce coupon.
  • Edit required discounts and add products.

This makes sure that this discount applies to specific products only. 

  • Give appropriate user roles permission to "Redeem coupon".

Since inline conditions are also installed, you can create coupons while creating discounts. 

You can also consider adding Commerce discount Extra module which provides various common discount condition and offer types.

Need more assistance regarding Drupal Commerce Module Development Contact us now and Feel free to share your views/feedback for any further queries.

Mar 14 2017
Mar 14

Facets allow users to search the site based on selected criteria. Using Facet API module we can add facets on site. For each field that is indexed, we can have facets. These facets can be configured and displayed on search pages. We have various configuration options available for this.

Many times we need to show these facets on pages that are not Search pages. Facet API module does provide this functionality. You need to create a view display of type "Facet blocks". Below are the steps to do so.

1 - Create a view of search index and a display of type "Facets Block". You can make this display in the same view as the search page.

Show facets on non-search page

2 - Configure "Facet field" and "Search page path". Search page path is the path of the search page.

Show facets on non-search page

The block is then available to be assigned to the region in blocks page.

Show facets on non-search page

However, there are several limitations to this:

  1. The facet links will always be styled as a list, no rewrite or format selection is possible.
  2. It is also not possible to use the normal Facet API widgets for these facets. This is because re-using the Facet API components from outside of the Facet API cannot be easily achieved.

In many cases, Professional Web Development need to show facets on the non-search pages as normal facets instead of the list. To achieve this, follow steps below:

  1. Create a facet block as mentioned above.
  2. Show this block on the non-search page as usual.
  3. Make sure that facets blocks are displaying on non-search page as well. Also, make sure that facet block is showing above views facets blocks i.e weight of facets block is higher than the views facets block.
  4. Edit the facet block and set "Display for searches of facets" to "All search page".

Doing this will make both the blocks visible on the non-search page. You can hide the block showing list using CSS. This solution works for Search API Database Search and Search API Solr Search.

The final result is something like this:

Show facets on non-search page
Feb 27 2017
Feb 27

Please follow the below steps to Generate custom pdf:

1: First you need to install and enable print module.
2: Download dompdf from https://github.com/dompdf/dompdf on github.
3: Put dompdf library in "/sites/all/modules/print/lib/dompdf".
4: Add below code at the starting of the custom module file.

    use Dompdf\Dompdf;

5: Create your html structure that you want to print in pdf and set your html structure to $result like below example.


    $result = '<table>
      <tr>
        <td>
          fitst row first column
        </td>
        <td>
          fitst row second column
        </td>
      </tr>
    </table>';
  

6: Add condition for print module and not empty result. 

  if (module_exists('print') && !empty($result))

7: If condition is TRUE then under condition add library path like that 

$file_path_include = drupal_get_path('module', 'print') .'/lib/dompdf/autoload.inc.php';
    require_once $file_path_include;

8: Create object for Dompdf.

    $dompdf = new Dompdf();

9: Add your html structure in object's load_html method.

    $dompdf->load_html($result);

10: Add below line to generate output.

 $dompdf->render();
    $pdfoutput = $dompdf->output();

11: Set file name and file path that you want to store. Keep the name dynamic if there are multiple pdfs by appending date-time.

$filename = 'demo.pdf';
    $filepath = 'sites/default/files/pdf/demo.pdf';

12: Write file using below code. This will generate pdf at "sites/default/files/pdf/demo.pdf".

    $fp = fopen($filepath, "w+");
    fwrite($fp, $pdfoutput);
    fclose($fp);

Here is the full code:

    use Dompdf\Dompdf;

    $result = '<table>
      <tr>
        <td>
          fitst row first column
        </td>
        <td>
          fitst row second column
        </td>
      </tr>
    </table>';

    if (module_exists('print') && !empty($result)) {
      $dompdf = new Dompdf();
      $dompdf->load_html($result);
      $dompdf->render();
      $pdfoutput = $dompdf->output();
      $filename = 'demo.pdf';
      $filepath = 'sites/default/files/pdf/demo.pdf';
      $fp = fopen($filepath, "w+");
      fwrite($fp, $pdfoutput);
      fclose($fp);
    }

Hope this demo helps you.Need more assistance regarding Drupal Web Design Services...!

Feb 01 2017
Feb 01

How to Use External Invite for User in Drupal 7? 

Imagine a scenario where you need to invite some of site users to accept some role on the site. You do not want to go and edit every such users and assign them said role. You also want leave the choice with users if they want to new role on site or not.

Enters User External Invite module.

This module allows users with proper permission to invite other users. Users have specified time before the invite expires. Inviting users also have option to cancel invite and resent invites.

Below is step to step guide to install and use this module.

1 - Download the module and install as usual.

Download the module from here: http://drupal.org/project/user_external_invite and install as usual.

2 - Configuration

Go to “admin/people/permissions” and set permission for required roles to send invite.

Go to admin/config/people/invite and set configurations as required.

Select which user roles can be invited, default role, expiration times and templates.
You can customise the emails that will be sent on various occasions here as well.

3 - Invite users

To invite users go to admin/people/invite and add address you want to invite. 

You can select role for which invite is being sent. You can also add custom message. This message will be added in email that is being sent to invited users.

4 - Manage Invites

You can manage invites from admin/people/invite/operations.

On this page, you can see list of all invites that are sent and status of each invite. From this page you can either cancel an invite or resent the invite.

The module also implements various checks such as you can not disable module till you have pending invites, making sure that roles are not changed when invites are pending for that role etc.

The module also provides a hook which can be used in case inviting email id is not present in the system.

Hope this article has helped you. Need more assistance regarding drupal development services..!

Jan 30 2017
Jan 30

Recently i had a problem where i need to migrate locations added by “Location” module in Drupal 6 site to Drupal 8 site. Currently “Location” module does not provide Drupal 8 version, so i used “Address” module in Drupal 8, but still i need to get all contents.

To solve this, i have created a custom module in Drupal 8. Steps to update locations:

  1. Create a folder /modules/custom/MODULENAME
  2. Create “MODULENAME.info.yml” file inside MODULENAME folder with following details:

           name: MODULE NAME (This can be anything you want)

           Description: MODULE DESCRIPTION
           Package: Custom

           type: module
           core: 8.x

           dependencies:
           - address:address

           More info on creating modules .info.yml file can be found here.

      3. Now, as i need to migrate data on cron run, i created “MODULENAME.module” file and implemented hook_cron

   <?php
/**
 * @file
 * File to migrate contents
 */

    function MODULENAME_cron() {
        Write code here…..
}

We need to connect to Drupal 6 database for exporting its content. Add the following code in /sites/default/settings.php along with the default database connection:

  $databases['external']['default'] = array (
  'database' => ‘DATABASENAME’,
  'username' => ‘USERNAME’,
  'password' => ‘PASSWORD’,
  'prefix' => '',
  'host' => 'localhost',
  'port' => ‘PORT’,
  'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql',
  'driver' => 'mysql',
);

We have used “External” for connecting to Drupal 6 database. “Default” is used for Drupal 8 database connection. So now there will be 2 database connections in settings.php

Code to switch to Drupal 6 database connection:

    // Switch to external database
\Drupal\Core\Database\Database::setActiveConnection('external');

// Get a connection going
$db = \Drupal\Core\Database\Database::getConnection();

Database Query to fetch locations from Drupal 6 database:

// Query to fetch all locations
  $query = $db->select('location', 'l');
  $query->join('location_instance', 'li', 'l.lid = li.lid');
  $query->fields('l', array('name', 'street', 'additional', 'city', 'province', 'postal_code', 'country', 'latitude', 'longitude'));
  $query->fields('li', array('nid', 'vid'));
  $location = $query->execute()->fetchAll();

Switch back to Drupal 8 database connection:

// Switch back
\Drupal\Core\Database\Database::setActiveConnection();

Code to update locations in Drupal 8 database: 

 // Query to import locations
  $last_location = count($location);
  foreach ($location as $key => $value) {
    // UK code is changed to GB in Drupal 8
    if ($value->country == 'uk') {
      $value->country = 'GB';
    }
    $node = node_load($value->nid);
    
    db_merge('node__field_location')
      ->key(array('entity_id' => $value->nid))
      ->fields(array(
          'bundle' => $node->getType(),
          'deleted' => '0',
          'entity_id' => $value->nid,
          'revision_id' => $value->vid,
          'langcode' => 'en',
          'delta' => '0',
          'field_location_address_line1' => $value->name,
          'field_location_address_line2' => $value->street,
          'field_location_locality' => $value->city,
          'field_location_postal_code' => $value->postal_code,
          'field_location_country_code' => strtoupper($value->country),
      ))
      ->execute();

    db_merge('node_revision__field_location')
      ->key(array('entity_id' => $value->nid))
      ->fields(array(
          'bundle' => $node->getType(),
          'deleted' => '0',
          'entity_id' => $value->nid,
          'revision_id' => $value->vid,
          'langcode' => 'en',
          'delta' => '0',
          'field_location_address_line1' => $value->name,
          'field_location_address_line2' => $value->street,
          'field_location_locality' => $value->city,
          'field_location_postal_code' => $value->postal_code,
          'field_location_country_code' => strtoupper($value->country),
      ))
      ->execute();

    // Show a message on screen once all the locations are updated.
    if ($key == ($last_location - 1)) {
      drupal_set_message('Migrated locations');
    }
  }

Note: I had same nids on drupal 6 & drupal 8 site as i used drupal 8 core migration.
  
4. Run Cron to import all locations.

Here is the full code written in hook_cron():

function MODULENAME_cron() {
  // Switch to external database
  \Drupal\Core\Database\Database::setActiveConnection('external');

  // Get a connection going
  $db = \Drupal\Core\Database\Database::getConnection();

  // Query to fetch all locations
  $query = $db->select('location', 'l');
  $query->join('location_instance', 'li', 'l.lid = li.lid');
  $query->fields('l', array('name', 'street', 'additional', 'city', 'province', 'postal_code', 'country', 'latitude', 'longitude'));
  $query->fields('li', array('nid', 'vid'));
  $location = $query->execute()->fetchAll();

  // Switch back
  \Drupal\Core\Database\Database::setActiveConnection();

  // Query to import locations
  $last_location = count($location);
  foreach ($location as $key => $value) {
    // UK code is changed to GB in Drupal 8
    if ($value->country == 'uk') {
      $value->country = 'GB';
    }
    $node = node_load($value->nid);
    
    db_merge('node__field_location')
      ->key(array('entity_id' => $value->nid))
      ->fields(array(
          'bundle' => $node->getType(),
          'deleted' => '0',
          'entity_id' => $value->nid,
          'revision_id' => $value->vid,
          'langcode' => 'en',
          'delta' => '0',
          'field_location_address_line1' => $value->name,
          'field_location_address_line2' => $value->street,
          'field_location_locality' => $value->city,
          'field_location_postal_code' => $value->postal_code,
          'field_location_country_code' => strtoupper($value->country),
      ))
      ->execute();

    db_merge('node_revision__field_location')
      ->key(array('entity_id' => $value->nid))
      ->fields(array(
          'bundle' => $node->getType(),
          'deleted' => '0',
          'entity_id' => $value->nid,
          'revision_id' => $value->vid,
          'langcode' => 'en',
          'delta' => '0',
          'field_location_address_line1' => $value->name,
          'field_location_address_line2' => $value->street,
          'field_location_locality' => $value->city,
          'field_location_postal_code' => $value->postal_code,
          'field_location_country_code' => strtoupper($value->country),
      ))
      ->execute();

    // Show a message on screen once all the locations are updated.
    if ($key == ($last_location - 1)) {
      drupal_set_message('Migrated locations');
    }
  }
}

Hope this code helps you to make things work..!!! Need more assistance regarding web development service...!

Jan 05 2017
Jan 05

Working with Drupal, often times we are required to import a large set of data in site. We have various ways to import data. Feeds module is simple and easy to use. Feeds out of the box supports many features which can cover a variety of scenarios. The module is well documented and you can access documentation here.

In this part, we are going to explain basics for how to import data using feeds.

To use feeds module, we need ctools and job scheduler module enabled.

The first step in the process is to turn required modules enabled. Enable Feeds, Feeds Admin UI. If you want an example of node importer and user importer then turn Feeds Import enabled.

Data Migration Through Feeds

Next step is to add an importer that lets you import data. To do this, go to "admin/structure/feeds" and click on "Add importer". If you have turned Feeds Import on, you will see example importers here.

Data Migration Through Feeds

Next step is to configure importer. There are various parts in this.

  1. Basic Settings
  2. Fetcher
  3. Parser
  4. Processor

Each is explained in detail below:

Data Migration Through Feeds

Basic Settings

Data Migration Through Feeds

These are basic settings for importers: 

Name and description are what you specified while creating the importer. "Attach to content type" is used to determine whether we want to attach importer to any content type form or use standalone form.  

We use the standalone form if we want to import by using a form that comes with the module at /import path. If we go with content type then import is executed by creating a node of selected content type.

Periodic updates are used to run import periodically. Proper cron configuration is required to run periodic updates. Keep periodic updates off if you are importing data one time only.

Import on submission will start importing process as soon as the form is submitted. There is an option to run the process in the background. This is particularly helpful when importing large data.

Fetcher

The Fetcher is where your feed is fetched from. We have two options here: File upload and HTTP fetcher.

Data Migration Through Feeds

File upload is where we upload the file from the local system. HTTP fetcher is where we provide content from URL. For this example, we are going with file upload. Each fetcher has its own settings.

Data Migration Through Feeds

For file upload fetcher, you can decide which types of file extensions are allowed and where uploaded files should be saved.

Parser

Parser determines the format of data. Options include following:

Data Migration Through Feeds

We are going with CSV parser for this example.

Processor

This determines what type of data we are importing. Options includes following

Data Migration Through Feeds

Node processor:  creates nodes upon import
Taxonomy term processor: created taxonomy terms upon import
User processor: creates users upon import

For this example, we are using node processor.

Node processor setting

Data Migration Through Feeds

These settings are used to decide how the processor will work. Each setting here impacts on what happens when import runs.

Bundle: This is what type of node want to create when import runs. Based on selected content type, we can create a mapping in next step.

Insert new nodes: Based on selection, new nodes are inserted based on unique key in mappings.

Update existing nodes: If unique key is repeating then based on selection existing nodes are updated, replaced or left untouched. This is particularly handy when we are using the same importer more than once.

Text format: This determines the format of text fields in selected node type. If you have HTML content, it's better to go to with Full HTML format.

Action to take when previously imported nodes are missing in the feed: This determines what to do when previously imported nodes are missing from current feed.

Author: The author of imported nodes. Leaving this empty will make anonymous user author of node. You also can make sure that user has permission to add this type of content with  Authorize checkbox.

Expire Nodes: Keep this set to never if you want do not want to delete imported nodes. Nodes will be deleted after selected time otherwise.

Mapping

Data Migration Through Feeds

This is where we determine which CSV column belongs to which field of content type. In custom drupal development, This is very important to match correct fields. For this example, we are adding title and body. The title is made unique field. Based on this and settings in the previous step, new nodes will be imported or updated.

The importer is ready and we can start importing nodes. Since we are using standalone form, we need to goto /import and upload CSV file as per our settings

Data Migration Through Feeds

Select the importer we just created.

Data Migration Through Feeds

We get a template sample on this page which helps in CSV file construction. Upload the CSV file in required format and import content. Upon completion, a status message is displayed.

In case, we want to delete all nodes that we imported we can do so by going to "Delete Items" tab on import page.

Feeds module out of the box provides many features and is very powerful. There are various other modules available that enhances feeds module functionality.

Hope this helps you, feel free to share your valuable feedbacks and inputs.

Dec 27 2016
Dec 27

Make multidomain work in multilingual site

The website we manage uses multilingual features. We have this site in 3 different languages. Language detection is done using URL.

Recently, the requirement came up to make subdomain on this site.

The following is step by step guide to create subdomain and make it work with multi language.

1 - Download module

Download and enable Domain access module and enable it.

You will receive an error  "domain access failed to load during the bootstrap phase".

Subdomains with multilingual sites

You have two options to solve this issue.

Option 1:

Copy following code in your settings.php

 /**
     * Add the domain module setup routine.
     */
    include DRUPAL_ROOT . '/path/to/modules/domain/settings.inc';

Make sure to replace "/path/to/modules/" to directory in which domain module is present. Normally it is "sites/all/modules".

Option 2:

If you are having difficulty determining the correct path, copy the following files into your settings folder.

domain > domain.bootstrap.inc
domain > settings.inc
domain > settings_custom_url.inc
The files should be in the same directory as your active settings.php file.

Then add the following lines:

/**
 * Add the custom_url_rewrite_outbound function.
 */
include 'settings.inc';

More details available here or you can hire drupal developer for help.

2 - Add domain

Navigate to "admin/structure/domain/create" and create domain.

Subdomains with multilingual sites

3 - Configure domain

To have domain specific configurations, you need to have Domain Configuration and Domain Settings sub modules enabled. Enabling this module allows you to have different settings on each domain. These settings include domain email address, site slogan, front page, menu settings etc.

Subdomains with multilingual sites

4 - Theme setting for domain (optional)

If you want your subdomain to have separate theme, you need to enable Domain Theme submodule. This module allows you to set theme for subdomain.

Subdomains with multilingual sites

5 - Set multi language for domain

Imagine a scenario where we have multilingual sites with domains de.example.com and en.example.com. Now, we want to create subdomains for this site. These subdomains will have URL like this: test.de.example.com and test.en.example.com.

Both de.example.com and test.de.example.com are in german language. Similarly, both en.test.com and test.en.example.com are in english language.

To implement this, we need to use Language MultiDomain module. Along with this module we need to use patch from here to make this work properly.

Once this module is enabled, navigate to "admin/config/regional/language" and edit language and set multiple domains in "Language Domain"

Subdomains with multilingual sites

Save and you are done. Now you have 4 domains.
 

That's so easy now :) Feel free to catch more quick solution blogs and your feedbacks would be very valuable. 

Dec 08 2016
Dec 08

Twig is a php template engine used in drupal 8. It is fast, secure and flexible. Template files and theme functions is replaced by *.html.twig template files in D8.

{{ }}

Print statement

{# #}

Comment statement

{% %}

Execution statement (Ex. for-loops)

{% set foo = 'bar' %}

Sets variable “foo” with value “bar”

{% set foo, bar = 'foo', 'bar' %}

Sets Multiple variable at a time.

This is equivalent to

{% set foo = 'foo' %}

{% set bar = 'bar' %}

{% set foo = [1, 2] %}

Set array “foo” with values “1” & “2”

{% set test = {'foo': 'bar'} %}

Set array “test” with key as “foo” & value as “bar”

{% set c = a ~ b %}

Concatenates string “a” & “b”. operands are converted into strings.

{{ dump(user) }}

Prints array if “user” is an array

if it is a variable then it will print variable value.

{{ dump(user|keys) }}

Prints “key” of “user”.

  1. Filters are separated by pipe symbol (|)
  2. Multiple filters can be chained
  3. Optional arguments appear in parentheses

{{ name|striptags|title }}

“striptags” removes all HTML tags from “name” and “title” title-cases it.

{{ 'temperature is less than 18'|t }}

“t” is used for translation of string

{{ list|join(', ') }}

Join list with comma(,)

{{ "now"|date('d/m/Y H:i', timezone="Europe/Paris") }}

Render “now”date with specific format and specific timezone

          Example:

<ul>
  {% for key, user in users %}
        <li>{{ user.username|e('html') }}</li>
  {% else %}
        <li><em>no user found</em></li>
  {% endfor %}
</ul>
  • If Condition

Example 1:

{% if temperature < 18 %}
<p>{{ 'temperature is less than 18'|t }}</p>
{% elseif temperature > 18 and temperature < 27 %}
<p>{{ 'temperature is between 18 & 27'|t }}</p>
{% else %}
<p>{{ 'temperature is greater than 18'|t }}</p>
{% endif %}

Example 2:

{% if users | length > 0  %}
        <p>There are {{ users | length }} users available.
{% endif %}

In this example, if there are users available, then it will print the number of users available.

{{ foo ? 'yes' : 'no' }}

If “foo” is set then it will return “yes” else “no”

{{ foo ?: 'no' }}

same as {{ foo ? foo : 'no' }}

{{ foo ? 'yes' }}

same as {{ foo ? 'yes' : '' }}

{{ foo ?? 'no' }}

if “foo” is defined and not null then it will return “foo” else “no”

{{ "foo #{bar} baz" }}

Value of #{bar} is replaced in the string

{{ "foo #{1 + 2} baz" }}

#{1 + 2} expression is evaluated and replaced

  1. Manually escaping

{{ user.username|e }}

{{ user.username|e(‘html’) }}

Default is “html” escape

{{ user.username|e('js') }}

Escape JS

{{ user.username|e('css') }}

Escape CSS

{{ user.username|e('url') }}

Escape URLs

  1. Automatically escaping

{% autoescape %}
   Automatically escaping text.
{% endautoescape %}

Default is “html” escape

{% autoescape 'js' %}
   Automatically escaping text.
{% endautoescape %}

Auto Escape ‘js’

{% include 'header.html.twig' %}

This is used to include “header.html.twig” in any other file.

{% macro input(name, value, type, size) %}
        <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
{% endmacro %}

In above example “Type” default value is set to “text” and “size” default value to “20”. It can also be defined as:

{% macro input(name, value = "", type = "text", size = 20) %}
<input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" size="{{ size }}" />
{% endmacro %}

Import macro in different file, and then use it:

{% import "forms.html" as forms %}
<p>{{ forms.input('username') }}</p>


  • Import individual macro names with alias:
{% from 'forms.html' import input as input_field %}
<p>{{ input_field('username') }}</p>


  • Import macro into same file:
{% import _self as forms %}

Hope this helps you all. Feel free to leave valuable feedbacks regarding the same. 

Nov 23 2016
Nov 23

Drush is a command line tool built to assist you in working with Drupal from the terminal. It comes by default with a bunch of useful commands, such as downloading, enabling or even updating modules.

With drupal, drush can be extended. It is really simple and everyone can write their own drush commands. As an example, I am going to show you how you to trigger a node save.

First, we will need a drush command file. The drush command file must end with .drush.inc.

It can be placed in a number of locations.

1. Drush folder in your home directory: ~/.drush. If you put the drush command file in that directory, your commands will be available for all your projects.

2. Inside a module: Add your command file to the module folder. This is more preferable if the command functionality is specific for a project.

In this file, we are going to implement the hook_drush_command() hook. This hook defines the drush command.

File: mymodule.drush.inc

Hook function: mymodule_drush_command()

<?php
function MYMODULE_drush_command() {
  $items  = array();
  $items['nodecreate'] = array(
    'callback'    => 'custom_drush_create_node',
    'description' => dt('Triggers a node-save'),
    'aliases' => array('nc'),
    'arguments'   => array(
      'title'     => "Title of node",
    ),
    'options' => array(
      'repeat' => 'Number nodes to create.',
    ),
    'examples' => array(
      'drush nc error' => 'Prints error as node title is blank.',
      'drush nc Test --repeat=5' => 'Creates 5 nodes with title Test.',
    ),
  );
  return $items;
}

Running the Drush help command for our own command drush help nc will list some useful information about this command (arguments, options, description, aliases, examples etc)

Callback Function

As we have written callback function, Drush expects a function to be declared called drush_create_node(). This default naming structure starts with drush followed by the name of the command all connected with underscores.

function MYMODULE_create_node($title) {
  $repeat = drush_get_option('repeat', 1);
  for ($i=0; $i < $repeat; $i++) {
    $node = new stdClass();
    $node->type = "page";
    $node->title = $title;
    $node->language = LANGUAGE_NONE;
    $node->uid = 1;
    $node = node_submit($node);
    node_save($node);
  }
  drupal_set_message(t('Created ' . $repeat . ' node with title ' . $title));
}

Now clear the drush cache: drush cc drush

And run command: drush nc OR drush nodecreate

Arguments and options

-> Arguments are mandatory whereas options are not.

-> Arguments are passed as function parameters (in order) while options are retrieved in the callback using a special helper function (drush_get_option).

We declared 1 argument (called title) and one option called repeat.

-> The argument type will be the first string that gets written after the command name in the terminal (drush nodecreate or drush nc).

-> The option will be an integer value that gets assigned to the --repeat flag in the command.

Ex. drush nc Test --repeat=2

This will create 5 nodes, with title “Test”

User input

Let’s make it so that if a user doesn’t pass an argument, we ask them what argument they’d like to pass and use the value they provide interactively.

This goes to the top of the command callback function before checking whether the correct argument was passed.

// Check for existence of argument
if (!$title) {
  $options = array(
    'Test' => t('Test'),
    '0' => t('Error'),
  );
  $title = drush_choice($options, t('Please choose a option.'));
}
…
  1. Everything happens only if the user has not passed an argument.
  2. We create an array of key-value pairs that will represent the choices we give the user. The array keys represent the machine name of the choice while the values, the human readable name.
  3. We pass this array along side a question string to the drush_choice() function that will return the machine name of the choice the user makes. And that becomes our new $title variable (the argument).

If the title is not set or error is returned, we can add code to print error in terminal.

…
  if(!$title || $title='error') {
    drupal_set_message(t('Error! No title set.'));
  }
…

Full Code will look something like this:

<?php
 
function MYMODULE_drush_command() {
  $items  = array();
  $items['nodecreate'] = array(
    'callback'    => 'custom_drush_create_node',
    'description' => dt('Triggers a node-save'),
    'aliases' => array('nc'),
    'arguments'   => array(
      'title'     => "Title of node",
    ),
    'options' => array(
      'repeat' => 'Number nodes to create.',
    ),
    'examples' => array(
      'drush nc error' => 'Prints error as node title is blank.',
      'drush nc Test --repeat=5' => 'Creates 5 nodes with title Test.',
    ),
  );
  return $items;
}
 
function MYMODULE_create_node($title) {
  if (!$title) {
    $options = array(
      'Test' => t('Test'),
      '0' => t('Error'),
    );
    $title = drush_choice($options, t('Please choose a option.'));
  }
  if(!$title || $title='error') {
    drupal_set_message(t('Error! No title set.'));
  }
  else {
    $repeat = drush_get_option('repeat', 1);
    for ($i=0; $i < $repeat; $i++) {
      $node = new stdClass();
      $node->type = "page";
      $node->title = $title;
      $node->language = LANGUAGE_NONE;
      $node->uid = 1;
      $node = node_submit($node);
      node_save($node);
    }
    drupal_set_message(t('Created ' . $repeat . ' node with title ' . $title));
  }
}

Feel free to drop your any queries/concern related to this blog. For Drupal Web Development we are always ready to help :) Stay tuned! 

Nov 23 2016
Nov 23

Views Tabs Field 

This module provides a field of Views which renders content in vertical or horizontal tabs. This allows us to show content on views page in the tabbed format. This module provides a field in view which can be used to show content on tabs.

Below are the steps to use this module.

1 - Download and enable module normally.

2 - Create a view and add fields in this view. The fields that you want to be displayed as tabs, make them exclude from a display.

3 - Add "Global: Tabs" field.

How To Use Views Tab Fields In Drupal

4 - Choose type of tabs you want : Horizontal Tabs or Vertical Tabs

Tabs Selection

5 - Under tabs, add as many tabs as you want. In Tab content, add fields from replacement pattern. You can add HTML in this as well.

Tabs Content

6 - Save the view. The final result is something like this depending upon which types of tabs you selected.

Tab Fields In Drupal

Hope this blog was very useful for you guys. Leave your comments and feedbacks. 

Nov 23 2016
Nov 23

As we all know, Drupal 8 having Symfony framework is making a great way with many changes/tweaks carried out into it.

There are many contributed modules available in Drupal 7 but do not exist yet for Drupal 8, so to migrate respective modules from Drupal 7 to Drupal 8 here’s the quick and easy steps which would lead towards success.

1) Copy paste D7 module to D8 directory.

- The folder structure for D7 was /sites/all/modules which is changed to /modules.

2) Convert your .info file:

  1.  If you visit the Drupal 8 module administration page at admin/modules, you will notice that your module is not listed. In order for Drupal 8 to detect your module, you will need to update the module's .info file to a new YAML format.
  2. Rename mymodule.info to mymodule.info.yml
 -- You can reuse the existing .info file and then convert it. Rename your mymodule.info file to mymodule.info.yml.
    -- Convert the file to the new YAML format
      --- For the most part, change all = to :
      --- For arrays (e.g. dependencies[] = node), use the following format in drupal 8:
            dependencies:
              - node
          ---- Replace 
                Drupal 7
                  stylesheets[all][] = css/layout.css
                  stylesheets[all][] = css/style.css
                  stylesheets[print][] = css/print.css
                Drupal 8
                  stylesheets:
                    all:
                      - css/layout.css
                      - css/style.css
                    print:
                      - css/print.css
          ---- Replace 
                Drupal 7
                  regions[header] = Header
                  regions[help] = Help
                Drupal 8
                  regions:
                    header: Header
                    help: Help
  • In drupal 7 we use ; for Comments And now in drupal 8 we use # for Comments.
  • ‘type’ is required. It indicates the type of extension. Values: module, theme or profile. For example: type: module
  • Remove files[] entries.
  • Convert configure links to route names
 In Drupal 8, specify route name instead of the system path.
            Drupal 7
              configure = admin/config/system/actions
            Drupal 8
              configure: action.admin

  • Change "core" & "Version" accordingly
  • Once you've converted the file, reload admin/modules and Enable your module.
  • Debugging .info.yml: If the module is not listed on modules page or you cannot enable the module, see the troubleshooting tips

3) Convert hook_menu():

  • Create module.routing.yml file
  • Routes in D8 are defined in module.routing.yml files in YAML format. This was previously defined in hook_menu().
  • Machine name for route should be module_name.sub_name. - The path has to be added to route e.g. /admin/test. Do not forget to add preceding slash.
  • _content will be pointing to the callback function and return of page callback is rendered inside a block.
  • Use _controller when you don’t want to render a fully themed HTML page.
  • _controller or _content keys may point to a function or a method of a class.

Ex: _content: \Drupal\MYMODULE\Controller\MYCLASS::MYMETHOD - hook_menu_alter() is no longer used.  

Ex.

   # Drupal 7 menu item
      $items['admin/test'] = array(
        'title' => 'Tests',
        'description' => "Test description.",
        'page callback' => 'test_admin_callback',
        'access arguments' => array('access content'),
        'type' => MENU_LOCAL_TASK,
        'file' => 'test.admin.inc',
      );

# Drupal 8 test.routing.yml snippet

 test.admin:
        path: '/admin/test'
        defaults:
          _controller: '\Drupal\test\Controller\TestController::adminOverview'
          _title: 'Tests'
        requirements:
          _permission: 'access content'

- Explanation of various parts:

test.admin: This is route machine name (module_name.sub_name). Other parts of code should use the machine name to refer to route.

  • path: Path of page. Do not forget to add leading slash (/)!
  • defaults: Page & title callbacks
  • requirements: Conditions to display the page (Permissions, module dependency etc).
  # Drupal 8 TestController.php
      /**
       * @file
       * Contains \Drupal\test\Controller\TestController.
       */

      namespace Drupal\test\Controller;

      use Drupal\Core\Controller\ControllerBase;

      /**
       * Controller routines for test routes.
       */
      class TestController extends ControllerBase {

        /**
         * Returns an administrative overview of test.
         *
         * @return array
         *   A render array representing the administrative page content.
         */
        public function adminOverview() {
          // ...
        }
      }

Explanation of various parts:

  • namespace This declares the prefix needed to fully qualify the name of the class we are defining.
  • use This allows to use ControllerBase instead of fully qualified name.
  • adminOverview() public method should be defined which return a renderable array.
  • Main components to make the route work: path and controller
  • We have provided ‘permission’ in 'requirements' section. so if visitor access the page '/admin/test' and have the permission to access content, then the output will be generated based on that method on the TestController class.
  • Reference for more details: https://www.drupal.org/node/2118147

4) drupal_valid_path is changed to PathValidator service:

    D7
    $is_valid = drupal_valid_path($path);

    D8
    $is_valid = \Drupal::service('path.validator')->isValid($path);

5) module_exists($module) is deprecated in Drupal 8:

- Use Drupal::moduleHandler()->moduleExists($hook).

6) Convert form, form_submit & form_validation functions:

- In Drupal 7, forms were built by a procedural function. Validation and submission were handled by following names: the name of your form function, followed by either _validate or _submit.

- Use: return drupal_get_form(FORMID);

- In Drupal 8, there is an interface called FormInterface.

- It has four methods:

  • getFormID()
  • buildForm()
  • validateForm()
  • submitForm()

- getFormID() is used to specify the form ID. It isn't used by drupal_get_form() or in validation or submission functions. Although it is used for theme functions, hook_form_alter() etc

- Here is the Drupal 8 version of the form above:

namespace Drupal\form_test;

      use Drupal\Core\Form\FormInterface;

      /**
       * Provides a test form object.
       */
      class FormTest implements FormInterface {

        /**
         * {@inheritdoc}
         */
        public function getFormID() {
          return 'form_test_form_test_object';
        }

        /**
         * {@inheritdoc}
         */
        public function buildForm(array $form, array &$form_state) {
          $form['element'] = array('#markup' => 'The FormTest::buildForm() method was used for this form.');

          $form['test'] = array(
            '#type' => 'textfield',
            '#title' => t('Test'),
          );

          $form['actions']['#type'] = 'actions';
          $form['actions']['submit'] = array(
            '#type' => 'submit',
            '#value' => t('Save'),
          );
          return $form;
        }

        /**
         * {@inheritdoc}
         */
        public function validateForm(array &$form, array &$form_state) {
          drupal_set_message(t('The FormTest::validateForm() method was used for this form.'));
        }

        /**
         * {@inheritdoc}
         */
        public function submitForm(array &$form, array &$form_state) {
          drupal_set_message(t('The FormTest::submitForm() method was used for this form.'));
          Drupal::config('form_test.object')
            ->set('test', $form_state['values']['test'])
            ->save();
        }

      }

- This form would be used in this way: return drupal_get_form('Drupal\form_test\FormTest');

- Form ids for article_node_form is changed to node_article_form. tags_taxonomy_term_form is changed to taxonomy_term_tags_form.

So replace:

if (strpos($form_id, '_node_form') !== FALSE) {

with
      
if (preg_match('#node_(.*?)_form#e', $form_id, $match))

7) variable_get()/variable_set() and variable_del() is removed in Drupal 8:

  • Use the State API to store temporary data. - Use the Configuration API for “deployable” changes (across multiple environments) and use the State API for a specific environment.

8) Info hooks use new Plugin system in Drupal 8:

  • If your module previously defined a hook_something_info(), it now changed to declaring a new class with an "annotation". Specify a specially formatted PHP comments above the class which will provide metadata.
  • Examples in the core are blocks, image styles, text formats, field widgets and formatters, and more.
  • More detail can be found here.

9) New theme system:

  • Drupal 8 theming engine is changed from PHPTemplate to Twig. It is easy to use for themers with better security. It also has clear separation between presentation and logic. Any theme-able output of module should provide a Twig template.
  • Details can be found here.

These are some of the key changes, I stated above. You can find full details of change records here. Search https://drupal.org/list-changes for keywords related to the problem, then follow the instructions there to fix it.

Debugging Techniques

Here are some debugging tips, if you encounter any errors during porting the module:

  1. If your module breaks on installation, Comment out hook_install() and hook_uninstall(). This will turn on your module.
  2. As long as the site is responding, you can use debug() for outputting the result of a variable in Drupal's messages area.
  3. If you're only getting a “white screen of death,” you should check your web server's PHP error log.
  4. Run the rebuild.php script (or drush rebuild). Note: To run the rebuild.php script, you must edit your settings.php file to set $settings['rebuild_access'] = TRUE;

Happy porting..!!!! Need assistance regarding Drupal Website Migration Contact us now!

Nov 23 2016
Nov 23

Tokens provides an input filter that replaces tokens with content. All tokens are binded to its function that returns content as a value for that token. Token can contain parameters that will be passed to its function.

To create custom token, three token api functions are used:

/**
 * Implements hook_token_info().
 */
function MYMODULE_token_info() {
  // Define a new token type.
  $types['custom'] = array(
    'name' => t("Custom Tokens"),
    'description' => t("Tokens that are custom."),
  );


  // Define any new tokens.
  $tokens['fname'] = array(
    'name' => t("First name"),
    'description' => t("First name token"),
  );


  $tokens['lname'] = array(
    'name' => t("Last name"),
    'description' => t("Last name token"),
  );


  return array(
    'types' => $types,
    'tokens' => array(
      'tn' => $tokens,
    ),
  );
}

We are creating custom token type here. Get a list of the existing tokens and types by calling token_get_info() to add token to existing types.

/**
 * Implements hook_tokens(). This hook will operate the token and replace it with it's value.
 */
function MYMODULE_tokens($type, $tokens, array $data = array(), array $options = array()) {
  $replacements = array();


  if ($type == 'custom') {
    // Loop through each of the available tokens.
    foreach ($tokens as $name => $original) {
      // Find our custom tokens by name.
      switch ($name) {
        case 'fname':
          // Work out the value of our token.
          $value = 'Deepali';
          // Give our token it's value!
          $replacements[$original] = $value;
          break
        case 'lname':
          // Work out the value of our token.
          $value = 'Agarwal';
          // Give our token it's value!
          $replacements[$original] = $value;
          break;
      }
    }
  }
  return $replacements;
}

Custom created token will now be available in the list of tokens. You can use Token Insert module to insert tokens into a textarea (plain text and wysiwyg textareas).

ARGUMENTS

You can pass additional contextual data to token_replace() function which is then passed to implementations of hook_tokens(). You can also use below code to pass the values of token dynamically.

/**
 * Implements hook_tokens(). This hook will operate the token and replace it with it's value.
 */
function MYMODULE_tokens($type, $tokens, array $data = array(), array $options = array()) {
  $replacements = array();


  if ($type == 'custom') {
    // Loop through each of the available tokens.
    foreach ($tokens as $name => $original) {
      // Replace token with desired values
      if (array_key_exists($name, $data)) {
        $replacements[$original] = $data[$name];  
      }
    }
  }
  return $replacements;
}


/**
 * Here we will use the token_replace() function to get the actual content after replacement.
 */
function MYMODULE_MYFUNCTION(&$variables) {
  $data = array();
  $data['fname'] = 'Deepali';
  $data['lname'] = 'Agarwal';
  $temp = "Full name : [custom:fname] [custom:lname]";
  $temp = token_replace($temp, $data);
  echo $temp;
  // After token replacement $temp variable will contain "Full name : Deepali Agarwal"
}

The $data parameter can contain either the $user object or the $node object required to calculate the appropriate values.

Feel free to share your valuable inputs/feedbacks. #letstalksolution and need more assistance regarding Drupal Web Development contact us now.

Nov 23 2016
Nov 23

For using basic & common classes for grid layout (For width), typography, alignment etc, we write css like:

For alignment

.margin-top--5px { margin-top: 5px; } .margin-top--10px { margin-top: 10px; } .margin-top--15px { margin-top: 15px; } ...

Typography

.h1-32pt { font-size: 32pt; } .h2-28pt { font-size: 28pt; } .h3-24pt { font-size: 24pt; } .h4-20pt { font-size: 20pt; } ...

Grid views

.width-1 { width: 8.33%; } .width-2 { width: 16.66%; } .width-3 { width: 25%; } .width-4 { width: 33.33%; } ...

  • But with use of below code, all the classes with respective css are generated at once and we don't have to write each css manually.
  • User can generate classes for different properties like width, height, padding, margin, font-size, line-height, etc.
  • Main benefits are any numerical units like px, %, vw, vh, em, rem, etc can be used
  • Whole series of classes like above will be generated by using small code snippet.

Example of generated class with respective value

Ex, width: 10px , width: 15px , width: 20px , … , width: 100px , width: 200px , …

Below code generate CSS properties like:

.margin-top--0per {
  margin-top: 0%;
}
.margin-top--1per {
  margin-top: 1%;
}
.padding-all--0px {
  padding: 0px;
}
.padding-all--10px {
  padding: 10px;
}

Note: Our demo creating 1 to 100 value.

SCSS Code:

/**
 * Adding margin, padding & width classes.
 * Ex. margin-top--10px, padding-all--50per, width--100per
 */


/* Map for position */
$position-map: (
  top: 'top',
  right: 'right',
  left: 'left',
  bottom: 'bottom',
);


$unit-map: (
  per: '%',
  px: 'px',
);


@for $size from 0 through 100 {
  @each $unit, $ut in $unit-map {
    @each $position, $pos in $position-map {
      .margin-#{$position}--#{$size}#{$unit} { margin-#{$position}: #{$size}#{$ut}; }
      .padding-#{$position}--#{$size}#{$unit} { padding-#{$position}: #{$size}#{$ut}; }
    }


    .margin-all--#{$size}#{$unit} { margin: #{$size}#{$ut}; }
    .margin-vertical--#{$size}#{$unit} {
      margin-top: #{$size}#{$ut};
      margin-bottom: #{$size}#{$ut};
    }
    .margin-horizontal--#{$size}#{$unit} {
      margin-right: #{$size}#{$ut};
      margin-left: #{$size}#{$ut};
    }


    .padding-all--#{$size}#{$unit} { padding: #{$size}#{$ut}; }
    .padding-vertical--#{$size}#{$unit} {
      padding-top: #{$size}#{$ut};
      padding-bottom: #{$size}#{$ut};
    }
    .padding-horizontal--#{$size}#{$unit} {
      padding-right: #{$size}#{$ut};
      padding-left: #{$size}#{$ut};
    }


    .width--#{$size}#{$unit} {
      width: #{$size}#{$ut} !important;
    }
  }
}

So, this is a smart code - use it and save your time guys! Feel free to add your views and comments for any Drupal Development Services. #letstalksolution

Nov 23 2016
Nov 23

GitFlow is a collection of Git commands to provide many repository operations with just single command. It helps to keep track of features, hotfixes and releases in projects. It is used for projects where you might have multiple streams of work running concurrently.

Installing git-flow

+ OSX

  • Homebrew: $ brew install git-flow-avh
  • Macports: $ port install git-flow-avh
     

+ Linux

$ apt-get install git-flow

+ Windows (Cygwin)

$ wget -q -O - --no-check-certificate

https://raw.github.com/petervanderdoes/gitflow-avh/develop/contrib/gitflow-installer.sh install stable | bash

See the Wiki for detailed Installation Instructions.

Initialization

You need to initialize a new repo inside an existing git repository. Run following command:

git flow init [-d]

-d flag will accept all defaults.

This will ask questions like which branches you would like to use as development and production branches, and how would your prefixes be named. Press Enter to accept default suggestions.
 

FEATURE BRANCHES

Using git-flow feature branches you can easily work on multiple features of project at the same time.

+ Create a feature branch

New feature branch is created from the 'develop' branch and switches to it.

git flow feature start MYFEATURE
Gitflow

+ List all feature branches

-   git flow feature

+ Publish a feature branch

Push a feature branch to the remote repository.

-   git flow feature publish MYFEATURE

Note: This can be used only once in a branch, next time you want to push any code in same feature branch you need to use:

 -   git push origin feature/MYFEATURE

+ Track a feature branch

Get a feature published by another user (Pull a feature branch). git flow feature track MYFEATURE

This is similar to:

  • git checkout feature/MYFEATURE
  • Git pull origin feature/MYFEATURE

+ Finish a feature branch

Finish the development of a feature. This action performs the following:

  • Merges MYFEATURE into 'develop'
  • Removes the feature branch
  • Switches back to 'develop' branch
  • git flow feature finish MYFEATURE
Gitflow

VERSIONED RELEASE

Using git-flow release branches you can create tagged and versioned releases. Create a new release branch when you have completed current version and it’s ready to deploy to production.

+ Create a release branch

New release branch is created from the 'develop' branch and switches to it. git flow release start MYRELEASE

Gitflow

+ List all release branches

 -    git flow release

+ Publish a release branch

Push a release branch to the remote repository.

-     git flow release publish MYRELEASE

+ Track a release branch

Get a release published by another user (Pull a release branch).

-     git flow release track MYRELEASE

+ Track a release branch

Get a release published by another user (Pull a release branch).

-     git flow release track MYRELEASE

+ Finish a release branch

Finish the development of a release. This action performs the following:

  • Merges MYRELEASE into ’master’
  • Tags the release with its name
  • Back-merges the release into 'develop'
  • Removes the release branch
git flow release finish MYRELEASE

Note: Don't forget to push your tags

git push --tags
Gitflow

HOTFIXING PRODUCTION CODE

Using hotfix branches you can act immediately upon an undesired bugs on production version.

+ Create a hotfix branch

New release branch is created from the 'develop' branch and switches to it.

git flow hotfix start MYHOTFIX

myhotfix argument marks the new hotfix release name.

Gitflow

+ List all hotfix branches

 -     git flow hotfix

+ Finish a hotfix branch

Finish the development of a release. This action performs the following:

  • Merges MYHOTFIX into ’master’ & ‘develop’
  • Tags master merge with the hotfix version.

git flow hotfix finish MYHOTFIX

Gitflow

 

    Advantages of Git Flow

  • Gitflow was developed to manage the branching mechanism with a standardised approach when developing features, handling releases and managing hotfixes.
  • Using multiple separate branches in Git will provide flexibility but gets complex. This is easy in gitflow.
  • Gitflow makes developer speed up the process with familiar branch structure.
  • Single command to do multiple things at a time.
  • Switching branches is easy.
  • Keep repository & process clean and tidy.

Feel free to share your views regarding Git Flow we follow strictly for every project to ensure best results and need any assistance for Web Development Service Contact us now!!

Nov 23 2016
Nov 23

Acquia Cloud is a Drupal-tuned application lifecycle management suite with a complete infrastructure to support Drupal deployment workflow processes from development and staging through to production.

Acquia dev desktop works very well for Mac and windows. To move site from linux system to acquia cloud we need to use command line. Acquia docs are very well written and provides detailed info about the process.

Below are the compiled steps to move site from local system to acquia cloud from ubuntu using command line.

Pre requisite for this is that you already created a acquia site.

Once your site is set, you need to move your data on newly created site.

1 - Create drush archive

Make sure that you have drush 4.5 or later. You can use " drush version" to find version of drush you are using.

Once you have proper version of drush, run following command to create drush archive of your site.

 drush archive-dump --destination=../mysite.tar.gz

This will create drush archive in parent directory of drupal setup. This includes your database as well.

Make sure not to include files folder in this if you have very large data as acquia allows 1GB data limit for each site. This can cause problem in later steps of process when we use this archive to import site.

2 - Move site archive to acquia cloud

Make sure that you have SSH access and your SSH key is added to your account. SSH keys are located at credentials tab in your account on acquia.

To move archive, do following

 scp mysite.tar.gz [site].[env]@[server]:/mnt/gfs/[site].[env]/import

Here, [site] is name of site on acquia cloud. [env] is the environment you are using. It is one of dev, test and production. [server] is full DNS of server hosting your site.

All these details can be found at your site dashboard under "Users and keys".

 Drupal site deployment over acquia cloud via command line

[site] is the string before the @ sign and the [server] is the string after the @ sign.

3 - import the site

Login to server using SSH

ssh [site].[env]@[server]

Use same [site] ,[env] and [server] from step above.

Import the site using

drush @[site].[env] ah-site-archive-import /mnt/gfs/[site].[env]/ import/mysite.tar.gz

This command will take some time to complete depending upon how large you site is. Once done you will have your site ready on acquia cloud.

Nov 23 2016
Nov 23

Looking at the heading, lets start with what is CRM core? From drupal.org, CRM core is

“CRM Core is a set of modules for managing contacts, activities and relationships within your Drupal website. It is designed to provide a basic framework for managing these items and interacting with other components of Drupal.”

CRM core includes various tools that provides that provides support for these entities and makes it easier to support in drupal website. One of such tools is User Synchronization.

CRM Core provides the ability to link a contact record to a user account. This is very useful in maintaining relationship between user and corresponding contact. Linked contacts can be loaded from user account.

User Synchronization is part of CRM core, you need to enable the module from module list.

Drupal CRM Core Module

User synchronization configuration can be accessed from admin/config/crm-core/user-sync.

User Synchronization works in 2 ways:

  • Contact to User Management
  • Setting up user synchronization

Contact to User Management

This allows you to link specific users to specific contacts. You will find it at admin/config/crm-core/user-sync/contact-to-user-management .

Initially, this page lists all the user accounts we have on the site. It also shows the contact records they are connected with. You get set of filters on top of the page to narrow down your search. You can edit and remove sync from this screen.

Drupal CRM Core Module

Setting up user synchronization

This allows you to create rules for linking user accounts and contact records. These rules will be enforced when new user accounts are created, and also when current users are synced. We have various options here.

Add a new rule:

Drupal CRM Core Module

On this page, we can create new rule sync user role with contact type. whenever a new user is created with specified role, contact for this user with specified types is also created. This also works for existing user. We can create multiple rules and set weight for them using drag and drop

Sync current users

Drupal CRM Core Module

This allows existing users to be synced with contacts based on rules defined above.

User synchronization is used for linking user accounts with contact records. It creates new contact and links them to users and allows us to define relationships from UI. This does not take into account the contact information already stored in the system.

Stay Connected for more such solution blogs. 

Jul 04 2016
Jul 04

Instagram block is easy to use and configure module that allows you to show your instagram photos on site in a block.

To use this module, you need to create Instagram client application. The steps are as follows:

1. Login to instagram with the account from which you want to show photos.

2. Goto https://www.instagram.com/developer/clients/manage/ and click on "Register a new client"

Instagram Module

3. Fill in all the details and register. Make sure you enter URL with HTTP or HTTPS in redirect URI. This URI should be URL of your website.

Instagram Module

4. Upon saving the Client, you will see client ID and client secret. This Client ID will be used later to generate access token to use with instagram module.

Instagram Module

Once Instagram client is created, it's time to download and enable instagram block module. The module can be found here . 

Steps to configure instagram_module:

1. Download this module and enable it normally.

2. Go to admin/config/services/instagram_block, you will see that you need to add user ID and access token here. Note that user ID is not same as Client ID generated with Instagram client.

Instagram Module

3. To get these details, we need to use following URL: https://www.instagram.com/oauth/authorize/?client_id=[client id from Instagram client]7&redirect_uri=[redirect URI from instagram client]&response_type=token (You might get "Implicit authentication is disabled" error. In this case, edit your client and uncheck "Disable implicit OAuth" from security tab.)

You will be asked to authorise the user and once you authorise you will be redirected to redirect URI you specified in client info. The URL will contain the access_token as well.

For example, http://test.com/#access_token=xxxxxxxxxx.yyyyyyyyyyyyyyyyyy Everything that precedes the first dot in access_token is the User id. Check this screenshot:

Instagram Module

4. Add these details in admin/config/services/instagram_block and save.

5. Goto admin/structure/block. Instagram blocks add a block called "Instagram Block - User Block". You can control how many images to show, image resolution and preview style from this block configuration. Assign this block to desired region and you are all set.

Instagram Module

Here’s the final results:

Instagram Module

You can also use multiblock to make a more than one instagram blocks on the site.

You can style this block the way you want. Change number of images showing and size of images.

Jul 04 2016
Jul 04

Batch processing is the execution of a series of jobs in a site without manual intervention each on a set or "batch" of inputs, rather than a single input. Batch operations in Drupal API is primarily designed to integrate nicely with the Form API workflow, but can also be used by non-Form API scripts.

1) Build a url using hook_menu to execute batch process.

/**
 * Implements hook_menu().
 */
function MYMODULE_menu() {
  $items = array();

  // Create a page with URL admin/batch
  $items['admin/batch'] = array(
    'title' => 'Demo Batch',
    'description' => 'Run batch operations.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('MYMODULE_batch_form'),
    'access arguments' => array('administer site configuration'),
    'type' => MENU_NORMAL_ITEM,
   );

  return $items;
}

2) Callback function for batch process MYMODULE_batch_form.

 /**
 * Callback function.
 * Create submit button in the form on admin/batch page
 */
function MYMODULE_batch_form(){
  $form = array();
  $form['submit'] = array('#type' => 'submit', '#value' => t('Start batch process'));
  return $form;
}

3) Submit function to set batch process. This just passes a function name to the batch API. The referenced function should build the array which tells batch API what to do. 

/**
 * Submit function for form.
 * Set the batch process to start on form submit.
 */
function MYMODULE_batch_form_submit($form, $form_state){
  batch_set(MYMODULE_build_batch());
}

4) Create the list of operations to call when the batch starts. Here we essentially build an array of future function calls, with arguments, and a finished function.

The important thing here is the $operations array. When the batch starts it will loop through $operations array.

/**
 * Generate batch process & its operations.
 */
function MYMODULE_build_batch() {
  $operations = array();

  // Here we can add multiple operation
   $operations[] = array('MYMODULE_process_data', array($arg1)); // operation with argument
   $operations[] = array('MYMODULE_process_data'); // operation without argument

  //Define your batch operation here
  $batch = array( 
    'title' => t('Batch operation processing'),
    'operations' => $operations,
    'finished' => 'MYMODULE_build_batch_finished',
    'init_message' => t('Initializing...'),
    'progress_message' => t('Operation @current out of @total.'),
    'error_message' => t('Found some error here.'),
    'file' => drupal_get_path('module', 'MYMODULE') . 'MYMODULE.module',
  );

  return $batch;
}

5) Create the operation code. This is the actual operation part - the thing that does the work. The arguments it receives will come from the $operations[] loop in step 4 above. Note the additional $context argument. This is in addition to the ones we provided in step 4 and lets us converse with the batch. This is useful for passing back status messages, etc.

 /**
 * This is the function that is called on each operation of batch process.
 */
function avert_replace_url_ru_batch_process($arg1 = '', &$context) {

  // Optional message displayed under the progressbar.
  $context['message'] = t('Now Processing....');

  $updated = FALSE;

  try {
    LOGIC FOR YOUR OPERATION...

    $updated = TRUE;
  }
  catch (\PDOException $e) {
    $error = $e->getMessage();
  }

  if ($updated) {
    drupal_set_message("Updated.");
  }
}

6) Let the user know we have finished and if there were any errors.

 /**
 * Batch finished 
 * Function runs after batch process finishes.
 */
function avert_replace_url_ru_build_batch_finished($success, $results, $operations) {
  if ($success) {
    // Here we could do something meaningful with the results.
    // We just display the number of data we processed.
    drupal_set_message(t('@count tables  processed.', array('@count' => count($results))));
  } else {
    // An error occurred.
    // $operations contains the operations that remained unprocessed.
    $error_operation = reset($operations);
    drupal_set_message(t('An error occurred while processing @operation with arguments : @args', array('@operation' => $error_operation[0], '@args' => print_r($error_operation[0], TRUE))));
  }
}

Feel free to contact us in-case of any concern/queries regarding the shared Blog or Drupal Development Services

Happy to help always :) 

May 25 2016
May 25

One of our clients wanted us to generate order PDF on order payment completion. The requirement included customized PDF which includes store logo, watermark and dynamic header and footer based on site language.

We used Commerce Billy PDF module to accomplish this task. Commerce Billy is a lightweight approach for a billing system for Drupal Commerce (Billy = Billing Light). 

Commerce Billy PDf is a part of Commerce Billy module.

This module adds a new order state called "invoiced". In this state, order gets its invoice PDF generated.

Here is how we did this.

Download and enable module.

Go to admin/commerce/config/billy-invoice and choose the settings that apply to your system.

Commerce Billy PDF to generate invoices

The settings include following:

1- Invoice number generation method

Commerce Billy PDF to generate invoices

This decides how invoice number should be generated. We have 3 options here:

  • Infinite (one single number, that is never reset, and incremented at each invoice generation): This means that invoice number will be a single number and will not be changed ever. Invoice id increases with each invoice.
  • Reset every year, with an id incremented at each invoice generation: This is a default option selected. Invoice numbers reset every year.
  • Reset every month, with an id incremented at each invoice generation: Invoice numbers reset every month.

2 - Pattern

Commerce Billy PDF to generate invoices

This is the pattern for invoice number. This pattern MUST include {id} which is invoice number id. Pattern can be anything depending upon how you selected invoice generation method.

3 - Current invoice id

Commerce Billy PDF to generate invoices

The number from which invoicing should start. This needs to be higher than the previous number set.

4 - Automatically invoice orders on checkout completion.

Commerce Billy PDF to generate invoices

Checking this will automatically move the order into "invoiced" state upon order completion and invoice PDF is generated. Make sure to clear cache once you change these settings.

One of our requirements was to generate invoice PDF only when the order payment is completed. Rules come handy in such scenarios.

We also needed to customise the invoice PDF that is being generated. Commerce billy PDF has a tpl file called "commerce_order--commerce_order--pdf.tpl.php". You can copy this file to your theme and make required customisation.

The module works out of the box and PDFs are generated without any problem. PDF can include any discounts that an order might have.

Drupal Development Services is the best choice!! The resulting PDF looks something like this:

Commerce Billy PDF to generate invoices
May 03 2016
May 03

WYSIWYG allows you to create content in whichever way you like, as the name suggests “What You See Is What You Get” it’s fairly simple for developers to work around WYSIWYG, you can create different layouts. In general, WYSIWYG implies the ability to directly manipulate the layout of a document without having to type or remember names of layout commands. The actual meaning depends on the user's perspective.

Although if you attempt to edit the content without any fair knowledge, you might break the div structure and it may not appear as you would have wanted it to.

For the same reason, I have created a custom content builder, which lets you edit the content without breaking the div structure with the help of different classes. It means, classes let you edit and create content with ease. I have built this with the help of “Paragraphs”, paragraphs allow site builders to make things cleaner so that they can give more editing power to your end-users. You can also add custom option fields and do conditional coding in your CSS, JS and preprocess functions so that end-users can have more control over the look and feel of each item. This is way much cleaner and stable than adding inline

CSS or classes inside the body field's source.

Machine name: content_textarea_wysiwyg

For this example we are going to allow editors to add extra WYSIWYG, photo galleries.

Steps to create a content builder:

1. Install & enable paragraphs module, we use paragraphs to create each element e.g. WYSIWYG.

Content Builder Using Paragraphs

 2. Create a new taxonomy term which will be used to create grid columns (by using classes).

  • Goto Admin Menu -> Structure -> Taxonomy Add Vocabulary (/admin/structure/taxonomy/add) to create a new Vocabulary called "Grid Columns" with the machine name "content_grid_columns"
Content Builder Using Paragraphs
  • Add new field called "Class"

   Machine name: field_single_textfield

   Field Type: text

   Widget Type: text field

Content Builder Using Paragraphs
  • While adding taxonomy, make sure to add classes correctly. For bootstrap theme use following classes: e.g. col-md-6, col-md-4

3. Create a paragraph bundles:

i. As per our example, to create the textarea (WYSIWYG):

  • Goto Admin Menu -> Structure -> Paragraph Bundles -> Add Paragraph Bundle (/admin/structure/paragraphs/add)
  • Create new bundle:

   Name: Textarea (WYSIWYG)

   Machine name: content_textarea_wysiwyg

Content Builder Using Paragraphs
  • On the manage fields screen create a new field with the following properties:

            Label: Body

            Machine name: field_body

            Field Type: Long text and summary

            Widget: Text area with a summary

Content Builder Using Paragraphs

ii. As per our example, to create the Gallery:

  • Create a new paragraph bundle

            Name: Gallery

            Machine name: content_gallery

Content Builder Using Paragraphs
  • On manage fields, create a new field with the following properties. This will be title of gallery:

            Label: Title

            Machine name: field_title

            Field Type: Text

            Widget: Text field

  • On manage fields, create another field with the following properties. This will be images for gallery:

            Label: Gallery

            Machine name: field_gallery

            Field Type: Image

            Widget: Image Allow "Number of values" to be "Unlimited" on image field.

Content Builder Using Paragraphs Content Builder Using Paragraphs

iii. Create a main paragraph item which is used to pull out all paragraph bundles created.

  • Create new bundle:

            Name: Column

            Machine name: content_grid_column

Content Builder Using Paragraphs
  • Create a new field. This field should join to our newly created taxonomy term: "Grid Columns"

            Label: Column Width

            Machine name: field_paragraph_column_class

            Type: Term reference

            Widget: Select list

  • Third field is used to pull in our different paragraph bundles. Select the available paragraph items for the page builder (Textarea (WYSIWYG), Gallery).

            Label: Element

            Machine name: field_paragraph_element_referenc

            Type: Paragraphs

            Widget: Embedded

Content Builder Using Paragraphs Content Builder Using Paragraphs

4. Install & enable Entity Construction Kit module with its dependencies - Entity API & Chaos tools.

Content Builder Using Paragraphs Content Builder Using Paragraphs Content Builder Using Paragraphs

5. Goto Admin Menu -> Structure -> Entity Type -> Add Entity Type (/admin/structure/entity-type/add) to create a new entity called "Section", with the bundle name "Section". Machine name is kept as "content_builder_section" for entity type. This will create a section.

Content Builder Using Paragraphs

6. Add existing field called field_paragraph_element_referenc to "Section" bundle with label "Column". In the field settings, select the Column paragraph & save. Allow "Number of values" to be "Unlimited".

Content Builder Using Paragraphs Content Builder Using Paragraphs Content Builder Using Paragraphs

7. Install & enable Entity reference & Inline Entity Form modules.

Content Builder Using Paragraphs

8. We can now add our entity Section to all the content types required.

  • Goto the content types & create a new field:

            Label: Grid Section

            Machine name: field_grid_section

            Type: Entity Reference

            Widget: Inline entity form - Multiple values

Content Builder Using Paragraphs

For the target type select our create entity: Section & the target bundle: Section.

Content Builder Using Paragraphs

For the field settings select: Allow users to add new Section.

Content Builder Using Paragraphs
  •  Add the existing field "Grid Section" for other content types.

9. Now that we have setup our entities & paragraph bundles, we need to do some preprocess work to make sure our grids actually work. To do this we alter the grids by using the column classes field we setup (Class (field_single_textfield))

  • Create a new module named "content_page_builder"
  • In our .module file: content_page_builder.module add a new include:
<?php
  /**
   * @file
   * Code for the Content Page Builder feature.
   */

  include_once 'inc/content_page_builder.preprocess.inc';
?>

Create a new directory called inc create a new file content_page_builder.preprocess.inc, with following code

<?php
  /**
   * @file
   * content_page_builder.preprocess.inc
   */

  /**
   * Implements template_preprocess_entity().
   * @param $vars
   */
  function content_page_builder_preprocess_entity(&$vars) {
    /*
     * Add grid class to each paragraph item.
     */
    if ($vars['entity_type'] === 'paragraphs_item' && $vars['paragraphs_item']->bundle == 'content_grid_column') {
      /*
       * Loop through available classes and set the content width accordingly.
       */
      $class_tid = !empty($vars['paragraphs_item']->field_paragraph_column_class['und']) ? $vars['paragraphs_item']->field_paragraph_column_class['und'][0]['tid'] : 'full';
      if (is_numeric($class_tid)) {
        $term = taxonomy_term_load($class_tid);
        $class = $term->field_single_textfield['und'][0]['value'];
      } else {
        $class = 'twelve columns';
      }

      /* $additional_class_tid = !empty($vars['paragraphs_item']->field_paragraph_additional_class['und']) ? $vars['paragraphs_item']->field_paragraph_additional_class['und'][0]['tid'] : 'none';
      if (is_numeric($additional_class_tid)) {
        $term = taxonomy_term_load($additional_class_tid);
        $additional_class = $term->field_single_textfield['und'][0]['value'];
      }*/

      $vars['classes_array'][] = $class;
      /*if (isset($additional_class)) {
        $vars['classes_array'][] = $additional_class;
      }*/
    }

    // Unset paragraphs title
    if ($vars['entity_type'] === 'content_builder_section') {
      $vars['title'] = NULL;
      $vars['classes_array'][] = 'row';
    }

    // Remove user display name from paragraph items.
    if (!empty($vars['content']['user_display_name'])) {
      unset($vars['content']['user_display_name']);
    }
  }
?>

 

Final Field view on content type

Content Builder Using Paragraphs Content Builder Using Paragraphs Content Builder Using Paragraphs Content Builder Using Paragraphs

 Final Result

Content Builder Using Paragraphs
Apr 07 2016
Apr 07

The Acquia Certified Drupal Site Builder is a credential intended for professionals who build Drupal sites using core and contributed modules. This exam is designed to validate skills and knowledge of a Drupal Site Builder. This focuses on knowledge of Drupal 7 and topic areas including Drupal features, Content and User Management, Content Modeling, Site Display, Community and Contributed Projects, Module and Theme Management, Security and Performance. It provides the means to validate skills and knowledge of a Drupal Developer. Acquia certificate programs are among the most desired skills to have and have earned respect from the developer community and IT professionals.

Dries has mentioned in one his blog posts that,

“A good certification is not just a rubber stamp, but a way for people to evaluate their own abilities, and make plans for improving their knowledge. In some countries, certification is really important to create a career path (something I learned when visiting India)”

I recently appeared for the exam of “Acquia Certified Drupal Site Builder”. Exams are always exciting. The learning, the application and the feeling of finally having completed it successfully is extremely satisfying. I would like to take this opportunity to share my experience with you. I came to know about this certification program from here and through a blog post by Angela “Webchick” Byron, who I follow closely.

About the Exam

There’s as such no preparation required for this exam, if you are a good developer with an experience of about a year or so, you can successfully complete and pass the exam. Exam mandates to score at least 68% to be able to pass and receive badge & certificate. It comprises of 50 questions spread across 7 different sections and duration is of 75 minutes. Questions are of two types, either select 1 correct answer from the 4 options or select all correct answers from the 4 options. They are based on real-life scenarios which test your problem-solving skills and not what you memorize via reading.

To take the exam you are either required to install a software on your machine called "Sentinel" or you can appear at one of the centers which have partnered with Acquia to conduct tests. Registration for the test is quite simple, to register please visit here.

Why is it important?

Many agencies, consultants and Drupal shops use the Acquia Certification Program in their hiring processes, as well as in team development and building. It gives a path to skills and knowledge growth for individuals, and at the same time identifies the gaps that need filling, both for a project team and for an individual.

It gives you visibility & credibility and allows you to carve a niche for yourself in Drupal community and partners. Besides this, you get a shiny badge and a certificate (That never hurts!)

Some last minute tips

  • Don’t rush into scheduling the exam, select a date which is most preferable to you.
  • Go through Drupal core and most popular contributed modules.
  • Keep it as simple as possible, don’t overthink while answering.
  • Read questions carefully.
  • If you are using test centers, make sure you reach there in time and carry required identity proofs with you.

I am thankful to my teammates who have always been very encouraging and motivating, the constant support has been very helpful to me. I would also like to express my gratitude towards my mentor, Saurabh Dhariwal without his guidance this wouldn’t have been possible.

Mar 15 2016
Mar 15

Audio conversion with Media recorder and audio converter using FFMPEG

We have faced a problem with browser's version support while delivering our one of the esteem project "Lingora" - we worked recently and thought to share with you people to save your precious time with Audio conversation functionality. Digging out for its solution really hacks our minds sometimes.

But now relax!! Here's the quick solution we imposed to save your time and guess what!! That works very well.

We used media recorder module to record the audios from a browser. Some of the important things to be considered while using this module:

  • Different browsers record audios with different formats.
  • iOS doesn't allow recording audios.
  • Chrome requires to enable extension and needs https.

Different browsers record audios with different formats:

Different browsers record audios in a different format. For example, Firefox on Ubuntu saves a file as .ogg. , chrome makes .wav files. These formats are not cross-browser compatible and not all audio formats can be played on all browsers.

To solve different formats issue, we used an audio-converter module. This module converts uploaded audios to .mp3. This module works well with an audio field. Since we are using media recorder, we used a patch from here to make it work for the audio recorder.

Along with this we also added a patch to make it work in file field as well. https://www.drupal.org/node/2687469

We still had an issue in firefox that it did not play .mp3 files. So we converted .mp3 files into .ogg and kept two files for one content.

We used FFMPEG in custom code to make conversion using following code:

exec('ffmpeg -i '.$source_file.' -acodec libvorbis '.$new_file_name.'.ogg');

iOS doesn’t allow to record audios

iOS doesn't support audio recording. Instead, we are given an option to upload a file. In file upload also, we can either take a photo or video or we can upload one from the gallery.

iOS doesn't allow to upload audio files. You can only access the gallery. The workaround here is to record a video from iOS devices and then convert it to an audio file using audio converter.

The audio converter works here as well and converts the recorded/uploaded video in mp3 file.

Chrome requires to enable extension and needs https

According to this, chrome 47 onwards to use media recorder you need to enable one flag and the site needs to be on https.

Hope this solution helped you, use this solution Blindly! Leave your valuable feedbacks, would get back to the easiest solution for complex tasks.

Jan 30 2016
Jan 30

Okay, so let’s face it. 15 years of the new millennium are already down. Ever-since, the new millennium started, the way Business is defined and steered has had new dimensions added to it; and so has been the advancement of newer thought process. Isolation is no more the thumb-rule to monopoly and collectiveness or outsourcing is the new course of action. Many have embraced this concept of lending some part of our work to be done elsewhere, in some distant part of another region or country while some are still aloof to it.

Whether one vouches for Outsourcing or doesn’t give it a thumbs-up, one cannot ignore it. Why? certainly because this practice of today was once the way of trade and commerce among the popular world civilizations. So what made even our ancestors a part of “Some by me, some by you” chain and eventually the business strategists giving it the current shape?

An African proverb, very aptly catches the essence of outsourcing - “If you want to go fast, go alone. But if you want to go far, go together”. Every business today has embraced outsourcing in one way or the other, so much so that the crucial question becomes “Why not outsourcing” instead of “Why outsourcing”?

As we progress, we take a look at few of the essential factors of this outsourcing concept!

Outsourcing in a nutshell

Superficially, the most common answer to “Why outsourcing” is “It helps in cost cutting”. Well, to an extent it still remains the major factor why companies consider it. Apparently, there are other vital factors too, which make outsourcing lucrative.

Outsourcing brings with it:

  • Accomplished Task Force
  • Substantial reduction in the operating cost -Flexi staffing
  • Enhanced levels of work efficiency
  • TAT reduction

Summation of all these factors, does lead to enhanced levels of profit and gains.

So before you stamp A Yes for an outsourced solution, ponder on these 5 fundamental questions that one should always consider in order to leverage the benefit of outsourcing.

  1. Does my organization NEED to outsource?
  2. What GAINS shall be reaped?
  3. WHAT has to be outsourced?
  4. What is the RIGHT TIME to outsource?
  5. WHOM should my organization outsource?

Stay tuned for the next blog of this series...

Nov 10 2015
Nov 10

Manatee swaths of Drupal development!!

Growing up and getting off the islands. Spark everywhere - A broader PHP engineering talent pool!!

The idea Drupal 8

The idea was to “re-scope the initiative so that it was more manageable and less daunting” says Dries Buytaert, Founder of Drupal.

How it came into existence

Continuous success of Drupal platform pushed contributors towards D8, here’s the digits and number Drupal has:

  • 1127761+ live sites
  • 1,168,980 people in 229 countries speaking 180 languages 
  • 37,930 of them are developers 
  • 30,276 modules, 2,128 themes, 930 distributions
  • 2,782 contributors to Drupal 8 so far 
  • Hundreds of Drupal books and thousands of documentation pages

The digits are still crawling high and rapidly increasing…

Cutting Edge of Drupal 8

A Killer Drupal 8 release: Drupal initiates below features:

  1. New Configuration Management
  2. Mobile in its DNA
  3. Effortless Authoring Experience
  4. Multilingual Capabilities

And many other features, you can seek more details from here.

Efforts put in D8. Thanks for tireless efforts by the contributors

Whom to congratulate?!! It’s an indefatigable efforts by Team,an incredible number of combined efforts of more than 3200 contributors on the planet.

Proof of Drupal - a legitimately global community.

How it'll change the game

Technology keeps advancing and so does Drupal!! Evolution of Drupal – Giving an Enterprise level solution.It’s a major milestone in Drupal or Open Source World and sustainability in your choice of Drupal 8 is an important factor which leads “Future-proofed” one.

D8 Release Party Ahmedabad

A dream taking shape..! Coordinating promotional for the official Drupal 8 launch party.AddWeb Solution planning to have a bash in regards to #celebr8d8 release and here are the details: 

  • Tea/coffee
  • A casual session if anybody want to share knowledge or experience on Drupal8
  • Cake and snacks
  • General discussion on how we can collaborate more to contribute

Check out more planning here and drop your views.

We would be more than happy to have all fellow Drupalists on 21st November, 2015 for Drupal bash at our premises

405, Silicon Tower, 
Above Freezland Restaurant, 
Law Garden, Off C.G. road, 
Ahmedabad-380009, India.

Ref: http://www.slideshare.net/webchickenator/evolution-of-drupal-and-the-drupal-community

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