Pasar al contenido principal
Penyaskito Blog

Main navigation

  • Home
  • Contacto
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, 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 (1)
  • 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
1 año ago
Drupal JavaScript behavior para destacar líneas de pedido editadas en el carrito de Drupal Commerce
1 año 8 meses ago
Actualización sencilla de un blog personal de Drupal 7 a 9: algunos consejos para migrar
1 año 9 meses ago
Drupalcamp Spain 2012
10 años 2 meses ago
Cumplimiento de la LOPD con el módulo LOPD de Drupal
10 años 4 meses ago
Pruebas de carga e informes desatendidos con JMeter
10 años 4 meses ago
Flujo de trabajo con Git y submodules para despliegues de Drupal
10 años 4 meses ago
Participando en Drupalcon Munich D8MI Sprint (en remoto)
10 años 5 meses ago
Charla en Drupal Sevilla: Contribuyendo a Drupal
10 años 5 meses ago
OOP & PSR-0-ify gettext .po file parsing and generation
10 años 5 meses ago

Comentarios recientes

Thanks for the comment
1 año 8 meses ago
Thanks for sharing this…
1 año 8 meses ago
Looking good!
1 año 9 meses ago
lopd
8 años 9 meses ago
LOPD
8 años 10 meses ago
Gridinit
10 años 3 meses ago
Ya la tiene
10 años 4 meses ago
Debería :-P
10 años 4 meses ago
Ei! tiene buena pinta...
10 años 4 meses ago
Buenas Fco Antonio,
10 años 4 meses 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
Josef Ottosson (dotnet)
Daniel Wehner
Jacob Rockowitz
Syndicate

Footer

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