Skip to main content
Penyaskito Blog

Main navigation

  • Home
Language switcher
  • English
  • Español
User account menu
  • Log in

Breadcrumb

  1. Home

Drupal planet

By penyaskito, 22 July, 2023
Image of some Dashboards, by Cristina Chumillas

Introducing The Dashboard Initiative

Last year, during DrupalCon Prague, Cristina approached me with an idea to include Dashboards in core. We met with Sascha, and since then a team emerged and we have been meeting more or less regularly defining how this would look like, and creating some proof of concepts.

What are we trying to solve?

When you log in to your Drupal site, you land on the /user page. There you can see how old your account on this site is. Useful, huh? Unless you have customized this behavior with contributed or custom modules, now you need to think about why you logged in, and go to wherever you can do what you wanted to do, which usually will take you several steps.

There's a big chance that this journey will start at the Content page, if you are a content manager or a content editor, but you might be a site builder, a site admin, or be responsible for other duties on your website. So we need a landing area where the content shown makes sense based on your recurring tasks. This is where customizable dashboards come into play.

Wearing multiple hats

Users often have multiple roles, or they might want to perform different tasks which might be unrelated to each other. The initial idea was to have a dedicated dashboard for each role.  That evolved into the idea that a user should have access to different dashboards, where groups of tasks and information might be grouped by their nature, not neccessarily by their role. You might wear your content manager hat one minute, and later you might need to perform some SEO duties.

So nothing should block you from having unlimited dashboards, and use the permissions system to give access to them to different roles.

Is this aligned with Drupal core goals?

Drupal Core Product Manager Lauri Eskola recently stated the three tracks were most efforts are concentrating in Drupal Core: 

  • Reduce the time it takes for site builders to become proficient with Drupal.

  • Empower site builders to deliver engaging editorial experiences.

  • Reduce the cost of keeping Drupal applications secure.

Adding dashboards helps with the second track, enabling site builders to make editors´ lives easier by providing customized dashboards based on their editors´ needs. There would be benefits to the other two tracks as well. Having clear journeys on some common tasks for different roles will help site builders to become proficient in Drupal; and can reduce the cost of keeping Drupal applications secure if we surface the need for security updates on the dashboard and make it easy to get up to date with security updates or news from the Drupal project and the Drupal Association.

How to get this into Drupal core

When new ideas go into core, usually they are introduced as Experimental modules, and that's also our aim. Similar to other existing admin tools (like the content listing admin page) in Drupal core, our approach is based on progressive enhancement. This translates to:

  • If you don't enable dashboard, you will be redirected to /user as of right now. Drupal core Standard profile cannot depend on an experimental module, so it won't be installed by default (for now).

  • We will ship Dashboard module with default dashboards for common tasks in core. 
  • If Layout Builder is also installed, you will be able to customize the layouts and blocks of those dashboards on the UI. 

How to get involved?

For now, we've been working in a sandbox, and we also have a GitHub project that contains an easy-to-evaluate site with some demo content, and triggers a Tugboat that we can use as demo or to manually test changes. Thanks to Tugboat.qa for providing that, and James for helping out setting it up!

If you want to help, we welcome you at the #dashboard channel on Drupal Slack.

Thanks to Gábor Hójtsy, Lauri Eskola and Cristina Chumillas for reviewing this blogpost.

Tags

  • Drupal
  • Drupal Core
  • Drupal planet
  • Dashboard Initiative
By penyaskito, 23 May, 2021

A Drupal JavaScript behavior for marking edited line items in the cart

There are lots of ways to learn about something, and everyone learns in a different way, having best results with different options: some people get better results with articles, others with videos, or others with examples. My favorite way is with examples. And when trying to learn how to achieve something with Drupal, my favorite examples are Drupal core itself.

If you need to get something done and not sure where to start, try to think: where have I seen that before?

You might even have an example in your codebase already. This article covers a real use-case I faced with Drupal Commerce, just simplifying it a bit for the sake of keeping it short. The user story is something alike: As a customer, I need to refresh the cart page instead of continuing checkout after editing the units of any order line, so I can see any better discounts that apply because of volume.  

So in terms of our form, this will mean: when in the Cart form page, disable the Checkout button when the user edits any quantity field, and enable the Update cart one.

Screenshot of the Shopping cart before editing it
Drupal commerce shopping cart form after applying some ugly styling with the purpose of having better differentiation between disabled and enabled buttons.

But for a better user experience, we want the user to notice which line item they modified. Where have I seen that before? In the locale translations interface.

If you enable the locale module, and add a second language to your site, you have an interface translation page where you can fill the translations of the interface strings.

The locale translation interface already mark edited translations, and that's in Drupal core
The locale translation interface UI already marks edited translations, and that's a valid example in Drupal core.

So we will look at the code and inspect how that's achieved. For me, the easiest way if you are not familiar with this piece of code is searching the UI strings or the url path of that page in my IDE, and dig until I find how that's working.

In this case, if you search for "Changes made in this table" you end up in web/core/modules/locale/locale.admin.js. JavaScript files are included via libraries, so if you search for the filename you will hit the drupal.locale.admin library defined at core/modules/locale/locale.libraries.yml. 

If you search now for that library, you end up finding some forms that include the library with:

$form['#attached']['library'][] = 'locale/drupal.locale.admin';

So that's everything involved in make this for the locale translation UI, now you need to mimic that.

Let's create our module. Fastest way is using drush, so I executed

ddev drush generate module-standard

And picked commerce_cart_forcerefresh as the name. From the next options, I only generated the libraries file, which I edited to look like:

commerce_cart_forcerefresh:
  js:
    js/commerce-cart-forcerefresh.js: {}
  css:
    component:
      css/commerce-cart-forcerefresh.css: {}
  dependencies:
    - core/jquery
    - core/drupal
    - core/drupal.form
    - core/jquery.once

 Our css will only be

form#views-form-commerce-cart-form-default-1 tr.changed {
  background: #ffb;
}

And our JavaScript:

(function ($, Drupal, drupalSettings) {
  'use strict';

  Drupal.behaviors.cartForceRefresh = {
    attach: function (context) {
      var $form = $('form#views-form-commerce-cart-form-default-1').once('cartItemDirty');
      var $updateSubmit = $('form#views-form-commerce-cart-form-default-1 #edit-submit')
        .prop('disabled', true);

      if ($form.length) {
        $form.one('formUpdated.cartItemDirty', 'table', function () {
          var $marker = $(Drupal.theme('cartItemChangedWarning')).hide();
          $(this).addClass('changed').before($marker);
          $marker.fadeIn('slow');
        });

        $form.on('formUpdated.cartItemDirty', 'tr', function () {
          var $row = $(this);
          var marker = Drupal.theme('cartItemChangedMarker');

          $row.addClass('changed');

          var $updateSubmit = $('form#views-form-commerce-cart-form-default-1 #edit-submit')
            .prop('disabled', false);
          var $checkoutSubmit = $('form#views-form-commerce-cart-form-default-1 #edit-checkout')
            .prop('disabled', true);

          if ($row.length) {
            $row.find('td:first-child .js-form-item').append(marker);
          }
        });
      }
    },
    detach: function (context, settings, trigger) {
      if (trigger === 'unload') {
        var $form = $('form#views-form-commerce-cart-form-default-1').removeOnce('cartItemDirty');
        if ($form.length) {
          $form.off('formUpdated.cartItemDirty');
        }
      }
    }
  };
  $.extend(Drupal.theme, {
    cartItemChangedMarker: function cartItemChangedMarker() {
      return '<abbr class="warning ajax-changed" title="' + Drupal.t('Changed') + '">*</abbr>';
    },
    cartItemChangedWarning: function cartItemChangedWarning() {
      return '<div class="clearfix messages messages--warning">' + 
        Drupal.theme('cartItemChangedMarker') + ' ' + 
        Drupal.t('Update the cart for the changes to take effect.') + '</div>';
    }
  });
})(jQuery, Drupal, drupalSettings);

Note that the form id is being used in some parts of our css and javascript, and that depends on the view id. Take that into account if you are not using the default view, you have multiple order types, etc. In my real case, I was able to use a class that my theme was adding to that form.  

The last thing is ensuring this behavior is added to our form. The easiest for this demo is using hook_form_FORMID_alter(), which again depends on the id of the view. Our commerce_cart_forcerefresh.module looks like:

<?php

use Drupal\Core\Form\FormStateInterface;

/**
 * @file
 * Primary module hooks for commerce_cart_forcerefresh module.
 */
function commerce_cart_forcerefresh_form_views_form_commerce_cart_form_default_1_alter(&$form, FormStateInterface $form_state, $form_id) {
  $form['#attached']['library'][] = 'commerce_cart_forcerefresh/commerce_cart_forcerefresh';
}

And that produces the desired result:

Final result with order line items cues when they were edited.
The Checkout button is disabled, the Update cart button is enabled, a cue is added on the line item, and a warning on the top of the page.

Hope this helps! Full source code at https://github.com/penyaskito/drupal_commerce_cart_forcerefresh

You can read more about JavaScript behaviors in this guide: How to integrate JavaScript in Drupal 8/9, by davidjguru. 

Tags

  • Drupal Commerce
  • JavaScript
  • Drupal behaviors
  • Drupal planet
By penyaskito, 1 May, 2021
Screenshot of XHGui, showing information from a WordPress request

Profiling Drupal, WordPress or any php application with DDEV, XHProf and XHGui

I've been a very late adopter of Docker containers. Probably because most of my work in the last years was not dependent on the PHP version, so I used the same PHP version and MySQL/MariaDB as RDBMS every time. For a server I managed I played with LXC as a container system, and served quite well, but I didn't really need to touch it often, and my friend Andrés helped most of the times. Docker seemed confusing to me and, hold my beer, I thought it wouldn't stay, so I didn't really want to learn it. You might say now that my career as seer is over.

Not easily but I got to understand it, and really embraced and loved tools like docker-compose on top of docker. And when I had to work on a Drupal site using Postgres, my best bet was DDEV, abstracting docker-compose complexities but with all the freedom to customize it for your needs.  So that's my platform of choice most of the times in terms of local environments now. And lucky us, it's open source.

And the implicit non-enforced well-citizen contract of opensource is that you have to contribute back if you get the chance to improve it*. In minor contributions I've done before I found the DDEV community really encouraging and welcoming, thanks Randy! So lately I was in the need of  detecting performance bottlenecks in a site, and I've been working on integrating the provided XHProf with XHGui into DDEV. I recently contributed the recipe so you can use it too in less than 5 minutes! See it here: XHGui integration in DDEV documentation. 

* Needed clarification: not saying that you have to give back, not everyone has the same privilege than I do. But *if you did accomplish it already, try to contribute it*. 

If you enable XHProf, already provided with DDEV, it already monitors your application, but you need to collect that profiling report and send it somewhere where you can explore it in a friendly way. That's where perftools/php-profiler comes into play. It has the ability to manage which data you want to profile, and send it to XHGui. XHGui will run on a separate container, will receive the data profiled, store the data in a mongodb database in another container, and render it to you on the browser, where you can see several listings, graphs and images that will help you understand what's going on on your application. You can also compare different requests to a same page, so you see how the performance evolves too. Interpretation of that data is a complex topic on its own, so I won't go deeper here.

So the good news is that you can integrate that now into any project you have with DDEV, in less than 5 minutes. The docs are specific for Drupal 8+ (a composer project) and WordPress (a non-composer project, kudos to roots/bedrock for changing that), so that probably covers examples for any PHP application out there.  Hope this helps!

As a bonus track, I also worked on a DDEV command for enabling/disabling xhprof without having to restart  your project, type a very long command or ssh into the container. That's on that DDEV-contrib page too, but it's probably going to be accepted upstream in DDEV soon too.

Thanks Randy "rfay" Fay, Andrey "andypost" Postnikov and Mateu "e0ipso" Aguiló for the feedback, help and inspiration on working on this! 

Tags

  • docker
  • docker-compose
  • php
  • XHProf
  • DDEV
  • XHGui
  • WordPress
  • Drupal
  • Drupal planet
By penyaskito, 26 April, 2021
Selenium IDE was used for deleting spam

A personal site upgrade from Drupal 7 to Drupal 9: some migration tips

Welcome to just another blog with complex infrastructure for just a few visits. It was about time.

This post was intended to be a detailed guide about how I used Drupal migrate tools for upgrading from D7 to D9, but, being fair, it was quite straight forward, so I will just share some tips. Take into account that my last attempt to blog lost its pace quite fast, so I could have been migrating it manually and probably had saved some time. But as someone who contributed to the migrate initiative and the Drupal to Drupal migrations, it's quite a shame that I never actually used it in a real project. 

My source site was a D7 site using the blog module, which has been removed from core in D8. I could just use the contributed version linked above, but I wanted to use an "article" content type from now on. Instead of creating a custom migration, I thought I would give a chance to the Drupal Migrate UI.

So even if we are not using a custom migration and we don't have to think about content mapping and such, there is basic cue that you need to take into account: upgrading your site is the best time for taking the trash out.

Upgrading your site is the best time for taking the trash out. E.g. delete spam.

So first steps would be removing spam. I had used a free Mollom account in the past (RIP, thanks for your service), but I didn't rethink my spam prevention strategy when it was discontinued. Thousands of comments and registered users were in my database, and that was slowing down a lot my first attempts to migrate. I hacked Drupal 7 so the comment and user forms filtered data that I was sure it was spam by altering their queries, and allowing me to delete more content at once. Then I recorded with Selenium IDE some automation for deleting those, as even if scripting or some SQL queries might be faster, sometimes I just enjoy watching the browser do stuff for me.

After that, the process was mostly straightforward. Install Drupal Migrate UI and its dependencies, follow the process and complete. But your content after the migration needs to be audited, so you didn't miss content or config in the process. Specially since the Drupal upgrade process doesn't support rollbacks yet (don't worry if you have your custom migrations, in that case it's supported).

Audit your content after your migration. You might have missed something.

In my case, I had to restart again as my multilingual content was not being migrated. I quickly figured out that the settings must be in place before your content is migrated. Install the multilingual modules if that's your case, and review your translation strategy and the related content translation settings before migrating, e.g. content types and taxonomies. 

If you have multilingual content, review your strategy and according settings before upgrading.

In my case, I had used tags in Spanish and English, and those were not mapped to each other when they were the same concept in the source site. From now on, and thanks to the vast improvements in multilingual support from D8 on, I wanted to change that. I did those mappings by hand, and here we are. 

I picked the new experimental Olivero theme and the new experimental Claro admin theme because they look great, but also because testing them is a good way of helping out them move forward.

I leave for another moment my choice of contrib modules for the site, some issues I needed to workaround by a small custom module, and my infrastructure setup based in docker containers. I promise that I will make that happen before another ton of years pass.

Tags

  • multilingual
  • migration
  • hello-world
  • Drupal
  • Drupal planet
By penyaskito, 10 November, 2012

Drupalcamp Spain 2012

On October 20th and 21st took place in Madrid the 3rd edition of the biggest Drupal event in Spain, Drupalcamp Spain 2012. On Saturday I participated on a panel with Pedro Cambra, Ramon Vilar, Juampy Novillo and José Reyero about how to contribute to Drupal, with modules, themes, translations, documentation, marketing, events... The slides we used as guide are available on Slideshare:

Contribuir en Drupal: Por dónde empiezo? from Christian Lopez

On Sunday, I made a session where I tried to explain the changes in multilanguage between Drupal 7 and Drupal 8, that will be a reality hopefully on August 2013. The slides are also available in Slideshare:

Multilenguaje en Drupal 8 from Christian Lopez

The videos of every session will be available soon at the Vimeo channel of the Spanish Drupal Association.

That weekend took place the first Assembly of the Spanish Drupal Association, where I was nominated as a Board Member. I hope to contribute with them spreading Drupal use in Spain.

And after a very busy weekend, a plane to Frankfurt. But that's part of another story.

Post image: Some rights reserved by Pedro Lozano

Tags

  • Drupal
  • Drupalcamp
  • Drupalcamp Spain
  • AED
  • multilingual
  • contributing
  • sessions
  • slideshare
  • Drupal planet
Drupal planet

Monthly archive

  • April 2025 (1)
  • July 2023 (1)
  • December 2021 (1)
  • May 2021 (2)
  • April 2021 (1)
  • September 2014 (1)
  • November 2012 (1)
  • September 2012 (2)
  • August 2012 (3)
  • June 2012 (6)

Recent content

Optimizing PhpStorm when it's slow or hangs
1 month 2 weeks ago
Introducing The Dashboard Initiative
1 year 10 months ago
Rewriting history on a git repository for editing author and email on past commits
3 years 4 months ago

Recent comments

I would recommend taking a…
1 year 9 months ago
This looks interesting
1 year 9 months ago
Thanks for the comment
4 years ago

Blogs I follow

  • Mateu Aguiló "e0ipso"
  • Gábor Hojtsy
  • Pedro Cambra
  • The Russian Lullaby, davidjguru
  • Can It Be All So Simple
  • Maria Arias de Reyna "Délawen"
  • Matt Glaman
  • Daniel Wehner
  • Jacob Rockowitz
  • Wim Leers
  • Dries Buytaert
Syndicate

Footer

  • Drupal.org
  • LinkedIn
  • GitHub
  • Mastodon
  • Twitter
Powered by Drupal