# Question
Title: Stop registration form from sending mails
I want to stop the registration form from sending "email activated" email to users, is there a way to stop them?

# Answer
> 1 votes
Google is your friend, a search for "drupal 7 user registration email settings" produced:

configure user registration, user email, and user picture settings on the Account settings page:

Administer \> Configuration \> People \> Account settings) in Drupal 7, and (Administer \> Settings \> User) in previous versions.

# Answer
> 0 votes
There is a checkbox in the *Account settings -\>Mail section -\> Account activation* that says "*Notify users*" just untick that and we are good to go.

To stop all e-mails, see: How can I stop Drupal from sending e-mails of any kind?

# Answer
> 0 votes
In drupal7 you need to go Administer \> Configuration \> People \> Account settings to stop sending emails.

---
Tags: emails
---


# Question
Title: How is composer including a dependent module?
I have a composer managed Drupal project based on drupal/recommended-project.

I'm installing a contrib module, domain\_group, and I'm seeing that composer is auto-including a required dependency, even though it's not listed in the module's composer.json. (I'm checking some other contrib modules and it seems that this is not specific to this module, but a consistent pattern across many modules.)

I run:

```
composer require drupal/domain_group

```

and it installs domain\_group and domain\_site\_settings.

Then when I run:

```
composer why drupal/domain_site_settings

```

and it tells me that drupal/domain\_group requires it.

While the dependency is not listed in domain\_group/composer.json, I do see that it is listed in domain\_group.info.yml.

Is composer somehow looking to the .info.yml to check for dependencies? Is this some kind of Drupal magic? What causes this? How does it work?

**Update:**

It seems that it's not checking the .info.yml, at least not locally. From @Clive, in the comments:

> \[`composer why` still lists the dependency\] even when \[the reference to domain\_site\_settings\] is not in domain\_group's local composer.json, composer.lock is deleted, the require line in domain\_group.info.yml is commented out and composer cache is cleared (just checked). Where is it getting the reference?

I'm now wondering if `composer why` checks the remote repositories, e.g. https://packages.drupal.org/8, and if there's something special about the d.o repository that compiles extra package dependencies from .info.yml files.

**domain\_group/composer.json**

```
{
    "name": "drupal/domain_group",
    "type": "drupal-module",
    "description": "This module provides integration between Domain and Group modules",
    "homepage": "http://drupal.org/project/domain_group",
    "authors": [], // etc...
    "support": {}, // etc...
    "license": "GPL-2.0+",
    "minimum-stability": "dev",
    "require": {
        "drupal/core": "^8.8 || ^9",
        "drupal/group": "~1.0",
        "drupal/domain": "~1.0"
    }
}

```

**domain\_group/domain\_group.info.yml**

```
name: 'Domain Group'
type: module
description: 'This module provides integration between Domain and Group modules.'
core: 8.x
core_version_requirement: ^8 || ^9
package: 'Domain'
dependencies:
  - group:group
  - domain:domain
  - domain_site_settings:domain_site_settings # Is composer somehow respecting this?

```

# Answer
I *think* this is what's happening:

1. The Drupal packager gets dependencies from domain\_group.info.yml, and adds them to the dependencies for the package record that composer will use.
2. When composer installs the package, it downloads the current version of the module code (which doesn't have the dependency in composer.json)
3. Composer doesn't use that composer.json for its record keeping - it stores the dependency from the packager in composer.lock, and also in vendor/composer/installed.json
4. `composer why` seems to use composer.lock first for its info, falling back to installed.json if unavailable.

So it doesn't really matter what's in the module's composer.json as far as Composer commands are concerned - it's looking at composer.lock or installed.json, which has domain\_site\_settings as a requirement from the original install.
> 2 votes

---
Tags: composer
---


# Question
Title: alter query view by adding fields in an andGroup within an orGroup
I have a multilanguage site with two languages: English and Dutch. Not all nodes are created in both languages. Some nodes even have no language ('und', 'zxx').

I want the default Drupal provided taxonomy view output all nodes of a taxonomy-term in the current and no node-language, AND, if present, other nodes of this term from the other language. The Drupal provided sql is:

```
SELECT "taxonomy_index"."sticky" AS "taxonomy_index_sticky", "taxonomy_index"."created" AS "taxonomy_index_created",
"node_field_data"."nid" AS "nid", "node_field_data"."langcode" AS "node_field_data_langcode"
FROM {node_field_data} "node_field_data"
LEFT JOIN {taxonomy_index} "taxonomy_index" ON node_field_data.nid = taxonomy_index.nid
WHERE ((taxonomy_index.tid = '14')) AND (("node_field_data"."langcode" IN ('nl', 'und', 'zxx'))
AND ("taxonomy_index"."status" = '1'))
ORDER BY "taxonomy_index_sticky" DESC, "taxonomy_index_created" DESC
LIMIT 11 OFFSET 0

```

I want, if applicable, extend the where clause so it becomes by example:

```
WHERE (((taxonomy_index.tid = '14')) AND ((("node_field_data"."langcode" IN ('en', 'und', 'zxx'))
AND ("taxonomy_index"."status" = '1')))
OR (("taxonomy_index"."tid" = '14') AND ("node_field_data"."nid" IN ('43'))
AND ("node_field_data"."langcode" NOT IN ('en', 'und', 'zxx'))))

```

I wrote a function example\_views\_query\_alter(ViewExecutable $view, QueryPluginBase $query) in which I found out which extra nodes should be added in the extra OR clause. By adding this code:

```
      $group_id = $query->setWhereGroup('OR');
      $query->addWhere($group_id, 'taxonomy_index.tid', $tid);
      $query->addWhere($group_id, 'node_field_data.nid', $missing, 'IN');
      $query->addWhere($group_id, 'node_field_data.langcode', $langcodes2, 'NOT IN');

```

But in the extended where clause I get OR instead of AND statements:

```
WHERE ((taxonomy_index.tid = '14')) AND ((("node_field_data"."langcode" IN ('en', 'und', 'zxx'))
AND ("taxonomy_index"."status" = '1'))
OR (("taxonomy_index"."tid" = '14') OR ("node_field_data"."nid" IN ('43'))
OR ("node_field_data"."langcode" NOT IN ('en', 'und', 'zxx'))))

```

But it should be an AND group within an OR Group, maybe like:

```
$query->setWhereGroup('OR');
$query->setWhereGroup('AND');
$query->addWhere(...

```

Who can help me out to achive this?

# Answer
> 0 votes
At first I found this function: $query-\>addWhereExpression(...) which allows me to write the query code myself.

I kept the problem the original, by views generated code, always results in this pseudocode:

cond1 AND (cond2 AND cond3)

The needed pseudoce shoudl be:

(cond1 AND cond2 AND cond3)

And I have to add an OR clause: (cond1 AND cond2 AND cond3)

In total giving this code:

(cond1 AND cond2 AND cond3) OR (cond4 AND cond5 AND cond6)

See my full working function below.

I hope others can benefit of this function and developpers will be inspired by the code. Currently there are only few examples and even less documentation.

```
<?php

/**
 * @file
 * example module file to alter the Drupal suuplied taxonomy_term view by adding nodes from the non-current language to the current language, avoiding duplicates based on the nid.
 */

use Drupal\views\ViewExecutable;
use Drupal\views\Plugin\views\query;
use Drupal\views\Plugin\views\query\QueryPluginBase;

/**
 * Implements hook_views_query_alter().
 *
 * Alters the taxonomy_term view.
 * Only valid for sites with two languages.
 * Add nodes of the other language to the view if they have a different nid.
 *
 * @param \Drupal\views\ViewExecutable $view
 * @param \Drupal\views\Plugin\views\query\QueryPluginBase $query
 */
function example_views_query_alter(ViewExecutable $view, QueryPluginBase $query){
  if ($view->id() == 'taxonomy_term' && $view->current_display == 'page_1') {
    $language = \Drupal::languageManager()->getCurrentLanguage()->getId();
    // If the view should not be modified for a specific language.
    // if ($language == 'en') {
    //   return;
    // }

    // Step 1.
    // Gather info about the current views query.
    // There are a lot of checks because we will be sure the information about
    // the current query can be interpreted and changed:
    // - an administrator can change the view
    // - drupal can change the implementation in a future version
    // - another hook_..._alter can change the query as well.
    $values = $errors = [];
    $in_operator = 'IN';
    $in_reverse = [
      'IN' => 'NOT IN',
      'NOT IN' => 'IN',
    ];
    $expected_conditions = [
      // fieldname => expected operator.
      'node_field_data.langcode' => 'in',
      'taxonomy_index.tid' => 'formula',
      'taxonomy_index.status' => '=',
    ];
    // Traverse through the 'where' part of the query and save relevant data.
    foreach ($query->where as $condition_group) {
      foreach ($condition_group['conditions'] as $condition) {
        // The $condition['field'] can contain several 'words'.
        $parts = explode(' ', $condition['field']);
        $fieldname = $parts[0];
        // Do we expect this fieldname?
        if (isset($expected_conditions[$fieldname])) {
          $parts = explode('.', $fieldname);
          // For our conveniance we use the short table names.
          $values[$parts[1]] = $condition['value'];
          if (strcasecmp($expected_conditions[$fieldname], $condition['operator']) != 0) {
            $error = TRUE;
            if ($expected_conditions[$fieldname] == 'in') {
              if (strtolower($condition['operator']) == 'not in') {
                $expected_conditions[$fieldname] = $condition['operator'];
                $error = FALSE;
              }
              $in_operator = strtoupper($expected_conditions[$fieldname]);
            }
            if ($error) {
              $errors[] = 'Unexpected operator ' . $condition['operator']
                . ' in field: ' . $fieldname
                . '. Expected: ' . $expected_conditions[$fieldname];
            }
          }
        }
        else {
          $errors[] = 'Unexpected condition field: ' . $fieldname;
        }
      }
    }

    // Step 2.
    // All values are transformed for the queries.
    // All expected conditions found?
    // Status may be absent / create the query part for status.
    if (!isset($values['status'])) {
      $values['status'] = '';
    }
    else {
      $values['status'] = " AND taxonomy_index.status = '" . $values['status'] . "'";
    }
    if (count($expected_conditions) != count($values)) {
      $errors[] = 'One or more conditions are missing.';
    }
    // If errors found, log these and quit. Better safe then sorry.
    if (!empty($errors)) {
      $message = 'Error in function example_views_query_alter() to alter view taxonomieterm.';
      foreach ($errors as $err) {
        $message .= '<br />- ' . $err;
      }
      \Drupal::logger('example')->critical($message);
      return;
    }

    // Get the current taxonomy tid value for queries.
    $values['tid'] = array_shift($values['tid']);
    // Substitute language placeholder with current language.
    foreach ($values['langcode'] as &$lc) {
      $lc = str_replace('***LANGUAGE_language_interface***', $language, $lc);
    }

    // Step 3.
    // Lookup if the other language has additional nodes.
    // Find all nids for the current node langages.
    $db = \Drupal::database();
    $current_lang = $db->query("SELECT node_field_data.nid AS nid
      FROM {node_field_data} node_field_data
      LEFT JOIN {taxonomy_index} taxonomy_index ON node_field_data.nid = taxonomy_index.nid
      WHERE taxonomy_index.tid = :tid AND node_field_data.langcode "
        . $in_operator . " (:langcodes[])" . $values['status'],
      [':tid' => $values['tid'], ':langcodes[]' => $values['langcode']])
      ->fetchCol();
    // Also for the other langauge.
    $missing_lang = $db->query("SELECT node_field_data.nid AS nid
      FROM {node_field_data} node_field_data
      LEFT JOIN {taxonomy_index} taxonomy_index ON node_field_data.nid = taxonomy_index.nid
      WHERE taxonomy_index.tid = :tid AND node_field_data.langcode "
        . $in_reverse[$in_operator] . " (:langcodes[])" . $values['status'],
      [':tid' => $values['tid'], ':langcodes[]' => $values['langcode']])
      ->fetchCol();
    // Get missing nids.
    $missing = array_diff($missing_lang, $current_lang);
    // No nodes to add?
    if (empty($missing)) {
      return;
    }

    // Step 4.
    // Change the where clause of the query.
    // We like to add only an OR group with the nids to add.
    // The current where clause creates the following pseudo code:
    // WHERE cond1 AND (cond2 AND cond3)
    // But we need:
    // WHERE (cond1 AND cond2 AND cond3)
    // No way was found to change the current query to the needed clause.
    // The goal is to expand the where clause with this result:
    // WHERE (cond1 AND cond2 AND cond3) OR (cond4 AND cond5 AND cond6)
    // So recreate the whole where clause.
    $query->where = [];
    // Create a new OR group using the original content.
    $group_id = $query->setWhereGroup('OR');
    $snippet = "taxonomy_index.tid = :tid AND node_field_data.langcode "
      . $in_operator . " (:langcodes[])" . $values['status'];
    $query->addWhereExpression($group_id, $snippet,
      [':tid' => $values['tid'], ':langcodes[]' => $values['langcode']]);
    $snippet = "taxonomy_index.tid = :tid AND node_field_data.nid IN (:missing[]) AND node_field_data.langcode "
      . $in_reverse[$in_operator] . " (:langcodes[])";
    $query->addWhereExpression($group_id, $snippet, [':tid' => $values['tid'],
      ':missing[]' => $missing, ':langcodes[]' => $values['langcode']]);
    // An alternative is to combine the two $snippets.
    // But this works: the OR statments are more clear.
  }
}

```

---
Tags: views, taxonomy-terms
---


# Question
Title: How to call a check for a product class in Ubercart
In the Ubercart D7 uc attribute module I am trying to run code on the fieldset attribute if the product is a specific product class in the module with an if statement.

This calls an if for payment received

```
      if ($op == 'update' && $order->order_status == 'payment_received' && $arg2 == 'completed') {
// ... your code goes here

```

}

How do I call it for a product class??

In uc\_attributes module at

```
   function uc_attribute_uc_form_alter(&$form, &$form_state, $form_id) {
  //. other code of module's function...
    $element[$key]['#type'] = 'fieldset';
    //    if (product class . ..) {
  // ... your code goes here

```

So that only the validate code runs on the field set if the product is the same product class

I have rest of the code largely ready to go

# Answer
> 0 votes
The **node type** is Ubercart class. Because, then you create a new class in `/admin/store/products/classes` it will create the new content type in `/admin/structure/types` So code will like next:

```
if ($op == 'update' && $order->order_status == 'payment_received' && $arg2 == 'completed') {
  if (is_array($order->products)) {
    foreach ($order->products as $product) {
      if ($product->type == 'SOMECLASS') {
         // DO something
      }
    }
  }
}

```

# Answer
> 0 votes
The function to use is:

```
uc_product_is_product($node)

```

This returns a boolean variable indicating whether the node is a product type.

---
Tags: ubercart
---


title: Hide field label with hook_form_alter()

text:
# Question
Title: Hide field label with hook_form_alter()
In `hook_form_alter()`, I am trying to disable/hide a field label in a custom form. I know I could use `$form['my_field']['#enableLabel'] = FALSE;`.

Is there a way without unsetting the title?

# Answer
> 9 votes
Unsetting not really, hide yes : use #title\_display and its get accessible to screenreader :

```
$form['my_field']['#title_display'] = 'invisible';

```

# Answer
> 2 votes
In my case I was trying to hide the label on quantity field provided by Drupal Commerce cart module and the following wasn't working:

```
$form['my_field']['#title_display'] = 'invisible';

```

I had to do this inside my hook\_form\_alter

```
$form['quantity']['widget'][0]['value']['#title_display'] = 'invisible';

```

# Answer
> -1 votes
you can try:

```
function hook_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {

    $form['title']['widget']['0']['value']['#title_display']=none;

 }

```

**Note:** change "title" by your machine name field.

---
Tags: forms
---


# Question
Title: How to alter the parameters in the core.services.yml file programatically?
I need to modify the filter\_protocols parameter inside the core.services.yml file programmatically to add a new protocol. I know how to alter a service but I don't find any information about how to alter a parameter.

```
parameters:
  session.storage.options:
    gc_probability: 1
    gc_divisor: 100
    gc_maxlifetime: 200000
    cookie_lifetime: 2000000
  twig.config:
    debug: false
    auto_reload: null
    cache: true
  renderer.config:
    required_cache_contexts: ['languages:language_interface', 'theme', 'user.permissions']
    auto_placeholder_conditions:
      max-age: 0
      contexts: ['session', 'user']
      tags: []
  factory.keyvalue:
    default: keyvalue.database
  http.response.debug_cacheability_headers: false
  factory.keyvalue.expirable:
    default: keyvalue.expirable.database
  filter_protocols:
    - http
    - https
    - ftp
    - news
    - nntp
    - tel
    - telnet
    - mailto
    - irc
    - ssh
    - sftp
    - webcal
    - rtsp

```

I know how to do this by copying the services.yml file inside the sites/default folder but at this time I need to do this programmatically.

Any idea about how to do it?

# Answer
You need a compiler pass:

> Compiler passes give you an opportunity to manipulate other service definitions that have been registered with the service container.

There are some Drupal specifics. An example which calculates a container parameter depending on tagged services is RegisterSerializationClassesCompilerPass. This class is registered in SerializationServiceProvider.

For simple use cases, where you don't need control over compiler pass priorities, you can use the `alter()` method of ServiceModifierInterface. This will then be executed in a shared core compiler pass ModifyServiceDefinitionsPass.

Example for adding `foo` to the filter protocols:

*src/MymoduleServiceProvider.php*

```
<?php

namespace Drupal\mymodule;

use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceProviderBase;

/**
 * Defines a service provider for the Mymodule module.
 */
class MymoduleServiceProvider extends ServiceProviderBase {

  /**
   * {@inheritdoc}
   */
  public function alter(ContainerBuilder $container) {
    $filter_protocols = $container->getParameter('filter_protocols');
    $filter_protocols[] = 'foo';
    $container->setParameter('filter_protocols', $filter_protocols);
  }

}

```
> 3 votes

# Answer
If you run script from Drupal root folder:

```
<?php

use Drupal\Core\DrupalKernel;
use Symfony\Component\HttpFoundation\Request;
$autoloader = require_once 'autoload.php';

use Symfony\Component\Yaml\Yaml;

$root = __DIR__;

$config = Yaml::parse(file_get_contents($root . '/core/config/install/core.extension.yml'));

$config['parameters']['filter_protocols'][] = 'NEW_PROTOCOL'; // Your protocol

$yaml = Yaml::dump($config);
file_put_contents($root . '/core/config/install/core.extension.yml', $yaml);

```
> 0 votes

---
Tags: 8, symfony-services
---


# Question
Title: How to get a custom block form in another custom block?
I have a custom block

```
/**
 * Provides block A.
 *
 * @Block(
 *   id = "block A",
 *   admin_label = @Translation("Block A"),
 * )
 */
class Block A extends BlockBase implements ContainerFactoryPluginInterface {

  public function defaultConfiguration() {
    return [some_value]
  }

  public function blockForm($form, FormStateInterface $form_state) {
    // Configuration form
  }
}

```

Now I have another block

```
/**
     * Provides block B.
     *
     * @Block(
     *   id = "block B",
     *   admin_label = @Translation("Block B"),
     * )
     */
    class Block B extends AnotherClass {

      public function defaultConfiguration() {
        return [some_value]
      }

      public function blockForm($form, FormStateInterface $form_state) {
        $form = BlockA->buildForm()
        return $form;
      }
    }

```

The `AnotherClass` extends `BlockBase`.

Is it possible to display the `Block A` configuration form in `Block B`?

Like `$form = BlockA->buildForm()`?

The problem with @Clive comment is that `Block A` is from the contrib module. So I can't change its base class.

# Answer
The answer is to use the `plugin.manager.block` service

Example:

In the `Block B` `BlockForm` method.

```
public function blockForm($form, FormStateInterface $form_state) {
  $block_manager = \Drupal::service('plugin.manager.block');
  $block_a = $block_manager->createInstance('block_a');
  $form = block_a->blockForm($form, $form_state)
  return $form;
}

```
> 0 votes

---
Tags: 8, blocks
---


# Question
Title: How do I get the order_id and order_number on the checkout completed page?
I'm trying to get the order information inside the mymodule\_preprocess\_page hook.

On the normal page I can get it via the $cart. But the $cart is not available on the checkout page anymore. How do I get the order information specifically the order\_id and order\_number.

This is what I do on other pages:

```
  $order_type = 'physical';
  $cart_provider = \Drupal::service('commerce_cart.cart_provider');
  $store = \Drupal::service('commerce_store.current_store')->getStore();
  $cart = $cart_provider->getCart($order_type, $store);
  if ($cart) {
    foreach ($cart->getItems() as $order_item) {
      $orderId = $order_item->get('purchased_entity')[0]->get('target_id')->getValue();
  }

```

# Answer
> 2 votes
The following should work:

```

  /** @var \Drupal\commerce_order\Entity\OrderInterface $order */
  $order = \Drupal::routeMatch()->getParameter('commerce_order');

```

---
Tags: commerce, 9
---


# Question
Title: How to get the contents created by a User?
How to get all contents from which the current user is author?

```
 $currentUser = \Drupal::currentUser();
 $user = User::load($currentUser->id());
//get the contents from which $user is author?

```

# Answer
```
$currentUser = \Drupal::currentUser();
$query = \Drupal::entityQuery('node');
$query->condition('uid', $currentUser->id());
$author_nids=$query->execute();

```
> 5 votes

---
Tags: users
---


title: how to set a node to anonymous author?

text:
# Question
Title: how to set a node to anonymous author?
Change node author works :

```
$node = \Drupal\node\Entity\Node::load($nid);
$node->set('uid','45');
$node->save();

```

But i don't know how to set it to anonymous user ? or unset it ?

```
 $node = \Drupal\node\Entity\Node::load($nid);
 // set uid to anonymous ?
 $node->save();

```

# Answer
> 3 votes
`$node->set('uid', 0); $node->save` should have worked. Maybe you forgot to flush caches.

To programmatically set the author to anonymous user on any entity you can call `$entitiy->setOwnerId(0);` and then `$entity->save();`, done.

```
$entity->setOwnerId(0);
$entity->save();

```

---
Tags: nodes, users
---


# Question
Title: How can I add an BCC or CC email with programmatically?
I am sending email programmatically and I am using plugin.manager.mail to send email, I want to add a CC ó BCC.

Do you know the way to achieve it?

# Answer
> 2 votes
What you want to do is ensure you are defining a message type in your custom module, you can then target the message within a hook\_mail\_alter() to add the desired CC or BCC.

```
function MYMODULE_mail_alter(array &$message) {
  $bccEmail = 'test@test.com';

  if ($message['xxx'] == 'xxx') {
    $existingBcc = isset($message['headers']['Bcc']) ? $message['headers']['Bcc'] : '';
    $message['headers']['Bcc'] = implode(' ', array_merge(
      explode(' ', $existingBcc), [$bccEmail])
    );
  }
}

```

https://git.drupalcode.org/project/bcc/-/blob/8.x-3.x/bcc.module has an example of adding a bcc.

---
Tags: emails
---


# Question
Title: Get specific element in site path in twig template
```
The current page url is http://website/p1/p2/p3/p4
In Drupal 8 twig, use function to get path p1/p2/p3/p4
{{ set site_path = path("<current>") }}
{{ site_path }}
The site_path is p1/p2/p3/p4
How to get the element in the path, for example, the second p2

```

# Answer
The split filter splits a string by the given delimiter and returns a list of strings:

```
{% set site_path = path('<current>') %}
{% set exppath = site_path|split('/') %}
{{ exppath.2 }}

```

another way to array element: `{{ exppath[2] }}`
> 0 votes

---
Tags: theming
---


title: Messing with several versions of drush

text:
# Question
Title: Messing with several versions of drush
When I run

```
drush --version

```

I get next output:

```
count(): Parameter must be an array or an object that implements     [warning]
Countable Table.php:789
 Drush Version   :  7.4.0

```

For getting rid of the warning, I have been told I to install drush version 8. So I issued:

```
composer require drush/drush:8

```

giving next result:

```
./composer.json has been updated
Running composer update drush/drush
Loading composer repositories with package information
Updating dependencies
Nothing to modify in lock file
Installing dependencies from lock file (including require-dev)
Nothing to install, update or remove
Generating autoload files
9 packages you are using are looking for funding.
Use the `composer fund` command to find out more!

```

So I suppose drush was already at version 8, but issuing the command above, I still have the same result: version 7.4.0. I am probably messing with several versions of drush.

I would like to understand better how to get rid of the old version, and being sure I am using the last one.

# Answer
You are mixing up the globally installed Drush with a locally installed Drush. To run the local Drush from your project, run `vendor/drush/drush/drush --version` from inside your project. This will give you 8.0.0 which is a bit old. Better get the latest Drush 8 release with `composer require drush/drush:^8`. Currently this is Drush 8.4.8.

Next, you can consider to either update the globally installed Drush (not recommended) `composer global require drush/drush:^8` or alternatively (recommended) install the Drush Launcher instead. The Drush Launcher is just a wrapper command to always pick up the project-local Drush. So you can easily run Composer-based D7 and D8/D8+ projects on the same machine in parallel and calling `drush --version` in a project will adjust accordingly.

If your project is D8 or D9 install Drush 10 instead `composer require drush/drush`.
> 2 votes

# Answer
This is how I solved my problem after trying a lot of things to upgrade composer and drush and finally crashing completely:

```
cd $HOME                                           # the current directory must be $HOME
rm -rf .composer                                   # remove all composer packages
curl -s https://getcomposer.org/installer | php    # install composer in $HOME
sudo mv composer.phar /usr/bin/composer            # move composer from local to global
composer -W global require drush/drush:^8.4        # install the version of drush I need

```

Voilà.

After this, the executable `composer.phar` is renamed to `composer`. So do not longer look at `$HOME/composer.phar` if you ever want to uninstall it. You will just have to delete `/usr/bin/composer`.

Check your $PATH: `$HOME/.config/composer/vendor/bin` must be included. Caution: some configurations have `$HOME/.composer/vendor/bin` in the $PATH variable, which is also possible, but you need to keep it coherent with the place where the packages are installed.
> 1 votes

---
Tags: drush
---


# Question
Title: How to determine if {{user_picture}} is not empty in the comment.html.twig template
Trying to determine if {{user\_picture}} is not empty in the comment.html.twig template.

`{{user_picture}}` refers to the Compact display mode for a User Entity.

The below code doesn't work.

```
{% if user_picture is not empty %}
      {{ user_picture }}
    {% else %}
      <img src="images/default_image" width="42" height="42">
    {% endif %}

```

Also tried this with no success...

```
{% if user_picture|render is not empty %}

```

Researched a bunch, and couldn't find any problems/solutions with this use case. What am I missing?

# Answer
The comment entity is available and you can get the author and its field data from that, e.g.

```
{% if comment.uid.entity.user_picture.target_id > 0 %}
  {{ user_picture }}
{% else %}
  ...
{% endif %}

```
> 3 votes

---
Tags: 8, theming
---


# Question
Title: Body text special characters are escaped
I have this code which I am using to get the body text from a node rewrite it with a regular expression and replace it, (I am replacing all you tube video links with embed codes)

```
function hook_entity_presave(Drupal\Core\Entity\EntityInterface $entity) {

  switch ($entity->bundle()) {
    case 'article':

    $node_body = $entity->get('body')->value;

    Drupal::logger('origin-body')->notice($node_body);

    $node_body_replaced = preg_replace('/(?:https:\/\/)?(?:http:\/\/)?(?:www\.)?(?:youtube\.com|youtu\.be)\/(?:watch\?v=)?(.+)/', '<iframe width="560" height="315" src="https://www.youtube.com/embed/$1" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>', $node_body);

      Drupal::logger('replaced-body')->notice($node_body_replaced);
      $body_encoded = htmlspecialchars($node_body_replaced, ENT_QUOTES, 'UTF-8');
      //$entity->body = $node_body_replaced;
      $entity->set( 'body',$body_encoded  ); // This appears to be where special characters are getting escaped

     break;
  }
}

```

Both my logger messages show normal text output in the drupal log so I guess it happens somwhere when the node gets saved or $entity-\>set(). I have tried running through htmlspecialchars() and it does nothing.

For example, I am migrating content from WordPress and after running through this function my normal HTML becomes

> \<p\>lorem ipsum dolar lorem ipsum dolar lorem ipsum dolar lorem ipsum dolar lorem ipsum dolar lorem ipsum dolar .\</p\> \<!-- /wp:paragraph --\>

I am using Drupal 9

# Answer
Assuming by *my normal HTML becomes* you mean the escaped HTML is rendering on the front end, you're probably just resetting the format for the body field with this line

```
$entity->set('body', $body_encoded);

```

You can target the value directly instead like this (and you shouldn't need to encode/decode anything):

```
$entity->body->value = preg_replace('/(?:https:\/\/)?(?:http:\/\/)?(?:www\.)?(?:youtube\.com|youtu\.be)\/(?:watch\?v=)?(.+)/', '<iframe width="560" height="315" src="https://www.youtube.com/embed/$1" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>', $node_body);

```

If that isn't working you can try setting the format too:

```
// Possibly change 'full_html' to whatever's appropriate for your site.
$entity->body->format = 'full_html';

```
> 1 votes

---
Tags: entities, 9
---


# Question
Title: How to properly delete fields, programmatically?
I have two content types on my site and I just deleted all the nodes using `$node->delete()`.

For one of the content types, I noticed that the node body fields did not get deleted, nor did one of the custom fields I made for the content type. All the fields for my other content type were deleted .

So I researched and found the `field_purge_field()` function. I am not sure how to actually use the function, which is defined as `function field_purge_field(FieldConfigInterface $field)`.
Can someone describe how I would actually load a field and then delete it in this manner (or in a better manner)?

Could I just truncate the tables?

# Answer
> 5 votes
`field_purge_field()` is described as follows:

> This function assumes all data for the field has already been purged and should only be called by `field_purge_batch()`.

On Drupal, fields are effectively removed during cron tasks; in fact, `field_purge_batch()` is called from `field_cron()`, the implementation of `hook_cron()` done from the Field module. There is no need for you to call neither `field_purge_field()` nor `field_purge_batch()`.

If you want to write code to purge fields for a node bundle, that code would be similar to the following one. (See the notes after the code, thought.)

```
  $properties = array(
    'entity_type' => 'node',
    'bundle' => 'Your bundle (a.k.a. content type)',
    'include_deleted' => TRUE,
  );

  $entity_manager = \Drupal::entityTypeManager();

  $fields = $entity_manager->getStorage('field_config')->loadByProperties($properties);

  $info = $entity_manager->getDefinitions();
  foreach ($fields as $field) {
    $entity_type = $field->getTargetEntityTypeId();

    // Proceed only if the module implementing the entity has not been uninstalled already.
    if (isset($info[$entity_type])) {
      $entity_manager->getStorage($entity_type)->purgeFieldData($field, 100);
      field_purge_field($field);
    }
  }

```

### Notes

* Purging the fields is thought to be ran during cron tasks; that is why `Drupal::entityManager()->getStorage()->purgeFieldData()` requires the batch size as argument. I used a value of 100, which would possibly allow the code to purge all the fields for that content type, but it could also cause a PHP timeout error. It would be better to run the code from PHP CLI, in order to avoid any execution time out.
* You should not need to use this code; in most of the cases, you should execute the cron tasks (which is when Drupal purges the fields of a deleted bundle), or use Drush to purge the fields, either running `hook_cron()` with it, or executing a specific Drush command (if it exists).

As for truncating the tables, that is the wrong way to proceed. It would not allow Drupal and third-party modules to delete the data they have for the fields; even if you know which tables Drupal uses, you could forget a table used by third-party modules.

# Answer
> 5 votes
The updated version for **Drupal 8.3.x** using non deprecated functions:

```
/**
 * Removes field_MY_FIELD_NAME.
 */
function MYMODULE_update_8001() {
  /* @var $entityFieldManager Drupal\Core\Entity\EntityFieldManager */
  $entityFieldManager = Drupal::service('entity_field.manager');

  $fields = $entityFieldManager->getFieldDefinitions('ENTITY_TYPE', 'BUNDLE');

  if (isset($fields['field_MY_FIELD_NAME'])) {
    $fields['field_MY_FIELD_NAME']->delete();
  }
}

```

Replace the following:

1. `ENTITY_TYPE` by the entity type, e.g.: `'taxonomy_term'`, `'node'`...
2. `BUNDLE` by the bundle, e.g.: `'article'`, `'tags'`...
3. `field_MY_FIELD_NAME` by the field machine name, e.g.: `'field_description'`...

# Answer
> 1 votes
The following will delete content and config of a field in all places used on the same entity type. NOTE: in D7 fields could be used across entity types (i.e. same field used on different bundles of same entity type) but in D8, this is not allowed:

```
use Drupal\field\Entity\FieldStorageConfig;
FieldStorageConfig::loadByName(ENTITY_TYPE_NAME, FIELD_NAME)->delete();

```

As with the other methods above, this will result in field remnants being left behind in the key\_value and cachetags tables. CRON runs will remove remnants from the key\_value table but I have not yet found a way to remove remnants from the cachetags table (short of simply using direct db calls).

\[EDIT\] I stated that field remnants from key\_value table would be removed during CRON; but in fact this only occurs if there had been data in that field. Not sure why this is different but likely a core bug.

# Answer
> 0 votes
I found this code for drupal 8 that worked very good for me here https://gist.github.com/jacerider/78b926acbb201666a0fc3a1f8102cb44

**This empty the field, them remove field from bundle**

I just added in a hook\_update\_N() https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Extension%21module.api.php/function/hook\_update\_N/8.2.x

```
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\Entity\FieldConfig;

$bundles = ['user'];

$fields['user_picture'] = [
  'entity_type' => 'user',
];

foreach ($bundles as $bundle) {
  foreach ($fields as $field_name => $config) {
    $field = FieldConfig::loadByName($config['entity_type'], $bundle, $field_name);
    if (!empty($field)) {
      $field->delete();
    }
  }
}

foreach ($fields as $field_name => $config) {
  $field_storage = FieldStorageConfig::loadByName($config['entity_type'], $field_name);
  if (!empty($field_storage)) {
    $field_storage->delete();
  }
}

```

# Answer
> 0 votes
On Drupal 8 we had a lot of issues after uninstalling the domain module.

We solved this with an install file

```
@file my_module.install

/**
 * Implements hook_update_N().
 */
function my_module_update_8800(&$sandbox) {
  /** @var \Drupal\Core\Field\DeletedFieldsRepositoryInterface $deleted_fields_repository */
  $deleted_fields_repository = \Drupal::service('entity_field.deleted_fields_repository');
  $fields = $deleted_fields_repository->getFieldDefinitions();
  foreach ($fields as $field) {
    field_purge_field($field);
  }
}

```

Hope this helps someone.

---
Tags: entities, 8
---


title: Translate tab missing

text:
# Question
Title: Translate tab missing
I am working on my first multilingual Drupal 9 website, and I face a challenge. I can't seem to get my contact webform elements translated. I am using Webform Version: 6.0.2. I have enabled translation on the content type, and I can translate the title, body, menu item fields. But I can't figure out how to translate the actual labels & placeholders of the form elements. I have watched this video (D8), but I do not have the Translate tab that he is using: https://www.videodrupal.org/video/20180321/translating-webforms

I suspect that there is some module og configuration setting that I am missing, but I can't figure out what it is, so any help would be appreciated. My available tabs are the same as the ones on the screenshot from the Webform module page: https://www.drupal.org/files/issues/2018-05-19/webfork-wizard-admin.png

# Answer
> 4 votes
The form labels are part of the webform's configuration, and their translation is therefore handled by the *Configuration Translation* (machine name: `config_translation`) module. When that module is enabled, it will add the Translate tab.

# Answer
> 0 votes
you need to activate the configuration translation module. To activate it, log on to your site with an administrator account and go to: Extend: / admin / modules, and look for configuration translation. Select it and press the install button to activate, and then the translation tab for your site configuration will appear.

---
Tags: webforms, i18n-l10n, 9
---


# Question
Title: How do I add an external link in a module?
I'm attempting develop a module in drupal-8 that provides a link to an external password reset system. Specifically on the login page I'd like a *reset my password* link that points to a link such as https://reset.my.password/

I've tried adding my module.links.task.yml file. What else do I have to create to add an external link?

# Answer
There seems to be very little information on this matter, maybe it's not that common to try to achieve something like this, but I stumbled across this thread while searching for a solution and I'd like to add my thoughts.

The comments stating that `links.task.yml` is for local tasks is right, but when it comes to external links, it's not that simple. As far as I know, you need routing to add links that way, and routing is for internal links, right?

So in the end I did it this way in Drupal 9:

```
  function my_module_menu_local_tasks_alter(&$data, $route_name) {
    $data['tabs'][0]['node.extra_tab'] = array(
      '#theme' => 'menu_local_task',
      '#link' => array(
        'title' => t('Extra tab'),
        'url' => Url::fromUri('https://theurl.com', array('attributes' => array('target' => '_blank'))),
        'localized_options' => array(
          'attributes' => array(
            'title' => t('Extra tab'),
          ),
        ),
      ),
    );
  }

```

You can also add something like `if ($route_name == 'entity.node.canonical')` to narrow it down and also `if (\Drupal::routeMatch()->getParameter('node') == 'my_content_type')` in case you want this to work only on certain content type(s).
> 2 votes

# Answer
You can implement `hook_menu_local_tasks_alter()`.

```
/**
 * Implements hook_menu_local_tasks_alter().
 */
function mymodule_menu_local_tasks_alter(&$data, $route_name, RefinableCacheableDependencyInterface &$cacheability) {
  foreach ($data['tabs'] as &$tab){
    $tab['routing_name']['#link']['url'] = Url::fromUri('https://reset.my.password/');
  }
}

```
> 0 votes

---
Tags: navigation
---


# Question
Title: What should the dynamic query for the following SQL string be?
What should the dynamic query for the following SQL string be?

```
SELECT p.id, p.name, GROUP_CONCAT(s.name) AS site_list
FROM sites s
INNER JOIN publications p ON(s.id = p.site_id)
GROUP BY p.id;

```

# Answer
```
$query = db_select('sites', 's')
  ->fields('p', array('id', 'name'))
  ->groupBy('p.id');

$query->addExpression('GROUP_CONCAT(s.name)', 'site_list');
$query->join('publications', 'p', 's.id = p.site_id');

```
> 8 votes

# Answer
You can use that query string directly with `db_query()`, with the difference that database table names need to be wrapped in curly brackets.

```
$query = db_query("SELECT p.id, p.name, GROUP_CONCAT(s.name) AS site_list FROM {sites} s INNER JOIN {publications p} ON p.id = s.site_id GROUP BY p.id");
$records = $query->fetchAll();
foreach ($records as $record) {
  // Do something.
}

```

I take that `ON s.id = p.site_id` really meant `ON p.id = s.site_id`, as `p.id` is the selected table field.

As Berdir said, `GROUP_CONCAT()` isn't implemented by all the database engines supported by Drupal; the query should be rewritten to avoid using it.

As Introduction to dynamic queries says, dynamic queries should be used:

* When the query parts vary depending on the context
* When other modules should be able to alter the executed query
* When porting Drupal 6 code that uses `db_rewrite_sql()`

The following code (taken from `comment_get_display_ordinal()`) is an example of the first point.

```
$query = db_select('comment', 'c1');
$query->innerJoin('comment', 'c2', 'c2.nid = c1.nid');
$query->addExpression('COUNT(*)', 'count');
$query->condition('c2.cid', $cid);
if (!user_access('administer comments')) {
  $query->condition('c1.status', COMMENT_PUBLISHED);
}
$mode = variable_get('comment_default_mode_' . $node_type, COMMENT_MODE_THREADED);
if ($mode == COMMENT_MODE_FLAT) {
  $query->condition('c1.cid', $cid, '<');
}
else {
  $query->where('SUBSTRING(c1.thread, 1, (LENGTH(c1.thread) -1)) < SUBSTRING(c2.thread, 1, (LENGTH(c2.thread) -1))');
}
return $query->execute()->fetchField();

```

As part of the query is added basing on some conditions, `db_select()` is used. The alternative would be using `db_query()` and concatenate the strings that form the query (which isn't preferable to using `db_select()`).

An example of the second point is code that needs to show a list of nodes to which the currently logged-in user has access. In this case, `db_select()` is used, and *node\_access* is added to the query with `SelectQuery::addTag()`, which allows modules that implement `hook_query_TAG_alter()` to alter the query, as explained in Query alteration (tagging).

The following code is an example of the third point. (See `blog_feed_user()`.)

```
  $nids = db_select('node', 'n')->fields('n', array('nid', 'created'))
    ->condition('type', 'blog')
    ->condition('uid', $account->uid)
    ->condition('status', 1)
    ->orderBy('created', 'DESC')
    ->range(0, variable_get('feed_default_items', 10))
    ->addTag('node_access')
    ->execute()
    ->fetchCol();
  $channel['title'] = t("!name's blog", array(
    '!name' => format_username($account),
  ));
  $channel['link'] = url('blog/' . $account->uid, array(
    'absolute' => TRUE,
  ));
  node_feed($nids, $channel);

```

That code is the Drupal 7 port of the following code.

```
  $result = db_query_range(db_rewrite_sql("SELECT n.nid, n.created FROM {node} n  WHERE n.type = 'blog' AND n.uid = %d AND n.status = 1 ORDER BY n.created DESC"), $account->uid, 0, variable_get('feed_default_items', 10));
  $channel['title'] = t("!name's blog", array(
    '!name' => $account->name,
  ));
  $channel['link'] = url('blog/' . $account->uid, array(
    'absolute' => TRUE,
  ));
  $items = array();
  while ($row = db_fetch_object($result)) {
    $items[] = $row->nid;
  }
  node_feed($items, $channel);

```

If your query falls in one of those cases, and you are showing just part of the query, then you can use `db_select()`, for example as the following code.

```
$query = db_select('publications', 'p')->fields('p', array('id', 'name'));
$alias = $query->innerJoin('sites', 's', '%alias.site_id = p.id');
$query->addExpression("GROUP_CONCAT($alias.name)", 'site_list');
$query->groupBy('p.id');

```
> 0 votes

---
Tags: 7, database
---


# Question
Title: {{ directory }} in Twig template in base theme breaks subtheme
Given a base theme called `parent` that is a child of `stable`, and a sub-theme called `child` whose base theme is `parent`, a Twig template implemented in the `parent` theme renders the wrong value for `{{ directory }}` variable.

1. In the parent theme, add a node.html.twig template with the following content:

```
{# contents of parent/templates/node.html.twig #}
file is themes/custom/parent/templates/content/node.html.twig<br>
directory is: {{ directory }}

```

Leave `node.html.twig` unimplemented in child theme, so that it inherits from parent.
2. Set default theme to `child`.
3. Visiting node/1 will print:

```
file is themes/custom/parent/templates/content/node.html.twig
directory is themes/custom/child

```

In theory, step 3 should produce a value of `themes/custom/parent` for the directory variable, otherwise, how can you depend on that variable to reference URLs of additional assets from the parent theme?

In actuality, `{{ directory }}` is acting exactly like `{{ active_theme_path() }}`.

As I understand it, this basically means that you cannot use `{{ directory }}` to reference any static assets in a base or parent theme, such as the path to an image icon, or using the `{{ source() }}` Twig function to insert an inline SVG. In my case, I'm trying to do just this:

`{{ source(directory ~ '/images/svg/resources.svg') }}`

But I get: **Twig\_Error\_Loader: Template "themes/custom/child/images/svg/resources.svg" is not defined.** Obviously, that file only exists in the parent theme.

Possibly this should be filed as a bug on drupal.org but wanted to check here first if this is already known and/or to award some karma points for a successful workaround.

**Update:**

My current workaround is to implement hook\_preprocess() and set a global template variable called `parent_theme_path` that points to the parent theme path.

```
// @file parent.theme
/**
 * Prepares global variables for all templates.
 */
function parent_preprocess(&$variables) {
  $handler = \Drupal::service('theme_handler');
  $variables['parent_theme_path'] = $handler->getTheme('parent')->getPath();
}

```

```
{# @file parent/templates/node.html.twig #}
{{ source( parent_theme_path ~ 'images/svg/resources.svg') }}

```

# Answer
> 2 votes
You can also use this:

```
{{ source('@parent/../images/svg/resources.svg') }}

```

where `@parent` is the name of the parent theme. `@parent` references to the `templates` folder, so `@parent/../` was added to go to the root directory of your theme.

---
Tags: 8, theming
---


# Question
Title: How to get field value from paragraph entity in twig?
Is it possible to **get the value from the field inside the paragraph in the red rectangle**. This code part is placed within a twig-template "`field--block-content--field-example.html.twig`"

```
{% for item in items %}
  {{ dd(item.content) }}
  {{ item.content }}
{% endfor %}

```

# Answer
> 3 votes
To get the raw value from an entity:

```
{{ entity.field_example.value }}

```

Where `entity` can be seen as `item.content['#paragraph']`

In my case:

```
item.content['#paragraph'].field_is_custom_package.value

```

---
Tags: theming
---


# Question
Title: Import configuration in CI/CD script does not work
I have a Drupal 8.9.13. I wanted to install a new module, for example the google\_tag. But in my case I do that in my CI/CD process. So to do that I did the following actions:

1. edit the composer.json in a way to contain the new module
2. the composer.lock is updated for the same reason (by enabling the module using drush)
3. updated the core.extension.yml in a way to contain a line
4. generated the \*.settings.yml by exporting the configuration.

During the deployment, first of all the code source is get from the git repo and then a 'composer install' is called. After that a "./vendor/bin/drush config-import -y" is called to import the configuration of the new module (event the module wasn't configured, there is a default config). And here I have the following error:

Unable to install the google\_tag module since it does not exist.

Does someone have any idea what might be the problem?

I saw also that in Drupal's database in the table config for the value of core.extension I have a value which usually describes the activated modules and the one I am trying to activate is not there.

# Answer
> 0 votes
After running `composer install`, you need to add a `drush cr` before you run `drush cim`. All together:

```
composer install
./vendor/bin/drush cr
./vendor/bin/drush config-import -y

```

---
Tags: 8, drush, importing-exporting
---


# Question
Title: How can I merge a content type's fields and settings from dev to staging?
I have an existing content type on staging. On dev we've added a ton of new fields and display and form setting changes, etc.

It's not practical to go through the config files and find all of the many dozens if not hundreds of interconnected config files related to the content types, its fields and its settings.

So I heard Features is the way to go (even though I thought config sync in D8 was supposed to make Features not needed anymore).

So I've created my Feature. Keeping it simple I just checked the single content type. Saved my feature module and moved it to staging.

When I try to enable to the module I get a screen full of "unable to install" because the configuration already exists errors. Ugg.

So I read that I should remove the configurations. But this removes content and/or associated configurations like vocabularies.

Surely there must be a practical way to merge a content type from dev to staging?

# Answer
> 2 votes
~~To fix the issue include config in optional directory instead of install directory in your features module. It will then only install the config which are not present in the new site. Which will solve your issue.~~

> When I try to enable to the module I get a screen full of "unable to install" because the configuration already exists errors.

Ok, I misread your question. To update existing config as well you need to do partial cim.

Copy the feature configs into a different directory named **feature-config-sync** (can be anything other than standard folder names) in default site config directory. And execute the below command. It will import new config and update existing configs if needed. Limitation is it wont delete any configs.

> drush cim --partial --source=../path/to/config/feature-config-sync

IIRC you can even map the module install/optional config directory as source during partial import.

Once your import is done you can delete the **feature-config-sync** directory.

---
Tags: configuration, features
---


# Question
Title: How many child pages can be added under a book parent item?
When using the Book module, is there any limit on the number of child items that can be added to a book parent node?

# Answer
> 1 votes
Yes, books in Drupal 8 and 9 have a maximum depth of 9, which is the value of the `BookManager::BOOK_MAX_DEPTH` constant.

```
const BOOK_MAX_DEPTH = 9;

```

# Answer
> 0 votes
If your question isn't about the hierarchy levels but the number of nodes that can be added in any level here's a question and answer with a lot more information:

How to increase book weight range for a same-level chapters

You can recreate the Book features with Entity reference fields if this is a very serious limitation for you.

---
Tags: nodes, 9
---


title: Custom Module not showing up

text:
# Question
Title: Custom Module not showing up
Playing around with Drupal 8 and trying to create a custom module with a custom block:

I have a module.info.yaml file:

```
name: Custom Module
description: Description Here
package: Custom Module
type: module
core: 8.x

dependencies:
  - block

```

I have a src/Plugin/CustomBlock/CustomBlock.php file:

```
<?php

/**
 * Provides a 'Custom' Block
 *
 * @Block(
 *   id = "custom_block",
 *   admin_label = @Translation("Custom Block"),
 * )
 */

namespace Drupal\custom_block\Plugin\Block;

use Drupal\Core\Block\BlockBase;

class CustomBlock extends BlockBase {
  /**
   * {@inheritdoc}
   */
  public function build() {
    return array(
      '#markup' => $this->t('Hello, World!'),
    );
  }
}

```

The module is in /modules/custom/module. I have installed contrib modules in /modules/contrib with no issue.

If I go to the Extend page, my module is not listed.

Please let me know what I am missing here?

# Answer
Typo on the module file name:

```
module.info.yaml

```

Should be

```
module.info.yml

```
> 12 votes

# Answer
**EDIT:** I think I'm mistaken, sorry! For now, just putting this note into the comment -- when I have a more definitive answer, I'll update again.

---

FWIW, with Drupal core 8.7.7, our custom module didn't show on /admin/modules until I added the new `core_version_requirement` key to `mymodule.info.yml`, i.e.

```
core_version_requirement: '^8'

```

For now, I kept the old `core: 8.x` line in there, too, b/c I'm unsure whether it's good/bad/neutral to have both -- I think it's still being decided, tbh. Speaking of which, there's a change record and a ton of issue threads (active and completed) you can check out to learn more, if you wish:

* 8.7.7 release notes excerpt:

> **Core versioning support in \*.info.yml files**
> Drupal 8.7.7 introduces a new `core_version_requirement` key to `*.info.yml` files, allowing contributed modules to specify specific versions for Drupal core compatiblity, as well as to indicate that they are compatible with both Drupal 8 and the forthcoming Drupal 9 release. See the change record for more details.
* Change record (mentioned in that excerpt)
* Main issue thread for this functionality -- plus all the related issues listed on the right.
> 1 votes

# Answer
Also make sure your web server has file permissions to the module.
> 0 votes

# Answer
Web server permissions may indeed be the issue, especially if everything else seems fine.

https://www.drupal.org/docs/security-in-drupal/securing-file-permissions-and-ownership

I constantly run this set of commands because my Windows ftp client sets ownership to my-user:my-user. Your web server might not be apache.

(requires a symlink to your drupal root folder from the server root)

```
cd /drupal9
sudo chown -R my-user:apache .
sudo find . -type d -exec chmod 750 '{}' \;
sudo find . -type f -exec chmod 640 '{}' \;
cd web/sites
sudo find . -type d -name files -exec chmod 770 '{}' \;
for d in ./*/files
do
   sudo find $d -type d -exec chmod 770 '{}' \;
   sudo find $d -type f -exec chmod 660 '{}' \;
done

```
> -1 votes

---
Tags: 8
---


title: Site broken with multiple errors

text:
# Question
Title: Site broken with multiple errors
I am trying to install a Drupal site locally on a new laptop that was working fine on the old one. I get the following errors.

> Notice: Undefined offset: 2 in Drupal\Core\Database\Database::parseConnectionInfo() (line 215 of /var/www/html/docroot/core/lib/Drupal/Core/Database/Database.php)

> Notice: Undefined index: driver in Drupal\Core\Database\Database::openConnection() (line 367 of /var/www/html/docroot/core/lib/Drupal/Core/Database/Database.php).

> Drupal\Core\Database\DriverNotSpecifiedException: Driver not specified for this database connection: default in Drupal\Core\Database\Database::openConnection() (line 368 of /var/www/html/docroot/core/lib/Drupal/Core/Database/Database.php).

It's running inside Docker, PHP version is 7.3.27 and Drupal version 8.9.

# Answer
> 2 votes
The notices and the exception are caused from the connection array not setting the *driver* property. In fact, `Database::parseConnectionInfo()` tries to access a numeric index of the array it gets with the following code.

```
// If there is no "driver" property, then we assume it's an array of
// possible connections for this target. Pick one at random. That allows
// us to have, for example, multiple replica servers.
if (empty($info['driver'])) {
  $info = $info[mt_rand(0, count($info) - 1)];
}

```

That code assumes that the array defining available connections is similar to the following one, in the case there are more connections for that target.

```
$databases['default']['replica'][] = [
  'database' => 'databasename1',
  'username' => 'sqlusername1',
  'password' => 'sqlpassword1',
  'host' => 'localhost',
  'port' => '3306',
  'driver' => 'mysql',
  'prefix' => '',
  'collation' => 'utf8mb4_general_ci',
];
$databases['default']['replica'][] = [
  'database' => 'databasename2',
  'username' => 'sqlusername2',
  'password' => 'sqlpassword2',
  'host' => 'localhost',
  'port' => '3306',
  'driver' => 'mysql',
  'prefix' => '',
  'collation' => 'utf8mb4_general_ci',
];

```

It cannot be the following one because the code is expecting the indexes for `$databases['default']['replica']` to be between 0 and 1.

```
$databases['default']['replica'][3] = [
  'database' => 'databasename1',
  'username' => 'sqlusername1',
  'password' => 'sqlpassword1',
  'host' => 'localhost',
  'port' => '3306',
  'driver' => 'mysql',
  'prefix' => '',
  'collation' => 'utf8mb4_general_ci',
];
$databases['default']['replica'][4] = [
  'database' => 'databasename2',
  'username' => 'sqlusername2',
  'password' => 'sqlpassword2',
  'host' => 'localhost',
  'port' => '3306',
  'driver' => 'mysql',
  'prefix' => '',
  'collation' => 'utf8mb4_general_ci',
];

```

Basing on the first notice message, this seems the case: The array containing the connection information, which is contained in the settings.php file, or which is passed to `Database::addConnectionInfo()` from a module, doesn't contain all the necessary information, starting from the *driver* property, and contains the wrong information.
In the case the notices and the exception are caused from Drupal when it reads the connection information from the settings.php file, that file could be corrupted, which would explain why it works in a case, but not in the other one.

As for why the connection settings work in one case but not in the other one, assuming that the part of the settings.php file defining the available connections is the same in both the case, or that the settings.php file isn't corrupted, that could happen because:

* The settings.php file used is different
* A module is adding information for a new connection in one case, and it's using a malformed array

Where Drupal looks for the settings.php file is explained in default.settings.php, which explains that, for a https://www.drupal.org:8080/mysite/test/ site, Drupal will look into the following directories.

* sites/8080.www.drupal.org.mysite.test
* sites/www.drupal.org.mysite.test
* sites/drupal.org.mysite.test
* sites/org.mysite.test
* sites/8080.www.drupal.org.mysite
* sites/www.drupal.org.mysite
* sites/drupal.org.mysite
* sites/org.mysite
* sites/8080.www.drupal.org
* sites/www.drupal.org
* sites/drupal.org
* sites/org
* sites/default

The default directory, sites/default is the last directory where Drupal searches the settings.php file. It's sufficient that another directory matching the domain name / port number is found that Drupal will use a different settings.php.

In the case the connection is added by a module, it could be that module is installed in one case, but not in the other one. Drupal looks for third-party modules in different directories, as described in Directory Structure. (*\[domain\]* means the domain name used to access the site.)

* /modules
* sites/\[domain\]/modules
* sites/default/modules

---
Tags: 8, database
---


# Question
Title: Using attachFileToField in a Functional test doesn't allow entering the alt text for an image
In a Functional test, I am doing:

```
    $this->drupalGet('node/add/article');

    $page->fillField('title[0][value]', 'Page 1 EN');

    $page->pressButton('Add image');
    // Using TestFileCreationTrait::getTestFiles().
    $image = current($this->getTestFiles('image'));
    $page->attachFileToField('field_image', $this->container->get('file_system')->realpath($image->uri));

```

This appears to work, in that if I then do:

```
    $page->pressButton('Save');

```

I get a form error because the image alt text is required.

But this doesn't work:

```
    $page->fillField('field_image[0][alt]', 'Image alt text EN');

```

and in the browser output, I don't see the subfields for the image.

In Functional*Javascript* tests that use attachFileToField(), there's typically a waitForFoo() call which waits for the form AJAX to update and show the image subfields.

But that's not available in Functional tests, and besides, there's no AJAX since there's no JS.

How can I get this to work?

(I'd really rather not use drupalPost() to send a massive edit array; using Mink to fill fields one by one is much better DX I find.)

# Answer
> 0 votes
Ah, it looks like this might do it:

```
    $page->fillField('field_image[0][alt]', 'Image alt text EN');
    $page->pressButton('Upload');
    // This reloads the page and the alt text field is now present.

```

---
Tags: testing
---


title: user_presave and user status

text:
# Question
Title: user_presave and user status
I have written this hook in my custom module, just to track the status changes of the users.

```
/**
 * Implements @see hook_user_presave
 */
function mycustommodule_user_presave(&$edit, $account, $category) {
$account_user_id_=$account->uid ;
 $new_status=  $edit['status'];
 $previous_status= $account->status;
watchdog('attention',
     'User  %ui changed status to: %newstat, previous status was %previousstat',
     array('%ui' => $account_user_id_, '%newstat' => $new_status, '%previousstat' => $previous_status, WATCHDOG_NOTICE));
}

```

I noticed for example that when a user activates his newly created account by clicking on the link sent by email, the status moves to 1 which is good . But I also noticed other message that I couldn't know its cause:

"user 34 changed status to: , previous status was 1" . What makes the status of a user moves from 1 to null ?

# Answer
> 1 votes
You get `NULL` for `$edit['status']` because the status hasn't been changed. The functions that calls `user_save()`, which invokes `hook_user_presave()` contain the following code.

```
// user_profile_form_submit()

$account_unchanged = clone $account;
entity_form_submit_build_entity('user', $account, $form, $form_state);

// Populate $edit with the properties of $account, which have been edited on
// this form by taking over all values, which appear in the form values too.
$edit = array_intersect_key((array) $account, $form_state['values']);
user_save($account_unchanged, $edit, $category);

```

```
// user_register_submit()

$account = $form['#user'];
entity_form_submit_build_entity('user', $account, $form, $form_state);

// Populate $edit with the properties of $account, which have been edited on
// this form by taking over all values, which appear in the form values too.
$edit = array_intersect_key((array) $account, $form_state['values']);
$account = user_save($account, $edit);

```

As the comment says, `$edit` contains only the account properties that have been changed, not every property. If you want to obtain the status value, you should use the following code.

```
$new_status = isset($edit['status']) ? $edit['status'] : $account->status;

```

---
Tags: 7
---


# Question
Title: How to set required POST parameters for routes
With GET method we can do something like this

```
example.user
  path: 'example/{user}'
  defaults:
    _controller: '\Drupal\example\Controller\ExampleController::content'
  requirements:
    _permission: 'access content'
    user: ^[a-zA-Z0-9_]+

```

so it sets `user` param as required and also limits its format.

But I'd like to do something similar with POST method. Tried this, but not working:

```
example.user
  path: 'example'
  methods:  [POST]
  defaults:
    _controller: '\Drupal\example\Controller\ExampleController::content'
  requirements:
    _permission: 'access content'
    user: ^[a-zA-Z0-9_]+

```

EDIT:

Sure I can set some logic inside controller like this, but it doesn't feel right:

```
public function store(Request $request) {
   $params = json_decode($request->getContent(), TRUE);
   //I'm trying to avoid using these conditions inside controller methods
   if(empty($params['user'])){
      //return error response
   }else{
     if (!preg_match('/^[a-zA-Z0-9_]+/', $params['user'])) {
       //return error response
     }
   }
  //return success response
}

```

So what's drupal way to do it?

# Answer
Drupal routing system only validates parameters passed via URL (or GET) using route placeholder. Read more here

For a POST method, you can validate parameters on your controller function or if you don't want to pass the request to your controller prior to validating it, technically you can validate the parameters by defining a custom function under the requirement.

```
  example.user
  path: '/example/name'
  defaults:
    _controller: '\Drupal\example\Controller\ExampleController::content'
  requirements:
    _custom_access: '\Drupal\example\Controller\ExampleController::access'

```

Then

```
namespace Drupal\example\Controller;

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Session\AccountInterface;

/**
 * Builds an example page.
 */
class ExampleController {

  /**
   * Checks access for a specific request.
   *
   * @param \Drupal\Core\Session\AccountInterface $account
   *   Run access checks for this account.
   *
   * @return \Drupal\Core\Access\AccessResultInterface
   *   The access result.
   */
  public function access(AccountInterface $account) {
    // parameters from the route and/or request as needed.
   // **Here you can access and validate parameters.**
    return AccessResult::allowedIf($account->hasPermission('do example things') && $this->someOtherCustomCondition());
  }

}

```
> 1 votes

# Answer
I did something like this before ( take a look at helper link ),it not required to tell router about type

just post your data and in your controller get them with

```
$value = \Drupal::request()->request->get('your_posted_variable');

```

Also you can get all $\_POST variables with

```
  \Drupal::request()->request->all(); // equal to $_POST

```
> 0 votes

# Answer
You don't need to set anything on the route if you have a default parameter for POST you can add the value directly in the controller:

```
\Drupal\example\Controller\ExampleController::content

```

Then you check if you have receive some value in the POST method and if you have values replace the default value.
> 0 votes

---
Tags: 8, routes
---


# Question
Title: How can i add a property(uuid) to existing custom entity base_table?
I want to add uuid property to existing custom entity extending ContentEntityBase. I've tried to a add the uuid entity key and installed field storage definition by:

```
function my_module_post_update_adduuid() {
  \Drupal::entityTypeManager()->clearCachedDefinitions();
  $definition_update_manager = \Drupal::entityDefinitionUpdateManager();

  /** @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $last_installed_schema_repository */
  $field_storage_definition = BaseFieldDefinition::create('uuid')
    ->setLabel(new TranslatableMarkup('UUID'))
    ->setReadOnly(TRUE);

  $definition_update_manager->installFieldStorageDefinition('uuid', 'custom_entity_id', 'custom_entity_id', $field_storage_definition);
}

```

The "problem" is that for custom entity with data\_table defined the property is added to the data\_table and not to the base\_table as expected. If the custom entity doesn't have data table defined the uuid property is written properly in base\_table.

Everything seems to work even though the uuid property is defined in data\_table but the schema is inconsistent compared to clean install with the uuid key present from the beginning.

# Answer
Found a workaround for this:

Step 1: Add uuid to entity keys in annotation.

Step 2: Add custom storage handler in annotation.

Step 3: Create the custom storage handler class extending SqlContentEntityStorage with getCustomTableMapping overridden:

```
/**
 * Use AddUuidTablemaaping to support adding uuid to base table.
 */
public function getCustomTableMapping(ContentEntityTypeInterface $entity_type, array $storage_definitions, $prefix = '') {
  $prefix = $prefix ?: ($this->temporary ? 'tmp_' : '');
  return AddUuidTableMapping::create($entity_type, $storage_definitions, $prefix);
}

```

Step 4: Create AddUuidTableMapping extending DefaultTableMapping and override create method.

```
public static function create(ContentEntityTypeInterface $entity_type, array $storage_definitions, $prefix = '') {
  $table_mapping = new static($entity_type, $storage_definitions, $prefix);

  $revisionable = $entity_type->isRevisionable();
  $translatable = $entity_type->isTranslatable();

  $id_key = $entity_type->getKey('id');
  $revision_key = $entity_type->getKey('revision');
  $bundle_key = $entity_type->getKey('bundle');
  $uuid_key = $entity_type->getKey('uuid');
  $langcode_key = $entity_type->getKey('langcode');

+ // The Magic, without this uuid will be added to dataTable if such exists.
+ if (array_key_exists('uuid', $storage_definitions)) {
+   $uuid_key = 'uuid';
+ }
+ // end Magic.

  $shared_table_definitions = array_filter($storage_definitions, function (FieldStorageDefinitionInterface $definition) use ($table_mapping) {
    return $table_mapping->allowsSharedTableStorage($definition);
  });

  $key_fields = array_values(
    array_filter(
      [$id_key, $revision_key, $bundle_key, $uuid_key, $langcode_key]
    )
  );

  $all_fields = array_keys($shared_table_definitions);
  $revisionable_fields = array_keys(array_filter($shared_table_definitions, function (FieldStorageDefinitionInterface $definition) {
    return $definition->isRevisionable();
  }));
  // Make sure the key fields come first in the list of fields.
  $all_fields = array_merge($key_fields, array_diff($all_fields, $key_fields));

  ...
}

```

Step 5: Create the field storage definition in post update hook:

```
$definition_update_manager = \Drupal::entityDefinitionUpdateManager();

/** @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $last_installed_schema_repository */
$field_storage_definition = BaseFieldDefinition::create('uuid')
  ->setLabel(new TranslatableMarkup('UUID'))
  ->setReadOnly(TRUE);

$definition_update_manager->installFieldStorageDefinition('uuid', $entity_type_id, 'my_custom_module', $field_storage_definition);

```

Step 6: Set uuid values for existing entities in baseTable:

```
$rows = $db_connection->select($entity_type_id, 'e')
  ->fields('e', ['id'])
  ->isNull('uuid')
  ->execute()
  ->fetchAll();

foreach ($rows as $row) {
  $db_connection->update($entity_type_id)
    ->fields(['uuid' => \Drupal::service('uuid')->generate()])
    ->condition('id', $row->id)
    ->execute();
}

```

Step 7: Update entity type and field storage schema:

```
$definition_update_manager = \Drupal::entityDefinitionUpdateManager();

$e = $definition_update_manager->getEntityType($entity_type_id);
$keys = $e->getKeys();
$keys['uuid'] = 'uuid';
$e->set('entity_keys', $keys);

$definition_update_manager->updateEntityType($e);
$field_storage_definition = $definition_update_manager->getFieldStorageDefinition('uuid', $entity_type_id);
$definition_update_manager->updateFieldStorageDefinition($field_storage_definition);

```

This last step will update the entity type definition in db + make uuid field not\_null in in baseTable.

Done.

Alternate solution is to use $definition\_update\_manager-\>updateFieldableEntityType, but will require overriding storage\_schema handler(assuming there are existing entities) for the entity type as it forces not\_null for entity keys by default.
> 3 votes

---
Tags: 8, entities
---


# Question
Title: Add meta tags on a page generated by a custom module
I am trying to modify the meta tags of one of the pages of my web. For example, I want to change the `title`, the `description` and add the `robots` tag.

I have tried using the token and metatag modules to change this, but it is too generic and I do not see the option to change it individually on each page.

Also keep in mind that the page on which I want to change these tags is generated by a custom module: creating the Twig manually and generating the URL by `routing.yml`. There's the problem, that by not generating by the back, it does not recognize it as a page.

# Answer
Inside module\_name.module:

```
/**
 * Implements hook_page_attachments().
 */
function module_name_page_attachments(array &$page) {
  $description = [
    '#tag' => 'meta',
    '#attributes' => [
      'name' => 'description',
      'content' => 'This is content.',
    ],
  ];
  $page['#attached']['html_head'][] = [$description, 'description'];
}

```
> 5 votes

# Answer
I've tested successfuly Metatag Routes module for Drupal 8. The module adds a button in the default metatag dashboard page. The user can configure metatags for custom controllers generated by code.

To complete the solution where we use a hook. We can add a condition to apply the meta description to a specific route:

```
/**
 * Implements hook_page_attachments().
 */
function module_name_page_attachments(array &$page) {
  // Check if the current route matches the desired route.
  $current_route_name = \Drupal::routeMatch()->getRouteName();

  if ($current_route_name == 'name_of_the_desired_route') {
    $description = [
      '#tag' => 'meta',
      '#attributes' => [
        'name' => 'description',
        'content' => 'This is the description of the desired route.',
      ],
    ];
    $page['#attached']['html_head'][] = [$description, 'description'];
  }
}

```
> 2 votes

# Answer
Metatag\_routes does not work if your path has parameters. For example, in my case:

```
avt_general.page_resources:
  path: '/group/{groupid}/recursos'
  defaults:
  ...

```

When you add new metatags for "/gruoup/something/recursos" the module associates the data with "avt\_general.page\_resources", so you cannot add more metatags to "/gruoup/something2/recursos". Hope this helps.
> 0 votes

---
Tags: 8, meta-tags
---


title: Hide/mask link URL in the web browser

text:
# Question
Title: Hide/mask link URL in the web browser
I'm new to Drupal and here is the requirement - There is a page that has a field (Doc link) of type Link. I give the URL of the Doc link field as /sites/default/files/docs/file1.html. Now when I click on this Doc link field it shows the entire folder structure of file1.html in the web browser. I don't want users to see the actual path where file1.html is stored. I want to be able to mask/hide this URL in the web browser. So instead of displaying https://xyz.com/sites/default/files/docs/file1.html I want to display a generic link - https://xyz.com/docs/latest/file1.html. Is there any module that I could use to achieve this?

# Answer
> 0 votes
It sounds like you want to store the file in Drupal's private file system. There is no need for a module - this is in core.

Files that are in the private system will:

1. Be protected from access by Drupal's core access control system. If the user don't have access to the page where the file field is attached, he/she will not have access to the file.
2. The URL will not be generally usable. (i.e. the link will be displayed in the browser's address field, but only works "inside" Drupal).
3. If you place your private files outside the webroot (strongly recommended), they can *only* be accessed through Drupal's access control system. There is no real URL that can be guessed.

There is plenty of tutorials about setting up and using a private file system in Drupal \[GIYF\]. Here is a link to one example:

https://www.cmsdrupal.com/blog/how-secure-private-files-drupal-tips-safely-configuring-your-file-system

---
Tags: 8, path-aliases
---


title: Database log details page blank

text:
# Question
Title: Database log details page blank
We're having a problem with our log detail page (/admin/reports/event/XXXXXX):

As you can see, the content is blank. There are no apparent errors in the Apache logs. View source shows that the entire page HTML is rendering properly, there's just nothing in the content div. Watchdog messages are showing up in the Recent Log Messages page (/admin/reports/dblog), it's just that when we click on one to get more detail, we get the blank detail page.

The details page was working a couple of weeks ago, and we haven't made any configuration changes since then. We had to restart Apache and MySQL recently, but I don't see why that would cause this.

Tried disabling and re-enabling the Database Logging module.

Any suggestions?

# Answer
> 8 votes
Check if there are a user with uid=0 in users database table.
If not, just add a new row with 0 uid and it'll resolve the problem.
Good luck!

# Answer
> 1 votes
I've struggled with this problem forever and after searching for about the 20th time for a quick fix I found this thread and decided to just hack a solution. I know this is a cardinal sin in the Drupal world but the offending function isn't hookable. Please don't kill me!

The problem appears to be something to do with errors that are created by an anonymous user. Watchdog entries reference the offending user. But if no UID is present the query finds nothing = empty error page. That's because the query performs an INNER JOIN on the user table, rather than a LEFT JOIN. So I went into the dblog.admin.inc file, found the dblog\_event() function and added this just after the initial query on line 154:

```
if(!$result){
$result = db_query('SELECT w.*, u.name, u.uid FROM {watchdog} w LEFT JOIN {users} u ON w.uid = u.uid WHERE w.wid = :id', array(':id' => $id))->fetchObject();
drupal_set_message(t('For whatever reason the user information was not logged with the watchdog entry so the user information might not be accurate.'));
}

```

Or you could just change the original query from an INNER JOIN to a LEFT JOIN...or the total fail safe: remove the user information entirely from the query.

No, that fix doesn't use the DB API but neither does the original query. Maybe some kind soul can submit a patch to the core team since this is such a critical feature for many admins.

For the record I'm using a Postgres 9.2.6 database, Drupal 7.26, PHP 5.3.10, Apache 2.2.22.

---

**Edit**: So, after five years returning to this page every time Drupal released an update, I finally decided to follow the direction in the top response (rather than my quick hack). For those wondering, here's what I did.

1. Created a new user with the username 'anonymous' and a fake email address. I set the new user as 'blocked'.
2. I don't have access to a nice database admin tool so I used the 'Execute PHP Code' option in the Development menu to run this little code snippet:

$query = db\_query("UPDATE {users} SET uid = '0' WHERE name = 'anonymous'"); if($query == 1) { drupal\_set\_message('User Updated'); }

Permanent problem solution. Yay.

# Answer
> 1 votes
I got this problem when running automated tests, which create a temporary user, and then delete that user. There seems to be a problem with how watchdog handles messages that were produced by no-longer existent users. They display as owned by "Anonymous," but have a link that includes a parameter of a now-nonexistent UID.

# Answer
> 0 votes
If you have Drush installed, you can use the watchdog-show command to view the details of the watchdog messages, which should give you an idea of why the details aren't showing up.

Alternatively, you can use a mysql client to query the watchdog table in the database, to see the details of the watchdog messages. The watchdog entries can be a bit difficult to read because they typically use a lot of token replacement during output, but you should be able to get an idea of the main error that's causing the problems with the log detail pages. The following query outputs the 5 most recent watchdog entries and might be enough to get you started:

```
SELECT * FROM watchdog ORDER BY wid DESC LIMIT 5\G;

```

# Answer
> 0 votes
If you getting blank page when you reach dblog page (admin/reports/dblog) then try drush delete wachdog

```
watchdog:delete (wd-del, wd-delete, wd) or shortcut drush wd all

```

---
Tags: log-messages
---


# Question
Title: Custom migration source plugin not enabled in source site error
I'm trying to write a custom source plugin for a d7-d8 migration. I want to extend the Drupal\node\Plugin\migrate\source\d7 Node.php plugin.

I'm getting the following error when I try to run the migration from the UI:

> Migration my\_node\_page did not meet the requirements. The module my\_xtra is not enabled in the source site. source\_module: my\_xtra.

When I run drush ms I get a different error

> No database connection configured for source plugin variable

The database connection key is specified in the group yml and works for other migrations which don't use the custom source plugin.

Here's my yml file

```
id: my_node_page
label: Node Basic Page
audit: true
migration_tags:
  - Drupal 7
  - Content
migration_group: my_group
source:
  plugin: my_xtra_node
  node_type: 'page'
process:

```

Here's my custom plugin.

```
    <?php

    namespace Drupal\my_extra\Plugin\migrate\source;

    use Drupal\node\Plugin\migrate\source\d7\Node;

    /**
     * Drupal 7 node source from database.
     *
     * @MigrateSource(
     *   id = "my_xtra_node",
     *   source_provider = "node",
     *   source_module = "my_xtra",
     *
     * )
     */
    class NodeXtra extends Node {

      /**
       * {@inheritdoc}
      */
      public function query() {
        // Select node in its last revision.
        $query = $this->select('node_revision', 'nr')
          ->fields('n', [
            'nid',
            'type',
            'language',
            'status',
            'created',
            'changed',
            'comment',
            'promote',
            'sticky',
            'tnid',
            'translate',
          ])
          ->fields('nr', [
            'vid',
            'title',
            'log',
            'timestamp',
          ]);
        $query->addField('n', 'uid', 'node_uid');
        $query->addField('nr', 'uid', 'revision_uid');
        $query->innerJoin('node', 'n', static::JOIN);

        // If the content_translation module is enabled, get the source langcode
        // to fill the content_translation_source field.
        if ($this->moduleHandler->moduleExists('content_translation')) {
          $query->leftJoin('node', 'nt', 'n.tnid = nt.nid');
          $query->addField('nt', 'language', 'source_langcode');
        }
        $this->handleTranslations($query);

        if (isset($this->configuration['node_type'])) {
          $query->condition('n.type', $this->configuration['node_type']);
        }

        return $query;
      }
}

```

Thanks!

# Answer
> 0 votes
Your source module should be "node" since that's what you're extending.

```
/**
* Drupal 7 node source from database.
*
* @MigrateSource(
*   id = "my_xtra_node",
*   source_module = "node"
* )
*/

```

---
Tags: 8, migrations
---


# Question
Title: Form APi Could not parse property path Unexpected token
Using

* Drupal 9.1.4
* symfony/property-access I think is on 5.2.0 \[that's the last version mentioned in the changelog\]

I have a custom built form with some date and number fields, for example:

```
$form['calculator']['chargeable_consideration'] = [
  '#type' => 'number',
  '#min' => 0,
  '#step' => 0.01,
  '#required' => TRUE,
  '#size' => 13,
  '#attributes' => [
    'placeholder' => '£0.00',
  ],
  '#title' => $this->t('description text (£) <button class="tooltip-anchor" title="pop up text">?</button>'),
  '#prefix' => '<div class="form-group">',
  '#suffix' => '</div>',
];

```

I have validation checking the dates are within a range, but nothing to check the numbers.

if I use the numbers

* 10000.85
* 123456789.70
* 1234567890

the form works as expected.

If I use the numbers

* 100000.85
* 123456789.71

I get the following error

```
Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException: Could not parse property path "calculator][chargeable_consideration". Unexpected token "[" at position 11. in Symfony\Component\PropertyAccess\PropertyPath->__construct() (line 111 of vendor\symfony\property-access\PropertyPath.php).
Symfony\Component\PropertyAccess\PropertyAccessor->getPropertyPath('calculator][chargeable_consideration') (Line: 120)
Symfony\Component\PropertyAccess\PropertyAccessor->getValue(Array, 'calculator][chargeable_consideration') (Line: 406)
Drupal\custom_form\Form\CustomForm->validateForm(Array, Object)
call_user_func_array(Array, Array) (Line: 82)

```

How do I prevent it from throwing this exception?

# Answer
> -1 votes
'#title' should be a plain text. you have added html code Button in it.

---
Tags: forms
---


# Question
Title: How can I create a Link to an URL containing a fragment identifier?
Is it possible to create a Link to a node with a hash in url (for opening a special Tab).

I use:

```
Link::createFromRoute('Title', 'entity.node.canonical', ['node' => 77]);

```

And I want to link to: "/node/77#example"

# Answer
> 1 votes
Please see public static function Link::createFromRoute

> array $options: The options parameter takes exactly the same structure. See \Drupal\Core\Url::fromUri() for details.

You can pass a 4th argument as an array with a `fragment` key value:

```
Link::createFromRoute($this->t('Title'), 'entity.node.canonical', ['node' => 77], ['fragment' => 'example']);

```

---
Tags: uri, 9
---


# Question
Title: I'm using the media video module, how do I upload alternative webm and mp4 versions?
Server hosted video. Using the core video field. I'd like to put up a webm version, but also a mp4 version encoded for safari. Create a video block with 2 source items

```
<video>
   <source src="sourcea.webm" type="video/webm">
   <source src="sourceb.mp4" type="video/mp4">
</video>

```

# Answer
> 1 votes
Built in and simple solution

1. Video media type display

Admin \> structure \> media \> video \> display

Under display set multiple videos to a single html tag

2. Set the video field to allow 2 videos to be uploaded

Admin \> structure \> media \> video \> field settings

3. Upload 2 videos

I've got 2 versions in the video tag

```
<video controls="controls" width="640" height="480">
  <source src="/files/2021-04/Destination-Pagoda-Final-Version-web.mp4" type="video/mp4">
  <source src="/files/2021-05/Pagoda-Final-Version-web.webm" type="video/webm">
</video>

```

---
Tags: media
---


# Question
Title: What is the correct order of disabling the default contact form on Drupal?
Drupal comes with a default contact form on each site we install. And while we already have the webforms and recaptcha module installed, some of our site owners still use the default contact form found in `/admin/structure/contact`.

I need to remove them because when they are enabled, the site is prone to spam attacks. I've had a few instances where some of the sites had form submissions with PDF file uploads of inappropriate content. So you see how this is a security risk for our organization.

I removed that contact form in my local site by disabling the following modules in this order:

* contact form
* contact storage
* contact

But I need to do this for all our sites now, so I wrote the following in my `hook_update_n()` function:

```
function my_profile_update_8011() {
    $config = \Drupal::configFactory();
    $moduleHandler = \Drupal::service('module_handler');

    if($moduleHandler->moduleExists('lightning_contact_form')) {
        Drupal::service('module_installer')->uninstall(['lightning_contact_form']);
    }
    if($moduleHandler->moduleExists('contact_storage')) {
        Drupal::service('module_installer')->uninstall(['contact_storage']);
    }
    if($moduleHandler->moduleExists('contact')) {
        Drupal::service('module_installer')->uninstall(['contact']);
    }

}

```

When I deployed this update to our dev environment, it would work on most of the sites, but fail on the rest. They all have this same error message:

```
The error output from the server was: >  [notice] Update started: my_profile_update_8011
>  [error]  SQLSTATE[42S22]: Column not found: 1054 Unknown column 'id' in 'where clause': SELECT 1 AS expression
> FROM
> {contact_message} t
> WHERE id IS NOT NULL
> LIMIT 1 OFFSET 0; Array
> (
> )
>
>  [error]  Update failed: my_profile_update_8011
 [error]  Update aborted by: my_profile_update_8011
 [error]  Finished performing updates.

```

All our sites share the same codebase, so I don't know where my update hook went wrong. Is there a better way of disabling these modules?

thanks, Kevin

# Answer
> 0 votes
You may be able to use this (untested) approach in your `hook_update_N()` implementation:

```
function my_profile_update_8011() {
  $config = \Drupal::configFactory();
  $moduleHandler = \Drupal::service('module_handler');

  $mods_removable = [
    'lightning_contact_form',
    'contact_storage',
    'contact',
  ];

  // Read the configuration.
  $module_data = $config->getEditable('core.extension')->get('module');

  foreach ($mods_removable as $module) {
    if($moduleHandler->moduleExists($module)) {
      // Unset the modules you do not need.
      unset($module_data[$module]);
    }

  // Write the configuration.
  $config->getEditable('core.extension')->set('module', $module_data)->save();
  }

```

See this answer for additional info.

---
Tags: 8
---


# Question
Title: Search api excerpt are empty in the result
I have installed search api module in drupal 8 and included excerpt in the search result.

But excerpt are always empty in the search result. (HTML div element is there but no content inside)

I searched in the forum and enabled the 'highlights' from settings as suggested. But no changes in the result (I cleared the cache)

Am i missing anything?

# Answer
> 10 votes
First go to this path **admin/config/search/search-api/index/default\_index/processors#edit-processors-highlight-settings** make sure this settings are enabled

Next i created a view Global search which has search Excerpt field.

Finally i was able to see the highlighted text word after searching

Actually this is not default search form that comes in drupal8 it is exposed form in block if you see in view exposed form setting i have enabled it. once this is done, exposed form comes in block just place that in which ever regions you want.

# Answer
> 1 votes
Go to /admin/config/search/search-api/index/**INDEXNAME**/processors and click "highlight". Do not forget to include the exerpt in your view fields.

# Answer
> 0 votes
Let me summarize the question anno 2021:

1. Create a Solr server + Default Solr content index (/admin/config/search/search-api)
2. Edit your server (/admin/config/search/search-api/server/**your\_default\_solr\_server**/edit). Open the advanced tab and select:Retrieve result data from Solr, Retrieve highlighted snippets

3. Edit your index and select the datasources (/admin/config/search/search-api/index/**your\_default\_solr\_index**/edit)
4. Add fields to search into. If you created a content type that exists of paragraphs or custom blocks. Be sure to add those fields in this section to get a result. (/admin/config/search/search-api/index/**your\_default\_solr\_index**/fields)

4. Go to the processors section inside the search index (admin/config/search/search-api/index/**your\_default\_solr\_index**/processors) Select 'highlight' and scroll down to the page to enable settings.

Select your preferences:

5. Create a view and add the "Search:Excerpt" into the fields section:

# Answer
> -1 votes
These days this config variable is at: /admin/config/search/search-api/index/default\_solr\_index/processors where "default\_solr\_index" is the machine name of your index.

The checkbox you want is called "Highlight"

---
Tags: 8, search
---


# Question
Title: How to have an environment specific drush.yml?
I'm using Drush 9. With the old drushrc.php I could easily include a local.drushrc.php that dynamically set the site url. How can I do it now?

This is similar to How can i set site url dynamically in Drush-9?. However I don't want to pass the url in every Drush command. I think of something like below in ../drush/drush.yml:

```
drush:
  paths:
    config:
      - ${env.home}/local.drush.yml

```

But I'd need another variable that point to the Drupal site directory (`sites/default` in a monosite, `sites/CURRENT-SITE` in a multisite). Does such a variable exist?

# Answer
Here's what I do on my sites and it works:

## Step 1: sites/default/drush.local.yml

```
options:
  uri: 'https://dev.example.com'

```

You can name this file however you want. Also, since this file varies from env to env, I ignore it with `.gitignore`. At the same time, I commit an `example.drush.local.yml`.

## Step 2: drush/drush.yml

* One level above Drupal installation, .i.e. where Drupal's `index.php` is located, I have my `composer.json` and `vendor` directories.
* In this directory, I create a directory named `drush` and in it, I place a `drush.yml` with these contents:

```
drush:
  paths:
    config:
      - web/sites/default/drush.local.yml

```

After this, I running `drush st` from that directory, will show you that the `drush.local.yml` has been detected and the site URI is set correctly.
> 7 votes

# Answer
Reading https://github.com/drush-ops/drush/blob/master/examples/example.drush.yml it seems you can put a site specific drush.yml in a sites directory. It is merged with the global file(s).

PS! I haven't tested this myself.
> 1 votes

---
Tags: drush
---


# Question
Title: Get all available promotions for specific commerce product
How to get all available promotions, which apply to specific product?

# Answer
> 0 votes
You can do it via `commerce_order.price_calculator` service:

```
$context = new Context($this->currentUser, $this->currentStore->getStore(), NULL, [
    'field_name' => $items->getName(),
]);
/** @var \Drupal\commerce\PurchasableEntityInterface $purchasable_entity */
$purchasable_entity = $items->getEntity();
$adjustment_types = array_filter($this->getSetting('adjustment_types'));
$result = $this->priceCalculator->calculate($purchasable_entity, 1, $context, $adjustment_types);

//Get promotions
$promos = $result->getAdjustments();

```

---
Tags: 8, commerce
---


# Question
Title: Best way to make an EntityFieldQuery query be case sensitive?
The case-sensitivity of queries created using EntityFieldQuery rely on the underlying database's collation \[\*\]. So if the collation for a particular field is 'utf8\_general\_ci', then all the queries created with EntityFieldQuery are case insensitive.

Given that it's not always possible to change the schema (say, if you are developing a module that acts on third party fields) how can you ensure case sensitivity when using EntityFieldQuery ?

My solution so far consists of using a tag and a hook\_query\_TAG\_alter hook to cast the value to a BINARY value (and EFQ allows us to create our own operator for this). Here is an example:

```
function mymodule_query_case_sensitive_alter($query) {
  foreach ($conditions as $index => $condition) {
    if (is_array($condition) && $condition['operator'] == '=c') {
      $placeholder = ':db_condition_placeholder_' . $query->nextPlaceHolder();
      $conditions[$index]['field'] = 'CAST(' . $conditions[$index]['field'] . ' AS BINARY) = ' . $placeholder;
      $conditions[$index]['value'] = array($placeholder => $conditions[$index]['value']);
      $conditions[$index]['operator'] = NULL;
    }
  }
}

```

Then one could run:

```
$r = (new EntityFieldQuery())
  ->fieldCondition('field_my_field', 'value', 'ExaMple', '=c')
  ->addTag('case_sensitive')
  ->execute();

```

Based on this, my questions are:

* It is quite cumbersome. Is there really no simpler way ?
* Does such a tag already exist ?
* This is limited to MySql field storage backend \- how could it be adapted to work on other field storage backends ?

\[\*\] I am assuming a MySql field storage backend here.

# Answer
> 4 votes
MySQL documentation shows you can simply define collation at a query level:

```
SELECT k COLLATE latin1_german2_ci AS k1;

 SELECT *
 FROM t1
 WHERE _latin1 'Müller' COLLATE latin1_german2_ci = k;

 SELECT *
 FROM t1
 WHERE k LIKE _latin1 'Müller' COLLATE latin1_german2_ci;

```

If collation is only thing that affects case sensitivity for you, that's the way to go, no need to cast anything.

Based on this, you can set the operator in the EntityFieldQuery to be 'COLLATE utf8\_general\_cs ='. For instance the following will work:

```
$r = (new EntityFieldQuery())
  ->fieldCondition('field_my_field', 'value', 'eXample', 'COLLATE utf8_general_cs =')
  ->execute();

```

This remains limited to MySQL field storage backend however.

# Answer
> 0 votes
Drupal 8 core patch documentation https://www.drupal.org/files/issues/entity-query-case-2068655-127\_0.patch

* Drupal considers LIKE case insensitive and the following is often used
* to tell the database that case insensitive equivalence is desired:
* @code
* db\_select('users')
* -\>condition('name', db\_like($name), 'LIKE')
* @endcode
* Use 'LIKE BINARY' instead of 'LIKE' for case sensitive queries.

---
Tags: 7, database
---