Pasar al contenido principal
Penyaskito Blog

Main navigation

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

Últimos artículos

Por penyaskito, 6 Enero, 2022

Reescribiendo el historial de commits de git para editar el autor y el email de commits pasados

Hoy necesité editar los commits de un repositorio creado recientemente donde sólo había commiteado yo, pero desde distintos entornos: mi setup habitual con la configuración correcta, otro equipo donde no había configurado la propiedad user.email global, y donde también había añadido el archivo de licencia directamente desde gitlab.  Básicamente, tres direcciones de email distintas y dos nombres distintos asociados a diferentes commits que quería unificar, ya que es bastante desorganizado para un repositorio tan reciente.

Como encontré en esta pregunta en StackOverflow sobre reescribiendo el autor de los commits, se puede arreglar con la herramienta git-filter-repo:

Para instalarla, usé brew (aunque está disponible en la mayoría de gestores de paquetes) con sólo: 

brew install git-filter-repo

Y después la puedes usar con:

git-filter-repo --name-callback \
'return name.replace(b"oldName", bytes("newNameWithUtf8Chars", "utf-8"))' \
--email-callback \
'return email.replace(b"oldmail@example.com", b"newmail@example.com")' \
--force

Después de esto tuve que reañadir el repositorio remote y hacer git push --force. Ten en cuenta que se reescribe el historial, así que puede que necesites (y tus compañeros) reclonar el repo. Recuerda planificarlo acordemente.

Ten también en cuenta que si la rama está protegida, puede que necesites editar permisos para poder hacer el push. Yo lo permití temporalmente siguiendo los pasos de Allow force push on a protected branch on Gitlab docs.

 

Tags

  • git
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

Paginación

  • Página 1
  • Página 2
  • Página 3
  • Página 4
  • Página 5
  • Página 6
  • Siguiente página
  • Última página

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
3 years 11 months 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 8 months ago
This looks interesting
1 year 9 months ago
Thanks for the comment
3 years 11 months 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