<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>drupal &amp;mdash; Arturo Linares</title>
    <link>https://arturo.linar.es/tag:drupal</link>
    <description></description>
    <pubDate>Fri, 19 Jun 2026 11:53:32 +0000</pubDate>
    <item>
      <title>Drupal development on Windows</title>
      <link>https://arturo.linar.es/drupal-development-windows?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[#drupal #windows&#xA;&#xA;Recently I wrote on how I replaced my MacBook Pro with a PC and still being productive when developing on several Drupal environments.While PopOS! is a wonderful Linux distro, I decided to give Windows a try. The primary reasons were the usual: hardware.&#xA;!--more--&#xA;The drivers are optimized to work on Windows and some small features that are nice-to-have only work on this controversial OS: Login with an infrared camera (Windows Hello), a solid workspace with several monitors (maybe this is the most important reason), better Dolby sound from the speakers and battery optimizations. Nothing critical really, but things that improve the experience.&#xA;&#xA;What means to have a productive development environment?&#xA;&#xA;This:&#xA;&#xA;Bash command line.&#xA;Fast docker environment (without unison, I&#39;ve had enough problems with it already).&#xA;Working git&#xA;Be able to launch several development environments, fast, from command line, and use custom domain names to access them and its related services.&#xA;A good editor, like Sublime Text or Visual Studio Code. (I know, I don&#39;t use PHP Storm and don&#39;t want to).&#xA;&#xA;Since I had tried WSL before (and failed), I decided to get rid of it and move directly to install a VM.&#xA;&#xA;An Ubuntu server checks first three items in the above list, but the next two can get messy. Sharing source code files with the Windows host as a Samba share defeats the purpose because its bad performance, symlinks weirdness, among other things. Sharing it using sshfs could be an option, but then how could I control docker?&#xA;&#xA;Editor&#xA;&#xA;The solution came on a recent iteration on Visual Studio Code: Remote code extensions, specifically, Remote Coding via SSH. This extension allowed me to open remote directories through SSH and work with them. This is not like running Dreamweaver to edit files on a server (remember?). It works splitting the editor in two pieces, a client (the UI) and a server, where the indexing and language processing extensions run. The result is a very responsive editor, while working remotely with all the extensions I would normally use. Even the integrated terminal in the editor opens automatically a bash prompt in the server, a really useful feature.&#xA;&#xA;This remote feature in Visual Studio Code gives me the full Linux experience right from the editor.&#xA;&#xA;However, I normally access docker websites using custom URLs, a pretty common practice. If using docksal, for example: super-awesome-client.docksal, db.super-awesome-client.docksal, etc., you get the idea. I needed to find a way to re-route custom URLs to my VM.&#xA;&#xA;Networking&#xA;&#xA;On a Mac or on Linux this would work out of the box after installing docksal (or lando or whatever docker wrapper you use). I wanted this to work the same way:&#xA;&#xA;I didn&#39;t want to configure each custom website manually&#xA;This had to work on new sites created with docksal&#xA;Should work on any network&#xA;&#xA;To achieve this transparent routing, I needed a static IP for the VM, so I could always reference it if I was at home, at a coffee shop, or in an airport. I&#39;m using Hyper-v, so I created a NAT using the instructions below:&#xA;&#xA;Windows 10: How to setup NAT network for Hyper-V guests?br&#xA;br&#xA;Then in the VM, configured the static IP address using netplan, created file /etc/netplan/01-netcfg.yaml (I used the same IP address as the guide to create the NAT above).&#xA;&#xA;network:&#xA;    ethernets:&#xA;        eth0:&#xA;            dhcp4: no&#xA;            dhcp6: no&#xA;            addresses: [192.168.200.11/24]&#xA;            gateway4: 192.168.200.1&#xA;            nameservers:&#xA;                addresses: [8.8.8.8]&#xA;&#xA;After enabling the ssh server in the VM, and configuring my ssh keys I was able to jump into the server from Windows very quickly, or even configure ConEmu or the new Windows Terminal to automatically connect to ssh.&#xA;&#xA;To access the web servers running in docker containers created with docksal, I installed a DNS proxy: Acrylic DNS Proxy, and used this hosts configuration:&#xA;&#xA;192.168.200.11 *.docksal&#xA;&#xA;Visual Studio Code Configuration&#xA;&#xA;In Visual Studio Code I installed the Remote Development extensions for SSH. The configuration was straight forward and worked really well (the extension guides you until you are successfully connected to a SSH server).&#xA;&#xA;Huge Drupal projects were indexed really fast and the performance when browsing them was like working on Linux. Debugging worked out of the box (this thanks to Docksal configs, but still, using docker-compose shouldn&#39;t be a problem).&#xA;&#xA;The only downside I&#39;ve found is that I can&#39;t commit, push or pull using the editor, which is not a big deal since I can use the integrated terminal. From the documentation:&#xA;&#xA;  If you clone a Git repository using SSH and your SSH key has a passphrase, VS Code&#39;s pull and sync features may hang when running remotely. Either use an SSH key without a passphrase, clone using HTTPS, or run git push from the command line to work around the issue.&#xA;&#xA;I ended up creating an SSH key for my repos without a passphrase.&#xA;&#xA;Recap&#xA;&#xA;Working on PHP natively on Windows it is still impossible, but using a VM and Visual Studio Code with Remote Code extensions works very well, with the price of more memory usage, but memory is cheap now, and that is one of the reasons I left Apple in the first place: to have an upgradable development computer.&#xA;&#xA;One unexpected bonus is that I can recover from suspend and the VM will load up with all services that were running, something that in Linux didn&#39;t happen; I had to to run fin up or docker-compose up again after the laptop woke.&#xA;&#xA;Aside: WSL2&#xA;&#xA;The Windows Subsystem for Linux v2 is about_ to be released, which uses a custom Linux Kernel in a very lightweight VM (it is already in Windows Internals fast ring, but expected to reach public release until 2020).&#xA;&#xA;In theory, it has all the same good points of this setup with a VM, and seems so be a great setup for a future development environment because of these:&#xA;&#xA;The VM file system can be accessed directly from Windows using a p9 file system (that alone is awesome).&#xA;The performance is very close to native one.&#xA;Shared memory and processor cores.&#xA;Much tighter integration, it is possible to run windows programs from within the VM.&#xA;&#xA;This technology can really change the development experience on Windows, lets see if it is up to the expectations when it is released (because I won&#39;t subscribe to the fast ring in my main development computer).&#xA;&#xA;Stranger things&#xA;&#xA;While setting the environment I found an error that didn&#39;t allow me to clone big repos or to push changes. It turned out to be a bug in my WiFi drivers. So if you find the error below and have an Intel WiFi card, update your drivers.&#xA;&#xA;$ git clone git@somrerepo-bla-bla.git&#xA;Cloning into &#39;somedir&#39;...&#xA;error: RPC failed; curl 56 GnuTLS recv error (-12): A TLS fatal alert has been received.&#xA;fatal: The remote end hung up unexpectedly&#xA;&#xA;`]]&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://arturo.linar.es/tag:drupal" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">drupal</span></a> <a href="https://arturo.linar.es/tag:windows" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">windows</span></a></p>

<p><img src="https://user.fm/files/v2-44bb602b05cc72314032900b5c5dba15/image_5.png" alt=""/></p>

<p>Recently I wrote on how I replaced my MacBook Pro with a PC and still being productive when developing on several Drupal environments.While <a href="https://system76.com/pop" rel="nofollow">Pop_OS!</a> is a wonderful Linux distro, I decided to give Windows a try. The primary reasons were the usual: hardware.

The drivers are optimized to work on Windows and some small features that are nice-to-have only work on this controversial OS: Login with an infrared camera (<em>Windows Hello</em>), a solid workspace with several monitors (maybe this is the most important reason), better Dolby sound from the speakers and battery optimizations. Nothing critical really, but things that improve the experience.</p>

<h2 id="what-means-to-have-a-productive-development-environment" id="what-means-to-have-a-productive-development-environment">What means to have a productive development environment?</h2>

<p>This:</p>
<ol><li>Bash command line.</li>
<li>Fast docker environment (without unison, I&#39;ve had enough problems with it already).</li>
<li>Working git</li>
<li>Be able to launch several development environments, fast, from command line, and use custom domain names to access them and its related services.</li>
<li>A good editor, like Sublime Text or Visual Studio Code. (I know, I don&#39;t use PHP Storm and don&#39;t want to).</li></ol>

<p>Since I had tried WSL before (and failed), I decided to get rid of it and move directly to install a VM.</p>

<p>An Ubuntu server checks first three items in the above list, but the next two can get messy. Sharing source code files with the Windows host as a Samba share defeats the purpose because its bad performance, symlinks weirdness, among other things. Sharing it using <code>sshfs</code> could be an option, but then how could I control docker?</p>

<h2 id="editor" id="editor">Editor</h2>

<p>The solution came on a recent iteration on Visual Studio Code: <strong>Remote code extensions</strong>, specifically, <strong>Remote Coding via SSH</strong>. This extension allowed me to open remote directories through SSH and work with them. This is not like running Dreamweaver to edit files on a server (remember?). <strong>It works splitting the editor in two pieces, a client (the UI) and a server</strong>, where the indexing and language processing extensions run. The result is a very responsive editor, while working <em>remotely</em> with all the extensions I would normally use. Even the integrated terminal in the editor opens automatically a bash prompt in the server, a really useful feature.</p>

<p>This remote feature in Visual Studio Code gives me the full Linux experience right from the editor.</p>

<p>However, I normally access docker websites using custom URLs, a pretty common practice. If using <a href="http://docksal.io" rel="nofollow">docksal</a>, for example: <code>super-awesome-client.docksal</code>, <code>db.super-awesome-client.docksal</code>, etc., you get the idea. I needed to find a way to re-route custom URLs to my VM.</p>

<h2 id="networking" id="networking">Networking</h2>

<p>On a Mac or on Linux this would work out of the box after installing docksal (or lando or whatever docker wrapper you use). I wanted this to work the same way:</p>
<ul><li>I didn&#39;t want to configure each custom website manually</li>
<li>This had to work on new sites created with docksal</li>
<li>Should work on any network</li></ul>

<p>To achieve this transparent routing, I needed a static IP for the VM, so I could always reference it if I was at home, at a coffee shop, or in an airport. I&#39;m using Hyper-v, so I created a NAT using the instructions below:</p>

<p><a href="https://anandthearchitect.com/2018/01/06/windows-10-how-to-setup-nat-network-for-hyper-v-guests/" rel="nofollow">Windows 10: How to setup NAT network for Hyper-V guests?</a><br>
<br>
Then in the VM, <strong>configured the static IP</strong> address using <code>netplan</code>, created file <code>/etc/netplan/01-netcfg.yaml</code> (I used the same IP address as the guide to create the NAT above).</p>

<pre><code class="language-yaml">network:
    ethernets:
        eth0:
            dhcp4: no
            dhcp6: no
            addresses: [192.168.200.11/24]
            gateway4: 192.168.200.1
            nameservers:
                addresses: [8.8.8.8]

</code></pre>

<p>After <strong>enabling the ssh server in the VM</strong>, and configuring my ssh keys I was able to jump into the server from Windows very quickly, or even configure <a href="https://conemu.github.io/" rel="nofollow">ConEmu</a> or the new <a href="https://github.com/microsoft/terminal" rel="nofollow">Windows Terminal</a> to automatically connect to ssh.</p>

<p>To access the web servers running in docker containers created with docksal, <strong>I installed a DNS proxy</strong>: <a href="http://mayakron.altervista.org/wikibase/show.php?id=AcrylicHome" rel="nofollow">Acrylic DNS Proxy</a>, and used this <code>hosts</code> configuration:</p>

<pre><code>192.168.200.11 *.docksal

</code></pre>

<h2 id="visual-studio-code-configuration" id="visual-studio-code-configuration">Visual Studio Code Configuration</h2>

<p>In Visual Studio Code I installed the <a href="https://code.visualstudio.com/docs/remote/ssh" rel="nofollow">Remote Development extensions for SSH</a>. The configuration was straight forward and worked really well (the extension guides you until you are successfully connected to a SSH server).</p>

<p>Huge Drupal projects were indexed really fast and the performance when browsing them was like working on Linux. Debugging worked out of the box (this thanks to Docksal configs, but still, using <code>docker-compose</code> shouldn&#39;t be a problem).</p>

<p>The only downside I&#39;ve found is that I can&#39;t commit, push or pull using the editor, which is not a big deal since I can use the integrated terminal. From the <a href="https://code.visualstudio.com/docs/remote/ssh#_known-limitations" rel="nofollow">documentation</a>:</p>

<blockquote><p>If you clone a Git repository using SSH and your SSH key has a passphrase, VS Code&#39;s pull and sync features may hang when running remotely. Either use an SSH key without a passphrase, clone using HTTPS, or run git push from the command line to work around the issue.</p></blockquote>

<p>I ended up creating an SSH key for my repos without a passphrase.</p>

<h2 id="recap" id="recap">Recap</h2>

<p>Working on PHP natively on Windows it is still impossible, but using a VM and Visual Studio Code with Remote Code extensions works very well, with the price of more memory usage, but memory is cheap now, and that is one of the reasons I left Apple in the first place: to have an upgradable development computer.</p>

<p>One unexpected bonus is that I can recover from suspend and the VM will load up with all services that were running, something that in Linux didn&#39;t happen; I had to to run <code>fin up</code> or <code>docker-compose up</code> again after the laptop woke.</p>

<h2 id="aside-wsl2" id="aside-wsl2">Aside: WSL2</h2>

<p>The Windows Subsystem for Linux v2 is <em>about</em> to be released, which uses a custom Linux Kernel in a very lightweight VM (it is already in Windows Internals fast ring, but expected to reach public release until 2020).</p>

<p>In theory, it has all the same good points of this setup with a VM, and seems so be a great setup for a <a href="https://www.youtube.com/watch?v=A0eqZujVfYU" rel="nofollow">future development environment</a> because of these:</p>
<ul><li>The VM file system can be accessed directly from Windows using a p9 file system (that alone is awesome).</li>
<li>The performance is very close to native one.</li>
<li>Shared memory and processor cores.</li>
<li>Much tighter integration, it is possible to run windows programs from within the VM.</li></ul>

<p>This technology can really change the development experience on Windows, lets see if it is up to the expectations when it is released (because I won&#39;t subscribe to the fast ring in my main development computer).</p>

<h2 id="stranger-things" id="stranger-things">Stranger things</h2>

<p>While setting the environment <a href="/en/blog/git-errors-when-using-hyper-v" rel="nofollow">I found an error</a> that didn&#39;t allow me to clone big repos or to push changes. It turned out to be a bug in my WiFi drivers. So if you find the error below and have an Intel WiFi card, update your drivers.</p>

<pre><code>$ git clone git@somrerepo-bla-bla.git
Cloning into &#39;somedir&#39;...
error: RPC failed; curl 56 GnuTLS recv error (-12): A TLS fatal alert has been received.
fatal: The remote end hung up unexpectedly

</code></pre>
]]></content:encoded>
      <guid>https://arturo.linar.es/drupal-development-windows</guid>
      <pubDate>Thu, 26 Sep 2019 05:04:31 +0000</pubDate>
    </item>
    <item>
      <title>How to make a title block with a custom background</title>
      <link>https://arturo.linar.es/how-make-title-block-custom-background?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[drupal&#xA;&#xA;A common request I’ve received lately when building sites in Drupal is to make the background of the page title configurable to allow the user upload and change it.&#xA;&#xA;!--more--&#xA;&#xA;Of course, this could be solved with a new block type with two fields, one for the image and one for the title. But then we would need to add the block for each page where we want to show the title.&#xA;&#xA;How does Drupal handle page titles? Looking in the block admin page, I found that actually it is a block. Well, we need the same functionality of that block in our new one.&#xA;&#xA;So looking for the class that contains the block machine name page\title\block I found Drupal\Core\Block\Plugin\Block\PageTitleBlock which implements TitleBlockPluginInterface. This is all the interface:&#xA;&#xA;    interface TitleBlockPluginInterface extends BlockPluginInterface {&#xA;&#xA;      /*&#xA;       Sets the title.&#xA;        @param string|array $title&#xA;       The page title: either a string for plain titles or a render array for&#xA;       formatted titles.&#xA;       /&#xA;      public function setTitle($title);&#xA;&#xA;    }&#xA;&#xA;Searching what uses that interface, I found that BlockPageVariant uses it to assign it a title:&#xA;&#xA;    foreach ($this-  blockRepository-  getVisibleBlocksPerRegion($cacheablemetadatalist) as $region =  $blocks) {&#xA;          / @var $blocks \Drupal\block\BlockInterface[] /&#xA;          foreach ($blocks as $key =  $block) {&#xA;            $blockplugin = $block-  getPlugin();&#xA;            if ($blockplugin instanceof MainContentBlockPluginInterface) {&#xA;              $blockplugin-  setMainContent($this-  mainContent);&#xA;              $maincontentblockdisplayed = TRUE;&#xA;            }&#xA;            elseif ($blockplugin instanceof TitleBlockPluginInterface) {&#xA;              $blockplugin-  setTitle($this-  title);&#xA;            }&#xA;      //...&#xA;&#xA;This means that if I create a custom block that implements TitleBlockPluginInterface I will get the page title automatically (thanks to BlockPageVariant)? Apparently, yes.&#xA;&#xA;Lets start writing the custom block extending PageTitleBlock to use the title field implementation:&#xA;&#xA;    /*&#xA;     @Block(&#xA;     id = &#34;myimagetitleblock&#34;,&#xA;     adminlabel = &#34;Image title block&#34;&#xA;     )&#xA;     /&#xA;    class ImageTitleBlock extends PageTitleBlock {&#xA;&#xA;      /*&#xA;       The image field.&#xA;        @var mixed&#xA;       /&#xA;      protected $image;&#xA;&#xA;      public function defaultConfiguration() {&#xA;        return [&#xA;          &#39;image&#39; =  [&#xA;            &#39;value&#39; =  &#39;&#39;,&#xA;          ],&#xA;          &#39;labeldisplay&#39; =  FALSE,&#xA;        ] + parent::defaultConfiguration();&#xA;      }&#xA;&#xA;      /&#xA;       {@inheritdoc}&#xA;       /&#xA;      public function build() {&#xA;        $build = [];&#xA;&#xA;        if (isset($this-  configuration[&#39;image&#39;])&#xA;          &amp;&amp; !empty($this-  configuration[&#39;image&#39;])) {&#xA;&#xA;          $imagefield = $this-  configuration[&#39;image&#39;];&#xA;          $imageuri = File::load($imagefield[0]);&#xA;&#xA;          $build[&#39;image&#39;] = [&#xA;            &#39;uri&#39; =  ImageStyle::load(&#39;titlebar&#39;)-  buildUrl($imageuri-  getFileUri()),&#xA;          ];&#xA;        } else {&#xA;          $build&#39;image&#39; = &#39;[&#39; . t(&#39;Picture&#39;) . &#39;]&#39;;&#xA;        }&#xA;&#xA;        return $build + parent::build();&#xA;      }&#xA;&#xA;      /*&#xA;       {@inheritdoc}&#xA;       /&#xA;      public function blockForm($form, FormStateInterface $formstate) {&#xA;&#xA;        $form[&#39;image&#39;] = array(&#xA;          &#39;#type&#39; =  &#39;managedfile&#39;,&#xA;          &#39;#uploadlocation&#39; =  &#39;public://images/&#39;,&#xA;          &#39;#title&#39; =  $this-  t(&#39;Image&#39;),&#xA;          &#39;#description&#39; =  $this-  t(&#34;Background image&#34;),&#xA;          &#39;#defaultvalue&#39; =  $this-  configuration[&#39;image&#39;],&#xA;          &#39;#uploadvalidators&#39; =  array(&#xA;            &#39;filevalidateextensions&#39; =  array(&#39;gif png jpg jpeg&#39;),&#xA;            &#39;filevalidatesize&#39; =  array(25600000),&#xA;          ),&#xA;          &#39;#states&#39; =  array(&#xA;            &#39;visible&#39; =  array(&#xA;              &#39;:input[name=&#34;imagetype&#34;]&#39; =  array(&#39;value&#39; =  t(&#39;Upload New Image&#39;)),&#xA;            )&#xA;          )&#xA;        );&#xA;&#xA;        return $form;&#xA;      }&#xA;&#xA;      public function validateConfigurationForm(array &amp;$form, FormStateInterface $formstate) {&#xA;        if (!$formstate-  getValue(&#39;image&#39;)) {&#xA;          $formstate-  setError($form[&#39;image&#39;], $this-  t(&#39;Select an image&#39;));&#xA;        }&#xA;      }&#xA;&#xA;      /*&#xA;       {@inheritdoc}&#xA;       /&#xA;      public function blockSubmit($form, FormStateInterface $formstate) {&#xA;        / Fetch the array of the file stored temporarily in database /&#xA;        $image = $formstate-  getValue(&#39;image&#39;);&#xA;&#xA;        $this-  configuration[&#39;image&#39;] = $image;&#xA;&#xA;        / Load the object of the file by it&#39;s fid /&#xA;        $file = \Drupal\file\Entity\File::load($image[0]);&#xA;&#xA;        / Set the status flag permanent of the file object /&#xA;        $file-  setPermanent();&#xA;&#xA;        / Save the file in database /&#xA;        $file-  save();&#xA;      }&#xA;    }&#xA;&#xA;After clearing Drupal cache, you will find your new shiny block waiting for you in the Add block list. The build method returns the processed image URL (using titlebar style) instead of the render array for an image so we can use it later.&#xA;&#xA;Let’s create titlebar image style and configure it accordingly (in my case, resize and crop to a predefined size will do).&#xA;&#xA;Then, update your theme to make sure that image url shows in a style attribute, or a data-* if you’re using javascript to load the image.&#xA;&#xA;block—image-title-block.html.twig&#xA;&#xA;    &lt;div{{ attributes.setAttribute(&#39;style&#39;,&#xA;        &#39;background: #787c8a url(&#39; ~ content.image.uri ~ &#39;) no-repeat center;&#39;) }}  {{ titleprefix }}&#xA;      {% if label %}&#xA;        h2{{ titleattributes }}{{ label }}/h2&#xA;      {% endif %}&#xA;      {{ titlesuffix }}&#xA;      {% block content %}&#xA;        {{ content }}&#xA;      {% endblock %}&#xA;    /div&#xA;    ...&#xA;&#xA;Lets test it!&#xA;&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://arturo.linar.es/tag:drupal" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">drupal</span></a></p>

<p>A common request I’ve received lately when building sites in Drupal is to make the background of the page title configurable to allow the user upload and change it.</p>

<p><img src="https://i.snap.as/douIYIb.png" alt=""/></p>



<p>Of course, this could be solved with a new block type with two fields, one for the image and one for the title. But then we would need to add the block for each page where we want to show the title.</p>

<p>How does Drupal handle page titles? Looking in the block admin page, I found that actually it is a block. Well, we need the same functionality of that block in our new one.</p>

<p>So looking for the class that contains the block machine name <em>page_title_block</em> I found <code>Drupal\Core\Block\Plugin\Block\PageTitleBlock</code> which implements <code>TitleBlockPluginInterface</code>. This is all the interface:</p>

<pre><code class="language-php">    interface TitleBlockPluginInterface extends BlockPluginInterface {

      /**
       * Sets the title.
       *
       * @param string|array $title
       *   The page title: either a string for plain titles or a render array for
       *   formatted titles.
       */
      public function setTitle($title);

    }

</code></pre>

<p>Searching what uses that interface, I found that <code>BlockPageVariant</code> uses it to assign it a title:</p>

<pre><code class="language-php">    foreach ($this-&gt;blockRepository-&gt;getVisibleBlocksPerRegion($cacheable_metadata_list) as $region =&gt; $blocks) {
          /** @var $blocks \Drupal\block\BlockInterface[] */
          foreach ($blocks as $key =&gt; $block) {
            $block_plugin = $block-&gt;getPlugin();
            if ($block_plugin instanceof MainContentBlockPluginInterface) {
              $block_plugin-&gt;setMainContent($this-&gt;mainContent);
              $main_content_block_displayed = TRUE;
            }
            elseif ($block_plugin instanceof TitleBlockPluginInterface) {
              $block_plugin-&gt;setTitle($this-&gt;title);
            }
      //...

</code></pre>

<p>This means that if I create a custom block that implements <code>TitleBlockPluginInterface</code> I will get the page title automatically (thanks to <code>BlockPageVariant</code>)? Apparently, yes.</p>

<p>Lets start writing the custom block extending <code>PageTitleBlock</code> to use the title field implementation:</p>

<pre><code class="language-php">    /**
     * @Block(
     *   id = &#34;my_image_title_block&#34;,
     *   admin_label = &#34;Image title block&#34;
     * )
     */
    class ImageTitleBlock extends PageTitleBlock {

      /**
       * The image field.
       *
       * @var mixed
       */
      protected $image;

      public function defaultConfiguration() {
        return [
          &#39;image&#39; =&gt; [
            &#39;value&#39; =&gt; &#39;&#39;,
          ],
          &#39;label_display&#39; =&gt; FALSE,
        ] + parent::defaultConfiguration();
      }

      /**
       * {@inheritdoc}
       */
      public function build() {
        $build = [];

        if (isset($this-&gt;configuration[&#39;image&#39;])
          &amp;&amp; !empty($this-&gt;configuration[&#39;image&#39;])) {

          $image_field = $this-&gt;configuration[&#39;image&#39;];
          $image_uri = File::load($image_field[0]);

          $build[&#39;image&#39;] = [
            &#39;uri&#39; =&gt; ImageStyle::load(&#39;title_bar&#39;)-&gt;buildUrl($image_uri-&gt;getFileUri()),
          ];
        } else {
          $build[&#39;image&#39;][&#39;#markup&#39;] = &#39;[&#39; . t(&#39;Picture&#39;) . &#39;]&#39;;
        }


        return $build + parent::build();
      }

      /**
       * {@inheritdoc}
       */
      public function blockForm($form, FormStateInterface $form_state) {

        $form[&#39;image&#39;] = array(
          &#39;#type&#39; =&gt; &#39;managed_file&#39;,
          &#39;#upload_location&#39; =&gt; &#39;public://images/&#39;,
          &#39;#title&#39; =&gt; $this-&gt;t(&#39;Image&#39;),
          &#39;#description&#39; =&gt; $this-&gt;t(&#34;Background image&#34;),
          &#39;#default_value&#39; =&gt; $this-&gt;configuration[&#39;image&#39;],
          &#39;#upload_validators&#39; =&gt; array(
            &#39;file_validate_extensions&#39; =&gt; array(&#39;gif png jpg jpeg&#39;),
            &#39;file_validate_size&#39; =&gt; array(25600000),
          ),
          &#39;#states&#39; =&gt; array(
            &#39;visible&#39; =&gt; array(
              &#39;:input[name=&#34;image_type&#34;]&#39; =&gt; array(&#39;value&#39; =&gt; t(&#39;Upload New Image&#39;)),
            )
          )
        );

        return $form;
      }

      public function validateConfigurationForm(array &amp;$form, FormStateInterface $form_state) {
        if (!$form_state-&gt;getValue(&#39;image&#39;)) {
          $form_state-&gt;setError($form[&#39;image&#39;], $this-&gt;t(&#39;Select an image&#39;));
        }
      }

      /**
       * {@inheritdoc}
       */
      public function blockSubmit($form, FormStateInterface $form_state) {
        /* Fetch the array of the file stored temporarily in database */
        $image = $form_state-&gt;getValue(&#39;image&#39;);

        $this-&gt;configuration[&#39;image&#39;] = $image;

        /* Load the object of the file by it&#39;s fid */
        $file = \Drupal\file\Entity\File::load($image[0]);

        /* Set the status flag permanent of the file object */
        $file-&gt;setPermanent();

        /* Save the file in database */
        $file-&gt;save();
      }
    }

</code></pre>

<p>After clearing Drupal cache, you will find your new shiny block waiting for you in the <em>Add block</em> list. The build method returns the processed image URL (using <em>title</em>bar_ style) instead of the render array for an image so we can use it later.</p>

<p>Let’s create <em>title</em>bar_ image style and configure it accordingly (in my case, resize and crop to a predefined size will do).</p>

<p><img src="https://i.snap.as/ewM5PVG.png" alt=""/></p>

<p>Then, update your theme to make sure that image url shows in a <code>style</code> attribute, or a <code>data-*</code> if you’re using javascript to load the image.</p>

<p><em>block—image-title-block.html.twig</em></p>

<pre><code class="language-twig">    &lt;div{{ attributes.setAttribute(&#39;style&#39;,
        &#39;background: #787c8a url(&#39; ~ content.image.uri ~ &#39;) no-repeat center;&#39;) }}&gt;
      {{ title_prefix }}
      {% if label %}
        &lt;h2{{ title_attributes }}&gt;{{ label }}&lt;/h2&gt;
      {% endif %}
      {{ title_suffix }}
      {% block content %}
        {{ content }}
      {% endblock %}
    &lt;/div&gt;
    ...

</code></pre>

<p>Lets test it!</p>

<p><img src="https://i.snap.as/xyEDlGa.png" alt=""/></p>

<p><img src="https://i.snap.as/FhuYnzy.png" alt=""/></p>

<p><img src="https://i.snap.as/MUvFtG2.png" alt=""/></p>
]]></content:encoded>
      <guid>https://arturo.linar.es/how-make-title-block-custom-background</guid>
      <pubDate>Sun, 18 Mar 2018 06:12:09 +0000</pubDate>
    </item>
    <item>
      <title>Setup Solr and Drupal using Docker</title>
      <link>https://arturo.linar.es/setup-solr-and-drupal-using-docker?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[#drupal #solr&#xA;&#xA;Last time I tried to create from scratch a Drupal instance that used a Solr server wasn’t as easy as I expected. Maybe command line tools for installing modules have spoiled me and I just expected that running something like drupal module:install solr would configure everything for me.&#xA;&#xA;Here is how I made it work, but please consider I’m not a Solr expert. If you know a better way to do this please let me know.&#xA;!--more--&#xA;Setup Drupal&#xA;&#xA;Let’s suppose we want to create a blog that uses a Solr for searching its contents.&#xA;&#xA;First, to create a basic Drupal install we can use a recently released project from WeKnow to quickly create a Drupal instance using Docker. Follow the README file keeping the default .env values so we can install Drupal using this command:&#xA;&#xA;    $ docker-compose exec --user=82 php drupal site:install standard --db-host=mariadb --db-name=drupal --db-user=drupal --db-pass=drupal --quiet&#xA;&#xA;Don’t forget to add the hosts entry, as mentioned in the README file, or use a proxy like hotel. The objective is to use a custom domain name that the web server can identify (if we use localhost and in the .env file we used  HOSTNAME=drupal.vm the web server won’t serve the request).&#xA;&#xA;You can verify it is working if you see a fresh Drupal install when you visit  http://drupal.develop.&#xA;&#xA;Install Solr&#xA;&#xA;Let’s install Solr now. We will need some config files found in the module to setup our Solr core, so lets download it inside the container:&#xA;&#xA;$ docker-compose exec --user=82 php composer req drupal/searchapisolr&#xA;&#xA;We will create a sample core named blog. Copy the files located at web/modules/contrib/searchapisolr/solr-conf/5.x/ to env/solr/blog/conf/ and create this file in env/solr/blog/core.properties (you will need to create the env dir):&#xA;&#xA;env/solr/blog/core.properties&#xA;    name=blog&#xA;    config=solrconfig.xml&#xA;    schema=schema.xml&#xA;    dataDir=/var/lib/solr/books&#xA;&#xA;Update docker-compose.yml to include the solr container and config its port and a way to access it using traefik (using http://solr.drupal.develop if using the defaults in .env):&#xA;&#xA;docker-compose.yml&#xA;    services:&#xA;      # ...&#xA;      solr:&#xA;        image: solr:5.5&#xA;        labels:&#xA;          &#39;traefik.backend=solr&#39;&#xA;          &#39;traefik.port=8983&#39;&#xA;          &#39;traefik.frontend.rule=Host:solr.${HOSTNAME}&#39;&#xA;        volumes:&#xA;          ./env/solr/blog:/opt/solr/server/solr/blog&#xA;          solrdata:/var/lib/solr&#xA;&#xA;    volumes:&#xA;      mysqldata:&#xA;        driver: &#34;local&#34;&#xA;      solrdata:&#xA;        driver: &#34;local&#34;&#xA;&#xA;  Don’t forget to add the solr.drupal.develop to your hosts file).&#xA;&#xA;After running docker-compose up -d (and adding to your hosts file the entry for solr.drupal.development) you will see this error in solr:&#xA;&#xA;This happened because the user and the uid has to match with the user the solr process runs as in the solr container. To fix this we have to create another container that fixes the perms where the core will write the index.&#xA;&#xA;We will use just-containers/base-alpine image, which has utilities to do this (s6).&#xA;&#xA;Add the docker file to update fix the perms and update docker-compose to use this new container:&#xA;&#xA;./dockerfiles/solrdata/Dockerfile&#xA;    FROM just-containers/base-alpine&#xA;    VOLUME [&#34;/var/lib/solr&#34;]&#xA;    ADD root /&#xA;&#xA;./dockerfiles/solrdata/root/etc/fixattrs.d&#xA;    /var/lib/solr false solr,8983 0755&#xA;&#xA;And update the docker-compose.yml to include this image:&#xA;    services:&#xA;      solr:&#xA;        image: solr:5.5&#xA;        labels:&#xA;          &#39;traefik.backend=solr&#39;&#xA;          &#39;traefik.port=8983&#39;&#xA;          &#39;traefik.frontend.rule=Host:solr.${HOSTNAME}&#39;&#xA;&#xA;        # add the volumes from the new container&#xA;        volumesfrom:&#xA;          solrdata&#xA;        # the core configs&#xA;        volumes:&#xA;          ./env/solr/blog:/opt/solr/server/solr/books&#xA;&#xA;      # added container to fix perms&#xA;      solrdata:&#xA;        build: ./dockerfiles/solrdata&#xA;        volumes:&#xA;          realsolrdata:/var/lib/solr&#xA;&#xA;    volumes:&#xA;      mysqldata:&#xA;        driver: &#34;local&#34;&#xA;      realsolrdata:&#xA;        driver: &#34;local&#34;&#xA;&#xA;Now if you run docker-compose up -d and you visit the Solr page you should see your errors fixed.&#xA;&#xA;Configure Solr&#xA;&#xA;Now that we have the server running, lets configure Drupal so we can use it as a search engine. Fortunately, most of the hard work is solved by enabling searchapisolrdefault module.&#xA;&#xA;    $ docker-compose exec --user=82 php drupal moi searchapisolr_defaults&#xA;&#xA;Go to the Search API configs (/admin/config/search/search-api) and you will see a new Solr server entry, although with errors because it is missing the server name and the core it will use.&#xA;&#xA;Click edit and set the host (ie. the solr container name, solr) and the core we created before (blog):&#xA;&#xA;Click save and you should be able to to see all green when you go back to the Search API config page. You can now start using your solar server for searching.&#xA;&#xA;  The defaults module contains a sample search page to easily get you started in /solr-search/content. It is implemented as a View.&#xA;&#xA;References&#xA;&#xA;You might find this links useful:&#xA;&#xA;Outrigger examples&#xA;Try Drupal&#xA;&#xA;Permalink: Setup Solr and Drupal using Docker&#xA;]]&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://arturo.linar.es/tag:drupal" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">drupal</span></a> <a href="https://arturo.linar.es/tag:solr" class="hashtag" rel="nofollow"><span>#</span><span class="p-category">solr</span></a></p>

<p>Last time I tried to create from scratch a Drupal instance that used a Solr server wasn’t as easy as I expected. Maybe command line tools for installing modules have spoiled me and I just expected that running something like <code>drupal module:install solr</code> would configure everything for me.</p>

<p>Here is how I made it work, but please consider I’m not a Solr expert. If you know a better way to do this please let me know.
</p>

<h2 id="setup-drupal" id="setup-drupal">Setup Drupal</h2>

<p>Let’s suppose we want to create a blog that uses a Solr for searching its contents.</p>

<p>First, to create a basic Drupal install we can use a recently released project from <a href="http://weknowinc.com" rel="nofollow">WeKnow</a> to quickly create a Drupal instance using Docker. Follow the README file keeping the default <code>.env</code> values so we can install Drupal using this command:</p>

<p>    $ docker-compose exec —user=82 php drupal site:install standard —db-host=mariadb —db-name=drupal —db-user=drupal —db-pass=drupal —quiet</p>

<p>Don’t forget to add the <code>hosts</code> entry, as mentioned in the README file, or use a proxy like <a href="https://github.com/typicode/hotel" rel="nofollow">hotel</a>. The objective is to use a custom domain name that the web server can identify (if we use localhost and in the <code>.env</code> file we used  <code>HOST_NAME=drupal.vm</code> the web server won’t serve the request).</p>

<p>You can verify it is working if you see a fresh Drupal install when you visit  <code>http://drupal.develop</code>.</p>

<h2 id="install-solr" id="install-solr">Install Solr</h2>

<p>Let’s install Solr now. We will need some config files found in the module to setup our Solr core, so lets download it inside the container:</p>

<pre><code>$ docker-compose exec --user=82 php composer req drupal/search_api_solr
</code></pre>

<p>We will create a sample core named *blog*<em>.</em> Copy the files located at <code>web/modules/contrib/search_api_solr/solr-conf/5.x/</code> to <code>env/solr/blog/conf/</code> and create this file in <code>env/solr/blog/core.properties</code> (you will need to create the <em>env</em> dir):</p>

<p><em>env/solr/blog/core.properties</em></p>

<pre><code>    name=blog
    config=solrconfig.xml
    schema=schema.xml
    dataDir=/var/lib/solr/books
</code></pre>

<p>Update <code>docker-compose.yml</code> to include the <code>solr</code> container and config its port and a way to access it using traefik (using <code>http://solr.drupal.develop</code> if using the defaults in <code>.env</code>):</p>

<p><em>docker-compose.yml</em></p>

<pre><code>    services:
      # ...
      solr:
        image: solr:5.5
        labels:
          - &#39;traefik.backend=solr&#39;
          - &#39;traefik.port=8983&#39;
          - &#39;traefik.frontend.rule=Host:solr.${HOST_NAME}&#39;
        volumes:
          - ./env/solr/blog:/opt/solr/server/solr/blog
          - solrdata:/var/lib/solr

    volumes:
      mysqldata:
        driver: &#34;local&#34;
      solrdata:
        driver: &#34;local&#34;
</code></pre>

<blockquote><p>Don’t forget to add the <code>solr.drupal.develop</code> to your hosts file).</p></blockquote>

<p>After running <code>docker-compose up -d</code> (and adding to your hosts file the entry for <code>solr.drupal.development</code>) you will see this error in solr:</p>

<p><img src="https://i.snap.as/rk3bXbc.png" alt=""/></p>

<p>This happened because the user and the uid has to match with the user the solr process runs as in the solr container. To fix this we have to create another container that fixes the perms where the core will write the index.</p>

<p>We will use <code>just-containers/base-alpine</code> image, which has utilities to do this (<a href="https://github.com/just-containers/s6-overlay" rel="nofollow">s6</a>).</p>

<p>Add the docker file to update fix the perms and update docker-compose to use this new container:</p>

<p><em>./dockerfiles/solr_data/Dockerfile</em></p>

<pre><code>    FROM just-containers/base-alpine
    VOLUME [&#34;/var/lib/solr&#34;]
    ADD root /
</code></pre>

<p><em>./dockerfiles/solr_data/root/etc/fixattrs.d</em></p>

<pre><code>    /var/lib/solr false solr,8983 0755
</code></pre>

<p>And update the <code>docker-compose.yml</code> to include this image:</p>

<pre><code>    services:
      solr:
        image: solr:5.5
        labels:
          - &#39;traefik.backend=solr&#39;
          - &#39;traefik.port=8983&#39;
          - &#39;traefik.frontend.rule=Host:solr.${HOST_NAME}&#39;

        # add the volumes from the new container
        volumes_from:
          - solr_data
        # the core configs
        volumes:
          - ./env/solr/blog:/opt/solr/server/solr/books

      # added container to fix perms
      solr_data:
        build: ./dockerfiles/solr_data
        volumes:
          - real_solr_data:/var/lib/solr

    volumes:
      mysqldata:
        driver: &#34;local&#34;
      real_solr_data:
        driver: &#34;local&#34;
</code></pre>

<p>Now if you run <code>docker-compose up -d</code> and you visit the Solr page you should see your errors fixed.</p>

<h2 id="configure-solr" id="configure-solr">Configure Solr</h2>

<p>Now that we have the server running, lets configure Drupal so we can use it as a search engine. Fortunately, most of the hard work is solved by enabling <code>search_api_solr_default</code> module.</p>

<pre><code>    $ docker-compose exec --user=82 php drupal moi search_api_solr_defaults
</code></pre>

<p>Go to the Search API configs (<code>/admin/config/search/search-api</code>) and you will see a new Solr server entry, although with errors because it is missing the server name and the core it will use.</p>

<p><img src="https://i.snap.as/atHaCF1.png" alt=""/></p>

<p>Click edit and set the host (ie. the solr container name, <em>solr</em>) and the core we created before (<em>blog</em>):</p>

<p><img src="https://i.snap.as/8YeLfr1.png" alt=""/></p>

<p>Click save and you should be able to to see all green when you go back to the Search API config page. You can now start using your solar server for searching.</p>

<blockquote><p>The defaults module contains a sample search page to easily get you started in <code>/solr-search/content</code>. It is implemented as a View.</p></blockquote>

<h2 id="references" id="references">References</h2>

<p>You might find this links useful:</p>
<ul><li><a href="https://github.com/phase2/outrigger-examples" rel="nofollow">Outrigger examples</a></li>
<li>Try Drupal</li></ul>

<p>Permalink: <a href="arturolinar.es/blog/post/setup-solr-and-drupal-using-docker" rel="nofollow">Setup Solr and Drupal using Docker</a></p>
]]></content:encoded>
      <guid>https://arturo.linar.es/setup-solr-and-drupal-using-docker</guid>
      <pubDate>Wed, 07 Mar 2018 06:20:41 +0000</pubDate>
    </item>
  </channel>
</rss>