# Question
Title: What do I enter in the API Username and API password for the Commerce CardPointe module so that it works with Clover Connect?
I have installed the Commerce CardPointe module in Drupal 10 and have a merchant account with Clover. The module asks me to input the API Username, API password, merchant ID and Site. I know the Merchant ID, but unsure what to enter in the API Username, API password and Site fields. I called technical support but they were only aware of an API token - which sounds different from API Username and password. Any help here would really be appreciated! New to Drupal and trying to figure this out for a client ASAP. Thank you!!

# Answer
> 1 votes
For those who find this later, it turns out you need to get the API username and password from technical support when you setup your CardPointe account. This will be different from the relationship you may have with Clover for POS sales established through your bank.

---
Tags: commerce, 10
---


# Question
Title: How do I get both published and unpublished records using entity query?
How can I get both published and unpublished records for a content type using entity field query?

As per documentation we can filter records by **status** either published or unpublished. But I need all records irrespective of node status whether is published are unpublished.

```
 <?php $query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'node')
      ->entityCondition('bundle', 'article')
      ->propertyCondition('status', 1) // only one value accepted here
      ->range(0, 1);

```

# Answer
The propertyCondition allows three arguments. You could pass in an array containing the two values and as third parameter the operator 'IN':

```
->propertyCondition('status', array(0, 1), 'IN')

```

Note: As you have been asking for nodes, and nodes AFAIK only have the status 0 or 1, you could also just omit the status property condition entirely and would get all nodes.

```
$query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'node')
  ->entityCondition('bundle', 'article')
  ->range(0, 1);

```
> 0 votes

# Answer
```
// Setup query to find show nearest to recording timestamp
$query = \Drupal::database()->select('node__field_programme_broadcast', 'nfp')
    ->fields('nfp', ['entity_id', 'field_programme_broadcast_value'])
    ->condition('nfp.field_programme_broadcast_value', $timestamp, '<')
    ->condition('nfp.field_programme_broadcast_end_value', $timestamp, '>')
    ->condition('nfd.status', 1)
    ->orderBy('nfp.field_programme_broadcast_value','DESC')
    ->range(0,1);

// add join (join method doesn't return a query object, so has to be done separately)
$query->join('node_field_data', 'nfd', 'nfd.nid = entity_id');

```

I'm working in Drupal 10.

Here's my working code. Worth noting that join doesn't return the query object, so you can't include that mid way through the query.

Also, node\_data\_field contains the status, so i do a join on nid as the key field.
> 0 votes

---
Tags: 7, entities, database
---


# Question
Title: Abstract Base Class versus Service with Queue API
I'm trying to figure out the best way to structure a custom module that uses the Queue API in D8. I'm new to D8 and a bit rusty with OOP so forgive me if I'm breaking some recommended design pattern here...

I created a single custom module where multiple different queues need to process items differently, but each queue has common functionality that they may need to use between each other. So my question is, should I create an abstract base class that extends `QueueWorkerBase`, make `processItem` abstract, and then add common functionality to the base class?

Something like this:

In src/Plugin/QueueWorker/ProcessQueueBase.php

```
abstract class ProcessQueueBase extends QueueWorkerBase {

  /**
   * @inheritDoc
   */
  abstract public function processItem($data);

  protected function doSomethingCommon(){
    // Some common logic
  }
}

```

In src/Plugin/QueueWorker/ProcessQueueOne.php

```
class ProcessQueueOne extends ProcessQueueBase {

  /**
   * @inheritDoc
   */
  public function processItem($data){
    // Do stuff now

    parent::doSomethingCommon();
  }
}

```

In src/Plugin/QueueWorker/ProcessQueueTwo.php

```
class ProcessQueueTwo extends ProcessQueueBase {

  /**
   * @inheritDoc
   */
  public function processItem($data){
    // Do stuff now

    parent::doSomethingCommon();
  }
}

```

**OR** should I create separate classes that all extend `QueueWorkerBase` and then just create services for the common functionality.

Something like this:

In DoSomethingService.php:

```
class DoSomethingService {

  public function doSomethingCommon(){
    // Do something
  }

}

```

In src/Plugin/QueueWorker/ProcessQueueOne.php

```
class ProcessQueueOne extends QueueWorkerBase {

  /**
   * @inheritDoc
   */
  public function processItem($data){
    // Do stuff

    $do_something = \Drupal::service('mymodule.doSomething');
    $do_something->doSomethingCommon();
  }
}

```

In src/Plugin/QueueWorker/ProcessQueueTwo.php

```
class ProcessQueueTwo extends QueueWorkerBase {

  /**
   * @inheritDoc
   */
  public function processItem($data){
    // Do stuff

    $do_something = \Drupal::service('mymodule.doSomething');
    $do_something ->doSomethingCommon();
  }
}

```

# Answer
> 0 votes
I would suggest an abstract base class, but instead of creating another function `::doSomethingCommon`, partially implement `ProcessQueueBase::processItem` and call `parent::processItem` from the subclasses.

# Answer
> 0 votes
Based on the context you provided I would suggest going for an Abstract class or a Trait.

I try to answer this questions when I doubt:

| Question | Abstract | Trait | Service |
| --- | --- | --- | --- |
| Does **doSomethingCommon()** relys on **QueueWorkerBase** methods or properties? | **Yes** | Yes | No |
| Is **doSomethingCommon()** a common functioanlity that may be used by any other class that does not extends from **QueueWorkerBase**, for example from a Controller or a Form? | No | **Yes** | Yes |
| Should I be able to call **doSomethingCommon()** individually from a function (or Drupal hook) or a global scope? | No | No | **Yes** |
| Should I be able to override or alter **doSomethingCommon()** functionallity in a class? | **Yes** | Yes | No |

Notice that some **Yes** are in highlighted. That is more of a personal preference on which I would use based on what will be easier to implement, maintain and understand from a quick look to the code.

I hope this helps you.

---
Tags: 8, queue
---


# Question
Title: How to alter submission data when inserting a submission
I am trying to alter the data in a webform submission before this is being saved in to the database.

In D7 i would do something like this:

```
function mymodule_webform_submission_presave($node, &$submission){
  if($submission->nid == 1234){
    $n = node_load(4321);
    $value = $n->field_value['und'][0]['value'];
    $submission->data[2][0] = $value;
  }
}

```

However I am not able to achieve the same in Drupal 9. I have tried using some of the `hook_webform_handler_invoke_alter` and `hook_webform_handler_invoke_METHOD_NAME_alter` hooks listed in webform.api.php without success. Are these the correct hooks to use, I have not been able to identify any "submission\_presave" hooks in the Drupal 9 version of webform module (currently v 6.2.2). Any help or guidance toward the best solution would be appreciated.

# Answer
> 1 votes
You can use **hook\_ENTITY\_TYPE\_presave()** to alter webform submission data before insert:

```
function Mymodulename_webform_submission_presave(\Drupal\webform\Entity\WebformSubmission $webform_submission){
  // Get webform type/variation
  $webform_type = $webform_submission->getWebform()->id();
  if($webform_type == 'contact'){
    // Get user submitted data
    $data = $webform_submission->getData();
    // Alter the data of title field
    $data['title'] = 'Testing tt';
    // Pass altered data
    $webform_submission->setData($data);
  }
}

```

Note: can pass parameter that specific to hook **Drupal\webform\Entity\WebformSubmission $webform\_submission** or in this way **\Drupal\Core\Entity\EntityInterface $entity** both are correct

You can follow this refrence to read more about hook\_ENTITY\_TYPE\_presave() hook.

---
Tags: hooks, webforms, 9
---


# Question
Title: How to hide CKEditor5 toolbar plugin based on user permissions?
I am using Drupal 10, CKEditor5, and a text format that has a good number of options in the toolbar including the source editing plugin. I have a custom module that defines a custom permission that I would like to use to show/hide (not disable completely) the source editing button.

What I've tried so far:

my\_module.permissions.yml

```
'show source button':
  title: 'Display Source button in CKEditor'
  description: 'Display the Source button in CKEditor.'

```

my\_module.module

```
// first try
function my_module_ckeditor5_plugin_info_alter(array &$plugin_definitions) : void {
  $currentUser = \Drupal::currentUser();

  if (!$currentUser->hasPermission('show source button')) {
    if (array_key_exists('ckeditor5_sourceEditing', $plugin_definitions)) {
      unset($plugin_definitions['ckeditor5_sourceEditing']);
    }
  }
}

// second try
function my_module_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  $currentUser = \Drupal::currentUser();

  if (!$currentUser->hasPermission('show source button')) {
    $form['#attached']['library'][] = 'ckeditor_source_button/ckeditor_source_button';
  }
}

```

my\_module.js

```
((Drupal) => {

  Drupal.behaviors.ckeditorSourceButtonBehavior = {
    attach: (context, settings) => {
      context.querySelectorAll('.ck-source-editing-button').forEach((element) => {
        element.classList.add('visually-hidden');
      });
    }
  }

})(Drupal);

```

Problems I've encountered with my methods:

1. When using the hook\_ckeditor5\_plugin\_info\_alter, one problem is that unsetting the sourceEditing plugin completely removes the functionality and causes issues with html tags
2. When using the hook\_ckeditor5\_plugin\_info\_alter, the second problem is that the current user is being cached and users that should be able to see the source button are not able to.
3. When using hook\_form\_alter, I'm still having the cache issue happening as I said above but also that the query for '.ck-source-editing-button' is null every time and so is a query for '.ck-editor'.

I've tried adding cache contexts (ex. $form\['#cache'\]\['contexts'\]\[\] = 'session';) of session, user, or user.permissions to the my\_module\_form\_alter but none of them seem to make a difference. Just for clarity, I've added these individually, never tried a combo of any of them

# Answer
> 1 votes
The simplest way to solve the problem would be to add one text format with the button, and one without, and assign those formats to roles with the appropriate permissions. It means two text formats to maintain, but no code to change.

The problem with the `plugin_info_alter` approach is it's not subject to cache contexts (or to be totally accurate it possibly is, or can be made to be, but in a way that's complicated to implement and/or undesirable from a performance perspective); whatever user's request caused that info to be built is the permission that will be used for the condition, not the current user when the info is loaded from cache and used in a particular form.

The JS approach won't work either, because the CKEditor instances aren't (guaranteed to be) created when your behavior first executes. There's a core issue to trigger an event when editors are ready, but the latest patch doesn't apply to 10.1.7, so that might be a pain to work with.

If your sole aim is to hide that button, it might just be best to replace the JS in your library with a CSS file containing a simple rule:

```
.ck-source-editing-button { display: none; }

```

(might need tweaking for specificity)

You should also add the cache context you mentioned in the form alter:

```
$form['#cache']['contexts'][] = 'user.permissions';

```

As the editors are part of the page markup, that should be enough to achieve the desired effect.

# Answer
> 0 votes
thank you for the proposed solutions !

i tested the last solution proposed, i just added !important to css file like this

```
  .ck-source-editing-button {
    display: none !important;
  }

```

and in .module

```
$currentUser = \Drupal::currentUser();
  if (!$currentUser->hasPermission('show source button')) {
    $form['#attached']['library'][] = 'ba_core/ckeditor_source_button';
  }

```

and it works ! no cache problems

---
Tags: javascript, hooks, caching, wysiwyg
---


# Question
Title: Limit facets (exposed filters) based on contextual filters in a view
I'm trying to limit terms in a view facet (exposed filter) based on a contextual filter.

I have products with a 'category/type' taxonomy term. The terms are 2-level, looking roughly like this:

* Category 1
    + Type a
    + Type b
    + Type c
* Category 2
    + Type d
    + Type e
    + Type f

We can assign multiple terms from the 2nd level to each product.

I have a view that shows only products within a 1st level category (e.g. Category 1) by contextual filter, and then allow the user further filter by 'Type' from a facet (should show 'Type a', 'Type b', 'Type c' when contextual filter is 'Category 1').

The problem is that, since we can assign terms from multiple categories to the same product, we are seeing types from 'Category 2' in the facets, despite the fact that we're filtering by 'Category 1'.

Is there a hook to alter the facets, based on the contextual filter applied to the view?

---

So far, I got as far as eliminating an option from an exposed filter:

```
use \Drupal\Core\Form\FormStateInterface;
/**
 * Implements hook_form_views_exposed_form_alter().
 */
function cd_general_form_views_exposed_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  $view_ids = [
    'duplicate_of_product_category_by_material',
    'product_category_by_material',
  ];
  if ($form_id == 'views_exposed_form' && in_array($form_state->get('view')->id(), $view_ids)) {
    unset($form['field_category_target_id']['#options'][118]);
  }
}

```

To test, I duplicated my view so that I have one with normal exposed filters, and the original with facets related to those filters. The code above works on the exposed filters, but not on my facets.

My next step is to look again for hooks in the facets and core\_views\_facets modules.

# Answer
I was able to use hook\_preprocess\_facets\_item\_list.

```
/**
 * Implements hook_preprocess_facets_item_list().
 */
function mymodule_preprocess_facets_item_list(&$variables) {
  $facet_id = $variables['facet']->id();

  if ($facet_id === 'content_category') {
    // In the product categories by material view, remove category
    // facets based on the contextual parent category.
    $path = \Drupal::service('path.current')->getPath();
    $contextual_category_name = explode('/', $path)[2];
    $contextual_category_term = \Drupal::entityTypeManager()
      ->getStorage('taxonomy_term')
      ->loadByProperties([
        'name' => $contextual_category_name,
        'vid' => 'product_category',
      ]);
    // First array element, only.
    $contextual_category_term = reset($contextual_category_term);
    foreach ($variables['items'] as $key_item => $item) {
      $tid = $item['#attributes']['data-drupal-facet-item-value'];
      $term = Term::load($tid);
      $parent = \Drupal::entityTypeManager()
        ->getStorage('taxonomy_term')
        ->loadParents($tid);
      // First array element only.
      $parent = reset($parent);
      $parent_id = $parent->id();
      if ($parent_id !== $contextual_category_term->id()) {
        // Remove facets not within the current parent category.
        unset($variables['items'][$key_item]);
      }
    }
  }
}

```
> 0 votes

---
Tags: views
---


# Question
Title: How should I troubleshoot a problem with sending email?
I have a Drupal 9.5 test server and a production server. The test server is working fine but the production server suddenly stopped sending emails (new accounts, custom form output, etc.). The settings should be the same on both but I double-checked the Drupal settings to be sure.

The Drupal logs don't say anything more than "error sending email". I tried the system maillog but there was nothing helpful there.

I also have the mimemail module installed. Neither mimemail nor system emails are going out.

Where should I look next to troubleshoot this? Is there a common solution to this problem?

Thanks!

# Answer
> 1 votes
If MailInterface::mail returns FALSE ("TRUE if the mail was successfully accepted for delivery, otherwise FALSE"), `Error sending email (from %from to %to with reply-to %reply)` is logged.

So, if this is PhpMail, which I think is the default MailInterface implementation, then PhpMail called PHP's mail function ("Returns true if the mail was successfully accepted for delivery, false otherwise"). That returned false.

Therefore, *probably* the email was not accepted for delivery by whatever way PHP is set up on that system to send mail. The answer to your question is: *you should next look to why PHP's mail function is returning false.*

Additionally some of the answers on https://stackoverflow.com/questions/3186725/how-can-i-get-the-error-message-for-the-mail-function/20203870#20203870 may be useful to you.

---
Tags: emails
---


# Question
Title: Migration of Layout builder enabled node to replace block content
I need to iterate through the article nodes that have enabled the layout builder and replace the content block in the layout builder section with a new content block. I need to do this via migration script. But it's not working as expected.

# yaml file

```
uuid: 599162cc-ba3b-4c4d-98c9-404958545305
langcode: en-gb
status: true
dependencies: {  }
_core:
  default_config_hash: lzIn3Lnhv-Efq1Lry7b0-99zKIaAiawY_AHBy_OJPxM
id: migrate_node
class: null
field_plugin_method: null
cck_plugin_method: null
migration_tags: null
migration_group: NAS
label: null
source:
  plugin: get_article_nodes
  key: default
process:
  nid: nid
  uid: uid
  uuid: uuid
  title: title
  status: status
  created: created
  changed: changed
  block_type_temp:
    plugin: migrate_node
    source_field: layout_builder_value
  layout_builder__layout:
    plugin: get
    source:
      - '@block_type_temp'
destination:
  plugin: 'entity:node'
  default_bundle: article
migration_dependencies: { }

```

# Source plugin

```
<?php

namespace Drupal\my_custom_module\Plugin\migrate\source;

use Drupal\Core\Database\Query\SelectInterface;
use Drupal\migrate\Plugin\migrate\source\SqlBase;
use Drupal\migrate\Row;

/**
 * Source plugin for article content.
 *
 * @MigrateSource(
 *   id = "get_article_nodes"
 * )
 */
final class GetArticleNodes extends SqlBase
{

  /**
   * {@inheritdoc}
   */
  public function query(): SelectInterface
  {
    $query = $this->select('node_field_data', 'n');
    $query->fields('n', [
      'nid',
      'vid',
      'type',
      'title',
      'uid',
      'langcode',
      'created',
      'changed',
      'status'
    ]);
    // $query->fields('nlbl', [
    //   'layout_builder__layout_section',
    // ]);
    // $query->leftJoin('node__layout_builder__layout', 'nlbl', 'nlbl.entity_id=n.nid');
    $query->condition('n.type', 'article', '=');
    return $query;
  }

  /**
   * {@inheritdoc}
   */
  public function fields(): array
  {
    $fields = [
      'nid' => $this->t('Node ID'),
      'vid' => $this->t('Vid'),
      'uid' => $this->t('Uid'),
      'title' => $this->t('Title'),
      'type' => $this->t('Type'),
      'created' => $this->t('Created timestamp'),
      'changed' => $this->t('Modified timestamp'),
      'status' => $this->t('Published'),
      'layout_builder__layout_section' => $this->t('Layout builder section'),
    ];

    return $fields;
  }

  /**
   * {@inheritdoc}
   */
  public function getIds(): array
  {
    return [
      'nid' => [
        'type' => 'integer',
        'alias' => 'n',
      ],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function prepareRow(Row $row): bool
  {
    $nid = $row->getSourceProperty('nid');
    $row->setSourceProperty('nid', $nid);

    $result = $this->getDatabase()->query(
      "SELECT bundle , entity_id, layout_builder__layout_section  FROM  node__layout_builder__layout
      WHERE  entity_id = $nid"
    );
    $layoutbuilder_data = [];
    foreach($result as $record ){
      $layoutbuilder_data[] = $record->layout_builder__layout_section;
    }
    $row->setSourceProperty('layout_builder_value', $layoutbuilder_data);
    return parent::prepareRow($row);
  }
}

```

# Process plugin

```
<?php

namespace Drupal\my_custom_module\Plugin\migrate\process;

use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\Row;
use Drupal\node\Entity\Node;
use Drupal\layout_builder\Section;
use Drupal\layout_builder\SectionComponent;
use Drupal\migrate\MigrateException;
use Drupal\my_custom_module\LayoutBase;
use Drupal\my_custom_module\LayoutMigrationItem;
use Drupal\my_custom_module\LayoutMigrationMissingBlockException;

/**
 * Custom process plugin for updating layout builder section.
 *
 * @MigrateProcessPlugin(
 *   id = "migrate_node"
 * )
 */
class MigrateNode extends LayoutBase{

    /**
     * {@inheritdoc}
     */
    public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property)
    {
        $nid = $row->getSourceProperty('nid');
        if (!isset($this->configuration['source_field'])) {
            throw new MigrateException('Missing source_field for layout builder process plugin.');
        }
        if($nid == '1429'){ // testing for a single node
          $values = $row->getSourceProperty($this->configuration['source_field']);
          if(isset($values) && is_array($values)){
            foreach($values as $key => $value){
              $sectionComponents = unserialize($value);// am geting section components here
              if ($sectionComponents instanceof Section) {
                $components = $sectionComponents->getComponents();
                foreach ($components as $key => $component) {
                  print_r($component->get('configuration'));
                  // $sectionComponents->insertComponent(1, $component);
                  $configurations = $component->get('configuration');
                  $configurations['id'] = "block_content:3208e4a9-24a2-489c-a0c2-122bcc9c5395"; // this is not working :(
                  $component->setConfiguration($configurations);
                }
              }
            }
          }
        }
        return $value;
    }
}

```

Can somebody help me pls?

# Answer
You return $value but you do not modify it, also please be aware that you use $value as a method argument and as a part of foreach.

You must return a value that is compatible with layout\_builder\__layout field

e.g. $section in an instance of \Drupal\layout\_builder\Section that contains updated components configuration.

```
return [
  'section' => $section,
]

```

or multiple sections

```
return $sections;

```

where $sections is an array that contains multiple sections in the following format:

```
$sections[] = [
  'section' => $section,
];

```
> 1 votes

# Answer
Thanks @alex. I think i was not sending the correct value, as well as not returning the sections as you said. I resolved it as follows:

```
   /**
   * {@inheritdoc}
   */
  public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
    // Return early if $value is not an array.
    if (!is_array($value)) {
      return NULL;
    }
    foreach ($value as $serializedData) {
      // Unserialize the data.
      $sections = unserialize($serializedData);
      // Check if unserialized data is an instance of Section.
      if ($sections instanceof Section) {
        // Update section components if necessary.
        $this->updateSectionComponents($sections);
      }
      $final_data[] = $sections;
    }
    return $final_data;
  }
    /**
       * Updates section components.
       *
       * @param \Drupal\layout_builder\Section $section
       *   The section object.
       */
      protected function updateSectionComponents(Section $section): void {
        $components = $section->getComponents();
        foreach ($components as $component) {
          // Check if values are instance of SectionComponent.
          if ($component instanceof SectionComponent) {
            // Update the configuration of each component.
            $this->updateComponentConfiguration($component, $section);
          }
        }
      }

      /**
       * Updates the configuration of a section component.
       *
       * @param \Drupal\layout_builder\SectionComponent $component
       *   The section component object.
       * @param \Drupal\layout_builder\Section $section
       *   The parent section object.
       */
      protected function updateComponentConfiguration(SectionComponent $component, Section $section): void {
        // Get component configuration.
        $configuration = $component->get('configuration');
        // Query to get new block content (id and uuid) based on revision_id.
        $newBlockIdData = $this->getNewBlockId($configuration['block_revision_id']); // helper function to get old block details
        if (!empty($newBlockIdData)) {
          $attached_block_id = $newBlockIdData[0]->id;
          $old_block_type = $newBlockIdData[0]->type;
          // Old block region.
          $region = $component->get('region');
          // Get new block id.
          $oldBlockData = $this->getOldBlockData($attached_block_id); // helper function to get new block (migrated earlier) from old block id
          if (!empty($oldBlockData) && $configuration['id'] == "inline_block:$old_block_type") {
            // Get new block type.
            $new_block_type = $oldBlockData[0]->bundle;
            // Set the new block id configuration.
            $configuration['id'] = "inline_block:$new_block_type";
            // Create new component.
            $newComponent = (new SectionComponent($this->uuid->generate(), $region, $configuration));
            // Append the new component within the section.
            $section->appendComponent($newComponent);
          }
        }
      }

```
> 0 votes

---
Tags: 8, migrations, layout-builder
---


# Question
Title: How do I print the help text description above the field for all textareas?
For all textareas, I want to print the help text description above the field input.

By default, it looks like this:

* Field Label
* Field Input
* Help Description

I want to arrange it like this:

* Field Label
* Help Description
* Field Input

This was actually a hidden configuration option added to Drupal 8.0 So, the basic idea is that I have to set `'#description_display' => 'before'` for every textarea.

To do that, I tried to use template\_preprocess\_textarea().

I added the following code to `mytheme/mytheme.theme`:

```
function MYTHEME_preprocess_textarea(&$variables) {
  $variables['#description_display'] = 'before';
}

```

I checked to make sure this code is called with `kint()` but it doesn't change the positioning of the help descriptions on the forms. I checked my theme (Bootstrap's) templates and they have logic for handling `before` and `after`. So what is the right way to call this to adjust the position of the help text on all textareas?

# Answer
> 5 votes
The `description_display` property is still working if supported by your active theme and I understand it is. Adding this to your `mytheme.theme` should do the trick :

```
function MYTHEME_preprocess_form_element(&$variables) {
  if ($variables['element']['#type'] == 'textarea') {
    $variables['description_display'] = 'before';
  }
}

```

Note that this might not work with formatted fields from the field API as the description field is in a parent template, see `text-format-wrapper.html.twig`. It would be a little more complicated to have this working for those fields.

# Answer
> 1 votes
There is a module for this, which supports Drupal 7,8,9,10+

https://www.drupal.org/project/top\_description

---
Tags: theming, 8
---


# Question
Title: Check If field exists on node and perform action?
How do I check to see if a field is being used on a node and if so, perform an action elsewhere on the page, such as moving a block or adding a body class?

# Answer
*How do I check to see if a field is being used on a node?*

```
$node->hasField($field_name)

```

*and if so, perform an action elsewhere on the page, such as moving a block?*

Configure multiple regions with different sets of blocks and then in a page template

```
{% if node.hasfield('field_image') %}
  {{ page.sidebar_second }}
{% endif %}

```

*or adding a body class?*

See How do I add body class on specific content\_type who have specific field?

In your case use `$node->hasField('field_image')` if you want to check for the presence of a field even if it is empty.
> 1 votes

---
Tags: 10
---


# Question
Title: How can I hide the Text Format selector when there is only one?
When a user has only a single text format option, just Filtered HTML for example, the selector is still shown even though there are no other options.

How can we remove this when there is only one option?

>

# Answer
> 2 votes
The form element type you're probably working with is a 'text\_format' type. This is a composite element/widget. You can modify this form structure using `hook_form_alter()`. It's a bit hidden, but you can change 2 properties on the widget to control that dropdown.

Make sure you are editing the form structure of the widget, not any containers. See the below code example for an idea on which element to update:

```
$form['description']['widget'][0]['#format'] = 'full_html';
$form['description']['widget'][0]['#allowed_formats'] = ['full_html'];

```

You can change 'full\_html' to 'restricted\_html' and see the editor change. Whatever mode you wish to enforce, you can set the machine name there and it'll set that as the default + hide the dropdown.

See `\Drupal\filter\Element\TextFormat.php` for some more info (contained in the annotation at the top of the file):

```
/**
 * Provides a text format render element.
 *
 * Properties:
 * - #base_type: The form element #type to use for the 'value' element.
 *   'textarea' by default.
 * - #format: (optional) The text format ID to preselect. If omitted, the
 *   default format for the current user will be used.
 * - #allowed_formats: (optional) An array of text format IDs that are available
 *   for this element. If omitted, all text formats that the current user has
 *   access to will be allowed.
 *
 * Usage Example:
 * @code
 * $form['body'] = array(
 *   '#type' => 'text_format',
 *   '#title' => 'Body',
 *   '#format' => 'full_html',
 *   '#default_value' => '<p>The quick brown fox jumped over the lazy dog.</p>',
 * );
 * @endcode
 *
 * @see \Drupal\Core\Render\Element\Textarea
 *
 * @RenderElement("text_format")
 */

```

If you're unsure what the machine name for your text format is, you can find the list under Configuration \> Content authoring \> Text formats and editors. The machine name of that format will be found in the URL as a named parameter. The three default ones you get with Drupal are `full_html`, `restricted_html`, and `basic_html`. Other modules may add text formats so you can find the full list there.

---
Tags: input-formats, 10
---


# Question
Title: How does the new config_exclude_modules setting work exactly?
Drupal 8.8 introduced a new setting: https://www.drupal.org/node/3079028

By adding these lines to your settings.local.php I expected the modules and their configuration to be excluded from config export.

```
$settings['config_exclude_modules'] = ['kint', 'devel'];

```

However:

* kint and devel still appear in my core.extensions.yml
* Yaml files related to devel still get generated.
* Modules are still removed and installed based on the core.extensions.yml

How does this feature work?

Edit: I upgraded from Drupal 8.7, that seems to keep this function to work properly.

# Answer
> 4 votes
Enable the module and activate the filter by declaring `$settings['config_exclude_modules']` in your settings.php file, eg:

```
$settings['config_exclude_modules'] = ['devel', 'stage_file_proxy'];

```

Now, when you export configuration (`drush config-export`), the selected modules should no longer show up in core.extension.yml and their configuration should not be exported.

Make sure first before doing that to remove these module from core.extensions.yml manually for the first time.

Update: It seems to be related to a drush issues: drush config:status does not respect excluded modules #4194

# Answer
> 0 votes
Addendum really, as I was looking at the logic in `ExcludedModulesEventSubscriber` that does most of the work for `config_exclude_modules`.

Say module `foo` has some config in `config/install`, and you exclude module `foo` and then install the module. The config that is installed from `config/install` will not necessarily be excluded by `config_exclude_modules` in the future.

For config to be excluded needs to belong to the module by name (`foo.*`) or there should be a dependency on the module in the config.

For example, say if your module `foo` installs a `view` that you would like to also be excluded config, then it will be excluded based on the dependencies of that view.

```
dependencies:
  module:
    - node
    - etc

```

However you can add an enforced dependency to prevent this.

```
dependencies:
  module:
    - node
    - etc
  enforced:
    module:
      - foo

```

---
Tags: 8, configuration
---


title: Database query with like condition

text:
# Question
Title: Database query with like condition
Currently I am using the following query to select the data from database

```
$result = db_query("select data from {config} where name like '%IPPOOL_POOL%' ");
while ($row = $result->fetchObject()) {
  $ips=$row->value;
}

```

But this generated an error like

> Undefined property: stdClass::$value

Please help me to implement same in Drupal 8.

# Answer
> 17 votes
According to Drupal db\_like doc page

> Get a database connection injected into your service from the container and call escapeLike() on it.

For example,

```
$injected_database->escapeLike($string);

```

Try something like:

```
$database = \Drupal::database();
$result = $database->select('table_name', 't')
    ->fields('t', ['field_1', 'field_2'])
    ->condition('field_1', "%" . $database->escapeLike($string) . "%", 'LIKE')
    ->execute()
    ->fetchAll();

```

Read Database api in Drupal 8 for a better understanding of database query in drupal 8.

# Answer
> 0 votes
If you are interested in syntax for query() (not for condition()) then here how to use like:

```
$db = \Drupal::database();
$oldTablesPrefix = $db->escapeLike('old_') . '%';
$oldTables = $db->query('SHOW TABLES LIKE :prefix', [':prefix' => $oldTablesPrefix])
  ->fetchCol();

```

---
Tags: 8, database
---


# Question
Title: How do I get the image URL with the same style setting in Manage Display?
I'm creating a paragraph template. It renders images and other fields to create a layout design. While I can successfully render all fields, the challenge is to render the style applied in the Manage Display. I don't want to give a specific style name, which might change over time.

Here is my code to render the image URL. I would like to know how to render an image URL with the same style in the Manage Display setting.

```
<img src="{{ file_url(paragraph.field_IMAGE_MACHINE_NAME.0.entity.uri.value) }}" />

```

# Answer
**If you only want to get style img url can use paragraph preprocess hook like this:**

```
  use Drupal\Core\Entity\Entity\EntityViewDisplay;
  use Drupal\image\Entity\ImageStyle;

  function Mymodulename_preprocess_paragraph(&$vars) {
    $paragraph = $vars['paragraph'];
    if($paragraph->field_img[0]){
      // Get image style name
      $component = EntityViewDisplay::load('paragraph.sing.default')->getComponent('field_img');
      $img_style = $component['settings']['image_style'];
      $img = $paragraph->field_img[0]->entity->getFileUri();
      $style_img_url = ImageStyle::load($img_style)->buildUrl($img);
      $vars['style_img_url'] = $style_img_url;
    }
  }

```

**Note:** In this paragraph.sing.default: paragraph denotes: paragraph entity, sing denotes: machine name of paragraph, default denotes: view mode

**Then simply need to add **style\_img\_url** in override paragraph.html.twig** like this :

```
<img src="{{ style_img_url }}">

```
> 1 votes

# Answer
You need to get image style in paragraph preprocess hook:

```
use Drupal\Core\Entity\Entity\EntityViewDisplay;

function Mymodulename_preprocess_paragraph(&$vars) {
  $component = EntityViewDisplay::load('paragraph.sing.default')->getComponent('field_img');
  $image_style = $component['settings']['image_style'];
  $vars['img_style'] = $image_style;
}

```

Note: In this paragraph.sing.default: paragraph denotes: paragraph entity, sing denotes: machine name of paragraph, default denotes: view mode

Then need to add following snippet code in override paragraph.html.twig as per drupal standard

```
    {% set image = {
      '#theme':      'image_style',
      '#style_name': img_style,
      '#uri': paragraph.field_img.0.entity.uri.value,
      '#alt': paragraph.field_img.0.alt,
      '#attributes': { class: img_style },
      }
    %}

    {# now print the image style, it display image with respective img style configured from mange display. #}
    <div>{{ image }}</div>

```

Note: Here you can see **img\_style** variable set in paragraph preprocess hook because field format display options are components of view modes, stored in the config entity EntityViewDisplay, that can not be accessed from field in paragraph twig directly.

So to fetch the value of image style configured in manage display at the backend , added paragraph hook.
> 0 votes

---
Tags: theming, paragraphs, 10
---


title: Using the Batch API with AJAX?

text:
# Question
Title: Using the Batch API with AJAX?
I have been trying to figure out how to use the Batch API in an overlay via AJAX, but are not sure that's possible at all? - As far as I can see the batch API, when javascript is enabled, calls the misc/batch.js, which at status=100 performs a window.location to return to the page who requested the batch.

To my knowledge, it's not possible to catch and prevent this event (Without showing the user a popup with the choice of staying on or leaving the page). - So right now I don't see a way to achieve this without running batch as if, javascript was not turned on?

Am I missing some Drupal magic here?

# Answer
It is possible without hacking core. It needs a bit of fiddling, but you can basically just use `Drupal.ProgressBar` for an existing batch process, providing your own `updateCallback` to control its behaviour.

I have successfully done this in every version of Drupal since Drupal 7, but can't share my code here unfortunately.

One example I found for Drupal 8 explains the gist of it, although it's not explicitly covering the modal part of the question: https://www.hashbangcode.com/article/drupal-8-running-batch-through-ajax-request

I'll share a slightly modified (and untested) version of the code example from the linked article:

```
(function ($, Drupal) {
  'use strict';

  Drupal.behaviors.account = {
    attach: function attach(context, settings) {
      var progressBar = void 0;

      function updateCallback(progress, status, pb) {
        $('#updateprogress').html(progress + '%');
        if (progress === '100') {
          pb.stopMonitoring();
          // And do whatever suits your needs, redirecting or updating some DOM
          // elements
        }
      }

      function errorCallback(pb) {
      }

      $.ajax({
        url: Drupal.url('account/loading/ajax'),
        type: 'POST',
        contentType: 'application/json; charset=utf-8',
        dataType: 'json',
        success: function success(value) {
          progressBar = new Drupal.ProgressBar('updateprogress', updateCallback, 'POST', errorCallback);
          progressBar.startMonitoring(BATCH_URL + '&op=do', 10);
        }
      });
    }
  };
})(jQuery, Drupal);

```

`BATCH_URL` is the url that you get after setting up the batch process in PHP. I would also end the process with a `BATCH_URL + '&op=finished'` to do cleanup on the server, evaluate the process or whatever makes sense in the specific context.

You can also extend the `Drupal.ProgressBar.prototype` object to further modify the behaviour of it and adapt it your needs, e.g. by overriding its `setProgress` to do more fine grained updates or provide server side generated messages.
> 0 votes

# Answer
****Steps To Run batch through jquery ajax request:****

**1. Create controller in which batch API code ‘ll be added**

**Snippet code:**

```
public function updateNodeData(Request $request, $nojs = 'nojs') {
    $method = $request->server->get('REQUEST_METHOD');
    $ajaxResponse = new AjaxResponse();
    // Add logic to check whether request to batch controller by
    // post method or not so that batch process not redirect to batch
    // window itself (i.e. by default core functionality)
    if ($nojs == 'ajax' && $method == 'POST') {
      $all_values = $request->request->all();
      $fields = $all_values['fields'];

      $node_storage = $this->entityTypeManager()->getStorage('node');
      $query = $node_storage->getQuery();

      $ids = $query->condition('type', $fields['content_type'])
        ->accessCheck(FALSE)
        ->execute();

      $batch_builder = new BatchBuilder();
      $batch_builder
        ->setTitle($this->t('Updating Data ..'))
        ->setFinishCallback([
          '\Drupal\batch_example_without_redirect\NodeData',
          'exportFinished',
        ])
        ->setInitMessage($this->t('Updating Node.'))
        ->setErrorMessage($this->t('The process has encountered an error.'));

      $ids_chunk = array_chunk($ids, 100);
      foreach ($ids_chunk as $id) {
        $batch_builder->addOperation([
          '\Drupal\batch_example_without_redirect\NodeData',
          'processNodeData',
        ],
        [
          $id,
        ]);
      }

      batch_set($batch_builder->toArray());

      // Get batch.
      $batch =& batch_get();

      $batch['progressive'] = TRUE;

      // Create the batch_process() to initiate batch process.
      $response = batch_process();

      $batch_id = $batch['id'] ?? '0';

      // Return the response to the ajax output.
      return $ajaxResponse->addCommand(new BaseCommand($batch_id, $response->getTargetUrl()));

    }

    return $ajaxResponse;

}

```

**2. Then call the controller/endpoint through jquery ajax request in which batch API code added so that batch process ‘ll be initiate**

**Snippet code:**

```
  // Call batch controller by ajax & post method.
        const url = 'update-node-data/ajax';
        $.ajax({
          url: Drupal.url(url),
          type: 'POST',
          data: {
            'fields': settings.user_submit_val.fields,
          },
          dataType: 'json',
          success: function success(value) {
            batch_id = value[0].command;
            // Display batch progress
            progressBar = new Drupal.ProgressBar('updateprogress', updateCallback, 'POST');
            progressBar.setProgress(0, 'Updating Data');
            progressBar.startMonitoring(value[0].data + '&op=do', 10);
          }
        });

```

**3. After batch completed its execution** :

Add check if progress == 100 that mean if all the items in batch processed, then call the batch finished callback endpoint through the jquery ajax request

**Snippet code:**

```
function updateCallback(progress, status, pb) {
    $('#updateprogress').html(progress + '%');
      if (progress === '100') {
        pb.stopMonitoring();
        // Call the finishCallback()
        $.ajax({
          url: Drupal.url('batch?id=' + batch_id + '&op=finished'),
          type: 'POST',
          contentType: 'application/json; charset=utf-8',
          dataType: 'json',
          success: function success(val) {
            window.location.reload();
          }
        });
      }
  }

```

**Note:** if you ‘ll skip above mentioned step 3 : then code added in batch finished callback ‘ll never execute.

**If you want to pass some dynamic user selected values to batch then you need to add some additional steps along with above mentioned steps:**

1. Create custom form & on submit form, call the ajax callback which ‘ll send user submit data from form to jquery.
2. Then in the attached jquery file get the user submit form values & pass these values to the batch controller by jquery ajax request.
3. Fetch the user submit form values in batch controller & use it as per logic.

**Note: For complete example you can download demo module from here to see working example of it for the reference.**
> 0 votes

---
Tags: ajax
---


# Question
Title: How do I get the node URL in a Twig template?
I am trying to get the node URL in a Twig template. `{{ content.node_link }}` returns the full link, but I only need the URL.

How do I retrieve the node URL?

# Answer
According to Functions - In Twig Templates you can use is simple by

```
 {{ path('entity.node.canonical', {'node': node.id}) }}

```
> 52 votes

# Answer
In a node.html.twig you can directly use `{{ url }}`

```
<a href="{{ url }}">read more</a>

```

For more information read comment in core template : `/core/modules/node/templates/node.html.twig` :

```
{#
/**
 * @file
 * Default theme implementation to display a node.
 *
 * Available variables:
 * - node: The node entity with limited access to object properties and methods.
 * ...
 * - url: Direct URL of the current node.
 * ...
#}
...

<article{{ attributes }}>

  {{ title_prefix }}
  {% if not page %}
    <h2{{ title_attributes }}>
      <a href="{{ url }}" rel="bookmark">{{ label }}</a>
    </h2>
  {% endif %}
  {{ title_suffix }}

```
> 21 votes

# Answer
I have found this solution that works, but is quite convoluted.

```
{{ path('entity.node.canonical', {'node': content['#node'].id()  }) }}

```
> 11 votes

# Answer
This worked for me:

```
<a href="{{ url('<current>') }}">{{ 'Reload'|t }}</a>

```
> 0 votes

---
Tags: theming, nodes, uri
---


# Question
Title: The uploaded file likely exceeded the maximum file size (32 MB) that this server supports on remove image
In my taxonomy terms,when I try to edit a term I got this error :

> An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size (32 MB) that this server supports.

even though I didn't try to upload a file,I tried to remove the uploaded file. and I don't have mod\_security module enable on my server .( as I search through google,there was problem with mod\_security apache module and ajax).

I have no idea why this is happening only on particular vocab terms,not in anywhere else.

# Answer
> 0 votes
For sure this is due to some problem with your sever setting, contact your webhosting company and talk with them regarding the issue.

It could be due to ModSecurity which is an is an open sourceweb application firewall Apache module. which help in providing an external security layer that increases security, and help in preventing attacks.

The other reason could be the size your allow to upload that can be change in your server setting. It is also important to check apache max\_execution\_time.

If you have control over your server then you can try to change your php file

post\_max\_size = 128 MB upload\_max\_filesize = 32MB

# Answer
> 0 votes
post\_max\_size = 128 MB upload\_max\_filesize = 32MB won't work, should be M not MB in the ini post\_max\_size = 128 M upload\_max\_filesize = 32M

# Answer
> 0 votes
If you're having this error and you are overriding the default webform template, you maybe forgot to add these form elements:

```
{{ form.form_build_id }}
{{ form.form_token }}
{{ form.form_id }}

```

---
Tags: ajax, files, media
---


# Question
Title: How to load commerce order based on purchased entity?
I know how to load an order based on its properties by following code

```
$orders = \Drupal::entityTypeManager()->getStorage('commerce_order')->loadByProperties(['type' => 'default'])

```

Since order items is a referenced entity can I load only orders based on purchased entity? Say I have Product Id ($product\_id) I need to load orders based on product id. How can I achieve this?

# Answer
**Get commerce order based on Purchased entity/Product id:**

```
$order_by_product = [];
$orders = \Drupal::entityTypeManager()->getStorage('commerce_order')->loadMultiple();
  foreach ($orders as $order) {
    foreach ($order->getItems() as $order_item) {
      $product_variation = $order_item->getPurchasedEntity();
      // Added condition if product id is 5
      if(isset($product_variation) && $product_variation->getProductId() == 5){
        $order_by_product[$order->id()] = $order;
      }
    }
  }

```

**Background Info:** There is not direct relation between the order & product if explore the commerce module that indicates there is not any property in order entity to load order by purchased entity/ product id using loadByProperties(), **however by above code can get the order by product id**
> 0 votes

---
Tags: commerce, 10
---


title: Drush Launcher does not work

text:
# Question
Title: Drush Launcher does not work
Drush Launcher gives an error in an environment updated to Drupal 10.2. The environment is Drupal 10.2, PHP 8.2, Drush 12.4, Drush Launcher 0.10.2. Specifically, I get the following error

```
[user@host drupal]$ drush status
The Drush launcher could not find a local Drush in your Drupal site.
Please add Drush with Composer to your project.
Run 'cd "/var/www/drupal" && composer require drush/drush'

```

However, Drush is installed by composer and is also listed in composer.json. I have updated from Drupal 9, but I can no longer run the Drush command with only "drush". I would like to be able to run it with "drush" instead of "vendor/bin/drush". ./bashrc or any other method other than passing it through $PATH, please. The following from Drupal 9 is ideal.

```
[user@host drupal]$ drush status
...
Drush script     : /usr/local/bin/drush
...

```

Please help.

# Answer
drush-launcher does not work because it is unmaintained. See its archived project page for alternatives.
> 0 votes

---
Tags: drush, composer, 10
---


# Question
Title: How can I avoid fields being disabled on AJAX requests (attribute disabled) in Drupal?
I want to make an input not be disabled when I submit an ajax request. I tried reversed variants in these answers, i.e. hanging an "AjaxStart" on the document.

```
$(document).ajaxStart(function() {
  $('#edit-search-request').removeAttr('disabled');
});

```

And tried to reassign `beforeSubmit` method:

```
drupalSettings.ajax['edit-search-request'].beforeSubmit = beforeSubmit;

function beforeSubmit (form_values, form, options) {
  #('#edit-search-request').attr('disabled', false).removeAttr('disabled');
}

```

They both fires up but the input still gets disabled during an AJAX request.

Why am I doing this: I got an input with auto-suggestions but on mobiles when the ajax request been sent - the input gets disabled and the keyboard on the phone hides as well. Then it pop-ups again. This is not good for UX. Maybe there is a better solution to resolve this issue.

# Answer
> 0 votes
I ended up with this solution

```
        // Disable [disabled] attribute on the input.
        const defaultBeforeSend = Drupal.Ajax.prototype.beforeSend;
        Drupal.Ajax.prototype.beforeSend = amendBeforeSubmit;

        function amendBeforeSubmit (xmlhttprequest, options) {

          defaultBeforeSend(xmlhttprequest, options);
          $('#edit-search-request').prop('disabled', false);

        }

```

# Answer
> 0 votes
Example of @mevsme broke our JS. I now use this example based on facets-views-ajax.js.

```
  var beforeSend = Drupal.Ajax.prototype.beforeSend;

  Drupal.Ajax.prototype.beforeSend = function (xmlhttprequest, options)
  {
    // Call the original Drupal method with the right context.
    beforeSend.apply(this, arguments);
    $('#edit-search-request').prop('disabled', false);
  }

```

---
Tags: forms, ajax, 9
---


# Question
Title: A site is upgraded but not to the latest version because Drush isn't upgraded to the lastest version - why does it happen and how to upgrade Drush?
A site was upgraded from 10.1.7 to 10.1.8 instead to 10.2.2.

There were no errors along the upgrade process but from `composer why-not drupal/core 10.2.2` it is indicated that my Drush version is too old.

```
drupal/core-recommended    10.1.8 requires         drupal/core (10.1.8)
drupal/core                10.2.2 conflicts        drush/drush (<12.4.3)
drupal/core                10.2.2 requires         symfony/console (^6.4)
drupal/recommended-project -      does not require symfony/console (but v6.3.12 is installed)
drupal/core                10.2.2 requires         symfony/dependency-injection (^6.4)
drupal/recommended-project -      does not require symfony/dependency-injection (but v6.3.12 is installed)
drupal/core                10.2.2 requires         symfony/event-dispatcher (^6.4)
drupal/recommended-project -      does not require symfony/event-dispatcher (but v6.3.12 is installed)
drupal/core                10.2.2 requires         symfony/http-foundation (^6.4)
drupal/recommended-project -      does not require symfony/http-foundation (but v6.3.12 is installed)
drupal/core                10.2.2 requires         symfony/http-kernel (^6.4)
drupal/recommended-project -      does not require symfony/http-kernel (but v6.3.12 is installed)
drupal/core                10.2.2 requires         symfony/mime (^6.4)
drupal/recommended-project -      does not require symfony/mime (but v6.3.12 is installed)
drupal/core                10.2.2 requires         symfony/routing (^6.4)
drupal/recommended-project -      does not require symfony/routing (but v6.3.12 is installed)
drupal/core                10.2.2 requires         symfony/serializer (^6.4)
drupal/recommended-project -      does not require symfony/serializer (but v6.3.12 is installed)
drupal/core                10.2.2 requires         symfony/validator (^6.4)
drupal/recommended-project -      does not require symfony/validator (but v6.3.12 is installed)
drupal/core                10.2.2 requires         symfony/process (^6.4)
drupal/recommended-project -      does not require symfony/process (but v6.3.12 is installed)
drupal/core                10.2.2 requires         symfony/yaml (^6.4)
drupal/recommended-project -      does not require symfony/yaml (but v6.3.12 is installed)
Not finding what you were looking for? Try calling `composer update "drupal/core:10.2.2" --dry-run` to get another view on the problem.

```

I was inclined in the comments to remove Drush, upgrade Drupal project, re-add Drush, and only then to try to update Drupal DB with Drush, but a shorter approach then removing and re-adding Drush would be to just update Drush.
I have always assumed that `composer update` on a Drupal project will also upgrade Drush due to the global nature of this command and because Drush is an add-on to the a Drupal project, but Drush isn't upgraded this way, so I ask here how to upgrade Drush?

# Answer
According to the error message

```
drupal/core                10.2.2 conflicts        drush/drush (<12.4.3)

```

Drupal core 10.2.2 has defined a conflict with Drush \<12.4.3. If you are still using Drush 11 update it first:

```
composer require 'drush/drush:^12.4' --no-update
composer update

```

The option `--no-update` in the first line only updates the composer.json file - the same as if you would have edited composer.json in a text editor - before the general composer update runs all updates in one step.

The reason Drush is not updated automatically is semantic versioning. A MAJOR version update can have API changes and therefore manual intervention is required. See https://semver.org/

If the update is still not working check the PHP version in the command line (php -v) and update composer itself:

```
composer self-update

```
> 0 votes

---
Tags: 10, upgrading
---


# Question
Title: Custom entity access control handler not being called
I'm trying to define an access control handler for a custom entity, and as far as I can tell it's never being called.

Here's my entity header:

```
  /**
   * Defines the Target entity.
   *
   * @ContentEntityType(
   *   id = "target",
   *   label = @Translation("Target"),
   *   label_collection = @Translation("Targets"),
   *   label_singular = @Translation("Target"),
   *   label_plural = @Translation("Targets"),
   *   label_count = @PluralTranslation(
   *     singular = "@count Target",
   *     plural = "@count Targets",
   *   ),
   *   handlers = {
~_ *     "access" = "Drupal\nm_base\TargetAccessControlHandler",
   *   },
   *   base_table = "nm_target",
   *   admin_permission = "access submissions",
   *   entity_keys = {
   *     "id" = "uuid",
   *   }
   * )
   */
  class Target extends ContentEntityBase {

```

And here's my access controller:

```
<?php

namespace Drupal\nm_base;

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityAccessControlHandler;
use Drupal\Core\Entity\EntityHandlerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Defines an access controller for the target entity.
 *
 * @see \Drupal\nm_base\Entity\Target.
 */
class TargetAccessControlHandler extends EntityAccessControlHandler implements EntityHandlerInterface {

  /**
   * {@inheritdoc}
   */
  protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
    devel_debug('check_access');
    return AccessResult::allowedIf(false);
  }
}

```

Executing an entity query as a non-admin user never triggers that devel\_debug. What am I missing?

# Answer
> 0 votes
I think that the entity query access is handled by some other access handler. The best way for you to check and investigate further is to check which query access handler is the entity using. Like this:

```
$entityType = \Drupal::entityTypeManager()->getDefinition('entity_type_id');
dsm($entityType->getHandlerClasses());

```

---
Tags: entities, 10
---


# Question
Title: What is the syntax for "if the image style does not exist and if no image style is selected"?
I'm encountering an issue when calling a preprocess function to load a styled image URL. This issue is based on the question here.

The following Preprocess Paragraph function sets the style based on the Manage Display. An error occurs if the Manage Display setting is "None (original image)".

How do we prevent errors during the preprocessing of the function when attempting to load an **image style that doesn't exist** or **when Manage Display is set to None (original image)**?

```
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\image\Entity\ImageStyle;

function Mymodulename_preprocess_paragraph(&$vars) {
  $paragraph = $vars['paragraph'];
  if($paragraph->field_img[0]){
    // Get image style name
    $component = EntityViewDisplay::load('paragraph.sing.default')->getComponent('field_img');
    $img_style = $component['settings']['image_style'];
    $img = $paragraph->field_img[0]->entity->getFileUri();
    $style_img_url = ImageStyle::load($img_style)->buildUrl($img);
    $vars['style_img_url'] = $style_img_url;
  }
}

```

Render

```
<img src="{{ style_img_url }}">

```

# Answer
This is intended to use the original file URL if no image style is selected.

```
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\image\Entity\ImageStyle;

function Mymodulename_preprocess_paragraph(&$vars) {
  $paragraph = $vars['paragraph'];
  if($paragraph->field_img[0]){
    // Get image style name
    $component = EntityViewDisplay::load('paragraph.sing.default')->getComponent('field_img');
    $img_style = $component['settings']['image_style'];
    if (!empty($img_style)) {
      $img = $paragraph->field_img[0]->entity->getFileUri();
      $style_img_url = ImageStyle::load($img_style)->buildUrl($img);
      $vars['style_img_url'] = $style_img_url;
    }
    else {
      $vars['style_img_url'] = $paragraph->field_img[0]->entity->getFileUrl();
    }
  }
}

```
> 1 votes

# Answer
Source code to get the original file URL on site of img field if no image style is selected along with added checks as well.

```
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\image\Entity\ImageStyle;

function Mymodulename_preprocess_paragraph(&$vars) {
  $paragraph = $vars['paragraph'];
  // Added check paragraph type Here sing is machine name of paragraph
  // & field_img exist or not
  if($paragraph->getType() == 'sing' && $paragraph->field_img[0]){
    // Get image style name
    $component = EntityViewDisplay::load('paragraph.sing.default')->getComponent('field_img');
    $img_style = $component['settings']['image_style'];
    if($img_style){
      $img = $paragraph->field_img[0]->entity->getFileUri();
      $style_img_url = ImageStyle::load($img_style)->buildUrl($img);
      $vars['style_img_url'] = $style_img_url;
    }else{
      // Get original img path
      $vars['style_img_url'] = $paragraph->field_img[0]->entity->createFileUrl();
      //$vars['style_img_url'] = file_create_url($img);
    }
  }
}

```

**Note:** Here use `file_create_url()` to get the original file URL i.e. is accessible by the user

if file\_create\_url() not work then can use createFileUrl()
> 0 votes

---
Tags: paragraphs, 10
---


# Question
Title: How do I redirect to a different form in a _custom_access routing call
In my D10 application, it is required that I have a parallel user login system separate from the Drupal system (it is a requirement I cannot change). On my routing, I have the following:

```
msul_tgif_star_xml_client.login:
  path: '/tgif/login'
  defaults:
    _title: 'TGIF Login'
    _form: 'Drupal\msul_tgif_star_xml_client\Form\LoginForm'
  requirements:
    _permission: 'access content'

msul_tgif_star_xml_client.somepage:
  path: '/somepath'
  defaults:
    _form: '\Drupal\msul_tgif_star_xml_client\Form\SomeRandomForm'
  requirements:
    _custom_access: '\Drupal\msul_tgif_star_xml_client\securityCheck::access'

msul_tgif_star_xml_client.someotherpage:
  path: '/somepath/other'
  defaults:
    _form: '\Drupal\msul_tgif_star_xml_client\Form\SomeotherRandomForm'
  requirements:
    _custom_access: '\Drupal\msul_tgif_star_xml_client\securityCheck::access'

```

For the access function I have:

```
final class securityCheck {
  public function access(AccountInterface $account, Request $request)
  {
    // Do some security checking based on factors such as IP address, tokens, etc.
    if($checkOK){
      return AccessResult::allowedIf($checkOK);
    }
    else{
      return new RedirectResponse(
                      \Drupal\Core\Url::fromRoute(
                        'msul_tgif_star_xml_client.login')
                      ->toString()
                 );
    }
  }
}

```

This does not work, of course, because the \_access\_callback demands a return type of AccessResult. How do I make the else clause redirect to the login form from the access callback?

# Answer
> 1 votes
You have to return an access result, for example:

```
return AccessResult::forbidden('Redirect me!');

```

This results in an access denied exception, which you can catch in an exception subscriber:

*src/EventSubscriber/MymoduleExceptionSubscriber.php*

```
<?php

namespace Drupal\mymodule\EventSubscriber;

use Drupal\Core\EventSubscriber\HttpExceptionSubscriberBase;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

class MymoduleExceptionSubscriber extends HttpExceptionSubscriberBase {

  protected function getHandledFormats() {
    return ['html'];
  }

  public function on403(ExceptionEvent $event) {
    $exception = $event->getThrowable();
    if ($exception instanceof AccessDeniedHttpException) {
      if ($exception->getMessage() == 'Redirect me!') {
        $response = new RedirectResponse('/different/login');
        $event->setResponse($response);
      }
    }
  }

}

```

*mymodule.services.yml*

```
services:
  mymodule.403_subscriber:
    class: Drupal\mymodule\EventSubscriber\MymoduleExceptionSubscriber
    tags:
      - { name: event_subscriber }

```

---
Tags: routes, redirection
---


title: All users can't login

text:
# Question
Title: All users can't login
After migrating from Drupal 9 to Drupal 10 all users can't login. I tried:

* Removing settings.php and adding a new version
* Emptying the sessions table in the database
* Clearing cache, updating database
* Clearing cache in browser
* Using different browser
* Saving the users account

Nothing works, the only this that does work is resetting the password. But its extra work for the user to do this so I'm still looking for a global fix so I can fix it for everyone at once.

Anyone with this same problem? Or a suggestion how to fix this?

# Answer
> 3 votes
From https://www.drupal.org/node/3322420

> Password hashing is changed
>
> When a site updates to Drupal 10.1.0 or later, a database-update function installs the Password Compatibility module. This allows existing users to use their credentials. If the module is not installed, existing users can login by requesting a new password.
>
> The first time an existing user logs in, a new hash is and saved to the database. From then on, the user can log in with the same credentials whether this module is installed or not.

---
Tags: authentication-authorization, 10
---


# Question
Title: Datetime validation preventing submission
I have a form which defines a datetime field as follows:

> ```
>    $form['register'][$i]['end_date_time'] = [
>       '#type' => 'datetime',
>       '#default_value' => new DrupalDateTime($data->field_event_time_end_value, 'Europe/London'),
>       '#date_date_element' => 'text',
>       '#date_date_format' => 'd/m/Y',
>       '#date_time_element' => 'text',
>       '#date_time_format' => 'H:i:s',
>       '#date_timezone' => 'Europe/London',
>       '#weight' => '0',
>     ];
>
> ```
>
> However whenever I try to submit the form every date field throws a validation error stating "The date is invalid. Please enter a date in the format 26/07/2019 16:47:43". I don't have any logic in my `validateForm(array &$form, FormStateInterface $form_state)` function yet so I am not sure why it is throwing an error.

I have tried different combinations of formatting the date and even stripped it back to the basic settings but I can't get a combination to work. I am currently on Drupal 8.7.5.

---

Update to include my full form code:

```
<?php

namespace Drupal\events_register\Form;

use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Database\Driver\mysql\Connection;
use Drupal\Core\Messenger\MessengerInterface;

/**
 * Class EventRegisterForm.
 */
class EventRegisterForm extends FormBase {

  /**
   * Drupal\Core\Database\Driver\mysql\Connection definition.
   *
   * @var \Drupal\Core\Database\Driver\mysql\Connection
   */
  protected $database;

  /**
   * Drupal\Core\Messenger\MessengerInterface definition.
   *
   * @var \Drupal\Core\Messenger\MessengerInterface
   */
  protected $messenger;

  /**
   * The webform storage.
   *
   * @var \Drupal\webform\WebformEntityStorageInterface
   */
  protected $webformStorage;

  /**
   * The webform submission storage.
   *
   * @var \Drupal\webform\WebformSubmissionStorageInterface
   */
  protected $webformSubmissionStorage;

  /**
   * Webform request handler.
   *
   * @var \Drupal\webform\WebformRequestInterface
   */
  protected $requestHandler;

  /**
   * Constructs a new EventRegisterForm object.
   *
   * @param \Drupal\Core\Database\Driver\mysql\Connection $database
   * @param \Drupal\Core\Messenger\MessengerInterface $messenger
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  public function __construct(Connection $database, MessengerInterface $messenger,  EntityTypeManagerInterface $entity_type_manager) {
    $this->database = $database;
    $this->messenger = $messenger;
    $this->webformStorage = $entity_type_manager->getStorage('webform');
    $this->webformSubmissionStorage = $entity_type_manager->getStorage('webform_submission');
    $this->requestHandler = $request_handler;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('database'),
      $container->get('messenger'),
      $container->get('entity_type.manager')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'event_register';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    //kint($this->webformSubmissionStorage->load(1));
    $form['register'] = [
      '#type' => 'table',
      '#caption' => t('Sample Table'),
      '#header' => [
        'nid' =>    t('Node ID'),
        'n_title' =>    t('Title'),
        'n_start_date' =>    t('Start Date'),
        'n_end_date' =>    t('End Date'),
        'sid' => t('Submission ID'),
        'student_id' => t('Student ID'),
        'registration_status' => t('Registration Status'),
      ],
      '#empty' => t('Nothing found'),
    ];
    // Get records from the DB.
    $query = $this->database->select('webform_submission', 'ws');
    $query->fields('ws', ['sid','webform_id']);
    $query->join('node_field_data', 'nfd', 'ws.entity_id = nfd.nid');
    $query->fields('nfd', ['nid', 'title', 'uid']);
    $query->join('node__field_event_time', 'nfet', 'nfd.nid = nfet.entity_id');
    $query->fields('nfet', ['field_event_time_value', 'field_event_time_end_value']);
    $query->leftJoin('node__field_event_tutor', 'nfetu', 'nfd.nid = nfetu.entity_id');
    $query->fields('nfetu', ['field_event_tutor_target_id']);
    $pager = $query->extend('Drupal\Core\Database\Query\PagerSelectExtender')->limit(10);
    $results = $pager->execute()->fetchAll();
    if ($results) {
      $i = 0;
      foreach ($results as $data) {
        $form['register'][$i]['node_id'] = [
          '#type' => 'item',
          '#markup' => $data->nid,
          '#weight' => '0',
        ];
        $form['register'][$i]['title'] = [
          '#type' => 'item',
          '#markup' => $data->title,
          '#weight' => '0',
        ];

        $form['register'][$i]['start_date_time'] = [
          '#type' => 'datetime',
          '#default_value' => new DrupalDateTime($data->field_event_time_value, 'Europe/London'),
          '#date_date_element' => 'date',
          '#date_date_format' => 'd/m/Y',
          '#date_time_element' => 'time',
          '#date_time_format' => 'H:i:s',
          '#date_timezone' => 'Europe/London',
          '#weight' => '0',
        ];
        $form['register'][$i]['end_date_time'] = [
          '#type' => 'datetime',
          '#default_value' => new DrupalDateTime($data->field_event_time_end_value, 'Europe/London'),
          '#date_date_element' => 'date',
          '#date_date_format' => 'd/m/Y',
          '#date_time_element' => 'time',
          '#date_time_format' => 'H:i:s',
          '#date_timezone' => 'Europe/London',
          '#weight' => '0',
        ];
        $form['register'][$i]['submission_id'] = [
          '#type' => 'item',
          '#markup' => $data->sid,
          '#weight' => '0',
        ];
        $submissionData = $this->webformSubmissionStorage->load($data->sid);
        $form['register'][$i]['student_id'] = [
          '#type' => 'item',
          '#markup' => $submissionData->getData()["student_id"],
          '#weight' => '0',
        ];
        $form['register'][$i]['student_id'] = [
          '#type' => 'item',
          '#markup' => $submissionData->getData()["student_id"],
          '#weight' => '0',
        ];
        $status_options = ['attending' => 'attending', 'attended' => 'attended', 'cancelled' => 'cancelled', 'system_cancellation' => 'system cancellation'];
        $form['register'][$i]['status'] = [
          '#type' => 'select',
          '#options' => $status_options,
          '#default_value' => $submissionData->getData()["registration_status"],
          '#weight' => 0,
        ];
        $i++;
      }
    }
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Submit'),
    ];
    $form['pager'] = array(
      '#type' => 'pager',
    );
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
    foreach ($form_state->getValues() as $key => $value) {
      \Drupal::messenger()->addMessage($key . ': ' . ($key === 'text_format'?$value['value']:$value));
    }
    parent::validateForm($form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    // Display result.
    foreach ($form_state->getValues() as $key => $value) {
      \Drupal::messenger()->addMessage($key . ': ' . ($key === 'text_format'?$value['value']:$value));
    }
  }
}

```

# Answer
> 0 votes
Try changing. `#date_date_element => 'date'`  type to date instead of text. And `#date_time_element => 'time'`  type to time instead of text.

---

# UPDATE

This code works just fine for me on Drupal 8.7.5:

```
for($i = 0; $i < 5; $i++){
  $form['test'][$i]['end_date_time'] = [
    '#type' => 'datetime',
    '#default_value' => DrupalDateTime::createFromTimestamp(time()),
    '#date_date_element' => 'date',
    '#date_date_format' => 'd/m/Y',
    '#date_time_element' => 'time',
    '#date_time_format' => 'H:i:s',
    '#date_timezone' => 'Europe/London',
    '#weight' => '0',
  ];
}

```

You might want to check the value of the `$data->field_event_time_end_value` field.
Also, check the DrupalDateTime.php docs for how to create a proper instance.

# Answer
> 0 votes
It's because the `datetime` elements are inside a `table` element.

There's a dead issue here: #3016258: Datetime and Datelist form elements are never validate when put in a table element (linking to a more general issue, that I suspect will eventually fix this: #3247373: Form API Table element is being treated as an input element, this can break fields that have a handler defined which are now no longer altering the data in the $form\_state array.).

There is 2 work arounds in issue #3247373, settings the `#input` to `FALSE` for your `table` should work in your use case:

```
$form['register'] = [
  '#type' => 'table',
  '#caption' => t('Sample Table'),
  '#header' => [
    'nid' =>    t('Node ID'),
    'n_title' =>    t('Title'),
    'n_start_date' =>    t('Start Date'),
    'n_end_date' =>    t('End Date'),
    'sid' => t('Submission ID'),
    'student_id' => t('Student ID'),
    'registration_status' => t('Registration Status'),
  ],
  '#empty' => t('Nothing found'),
  '#input' => FALSE
];

```

Do note this a workaround, and it might have other consequences, I've used the workaround before without any complications.

---
Tags: 8, forms
---


# Question
Title: How do I disable the Update manager module?
Is it possible to disable The *Update manager* module without uninstalling it?

# Answer
You cannot disable a module as you do with Drupal 7 or previous versions, and this is valid also for the *Update manager* module. You could eventually avoid the module checks for new module versions, but the module itself doesn't have a setting for that in admin/reports/updates/settings.

>

What you could do is, using a custom module, avoid that Drupal invokes `update_cron()` which is the hook that periodically checks for updates. For this, it is sufficient to implement `hook_implements_alter()`, for example using the following code. (It assumes the module machine name is *mymodule*; if the module machine name is different, change the function name.)

```
function mymodule_implements_alter(&$implementations, $hook) {
  if ($hook == 'cron') {
    unset($implementations['update']);
  }
}

```

You could still manually check for updates from admin/reports/updates, but Drupal would not check for updates during cron tasks.

If then you also need to disable the route set by the *Update manager* module, you could also do it as shown in How do I alter the route defined by another module?
This is, I think, the closer you could get to disable the *Update manager* module.
> 2 votes

# Answer
No, modules can only be uninstalled in Drupal 8.

There's no such concept as disabling a module any more, just uninstalling.
> 1 votes

# Answer
As already @Clive stated,

> Drupal 8 has removed the ability to disable modules for many reasons.

However, there is a way, but it is not advisable. Disable modules module has been created for that specific purpose only.

But again as please read what stated on that module page as below,

> This module brings back the ability to (temporarily) disable modules from the UI or with Drush. Note, that there's **no** guarantee for your content, config or even your site after you disable a module.

So, think before using.
> 0 votes

# Answer
Here's how GovCMS disables the cron updates check in Drupal 9 and 10.

```
/**
 * Implements hook_update_projects_alter().
 * Disables updates from all modules
 */
function govcms_security_update_projects_alter(&$projects) {
  \Drupal::keyValueExpirable('update_available_releases')->deleteAll();
  \Drupal::keyValueExpirable('update_available_releases')->setMultiple([]);
  $projects = [];
}

/**
 * Implements hook_cron().
 *
 * Make sure that when cron runs, available data is not fetched.
 */
function govcms_security_cron() {
  \Drupal::keyValueExpirable('update_available_releases')->deleteAll();
  \Drupal::keyValueExpirable('update_available_releases')->setMultiple([]);
}

```

The system module also has this:

```
  if (!\Drupal::config('system.advisories')->get('enabled')) {
    return;
  }

```

So you can set this in your settings.php file to disable checking for security advisories.

```
$config['system.advisories']['enabled'] = FALSE;

```
> 0 votes

---
Tags: 8
---


# Question
Title: How To Show Version Of Migrate Tool Installed
Is there an Ubuntu (Linux) command line I can use to see current version of the Migrate Tool installed? I don't have access to the front end (admin/modules), just the back-end. I thought maybe some of the drush commands would work but I've not run into any luck.

# Answer
> 1 votes
* The Drush pm:list command shows the versions of all modules on a site.
* The Composer show command shows the versions of all code on a site.

---
Tags: drush
---


# Question
Title: How to inject extra data into entity reference checkbox form widget
I've a taxonomy tree used in an entity reference field, which requires some custom rules (e.g. *if any option of subtree A is chcked, then any option of subtree B is disallowed* or *if any option of subtree C is checked, parents must be checked too*).

The field widget for this tree is set to multi-value option buttons (checkboxes). I need to create some JS behaviors that assist editors with those rules during input. I need extra info from the taxonomy term entity so my JS behaviors can act on it.

Currently the form element if rendered like this:

```
<input data-drupal-selector="edit-FIELD-MY-TAXONOMY-ID" type="checkbox" id="edit-FIELD-MY-TAXONOMY-ID" name="FIELD-MY-TAXONOMY[ID]" value="ID" class="form-checkbox form-boolean form-boolean--type-checkbox">

```

I need extra informatio like this

```
<input data-term-parent-id="XXX" data-term-field-foo-value="YYY" ...>

```

I know how to write PHP entity queries and the JS, but I don't know which hook to use to inject my those attributes. I've tried `hook_field_widget_single_element_form_alter` , `hook_field_widget_complete_form_alter` and `template_preprocess_form_element` \- and not a single one of these contains information about the taxonomy term entity or the vocabulary, they all just "see" the IDs and labels as strings (and the label strings are not even the term titles, they have `-` in front if them due hierarchy).

The taxonomy is used in multiple nodes with multiple different field names, so guessing on field name is not a good idea. Which hook allows me to alter the checkbox form element AND contains a **reliable** link to the reference entity object?

# Answer
> I've tried hook\_field\_widget\_single\_element\_form\_alter , hook\_field\_widget\_complete\_form\_alter ... and not a single one of these contains information about the taxonomy term entity or the vocabulary ... contains a reliable link to the reference entity object?

The main object for fields is the field item list, which is provided in $context. From there you can go up to get the parent entity:

```
$entity = $context['items']->getEntity();

```

or downstream to get the referenced entities. See https://drupal.stackexchange.com/a/309863/47547

Which can be taxonomy terms depending on whether you have placed the field in a vocabulary or your field is storing references to taxonomy terms. In either case the bundle name is the vocabulary.
> 1 votes

---
Tags: forms
---


# Question
Title: How can I rearrange a menu link programmatically?
I created *Menu Link Content* entities and saved them in a menu.

```
$menu_link = MenuLinkContent::create([
  'title' => 'Menu item',
  'bundle' => 'menu_link_content',
  'link' => [['uri' => 'internal:/']],
  'menu_name' => 'testing',
])->save();

$menu_link_2 = MenuLinkContent::create([
  'title' => 'Menu item',
  'bundle' => 'menu_link_content',
  'link' => [['uri' => 'internal:/']],
  'menu_name' => 'testing',
])->save();

```

How can I rearrange `$menu_link_2` to be a child of `$menu_link`?

Looking at `menu_ui`, they seem to update *Menu Link Content* in a given order, and then rebuild the menu tree; finally, the *Menu* entity is saved.

```
$menu_link_manager = \Drupal::service('plugin.manager.menu.link');
$menu_link_manager->updateDefinition($menu_link_2->getPluginId(), [
  'parent' => $menu_link->getPluginId(),
]);

```

How can I rebuild the menu tree and save the menu?

*Why?* I'm writing a Kernel test for a module that fires when the menu link content has been saved AND then when the Menu itself has been saved.

# Answer
> 0 votes
You can rearrange a custom menu link when you set the 'weight' of the menu. See below code.

```

$menu_link = MenuLinkContent::create([
  'title' => 'Menu item',
  'bundle' => 'menu_link_content',
  'link' => [['uri' => 'internal:/']],
  'menu_name' => 'testing',
  'weight' => 0,
])->save();

$menu_link_2 = MenuLinkContent::create([
  'title' => 'Menu item',
  'bundle' => 'menu_link_content',
  'link' => [['uri' => 'internal:/']],
  'menu_name' => 'testing',
  'weight' => 0,
])->save();

```

# Answer
> 0 votes
Some nice helpers for rearranging existing menus programmatically. https://www.drupal.org/project/codit\_menu\_tools

---
Tags: 8, testing
---


# Question
Title: Drupal root not found on multisite setup after upgrading Drush 10 to 11/12
This is a (25 sites) multisite setup running smoothly on Drupal 9.4 with Drush 10.x still. Upgrading to Drupal 10.1.x and Drush 11 or 12 however makes Drush unusable.

Dir layout as installed by drupal/core-recommended:

```
/var/www/projectroot/
├── composer.json
├── composer.lock
├── config
│   └── mysite.com
├── vendor
│   ├── asm89
│   │   └── stack-cors
│   ├── behat
│   │   ├── mink
│   │   ├── mink-browserkit-driver
│   │   └── mink-goutte-driver
│   ├── bin
│       └── drush
│   ├── bjeavons
│   │   └── drupal-finder
│   ├── drush
│   │   └── drush
│   │       ├── appveyor.yml
:   :       :
│   │       ├── drush
│   │       └── drush.bat
:   :
│   └── wikimedia
│       └── composer-merge-plugin
└── web
    ├── libraries
    ├── modules
    │   ├── contrib
    │   └── custom
    ├── profiles
    ├── robots.txt
    ├── sites
    │   ├── default
    │   └── mysite.com
    │       ├── files
    │       ├── services.yml
    │       └── settings.php
    ├── sites.php
    ├── themes
    │   └── mytheme
    ├── update.php
    └── web.config

```

`:` denotes items I think that are not relevant are left out. The vendor directory is actually much bigger:

```
$ find vendor | wc -l
41837

```

composer.json:

```
{
    "name": "myproject",
    "description": "myproject",
    "type": "project",
    "require": {
        "cweagans/composer-patches": "^1.7",
        "drupal/admin_feedback": "^2.2",
        "drupal/admin_toolbar": "*",
        "drupal/allowed_formats": "^1.3",
        "drupal/animated_gif": "^2.0",
        "drupal/asset_injector": "^2.7",
        "drupal/better_passwords": "2.x",
        "drupal/block_content_machine_name": "dev-3286263-automated-drupal-10 as 3.0.2",
        "drupal/bootstrap_barrio": "^5.1",
        "drupal/ckeditorheight": "^1.8",
        "drupal/config_split": "^2.0@RC",
        "drupal/config_update": "*",
        "drupal/cookies": "^1.0",
        "drupal/core": "10.1.*",
        "drupal/core-composer-scaffold": "10.1.*",
        "drupal/core-project-message": "10.1.*",
        "drupal/core-recommended": "10.1.*",
        "drupal/csv_importer": "^1.15",
        "drupal/draggableviews": "^1.2",
        "drupal/easy_breadcrumb": "^2.0",
        "drupal/editor_file": "*",
        "drupal/facets": "^2.0",
        "drupal/file_extractor": "^4",
        "drupal/fixed_block_content": "dev-3357439-ui-updating-to",
        "drupal/focal_point": "^2.0",
        "drupal/fontyourface": "^3.5",
        "drupal/glossify": "^2.0",
        "drupal/imce": "*",
        "drupal/libraries": "^4.0",
        "drupal/matomo": "^1.12",
        "drupal/menu_block": "*",
        "drupal/menu_breadcrumb": "*",
        "drupal/menu_item_role_access": "^2.0",
        "drupal/menu_trail_by_path": "*",
        "drupal/metatag": "^1.16",
        "drupal/migrate_plus": "^6.0",
        "drupal/migrate_process_url": "^2.0.x-dev",
        "drupal/migrate_tools": "^6.0",
        "drupal/override_node_options": "^2.6",
        "drupal/paragraphs": "^1.12",
        "drupal/paragraphs_edit": "^2.0",
        "drupal/pathauto": "^1.3",
        "drupal/permissions_by_term": "^3.1",
        "drupal/restui": "^1.17",
        "drupal/search_api": "^1.11",
        "drupal/simple_sitemap": "^4.1",
        "drupal/sitemap": "^2.0",
        "drupal/smart_trim": "*",
        "drupal/svg_image": "^1.14",
        "drupal/term_csv_export_import": "^4.0",
        "drupal/toc_js": "^2.1",
        "drupal/tome": "1.8.*",
        "drupal/unique_field_ajax": "^2.0",
        "drupal/user_current_paths": "^2.0",
        "drupal/user_redirect": "^1.0",
        "drupal/video_embed_field": "^2.0",
        "drupal/video_embed_wysiwyg": "^2.0",
        "drupal/views_ajax_history": "^1.2",
        "drupal/views_block_area": "^1.0",
        "drupal/viewsreference": "^2.0",
        "drupal/webform": "^6.0",
        "drush/drush": "^11",
        "http-interop/http-factory-guzzle": "^1.0",
        "guzzlehttp/guzzle": "^7.4",
        "phpmailer/phpmailer": "^6.0",
        "toc/toc": "*",
        "wikimedia/composer-merge-plugin": "^2.0"
    },
    "require-dev": {
        "behat/mink": "*",
        "behat/mink-goutte-driver": "*",
        "drupal/devel": "*",
        "mikey179/vfsstream": "~1.2",
        "phpunit/phpunit": "^9",
        "symfony/css-selector": "^4.4"
    },
    "authors": [
        {
            "name": "me",
            "email": "me@mysite.com"
        }
    ],
    "repositories": [
        {
            "type": "composer",
            "url": "https://packages.drupal.org/8",
            "exclude": [ "drupal/search_api_meilisearch", "drupal/better_passwords", "drupal/block_content_machine_name", "drupal/fixed_block_content" ]
        },
        {
            "type": "package",
            "package": {
                "name": "toc/toc",
                "version": "v0.3.2",
                "type": "drupal-library",
                "dist": {
                    "url": "https://raw.githubusercontent.com/jgallen23/toc/greenkeeper/update-all/dist/toc.min.js",
                    "type": "file"
                }
            }
        },
        {
            "type": "git",
            "url": "git@mygitserver:search_api_meilisearch"
        },
        {
            "type": "git",
            "url": "https://git.drupalcode.org/issue/better_passwords-3296626.git"
        },
        {
            "type": "git",
            "url": "https://git.drupalcode.org/issue/block_content_machine_name-3286263.git"
        },
        {
            "type": "git",
            "url": "https://git.drupalcode.org/issue/fixed_block_content-3357439.git"
        }
    ],
    "minimum-stability": "dev",
    "extra": {
        "patches": {
            "drupal/core": {
                "Field options 'Rewrite results' and 'Link to the Content' applied together is not working as expected": "https://www.drupal.org/files/issues/2018-11-12/views_link_trim-2600456-22.patch",
                "Add file-mime formatter and make it output icons": "https://www.drupal.org/files/issues/2021-07-15/2907414-31.patch",
                "Large decimal number do not validate as they should": "https://www.drupal.org/files/issues/2022-07-26/2230909-269.patch",
                "Fix unnecessary multiple settings updates in AJAX calls": "https://www.drupal.org/files/issues/2021-05-12/2771361-42.patch",
                "Add a 'machine_name' widget for string field types with a UniqueField constraint": "https://www.drupal.org/files/issues/2023-05-17/drupal-10-1x-machine-name-widget-2685749-116.patch"
            },
            "drupal/toc_js": {
                "Disable showing the ToC as a default": "patches/toc_js_per_node_default_off.diff",
                "Fix umlaut and trailing special char conversion in ToC": "patches/toc_js_umlaut_and_trailing_char_fix.patch"
            },
            "drupal/metatag": {
                "Unset link rel: shortlink, edit-form, version-history and revision, and meta name: Generator, MobileOptimized, HandheldFriendly, viewport from <head> https://www.drupal.org/project/metatag/issues/2735195": "https://www.drupal.org/files/issues/2021-04-29/metatag-unset-2735195-59.patch"
            },
            "drupal/bootstrap_barrio": {
                "Add missing jquery.once dependency for affix.js (https://www.drupal.org/project/bootstrap_barrio/issues/3278329 and ../3366908) #9505-10": "https://www.drupal.org/files/issues/2023-06-14/3366908-2.patch"
            },
            "drupal/webform": {
                "Fix broken result export with drush": "https://www.drupal.org/files/issues/2023-01-26/3335851-4.patch"
            },
            "drupal/permissions_by_term": {
                "Fix content is not editable with 3.1.22": "https://git.drupalcode.org/project/permissions_by_term/-/merge_requests/20.patch"
            },
            "drupal/facets": {
                "Facets with AJAX not working": "https://www.drupal.org/files/issues/2023-02-07/facets-with-ajax-not-working-in-most-of-situations-3052574-174.patch"
            }
        },
        "drupal-scaffold": {
            "locations": {
                "web-root": "./web"
            }
        },
        "enable-patching": true,
        "installer-paths": {
            "web/core": [ "type:drupal-core" ],
            "web/modules/contrib/{$name}": ["type:drupal-module"],
            "web/libraries/{$name}": ["type:drupal-library"],
            "web/profiles/contrib/{$name}": ["type:drupal-profile"],
            "web/themes/contrib/{$name}": ["type:drupal-theme"],
            "web/drush/contrib/{$name}": ["type:drupal-drush"],
            "web/themes/custom/{$name}": ["type:drupal-custom-theme"]
        },
        "merge-plugin": {
            "include": [
                "web/modules/contrib/webform/composer.libraries.json"
            ]
        }
    },
    "prefer-stable": true,
    "conflict": {
        "drupal/drupal": "*"
    },
    "config": {
        "sort-packages": true,
        "optimize-autoloader": true,
        "allow-plugins": {
            "composer/installers": true,
            "drupal/console-extend-plugin": true,
            "cweagans/composer-patches": true,
            "drupal/core-composer-scaffold": true,
            "drupal/core-project-message": true,
            "wikimedia/composer-merge-plugin": true,
            "php-http/discovery": true
        }
    }
}

```

As suggested in the comments, I reduced this to a minimum viable setup:

```
{
    "name": "myproject",
    "description": "myproject",
    "type": "project",
    "require": {
        "drupal/core": "*",
        "drupal/core-composer-scaffold": "*",
        "drupal/core-project-message": "*",
        "drupal/core-recommended": "*",
        "drush/drush": "*"
    },
    "authors": [
        {
            "name": "me",
            "email": "me@mysite.com"
        }
    ],
    "minimum-stability": "dev",
    "extra": {
        "drupal-scaffold": {
            "locations": {
                "web-root": "./web"
            }
        },
        "enable-patching": true,
        "installer-paths": {
            "web/core": [ "type:drupal-core" ],
            "web/modules/contrib/{$name}": ["type:drupal-module"],
            "web/libraries/{$name}": ["type:drupal-library"],
            "web/profiles/contrib/{$name}": ["type:drupal-profile"],
            "web/themes/contrib/{$name}": ["type:drupal-theme"],
            "web/drush/contrib/{$name}": ["type:drupal-drush"],
            "web/themes/custom/{$name}": ["type:drupal-custom-theme"]
        },
            "merge-plugin": {
            "include": [
                "web/modules/contrib/webform/composer.libraries.json"
            ]
        }
    },
    "prefer-stable": true,
    "conflict": {
        "drupal/drupal": "*"
    },
    "config": {
        "sort-packages": true,
        "optimize-autoloader": true,
        "allow-plugins": {
            "composer/installers": true,
            "drupal/console-extend-plugin": true,
            "drupal/core-composer-scaffold": true,
            "drupal/core-project-message": true,
            "php-http/discovery": true
        }
    }
}

```

After this, I ran `composer update` (as the recommended `composer update "drupal/core-*" -W` would not update drupal/core as it was still locked to 9.4.x). Still no joy.

On Drupal 9.4 and Drush 10.x, I was able to just use `drush -l mysite.com` when in /var/www/projectroot (or somewhere below, as well).

Now, no matter the CWD, I cannot for the life of me get drush to output meaningful stuff.

`user@webserver:/var/www/projectroot/web $ ../vendor/drush/drush/drush --root=sites/mysite.com` or `user@webserver:/var/www/projectroot $ vendor/bin/drush --root=web/sites/mysite.com` or any other parameter combination with `-l` or `--url` or using absolute paths does not yield anything meaningful. With drush 11 I get `! [NOTE] Drupal root not found. Pass --root or a @siteAlias in order to see Drupal-specific commands.`

With is really strange, as drush seems to find the root (last line):

```
user@webserver:/var/www/projectroot $ vendor/bin/drush st
PHP binary    : /usr/bin/php8.1
PHP config    : /etc/php/8.1/cli/php.ini
PHP OS        : Linux
PHP version   : 8.1.21
Drush script  : /var/www/projectroot/vendor/bin/drush
Drush version : 11.6.0
Drush temp    : /tmp
Drush configs : /var/www/projectroot/vendor/drush/drush/drush.yml
Drupal root   : /var/www/projectroot/web

```

Drush is installed via composer, as per the docs.

Drush 12 only gives me: `The alias references a Drupal site that does not contain its own copy of Drush. Please add Drush to this site to use it.` (Note the double blank after alias; I consider this a bug, the error message should be different when a variable is undefined). I can get similar output to Drush 11 if I use `@none` as an alias.

I don't need aliases and only want to use drush locally on this dev-server. Deployment has its own automatic framework.

Do I need a copy of drush in every multisite's `sites/<sitename>` dir? I know global drush is not supported anymore, I switched from a system-wide installed drush to one in my project dir installed via composer (like it is still now) back at Drupal 8 and consider this locally installed, but I may be wrong? If so, how?

Happy to provide more debug info if needed.

Thanks in advance for anyone taking a look at this!

# Answer
> 2 votes
The issue was with `composer.json` needing another require statement for some version between 9.4 and 10.1, most likely \>=10.x.

This fixed all mentioned drush issues regarding `! [NOTE] Drupal root not found. Pass --root or a @siteAlias in order to see Drupal-specific commands.` or `The alias references a Drupal site that does not contain its own copy of Drush. Please add Drush to this site to use it.`:

`composer require "composer/installers" "^2.0"`

Now, if you have a multisite setup that has worked on Drupal 8 and 9 without a `web/sites/sites.php` file (like me) and just using subdirectories that are called the same way as their URL, it seems you now have to explicitly define them in `web/sites/sites.php` (instantiate it using `example.sites.php`; just add `$sites['mysite.org'] = 'mysite.org';` for every multisite).

Without that, my drush 12.1 + Drupal 10.1 were not able to correctly determine the site path (always set to `sites/default` when issuing `drush -l mysite.org st`).

Make sure your `composer.json` looks as similar as possible to the one you get with a fresh install as outlined in the official docs, currently at https://www.drupal.org/docs/develop/using-composer/starting-a-site-using-drupal-composer-project-templates. This helped me pinpoint the differences.

I consider this a bug in drush, there should be some check whether the needed packages are installed before failing in a way that does not give users a clue a package is missing.

The `sites.php` that is now needed should probably be part of the Drupal \>=10.x release notes. If anyone knows how to disable the necessity for this, please chime in! This is an additional step in multisite creation/instantiation that is unnecessary imho, if there are no aliases/ports needed. It would be nice to have this as default again.

# Answer
> 0 votes
I ran into this but in my case the issue was I was using Docker, where I encountered this error: "Composer plugins have been disabled for safety in this non-interactive session. Set COMPOSER\_ALLOW\_SUPERUSER=1 if you want to allow plugins to run as root/super user". Adding "COMPOSER\_ALLOW\_SUPERUSER=1" to my Dockerfile solved it. Hope it helps someone

---
Tags: drush, upgrading, 10
---