Upgrade Your Drupal Skills

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

See Advanced Courses NAH, I know Enough
Jul 16 2021
Jul 16

Since Drupal 8 there Tour module is in core. It is very useful when you need to guide user within your application. We use it In EK management tools targeted at users with small to medium scale companies. They usually have limited resources and time to spend on back office trainings. This is where the Tour module is very convenient to introduce functionalities to users who can quickly grasp the functions available to manage their back office.

You can try the latest version of Tour with the demo application where many of the forms and pages have a tour guide for users.

Jul 05 2021
Jul 05

Ek_jitsi 2.0.0-rc1 release

EK_jitsi is an integration of Jitsi video conferencing solution.

By installing this module you can:

  • create block to access Jitsi videos from your site

  • join existing room based on room name

  • create room with random name in 2 clicks

  • insert Jitsi video field into content pages

The latest beta release includes the video content field. It can be added to content like basic page or article.Embed field start a video room automatically based on the page title or alias as jitsi video url. Multiple settings are available to set custom jitsi server, script or display options.

If combined with a timestamp field, you can set a video start date and the page will show a countdown before start. This feature can be used for instance to send invitation in advance to join a conference at a certain time.

timestamp video

Documentation is available in Drupal and an installed demo is available here (You may access 2 content pages that demonstrated the embed field, one with a timestamp feature that launches video on a future date and one with active video field).

Jun 01 2021
Jun 01

Ek_jitsi 8.x-1.0-beta5 release

EK_jitsi is an integration of Jitsi video conferencing solution.

By installing this module you can:

  • create block to access Jitsi videos from your site

  • join existing room based on room name

  • create room with random name in 2 clicks

  • insert Jitsi video field into content pages


The latest beta release includes the video content field. I can be added to content like basic page or article.Embed field start a video room automatically based on the page title or alias as jitsi video url

If combined with a timestamp field, you can set a video start date and the page will show a countdown before start. This feature can be used for instance to send invitation in advance to join a conference at a certain time.

timestamp video

Documentation is available in Drupal and an installed demo is available here.

Aug 12 2020
Aug 12

In previous article, we have built a simple custom view from sales data. In this article we will show how to create a relationship with another table, add access control and a menu link. You can also watch the video with complete built.


Table relationship

The purchases are linked to a supplier ID. This ID is a reference in the Address book table. To create the relationship with address book, click on "Add" under "ADVANCED":

relationship add

In the tables list, select the "Client" linked to sales:

select table

After the table is linked, you can add new fields from Address book. We will need the Address book ID and the Name (of supplier).

You can click on "Add" in fields section:

add fields

In the field list, Select the ID and Name and configure the data columns. We only display the Name and hide the ID:

configure fields

Like for the link to document, we can add a link to the supplier data. Open the "Name" field for editing by clicking on "(Address book link to purchase) ek address book: Name (Supplier) ". In the configuration, under "REWRITE RESULTS", select "Output this field as a custom link" and enter the path to the address book "/address_book/{{ id_1 }}" where {{ id_1 }} is the variable for ABID that we selected previously.

configure link

We now have a table with supplier name linked to Address book:



Access to views can be controlled like any other page in Drupal. you can restrict access by role or permission.

to do that, go to page settings and click "Unrestricted" to edit access:

page settings

In the form, select the type of access control you want, role or permission. In our case with select a "Permission" which is attached to the sales module to list purchases.




The last step is to create a menu for easy navigation to the data view. In the page settings section, click "No menu" to open the menu form:

page settings menu

We create a normal menu item to be listed under parent sales menu:

menu select

The custom view is now ready and can be saved. Users with correct access permission will be able to open, filter and read data on the new page.

You can watch our video for building this custom view or test the full example result with our online demo

build custom view part 2

Aug 03 2020
Aug 03

Views is an integrated function from Drupal which allows to build content views from available data. Content views are any filtered data display in a form of table, list or grid.

In this article, the sample view we are building is made of data managed by EK modules for business back office. But it may apply to any data from a Drupal module.

Views are very useful to extract custom data for analysis or compilation. You can add filters and access control to share pages and content.

You can also test the full example below result on our online demo


A new custom purchase list view

(check out complete video here as well)

To access view building form you must have permission Administer views. Check with administrator if you need to update permissions in your application.

To access the Views User interface, navigate to : /admin/structure/views

On this page, click the button Add view to start a new content view:

add view

In the subsequent form, fill-up the information needed to create the view: name and description, page title and path (path is the url to view display and can be inserted in the application menu) and display settings as in the example below:


add view form


After clicking "Save and edit" we are redirected to the content edit form.


View content building

The first step is to select the fields from content database that will be displayed in the table. For that selection we click on "Add" in fields section:


add fields

In the field list, we select following fields:

  • Serial number
  • Document date
  • Local currency
  • Total value in local currency

And format each field column name.

fields selected


The view content can be previewed in the lower part of the form:


preview 1

Add link to field

To improve view display, we are going to add a hyperlink to the serial number to redirect user to a display of the document with full information.

First we click on ek sales Purchase unique id (ID) to edit its configuration: hide from display and remove comma separated formatting:

config ID


Then we click on the field ek sales: Serial number (Ref.) to edit its configuration. In the "REWRITE RESULTS" section, we select "Output this field as a custom link" and enter the path to the document with ID:


config link


{{ id }} indicates a variable element which is equal to the value of the row ID. If you do not indicate the proper variable, the link will not work.

Now the view display shows Ref. column with hyperlink to the document and the ID column is hidden.


preview 2

Add a filter by date

To help user select data from view, the next step shows how to include a filter. The filter will select purchase rows based on date field.

First, click "Add" in the filter section:

button 2

Select the date field:

select date field

And configure with following settings:


config filter

A new box is included in the view to filter content by date:


The View "Custom Purchases" is now completed and it can be accessed by users via default path /custom-purchases indicated in the view form (you can edit this path to any valid path you want to set) :


user view


In the next article, we will look at table relationship to link data with other tables and other display enhancements.

You can also watch our video for building a custom view:



build custom view part 1

Jul 18 2020
Jul 18

If you use swiftmailer + simplenews, you may want to send html email with custom css.

To achieve that,you have to customise 2 twig templates.

The first template is the default template that is provided by swiftmailer: simplenews-newsletter-body.html.twig.

You can use this template to build your own email body with content you like to use. For example, in the template below, an hero image is inserted using table layout.

             {{ build }}
             {% if not opt_out_hidden %}
              {% if format == 'html' %}
              {% else %}
              -- {{ unsubscribe_text }} : [simplenews-subscriber:unsubscribe-url]
              {% endif %}
            {% endif %}
{% if key == 'test' %} - - - {{ test_message }} - - - {% endif %}

You can rename the file following the naming convention for twig template and place it into you theme templates folder:

 * Copy this file in your theme directory to create a custom themed body.
 * Rename it to override it. Available templates:
 *   simplenews-newsletter-body--[newsletter_id].html.twig
 *   simplenews-newsletter-body--[view mode].html.twig
 *   simplenews-newsletter-body--[newsletter_id]--[view mode].html.twig

Once you have designed you email body you need to design the swiftmailer template with the required css style. to do that, create a file called swiftmailer--simplenews.html.twig into the same template folder as the previous file. You can build your html file with inserted css style following the sample structure below.


/* content formated by simplenews-newsletter-body */ {{ body }}

With this technique you can design any type of email newsletter with rich content that adapt to desktop and mobile displays.

example template
Jun 14 2020
Jun 14

Jitsi video conference

Best video conferences are built on Jitsi. Jitsi is a set of open-source projects that allows you to easily build and deploy secure video conferencing solutions. At the heart of Jitsi are Jitsi Videobridge and Jitsi Meet, which let you have conferences on the internet, while other projects in the community enable other features such as audio, dial-in, recording, and simulcasting.

We have integrated Jitsi service with Drupal to access video conference directly from a Drupal site. You can create random or custom conference rooms or join existing conference. By default, the Jitsi meet server is used to establish connections, but you can specify your own server in settings.

Please check and try the module project which is in development version alpha3.

Some improvement have been made with layout, option to chose layout type, window sise and a side menu for some jitsi settings.

Jitsi window
Jun 14 2020
Jun 14

In previous article we have seen how we create a very simple theme with 1 main content region as page layout.

theme admin

Lets look now at the custom module to handle theme switch and content display.

Part 2: the custom module

The module called "land_page" structure is as follow:


Lets look at the most important parts specific to the land page.

land_page.info.yml : this is the standard module info settings.

land_page.rounting.yml : in this file we will define our land page routes. Those route will be the reference to switch theme. I.e.

path: '/land-page _controller: '\Drupal\land_page\Controller\Controller::defaultLandPage'
_access: 'TRUE'

The next important part is the class that manage the theme switching in ThemeNegotiator.php (see Drupal):

negotiateRoute($route_match) ? true : false;
     * @param RouteMatchInterface $route_match
     * @return null|string
    public function determineActiveTheme(RouteMatchInterface $route_match)
        return $this->negotiateRoute($route_match) ?: null;
     * Function that does all of the work in selecting a theme
     * @param RouteMatchInterface $route_match
     * @return bool|string
    private function negotiateRoute(RouteMatchInterface $route_match)
        if ($route_match->getRouteName() == 'default_land_page')
            return 'ek';
        return false;

When the visitor navigate on the website, the theme negotiator will check if the route = land page route and sitch to the appropriate theme.

In order to achieve the above you need to declare a service.

land_page.services.yml :

        class: Drupal\land_page\Theme\ThemeNegotiator
          - { name: theme_negotiator, priority: 1000 }

In the Controller.php we will create the function that is called in the land_page.routing.yml (defaultLandPage()):

 * Default land page
 * @return array
 public function defaultLandPage() {   
    $items = [];
    $items['asset'] = drupal_get_path('module', 'land_page') . "/assets/";   
    return array(
        '#theme' => 'land_page',
        '#items' => $items,
        '#title' => '',
        '#attached' => array(
            'library' => array('land_page/land_page'),

In this function we define which template to use, we pass some $items for content and attach our library.

The them template is defined in land_page.module :

 * Implementation hook_theme().
function land_page_theme() {
  return array(
    // default
    'land_page' => array
      'template' => 'land_page',
      'variables' => array('items' => array(), 'data' => array()),

The library is defined in land_page.libraries.yml . Library will be very important as it will define all the custom css, js or external resources needed to render the page. A simple example of library  that include custom css, js and fonts will be:

  version: 1
      //fonts.googleapis.com/css?family=Barlow:400,500,600&display=swap: { type: external }
      css/land_page.css: {}
    js/js.min.js: {}
    js/land_page.js: {}

In the hook_theme(), the template called is "land_page" which is a twig template under template folder: land_page,html.twig. In this template you will build your html content to render the actual land page. This is where your creativity will start.

On big advantage of twig templates is that you can insert content from other Drupal source like webform or existing blocks directly into the land page.

Now you can install your module, navigate to your /land-page url and access to your land page content.

Jun 14 2020
Jun 14

Landing pages are great for product presentation and customer engagement.

There are a must for today marketing campaigns, mobile advertising and sales development.

landing pages

There is no easy way to build a simple landing page in Drupal.

You can use custom themes or modules to manage layout like parade but it is not that simple. Layout options are limited. For instance. The module does the job; you can build a simple landing page without custom development, but requires a lot of dependencies for a simple page and you may still have to do some css editing.

In this article we will explain how our landing page has been constructed within Drupal 8 website using separate dedicated theme and a custom module with twig templates.

The original page which is a stand alone 1 page theme is now fully integrated in the website.

It may not be the best method, but it can be easily replicated and give more room for creativity and extra flexibility compared to a layout module or a full land page theming construction.

Part 1: the custom theme

To achieve that, we created a custom theme with only 1 region for content. When building a 1 page theme, you usually do not want side columns or extra headers and footers.

To create your theme, you only need 1 file to be saved under your custom theme folder in the Drupal 8 "themes" folder: myTheme.info.yml.

type: theme
base theme: false name: 'EK'
description: 'Built to use land page'
version: VERSION
core: '8.x' regions:
  content: Content
  footer: Footer

This is what is needed to create the basic theme that will be used in our landing page. We keep a region "footer" to insert hidden blocks or content.

This theme will be called on specific routes names and will replace the default theme of the site.

You can add a screenshot image also in the theme folder if you want to enhance your admin view.

theme admin

In the next step we will explain how our custom module switch theme for dedicated url and build the landing page with twig template. For that step you will need some knowledge on creating a simple module, insert libraries and make a twig template.

Jan 03 2019
Jan 03


EK application has a module that store personal documents for user. When user account is deleted, those documents may be transferred to another account.

To achieve that, we need to alter the user account cancel form when building the form, validating and submitting it.

Let's review the 3 steps.


The form before altering it looks like this

cancel user account before hook

We need to add a field to select another user account to which the document of the canceled account will be moved to.

To achieve that we Implements hook_form_alter() in MyModule.module:

function MyModule_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {    
  if ($form_id == 'user_multiple_cancel_confirm') {
        $form['move_uid_documents'] = [
      '#type' => 'textfield',
      '#title' => t('Move uer documents'),
      '#autocomplete_route_name' => 'MyModule.user_autocomplete',
      '#description' => t('Select to whom to transfer personal documents'),
    $form['#validate'][] = 'MyModule_form_user_delete_validate';
    $form['#submit'][] = 'MyModule_form_user_delete_submit';
    return $form;

What we can notice here is:

  • We alter selected form defined by form ID. In this case : "user_multiple_cancel_confirm";
  • We create the required field by returning $form['move_uid_documents'] ;
  • We add 2 new actions for validation, $form['#validate'][], and submit, $form['#submit'][],  for the next steps.

After altering the form will look like this:

cancel user account after hook

We have a new field to select user. In our case, we also have an autocomplete function that helps selecting existing user. However, we need to ensure that the value entered in the field is really an existing user. This is the part handled by the validation.



The validation is defined in MyModule_form_alter by adding validate callback named MyModule_form_user_delete_validate. Therefore, we need to create the function with thah particular name in MyModule.module.

function MyModule_form_user_delete_validate(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {   
  if ($form['#form_id'] == 'user_multiple_cancel_confirm') {
        if ($form_state->getValue('move_uid_documents') <> '') {
            $query = "SELECT uid FROM {users_field_data} WHERE name = :n";
            $data = db_query($query, [':n' => $form_state->getValue('move_uid_documents')])
            if ($data) {
                $form_state->setValue('move_uid_documents', $data);
            } else {
                $form_state->setErrorByName('move_uid_documents', t('Unknown user to move documents'));
     return $form;

Here the function will check against user_field_data table that the id is valid.

If not an error message will be displayed:

cancel user account validation error

However, if valid, we store the value to be used in the next step which is the submission.


As for validation, the submission is defined in MyModule_form_alter by adding validate callback named MyModule_form_user_delete_submit.

function MyModule_form_user_delete_submit(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
  if ($form['#form_id'] == 'user_multiple_cancel_confirm') {
        foreach($form_state->getValue('accounts') as $key => $id) {               
           ->fields(['uid' => $form_state->getValue('move_uid_documents'), 'folder' => t('Moved from user @u', ['@u' => $id])])
           ->condition('uid', $id)->execute();
     \Drupal::messenger()->addStatus(t('Documents moved to user @u', ['@u' => $form_state->getValue('move_uid_documents')]));
     return $form;

In the function above, we pick the id of each user account that is canceled and change to new user id in the document table.

The function also display a message to confirm actions: both cancellation and the submit hook have been executed.

cancel user submit alert

Please feel free to comment or suggest improvements.

Thank you.

Jul 25 2017
Jul 25

This is an example of anti-virus implementation with an Ubuntu server.

Our back office management solution allows users to upload files in various sections of the application for storage or file sharing. For this reason, checking of files for virus is an important advantage.

We use the ClamAV module integration from Drupal 8.

1) Install ClamAV on Ubuntu

Installation on Ubuntu server is straight forward.  However, it is better to install with clamav-daemon clamav-freshclam options for later settings

You can test with clamscan -r /home for instance

For further options you may refer to ClamAV website.

2) Install and set-up Drupal module

Module installation on Drupal 8 has no specific requirements.

As indicated on the module page, "Daemon mode" is preferred when executing the scan.

In the settings page (/admin/config/media/clamav), select Daemon mode (over Unix socket) in scan mechanism

You need to indicate the path for the socket pointing file; it can be found in the configuration file  : /etc/clamav/clamd.conf.

Input the file path into next setting:

3) Test

When uploading a file on the server via any upload interface, the file is scanned and validated. Scanning process is logged:

The Eicar test virus file is filtered when uploaded:

If you have implemented ClamAV with Drupal and have further comments, please feel free input your own.

Thank you.

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