Webmention.io integration for Drupal 8

I've had my site for quite some time now, the internet archive goes way back to 2002 even! To be fair, most of the content until 2007 wasn't that interesting (not sure what makes me think it is nowadays though, but okay ... ), but it was mostly the primary source of well .. me :). Apart from that, I also use Twitter, but I want to turn this around and let my site be the primary source. The IndieWeb movement is something I only recently discovered, but somehow, the philosophy was in my mind for the last few weeks, and I am not the only one that is talking about it.

So, as a first small step, to figure out who links to my content, I created a simple Drupal 8 module that can receive and store webmentions and pingbacks from Webmention.io. The source is available at https://github.com/swentel/webmention_io. I'll move this drupal.org at some point once it gets into a more polished state, but it also depends on further iterations of getting more interaction feedback to my site.

Update: The repository has already moved to https://github.com/swentel/indieweb - sorry in case you were using it already :) The module allows you to receive webmentions, send webmentions to brid.gy which will look for the microformats added by the module on content and images. Micropub and Microsub are on the way too!

https://brid.gy/ is polling Twitter and forwarding those mentions to this site, and currently likes are being exposed and replies are stored as comments, which is very cool! See the sweet magic happening right on note #4.

Quasi un'Utopia : the very beginning

I like personal challenges. For 2018 my main goal is being able to play Piano Sonata No. 14 (aka Quasi una fantasia or Moonlight Sonata) by Beethoven. To make it even more stressful, december 8 has been set as tentative date where I will perform the piece in public, a bet I made with my father in law. Place to be is a small church in Zevergem, Belgium, which has great acoustics. Don't put this in your agenda yet as I will make the final decision on september the first. If I don't feel confident enough that I will make it, there won't be a public performance, at least this year :)

It's a huge shot in the dark, given my limited (read, practically nonexistent) skills on the piano at this moment. I made a rough schedule with monthly goals which, currently, seem very doable. The first (and quite important) goal has been achieved: actually owning a decent piano at home. To make it a bit more fun for myself, I'm going to post monthly video updates. It will keep me focused as there's some pressure now since I made this public, which I doubted doing at first. So without further ado, here's the intro video. A special thank you to @plach__ for suggesting me the working title of this challenge: Quasi un'Utopia.

Migrated to Drupal 8, using Vue.js in the front

Update 30/09/2019: switched back to a Drupal theme

If you can read this with a beautiful background picture of Isle of Man, then it means your javascript is working. If not, well, then maybe you disabled javascript, or you are reading this in a feed somewhere :) Or this is a bot and I haven't completely killed my great SEO rankings ...

Either way, I've just migrated my site to Drupal 8 using Vue.js to render the content. Most of the data is served using the excellent JSON API module, some are served using simple custom controllers like the front page content, search results, 404, 403 and the Vue.js index file. It's been an interesting experience already, and I'm still learning a lot about this framework which, so far, is really refreshing and very easy to work with. I even have a fancy progress bar working! But also handy edit links when I'm browsing my own site as an authenticated user with the right permissions.

I also took the liberty to clean up the content from the last 10 years. The oldest page is actually from 2007 somewhere, how time flies! A lot of blog posts have been unpublished as they weren't really relevant anymore, or simply boring. Comments (and the ability to post one) will come back in the next couple of weeks too; along with updates to the styling as well to make sure this is all readable on most devices :)

Update february 9: you can post comments again!


De splitsing

Last year I spent a lot of evenings writing a script for a play I had in mind. And finally it's there, the first production is coming the first weekend of december. You are all invited! If you don't understand dutch, make your own story while watching, who knows we come up with an idea for another play :) More information and tickets reservation are available on okermaan.be

De splitsing

Configuration split: deploy subsets of configuration in Drupal 8

From the Configuration split project page: "The Drupal 8 configuration management works best when importing and exporting the whole set of the sites configuration. However, sometimes developers like to opt out of the robustness of CMI and have a super-set of configuration active on their development machine and deploy only a subset. The canonical example for this is to have the devel module installed or having a few block placements or views in the development environment and then not export them into the set of configuration to be deployed, yet still being able to share the development configuration with colleagues."

This small utility module for Drupal 8 allows you to exactly achieve this use case (and many others). I will cover two common scenarios, explaining how to configure your site and which commands you need to run:

  1. Have (development/ui) modules installed on your dev environment, uninstalled on production
  2. Have modules installed on your production environment, but not on development

I've also recorded a screencast in which I configure and run all the commands explained underneath. The best way in the end is to play around with the module itself of course.

Configuration split 101

Configuration split exposes a configuration entity which controls what you want to split off. Currently you can

  • blacklist modules: any configuration that this module owns will automatically be blacklisted too.
  • blacklist configuration: settings or configuration entities. These will be removed from the active sync directory.
  • graylist configuration: settings or configuration entities. These will not be removed if they are in the active sync directory, but also not exported if they are not there yet. I won't cover this functionality in this article post, that will be for another time.

After you configured one or more configurations split entities, you can use the drush commands to export and import configuration, based on one or more of those config entities. When it comes to exporting configuration, you can not use the existing drush core command (config-export / cex). Using drush config-import (cim) or the UI to import may still be used, but this depends on your setup. Drupal Console commands are under revision. Important: since feb 2017, due to https://www.drupal.org/node/2855319 and https://github.com/drush-ops/drush/pull/2471, you can use the standard drush commands without any problems!

Each config split entity defines the directory in which the splitted configuration will live in case there are some. Not all modules define configuration, so there's a possibility that this directory is empty after you do an export.

An important technical aspect is that the module does not interfere with the active configuration but instead filters on the import/export pipeline. What simple happens is this: just before the actual writing, it will check the configuration and remove any entries that you don't want to be there. Then your active configuration is written away. Config that doesn't belong into the active configuration will be moved to separate directory. On import, the opposite happens: it will merge settings back in and set modules to the installed state just before core configuration then starts importing.

Last, but not least: you can swap the config.storage.sync service so that the synchronize screen will use the config split config entities for importing. More information is in the README file and in the video.

Scenario 1: modules installed on dev, not on production

Step 1

After you installed the module, go to 'admin/config/development/configuration/config-split' and click on 'Add configuration split'. Naming is important, so we'll enter 'Dev split' as the name for this configuration. We'll also create a directory called 'sync-dev-split'. Now toggle the modules that you don't want to have installed on production. In this case, we're going to blacklist Devel, Devel Kint, Field UI, Views UI and database logging. Additionally, also toggle system.menu.devel. This configuration is owned by the system module, so there's no dependency on the devel module. There's no problem having this config on your production site though, so it's not required to blacklist it.

Optionally, you can also blacklist the configuration split module because it doesn't necessarily have to be installed on production. We're not doing that here, because in the second scenario, we're building further on this one and we want to have it installed on the production environment as well.

Step 2

Go to your command line interface and run following command:

swentel@dev:/home/drupal/drupal-core$ drush csex --split=dev_split

The split option is not required, but when starting to work with the module, it helps you to know which config split will be used for this command. It's possible to override the status of any config split config entity so that eventually, you can omit the split option. Sadly enough, you don't get any feedback (yet), but after the command has run you should see various files in your sync-dev-split directory:

swentel@dev:/home/drupal/drupal-core$ ls sync-dev-split/
dblog.settings.yml  devel.settings.yml  field_ui.settings.yml  system.menu.devel.yml

Step 3

You can now commit your active sync and go to production. You don't necessarily have to put the sync-dev-live directory in your version control because production doesn't need to know about these files at all. Once you have pulled on production, go to the synchronize screen to verify what is staged. You will see that the setting files will be removed and core.extension will be changed uninstalling the development modules and installing configuration split. Since we only have one config split configuration, you can hit 'Import all' here or run drush config-import, (which is the drush core command). Note that if you don't use the UI, you can blacklist the configuration manager module as well.

You might wonder why we don't use the 'config-split-import' command from the module itself: this would import the active sync directory and also include the modules and settings again that we have blacklisted. And that's not what we want. This seems confusing at first, but ultimately, if this is your setup, you just keep on using the core / drush commands to import your staged configuration on production.

# This command can be used now, but not anymore further on when we will add scenario 2.
swentel@live:/home/drupal/drupal-core$ drush cim

Scenario 2: modules installed on production, not on dev

Step 1

This scenario builds further on the first one. Config split is now installed on our live website. Create a new config split configuration called 'Live split'. You will need a new directory for this second config split, so make sure it's there. On production we still want to log events and for that we're going to use the syslog module. Install the module, so that we can now blacklist it for the live split configuration.

Step 2

Go to your command line interface and run following commands:

swentel@live:/home/drupal/drupal-core$ drush csex --split=live_split

Again, no feedback here, but after the command has run you should see the syslog settings file in your sync-live-split directory:

swentel@live:/home/drupal/drupal-core$ ls sync-live-split/

Doing exports and imports on dev or live

From now on, if you want to import new settings whether it's on your dev or live environment, you can not use the drush core commands anymore. Use following commands:

# to export on your dev environment
swentel@dev:/home/drupal/drupal-core$ drush csex --split=dev_split
# to import on your dev environment
swentel@dev:/home/drupal/drupal-core$ drush csim --split=dev_split

# to export on your live environment
swentel@live:/home/drupal/drupal-core$ drush csex --split=live_split
# to import on your live environment
swentel@live:/home/drupal/drupal-core$ drush csim --split=live_split

The future

Please join us in the issue queue because there's still some work:

  • Optimize the user interface and allow wildcards
  • Add confirmation and feedback in the drush commands

Ultimately, this functionality should live in core. A core issue to discuss this is at https://www.drupal.org/node/2830300.

Stop telling users that another user has modified the content in Drupal 8

Every Drupal developer knows the following error message (maybe some by heart): The content has been modified by another user, changes cannot be saved. In Drupal 8 the message is even a bit longer: The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved. While this inbuilt mechanism is very useful to preserve data integrity, the only way to get rid of the message is to reload the form and then redo the changes you want to make. This can be (or should I say 'is') very frustrating for users, especially when they have no idea why this is happening. In an environment where multiple users modify the same content, there are solutions like the Content locking module to get overcome this nagging problem. But what if your content changes a lot by backend calls ?

On a big project I'm currently working on, Musescore.com (D6 to D8), members can upload their scores to the website. On save, the file is send to Amazon where it will be processed so you can play and listen to the music in your browser. Depending on the length of a score, the processing might take a couple of minutes before it's available. In the meantime, you can edit the score because the user might want to update the title, body content, or add some new tags. While the edit form is open, the backend might be pinging back to our application notifying the score is now ready for playing and will update field values, thus saving the node. At this very moment, the changed time has been updated to the future, so when the user wants to save new values, Drupal will complain. This is just a simple example, in reality, the backend workers might be pinging a couple of times back on several occasions doing various operations and updating field values. And ironically, the user doesn't even have any permission to update one or more of these properties on the form itself. If you have ever uploaded a video to YouTube, you know that while your video is processing you can happily update your content and tags without any problem at all. That's what we want here too.

In Drupal 8, validating an entity is now decoupled from form validation. More information can be found on the Entity Validation API handbook and how they integrate with Symfony. Now, the validation plugin responsible for that message lives in EntityChangedConstraint and EntityChangedConstraintValidator. Since they are plugins, we can easily swap out the class and depending on our needs only add the violation when we really want to. What we also want is to preserve values of fields that might have been updated by a previous operation, in our case a backend call pinging back to tell us that the score is now ready for playing. Are you ready ? Here goes!

Step 1. Swap the class

All plugin managers in Core (any plugin manager should do that!) allow you to alter the definitions, so let's change the class to our own custom class.

 * Implements hook_validation_constraint_alter().
function project_validation_constraint_alter(array &$definitions) {
  if (isset(
$definitions['EntityChanged'])) {
$definitions['EntityChanged']['class'] = 'Drupal\project\Plugin\Validation\Constraint\CustomEntityChangedConstraint';

For the actual class itself, you can copy the original one, but without the annotation. The constraint plugin manager doesn't need to know about an additional new one (unless you want it to of course).

namespace Drupal\project\Plugin\Validation\Constraint;


 * Custom implementation of the validation constraint for the entity changed timestamp.
class CustomEntityChangedConstraint extends Constraint {
$message = 'The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved. In case you still see this, then you are really unlucky this time!';

Step 2: alter the node form

We want to be able to know that a validation of an entity is happening when an actual form is submitted. For this, we're adding a hidden field which stores a token based on the node id which we can then use later.

 * Implements hook_form_BASE_FORM_ID_alter() for \Drupal\node\NodeForm.
function project_form_node_form_alter(&$form, &$form_state) {
/** @var \Drupal\Node\NodeInterface $node */
$node = $form_state->getFormObject()->getEntity();
  if (!
$node->isNew() && $node->bundle() == 'your_bundle' && $node->getOwnerId() == \Drupal::currentUser()->id()) {
$form['web_submission'] = [
'#type' => 'hidden',
'#value' => \Drupal::csrfToken()->get($node->id()),

Step 3: Validating the entity and storing an id for later

We're getting to the tricky part. Not adding a violation is easy, but the entity that comes inside the constraint can't be changed. The reason is that ContentEntityForm rebuilts the entity when it comes in the submission phase, which means that if you would make any changes to the entity during validation, they would be lost. And it's a good idea anyway as other constraints might add violations which are necessary. To come around that, our constraint, in case the changed time is in the past, will verify if there is a valid token and call a function to store the id of the node in a static variable which can be picked up later.

namespace Drupal\project\Plugin\Validation\Constraint;


 * Validates the EntityChanged constraint.
class CustomEntityChangedConstraintValidator extends ConstraintValidator {

   * {@inheritdoc}
public function validate($entity, Constraint $constraint) {
    if (isset(
$entity)) {
/** @var \Drupal\Core\Entity\EntityInterface $entity */
if (!$entity->isNew()) {
$saved_entity = \Drupal::entityManager()->getStorage($entity->getEntityTypeId())->loadUnchanged($entity->id());
// A change to any other translation must add a violation to the current
        // translation because there might be untranslatable shared fields.
if ($saved_entity && $saved_entity->getChangedTimeAcrossTranslations() > $entity->getChangedTimeAcrossTranslations()) {
$add_violation = TRUE;
          if (
$entity->getEntityTypeId() == 'node' && $entity->bundle() == 'your_bundle' &&
$this->isValidWebsubmission($entity->id())) {
$add_violation = FALSE;

// Store this id.
project_preserve_values_from_original_entity($entity->id(), TRUE);

// Add the violation if necessary.
if ($add_violation) {

   * Validate the web submission.
   * @param $value
   *   The value.
   * @see project_form_node_form_alter().
   * @return bool
public function isValidWebsubmission($value) {
    if (!empty(\
Drupal::request()->get('web_submission'))) {
      return \
Drupal::csrfToken()->validate(\Drupal::request()->get('web_submission'), $value);



 * Function which holds a static array with ids of entities which need to
 * preserve values from the original entity.
 * @param $id
 *   The entity id.
 * @param bool $set
 *   Whether to store the id or not.
 * @return bool
 *   TRUE if id is set in the $ids array or not.
function project_preserve_values_from_original_entity($id, $set = FALSE) {
$ids = [];

  if (
$set && !isset($ids[$id])) {
$ids[$id] = TRUE;

  return isset(
$ids[$id]) ? TRUE : FALSE;

Step 4: copy over values from the original entity

So we now passed validation, even if the submitted changed timestamp is in the past of the last saved version of this node. Now we need to copy over values that might have been changed by another process that we want to preserve. In hook_node_presave() we can call project_preserve_values_from_original_entity() to ask if this entity is eligible for this operation. If so, we can just do our thing and happily copy those values, while keeping the fields that the user has changed in tact.

 * Implements hook_ENTITY_TYPE_presave().
function project_node_presave(NodeInterface $node) {
  if (!
$node->isNew() && isset($node->original) && $node->bundle() == 'your_bundle' && project_preserve_values_from_original_entity($node->id())) {
$node->set('your_field', $node->original->get('your_field')->value);
// Do many more copies here.

A happy user!

Not only the user is happy: backends can update whenever they want and customer support does not have to explain anymore where this annoying user facing message is coming from.

Taking a (Drupal 8) website offline using AppCache

A native mobile application which can cache the data locally is a way to make content available offline. However, not everyone has the time and/or money to create a dedicated app, and frankly, it's not always an additional asset. What if browsers could work without network connection but still serve content: Application Cache and/or Service Workers to the rescue!

For Frontend United 2016, Mathieu and I experimented to see how far we could take AppCache and make the sessions, speakers and some additional content available offline using data from within the Drupal site. There are a couple of pitfalls when implementing this, of which some are nasty (see the list apart link at the bottom for more information). Comes in Drupal which adds another layer of complexity, with its dynamic nature of content and themes. Javascript and css aggregation is also extremely tricky to get right. So after trial and error and a lot of reading, we came up with the following concept:

  1. Only add the manifest attribute to all "offline" pages which are completely separate from "online pages", even though they might serve the same content. In other words, you create a sandboxed version of some content of your site which can live on its own. Another technique is a hidden iframe which loads a page which contains the html tag with the manifest attribute. You can embed this iframe on any page you like. This gives you the option to create a page where you link to as an opt-in to get a site offline. Both techniques give us full control and no side affects so that when network is available the site works normally.
  2. You define the pages which you want to store in the cache. They are served by Drupal, but on a different route than the original (e.g. node/1 becomes offline/node/1) and use different templates. These are twig templates so you can override the defaults to your own needs. Other information like stylesheet and javascript files can be configured too to be included.
  3. The manifest thus contains everything that we need to create the offline version when your device has no network connection. In our case, it contains the list of speakers and sessions, content pages and some assets like javascript, stylesheet, logo and images.

Note: currently, the website isn't app-cache enabled anymore, preparing for 2017.

Offline in the browser or on the homescreen

Go to the Offline homepage of Frontend United and wait until the 'The content is now available offline!' message appears, which means you just downloaded 672 kb of data - it is really really small, surprising no? Now switch off your network connection and reload the browser: still there! Click around and you'll be able to check the offline version at any time. If you're on a mobile device, the experience can be even sweeter: you can add this page to your homescreen, making it available as an 'app'. On iOS, you need to open the app once while still being connected to the network. We really do hope safari/iOS fixes this behavior since this is not necessary on Android. After that, turn off your network and launch the app again. Oh, and it works on a watch too if you have a browser on it. If that isn't cool, we don't know what else is! We have a little video to show you how it looks like. Watch (pun intended) and enjoy! Oh, in case we make changes to the pages, you will see a different notification telling you that the content has been updated - if your device has network of course.

Drupal integration

We've created a new project on Drupal.org, called Offline App, available for Drupal 8. The project contains the necessary code and routes for generating the appcache, iframe, pages (nodes and views) and settings to manipulate the manifest content. 3 new regions are exposed in which you can place the content for offline use. Those regions are used in offline-app-page.html.twig - but any region is available if you want to customize. Two additional view modes are created for content types and the read more link can be made available in the 'Offline teaser' mode. Formatters are available for long texts to strip internal links and certain tags (e.g. embed and iframe) and for images that will make sure that 'Link to content' is pointing to the 'Offline path'. Last, but not least, an 'Offline' Views display is available for creating lists. We're still in the process in making everything even more flexible and less error-prone when configuring the application. However, the code that is currently available, is used as is on the Fronted United website right now.

This module does not pretend to be the ultimate solution for offline content, see it as an example to quickly expose a manifest containing URL's from an existing Drupal installation for an offline version of your website. Other Drupal projects are available trying to integrate with AppCache or Service workers, however, some are unsupported or in a very premature state, apart from https://www.drupal.org/project/pwa. Note that I've been in contact with Théodore already and we'll see how we combine our efforts for coming up with one single solution instead of having multiple ones.

What about service workers ?

Not all browsers support the API yet. Even though AppCache is marked deprecated, we wanted to make sure everyone could have the same offline experience. However, we'll start adding support for service workers soon using the same concept.

We're also planning to start experimenting with delivering personal content as well, since that's also possible, yet a little trickier.



This is not a post about caching for applications, but a different kind, the kind where people all over the world hide artifacts and you must search and log them. Welcome to the world of Geocaching!

I've been introduced to  this recently and must admit I like the concept very much. It brings you to places you'll probably never end up otherwise, even in your own neighbourhood, and just recently while visiting Berlin. Anytime I see someone strolling around at the same spot for a while, I know what this person is doing now! You can also hide your own caches, so that's what I did. My first geocache called 'De dokwerker' has been published. So if you're ever in Gent (Belgium), go and find it! :)


Drupal 8 logo Pepper's ghost illusion

Turning your smartphone into a hologram projector has become fairly popular nowadays, although it's technically called Pepper's ghost to create the illusion. Anyway, I had to try it myself too, right ? Building the pyramid isn't that hard and the first try-out is pretty cool to be honest.

So with that going fine, I opened up Blender to create my own animation just for fun. I took the Drupal 8 logo, ripped it apart and let it build itself up again. I quickly wrote an Android application that would just play 4 videos on a surface; and each video in a different rotation. The app is attached at the end if you want to try it out yourself (works well on Motorala, with Lollipop). If all goes well, I'll make it possible to select your own video and make the code public on GitHub. But for now, just enjoy the video. Or start building your own pyramid, it's really fun.

Attachment Size
Android D8 Pepper's Ghost 1.68 MB

Mobile support for chosen

Note: this patch is quite old, so be aware there might be errors on the latest version on chosen.

Chosen is a jQuery plugin which makes select boxes more user-friendly. Currently, support is disabled on mobile devices for various reasons. Enabling the support requires a hack in the source code for now in the AbstractChosen.browser_is_supported function to return true when the userAgent finds Android or iPhone/iPad. You can download the patched version at the bottom (or detail) of the article. The current version is 1.1.0

Chosen will now be enabled on mobile, but touching the element will not work nicely since the plugin doesn't listen for the touchstart event to trigger the functionality. Opening the select box requires touching the element for at least a couple of seconds, which isn't really user-friendly. Luckily, there's an easy way to get around this by writing a couple lines of javascript:

    // Chosen touch support.
    if ($('.chosen-container').length > 0) {
      $('.chosen-container').on('touchstart', function(e){
        e.stopPropagation(); e.preventDefault();
        // Trigger the mousedown event.

That's it, fully functional chosen support on mobile devices!

Attachment Size
chosen.jquery.min_.js.txt 26.33 KB

Crowdsourced translation of Gentse Feesten application

July is approaching fast, which means that Ghent is getting ready for yet another 10 days of celebrating. Just like last year, we'll release our app to make sure you know your way around. Some events are public already of which one is my top favorite so far: Madensuyu is playing on the Boomtown festival on july 22. You will not regret attending that gig!

Last year, the Android version was only available in one language, namely Dutch. This year, we want try todo a little better and get as many languages supported as possible: I'm thinking of English, Spanish, German, Italian, French to begin with. But don't hesitate to translate in any language :) Another plus would be translating everything to Gentsch, the local dialect from Gent. So, I'm looking for people who are willing to translate the strings which should only take maybe 30 minutes of your time. You'll also be credited on the Google Play store, inside the app and in code. One remark: you can leave the locations as is, there's no value in translating those.

I've uploaded all strings using Google's Translator Toolkit which has a friendly interface hiding away the XML. If you have a google account, send me your e-mail through the contact form or just let me know in case I have your contact details already. If you feel comfortable hand editing XML files directly, you can also download the file underneath and send it back to me.

Current status

  • Dutch - needs some update
  • English - needs revision
  • French - done - Thank you Katrien!
  • German - done - Thank you Katrien!
  • Spanish - done - thank you Ruben Teijeiro!
  • Polish - done - thank you Jakub Piasecki!
  • Hungarian - done - thanks you Zsófi Major!
  • German - ongoing

Not a translator, but a die-hard coder ?

The code is still free, so you're welcome to hack along on GitHub.


I usually don't post a lot of pictures in my posts, but this is one is just to cute. I started at Krimson this monday and brought my cookiemonster doll with me - and my arcade machine too, so anyone near the Dampoort, call me and we'll have a game of Puzzle Bobble :) One of the companies across the building also had his teddy bear with him and took the adorable picture underneath. Best Bears Forever.