D8 https://www.carloscarrascal.com/ es Crear bloques por código en Drupal 8 https://www.carloscarrascal.com/blog/crear-bloques-por-codigo-en-drupal-8 <article data-history-node-id="44" class="node node--type-blog-post node--view-mode-rss group-one-column ds-2col-stacked-fluid clearfix"> <div class="group-header"> <div class="field field--name-node-title field--type-ds field--label-hidden field--item"><h1> Crear bloques por código en Drupal 8 </h1> </div> <div class="field field--name-node-post-date field--type-ds field--label-hidden field--item">Viernes, Agosto 23, 2019 - 14:08</div> </div> <div class="group-left"> <div class="field field--name-field-tags field--type-entity-reference field--label-hidden field--items"> <div class="field--item"><a href="/tags/d8" hreflang="es">D8</a></div> <div class="field--item"><a href="/tags/drupal" hreflang="es">Drupal</a></div> </div> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><p>En Drupal los bloques son una de las principales herramientas de que disponemos para mostrar distintos componentes o contenidos, y colocarlos en la página en distintas posiciones. Drupal viene con una serie de bloques por defecto, y cada módulo puede definir sus propios bloques, así que una de las cosas básicas de trabajar con Drupal pasa por aprender a crear nuestros propios bloques. </p> <p>Empezaremos por lo básico y luego lo complicaremos un poco para tocar algunos conceptos más interesantes.</p> <p> </p> <h2>Creando un bloque sencillo</h2> <p>Lo primero que vamos a necesitar es crear un nuevo módulo (o utilizar uno que ya tengamos) e instalarlo, para tener un sitio donde meter nuestro código.</p> <p>En Drupal 8 todos los bloques son instancias de la clase <a href="https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Component%21Plugin%21PluginBase.php/class/PluginBase/8.2.x">Plugin</a>, y el gestor de bloques (<a href="https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Block%21BlockManager.php/class/BlockManager/8.2.x">Block manager</a>) se encargará de encontrar los bloques que hayamos definido por medio de anotaciones de PHP en nuestras clases.</p> <p>Primero crearemos la clase para nuestro bloque en un archivo dentro de nuestro módulo, que deberá estar en el directorio</p> <pre> src/Plugin/Block </pre> <p>El nombre del archivo debe coincidir con el nombre de la clase, o no funcionará. Por ejemplo, vamos a crear un bloque HolaMundo, por lo que crearemos el siguiente archivo:</p> <pre> src/Plugin/Block/HolaMundo.php</pre> <p>Con el siguiente código:</p> <pre> <code>&lt;?php namespace Drupal\hola_mundo\Plugin\Block; use Drupal\Core\Block\BlockBase; /** * Clase de control del bloque HolaMundo. * * @Block( * id = "hola_mundo", * admin_label = @Translation("Hola mundo"), * category = @Translation("Hola mundo"), * ) */ class HolaMundo extends BlockBase { /** * {@inheritdoc} */ public function build() { return [ '#markup' =&gt; $this-&gt;t('Hola mundo!'), ]; } }</code></pre> <p> </p> <p>Ahora debemos limpiar la caché, y ya podremos añadir nuestro nuevo bloque a una página desde el menú <em>Estructura &gt; Diseño de bloques</em>, y colocarlo en la región que queramos.</p> <p>Como veis, nuestra clase extenderá de <a href="https://api.drupal.org/api/drupal/core!lib!Drupal!Core!Block!BlockBase.php/class/BlockBase/8.2.x">BlockBase</a>, debemos poner las anotaciones de PHP (<em>id</em>, <em>admin_label</em> y <em>category</em>), y solamente tenemos que implementar el método <em>build()</em> que tiene que devolver un array para ser renderizado. Si utilizamos <em>#markup</em>, podemos devolver ahi todo el código HTML que queramos que pinte nuestro bloque.</p> <p> </p> <h2>Usando templates para nuestros bloques</h2> <p>Como en la mayoría de los casos lo que queremos pintar en el bloque no será algo tan sencillo como este ejemplo, es buena idea definir un template, de forma que podamos separar la lógica de la presentación, y evitemos tener que meter todo el HTML de esta forma dentro de <em>#markup</em>, que es bastante poco elegante, por decir algo suave.</p> <p>Configurar un template o plantilla para nuestro nuevo bloque es bastante sencillo. La forma mas simple no requiere prácticamente nada, y aunque he visto muchos ejemplos donde dicen que es necesario implementar un <em>hook_theme()</em>, esto no es estrictamente necesario.</p> <p>Primero vamos a hacer unas modificaciones en nuestra función <em>build()</em> del bloque que acabamos de crear para pasarle algunas variables al template, y evitar meter código HTML en nuestras clases PHP. Ahora en vez de devolver todo dentro de #markup, pasaremos distintas variables a las que asignamos el nombre que queramos, en este caso serán <em>title</em> y <em>description</em>, pero podemos llamarlas como queramos: </p> <pre> /** * {@inheritdoc} */ public function build() { return [ 'title' =&gt; t('Hola mundo!'), 'description' =&gt; t('Esto es un bloque que pinta un hola mundo'), ]; }</pre> <p>Listo, sigamos adelante. Solamente con lo que tenemos hasta ahora, Drupal asignará por defecto ciertos templates para aplicar a nuestro bloque. Si activamos el <em>theme debug</em>, podremos ver los nombres de fichero que Drupal buscará dentro del tema que tengamos activo, que para nuestro caso son estos:</p> <pre> &lt;!-- FILE NAME SUGGESTIONS: * block--holamundo.html.twig * block--hola-mundo.html.twig * block--mimodulo.html.twig x block.html.twig --&gt;</pre> <ul></ul><p>Drupal utilizará el template de bloque por defecto <em>block.html.twig </em>cuando ninguno de esos otros ficheros existe.</p> <p>Entonces solo tenemos que crear el archivo de template para el bloque dentro de nuestro tema, usando alguno de esto nombres para el fichero. Es buena idea crear un directorio dentro del tema llamado <em>templates/block</em>, para tenerlos mejor organizados:</p> <pre> themes/custom/nuestro_tema/templates/block/block--hola-mundo.html.twig</pre> <p>Y dentro de este template utilizaremos las variables que hemos definido en <em>build()</em> para mostrar la información:</p> <pre> {# /** * @file * Hola mundo block. */ #} &lt;div class="wrapper hola-mundo--block"&gt; &lt;h1&gt;{{ content.title }}&lt;/h1&gt; &lt;p&gt;{{ content.description }}&lt;/p&gt; &lt;/div&gt;</pre> <p>Importante aquí que nuestras variables deben prefijarse con <em>content.nombre_de_variable</em>, o no veremos ningún resultado.</p> <p>No queda más que borrar caches y probar nuestro nuevo bloque.</p> <p> </p> </div> </div> <div class="group-footer"> <section> <h2>Añadir nuevo comentario</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=44&amp;2=comment&amp;3=comment" token="jpdZrMBRWUjAYN_ne_tmNTYNeAZl-k3ekseHN6k5s9s"></drupal-render-placeholder> </section> </div> </article> Fri, 23 Aug 2019 12:08:55 +0000 root 44 at https://www.carloscarrascal.com Importando metatags con Drupal 8 y Migrate https://www.carloscarrascal.com/blog/importando-metatags-con-drupal-8-y-migrate <article data-history-node-id="41" class="node node--type-blog-post node--view-mode-rss group-one-column ds-2col-stacked-fluid clearfix"> <div class="group-header"> <div class="field field--name-node-title field--type-ds field--label-hidden field--item"><h1> Importando metatags con Drupal 8 y Migrate </h1> </div> <div class="field field--name-node-post-date field--type-ds field--label-hidden field--item">Viernes, Octubre 12, 2018 - 20:11</div> </div> <div class="group-left"> <div class="field field--name-field-tags field--type-entity-reference field--label-hidden field--items"> <div class="field--item"><a href="/tags/d8" hreflang="es">D8</a></div> <div class="field--item"><a href="/tags/migrate" hreflang="es">Migrate</a></div> <div class="field--item"><a href="/tags/drupal" hreflang="es">Drupal</a></div> </div> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><p>Estoy en mitad de un proyecto en el que estamos importando a Drupal 8 contenidos que vienen de un repositorio central, y se consumen usando una API REST en formato JSON.</p> <p>Hasta ahora esta funcionando bastante bien y sin muchos dolores de cabeza, pero tratando de importar los metadatos que vienen con nuestros contenidos a los campos que trae el módulo <a href="https://www.drupal.org/project/metatag">Metatag</a> he tenido que pegarme un poco hasta dar con la solución.</p> <p>Actualmente (octubre de 2017), el módulo Metatag no tiene soporte para Migrate en forma de plugins. Hay <a href="https://www.drupal.org/node/2563649">un ticket abierto</a> desde hace tiempo, con varios parches disponibles, pero aún no está completo. Lo bueno es que en ese mismo hilo hay un par de personas que cuentan como lo han hecho sin necesitar el parche, que era justo lo que yo intentaba, pero se me estaba escapando un detalle: serializar.</p> <p>Para esta importación estamos usando los siguientes módulos:</p> <ul><li><a href="https://www.drupal.org/project/migrate">migrate</a>: en el core de Drupal</li> <li><a href="https://www.drupal.org/project/migrate_tools">migrate_tools</a>: en contrib</li> <li><a href="https://www.drupal.org/project/migrate_plus">migrate_plus</a>: en contrib</li> </ul><p>Para importar contenido usando <em>migrate_plus</em> vamos a necesitar una serie de componentes:</p> <ul><li>Un archivo YML de control para la migración, donde especificaremos el tipo de proceso, los campos entrada y salida</li> <li>Un plugin de importación de contenido, que será una clase que extienda de <a href="https://api.drupal.org/api/drupal/core%21modules%21migrate%21src%21Plugin%21migrate%21source%21SourcePluginBase.php/class/SourcePluginBase/8.6.x">SourcePluginBase</a>, y donde podremos hacer algunas transformaciones.</li> <li>Lo normal será que hayamos creado un módulo custom para controlar toda nuestra importación.</li> </ul><p>No me quiero meter mucho en detalle de cómo se hace la importación usando migrate_plus, porque quiero escribir un artículo entero para ello, así que me voy a centrar en importar los metadatos.</p> <p>Primero el archivo YML que controla la importación en mi módulo, migrate_plus.migration.json_page.yml:</p> <pre> id: json_page label: 'JSON migration for content type: Page' migration_group: my_group source: plugin: json_page cache_counts: true high_water_property: name: changed destination: plugin: entity:node bundle: page process: uuid: uuid langcode: language type: plugin: default_value default_value: page title: title created: changed changed: changed 'body/format': 'body/format' 'body/value': 'body/value' 'body/summary': 'body/summary' 'field_page_teaser/format': 'teaser/format' 'field_page_teaser/value': 'teaser/value' 'path/alias': alias 'promote/value': plugin: default_value default_value: 0 field_meta_tags: meta_tags # use forced module dependency so uninstall/reinstall works properly dependencies: enforced: module: - my_module_name</pre> <p>Lo pongo entero por si ayuda para ver como estoy importando otros campos, pero lo importante es esta línea para los metadatos:</p> <pre> field_meta_tags: meta_tags </pre> <p>Esta línea va a intentar importar los metadatos desde un campo 'meta_tags'  en el origen (el <em>source</em>), a un campo llamado <em>field_meta_tags</em>, que ya hemos creado en nuestro tipo de contenido, en este caso <em>page</em>, y que es de tipo Meta tags.</p> <p>En mi caso, los metadatos vienen en JSON que representa un nodo en este formato. Atención al detalle de que es '<em>metatags</em>', no '<em>meta_tags</em>':</p> <pre> "metatags":{ "title":"a simple node title | [site:name]", "og:title":"a simple node title", "twitter:title":"a simple node title", "description":"a simple description", "og:description":"a simple description", "og:updated_time":"2018-08-27T08:40:34-04:00", "article:published_time":"2017-04-13T04:26:00-04:00", "article:modified_time":"2018-08-27T08:40:34-04:00", "twitter:description":"a simple description", "canonical":"[site:url][current-page:url:path]", "og:site_name":"[site:name]", "og:type":"article", "og:url":"[site:url][current-page:url:path]", "twitter:card":"summary", "twitter:url":"[site:url][current-page:url:path]" },</pre> <p>Como veis ya vienen con tokens que Drupal va a entender, asi que genial. Lo que vamos a hacer es utilizar el plugin que hemos definido para esta importación (<em>json_page</em>) para coger estos datos que vienen en el source de la importación como '<em>metatags</em>', darles el formato adecuado, y colocarlos en '<em>meta_tags</em>', que es lo que espera el proceso de migración, definido en el archivo YML de antes.</p> <p>Este es el código de mi plugin:</p> <pre> class MyJsonPlugin extends SourcePluginBase { public function prepareRow(Row $row) { // Set URL alias. $alias = $row-&gt;getSourceProperty('alias'); if (!empty($alias)) { $row-&gt;setSourceProperty('alias', '/' . $alias); } // Set meta tags. $row-&gt;setSourceProperty('meta_tags', $row-&gt;getSourceProperty('metatags')); return parent::prepareRow($row); } protected function processMetaTags($data) { $metatags = []; if ($data) { foreach ($data as $key =&gt; $value) { $transforms = [ 'twitter:card' =&gt; 'twitter_cards_type', 'twitter:url' =&gt; 'twitter_cards_page_url', 'twitter:description' =&gt; 'twitter_cards_description', 'twitter:site' =&gt; 'twitter_cards_site', 'twitter:site:id' =&gt; 'twitter_cards_site_id', 'twitter:creator' =&gt; 'twitter_cards_creator', 'twitter:creator:id' =&gt; 'twitter_cards_creator_id', 'twitter:title' =&gt; 'twitter_cards_title', 'canonical' =&gt; 'canonical_url', 'og:url' =&gt; 'og_url', 'og:title' =&gt; 'og_title', 'og:site_name' =&gt; 'og_site_name', 'og:type' =&gt; 'og_type', 'og:description' =&gt; 'og_description', 'og:image' =&gt; 'og_image', 'og:image:url' =&gt; 'og_image_url', 'og:image:secure_url' =&gt; 'og_image_secure_url', 'og:image:type' =&gt; 'og_image_type', 'article:published_time' =&gt; 'article_published_time', 'article:modified_time' =&gt; 'article_modified_time', ]; if (array_key_exists($key, $transforms)) { $key = $transforms[$key]; } if ($value) { $metatags[$key] = $value; } } } if (!$metatags) { return NULL; } return serialize($metatags); } ... } </pre> <p>Básicamente hay dos funciones importantes: en <em>prepareRow</em>, cogemos los metadatos desde '<em>metatags</em>' que tiene el JSON, los formateamos y los volvemos a dejar bien bonitos en '<em>meta_tags</em>', y en la función <em>processMetaTags</em> los procesamos y serializamos.</p> <p>Aquí hay dos cosas a tener en cuenta:</p> <ul><li>Mis datos de origen en el JSON, vienen con los nombres de campo de Drupal 7, por ejemplo <em>twitter:card</em>, y los nombres de los campos de Drupal 8 son diferentes, por eso en la función <em>processMetaTags</em> revisamos las claves las cambiamos por las que corresponden. En esta lista no están todas las posibles pero sí las más importantes. Intentaré actualizarla en el futuro cuando la tenga completa.</li> <li>Una vez construido el array con los meta datos, tenemos que <strong>serializarlo</strong> (función <a href="http://php.net/manual/en/function.serialize.php">serialize</a> de PHP) antes de llamar a <em>setSourceProperty</em>, porque si no lo hacemos, los datos no se guardarán correctamente.</li> </ul><p>Y esto es todo amigos, si teneis alguna idea mejor o algún problema podéis dejar una nota en los comentarios.</p> <p> </p> <h3>Referencias</h3> <ul><li>El artículo original con el que empece a jugar con Migrate: <a href="https://www.metaltoad.com/blog/drupal-8-migrating-data-json-files">https://www.metaltoad.com/blog/drupal-8-migrating-data-json-files</a></li> <li>El ticket del modulo Metatags para incluir los plugins de Migrate: <a href="https://www.drupal.org/node/2563649">https://www.drupal.org/node/2563649</a></li> </ul></div> </div> <div class="group-footer"> <section> <h2>Comments</h2> <article data-comment-user-id="0" id="comment-38" about="/en/comment/38" typeof="schema:Comment" class="js-comment"> <mark class="hidden" data-comment-timestamp="1565734782"></mark> <footer> <article typeof="schema:Person" about="/user/0"> </article> <p><span rel="schema:author">Subido por <span lang="" typeof="schema:Person" property="schema:name" datatype="">Salvador B. (no verificado)</span> el Vie, 26/10/2018 - 14:49</span> <span property="schema:dateCreated" content="2018-10-26T12:49:17+00:00" class="hidden"></span> </p> <a href="/en/comment/38#comment-38" hreflang="en">Enlace permanente</a> </footer> <div> <h3 property="schema:name" datatype=""><a href="/en/comment/38#comment-38" class="permalink" rel="bookmark" hreflang="en">Migración Meta tags</a></h3> <div property="schema:text" class="field field--name-comment-body field--type-text-long field--label-hidden field--item"><p>Hola Carlos, te quería agradecer tu articulo. </p> <p>Te he encontrado a partir del ticket de Metatags (<a href="https://www.drupal.org/node/2563649">https://www.drupal.org/node/2563649</a>). Te has ganado un follower! :-D</p> <p>Saludos!</p> </div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=38&amp;1=default&amp;2=en&amp;3=" token="Kp71kBs1el410NhBpfenh3PMri37GPQSw9Csso9QNSI"></drupal-render-placeholder> </div> </article> <h2>Añadir nuevo comentario</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=41&amp;2=comment&amp;3=comment" token="gL6owGXCmF4P6iAHBLDYmzBZgqth1K2tPxIOuqJ0FrQ"></drupal-render-placeholder> </section> </div> </article> Fri, 12 Oct 2018 18:11:26 +0000 root 41 at https://www.carloscarrascal.com Como añadir Google Fonts a nuestro tema de Drupal 8 https://www.carloscarrascal.com/blog/como-anadir-google-fonts-nuestro-tema-de-drupal-8 <article data-history-node-id="35" class="node node--type-blog-post node--view-mode-rss group-one-column ds-2col-stacked-fluid clearfix"> <div class="group-header"> <div class="field field--name-node-title field--type-ds field--label-hidden field--item"><h1> Como añadir Google Fonts a nuestro tema de Drupal 8 </h1> </div> <div class="field field--name-node-post-date field--type-ds field--label-hidden field--item">Lunes, Octubre 30, 2017 - 12:05</div> </div> <div class="group-left"> <div class="field field--name-field-tags field--type-entity-reference field--label-hidden field--items"> <div class="field--item"><a href="/tags/drupal" hreflang="es">Drupal</a></div> <div class="field--item"><a href="/tags/d8" hreflang="es">D8</a></div> </div> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><p>Esta es la mejor forma de añadir fuentes de Google en vuestro tema de Drupal 8 sin tocar un template.</p> <p>Primero, incluimos en nuestro fichero <em>nuestro-tema.info.yml</em>:</p> <pre> libraries: - 'bs_css/fonts' </pre> <p>Después, incluimos las fuentes en el archivo <em>nuestro-tema.libraries.yml</em>:</p> <pre> fonts: css: theme: '//fonts.googleapis.com/css?family=Coda:regular,800|Comfortaa:regular,700,300|Raleway:regular&amp;subset=latin-ext': { type: external, minified: true } </pre> <p>Es una buena práctica agrupar todas las fuentes en la misma llamada, en lugar de poner una entrada para cada fuente, ya que nos ahorraremos algunas peticiones HTTP.</p> <p>Si no podeis tocar código (o no quereis), existe un módulo llamado <a href="https://www.drupal.org/project/fontyourface">@font-your-face</a>, que nos permite añadir y configurar fuentes de varios proveedores, no solo Google, desde la interfaz de administración de Drupal.</p> </div> </div> <div class="group-footer"> <section> <h2>Añadir nuevo comentario</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=35&amp;2=comment&amp;3=comment" token="mOajXVYpiePDV-bhaUUBcAQM6DMmHS4y_f9c9tTURUM"></drupal-render-placeholder> </section> </div> </article> Mon, 30 Oct 2017 11:05:04 +0000 root 35 at https://www.carloscarrascal.com Bootstrap + Less + Gulp para Drupal 8 https://www.carloscarrascal.com/blog/bootstrap-less-gulp-para-drupal-8 <article data-history-node-id="33" class="node node--type-blog-post node--view-mode-rss group-one-column ds-2col-stacked-fluid clearfix"> <div class="group-header"> <div class="field field--name-node-title field--type-ds field--label-hidden field--item"><h1> Bootstrap + Less + Gulp para Drupal 8 </h1> </div> <div class="field field--name-node-post-date field--type-ds field--label-hidden field--item">Lunes, Octubre 16, 2017 - 21:19</div> </div> <div class="group-left"> <div class="field field--name-field-tags field--type-entity-reference field--label-hidden field--items"> <div class="field--item"><a href="/tags/d8" hreflang="es">D8</a></div> <div class="field--item"><a href="/tags/drupal" hreflang="es">Drupal</a></div> <div class="field--item"><a href="/tags/bootstrap" hreflang="es">Bootstrap</a></div> <div class="field--item"><a href="/tags/gulp" hreflang="es">Gulp</a></div> <div class="field--item"><a href="/tags/less" hreflang="es">less</a></div> <div class="field--item"><a href="/tags/javascript" hreflang="es">Javascript</a></div> </div> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><p><a href="https://gulpjs.com/">Gulp.js</a> es un gestor de tareas en Javascript que nos va ayudar a automatizar tareas repetitivas en nuestro proyecto Drupal. Una de las que mas vamos a repetir mientras estamos desarrollando es compilar el tema para generar el <em>CSS</em> si estamos utilizando un preprocesador como <em>SASS</em> o <em>Less</em>, con el tema de Bootstrap, por ejemplo, o con nuestro propio tema '<em>custom</em>'.</p> <p>Añadir <em>Gulp</em> a nuestro tema para compilar nos proporciona unas cuantas ventajas, entre las que podemos destacar:</p> <ul><li>Recompilar el tema automáticamente cada vez que modificamos un archivo <em>less</em>.</li> <li>Y recargar automáticamente el navegador cuando cambia el <em>CSS</em>.</li> <li>Minificar el <em>CSS</em> para mejorar el rendimiento.</li> <li>Ejecutar test y chequear el código al compilar el tema, para controlar errores de sintaxis, estándares, etc.</li> <li>Gestionar dependencias y minificar nuestros ficheros <em>Javascript</em>.</li> <li>Realizar operaciones con ficheros: copiar, mover, borrar, etc.</li> </ul><p>Vamos a ver una forma sencilla y rápida que nos dará un buen punto de partida para comenzar a utilizar <em>Gulp</em> para compilar nuestro tema de Drupal 8 basado en <em>Bootstrap</em>. Yo estoy utilizando la versión normal que viene con Less por defecto. Si estáis usando <em>SASS</em> en vez de Less podéis darle un vistazo a <a href="http://www.abhishekanand.in/article/set-drupal-8-theme-gulp-bower-bootstrap-sass-fontawesome">este artículo</a> (en inglés).</p> <p>Si habéis seguido las instrucciones para crear un subtema de <em>Bootstrap</em>, ya tendréis una estructura parecida a esta:</p> <ul><li>DRUPAL_ROOT/themes/custom/vuestro_subtema <ul><li>less <ul><li>style.less &lt;-- Este el archivo principal Less para compilar el tema.</li> </ul></li> <li>css <ul><li>style.css &lt;-- Archivo CSS resultado de compilar el tema.</li> </ul></li> </ul></li> </ul><p>En este punto, y si no queremos complicarnos la vida, podremos compilar nuestro tema de forma sencilla con un comando llamando al compilador de Less:</p> <pre> lessc less/style.less &gt; css/style.css </pre> <p>Yo tenía esta línea metida en un script bash sencillo llamado <em>compile.sh</em>, y lo ejecutaba cuando necesitaba compilar, así:</p> <pre> sh ./compile.sh</pre> <p>Pero claro, si estáis leyendo es que os va la marcha, así que vamos a ver cómo montamos Gulp en un periquete. Primero vamos a necesitar crear en la raíz de nuestro tema (subtema si usais Bootstrap), un archivo package.json para controlar las dependencias de Npm. Será muy sencillo:</p> <pre> { "name": "nombre_del_tema", "version": "0.0.0", "description": "Paquete para compilar subtema de Bootstrap con Gulp", "author": "nombre<your name="">", "contributors": [ { "name": "nombre<your name="">", "email": "direccion@email.com<your email="">" } ], "dependencies": {}, "devDependencies": {}, "license": "Private" } </your></your></your></pre> <p>Obviamente vamos a necesitar tener instalado npm, ya que lo vamos a utilizar para instalar cómodamente todos los paquetes y dependencias que necesitamos.</p> <p>Primero instalamos <em>Gulp</em> como paquete global para no tener problemas con el ejecutable:</p> <pre> npm install -g gulp</pre> <p>Una vez instalado, desde la raíz de nuestro tema, instalamos las dependencias y le decimos a npm que las añada a nuestro fichero <em>package.json</em> con el parámetro <em>--save-dev</em>:</p> <pre> npm install gulp gulp-less gulp-livereload gulp-sourcemaps gulp-watch --save-dev</pre> <p>Después de ejecutar este comando deberíamos tener un directorio <em>node_modules</em> con los paquetes que acabamos de instalar:</p> <pre> ll node_modules/ total 0 drwxr-xr-x 12 charles staff 408 16 Oct 19:36 gulp drwxr-xr-x 8 charles staff 272 16 Oct 19:36 gulp-less drwxr-xr-x 11 charles staff 374 16 Oct 19:36 gulp-livereload drwxr-xr-x 8 charles staff 272 16 Oct 19:36 gulp-sourcemaps drwxr-xr-x 7 charles staff 238 16 Oct 19:36 gulp-watch</pre> <p>Y en nuestro archivo <em>package.json</em> se habrán incluido las dependencias (los números de versión pueden cambiar):</p> <pre> "devDependencies": { "gulp": "^3.9.1", "gulp-less": "^3.3.2", "gulp-livereload": "^3.8.1", "gulp-sourcemaps": "^2.6.1", "gulp-watch": "^4.3.11" }, </pre> <p>Ahora necesitamos crear el archivo de control para <em>Gulp</em>, llamado <em>gulpfile.js</em>, también en la raíz de nuestro tema, con lo básico para empezar a funcionar:</p> <pre> var gulp = require('gulp'); var less = require('gulp-less'); var watch = require('gulp-watch'); var livereload = require('gulp-livereload'); var sourcemaps = require('gulp-sourcemaps'); gulp.task('less', function () { gulp.src('less/style.less') .pipe(sourcemaps.init()) .pipe(less()) .pipe(sourcemaps.write('.')) .pipe(gulp.dest('css')) .pipe(livereload()); }); gulp.task('watch', function() { livereload.listen(); gulp.watch('less/*.less', ['less']); }); gulp.task('default', ['watch']); </pre> <p>Con esto deberíamos poder ejecutar dos comandos para compilar:</p> <pre> gulp less</pre> <p>Que simplemente compilará el tema creando los CSS dentro del directorio css, o también:</p> <pre> gulp watch</pre> <p>Que dejará el proceso corriendo, vigilando modificaciones en nuestros archivos less y recompilando el tema si detecta algún cambio. Este comando se ejecutará por defecto si solo hacemos:</p> <pre> gulp</pre> <p>Vamos a ver en detalle que hemos metido en este fichero de control de <em>Gulp</em>:</p> <ul><li>Las líneas <em>var</em> van a cargar las dependencias que necesitamos. <ul><li><strong>Gulp</strong> y <strong>gulp-less</strong>: Carga la libreria y la extension para less.</li> <li><strong>gulp-watch</strong>: Carga el módulo para vigilar los archivos del tema.</li> <li><strong>gulp-livereload</strong>: Carga el módulo <a href="http://livereload.com/">livereload</a> para automatizar la recarga del navegador. Para que funcione tenemos que instalar una <a href="http://livereload.com/extensions/">extensión del navegador</a>. Hay extensiones disponibles para Chrome, Firefox y Safari.</li> <li><strong>gulp-sourcemaps</strong>: Carga el paquete para generar sourcemaps de nuestros ficheros CSS. Tal como está configurado creará un fichero <em>.map</em> separado en el mismo directorio /css. Estos ficheros sirven para ayudar en la depuración de nuestros estilos.</li> </ul></li> <li>Tarea <strong>less</strong>: <ul><li>Compila el CSS, aplica el módulo de <em>livereload</em> y genera los sourcemaps necesarios.</li> </ul></li> <li>Tarea <strong>watch</strong>: <ul><li>Activa el <em>livereload</em> y vigila los archivos con extension <em>.less</em>, dentro de la carpeta less por si cambian, lo que recompilará el tema completo.</li> </ul></li> <li>Tarea <strong>default</strong>: <ul><li>Se ejecuta cuando corremos gulp sin parámetros, y por defecto lanzará la tarea <em>watch</em>.</li> </ul></li> </ul><p>Con estos sencillos pasos tendremos configurado <em>Gulp</em> en muy poco tiempo, y a partir de esta configuración inicial podremos ir ampliando e introducir otras mejoras o librerías en nuestro proyecto Drupal.</p> <p> </p> <h2>Referencias</h2> <ol><li><a href="https://eureka.ykyuen.info/2015/03/09/drupal-7-setup-bootstrap-3-theme-with-gulp-for-less-compilation/">DRUPAL 7 – SETUP BOOTSTRAP 3 THEME WITH GULP FOR LESS COMPILATION</a></li> <li><a href="http://www.abhishekanand.in/article/set-drupal-8-theme-gulp-bower-bootstrap-sass-fontawesome">Set up a Drupal 8 theme with Gulp, Bower, Bootstrap Sass, &amp; FontAwesome</a></li> <li><a href="http://brandonclapp.com/what-is-gulp-js-and-why-use-it/">What is gulp.js and why use it?</a></li> </ol></div> </div> <div class="group-footer"> <section> <h2>Añadir nuevo comentario</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=33&amp;2=comment&amp;3=comment" token="SOCxtNpb2aUdz8W91NLOhKstqWZQIwlggK7ehFpqvWc"></drupal-render-placeholder> </section> </div> </article> Mon, 16 Oct 2017 19:19:41 +0000 root 33 at https://www.carloscarrascal.com Configuración en Drupal 8. Parte 2: Config Split https://www.carloscarrascal.com/blog/configuracion-en-drupal-8-parte-2-config-split <article data-history-node-id="32" class="has-header-image node node--type-blog-post node--view-mode-rss group-one-column ds-2col-stacked-fluid clearfix"> <div class="group-header"> <div class="field field--name-node-title field--type-ds field--label-hidden field--item"><h1> Configuración en Drupal 8. Parte 2: Config Split </h1> </div> <div class="field field--name-field-header-image field--type-image field--label-hidden field--item"> <img src="/sites/default/files/styles/full_width/public/2017-10/2017-10-08-17-15-08_0.jpg?itok=kgUcKuCT" width="850" height="478" alt="Configuración en Drupal 8. Parte 2: Config Split" typeof="foaf:Image" class="img-responsive" /> </div> <div class="field field--name-node-post-date field--type-ds field--label-hidden field--item">Sábado, Octubre 7, 2017 - 17:26</div> </div> <div class="group-left"> <div class="field field--name-field-tags field--type-entity-reference field--label-hidden field--items"> <div class="field--item"><a href="/tags/drupal" hreflang="es">Drupal</a></div> <div class="field--item"><a href="/tags/d8" hreflang="es">D8</a></div> </div> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><p>En esta segunda parte de la gestión de la configuración en Drupal 8, vamos a ver cómo podemos mantener diferentes configuraciones para cada entorno.</p> <p>Si te has perdido la <a href="https://www.carloscarrascal.com/blog/gestion-de-la-configuracion-en-drupal-8">primera parte de este artículo</a>, en ella vimos los principios básicos para gestionar la configuración de nuestros sitios con Drupal 8, y deberías darle un repaso antes de seguir con este.</p> <p>Antes de empezar vamos a hacer un pequeño ejemplo y explicar cuales son los casos de uso.</p> <h2>El problema</h2> <p>En cualquier proyecto en el que tengamos varios entornos (integración, desarrollo, producción, etc.) es muy probable que queramos tener configuraciones distintas de nuestro sitio para cada uno de ellos, o varios, o uno solo. Por ejemplo:</p> <ul><li>Activar módulo <em>Devel</em> en el entorno de desarrollo. Este es el ejemplo típico.</li> <li>Distinta configuración de URLs o IPs de servidores con los que tengamos integraciones. Por ejemplo, podemos tener un servidor <a href="https://es.wikipedia.org/wiki/Protocolo_Ligero_de_Acceso_a_Directorios">LDAP</a> para desarrollo y otro para producción.</li> <li>Configuración específica para producción: caches, módulos de log, varnish, CDNs, etc.</li> </ul><p>Normalmente esto supone que tendremos un bloque de configuración gordo que será común a todos los entornos, pero también bloques específicos que sólo se aplicarán en algunos de ellos.</p> <p>En principio, podríamos sobreescribir la configuración añadiendo líneas al archivo <em>settings.php</em> de los diferentes entornos que tengamos, así:</p> <pre> $config['system.logging']['error_level'] = 'verbose';</pre> <p>Para casos sencillos y concretos puede ser una solución válida, pero usar <em>$config</em> tiene varios inconvenientes:</p> <ul><li>No se puede añadir nueva configuración, solo modificar la ya existente.</li> <li>No se puede hacer un <em>unset</em>, no se borrará la configuración.</li> <li>No se pueden activar o desactivar módulos de esta forma.</li> <li>El fichero settings sería complicado de mantener si empezamos a meter morralla.</li> </ul><p>Se va viendo ya donde está el problema, ¿verdad?</p> <p> </p> <h2>¿Que solución hay?</h2> <p>La gente de <a href="http://nuvole.org/">Nuvole</a> ha estado desarrollando una serie de módulos que nos van a ayudar con este pequeño jaleo. Vamos a ver <em>Config Filter</em>, que va a ser la clave para solucionar el problema que acabamos de ver. La primera versión estable <a href="http://nuvole.org/blog/2017/aug/21/stable-release-config-split-and-config-filter">ha visto la luz</a> hace bien poquito, y tuve la suerte de asistir a la conferencia que dió <a href="https://events.drupal.org/vienna2017/sessions/advanced-configuration-management-config-split-et-al">Fabian Bircher en Drupalcon Viena 2017,</a> porque era exactamente lo que estaba buscando.</p> <p>Usando <em>Config Split</em> vamos a poder agrupar y separar bloques de configuración, que podremos activar o desactivar en cada entorno, así como exportar o importar los ficheros desde directorios diferentes a nuestro <em>sync</em>. Este y otros módulos desarrollados también por <em>Nuvole</em>, necesitan el módulo <em>Config Filter,</em> como una dependencia en la que se basan.</p> <p> </p> <h2>Cómo funciona</h2> <p>Para cada bloque de configuración  (<em>Split</em>) que configuremos vamos a poder indicar un directorio donde se guardarán los archivo de configuración de los componentes de ese <em>Split</em>. Mientras el <em>Split</em> esté activo, la configuración de sus componentes se exportará e importará siempre desde ese directorio.</p> <p>Hay dos modos de funcionamiento para la configuración:</p> <ul><li><strong>Split completo:</strong> Inicialmente se llamaba <em>Blacklist</em> o lista negra. Los componentes indicados aquí se gestionarán exclusivamente desde el <em>Split</em>, por lo que sus archivos de configuración se eliminarán del directorio <em>Sync</em> principal. En este modo podremos incluir módulos completos o bien componentes concretos de esos módulos.</li> <li><strong>Split condicional:</strong> Llamado inicialmente <em>Graylist</em>, con este modo la configuración de sus componentes no se elimina del <em>Sync</em> global, pero mientras el Split este activo, se importará y exportará al Split. Esto nos permite tener configuraciones alternativas por entorno. En este modo solo se nos permite incluir componentes específicos.</li> </ul><p>Además, para cada <em>Split</em> podremos definir un peso, de forma que si varios <em>Splits</em> definen la misma configuración, el peso marcará cual tiene prioridad.</p> <p><em>Config Split</em> incorpora dos nuevos comandos de <em>Drush</em> para manejar la configuración, son<em> config-split-import</em> y <em>config-split-export</em>:</p> <pre> drush csim drush csex</pre> <p> </p> <h2>Instalación</h2> <p>Lo primero que necesitamos hacer es instalar y activar los módulos:</p> <pre> drush dl config_filter config_split drush en config_filter config_split </pre> <p>Una vez activados vamos a configurar el <em>Split</em> de la configuración. Vamos al panel de administración, en <em>Inicio</em> »  <em>Administración</em> » <em>Configuración</em> » <em>Development</em> » <em>Sincronizar</em>, donde podemos comenzar a crear grupos.</p> <p><img alt="Listado de Splits" class="img-responsive shadow" data-entity-type="file" data-entity-uuid="c8c3a93f-ea04-44cf-b6fd-c427c646f793" src="/sites/default/files/inline-images/configuration-split-screen1.png" /></p> <p>En la imagen se puede ver un <em>Split</em> configurado que hemos llamado <em>Dev</em>, y que está activo en este entorno. Vamos a hacer el ejemplo sencillo de usar este Split para activar el módulo Devel en el entorno de desarrollo. Veámos la configuración básica de este Split en la siguiente imagen.</p> <p><img alt="Configuración básica del Split" class="img-responsive shadow" data-entity-type="file" data-entity-uuid="4a3badef-147b-4ad4-a40f-fbe79e32a59f" src="/sites/default/files/inline-images/configuration-split-edit-basico.jpg" /></p> <p>Indicamos la ruta física donde se van a guardar los archivos de configuración de este split, relativa a la raíz de Drupal. Le damos un peso e indicamos si está activo o no.</p> <p>A continuación, y para este ejemplo, seleccionamos el módulo Devel en el bloque de Complete Split, para sacar completamente la configuración de este módulo de la general, ya que solamente vamos a querer activarlo en desarrollo.</p> <p><img alt="Configuración de opciones para el Split" class="img-responsive shadow" data-entity-type="file" data-entity-uuid="3f237228-58a6-40a1-bb6b-d347b5890596" src="/sites/default/files/inline-images/configuration-split-edit-opciones.jpg" /></p> <p>Guardamos la configuración del Split, y para aplicarla tenemos que exportar, con Drush <em>config-split-export</em>:</p> <pre> drush csex</pre> <p>Cuando termine el comando, los archivos de configuración de <em>Devel</em> estarán en el directorio del split, y como los pusimos en el modo completo, serán eliminados del directorio <em>Sync</em> general. En mi caso, tengo algo así.</p> <pre> $ ll dev/ total 24 -rw-rw-r-- 1 charles staff 281 7 Oct 15:21 devel.settings.yml -rw-rw-r-- 1 charles staff 279 7 Oct 15:21 devel.toolbar.settings.yml -rw-rw-r-- 1 charles staff 283 7 Oct 15:21 system.menu.devel.yml</pre> <p>Ahora podemos ir a nuestro entorno de producción y desactivamos en nuestro entorno de producción este <em>split</em>, sobreescribiendo la configuración en el archivo <em>settings.php</em> del servidor:</p> <pre> // Desactivar configuracion de split de Dev $config['config_split.config_split.dev']['status'] = FALSE; </pre> <p>Por supuesto que esto podemos hacerlo al revés, configurarlo como desactivado por defecto, y activarlo solamente en nuestro entorno de desarrollo, y en este caso puede que fuese la mejor opción, porque nos ahorraremos tocar nada de configuración en producción.</p> <p> </p> <h2>Flujo de trabajo</h2> <p>Esta es una lista básica de los pasos que vamos a necesitar para trabajar con esta configuración en varios entornos. Espero que ayude a clarificar el proceso.</p> <p>En nuestro entorno local:</p> <ol><li>Obtener la base de datos de producción y bajarla a nuestro entorno local.</li> <li>Borrar caches (<em>drush cr</em>).</li> <li>Actualizar nuestro repositorio de control de versiones. La forma de hacer esto dependerá mucho de nuestro proyecto, pero básicamente lo que queremos es hacer un <em>git pull</em>.</li> <li>Importar la configuración de Dev que hemos preparado. Al venir de producción ésta BD no tiene esa configuración (<em>drush csim dev</em>).</li> <li>Comprobar si hay algún cambio de configuración que se haya hecho en producción, y si es así exportarlo para no perderlo (<em>drush csex</em>).</li> <li>Guardar estos cambios en el control de versiones (<em>git commit</em> + <em>git push</em>).</li> <li>Trabajar en las modificaciones de configuación que queramos hacer sobre nuestro entorno local.</li> <li>Exportar nuestros cambios (<em>drush csex</em>).</li> <li>Subirlos al control de versiones (<em>git commit</em> + <em>git push</em>).</li> </ol><p>En el entorno de producción:</p> <ol><li>Desplegar los cambios en los ficheros de configuración en el entorno de producción. Esto dependerá de la forma en que estemos trabajando. Puede ser tan sencillo como hacer un <em>git pull</em>, o desplegar con un <em>Jenkins</em>.</li> <li>Importar la configuración con los últimos cambios. Si nuestro <em>Config Split</em> de <em>Dev</em> está desactivado en el <em>settings.php</em>, esa configuración no se importará (<em>drush csim</em>).</li> </ol><p> </p> <h2>Conclusiones</h2> <p><em>Config Split</em> es justo la pieza que me faltaba para poder hacer una gestión medianamente decente de la configuración en Drupal 8 para proyectos grandes, sin usar <em>Features</em>.</p> <p>Hasta ahora me esta funcionando sin problemas y en general me gusta como se configura y trabaja con el. Recomiendo sin duda darle una oportunidad porque los chicos de <em>Nuvole</em> han hecho un gran trabajo,</p> <p> </p> <h2>Referencias</h2> <ol><li><a href="https://www.drupal.org/docs/8/modules/configuration-split/creating-a-simple-split-configuration-dev-modules-only-in-dev">Creating a simple split configuration: Dev modules only in dev environments</a></li> <li> <p><a href="https://events.drupal.org/vienna2017/sessions/advanced-configuration-management-config-split-et-al">Advanced Configuration Management with Config Split et al.</a> (Drupalcon Viena 2017)</p> </li> <li> <p><a href="http://nuvole.org/blog/2017/aug/21/stable-release-config-split-and-config-filter">http://nuvole.org/blog/2017/aug/21/stable-release-config-split-and-conf…</a></p> </li> </ol></div> </div> <div class="group-footer"> <section> <h2>Añadir nuevo comentario</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=32&amp;2=comment&amp;3=comment" token="cBy-k65ZpSZzYuUeFvRtIUCCtXu8GaU6qLNQDMe_kRI"></drupal-render-placeholder> </section> </div> </article> Sat, 07 Oct 2017 15:26:51 +0000 root 32 at https://www.carloscarrascal.com Related By Terms para Drupal 8 https://www.carloscarrascal.com/blog/related-terms-para-drupal-8 <article data-history-node-id="27" class="node node--type-blog-post node--view-mode-rss group-one-column ds-2col-stacked-fluid clearfix"> <div class="group-header"> <div class="field field--name-node-title field--type-ds field--label-hidden field--item"><h1> Related By Terms para Drupal 8 </h1> </div> <div class="field field--name-node-post-date field--type-ds field--label-hidden field--item">Miércoles, Octubre 4, 2017 - 00:17</div> </div> <div class="group-left"> <div class="field field--name-field-tags field--type-entity-reference field--label-hidden field--items"> <div class="field--item"><a href="/tags/drupal" hreflang="es">Drupal</a></div> <div class="field--item"><a href="/tags/d8" hreflang="es">D8</a></div> <div class="field--item"><a href="/tags/relatedbyterms" hreflang="es">RelatedByTerms</a></div> </div> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><p>Ayer publiqué la primera version estable de la rama 8.x de <a href="https://www.drupal.org/project/relatedbyterms">Related By Terms</a>. La funcionalidad es prácticamente la misma que tiene <a href="https://www.carloscarrascal.com/blog/related-terms-para-drupal-7">la versión de Drupal 7</a>, pero como podréis imaginar, he tenido que reescribir prácticamente todo el módulo. Lo único que se ha salvado, y de milagro es la query que estaba utilizando para obtener los nodos relacionados.</p> <p>Para refrescar la memoria, este módulo nos da:</p> <ul><li>Un bloque que podemos colocar donde queramos que mostrará una lista de nodos relacionados con el que estamos viendo, ordenados por el número de coincidencias en términos de taxonomías.</li> <li>Una página de configuración en el panel de administración donde podemos configurar el número de relacionados a mostrar y el <em>Display mode</em> que se usará para pintarlos.</li> <li>En el caso de la versión para Drupal 8, un servicio que puede ser usado por otros módulos para obtener simplemente la lista de relacionados, para procesarlos de la manera más conveniente.</li> </ul><p>Por supuesto, tengo instalado el módulo en este sitio, es el bloque que podéis ver en la barra lateral derecha debajo del título 'Relacionado'.</p> <p>Esta es la lista de los nuevos y flamantes componentes del módulo:</p> <pre> relatedbyterms/LICENSE.txt relatedbyterms/README.md relatedbyterms/relatedbyterms.info.yml relatedbyterms/relatedbyterms.install relatedbyterms/relatedbyterms.links.menu.yml relatedbyterms/relatedbyterms.module relatedbyterms/relatedbyterms.permissions.yml relatedbyterms/relatedbyterms.routing.yml relatedbyterms/relatedbyterms.services.yml relatedbyterms/config/install/relatedbyterms.settings.yml relatedbyterms/config/schema/relatedbyterms.schema.yml relatedbyterms/src/Form/RelatedByTermsForm.php relatedbyterms/src/Plugin/Block/RelatedByTermsBlock.php relatedbyterms/src/RelatedByTermsService.php relatedbyterms/src/RelatedByTermsServiceInterface.php</pre> <p>Como podéis ver si habéis dado un vistazo al código de la versión 7, aquí hay bastante chicha para un módulo tan sencillo. Vamos a ver qué hace cada cosa:</p> <ul><li>Los archivos <em>README.md</em> y <em>LICENSE.txt</em> no creo que necesiten mucha explicación.</li> <li>Archivo <em>info.ym</em>l: Información básica del módulo.</li> <li>El fichero <em>install</em> contiene una simple inicialización de permisos.</li> <li><em>links.menu.yml</em>: Define el elemento del menú de administración para acceder a la configuración del módulo.</li> <li>En el archivo <em>.module</em> tan solo hay una implementación de <em>hook_help()</em> para mostrar un texto de ayuda.</li> <li><em>permissions.yml</em> tiene la definición de un permiso especial para acceder a la configuración.</li> <li><em>routing.yml</em> define una ruta para la página de administración, le asigna una clase de control para crear el formulario (<em>RelatedByTermsForm</em>) y comprueba que el usuario tenga el permiso necesario para acceder.</li> <li><em>services.yml</em> sirve para declarar un nuevo servicio <em>relatedbyterms.manager</em>, y le asigna una clase de control, <em>RelatedByTermsService</em>. Este servicio puede ser utilizado por otros módulos para obtener una lista de nodos relacionados a partir de un <em>Nid</em>.</li> <li>Luego tenemos un directorio install, que guarda los archivos de configuración del módulo.</li> <li>Y por fin en el directorio <em>src</em>, encontramos: <ul><li><em>RelatedByTermsForm</em>: Controla el formulario de administración del módulo.</li> <li><em>RelatedByTermsBlock</em>: Para definir el bloque que va a mostrar los contenidos relacionados.</li> <li><em>RelatedByTermsServiceInterface</em>: Interfaz para la definición de los métodos del servicio.</li> <li><em>RelatedByTermsService</em>: Clase que define el servicio. Esta clase contiene la lógica de negocio del módulo, y puede ser cargado desde otros módulos.</li> </ul></li> </ul><p>Espero que lo probéis. Cualquier comentario, peticiones, o si veis que he metido la pata en algún sitio, dejad un comentario, poneros en contacto conmigo, o mejor aún, crear un ticket en Drupal.org para el módulo.</p> <p>Saludos</p> <p> </p> </div> </div> <div class="group-footer"> <section> <h2>Añadir nuevo comentario</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=27&amp;2=comment&amp;3=comment" token="TA0hR5mKqqgyHVFJ9N2hid4Y-krX49ZXWc4IWg3H7iI"></drupal-render-placeholder> </section> </div> </article> Tue, 03 Oct 2017 22:17:37 +0000 root 27 at https://www.carloscarrascal.com Como instalar composer en Debian 8 https://www.carloscarrascal.com/blog/como-instalar-composer-en-debian-8 <article data-history-node-id="30" class="node node--type-blog-post node--view-mode-rss group-one-column ds-2col-stacked-fluid clearfix"> <div class="group-header"> <div class="field field--name-node-title field--type-ds field--label-hidden field--item"><h1> Como instalar composer en Debian 8 </h1> </div> <div class="field field--name-node-post-date field--type-ds field--label-hidden field--item">Domingo, Octubre 1, 2017 - 01:58</div> </div> <div class="group-left"> <div class="field field--name-field-tags field--type-entity-reference field--label-hidden field--items"> <div class="field--item"><a href="/tags/debian" hreflang="es">Debian</a></div> <div class="field--item"><a href="/tags/d8" hreflang="es">D8</a></div> <div class="field--item"><a href="/tags/drupal" hreflang="es">Drupal</a></div> </div> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><p>Instalar composer en Debian 8 es muy sencillo:</p> <pre> <code>$ cd /usr/src $ sudo apt-get install curl php5-cli $ curl -sS <a href="https://getcomposer.org/installer" rel="nofollow">https://getcomposer.org/installer</a> | sudo php -- --install-dir=/usr/local/bin --filename=composer </code></pre> <p>Después podemos comprobar si se ha instalado correctamente:</p> <pre> <code>$ composer --version</code></pre> <p>A día de hoy, este comando muestra:</p> <pre> Composer version 1.5.2 2017-09-11 16:59:25</pre> </div> </div> <div class="group-footer"> <section> <h2>Añadir nuevo comentario</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=30&amp;2=comment&amp;3=comment" token="8wewMAKC0uqsWM0QjaNynVaLnWgPdKnjktil3Z76i3I"></drupal-render-placeholder> </section> </div> </article> Sat, 30 Sep 2017 23:58:12 +0000 root 30 at https://www.carloscarrascal.com Gestión de la configuración en Drupal 8 https://www.carloscarrascal.com/blog/gestion-de-la-configuracion-en-drupal-8 <article data-history-node-id="28" class="node node--type-blog-post node--view-mode-rss group-one-column ds-2col-stacked-fluid clearfix"> <div class="group-header"> <div class="field field--name-node-title field--type-ds field--label-hidden field--item"><h1> Gestión de la configuración en Drupal 8 </h1> </div> <div class="field field--name-node-post-date field--type-ds field--label-hidden field--item">Sábado, Septiembre 23, 2017 - 12:45</div> </div> <div class="group-left"> <div class="field field--name-field-tags field--type-entity-reference field--label-hidden field--items"> <div class="field--item"><a href="/tags/drupal" hreflang="es">Drupal</a></div> <div class="field--item"><a href="/tags/d8" hreflang="es">D8</a></div> </div> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><p>Una de las novedades mas celebradas de Drupal 8 ha sido sin duda la nueva gestión de la configuración. Es un concepto clave para trabajar con Drupal profesionalmente, y que no existía de forma nativa en Drupal 7.</p> <p>Para ilustrar el problema, vamos a verlo con un ejemplo. En cualquier proyecto de desarrollo con Drupal que se precie lo normal es mantener varios entornos:</p> <ul><li><em>Desarrollo</em>: Donde se llevan a cabo los cambios por los desarrolladores.</li> <li><em>Producción</em>: El entorno donde está publicada la versión real de la aplicación, a la que acceden los usuarios.</li> <li>Opcionalmente: Entornos intermedios de <em>integración</em> o <em>preproducción</em>, <em>testing</em>, <em>UAT</em>, etc. </li> </ul><p>Para no complicar mucho el ejemplo, pongamos que tan solo tenemos un entorno de desarrollo y otro de producción. Cuando un desarrollador crea una nueva funcionalidad,  se utiliza un entorno de desarrollo, y una vez la funcionalidad está completa y probada, se decide que es el momento de desplegarla en el entorno de producción. En algunos casos puede que sea tan sencillo como subir un módulo nuevo y listo, pero muchas veces nos encontraremos con la necesidad de crear elementos como vistas, o tipos de contenido, que ya tenemos en el entorno de desarrollo, pero que habría que crear de nuevo en el entorno de producción.</p> <p>Aquí era donde empezaban los problemas con Drupal 7, ya que no había una forma nativa de exportar configuración, y aunque en algunos casos no nos llevaría mucho tiempo crear los componentes a mano en el otro entorno, no es una práctica nada recomendable, por mil razones, pero principalmente porque es fácil cometer errores y por la falta de un sistema de control de versiones que nos permita volver a un estado anterior.</p> <p>Teníamos que usar el módulo <em>Features</em>, que aunque fué una bendición, no estaba exento de sus propios demonios, como problemas de sobreescritura (<em>Overrides</em>) en algunos componentes, <em>Features</em> que no se pueden revertir, dependencias y conflictos entre distintas Features, y un largo etcétera.</p> <p>Además, en muchos casos cada módulo almacenaba la configuración como podía o quería. Una opción bastante común era usar variables, pero al final esa tabla era como un agujero negro donde cada módulo podía dejar cualquier cosa, ya que era fácil exportar después esas variables con <em>Features</em> y <em>Strongarm</em>, pero podía descontrolarse muy fácilmente. </p> <p>La nueva gestión de configuración que hace Drupal 8 pretende ayudar a solucionar todos esos problemas, y de momento parece que lo esta consiguiendo.</p> <p>Para empezar, ahora cada módulo puede definir su propia configuración en un archivo <em>Yaml</em> dentro de su directorio <em>config</em>. Este archivo contendrá todos los valores de configuración inicial que el módulo necesita. Por ejemplo, un viejo conocido como <em>Pathauto</em> tiene esta configuración:</p> <pre> enabled_entity_types: - user punctuation: hyphen: 1 verbose : FALSE separator : '-' max_length : 100 max_component_length: 100 transliterate : TRUE reduce_ascii : FALSE case : TRUE ignore_words : 'a, an, as, at, before, but, by, for, from, is, in, into, like, of, off, on, onto, per, since, than, the, this, that, to, up, via, with' update_action : 2 </pre> <p>Esto complica un poco la creación de módulos, pero a cambio permite tener perfectamente estructurada la configuración de cualquier cosa. Por ejemplo, esto es un fragmento del tipo de contenido que uso para publicar artículos en esta página:</p> <pre> uuid: 8cec381d-8247-47b3-b899-ed0adfb3bd36 langcode: en status: true dependencies: config: - core.entity_view_mode.node.teaser - field.field.node.blog_post.body - field.field.node.blog_post.comment - field.field.node.blog_post.field_blog_post_image - field.field.node.blog_post.field_tags - field.field.node.blog_post.field_teaser - image.style.teaser - node.type.blog_post module: - ds - image - text - user ... continúa, es muy muy largo </pre> <p>Lo que nos va a permitir este sistema es poder exportar a un puñado de archivos toda la configuración de nuestro sitio Drupal, copiarlos, moverlos, pasarlos a un control de versiones tipo Git, y después importarlos en otro de nuestros entornos para tener disponibles todos los nuevos cambios o componentes.</p> <p>Todo puede hacerse desde la administración de Drupal, pero es mucho más cómo utilizar <em>Drush</em> para todo el proceso.</p> <p>Vamos a ver entonces cómo trabajar con la configuración.<br />  </p> <h2>Configuración inicial</h2> <p>Por defecto cuando instalamos Drupal 8 va a guardar la configuración en un directorio con nombre aleatorio, dentro de la carpeta <em>files</em>. Algo así:</p> <pre> sites/default/files/config_XXXX</pre> <p>Dentro de ese directorio se podrán ir almacenando los ficheros de configuración. Lo ideal sería mantener los archivos de configuración fuera del <em>DocumentRoot</em> de nuestro servidor web para que no sean accesibles, e incluirlos en nuestro sistema de control de versiones. Para cambiar este directorio de configuración podemos añadir lo siguiente en el archivo <em>settings.php </em>de nuestro sitio:</p> <pre> $config_directories = array( CONFIG_SYNC_DIRECTORY =&gt; '/directorio/de/configuracion/sync', );</pre> <p>Ahora Drupal va a utilizar ese directorio para importar y exportar los archivos.<br />  </p> <h2>Exportar configuración</h2> <p>Como ya hemos comentado, tenemos dos opciones para hacer la exportación, bien desde el menú de administración o utilizando <em>Drush</em>. Podemos acceder desde Inicio  <em>Administración</em> &gt; <em>Configuración</em> &gt; <em>Desarrollo</em> &gt; <em>Sincronizar</em>, y:</p> <ul><li>Podemos exportar todo en un único fichero comprimido que y descargarlo desde el servidor con el navegador directamente a nuestro disco. Esto puede ser muy útil en determinados momentos si no tenemos un acceso por SSH a mano, por ejemplo.</li> <li>Exportar componentes individuales, seleccionando el que nos interese. Esto nos mostrará la configuración de ese elemento en formato Yaml, pero solo podremos copiarla y pegarla en un fichero o en otro entorno de forma manual.</li> </ul><p>Sin embargo usando Drush podemos generar de una vez todos los ficheros en nuestro directorio <em>sync</em>, con el comando <a href="https://drushcommands.com/drush-8x/config/config-export/"><em>config-export</em></a>, o su alias <em>cex</em>:</p> <pre> drush cex</pre> <p>Cuando termine el comando tendremos un archivo <em>.yml</em> por cada elemento de configuración de nuestro sitio. Además, tiene algunas opciones que pueden ser interesantes, como <em>--add</em> o <em>--commit</em> para incorporar automáticamente los cambios a nuestro repositorio Git.</p> <h2>Importar configuración</h2> <p>Para realizar el proceso inverso e importar configuración con nuevas funcionalidades o cambios, podremos utilizar la interfaz de administración de manera análoga a la exportación:</p> <ul><li>Importar toda la configuración en un único archivo comprimido,</li> <li>Importar configuraciones individuales, pegando el contenido en formato Yaml en la caja de texto.</li> </ul><p>De nuevo la mejor opción suele ser usar Drush, para lo que primero colocaríamos los ficheros de configuración que queremos importar en nuestro directorio <em>sync</em>, bien a mano, con un script o con <em>git pull</em>, y después ejecutamos <a href="https://drushcommands.com/drush-8x/config/config-import/"><em>config-import</em></a>, o su alias corto:</p> <pre> drush cim</pre> <p>Este comando nos mostrará primero las configuraciones que van a modificarse al importar, indicando si son nuevos bloques, modificaciones o eliminaciones, como se puede ver en la siguiente captura:</p> <p><img alt="Ejemplo de drush config-import" class="img-responsive shadow" data-entity-type="file" data-entity-uuid="f6605c50-4489-4c79-b553-6456b17cd4d4" src="/sites/default/files/inline-images/drush%20config-import.png" /></p> <p>Hay que tener cuidado, porque una vez aceptemos los cambios se modificará nuestra instalación de Drupal, con lo que podemos perder información, por lo que siempre es recomendable sacar una copia de la base de datos antes de hacer la importación.</p> <p>Puede ser interesante utilizar la opción <em>--partial,</em> que no eliminará la configuración de elementos si faltan sus archivos de configuración.</p> <h2>Otras operaciones con Drush</h2> <p>Usando <em>Drush</em> también es posible obtener una lista de todas las configuraciones disponibles:</p> <pre> drush config-list</pre> <p>Una vez sepamos el nombre de la configuración que buscamos podemos obtenerla y editarla directamente desde la consola con el editor que tengamos configurado por defecto, y se guardará como configuración activa.</p> <p>Por último, tenemos un comando que nos permite importar directamente la configuración desde otro sitio Drupal directamente usando los alias de <em>Drush</em>:</p> <pre> drush config-pull @produccion @desarrollo</pre> <p>Este comando exportará la configuración del sitio <em>@produccion</em> y la importará en <em>@desarrollo</em>, guardando además los correspondientes archivos de configuración en el directorio sync de <em>@desarrollo</em>. </p> <h2>Conclusiones</h2> <p>Hasta ahora todo lo que he hecho con la configuración de Drupal 8 ha funcionado prácticamente sin problemas, y tener toda esta capacidad incluida en el core es sin duda un gran paso adelante. Todavía tendremos que ver cómo se comporta o que problemas pueden surgir en proyectos grandes y complejos, o cuando entren en juego elementos más complicados como lo que podíamos hacer en Drupal 7 con <em>Features overrides</em>, por ejemplo.</p> <p>De momento, y como ya ha apuntado mucha gente, me parece que este es el gran cambio que va a aportar Drupal 8 con respecto a la versión anterior.</p> <p> </p> <h2>Referencias</h2> <p><a href="https://drushcommands.com/drush-8x/config/">https://drushcommands.com/drush-8x/config/</a></p> <p><a href="https://chromatichq.com/blog/drupal-7-features-vs-drupal-8-configuration-management">https://chromatichq.com/blog/drupal-7-features-vs-drupal-8-configuratio…</a></p> <p><a href="https://www.drupal.org/docs/8/configuration-management/managing-your-sites-configuration">https://www.drupal.org/docs/8/configuration-management/managing-your-si…</a></p> </div> </div> <div class="group-footer"> <section> <h2>Añadir nuevo comentario</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=28&amp;2=comment&amp;3=comment" token="MhJr4jB4OBSufd7SKEhVwPShYGWGG1grOo6UzQp4acs"></drupal-render-placeholder> </section> </div> </article> Sat, 23 Sep 2017 10:45:51 +0000 root 28 at https://www.carloscarrascal.com Estrenando Drupal 8. Primeras impresiones https://www.carloscarrascal.com/blog/estrenando-drupal-8-primeras-impresiones <article data-history-node-id="26" class="node node--type-blog-post node--view-mode-rss group-one-column ds-2col-stacked-fluid clearfix"> <div class="group-header"> <div class="field field--name-node-title field--type-ds field--label-hidden field--item"><h1> Estrenando Drupal 8. Primeras impresiones </h1> </div> <div class="field field--name-node-post-date field--type-ds field--label-hidden field--item">Miércoles, Septiembre 20, 2017 - 20:47</div> </div> <div class="group-left"> <div class="field field--name-field-tags field--type-entity-reference field--label-hidden field--items"> <div class="field--item"><a href="/tags/drupal" hreflang="es">Drupal</a></div> <div class="field--item"><a href="/tags/d8" hreflang="es">D8</a></div> </div> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><p><img alt="Drupal 8 Logo" class="pull-left align-left" data-entity-type="file" data-entity-uuid="dfed7124-80eb-4a08-ada1-49379d92da00" src="/sites/default/files/inline-images/drupal%208%20logo%20CMYK%2072.png" width="100px" />La versión 8.0.0 de Drupal salió oficialmente en <a href="https://www.drupal.org/blog/drupal-800-released">noviembre de 2015</a>, así que bueno, solo he tardado dos años en actualizar mi Drupal 7. No esta mal.</p> <p>Desde entonces ha llovido mucho, y el estado general de Drupal 8 ha mejorado bastante, hay que reconocerlo, porque hasta esta versión 8.3.7 que acabo de instalar ha pasado de todo, pero aun así mi primera impresión es que todavía le falta mucho camino para estar realmente maduro, sobre todo a nivel de módulos contribuidos. Hay muchos de ellos clave en una instalación normal de Drupal 7 que aun están muy verdes en sus versiones para D8.</p> <p>Para este blog que es un proyecto muy, muy sencillo, esto es a grandes rasgos lo que tengo instalado:</p> <ul><li>Drupal: 8.3.7 corriendo sobre Apache en Debian.</li> <li>Tema personalizado basado en Bootstrap 8.x-3.5</li> <li><em>Pathauto</em> y <em>Menu Trail by Path</em> para controlar URLS y menús de forma sencilla.</li> <li><em>Recaptha</em> para los formularios y comentarios. Hasta ahora funciona muy bien, casi ningún comentario... xD</li> <li><em>XMLSitemap</em>, altamente recomendable para SEO y tener contento a Google.</li> <li><em>Metatag</em>, aunque aun no lo he configurado mucho.</li> <li><em>Google Analytics</em></li> <li><em>Display Suite</em> para controlar la presentación de los nodos porque ahorra muchas plantillas y me gusta.</li> <li><em>TagClouds</em> para la nube de etiquetas que aparece en la barra lateral, que parece que da un poco de vida, pero en una version de desarrollo, 8.x-1.x-dev.</li> <li><em>RelatedByTerms</em>, mi propio módulo <em>contrib</em> que he aprovechado para ponerme a picar en Drupal 8,</li> <li><em>SMTP</em> para el correo electrónico, gracias a Mailgun.</li> </ul><p>Todo esta funcionando mas o menos igual que antes con D7, pero como he dicho este es un proyecto muy sencillo. Vamos al grano...</p> <p> </p> <h2>La parte buena</h2> <p>A estas alturas ya se hablado y comentado mucho de Drupal 8, así que llego un poco tarde. Aún así, espero que este artículo pueda ser útil si aun no has dado el paso para empezar con Drupal 8, aunque solo sea como confirmación de lo que el resto de la gente cuenta.</p> <p>Lo primero a destacar son los módulos gordos que ahora están incluidos en el core de Drupal:</p> <ul><li><em>Views</em>, por fin.</li> <li><em>CKEditor</em> configurado de serie.</li> <li><em>Breakpoint</em> y <em>Reponsive image</em> para el control de imágenes en dispositivos móviles.</li> <li><em>Quick edit</em> es bastante molón.</li> <li>Los módulos de idioma: <em>Language</em>, <em>Configuration translation</em>, <em>Content translation</em> e <em>Interface translation</em>.</li> <li><em>Configuration manager</em>, una de las grandes mejoras de esta versión para trabajar de verdad con Drupal.</li> </ul><p>El segundo punto es que a nivel de gestión de contenido, todo sigue mas o menos en su sitio, por lo que el trabajo de los redactores (si los tenéis) no se verá muy afectado. A nivel de interfaz la verdad es que todo son mejoras.</p> <p>Mirad por ejemplo una captura de la edición rápida mientras escribía este artículo:</p> <img alt="Quick edit con Drupal 8" class="img-responsive shadow align-center" data-entity-type="file" data-entity-uuid="0a6debb7-f266-43b5-99a5-bfd42a9f0d8e" src="/sites/default/files/inline-images/Screen%20Shot%202017-09-20%20at%2020.07.40.png" /><p> </p> <p>Te permite editar el contenido en vivo, manteniendo y aplicando los estilos CSS mientras escribes. Sin instalar ningún módulo extra. Esto es un caramelo para enseñar a potenciales clientes. Queda genial en una demo y me tiene muy contento.</p> <p>Con el sistema de plantillas (templates) aún no me he metido a fondo, pero lo que he visto hasta ahora de Twig me gusta. Está mas orientado a separar el diseño y presentación de la página de la lógica de negocio que ahora si la podemos tener bien estructurada usando controladores, lo que me lleva a lo que para mi es a la vez la mayor mejora y posiblemente el mayor obstáculo que va a encontrar mucha gente para trabajar con Drupal 8 ...</p> <p><strong>El código</strong>. Nos olvidamos de viejas chapuzas escribiendo PHP barato, y ahora si, tenemos una arquitectura potente, escalable, con orientación a objetos de verdad: clases, interfaces, factorías, etc. y mejor separación entre código, configuración, lógica de negocio y presentación de datos.</p> <p>He disfrutado como un enano picando la versión para D8 de mi módulo <a href="https://www.drupal.org/project/relatedbyterms">RelatedByTerms</a>, y espero poder publicar en breve una versión inicial. Viniendo de tantos años en Java mi pequeño corazoncito Javero lloraba cuando al fin pude crear un controlador en un módulo de Drupal con sentido de verdad.</p> <p>El inconveniente de esto puede ser que técnicamente va a ser mas complejo trabajar con Drupal 8. Hay que tener una buena base de orientación a objetos y eso puede ser un problema para mucha gente que trabaja con Drupal 7, y que hasta ahora no necesitaban grandes esfuerzos de aprendizaje para escribir módulos sencillos.</p> <p>Por último, y ya se que llego tarde y ya se ha hablado mucho del tema, la gestión de la configuración que hace Drupal 8 sin necesidad de usar el módulo Features es otra de las grandes mejoras de esta versión. Exportar e importar configuración de un entorno a otro va a ser ahora un poquito mas fácil y creo que nos va a ahorrar unos cuantos juramentos en Arameo a mas de uno.</p> <p> </p> <h2>Las cosas malas</h2> <p>Cambiar el idioma por defecto sigue siendo una odisea. De hecho hay avisos por todos lados en las pantallas de configuración avisando de que no se haga. ¿Entonces para que tenemos esa opción?</p> <p>Muchos módulos que llevamos años usando en Drupal 7 todavía están muy verdes. Algunos ejemplos son <a href="https://www.drupal.org/project/context">Context</a>, aun en beta2, <a href="https://www.drupal.org/project/smtp">SMTP</a> que está en beta3, <a href="https://www.drupal.org/project/xmlsitemap">XMLSitemap</a>  que sigue en alpha2, y una larga lista. La parte positiva de esto es que si te apetece contribuir al desarrollo de Drupal, este es un gran momento para hacerlo.</p> <p>La otra desventaja, como ya he comentado antes es que aumenta la complejidad de crear nuestros módulos, y habrá que meter un buen número de horas de aprendizaje. Si piensas que vas a pasar de D7 a D8 en unos días, siento desilusionarte pero va a ser que no.</p> <p>Otra cosa que he notado, es que he visto bastantes errores al tratar de hacer cosas, como activar un módulo y tener un pantallazo blanco hasta que borras la cache, petardazos al guardar alguna pantalla de configuración, pero no tengo ejemplos concretos. Pasa de vez en cuando.</p> <p>Por ejemplo, los enlaces de la configuración de los contenidos y términos de la pestaña de configuración del módulo <em>XMLSitemap</em> están rotos, y hay que pasarlo a la version de desarrollo para que se arreglen, o acceder desde la pestaña de <em>Sitemap entities</em>, que si que funcionan, aunque tarde un rato en darme cuenta.</p> <p> </p> <h2>Problemas gordos</h2> <p>Las cosas mas chungas que me he encontrado durante la migración desde D7, no han sido muchas, pero si que son graciosas.</p> <p>Lo primero, hay unos módulos experimentales que vienen en el core para ayudarnos con la migración: <em>Migrate</em> (viejo conocido ya en D7), <em>Migrate Drupal</em> y <em>Migrate Drupal UI</em>. En mi caso era bastante sencillo, con un solo tipo de contenido custom, un par de vistas, términos de taxonomías estándar y poco mas...</p> <p>Pues la migración me la hice a mano. Con los pocos artículos que tengo publicados tardaba menos en copiar y pegar que ponerme a hacer un proceso de migración, ya que lo intenté con la importación automática y fue bastante desastroso:</p> <ul><li>Creó el tipo de contenido custom y las vistas, pero...</li> <li>Los contenidos del campo body no se copiaron.</li> <li>Cuando los intentaba poner a mano no se veían en las páginas.</li> <li>Las vistas no funcionaban bien.</li> <li>Las taxonomías tampoco...</li> </ul><p>Al final tuve que empezar otra vez tirando de un backup que hice antes de lanzar la migración. Acordaros de <strong>hacer un backup antes</strong> si queréis probarlo.</p> <p>Otro problema fue al activar el módulo <em>Context</em> (8.x-1.0-alpha1) con los módulos de traducción activados. Vais a ver una buena cantidad de pantallazos blancos, y este error:</p> <pre> <code>Error: Call to a member function getPath() on null in /var/www/whatever/web/core/modules/config_translation/src/ConfigNamesMapper.php on line 233</code></pre> <p>La <a href="https://www.drupal.org/node/2802253">incidencia</a> ya esta resuelta, pero hay que pasarse a la versión de desarrollo del módulo, porque aun no han publicado una release con este código arreglado. De momento no necesito el módulo y lo tengo desactivado.</p> <p> </p> <h2>Conclusiones</h2> <p>En general creo que todos los cambios de arquitectura van a contribuir mucho en llevar a Drupal a un mercado mas "profesional", y al mismo tiempo tener el efecto negativo de alejarlo un poco de todos los usuarios menos técnicos, como pueden ser agencias de diseño, o incluso diseñadores puros sin conocimientos avanzados de programación (conozco unos cuantos), que trabajan con Drupal como freelance para pequeños y medianos clientes.</p> <p>Por otro lado, me parece que mientras se madura el nivel de los módulos contribuidos vamos a tener Drupal 7 para una buena temporada, y no me extrañaría en absoluto <a href="http://chocolatelilyweb.ca/blog/coming-drupal-fork">que algún fork de Drupal</a> (véase <a href="https://backdropcms.org/">Backdrop CMS</a>) o similar se lleve el gato al agua y extienda la vida útil de Drupal 7 de forma indefinida.</p> <p>Por mi parte, seguiré formándome por mi cuenta en Drupal 8 y empezaré a preparar el plan de formación para mi equipo de trabajo en D8 para el año que viene.</p> <p> </p> </div> </div> <div class="group-footer"> <section> <h2>Añadir nuevo comentario</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=26&amp;2=comment&amp;3=comment" token="DlHzb4c3ll6eORQiZAxAlDl2xxGjcquBWx-IdVT-xfk"></drupal-render-placeholder> </section> </div> </article> Wed, 20 Sep 2017 18:47:33 +0000 root 26 at https://www.carloscarrascal.com