The Drupal 8 plugin system – part 4

We defined what a plugin isdiscussed some plugins used in core and wrote our own custom pluginpreviously. We shall tune it up a bit in this post.

Real world plugins have a lot more properties than the label property mentioned in our breakfast plugin. To make our plugin more “real world”, we introduce 2 properties, image and ingredients. It makes more sense now to have a custom annotation for breakfast instead of using the default Plugin annotation.

How different are custom annotations from the usual Plugin annotation?

1) They convey more information about a plugin than what an @Plugin does. Here’s a custom annotation for a views display plugin from search_apitaken from here.

/**
 * Defines a display for viewing a search's facets.
 *
 * @ingroup views_display_plugins
 *
 * @ViewsDisplay(
 *   id = "search_api_facets_block",
 *   title = @Translation("Facets block"),
 *   help = @Translation("Display the search result's facets as a block."),
 *   theme = "views_view",
 *   register_theme = FALSE,
 *   uses_hook_block = TRUE,
 *   contextual_links_locations = {"block"},
 *   admin = @Translation("Facets block")
 * )
 */

2) Custom annotations are a provision to document the metadata/properties used for a custom plugin. Check out this snippet from FieldFormatter annotation code for instance:

  /**
   * A short description of the formatter type.
   *
   * @ingroup plugin_translatable
   *
   * @var \Drupal\Core\Annotation\Translation
   */
  public $description;

  /**
   * The name of the field formatter class.
   *
   * This is not provided manually, it will be added by the discovery mechanism.
   *
   * @var string
   */
  public $class;

  /**
   * An array of field types the formatter supports.
   *
   * @var array
   */
  public $field_types = array();

  /**
   * An integer to determine the weight of this formatter relative to other
   * formatter in the Field UI when selecting a formatter for a given field
   * instance.
   *
   * @var int optional
   */
  public $weight = NULL;

That gave us a lot of information about the plugin and its properties. Now that you are convinced of the merits of custom annotations, let’s create one.

Checkout the module code if you haven’t already.

$ git clone git@github.com:badri/breakfast.git

Switch to the custom-annotation-with-properties tag.

$ git checkout -f custom-annotation-with-properties
Custom annotation with properties

and enable the module. The directory structure should look like this: 

The new Annotation directory is of interest here. Custom annotations are defined here.

  /**
   * A glimspe of how your breakfast looks.
   *
   * This is not provided manually, it will be added by the discovery mechanism.
   *
   * @var string
   */
  public $image;

  /**
   * An array of igredients used to make it.
   *
   * @var array
   */
  public $ingredients = array();

Now, the plugin definition is changed accordingly. Here’s the new annotation of the idli plugin.

/**
 * Idly! can't imagine a south Indian breakfast without it.
 *
 *
 * @Breakfast(
 *   id = "idly",
 *   label = @Translation("Idly"),
 *   image = "https://upload.wikimedia.org/wikipedia/commons/1/11/Idli_Sambar.JPG",
 *   ingredients = {
 *     "Rice Batter",
 *     "Black lentils"
 *   }
 * )
 */

The other change is to inform the Plugin Manager of the new annotation we are using. This is done in BreakfastPluginManager.php.

    // The name of the annotation class that contains the plugin definition.
    $plugin_definition_annotation_name = 'Drupal\breakfast\Annotation\Breakfast';

    parent::__construct($subdir, $namespaces, $module_handler, 'Drupal\breakfast\BreakfastInterface', $plugin_definition_annotation_name);

Let’s tidy the plugin by wrapping it around an interface. Though this is purely optional, the interface tells the users of the plugin what properties it exposes. This also allows us to define a custom function called servedWith() whose implementation is plugin specific.

With the plugin class hierarchy looking like this now: 

The servedWith() is implemented differently for different plugin instances.

// Idly.php
  public function servedWith() {
    return array("Sambar", "Coconut Chutney", "Onion Chutney", "Idli podi");
  }
// MasalaDosa.php
  public function servedWith() {
    return array("Sambar", "Coriander Chutney");
  } 

We make use of the interface functions we wrote in the formatter while displaying details about the user’s favorite breakfast item.

// BreakfastFormatter.php
    foreach ($items as $delta => $item) {
      $breakfast_item = \Drupal::service('plugin.manager.breakfast')->createInstance($item->value);
      $markup = '<h1>'. $breakfast_item->getName() . '</h1>';
      $markup .= '<img src="'. $breakfast_item->getImage() .'"/>';
      $markup .= '<h2>Goes well with:</h2>'. implode(", ", $breakfast_item->servedWith());
      $elements[$delta] = array(
        '#markup' => $markup,
      );
    }

And the user profile page now looks like this.

Breakfast plugin first cut
Breakfast plugin first cut

Derivative plugins

We have Idly plugin instance mapped to the Idly class and Uppuma instance mapped to the Uppuma class. Had all the plugin instances been mapped to a single class, we would have got derivative plugins. Derivative plugins are plugin instances derived from the same class. They are employed under these scenarios: 1. If one plugin class per instance is an overkill. There are times where you don’t want to define a class for each plugin instance. You just say that it is an instance of a particular class that is already defined. 2. You need to dynamically define plugin instances. The Flag module defines a different Flag Type plugin instance for different entities. The entities in a Drupal site are not known beforehand and hence we cannot define one instance each for every entity. This calls for a plugin derivative implementation.

Lets add derivative plugins to our breakfast metaphor.

$ git checkout -f plugin-derivatives

Here’s a new user story for the breakfast requirement. We are going to add desserts to our menu now. All desserts are of type Sweet. So, we define a derivative plugin called Sweet which is based on breakfast.

This calls for 3 changes as shown in the new directory structure outlined below:

1) Define the Sweet plugin instance class on which all our desserts are going to be based on.

/**
 * A dessert or two whould be great!
 *
 *
 * @Breakfast(
 *   id = "sweet",
 *   deriver = "Drupal\breakfast\Plugin\Derivative\Sweets"
 * )
 */
class Sweet extends BreakfastBase {
  public function servedWith() {
    return array();
  }
}

Note the “deriver” property in the annotation.

2) Next, we define the deriver in the Derivative directory.

/**
 * Sweets are dynamic plugin definitions.
 */
class Sweets extends DeriverBase {

  /**
   * {@inheritdoc}
   */
  public function getDerivativeDefinitions($base_plugin_definition) {
    $sweets_list = drupal_get_path('module', 'breakfast') . '/sweets.yml';
    $sweets = Yaml::decode(file_get_contents($sweets_list));

    foreach ($sweets as $key => $sweet) {
      $this->derivatives[$key] = $base_plugin_definition;
      $this->derivatives[$key] += array(
        'label' => $sweet['label'],
        'image' => $sweet['image'],
        'ingredients' => $sweet['ingredients'],
      );
    }

    return $this->derivatives;
  }
}

3) The derivative gets sweets info from the sweets.yml present in the module root directory. This could even be an XML/JSON or any file format which holds metadata. I’ve used a YAML file for consistency’s sake.

mysore_pak:
  label: 'Mysore Pak'
  image: 'https://upload.wikimedia.org/wikipedia/commons/f/ff/Mysore_pak.jpg'
  ingredients:
    - Ghee
    - Sugar
    - Gram Flour

jhangri:
  label: 'Jhangri'
  image: 'https://upload.wikimedia.org/wikipedia/commons/f/f8/Jaangiri.JPG'
  ingredients:
    - Urad Flour
    - Saffron
    - Ghee
    - Sugar

The class hierarchy for derivative plugins looks a little big bigger now.

Plugin derivatives

Clear your cache and you must be able to see the sweets defined in the yaml file in the breakfast choice dropdown of the user profile.

Enjoy your dessert!

That concludes our tour of Drupal 8 plugins. Hope you liked it and learnt something. Stay tuned for the next series about services.

The Drupal 8 plugin system – part 3

Checkout part 1 and part 2 for understanding the concept of plugins. In this installment, we will be

  1. Implementing a new plugin from existing plugin types.
  2. Implementing a new plugin type using the annotation based discovery mechanism.

As an exercise, let’s first construct an imaginary scenario where the user of your Drupal site wants choose what they want for breakfast from a list of breakfast menu items. To add a dash of variety, all the breakfast items in the code are of South Indian culinary. You can checkout the code and suit yourself, change the breakfast items etc.

For this exercise, checkout the module code first and enable it.

$ git clone git@github.com:badri/breakfast.git

In order to select their breakfast item of choice, the user needs to be presented with a list of choices in their profile. The lame way is to create a list field in the user profile and add all the breakfast choices. This offers limited functionality and is not pluggable. We can do better than that. So, let’s ahead and create a custom field called breakfast choice.

This functionality is there in the custom-field-no-plugin tag of the code you previously checked out. You can switch to that tag by:

$ git checkout -f custom-field-no-plugin

After you enable the breakfast module, go to the user profile and create a new field of type “breakfast choice”. As the tag says, we haven’t created any custom plugin type yet. But we do create a new plugin from the existing plugin types for our custom field. In fact, we create 3 new plugins(one each for the field type, field formatter and field widget). Our directory structure looks like this:

All the breakfast menu items come from a single location, your custom field widget, the BreakfastWidget.php file.

  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
    $value = isset($items[$delta]->value) ? $items[$delta]->value : '';
      $options = array(
        'idly' => 'Idly',
        'dosa' => 'Dosa',
        'uppuma' => 'Uppuma',
      );

    $element = array(
      '#type' => 'select',
      '#options' => $options,
      '#default_value' => $value,
      '#multiple' => FALSE,
    );

    return array('value' => $element);
  }

Though it works, this is not a good design for 2 reasons:

  1. You are hardcoding in the presentation layer. Widgets define the way you present the input element in a form. You can’t define your data there.
  2. It is not pluggable. Other developers have to open BreakfastWidget.php to add new Breakfast items.
  3. It cannot be extended. What if I want to add additional properties to my breakfast items, like images, ingradients or price? I cannot do this in the current setup.

We will address problems 1 and 2 for now. i.e., we add the ability to create new breakfast items outside of the Breakfast Widget file. We make breakfast items “pluggable”. Other modules can add new breakfast items it were a plugin, which is exactly what we do next.

To get the plugin version of the module, do:

$ git checkout -f plugin-default-annotation

Now, our directory structure looks like this:

The BreakfastPluginManager is, as the name says, a service used to manage various breakfast plugins across modules. The plugin manager’s constructor class deserves some explanation.

  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) {
    $subdir = 'Plugin/Breakfast';

The $subdir tells the plugin manager where to look for Breakfast plugins in a module.

    $plugin_definition_annotation_name = 'Drupal\Component\Annotation\Plugin';

The $plugin_definition_annotation_name is the namespaced name of the annotation class which is used to annotate all Breakfast plugins. Plugin is the default annotation. We can define custom annotations as well, which will be the subject of the next installment in this series.

    parent::__construct($subdir, $namespaces, $module_handler, $plugin_definition_annotation_name);

    $this->alterInfo('breakfast_info');

    $this->setCacheBackend($cache_backend, 'breakfast_choice');
  }

alterInfo tells us that this plugin definition can be altered by implementing hook_breakfast_info_alter.

Plugin definitions are cached, which is why we need to run drush cr(the equivalent of drush cc all in D8) every time we alter the plugin definitions. The setCacheBackend defines the cache backend for our plugin. In the current context, we are not customizing it too much.

Another major change is the new file breakfast.services.yml. It contains metadata about the breakfast plugin manager service which we discussed above.

services:
  plugin.manager.breakfast:
    class: Drupal\breakfast\BreakfastPluginManager
    arguments: ['@container.namespaces', '@cache.default', '@language_manager', '@module_handler']

One or more services can be defined in the services.yml file. Each entry contains a machine name of the service, the class that implements the service and dependencies(if any) can be passed as arguments. The @ prefix for the arguments indicates that the corresponding argument is in itself a service.

The field type we added earlier hasn’t changed, but the widget has been revamped. We no longer hardcode any breakfast items. Instead, we dynamically pull all plugin instances of type Breakfast.

Here’s how:

    $options = array();
    $breakfast_items = \Drupal::service('plugin.manager.breakfast')->getDefinitions();
    foreach($breakfast_items as $plugin_id => $breakfast_item) {
      $options[$plugin_id] = $breakfast_item['label'];
    }

Any module can now define a new breakfast menu item and expect it to show up in the user profile’s breakfast field dropdown. We’ve created 3 breakfast items in our module to illustrate this. Let’s pick an example breakfast plugin, my favorite. Masala dosa.

Image courtesy

err, I meant:

/**
 * Adds Masala Dosa to your Breakfast menu.
 *
 *
 * @Plugin(
 *   id = "masala_dosa",
 *   label = @Translation("Masala Dosa")
 * )
 */
class MasalaDosa extends PluginBase {
  // Nothing here yet. Just a placeholder class for a plugin
}

Nothing fancy there. Just a placeholder class and some metadata in the @Plugin annotation.

Phew! It took more time to add a masala dosa plugin that to make a masala dosa. Each breakfast item being a unique plugin instance sounds a bit like an overkill. We will address this and problem #3 detailed above(plugins having different properties like picture) in the next post!

The Drupal 8 plugin system – part 2

We saw in part 1 how plugins help us in writing reusable functionality in Drupal 8. There are a lot of concepts which plugins share in common with services, like:

  1. limited scope. Do one thing and do it right.
  2. PHP classes which are swappable.

Which begs the question, how exactly are plugins different from services? If your interface expects implementations to yield the same behaviour, then go for services. Otherwise, you should write it as a plugin. This needs some explaining. For instance, if you are creating an interface to store data in a persistent system, like MySQL or MongoDB, then it would be implemented as a service. The save() function in your interface will be implemented differently for both the services, but the behaviour will be the same, i.e., it takes data as input parameters, stores them in the respective data store and returns a success message.

On the other hand, if you are creating an image effect, it needs to be a plugin. (It already is. Check image effects as plugins). The core concept of image plugins is to take in an image, apply an effect on it and return the modified image. Different image effects yield different behaviours. An image scaling effect might not produce the same behaviour as that of an image rotating effect. Hence, each of these effects need to be implemented as a plugin. If any module wants to create a new image effect, it needs to write a new plugin by extending the ImageEffectBase class.

Plugins used in core

Let’s take a look at the major plugin types provided by Drupal 8 core. An example plugin of each plugin types will be the subjects of future blog posts.

  1. Blocks Drupal 8 finally got blocks right. Custom blocks can be created from the BlockBaseclass.
  2. Field Types, Field Widgets and Field Formatters Check part 1 for how this is done in Drupal 8.
  3. Actions Drupal 8 allows module developers to perform custom actions by implementing the ActionBase class. Blocking a user, unpublishing a comment, making a node sticky etc. are examples of actions.
  4. Image Effects Image effects are plugins which manipulate an image. You can create new image effects by extending ImageEffectBase. Examples of core image effects are CropImageEffect and ScaleImageEffect.
  5. Input filters User submitted input is passed through a series of filters before it is persisted in the database or output in HTML. These filters are implemented as plugins by implementing the FilterBase class.
  6. Entity Types In Drupal parlance, entities are objects that persist content or configuration in the database. Each entity is an instance of an entity type. New entity types can be defined using the annotation discovery mechanism.
  7. Views related plugins A large collection of different plugin types are employed by views during the querying, building and rendering stages.

Plugin Discovery

Plugin discovery is the process by which Drupal finds plugins written in your module. Drupal 8 has the following plugin discovery mechanisms:

  1. Annotation based. Plugin classes are annotated and have a directory structure which follows the PSR-4 notation.
  2. Hooks. Plugin modules need to implement a hook to tell the manager about their plugins.
  3. YAML files. Plugins are listed in YAML files. Drupal Core uses this method for discovering local tasks and local actions.
  4. Static. Plugin classes are registered within the plugin manager class itself. This is useful if other modules should not create new plugins of this type.

Annotation based discovery is the most popular plugin discovery method in use. We will briefly look at how we create a new plugin type using this method in the next part.

The Drupal 8 plugin system – part 1

Plugins are swappable pieces of code in Drupal 8. To see how different they are from hooks, let’s take an example where we want to create a new field type.

In Drupal 7, this involves:

  1. Providing information about the fieldhook_field_info – describes the field, adds metadata like label, default formatter and widget.hook_field_schema – resides in the module’s .install file. Specifies how the field data is stored in the database.hook_field_validate – validates the field content before it is persisted in the database.hook_field_is_empty – criteria which decides when this field is considered “empty”.
  2. Describe how the field will be displayed using,hook_field_formatter_info – metadata about different types of formatters.hook_field_formatter_view – implementation of the formatters defined above, mostly spits out HTML.
  3. Define a widget for the field type.hook_field_widget_info – provides information about widgets. For instance, a calendar would be a widget for a date field.hook_field_widget_form – implements the widgets.

In Drupal 8, all the 3 steps mentioned above(field info, formatters and widgets) are 3 types of plugins.

  1. Providing information about the field. This is done by the FieldType plugin which extends FieldItemBase. The FieldItemBase class has schema, metadata and validation encapsulated which will need to be overriden by the developer.
  2. Field formatter. Implemented by extending FormatterBase. the viewElements() function needs to be overridden to provide a renderable array for a field value. This is similar to the hook_field_formatter_view in Drupal 7.
  3. Widgets are implemented by subclassing WidgetBase.

There is many more to creating field types than that, but that’s all you need to know from a plugins perspective.

What did we gain by changing fields from being a hook based system to plugins based? For one, the code became more encapsulated. The definition and implementation is wrapped in one place. Another benefit we reaped by following OO principles is inheritance. Plugins are designed to be extensible. This allows us to subclass similar functionality and achieve one of the most coveted ideals of software engineering, i.e. code reuse. 

In the next part, we will look at the concept of plugin discovery and some common plugin types exposed by Drupal 8 core.

Annotations in Drupal 8

Annotations are PHP comments which hold metadata about your function or class. They do not directly affect program semantics as they are comment blocks. They are read and parsed at runtime by an annotation engine.

Annotations are already used in other PHP projects for various purposes. Symfony2 uses annotations for specifying routing rules. Doctrine uses them for adding ORM related metadata.Though handy in various situations, their utility is debated about a lot, like:

  1. How to actually differentiate between annotations and actual user comments?
  2. Why put business logic inside comment blocks. Shouldn’t they be a part of core language semantics?
  3. Annotations blur the boundary between code and comments. If the developer misses an annotation(Remember, its not a program semantic). It might compile fine, but might not work as expected.
  4. Another closely related gripe is, annotations are hard to test and debug.

Most of the acquisitions are around the concept of annotations implemented as comments in PHP. There is however, a proposal to add it as a first class language feature. In the meantime, we are stuck to using comment based annotations.

Annotations are not all evil. They make it easier to inject behaviour without adding a lot of boilerplate.

Here is an example taken from stackoverflow which shows how annotations can cut a lot of boilerplate code.

Let’s say we want to inject weapon object to a Soldier instance.

class Weapon {
    public function shoot() {
        print "... shooting ...";
    }
}

class Soldier {
    private $weapon;

    public function setWeapon($weapon) {
        $this->weapon = $weapon;
    }

    public function fight() {
        $this->weapon->shoot();
    }
}

If the DI is done by hand, then:

$weapon = new Weapon();

$soldier = new Soldier();
$soldier->setWeapon($weapon); 
$soldier->fight();

We could go a step further and decouple the DI and put it in an external file, like:

Soldier.php

$soldier = Container::getInstance('Soldier');
$soldier->fight(); // ! weapon is already injected

soldierconfig.xml

<class name="Soldier">
    <!-- call setWeapon, inject new Weapon instance -->
    <call method="setWeapon">
        <argument name="Weapon" />
    </call>
</class>

If we use annotations instead:

class Soldier {
    ...

    /**
     * @inject $weapon Weapon
     */
    public function setWeapon($weapon) {
        $this->weapon = $weapon;
    }

Annotations also give the additional advantage of having both code and metadata co-located. I think this is another reason why it was decided to use annotations and do away with external configuration files in Drupal 8(at least for plugins).

The Drupal part

Drupal 8 borrows annotations syntax from Doctrine. Drupal 7 had metadata tucked away in info hooks. This involves reading the whole module file into memory for every request. Annotations, on the other hand, are tokenized and parsed and don’t incur as much memory requirements as in the hook based approach. Also, docblocks are cached by the opcode cache.

Syntax

Drupal annotations are nested key value pairs very similar to json dumps. There are some gotchas though. You MUST use double quotes for strings and no quotes at all for numbers. Lists are represented by curly brackets and booleans by TRUE and FALSE without quotes. Here’s a code dump of annotations in action, from TextDefaultFormatter.php file in text module.

/**
 * Plugin implementation of the 'text_default' formatter.
 *
 * @FieldFormatter(
 *   id = "text_default",
 *   label = @Translation("Default"),
 *   field_types = {
 *     "text",
 *     "text_long",
 *     "text_with_summary",
 *   },
 *   quickedit = {
 *     "editor" = "plain_text"
 *   }
 * )
 */
class TextDefaultFormatter extends FormatterBase {
...

With so many conveniences, I hope annotations become a first class citizen of PHP pretty soon!

Drupal 8 asset management using libraries.yml

One of the things you are likely to do if you write a custom module or a theme is include third party Javascript and/or CSS assets in it. Previously, this used to be a clumsy hook_library_info()array but is replaced by a YML file in D8. It makes asset management look more organized and easier to edit. Let’s see how to do this for the colorbox module.

The new YML file will have the naming convention modulename.libraries.yml. So, ours will be colorbox.libraries.yml and will reside in the top level of the module directory like all other YML files.

Each entry has a unique name and a set of properties.

colorbox:
  version: VERSION
  js:
    js/colorbox.js: {}
  dependencies:
    - core/jquery
    - core/drupal

Here, colorbox is the name of the asset bundle to be included. This identifier will be used to refer the asset in the module or another yml file. The js property lists all the js files needed for the asset. The {} next to it can be used for specifying metadata like cachepreprocessminify and weight.

An example entry from core.libraries.yml:

js:
  assets/vendor/classList/classList.min.js: { weight: -21, browsers: { IE: 'lte IE 9', '!IE': false }, minified: true }

The dependencies are the assets list which colorbox expects to be loaded. The core/jquerymeans the jquery asset present in core.libraries.yml.

Here’s how the jquery entry looks in core.libraries.yml:

jquery:
  remote: https://github.com/jquery/jquery
  version: 2.1.3
  license:
    name: MIT
    url: https://github.com/jquery/jquery/blob/2.1.3/MIT-LICENSE.txt
    gpl-compatible: true
  js:
    assets/vendor/jquery/jquery.min.js: { minified: true, weight: -20 }

Likewise, the core/drupal dependency needs to be put up if the respective js exposes a Drupal setting.

Colorbox itself listed as a dependency in colorbox.libraries.yml for a theme variant:

stockholmsyndrome:
version: VERSION
js:
    styles/stockholmsyndrome/colorbox_style.js: {}
css:
    theme:
    styles/stockholmsyndrome/colorbox_style.css: {}
dependencies:
    - colorbox/colorbox
    - core/jquery
    - core/drupal

Including the assets in a page

The colorbox assets declared above can be included in a page by implementing the hook_page_attachments hook.

function colorbox_page_attachments(&$page) {
    ...
    $page['#attached']['library'][] = 'colorbox/colorbox';

Configurables can be passed from Drupal/PHP domain to js using the drupalSettings key.

    $js_settings = array(
      'opacity' => '0.85',
      'current' => t('{current} of {total}'),
      'previous' => t('« Prev'),
      'next' => t('Next »'),
      'close' => t('Close'),
      'maxWidth' => '98%',
    );
    $page['#attached']['drupalSettings']['colorbox'] = $js_settings;

Much of libraries.yml functionality overlaps with the hook_libraries_info of libraries module. The same thing can be accomplished by implementing the libraries_info hook, downloading colorbox jquery release in the libraries directory and calling:

libraries_load('colorbox', $variant);

In Drupal 7, this is the de facto way of handling third party js assets.

Why 2 ways to do the same thing?

The libraries module is also ported to Drupal 8 and can technically do the same thing, but offers 2 advantages:

  • The same asset can be used by more than 1 module. For example, colorbox jquery library can be used by both colorbox module and a custom theme.
  • Libraries module also facilitates loading of third party assets written in PHP.

Speeding up Drupal 8 module creation using Drupal Console

Though Drupal 8 is technically advanced compared to its predecessor, writing a module involves a lot of boilerplate code. There were many gotcha moments when I forgot to add a namespace and got puzzling errors. Fortunately, all that code can be generated automatically using a tool called the Drupal Console. Drupal Console is another cool addition to the Proudly Found Elsewhere school of thought as it leverages the Symfony Console component to handle the CLI part.

Installing

Note that Drupal Console supports only Drupal 8.0.0-beta4 at the time of writing this.

Get the latest version:

$ curl -LSs http://drupalconsole.com/installer | php

Move it to somewhere convenient so that it can be used throughout the system:

$ mv console.phar /usr/local/bin/drupal

Go to the drupal root directory of any Drupal 8 beta4 setup and run:

$ drupal list
Drupal version Drupal Console 0.5.2 - prod

Usage:
[options] command [arguments]

Options:
 --help (-h)           Display this help message.
 --quiet (-q)          Do not output any message.
 --verbose (-v|vv|vvv) Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug.
 --version (-V)        Display this application version.
 --ansi                Force ANSI output.
 --no-ansi             Disable ANSI output.
 --no-interaction (-n) Do not ask any interactive question.
 --bootstrap-file (-b) Path to Drupal bootstrap file (core/includes/boostrap.inc).
 --shell (-s)          Launch the shell.
 --env (-e)            The Environment name. (default: "prod")
 --no-debug            Switches off debug mode.

Available commands:
 cr                            Rebuild and clear all site caches.
 drush                         Run drush into console
 help                          Displays help for a command
 list                          Lists commands
cache
 cache:rebuild                 Rebuild and clear all site caches.
config
 config:debug                  Show the current configuration
container
 container:debug               Displays current services for an application
generate
 generate:command              Generate commands for the console
 generate:controller           Generate controller
 generate:entity:config        Generate EntityConfig
 generate:entity:content       Generate EntityContent
 generate:form:config          Generate ConfigFormBase
 generate:module               Generate a module
 generate:plugin:block         Generate plugin block
 generate:plugin:imageeffect   Generate image effect plugin
 generate:service              Generate service
router
 router:debug                  Displays current routes for an application
 router:rebuild                Rebuild routes

Usage

Drupal Console currently supports generating PSR-4 compliant code for plugins, controllers, modules, services, entities and forms.It also has basic debugging commands for listing current configuration and routes. Never write a Drupal 8 module from scratch again. Use Drupal Console instead!

Resources

Developing an admin theme in Drupal

From the very beginning, I used to wonder if there are any fancier alternatives to the “drupal blue” interface we take for granted in Drupal admin themes. I then discovered the best fit(so far) in Rubik. Since Rubik, not much has happened in admin theme space. I’d rate shiny as a close second.

How much effort goes into creating a fancy looking admin theme from scratch? Is it very straight forward and easy? I first decided what an admin theme would need to have:

  • configurable interface I don’t mean the configurable theme settings(which is useful though). I should be able to cook up my own color and font combinations.
  • shouldn’t be a subtheme by itself. Rubik has this problem. It needs Tao. Not sure why exactly, but having 2 themes for admin increases my code footprint a lot.
  • Responsive ain’t needed. We don’t need admin themes to be responsive.They are going to be used by site editors in their desks. So, we can rule out all device support safely for admin themes. Responsive bartik seems to have a different take on this.
  • icons, icons. Visual iconography is sparesely used in most admin themes, excepting rubik. This needs a lot of improvement, given the advent of tools like fontawesome.

With these design goals in mind, I started brewing a simple admin theme for Kalvi. The choice of tools needs a brief mention here.

SASS

No brainer. To have a configurable UI of sorts(goal #1), this was quintessential. SASS is “dev configurable” and meets the goal perfectly. I might switch to a non ruby based setup(like libsass/node) sometime in the future.

FontAwesome

From whatever research I did, no admin theme uses Fontawesome. I believe this could be a great combination instead of using image icons.

Rubik adds icons using the theme_admin_block_content function. I adapated the same code. Font awesome needs a slightly different approach.

Here’s the SASS code for adding Fontawesome icons via code:

@mixin icon($icon) {
  @extend %icon;
  content: $icon;
}

@mixin icon-before($icon, $padding: 10px) {
  &:before {
    @include icon($icon);
    padding-right: $padding;
  }
}

%icon {
  font-family: FontAwesome;
font-weight: normal;
  font-style: normal;
  display: inline-block;
  text-decoration: inherit;
  line-height: 1;
}

If I wanted to add an icon to a class, I just needed to call the “icon-before” mixin with the appropriate icon glyph, like

@include icon-before($icon-cubes);

I had to do some dirty hacks too, like add warning, info and error icons by changing the DOM via js. Drupal could use some theming flexibility here. core maintainers, please take note.

iCheck

I was bored of the stock checkboxes and radio buttons. Turns out this guy was too, and he created iCheck. I used it to customize the look and feel of option elements in forms.

Bower

We’re just getting started and already 2 3rd party dependencies. Bower was the defacto tool to manage frontend dependencies. Note that these needs to be explicitly checked in, as Drupal.org does not package bower dependencies, though I hope it does some day as Bower is becoming increasingly indispensible for frontend dependency management.

Grunt

Maninly used for grunt watch. Compiles SCSS to CSS and reloads the page if any js/CSS files are modified. There are a lot of tools popping up in this segment, like Gulp.js and Broccoli.

Susy2

Layout and grid systems are misunderstood as only meant for responsive design. I presonally find semantic grids(especially the clean syntax of Susy2) very productive for general HTML layout.

A few git commits and several doses of caffine later, this is what I end up with.

Why .npm?

I spent a couple of frustrating hours with a strange issue where drush crashes silently when I try to enable the theme. This had to do with node_modules directory containing .info files and drush wrongly interpreting it as a module/theme. The solution was to move all node related tools to a .npm directory. Apparently, drush does not traverse directories beginning with a dot. This is documented here and here.

List of common elements

Admin themes don”t need lots of regions, unless you are doing something creepy. But you have to take care to have a checklist of items to be themed:

  1. form elements
  2. breadcrumb
  3. color scheme
  4. pagination elements
  5. warning, error messages

Other cool TODO ideas

  1. convert it to a frontend framework based theme(like bootstrap, semantic UI or Bourbon) for uniformity’s sake.
  2. Create a specialized UI for panels/panel based workflow(IPE).

It is not the perfect Drupal theme, but at least I shipped it. Download it from drupal.org. Let me know what you think!

Colophon

I wanted to host my blog on a platform which is not tied to a single backend or stack, is dev friendly(git push deploys your app etc) and has a copious free tier. Also, ability to tweak around the look and feel and no-fluff things like markdown were attractive prospects. Openshift seems like a good fit and can’t complain so far. Oh, and did I mention its dev-friendly too? Ghost comes to mind when I think of a no-fluff blogging platform. I was able to tweak a ghost quickstart with a theme borrowed from here, and behold, ladies and gentlemen, the result is in front of you to judge.

Update(27th December 2015)

Lot of things changes since I hosted this blog. I figured that I was slowly iterating towards a CMS-y blog. Things like managing e-mail courses, contact forms, heavy editing, revisions etc. Things which are best done by a CMS(think Drupal). This blog is now powered by Drupal 7, Panopoly to be precise and hosted using DigitalOcean(Note: referral link).