Pasar al contenido principal
Penyaskito Blog

Main navigation

  • Home
Language switcher
  • English
  • Español
User account menu
  • Iniciar sesión

Sobrescribir enlaces de ayuda a la navegación

  1. Inicio

Drupal planet

Por penyaskito, 23 Mayo, 2021

Drupal JavaScript behavior para destacar líneas de pedido editadas en el carrito de Drupal Commerce

Hay muchas formas de aprender algo, y a cada persona le funcionan mejor distintas opciones: algunos prefieren leer guías o artículos, otros prefieren videos, y otros prefieren ejemplos. A mi me funciona mejor con ejemplos, así que cuando necesito aprender algo nuevo en Drupal, intento recurrir al propio código de Drupal core.

Si necesitas conseguir algo con código y no tienes claro por dónde empezar, piensa: ¿dónde he visto esto yo antes?

Puede que encuentres un ejemplo en tu propio código. Este artículo cubre un caso de uso real que he tenido con Drupal Commerce, simplificado un poco por la brevedad del artículo. La historia de usuario sería algo como: Como cliente, quiero refrescar el carrito en lugar de continuar con la compra después de editar la cantidad de cualquier línea de pedido, para poder ver si hay descuentos mejores que apliquen por cuestiones de volumen de compras.  

Para nuestro formulario, esto significa: cuando esté en la página de Carrito, deshabilita el botón de Checkout cuando el usuario edite la cantidad, y habilita el botón de Actualizar carrito.

Screenshot of the Shopping cart before editing it
El formulario del carrito de Drupal commerce después de aplicar estilos horribles con el propósito de mostrar mejor las diferencias entre botones habilitados y deshabilitados.

Pero para una mejor experiencia de usuario, queremos marcar para el usuario qué líneas de pedido ha editado. ¿Dónde hemos visto esto antes? En el formulario de traducción de la interfaz.

Si habilitas el módulo locale, y añades algún otro idioma en tu web, tendrás la página de traducción de interfaz

The locale translation interface already mark edited translations, and that's in Drupal core
La UI de traducción de interfaz ya marca como editadas las traducciones que editamos, y es un ejemplo perfecto en el core de Drupal.

Así que vamos a mirar el código e inspeccionar cómo hacerlo. Para mí, la mejor forma si no estás familiarizado con ese código es buscar las cadenas de la interfaz afectadas, o la ruta de esa página en mi IDE, e indagar hasta que encuentro cómo funciona.

En este caso, si buscas "Changes made in this table" encuentras core/modules/locale/locale.admin.js. Los archivos JavaScript se incluyen vía libraries, por lo que buscando ese nombre de archivo te chocarás con la biblioteca drupal.locale.admin definida en core/modules/locale/locale.libraries.yml. 

Si buscas ahora esa biblioteca, terminas en varios formularios que la incluyen con:

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

Pues esto es todo lo que está involucrado en hacer esta interfaz de traducciones, ahora sólo tienes que imitarlo.

Vamos a crear un módulo. El modo más rápido es con drush, ejecutando

ddev drush generate module-standard

He seleccionado commerce_cart_forcerefresh como nombre. Para el resto de opciones, he seleccionado generar el archivo libraries, que quedará así:

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

 En nuestro css:

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

Y en nuestro 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);

Ojo que el id del formulario se usa en varias partes de nuestro css y javascript, y que depende del id de la view de Drupal. Tenlo en cuenta si no usas la vista por defecto, si tienes diferentes tipos de pedido, etc. En mi caso real, mi tema ya proveía una clase css en ese formulario que podía usar en lugar del id.  

Lo último es asegurar que el behavior se añade a nuestro formulario. La forma más sencilla para esta demo es implementar hook_form_FORMID_alter(), que también depende del id de la vista. Nuestro archivo commerce_cart_forcerefresh.module es:

<?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';
}

Y produce el resultado esperado:

Final result with order line items cues when they were edited.
El botón de Checkout se deshabilita, el botón de Actualizar carrito se habilita, se añade un indicador a cada línea de pedido editada, y se muestra un aviso en la parte superior de la página.

¡Espero que sirva! Todo el código lo tienes en https://github.com/penyaskito/drupal_commerce_cart_forcerefresh

Puedes leer más sobre behaviors de JavaScript en esta guía: JavaScript & Drupal 101 TUTORIAL HANDBOOK TOTAL MAX POWER 2000 (amor al primer behavior), de davidjguru. 

Tags

  • Drupal Commerce
  • JavaScript
  • Drupal behaviors
  • Drupal planet
Por penyaskito, 2 Mayo, 2021
Captura de pantalla de XHGui, mostrando información de una petición a WordPress

Analizando el rendimiento de Drupal, WordPress o cualquier aplicación php con DDEV, XHProf y XHGui

He llegado a los contenedores en Docker muy tarde. Probablemente debido a que mi trabajo en los últimos años no dependía de versiones de PHP, siempre usaba la última disponible, y siempre usé MySQL/MariaDB como motor de base de datos. Para un servidor que gestionaba usé LXC para tener aplicaciones en contenedores, y me funcionó bien, pero nunca tuve que tocarlo a menudo, y mi amigo Andrés me ayudó la mayoría de las veces. Docker me parecía confuso y, sujétame el cubata, pensé que era una moda, así que no quise aprenderlo. Así soy yo, todo un visionario, saludo en la escalera siempre y soy amigo de mis amigues.

Con alguna dificultad, pero llegué a comprenderlo, y desde entonces he adoptado y me apasionaron herramientas como docker-compose. Y cuando tuve que trabajar en un proyecto con Drupal usando Postgres, mi mejor amigo fue DDEV, abstrayendo las complejidades de docker-compose pero manteniendo la libertad para personalizar el entorno a tus necesidades. Así que es mi elección como plataforma de desarrollo la mayoría de las veces. Y por fortuna, es software libre.

El contrato implícito no-forzado del software libre es que tienes que contribuir si tienes la oportunidad de mejorarlo*. En contribuciones menores que hice la comunidad de DDEV fue acogedora y entusiasta, gracias Randy! Así que últimamente necesitaba buscar cuellos de botella de rendimiento en una web, y he estado trabajando en integrar XHProf con XHGui en DDEV. He contribuido la receta, así que tú también puedes usarlo en menos de 5 minutos! Puedes verlo aquí (en inglés): XHGui integration in DDEV documentation. Si crees que es necesario traducirlo, déjame un comentario.

* Aclaración: no digo que tengas que contribuir, no todes tenemos el mismo privilegio que yo. Pero *si ya lo hiciste, intenta contribuirlo*. 

Si habilitas XHProf, ya proporcionado por DDEV, monitoriza tu aplicación, pero necesitas capturar esa información y enviarla a algún sistema que te haga sencillo explorarla y entenderla. Ahí interviene perftools/php-profiler. Tiene la capacidad de gestionar que datos necesitamos en el análisis, y enviarlos a XHGui. XHGui corre en un contenedor independiente, recibe los datos, los almacena en una base de datos mongodb en otro contenedor, y los renderiza en tú navegador, dónde podrás verlos en diferentes listados, grafos y gráficos que te permitirán entender mejor que está pasando en tu aplicación. Puedes también comparar distintas peticiones a una misma página y ver cómo evoluciona el rendimiento con el paso del tiempo. El análisis e interpretación de esos datos es un tema complejo en sí, así que no entraré en más profundidad en este artículo.

Así que las buenas noticias son que puedes integrar esto ahora en cualquier proyecto que uses con DDEV, en menos de 5 minutos. La documentación es específica para Drupal 8+ (que usa composer) y WordPress (que no usa composer, aunque puedes hacerlo gracias a roots/bedrock), por lo que cubre ejemplos para diversos tipos de aplicaciones PHP. ¡Espero que sirva!

Como bonus, también he escrito un comando para (des)activar xhprof en DDEV sin tener que reiniciar el proyecto, escribir un comando de quince líneas, o hacer ssh al contenedor. Está en la misma página de DDEV-contrib, pero probablemente sea aceptado en DDEV pronto.

¡Gracias a Randy "rfay" Fay, Andrey "andypost" Postnikov y Mateu "e0ipso" Aguiló por consejos, ayuda e inspiración para completar esto! 

Tags

  • php
  • XHProf
  • DDEV
  • XHGui
  • WordPress
  • Drupal
  • Drupal planet
Por penyaskito, 26 Abril, 2021
Selenium IDE se usó para borrar spam

Actualización sencilla de un blog personal de Drupal 7 a 9: algunos consejos para migrar

Bienvenida a otro blog con una infraestructura compleja para tener cuatro visitas. Ya era hora.

Este artículo estaba previsto que fuera una guía detallada de como usé las herramientas de migraciones de Drupal para actualizar de D7 a D9, pero, siendo sincero, fue bastante sencillo, así que me quedaré en un par de consejos. Ten en cuenta que mi último intento de escribir terminó bastante rápido, por lo que hubiera sido más rápido hacer la migración a mano. Pero como alguien que ha contribuido a la iniciativa de migrate y las migraciones de Drupal a Drupal, me daba penita que nunca lo he usado en un proyecto real. 

Mi sitio origen es un D7 con el módulo blog, que fue eliminado en D8 del core. Podría haber usado el módulo contribuído enlazado, pero quería sólo un tipo de contenido "artículo" de aquí en adelante. En lugar de crear una migración personalizada, quise darle una oportunidad a Drupal Migrate UI.

A pesar de no tener una migración personalizada y tener que pensar en cómo mapeamos el contenido para migrar, algo principal a tener en cuenta: la actualización es el mejor momento para hacer limpieza.

La actualización de tu web es el mejor momento para hacer limpieza de contenidos. P.ej. borrar spam.

Así que el primer paso fue borrar spam. Usé una cuenta gratuita de Mollom (DEP, gracias por tu ayuda), pero no repensé mi estrategia para prevención de spam cuando el servicio cerró. Miles de comentarios y usuarios registrados estaban en mi base de datos, y eso estaba realentizando mis primeros intentos de migración. Parcheé Drupal 7 para que los formularios de comentarios y usuarios me permitieran borrar más entidades de una vez y aseguraran que eran todos spam modificando las consultas a la base de datos. Después grabé con Selenium IDE una automatización para borrarlos. Incluso siendo más rápido programarlo o algunas consultas SQL, a veces hay un pequeño placer en ver mi navegador hacer cosas por mí.

Después de limpiar morralla, el proceso es muy sencillo. Instalar Drupal Migrate UI y sus dependencias, seguir el procedimiento y esperar a que termine. Pero es una buena idea auditar tu contenido después de la migración, para asegurar que no has olvidado contenido o configuraciones en el proceso. Especialmente porque estas actualizaciones no permiten aún la marcha atrás (rollback) -  no te preocupes si tienes migraciones personalizadas, en ese caso si está soportado.

Audita tu contenido después de la migración. Has podido dejarte algo atrás.

En mi caso, tuve que reiniciar porque mi contenido multiidioma no estaba migrado. Rápidamente descubrí que las configuraciones de multilenguaje deben hacerse antes de la migración de tu contenido. Instala los módulos relacionados si ese es tu caso, y revisa tu estrategia de traducción y sus configuraciones antes de migrar, por ejemplo, tus tipos de contenidos y taxonomías. 

Si tienes contenido multiidioma, revisa tu estrategia y configuraciones acordemente antes de la migración.

En mi caso, tenía etiquetas en castellano e inglés, que no estaban relacionadas entre sí a pesar de ser el mismo término en el sitio de origen. A partir de ahora, gracias en parte al mejor soporte desde Drupal 8 para sitios con diversos idiomas, quería cambiar eso. Hice esas relaciones manualmente, y aquí estamos. 

He elegido el nuevo tema experimental Olivero y el nuevo tema experimental de administración Claro porque son bonitos, más accesibles y funcionales, pero también porque probarlos es una gran forma de ayudar a que mejoren.

Dejo para otro momento mi elección de módulos contribuidos para la web, alguna incidencia que tuve que solventar con un pequeño módulo custom, y la configuración de mi infraestructura basada en contenedores de Docker. Prometo que lo contaré antes de que pasen otros tantos años.

Tags

  • multiidioma
  • migraciones
  • hola-mundo
  • Drupal
  • Drupal planet
Por penyaskito, 10 Noviembre, 2012

Drupalcamp Spain 2012

El pasado 20 y 21 de octubre se celebró en Madrid la tercera edición del mayor evento nacional de Drupal en España, la Drupalcamp Spain 2012. El sábado participé en un panel con Pedro Cambra, Ramon Vilar, Juampy Novillo y José Reyero sobre cómo contribuir a Drupal, a través de las múltiples maneras de colaborar: módulos, temas, traducciones, documentación, difusión, eventos... El material que utilizamos de guión está disponible en Slideshare:

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

El domingo participé de una sesión donde intenté descubrir los cambios respecto al multiidioma en la próxima versión de Drupal, Drupal 8, cuya salida está prevista para Agosto de 2013. El material utilizado de guión está también disponible en Slideshare:

Multilenguaje en Drupal 8 from Christian Lopez

Los videos de estas charlas, junto al del resto de sesiones, estarán disponibles próximamente en el canal de Vimeo de la Asociación Española de Drupal.

Durante el fin de semana también tuvo lugar la primera Asamblea de la Asociación Española de Drupal, de la cual salí como miembro de la Junta. Espero poder contribuir con ella a la difusión de Drupal en España.

Y después de un fin de semana cargado de emociones, un avión hacia Frankfurt. Pero eso ya es otra historia.

Imagen de cabecera: Algunos derechos reservados por Pedro Lozano

Tags

  • Drupal
  • Drupalcamp
  • Drupalcamp Spain
  • AED
  • multiidioma
  • contribuyendo
  • sesiones
  • slideshare
  • Drupal planet
Drupal planet

Archivo mensual

  • Enero 2022 (1)
  • Mayo 2021 (2)
  • Abril 2021 (1)
  • Noviembre 2012 (1)
  • Septiembre 2012 (3)
  • Agosto 2012 (3)
  • Junio 2012 (6)

Contenido reciente

Reescribiendo el historial de commits de git para editar el autor y el email de commits pasados
3 years 4 months ago
Drupal JavaScript behavior para destacar líneas de pedido editadas en el carrito de Drupal Commerce
4 years ago
Analizando el rendimiento de Drupal, WordPress o cualquier aplicación php con DDEV, XHProf y XHGui
4 years ago

Comentarios recientes

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 que sigo

  • 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

Pie de página

  • Drupal.org
  • LinkedIn
  • GitHub
  • Mastodon
  • Twitter
Funciona con Drupal