We Launched a Blog on Web and Business!
High five! We did it! It is an important day in WEB4PRO history.
We had been working on it for one year, and finally, we launched our new blog!
Our Blog is a Place for Your Development
And we are happy to invite everybody for staying here and reading our new articles on web tools, business, marketing, and self-development. We are going to share our news with you here, represent our new products, and provide you only with the most useful things from the Internet world and business development.
Our team aims to make you interested and become your best helper in growing your business success. Here you’ll find the best practices on Drupal, Magento®, and WordPress development, get acquainted with the latest web design trends, learn more about modern technologies, and interesting approaches to running the business and marketing strategy.
Come round and stay with us forever! We promise you’ll not be bored 🙂
Welcome to WEB4PRO blog!
Drush and Drupal Console: Test-Drive
Drush and Drupal Console are the utilities with a command line interface. Both of them drastically speed up website administration and development. After the installation, you’ll have an ability to take the specific actions just entering the command into the terminal window, while the same actions could require several steps in a browser, for example.
Drupal Console works only with Drupal 8, and Drush works with Drupal 6, 7, 8.
We think you should know both of these tools if you will work with Drupal 8.
Comparing Drush and Drupal Console
What can we do using these tools? – Look through the list below.
With Drush you can:
- Download and install Drupal and contributed modules;
- Update Drupal and contributed modules;
- Run updates;
- Clean cash;
- Run CRON and Drupal on a light web server;
- Import, export, and merge configurations;
- Add users and set up the roles for them and access permission for roles;
- Make Drupal backup and recovery;
- Copy database and files to the remote server;
- Make a copy of twig templates.
With Drupal Console you can:
- Generate code for:
— Console commands;
— Content types;
— Controllers;
— Entities;
— Form alter hooks;
— Fields types, field widgets, and output widgets.;
— Images effects;
— Rest resources;
— Services;
— Themes; - Switch the website to the maintenance mode and back;
Run unit tests.
Hope that you are interested and ready to start working. We think that Linux and Mac OS users don’t need telling about terms. But as for Windows users, we’ll recommend them to use Git Bush instead of a standard console. You can download Git for Windows here.This article will be useful for those who has the skills of working with the console. So let’s open our console!
Working with Drush and Drupal Console
Drush and Drupal Console are very similar, but they are two different packages. Therefore they should be installed separately:
After the installation of packages, make sure that you did everything correctly. Enter Drush version and Drupal version in a console for this. Then you’ll see something like the information showed below. The numbers of versions could be different in your system.
<div class="half-left">
<p><strong>Drush</strong></p>
<p>$ drush version<br>Drush Version : 8.0-dev</p>
</div>
Drupal Console
$ drupal --version
Drupal Console version 0.9.1
Now let’s show the power of Drush in Drupal Console using them together. First of all, we’ll use Drush for Drupal 8 download, installation, and run via pm-download (dl) and quick-drupal (qd) commands.
$ drush dl drupal-8.0.x
Project drupal (8.0.x-dev) downloaded to /Users/ga/tmp/intro-drush/drupal-8.0.x-dev.
Project drupal contains:
- 1 profile: standard
- 13 themes: …
- 62 modules: …
$ cd drupal-8.0.x
$ drush qd --use-existing --uri=http://localhost:8383 --profile=standard
You are about to DROP all tables in your ‘…/drupal-8.0.x-dev.sqlite’ database. Do you want to continue? (y/n): y
Starting Drupal installation. This takes a while. Consider using the --notify global option.
Installation complete. User name: admin User password: …
Congratulations, you installed Drupal!
Caching 'uri' localhost:8383 in …/drush/drushrc.php
HTTP server listening on localhost, port 8383 (see http://localhost:8383/), serving site localhost:8383, logged in as admin...
PHP 5.6.12 Development Server started at Thu Sep 24 15:02:53 2015
Listening on http://localhost:8383
Document root is …/drupal-8.0.x-dev
Press Ctrl-C to quit.
After you have seen the message in the terminal shown above, it should be a little pause, and the browser will open the just installed Drupal website in few seconds. Quickly and simply!
Then we’ll use Drupal Console for creating the module for Drupal 8. Type the commands shown below in the terminal.
$ drupal generate:module
Welcome to the Drupal module generator
Enter the new module name: hello
Enter the module machine name [hello]: hello
Enter the module Path [/modules/custom]: /modules/custom
Enter module description [My Awesome Module]: Hello World
Enter package name [Other]: Other
Enter Drupal Core version [8.x]: 8.x
Do you want to add a composer.json file to your module [no]? yes
Would you like to add module dependencies [no]? no
Do you confirm generation [yes]? yes
Generated or updated files
Site path: …/drupal-8.0.x-dev
1 - modules/custom/bello/bello.info.yml
2 - modules/custom/bello/bello.module
Perhaps, you thought that you knew how to develop modules for Drupal 8, didn’t you? In any case, you have just created module via Drupal Console using only one single command. It’s cool, but the module lacks its own controller. And the module without the controller in Drupal 8 is just like a module without hook_menu() for Drupal 7. So, let’s go on and make our module do something. We are going to use the features of Drupal Console again:
$ drupal generate:controller
Welcome to the Drupal Controller generator
Enter the module name: hello
Enter the Controller class name [DefaultController]: HelloController
Controller title: Hello World
Enter the action method name [index]: greet
Enter the route path [hello/greet/{param_1}/{param_2}]: hello/greet/{world}
Controller title (empty to start with code generation):
Do you want to generate a unit test class [yes]? yes
Do you want to load services from the container [no]? no
Do you confirm generation [yes]? yes
Generated or updated files
Site path: …/drupal-8.0.x-dev
1 - modules/custom/hello/src/Controller/HelloController.php
2 - modules/custom/hello/hello.routing.yml
3 - modules/custom/hello/Tests/Controller/HelloControllerTest.php
[ ] Rebuilding routes, wait a moment please
[ ] Done rebuilding route(s).
Controller generation successfully completed. Let’s look what it generated for us:
$ tree modules/custom/
modules/custom/
??? hello
??? Tests
? ??? Controller
? ??? HelloControllerTest.php
??? composer.json
??? hello.info.yml
??? hello.module
??? hello.routing.yml
??? src
??? Controller
??? HelloController.php
Open HelloController.php file in your favorite editor and replace the things transmitted to a function t() with the data shown in the example below.
class HelloController extends ControllerBase {
/**
* Greet.
*
* @return string
* Return Hello string.
*/
public function greet($world) {
return [
'#type' => 'markup',
'#markup' => $this->t('Hello, @world!', array('@world' => $world))
];
}
}
Now it looks like the right Hello World module. Let’s turn it on using Drush.
$ drush en hello
The following extensions will be enabled: hello
Do you really want to continue? (y/n): y
hello was enabled successfully.
As you can see, it’s pretty simple to manage Drupal and generate code using Drush and Drupal Console. And these powerful tools can become your best friends while working on Drupal 8. If you need any help on Drupal development, we’ll do these works for you with great pleasure. Just fill “Ask Questions” form below, and we’ll contact you.
Arg Function: How to Replace It in Drupal 8
Arg function was excluded from Drupal 8. But you can replace it simply.
We have url /node/1
Arg Function in Drupal 7
On Drupal 7 we used it in such way:
echo arg(0); // node
echo arg(1); // 1
Arg Function in Drupal 8
In Drupal 8 we use OOP replacement of this function:
$route_match = Drupal::routeMatch()->getParameter('node');
Drupal::routeMatch()->getParameter('node')
Returns the processed value of the “node” route.
As the result, we have an object, from which we can get not only node id but also fields of a node. For example:
$node_id = $route_match->id(); // We’ve got node id (1)
$node_bundle = $route_match->bundle(); // We’ve got bundle nodes (article).
You may find the following example of function replacement in Drupal 8 on the Internet:
$path_args = explode('/', current_path());
echo $path_args[0]; // node
echo $path_args[1]; // 1
In such case of use, we can catch an error with empty or not existing elements.
That’s why, it’s better to use OOP variant of function replacement, which is suggested by the official drupal.org resource. That’s all. Simply and quickly 🙂 But if you have any difficulties, please, contact us and we’ll save your time and do everything related to Drupal development and customization for you.
Form_state in Drupal 7 and Drupal 8
Drupal 7 uses hook form(), form validate(), form submit() hooks for creation, validation, and submission of the form. $form_state array is used for getting values of the filled fields. And today we are going to tell the difference between form_state in Drupal 7 and Drupal 8.
Here is how it works in Drupal 7:
// Get the field value
echo $form_state['values']['field_id'];
// Get all values of the form
$values = $form_state['values'];
As Drupal 8 is completely based on OOP, the form is an entity here. So, forms are classes (OOP), which are created, implementing an interface:
DrupalCoreFormFormBuilderInterface.
There are several abstract form classes for different tasks in Drupal 8 core, which you should base on while subclassing:
- ConfigFormBase – for creating the forms with settings;
- ConfirmFormBase – a form for the confirmation;
- FormBase – the base class for all other form types.
All data, which the user entered while filling the form, will be in a $form_state variable. But $form_state is an entity in Drupal 8, so you should use the following code to get the necessary field value:
// field_id - name of the form element (a key).
echo $form_state->getValue('field_id');
Use the following method for getting all values:
// Get all form values
$values = $form_state->getValues();
That’s the way it works. We hope it’s helpful for you, but nevertheless, if you need any help, contact us, and we’ll do all necessary work on Drupal development for you.
How to Replace Theme() Function in Drupal 8
Have you already worked with Drupal 8? If yes, this post could be useful for you. Sooner or later almost any web developer faces the surprise: theme() function is not included in Drupal 8. And those who is going to try a new Drupal 8 platform should be ready for changes. Today we’ll cope with this and find another effective and useful way.
Let me introduce a bit of history.
Theme () Function
Drupal 7 includes the theme() function. It is responsible for wrapping fields and the other outputting data in some HTML. theme() is pretty useful in many cases: it can theme the output of standard tables, menus, fields, breadcrumbs, etc.
But what to do without it in Drupal 8? Now I’m going to share one useful way of how to act in this case and replace theme() in Drupal 8. Here we go!
Let’s Look at Drupal 7 Theme() Function
In Drupal 7, we can override all theme() functions at the level of your module.
The function could be overridden by adding another function with similar input parameters but the changed name (usually module name instead of theme_) into your module code.
A name of the theme (for example, ‘socbutt_buttons’) and an associative array of variables for transferring to hook_theme() are transferred into this function as parameters.
Here is how it works in Drupal 7:
echo theme('socbutt_buttons', array('layout' => SOCBUTT_LAYOUT_VERTICAL));
/**
* Implements hook_theme().
*/
function socbutt_theme() {
return array(
'socbutt_buttons' => array(
'variables' => array(
'layout' => SOCBUTT_LAYOUT_VERTICAL,
),
),
);
}
function theme_socbutt_buttons($variables) {
// Here is the code, which defines the look of things we want to display.
…
// The array for displaying.
return $output;
}
What About Drupal 8 Theme() Function?
The theme() function has been removed in Drupal 8. So you need to create a renderable array for theming required element. It is a data array which then is transmitted to the render() function.
Let’s do it in Drupal 8:
$items_array = array(
'#theme' => 'theme_name',
'#items' => $items;
);
echo Drupal::service('renderer')->render($items_array);
There you are! It’s an effective solution, which will help you to cope with your tasks without theme() and make the work with Drupal 8 more convenient.
Hope that this quick tip was useful for you and we’ll be glad to know your suggestions on this topic. Good luck to you! If you need any help, please contact us and we’ll do all these things on Drupal development.
WordPress Option Fields with Multiple Values
Many of you have already faced a question – “how to create a custom settings page in WP administration area?”, and Google is pleased to offer a dozen of good articles that clearly explain how to declare a list of custom fields on a separate (additional) page in admin area. But what if we need several different links. How to do this?
For example, these fields could be:
- with the “copyright” text;
- mailing address;
- phone number;
- text for you custom HTML-block;
- social links.
And just by the example of the last item in the list a solution for the question in this article – additional options page with social links, will be considered.
The number of links may be a dozen or more. And placing the options page all the ten fields at once (facebook, google , twitter, Instagram, etc.) will be at least not rationally – because not all these fields are necessary for our website. And what if we need two different links on Facebook or more? jQuery WordPress Settings API will be used as the solution. The implementation will consist of the following things: from a list of known social networks you can select the desired option with the social network name and add an instance for referring to the settings form. It will look something like this:

Within this article we’ll not go into details of working with WP Settings API, you can read the official documentation and examples here. We used the example 2 of this source to build a page with social network URL fields.
A class for our page with social network URL fields looks the following way:
<?php
/**
* WP Theme Options
*
* @link http://codex.wordpress.org/Shortcode_API
*
* @package WordPress
* @subpackage WP-Theme
*/
/**
* Class WPThemeSettingsPage
*/
class WPThemeSettingsPage {
/**
* Holds Social profiles. You can add more in __construct() function.
*
* @var array
*/
public $social = array();
/**
* Holds the values to be used in the fields callbacks
*
* @var $options
*/
private $options;
/**
* Start up
*/
public function __construct() {
$this->social = array(
'facebook' => __( 'Facebook' ),
'twitter' => __( 'Twitter' ),
'googleplus' => __( 'Google ' ),
'instagram' => __( 'Instagram' ),
'linkedin' => __( 'LinkedIn' ),
);
add_action( 'admin_menu', array( $this, 'add_plugin_page' ) );
add_action( 'admin_init', array( $this, 'page_init' ) );
}
/**
* Add options page
*/
public function add_plugin_page() {
// This page will be under "Settings".
add_theme_page(
__( 'Theme Options' ),
__( 'Theme Options' ),
'manage_options',
'theme_options',
array( $this, 'create_theme_options_page' )
);
}
/**
* Options page callback
*/
public function create_theme_options_page() {
// Set class property.
$this->options = array( 'wp_social_profiles' => get_option( 'wp_social_profiles' ) ); ?>
<div class="wrap">
<!-- <h2>My Settings</h2> -->
<form method="post" action="options.php">
<?php
// This prints out all hidden setting fields.
settings_fields( 'wp_options_group' );
do_settings_sections( 'theme_options' );
submit_button();
?>
</form>
</div>
<?php
}
/**
* Register and add settings
*/
public function page_init() {
register_setting(
'wp_options_group', /* Option group */
'wp_social_profiles', /* Option name */
array( $this, 'sanitize_profiles' ) /* Sanitize */
);
add_settings_section(
'setting_section_id', /* ID */
__( 'WP Theme Options' ), /* Title */
array( $this, 'print_section_info' ), /* Callback */
'theme_options' /* Page */
);
add_settings_field(
'wp_social_profiles', /* ID */
__( 'Social Profiles' ), /* Title */
array( $this, 'social_profile_callback' ), /* Callback */
'theme_options', /* Page */
'setting_section_id' /* Section */
);
}
/**
* /**
* Sanitize each setting field as needed.
*
* @param array $input Contains all settings fields as array keys.
* @return array
*/
public function sanitize_profiles( $input ) {
$new_input = array();
// Sanitize Social Profiles values.
foreach ( (array) $input as $name => $element ) {
foreach ( $element as $index => $value ) {
if ( ! empty( $value ) ) {
$new_input[ $name ][ $index ] = esc_url( $value );
}
}
}
return $new_input;
}
/**
* Print the Section text
*/
public function print_section_info() {
esc_html_e( 'Enter your settings below:' );
}
/**
* Get the settings option array and print one of its values
*/
public function social_profile_callback() {
if ( ! empty( $this->options['wp_social_profiles'] ) ) {
foreach ( (array) $this->options['wp_social_profiles'] as $name => $element ) {
foreach ( $element as $index => $value ) { ?>
<div class="wp-social-profile">
<label for="wp_social_profiles_<?php echo esc_attr( $name ); ?>_<?php echo esc_attr( $index ); ?>" class="wp-option-label">
<?php echo esc_html( $this->social[ $name ] ); ?>:
</label>
<input
type="text"
id="wp_social_profiles_<?php echo esc_attr( $name ); ?>_<?php echo esc_attr( $index ); ?>"
name="wp_social_profiles[<?php echo esc_attr( $name ); ?>][]"
class="<?php echo esc_attr( $name ); ?>"
value="<?php echo esc_attr( $value ); ?>"
placeholder="<?php esc_attr_e( 'http://' ); ?>"
/>
<button class="button wp-social-remove"><b>–</b></button>
</div>
<?php
}
}
} else { ?>
<div class="wp-social-profile">
<label for="wp_social_profiles_facebook_1" class="wp-option-label"><?php echo esc_html( $this->social['facebook'] ); ?>:</label>
<input
type="text"
id="wp_social_profiles_facebook_1"
name="wp_social_profiles[facebook][]"
class="facebook"
value=""
placeholder="<?php esc_attr_e( 'http://' ); ?>"
/>
<button class="button wp-social-remove">-</button>
</div>
<?php } ?>
<hr>
<div class="wp-social-profile-selector-wrapper">
<label for="social_profile_selector" class="wp-option-label"><?php esc_attr_e( 'Select profile: ' ); ?></label>
<select id="social_profile_selector">
<?php
foreach ( $this->social as $name => $option ) { ?>
<option <?php selected( $name, 'facebook' ); ?> value="<?php echo esc_attr( $name ); ?>"><?php echo esc_html( $option ); ?></option>
<?php } ?>
</select>
<button id="social_profile_add" class="button">Add new...</button>
</div>
<?php
}
}
if ( is_admin() ) {
$settings_page = new WPThemeSettingsPage();
}
function load_option_page_style() {
wp_register_script( 'wptheme-options-script', get_template_directory_uri() . '/inc/js/theme_options.js', array( 'jquery' ), '1.0.0', true );
wp_enqueue_script( 'wptheme-options-script' );
}
add_action( 'admin_enqueue_scripts', 'load_option_page_style' );
And JS, which allows us to add/ remove fields:
// Remap jQuery to $.
(function ($) {
// Re-index profiles.
function RefreshProfilesIndex(selector) {
$(document).find("[id^=social_profile_" selector "]").each(function (index) {
$(this).attr('id', 'social_profile_' selector '_' (index));
$(this).closest('div').find('label').attr('for', 'social_profile_' selector '_' (index));
});
}
// Capitalize first letter on string.
function toTitleCase(str) {
return str.replace(/wS*/g, function (txt) {
return txt.charAt(0).toUpperCase() txt.substr(1).toLowerCase();
});
}
// Update Events.
function RefreshEventListener() {
// Remove handler from existing elements
$("button.wp-social-remove").off();
// Re-add event handler for all matching elements
$("button.wp-social-remove").on("click", function (event) {
event.preventDefault();
var selected = $(event.target).parent('div').find('input').attr('class');
$(event.target).parents('div.wp-social-profile').css('visibility', 'hidden').slideUp("normal", function () {
$(this).remove();
RefreshProfilesIndex(selected);
});
});
}
// Select options toggle refresh.
function RefreshSelectOptions(target_id) {
if (target_id === undefined) {
var $target = $(document).find("select.select-toggle option");
} else {
var $target = $(document).find("#" target_id).closest("form").find("select.select-toggle option");
}
$target.on("mousedown", function () {
var $self = $(this);
if ($self.prop("selected"))
$self.prop("selected", false);
else
$self.prop("selected", true);
return false;
});
}
/* trigger when page is ready */
$(document).ready(function () {
RefreshEventListener();
$("#social_profile_add").on("click", function (event) {
event.preventDefault();
var selected = $("#social_profile_selector").val();
var count = parseInt($(document).find("[id^=wp_social_profiles_" selected "]").length);
var $clone = $(document).find(".wp-social-profile").first().clone();
$clone = $('<div>').addClass("wp-social-profile");
$clone.html(
'<label for="wp_social_profiles_' selected '_1" class="wp-option-label">' toTitleCase(selected) ':</label>'
'<input '
'type="text" id="wp_social_profiles_' selected '_1" '
'name="wp_social_profiles[' selected '][]" '
'class="' selected '" '
'value="" '
'placeholder="http://" />'
'<button class="button wp-social-remove"><b>-</b></button>');
$clone.insertBefore($(document).find(".wp-social-profile-selector-wrapper").prev()).hide().css({visibility: 'hidden'}).slideDown("normal", function () {
$(this).css('visibility', 'visible');
});
RefreshEventListener();
});
RefreshSelectOptions();
});
$(".widget-control-save").on("click", function (event) {
setTimeout(function () {
RefreshSelectOptions(event.target.id)
}, 500);
});
}(window.jQuery || window.$));
Let’s consider the code shown above.
As you can see, the fields are stored as an associative array where the links are grouped by social network title. To let the WP Settings API preserve values from input in the form as array, you should specify the attribute name using the following format:
<input name=wp_social_profiles[facebook][] value=”http://facebook.com/” />
<input name=wp_social_profiles[twitter][] value=”http://twitter.com/” />
<input name=wp_social_profiles[twitter][] value=”http://twitter.com/helloworld” />
<input name=wp_social_profiles[googleplus][] value=”http://googleplus.com/” />
<input name=wp_social_profiles[linkedin][] value=”http://linkedin.com/” />
The key point is the suffix [] in the attribute name. After submitting the form setting with fields in the format, as above, we get an associative array from function get_options( “wp_social_profiles”), such as in the picture above.
If we consider our settings page class more carefully, we declare a “social” array in the construct method:
$this->social = array(
'facebook' => __( 'Facebook' ),
'twitter' => __( 'Twitter' ),
'googleplus' => __( 'Google ' ),
'instagram' => __( 'Instagram' ),
'linkedin' => __( 'LinkedIn' ),
);
We’ll route the array with a frontage circle that’s why it could be added among the other social networks. The format is the following: the key of the array element is something like a slug of the social network, and the element value is a header field which we use to display the Preferences page:

JS-script shown in the example above implements the events: delete, and create a new DOM-element on the form, and recalculates the id and for attributes for label and input elements.
It was a quick tip, and we’ll be happy if it’s useful for you. If you have any difficultness with it, contact us and we’ll make it work. Save your time and trust us your WordPress based website!
How to Create Custom TWIG-Filter in Drupal 8
In Drupal 8 templating passed from commonly-used PHP Template to a new and progressive Twig templating engine long ago. Let’s learn how to create custom Twig-filters in Drupal 8.
In Twig templates, we can’t use traditional PHP-functions, and the syntax is also generally different. In Twig templates filters are used as a replacement for the functions. Filters are designated with a “pipe” symbol – “|”:
{{ data|filter() }}
or with additional arguments:
{{ data|filter(arg2, arg3) }}
Where “data” variable is the first argument of the filter, and the arguments in brackets are the second, the third, etc.
Twig Filters
Twig has a standard set of filters. You can familiarize yourself with them in the official documentation – http://twig.sensiolabs.org/doc/filters/index.html
But what if we need our own Twig-filter in Drupal 8, or in PHP language – a custom function? To do this, we need our custom module to declare the service – “twig_service” and prepare class which describes its functionality.
As an example, let’s set the task: create Twig-filter with currency conversion. Google Currency Converter API will be used for the conversion. We will send a request with required amount and currency. In response, we will get the result of the conversion according to the latest exchange rates.
Thus, we need to transfer three parameters to the filter:
- the sum (amount);
- original currency (curr_from);
- target currency (curr_to).
That’s how the filter call will look:
{{ amount|filter(curr_from, curr_to) }}
Let’s define what we need to prepare for the demonstration of twig-filter creation. First of all, we need to create a custom module. How to do this can be found here. I have prepared a module called twig_filter. The module defines route and page controller, also defines twig-pattern and transfers the required for the filter parameters to the template.
Module file list

The module’s declaration file twig_filter.info.yml:
name: Twig Filter Test
type: module
description: 'Twig Filter Demonstration'
package: customs
version: '8.x'
core: '8.x'
project: 'twig_filter'
Route declaration file twig_filter.routing.yml:
twig_filter.result:
path: '/twig-filter/{curr_from}/{curr_to}/{amount}'
defaults:
_controller: 'Drupaltwig_filterControllerTwigFilterController::testTwigPage'
_title: 'Twig filter Page'
requirements:
_permission: 'access content'
Controller Class file TwigFilterController.php:
<?php
/**
* @file
* Contains Drupaltwig_filterControllerTwigFilterController.
*/
namespace Drupaltwig_filterController;
use DrupalCoreControllerControllerBase;
/**
* Class TwigFilterController.
*
* @package Drupaltwig_filterController
*/
class TwigFilterController extends ControllerBase {
/**
* {@inheritdoc}
*/
public function testTwigPage($curr_from, $curr_to, $amount) {
// Construct element and it's data.
$element = array(
'#theme' => 'twig_filter_test_page',
'#curr_from' => $curr_from,
'#curr_to' => $curr_to,
'#amount' => $amount,
);
return $element;
}
}
Twig-template file twig-filter-test-page.html.twig
(Explanation of the filter use):
<div>
<h4>{% trans %}Google Currency API response results: {% endtrans %}</h4>
{{ amount|currency(curr_from, curr_to) }}
</div>
Module functions File twig filter.module:
<?php
/**
* Implements hook_theme().
*/
function twig_filter_theme($existing, $type, $theme, $path) {
return [
'twig_filter_test_page' => [
'render element' => 'twig_filter_test_page',
'path' => $path . '/templates',
'template' => 'twig-filter-test-page',
'variables' => [
'curr_from' => NULL,
'curr_to' => NULL,
'amount' => NULL,
],
],
];
}
Next, when we already have a module that defines a new route and page controller, we need to realize the following tasks:
- declare twig_service module service;
- create a class, which will describe the implementation of the functional Twig-filter.
To declare a service in the module, you need to create a file [module_name].services.yml. In my case, it will be twig_filter.services.yml:

Write the following in the twig filter.services.yml file:
services:
twig_filter.twig.twig_extension:
class: Drupaltwig_filterTwigCurrencyTwigExtention
public: false
tags:
- { name: twig.extension }
Where we specify the file with twig-filter functionality class.
Now you should only create a class file that specifies the service.
In the “src” folder, which should be located in the root directory of the module, create “Twig” folder, and then create a file named CurrencyTwigExtention.php. We have the following structure of the module file:

We make CurrencyTwigExtension.php file:
/**
* Class CurrencyTwigExtention.
*
* @package Drupaltwig_filterTwig
*/
class CurrencyTwigExtention extends Twig_Extension {
/**
* Filter call.
*
* @return array|Twig_SimpleFilter[]
*/
public function getFilters() {
return array(
new Twig_SimpleFilter('currency', array($this, 'currencyCalcFilter')),
);
}
/**
* Use Google Currency Converter API.
*
* @param int $amount
* Amount.
* @param string $curr_from
* From currency.
* @param string $curr_to
* To currency.
*
* @return mixed
* API response.
*/
public function currencyCalcFilter($amount, $curr_from = 'USD', $curr_to = 'EUR') {
$url = "http://www.google.com/finance/converter?a={$amount}&from={$curr_from}&to={$curr_to}";
$request = curl_init();
$timeOut = 100;
curl_setopt($request, CURLOPT_URL, $url);
curl_setopt($request, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($request, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)");
curl_setopt($request, CURLOPT_CONNECTTIMEOUT, $timeOut);
$response = curl_exec($request);
curl_close($request);
preg_match("/<div id=currency_converter_result>(.*)<span class=bld>(.*)</span>/", $response, $converted);
$output = array(
'#type' => 'markup',
'#markup' => sprintf("<div>%s</div>", implode('', [$converted[1], $converted[2]])),
);
return $output;
}
/**
* Get filter name.
*
* @return string
* Filter name.
*/
public function getName() {
return 'twig_filter.twig_extension';
}
}
When using |currency Twig-filter, currencyCalcFilter() the method tethered to it is called, in which we make HTTP-request to Google currency converter API with the required parameters. And then we cut out the useful text from the reply with a regular expression and give the results via a build-container for rendering content.
If you did everything right, when after navigating /twig-filter/USD/UAH/1500 page, we get the following result:

Don’t forget to clear the cache after changes in the twig templates or module code.
We hope this article was useful for you, and you are ready to create a custom filter. But if you need some help, feel free to contact us and we’ll solve your issues related to Drupal development.
Make Friends with Your Customers via Emails
Before we start talking about email marketing, let’s decide what we want from our customers. Maybe every business owner aims to grow sales and wants his customers to buy more. But what if someone buys an expensive product at once, drastically grows your daily sales, goes away and doesn’t come back? Is it exactly what you need? – I think, no. We want our customers to return to our shop again and again, tell their friends about our store and bring them too. But people have a deal with someone they can trust. And whom do we trust more? – Of course, our friends. So, our task is to turn the customers into our friends for building long-term relationships and growing their love to our brand. And here email marketing is the best helper.
How to Make Friends with a Customer?
Get Acquainted
All of us start the friendship with an acquaintance and the first meet. So to leave a positive impression, use a beautiful, clear and modern design for your newsletter. Also, it should be responsive and suit any mobile device. By the way, we have several good email templates that can help you to build wonderful newsletters. View them on ThemForest.
And the thing you should take into consideration is the following: never send newsletters without the agreement. It’s much better to introduce yourself to the customers and familiarize themselves with the content they will get in future. How to do this? – Very simple. Send the first letter to approve the subscription, where you’ll announce your newsletter, the topics you will cover, offers, and the schedule of your visits (like once a week or two times a month).
Give your customer an opportunity to unsubscribe, if you don’t want to get into spam. If he likes you from the first view, everything will be okay. So, continue on.
Find the Touch Points
What unites us with our friends? – Shared hobbies and interests. The best way to become interesting to your subscribers is to know them better and segment your base.
For example, you see that some lady buys the cosmetic product for skin in your online store. So the topic of modern cosmetics, skin care, and beauty advice could be interesting for her. But at the same time, some man bought an expensive perfume in your shop. Of course, he interests in other topics, such as new man perfumes, some modern accessories and other. That’s why we need segmentation.
Lifehack. Send the survey in your first letter where you ask for an agreement for the subscription. With its help, you’ll learn which of suggested topics can be interesting for each client. This way you’ll segment your base and will be interesting for everyone.
Be Positive

Remember, everything you send to your friends should brighten their day. Am I right? Avoid the negative thoughts and words in your letter, such as problem, pain, awful, suffering, etc. Did you feel comfortable while reading this list? I didn’t. So, turn on the happiness in your readers’ subconscious, and then they will wait for communication with you with great pleasure.
Be Honest
Never. Yes, never trick your friend. If it happens once, the trust reached for a very long time of your communication will be lost. Here we should consider this topic a bit more.
Now everybody talks about Open Rate and how drastically it depends on the email subject. This everything is great, except a little detail. In pursuit of the high results, sometimes we forget about the fact that alive and sensitive person is sitting in front of us on the further side of the monitor. And perhaps, this person likes to read our newsletter. He (or she) opens the mailbox and sees the teaser and promising subject, opens the letter, and then sees not those things he (she) expected. Your friend feels cheated. We can’t be having that.Email subject is not a straight road to the success and high sales. It’s a good way to attract an attention of the subscriber to something even more interesting inside your letter. It’s like a teaser trailer for some new movie. You must admit that it’s pretty pity when the teaser is much better than the film.
Be Generous
Never stint useful content for your friends. The higher quality of our newsletter is, the more chances it has to become the favorite one. Say, why the large online store of technical devices can’t send their customers weekly or monthly digest of interesting articles from the world of gadgets and, of course, show their new products with it? What if your friend wants to buy something useful? 🙂
And how about sending the preview for some new interesting books at the online book shop to your subscriber? You’ll do him a great serve explaining the books’ content. Why should he search it by himself, if you can save his time and help with it? – The readers will be interested if you’ll share your opinion. So, always share the best information with friends.
Be Tactful
You have some newsletter schedule. For example, once a week. It’s not a good idea to flood your friend with letters. It’s tactless. Anyone needs some personal space and has some business. The subscribers’ time is invaluable. And they won’t want to waste it for removing emails they don’t ready to read (or send them to spam).
As for advertisement, here is the same thing. Advertising is a too aggressive approach. But there is nothing to do without it. We can just dress it in something more interesting or hide. It should be invisible and readable between the rows. You don’t like showing up and obtrusive, don’t you? – So do your friends – they also hate ads.
Give the links to product and landing pages carefully, don’t impose your subscribers the necessity of buying and paying money. Our task is to interest the reader. We should show our product and its advantages, answer all questions our customer could have, ground the usefulness of our product. And only then our client can be confident and ready to buy.
Give Presents

Everybody likes presents. You must admit that it’s always pleasant to get the surprise. And email marketing is a perfect tool for gifting. Sometimes give your customers the discounts and bonuses in the newsletter. Gifts for order motivate people to shop and leave a positive impression. By the way, in this case, your friends feel their exclusivity: a bonus is a priority only for your subscribers and only in your newsletter. You can set up the automated emails with bonuses for some actions and save your time.
Be Friends
“Don’t walk behind me; I may not lead. Don’t walk in front of me; I may not follow. Just walk beside me and be my friend.”
— Albert Camus
A wise person said these words, and they are completely true. Write your emails to no customer or a person who has and influence on your business. Write to your friend. The polite tone of your emails engages people to communicate and trust you. Talk with your reader on the same wavelength and learn to explain your ideas in a friendly way. Don’t sell, but tell, share, and convince. And smile 🙂
Let’s Summarize
How to become friends with your customers and sell more using email marketing?
- Get acquainted and ask for agreement to visit their mailbox;
- Look stylish and beautiful in your newsletters. Modern design of the email template plays an important role;
- Choose interesting but honest subject for your email;
- Specify correct From email address and From name, if you want to communicate with customers;
- Send only useful content and information;
- Communicate friendly, grammatically correct and respectfully;
- Advertise your products carefully. Prove their value, tell more about them;
- Often make pleasant surprises, such as discounts, bonuses, and presents;
- Be interesting and fond of your subscribers’ hobbies;
- Always stay with your readers, even if they buy nothing but open your newsletter – try to find something they need;
- Clean your base regularly – if someone doesn’t open your newsletters during several months, remove this contact. Don’t waste your and his (her) time.
And remember that your Friend is a value. And while you have a friend, give him only qualitative products, useful content, and honest offers.
There were the base principles of the real friendship between a customer and business. Winning the customers’ confidence, we can be friends with them for a very long time, or even forever.
Write interesting letters and make friends!
And a bit later, we’ll talk about how to create such successful emails.
How to Code Display Suite Fields in Drupal
Display Suite module is a useful method of markup development in Drupal. It provides you with the full control on the way of displaying your content via dragging and dropping the fields on the admin panel. This module allows you to create the own fields, which are not “fields” in common understanding. They will appear and be acceptable only on the page of management of displayed content. Using these fields, you can display anything on the content page, moving it in the admin panel with the help of a mouse.
Display Suite Fields in Drupal 7
In Drupal 7, this everything is realized with hook_ds_fields_info().
function hook_ds_fields_info($entity_type) {
$fields = array();
// Field definition, in this case, using technical name “title”.
// They should be unique only within the limits of one entity and among//
the other DS fields. So, the title here won't overlap title from the entity
$fields['title'] = array(
// title: The title of the field, which will be displayed on the page of management of displayed content.//
'title' => t('Title'),
// field_type: Field type
// - DS_FIELD_TYPE_THEME : requests theme() function.
// - DS_FIELD_TYPE_FUNCTION : the field result terurns as a function.
'field_type' => DS_FIELD_TYPE_FUNCTION,
// ui_limit: Allows you to specify in which variants of display within the entity limits the field is defined for // field will be accessible for use in the following format: $bundle|$view_mode. You can replace some parts with *, this way
// specifying what will be accessible in all variants.
'ui_limit' => array('article|full', '*|teaser'),
// file: The path to a file which contents the code is specified here. It's used only
//for DS_FIELD_TYPE_FUNCTION type. The path should be full, for example:
// drupal_get_path('module', 'MYMODULE') . '/inc/file.inc',
'file' => 'optional_filename',
// function: The function name which will return the result for
// a field. Only for DS_FIELD_TYPE_FUNCTION type.
'function' => 'theme_ds_title_field',
// properties: Additional field settings.
'properties' => array(
// formatters: optionally for the function and required for
// DS_FIELD_TYPE_THEME. By the way, for the second one, you should also // // //register the theming function via hook_theme()
//if it’s not present. It will be requested as theme('function');
//In this array, the key is a function of theme(), and the value is only a tag for something in// the admin panel. It will be a select where you can choose the output format. 'formatters' => array(
'node_title_nolink_h1' => t('H1 title'),
'node_title_link_h1' => t('H1 title, linked to node'),
),
// settings & default: Optionally, used only if you want
// to add some field settings which will be present in UI.
'settings' => array(
'wrapper' => array(
'type' => 'textfield',
'description' => t('Eg: h1, h2, p'),
),
'link' => array(
'type' => 'select',
'options' => array('yes', 'no'),
),
),
'default' => array(
'wrapper' => 'h2',
'link' => 0,
),
),
);
return array('node' => $fields);
}
Field Type: DS_FIELD_TYPE_THEME
This field requests the specified function and transfers there all necessary data, field settings, and entity object (if it’s present), to let you get all entity data and a lot of useful information. The function should return the value of this field (row).
Example of Realization in Drupal 7
Let’s imagine that we need to output the number of words from the body field for the article material.
/**
* Implements hook_ds_fields_info().
*/
function mymodule_ds_fields_info($entity_type) {
$fields = array();
if ($entity_type == 'node') {
$fields['word_count'] = array(
'title' => 'DS: The number of words in the text',
'field_type' => DS_FIELD_TYPE_FUNCTION,
// We need this field only in a full format of displaying article material
'ui_limit' => array('article|full'),
// The function name
'function' => 'mymodule_ds_field_word_count',
);
return array($entity_type => $fields);
}
return;
}
/**
* The function which will be requested and return the result to the field.
* If the function returns nothing, false or null, it's
*considered as an empty field, and it won't be rendered.
*
* @param $field
* It contains all necessary information for us.
*/
function mymodule_ds_field_word_count($field) {
// The entity object, which this field was requested for, is contained in
// $field['entity']. For simplicity and clearness of code we use EMW.
$entity_wrapper = entity_metadata_wrapper('node', $field['entity']);
if ($body_value = $entity_wrapper->body->value->value()) {
// Returning of result.
return format_string(
'<strong>The number of worts in the text:</strong> @word_count',
array(
'@word_count' => str_word_count(strip_tags($body_value))
)
);
}
}
Field Type: DS_FIELD_TYPE_THEME
This field type requests theme() function, allowing us to transfer values to tpl.php file. It’s useful when we need to enter much wrapping. Also, it helps in case of field ability to change depending on some values. Building HTML right in PHP basing on the conditions through the function will be just unreadable. But the template is pretty helpful here.
Display Suite Fields in Drupal 8
In Drupal 8 hook_ds_fields_info() is replaced with the system of plugins. Display Suite comes with two plugin types DsField and DsFieldTemplate. You can use only DsField for creating fields. Now all fields are specified via one plugin. Drupal 8 has a new feature. Here is the difference: in Drupal 7 the field returned a row with a value, but in Drupal 8 the field must return the render array. So, if you want to return a row, you have to return the render array with markup.
Example of Realization in Drupal 8
Let’s imagine that we need to output the number of words from the body field for the article materials.
namespace DrupalmymodulePluginDsField;
use DrupaldsPluginDsFieldDsFieldBase;
use DrupalComponentRenderFormattableMarkup;
/**
* The field which outputs the number of words in the content.
*
* @DsField(
* id = "word_count",
* title = @Translation("DS: Word count"),
* provider = "mymodule",
* entity_type = "node",
* ui_limit = {"article|full"}
* )
*/
class WordCount extends DsFieldBase {
/**
* {@inheritdoc}
*
* The method which should return the result to the field.
*/
public function build() {
// Writing the object of the current entity into a variable for the convenience
$entity = $this->entity();
// Check if there is a value in a body field. If the field returnes nothing
// it takes like empty field and it isn't outputed.
if ($body_value = $entity->body->value) {
return [
'#type' => 'markup',
'#markup' => new FormattableMarkup(
'<strong>?????????? ???? ? ??????:</strong> @word_count',
[
'@word_count' => str_word_count(strip_tags($body_value))
]
)
];
}
}
}
So, this was a quick tip on how to implement your own Display Suite modules in Drupal, no matter what version you use. Hope that it was helpful. If you want to save your time, trust us these works on Drupal, and we’ll do everything you need.
Marketing Kit: New Branding Approach
Perhaps, you own some business and even have the website. If your answer is “No”, it’s a good idea to think about creating a website. Surely, it will help you to extend your brand. But is you answered “Yes”, it’s great, you are doing well! If you have a modern website, you may follow your unique style and idea. And what if to move on and make something different for your branding? Something that can help you stand out from the crowd and present your brand in the best way. Feel the benefits of Marketing kit.
What Is Marketing kit?
Marketing kit is a personalized commercial proposal, an advertising presentation, performed like a beautiful paper- or e-book. It is a bit similar to the website but has another format and logical structure.
The main requirements for these materials: a very attractive modern cover and powerful texts with qualitative images and infographics.
How It Works
Imagine that you are sitting in the comfortable armchair and looking through a beautifully designed photo album. Your visual sensors are doing a great job: you are gathering the information, memorize it better, and even make up your mind following the most powerful impressions. And our visual memory works great with beautiful images and short but powerful texts.
This type of book is like a whole story about the company and its products: it leads the reader step by step, page by page, convincing him in choosing your brand, and then he makes up his mind.
The main task of marketing kit is to leave a positive impression about your business, show all your benefits and opportunities for 3 minutes. And, as a result, you are memorable, and the customer is confident.
The Benefits of Marketing Kit
- It boosts your sales;
- Helps to find new clients and partners;
- Wins the rivals;
- Creates the positive brand image;
- Increases the customer loyalty and confidence.
How to Create Your Marketing Kit
First of all, you should consider the main idea of your brand, point out the priorities, mission, differences from the others, and benefits of your product. The following milestones can help you to create a good marketing kit that has the influence on your goal audience.
Brainstorm and Research
When we do something new, at first we generate ideas. And now it’s time to think out the philosophy of your business. Just gather the information, all possible things you can find. Now you should look at your website and note the most important things about your business, answering the following questions:
What is your brand motto?
Who are your customers? What do they need?
Which customer problems can you solve?
What are your brand colors and design style?
Do you know the product you want to sale and its benefits?
Who are your rivals? What makes you different from them?
What do your customers think about your business?
How can you help a client reach the success?
Perhaps, you’ll find the answers to these questions while thinking carefully and looking at your website. Perhaps, you have even done it before. But now the main task is to organize such useful things and conduct the research. Don’t forget to learn more about your competitors. You can use the method of Sherlock for these purposes.
Building Structure and Answering the Questions
It is the most crucial point. You should create a logic of your book and write powerful texts. It should answer the client’s questions page by page. Each page for each question. Here is an important moment: don’t accuse a customer of his hidden problems. You should just show them and then give an effective solution at the end.
A marketing kit has the following structure:
- beautiful cover with a powerful motto and your brand name;
- the hidden client problems and wishes, some story or your main idea;
- the presentation of your product and how it works;
- for whom it will be useful and suitable;
- the benefits of your product;
- product additional possibilities;
- why you? (here you can show some differences from the rivals);
- portfolio of your best works;
- the clients’ feedbacks;
- how you work;
- some powerful motto and call to action.
At the end of the trip across your book, the client should be completely confident in your brand and want to buy your product. – The successful marketing kit acts in this way.
Creative Design for the Best First Impression
And here is the sweetest part of your preparation. Now you have many ideas, even much more information, the structure, and powerful texts. It’s time to dress this up and make it look just amazing. Do you remember the question about your brand colors and style? – Take them into consideration, while creating your cover and book look.
The design could be different, but remember that it should be focused on your main thoughts and powerful texts. It’s better to use clean and modern design, add qualitative infographics and pictures to each page. And follow the rule: one page for one item of the plan above. Focus on details is our everything.
If you’ve done all discussed things, congratulations! Now you can celebrate the release of your first marketing kit! Send it like e-book to your clients via email, or print it and show at the business meetings, conferences, exhibitions, and of course, on the life meetings with customers. And let it bring you success and boost your sales drastically!
Wish you inspiration and a lot of great ideas!