# Question
Title: How to add custom submit logic to search_block_form?
I want to alter the input typed into the default search block in a way that when users search for "möp" (with a German umlaut) the search should be broadened to "möp OR moep" automatically.

So I simply tried to append a custom form submit to the `search_block_form` and simply expand the string by a certain logic. But then I found out that the `search_block_form` seems to be a little bit special. In *core/modules/search/src/Form/SearchBlockForm.php* I found the following comment.

> ```
>   /**
>    * {@inheritdoc}
>    */
>   public function submitForm(array &$form, FormStateInterface $form_state) {
>     // This form submits to the search page, so processing happens there.
>   }
>
> ```

My question now is: Where do I actually add the logic exactly if not in the form submit? I don't find any redirect or controller that would help here. Could you please point me in the right direction? Or maybe, you even have a better idea than appending the logic this way?

---

I removed the D8 tag as I guess the logic behind this is pretty equal amongst all common Drupal versions.

# Answer
You can do this in hook\_search\_preprocess():

> Preprocess text for search.
>
> This hook is called to preprocess both the text added to the search index and the keywords users have submitted for searching. The same processing needs to be applied to both so that searches will find matches.

This is not part of the preprocess in theming. This hook is called directly from the search module. Btw. German umlauts are already replaced by default.
> 1 votes

# Answer
I am not certain if this will help, but I found from the module documentation the following

> search\_invoke\_preprocess() is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. Use a custom implementation of SearchTextProcessorInterface instead. See https://www.drupal.org/node/3078162

According to which preprocessing is moved to an internal method https://www.drupal.org/project/drupal/issues/3075703

I guess the last resort I found was creating a preprocess plugin as described in https://www.drupal.org/docs/8/modules/search-api/getting-started/processors
> 1 votes

# Answer
As `hook_preprocess_search()` currently is not preprocessing (issue here) I found a workaround for now. In the end I had to have the logic in two places.

First, I created a custom block that contains a custom form that roughly mimics the search block form by sending/redirecting the search string (after my desired preprocessing) to the search page. That's how my submit function looks like.

```
public function submitForm(array &$form, FormStateInterface $form_state) {

  $input = $form_state->getValue('MYTEXTFIELD');

  // Trim whitespaces.
  $input = trim($input);
  $input = preg_replace('/\s+/', ' ', $input);

  // Rewrite search string to match my logic.
  // Searching for 'Müller' rewrites to 'Müller OR Mueller'
  $processed = _rewrite_search_string($input);

  // The rest now is routine as Drupal replaces spaces with plus
  // signs between two words in a multiple word string.
  // Let's do the same.
  $keys         = explode(' ', $processed);
  $max          = sizeof($keys);
  $search_query = '';

  if ($max == 1) {
    $search_query = $keys[0];
  }

  else {
    for ($i = 0; $i < $max - 1; $i++) {
      $search_query .= $keys[$i] . '+';
    }
    $search_query .= $keys[$max - 1];
  }

  // Finally, redirect to the search page with the expected
  // query pattern. For example 'Müller+OR+Mueller'.
  $url = Url::fromUserInput('/search/content?keys=' .
    $search_query);
  $form_state->setRedirectUrl($url);

}

```

Second, the same logic but in a different way needed to get appended to the real `'search_form'` on the search page. So I added a custom submit function that looks the same as the original search form submit function except for the little addition that adds the desired logic.

```
function MYMODULE_form_alter(&$form, FormStateInterface $form_state, $form_id) {

  if ($form_id == 'search_form') {

    $form['#submit'][] = 'MYCUSTOMSUBMIT';
  }
}

function MYCUSTOMSUBMIT(&$form, FormStateInterface $form_state) {

  $input = $form_state->getValue('keys');

  // Here the same as above. 'Müller' gets rewritten to 'Müller OR Mueller'.
  $processed = _rewrite_search_string($input);

  $query['keys'] = $processed;

  // Rest is just c/p.
  $route = 'search.view_' . $form_state->get('search_page_id');
  $form_state->setRedirect(
    $route,
    [],
    ['query' => $query]
  );
}

```

---

As soon as `hook_search_preprocess()` gets fixed I mark @4k4's answer as the correct one.
> 0 votes

---
Tags: forms, search
---


# Question
Title: How to send email with commas in the name?
I am trying to send email of the format: `'Tom Smith, Jr <ts@example.com>'`; but it fails due to the comma in the name.

This closed issue: https://www.drupal.org/project/drupal/issues/2936032 dealt with handling commas in the site name for handling the From address. It basically added a helper function to wrap the name part in double quotes. But this doesn't work for the To address.

Any suggestions other than simply removing the comma?

# Answer
`Tom Smith, Jr <ts@example.com>` and `'Tom Smith, Jr <ts@example.com>'` are not valid email addresses, but `"Tom Smith, Jr" <ts@example.com>` is. Drupal doesn't "fix" addresses to make them RFC 2047 compliant, you need to do that yourself or use a helper module (such as Mime Mail) which will take care of a lot of these little details for you. This is especially important if you want to use non-ASCII characters in your email addresses.

Drupal 8 core provides a `\Drupal\Component\Utility\Mail::formatDisplayName()` method you can use, but it is well-known to do the wrong thing in many cases and has been removed in Drupal 9.2. This is part of the reason the above-mentioned helper modules exist.
> 2 votes

---
Tags: 8, emails
---


title: List all values in same row in a view

text:
# Question
Title: List all values in same row in a view
I have a view that displays the authors of an article and links to their author page.

Currently the view outputs each author in its own row as a list. I'd like to have it display the authors in one row separated by commas.

It's currently outputting like this:

Suzanne Smith

Jack Johnson

Alex Anderson

I'd like it to output like this: Suzanne Smith, Jack Johnson, Alex Anderson

I came across this question which recommended using the Views Aggregator Plus module, which I have installed. However the screenshots don't work. https://www.drupal.org/forum/support/post-installation/2019-09-19/struggling-to-list-all-values-in-field-on-same-row-in

Can anyone help with this issue?

Thank you!

# Answer
> 1 votes
If you are displaying the Article's Author field in your view, i.e. by setting *Format \> Show* to "Fields", you can configure your field to display all values in the same row. Click on the field (to edit its settings) and expand the "Multiple Field Settings" section. Check the "Display all values in the same row" checkbox.

---
Tags: 8, views
---


title: Find latest core version number

text:
# Question
Title: Find latest core version number
How do I get the latest versions for the Drupal release installed on the server without actually downloading it?

I am writing non-Drupal code that retrieves the latest versions of the Drupal release installed on the server, and verify the server is updated or need to be updated.

# Answer
For Drupal 7, fetch the XML at https://www.drupal.org/node/3060/release/feed?api\_version\[0\]=103 to get the release history. The first item's title contains the latest version.

```
<?xml version="1.0" encoding="utf-8" ?>
<rss version="2.0" xml:base="https://www.drupal.org/node/3060/release?
    api_version[103]=103" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Releases for Drupal core</title>
    <link>https://www.drupal.org/node/3060/release?api_version[103]=103</link>
    <description></description>
    <language>en</language>
      <item>
    <title>drupal 7.32</title>
    <link>https://www.drupal.org/drupal-7.32-release-notes</link>
    <description>...

```

If you want information for all major versions (including 8 and 9), you may simply use `https://www.drupal.org/node/3060/release/feed`. You are then required to go through the items until yo find a release for the major version you are looking for.
> 3 votes

# Answer
You can get a list of the most recent releases of Drupal or any contrib project with the Drush command `pm-releases`.

```
$ drush pm-releases drupal
 Project  Release  Date         Status
 drupal   7.x-dev  2014-Oct-15  Development
 drupal   7.32     2014-Oct-15  Supported, Recommended, Security

```

If you want to see just the version numbers:

```
$ drush pm-releases drupal --fields=Release --field-labels=0
 7.x-dev
 7.32

```

Regrettably, this command is missing an option to remove the dev release. This is an oversight that we should add to Drush.

**Postscript:**

You might also want to try running the command `drush pm-updatestatus`, perhaps with the `--security-only` flag, and compare against your requirements.
> 3 votes

# Answer
This is a very old topic, but you could base your system today on the Commander API :

This URL provides the list of version of released versions of Drupal : https://repo.packagist.org/p2/drupal/core.json

This URL provides the list of security advisories associated with Drupal : https://packagist.org/api/security-advisories/?packages\[\]=drupal/core
> 0 votes

# Answer
Since Drupal Core versions are hosted on Gitlab (see: https://git.drupalcode.org/project/drupal and the repository is publicly accessible, I'd rely on the Gitlab API to get that piece of information.

Looking at the repository, we can see that tags are used to mention the various release versions. Using Gitlab Tags API, we can fetch the tags into a JSON file and easily loop through it to get the latest Drupal version.

e.g. https://git.drupalcode.org/api/v4/projects/59858/repository/tags?search=^7 will get all the D7 tags and since the default order is by updated date, the first entry of the JSON file is the tag associated to the latest D7 released version.
> 0 votes

---
Tags: 7
---


# Question
Title: Exclude settings from a contrib module on export
I've two environment, **dev** and **staging**. I'm using the ReCaptcha module.

In the developpement environment, I have some api keys a other config which until now, are exported thanks to `drush cex` inside the *recaptcha.settings* file

```
site_key: mysite_key
secret_key: mysite_secret
verify_hostname: false
use_globally: false
widget:
  theme: dark
  type: image
  size: ''
  tabindex: 0
  noscript: false
_core:
  default_config_hash: mydefault_hash
langcode: fr

```

If I update my staging files website, the config from the development is imported with `drush cim`.

How can I, without another module, prevent this ?

In my *settings.local.dev.php*, I've added this line and ran `drush cex` :

```
$settings['config_exclude_modules'] = ['recaptcha'];

```

And it gave me this result

The config is completly deleted, but I'd like to keep all the config but not the *site\_key / secret\_key* (such as *theme* etc...)

I wish I could have a config for the development and the staging separetly, but still using drush commands.

# Answer
> 4 votes
You don't need to exclude the API keys from export; instead, you can set them in code, and then it doesn't matter what gets exported. (...probably. Some modules will not work with this approach.)

You can override config directly in `settings.php`.

Here's an example with Swiftmailer module taken from one of my sites.

**swiftmailer.transport.yml**

```
transport: smtp
smtp_host: smtp.example.com
smtp_port: 465
smtp_encryption: ssl
smtp_credential_provider: swiftmailer
smtp_credentials:
  swiftmailer:
    username: ''
    password: ''
sendmail_path: /usr/sbin/sendmail
sendmail_mode: bs
spool_directory: ''
_core:
  default_config_hash: RSNbRD3ekSaZoE19f3eHhm93gKOcD2PtmaAVmV3XMes
langcode: ja

```

Now I want to use a different API key for dev + production. So, in `settings.php`, I set the config manually:

```
  $config['swiftmailer.transport']['smtp_credentials']['swiftmailer']['username'] = 'apikey';
  $config['swiftmailer.transport']['smtp_credentials']['swiftmailer']['password'] = 'ABCDEFGHIJKLMNOP'

```

In my case I'm using a switch to detect an environment variable (live, test, dev, etc.) because that's how the host Pantheon does it, but you can do the same thing with `settings.local.dev.php` files.

Config in `settings.php` will never be exported; drush will ignore it. However, Drupal will always prioritize config set in code over `.yml` files. As a result, if you need to change this, you will have to update the code itself; you won't be able to make changes from the UI anymore.

Also note that it is a potential security issue to check API keys into version control like this.

The Config Override module adds a warning to admin screens that reminds you that you are overriding values in code.

If security is a concern, there's the Lockr module, which was built specifically to handle the use case of juggling API keys, but you have to pay for that.

---
Tags: drush, configuration, importing-exporting, 9
---


title: Numeric field type in Form API

text:
# Question
Title: Numeric field type in Form API
I am trying to add a "number" field type to a form using FAPI:

```
$form['phone_number']['areacode'] = array(
  '#type' => 'textfield',
  '#title' => '---',
  '#width' => '30%',
  '#align' => 'center',
  '#required' => true,
  '#maxlength' => 3
);

```

I change the TYPE to "number" and the field doesn't get generated at all. The number module is enabled. I have implemented the following theme functions:

* `MYTHEME_form_element`
* `MYTHEME_textfield`
* `MYTHEME_container`

What could be behind this field not showing up when I use `#type = number` or `#type = number_integer`, etc?

This might have something to do with it:

Creating Number fields (integer and decimal) manually in code for custom forms

However I actually want the type to render as "number" in HTML so smart phones show a numeric dialer

Any ideas?

# Answer
```
$form['my_element'] = array(
    '#type' => 'textfield',
    '#attributes' => array(
        ' type' => 'number', // insert space before attribute name :)
    ),
    '#title' => 'My number',
    '#required' => true,
    '#maxlength' => 3
);

```
> 16 votes

# Answer
As noted by @fritzmg , the Number element is included in core as of Drupal 8.0.

For example:

```
$form['phone_number']['areacode'] = array(
  '#type' => 'number',
  '#title' => '---',
  '#required' => true,
);

```
> 7 votes

# Answer
You can install and use module Elements for it.
> 5 votes

# Answer
I'm a bit late to the party. The problem is that theme\_textfield (D7: `includes/form.inc` , lines 3924ff) hard-codes `$element['#attributes']['type'] = 'text';`.

You may wrap said call in a `if (!isset($element['#attributes']['type'])) { ... }` or wait for the patch https://www.drupal.org/node/2545318 to be applied.
> 2 votes

# Answer
this is your code below..

```
$form['phone_number']['areacode'] = array(
'#type' => 'textfield',
'#title' => '---',
'#width' => '30%',
'#align' => 'center',
'#required' => true,
'#maxlength' => 3
);

```

please tell me what is \['phone\_number'\] , it's a container or not or its exist in your form or not and if you want to create custom field in your form then you should use hook\_form\_alter hook\_form\_alter

like

```
  function mymodule_form_alter($form_id, &$form) {
 if ($form_id == 'form_id') {

$form['field_name'] = array(
 '#type' => 'textfield',
 '#title' => t('title'),
 '#description' => t('Description.'),
 '#width' => '30%',
 '#align' => 'center',
 '#required' => true,
 '#maxlength' => 3
 );
  }
   }

```

hope this help you..
> 1 votes

# Answer
I don't see it working with invalid value for the **#type** field, however you can do something like that:

The form element:

```
$form['my_element'] = array(
    '#type' => 'textfield',
    '#attributes' => array(
        'data-type' => 'number',
    ),
    '#title' => '---',
    '#width' => '30%',
    '#align' => 'center',
    '#required' => true,
    '#maxlength' => 3
);

```

And implementing **theme\_textfield**

```
if(isset($element['#attributes']['data-type']) && $element['#attributes']['data-type'] === 'number'){
    $output = '<input type="number" />';
}else{
    $output = '<input' . drupal_attributes($element['#attributes']) . ' />';
}

```
> 0 votes

---
Tags: forms
---


# Question
Title: Error when trying to access Webform settings
When I try to go to the Structure \> Webforms \> Settings and Webforms I am getting the following error. I tried the `$ drush webform-repair` command, but that doesn't work. My Drupal version is 8.6.4.

> The website encountered an unexpected error. Please try again later. TypeError: Argument 4 passed to Drupal\webform\EntitySettings\WebformEntitySettingsBaseForm::appendBehaviors() must be of the type array, null given, called in /Applications/MAMP/htdocs/ubci+webform/modules/webform/src/EntitySettings/WebformEntitySettingsGeneralForm.php on line 274 in Drupal\webform\EntitySettings\WebformEntitySettingsBaseForm-\>appendBehaviors() (line 104 of modules/webform/src/EntitySettings/WebformEntitySettingsBaseForm.php).

And I am getting another error when I try to download the results on Webforms \> Download.

> The website encountered an unexpected error. Please try again later. Error: Unsupported operand types in Drupal\webform\Form\WebformResultsExportForm-\>buildForm() (line 57 of modules/webform/src/Form/WebformResultsExportForm.php). Drupal\webform\Form\WebformResultsExportForm-\>buildForm(Array, Object, Object)

# Answer
> 2 votes
I found the solution on the maintainer's blog: https://www.jrockowitz.com/blog/managing-webform-configuration. Go to `admin/structure/webform/config/advanced` and click 'Repair configuration'.

> ### Repairing admin configuration and webform settings
>
> If you happen upon some webform configuration that is outdated, you can run the `drush webform:repair` command or click the 'Repair configuration' via the admin UI under that 'Advanced' configuration tab (`admin/structure/webform/config/advanced`)

# Answer
> 0 votes
In my case, I was working on a migrated site and didn't have the `webform.settings` config.

To fix:

1. Grab config from an existing site: `/admin/config/development/configuration/single/export`
2. Import the config to current site: `/admin/config/development/configuration/single/import`

I had some errors about themes not being present, so I had to create dummy themes, install them, then uninstall them before the config import would work.

---
Tags: 8, webforms
---


# Question
Title: How to get the inreverse selection of a Views query?
Imagine that you can easily create a view that gives you a subset of some content on my site. However, you want the opposite view - all the content minus that subset - and that view is very hard or impossible to create.

The question then becomes: Is it possible to invert the result/selection of a Views query?

NB! Sorting is irrelevant for this question: It's all about the returned selection of items, not the order of the items.

# Answer
> 1 votes
It is simple. Go back in your Views Settings and find Sort criteria. There you can either reversely sort bu date: Content: Post date (desc) or by alphabetic order. It depends what field you take as sorting criteria.

# Answer
> 0 votes
use a "contextual filter" for a field, say userid. At the very bottom click the "more" link and select the "Exclude: If selected, the numbers entered for the filter will be excluded rather than limiting the view" checkbox.

This will revert your filter selection and show all the userids other than the one selected in the filter.

(I'm using D7 with views3).

# Answer
> 0 votes
I know the question is old, but I think it's still very much relevant and I didn't find much about this problem here.

Yes, I think there is a (sandbox) module that solves this problem - Other View Filter. Using that module you first create the view that you want to inverse. Then create a view that displays "everything", and use the first view as a filter in this views (using the "Other View Filter" module). Voila, you have the inverse.

I haven't tested the module myself, but the idea and code looks good.

**Added:** Maybe the (sandbox) module Views filter view is even better?

# Answer
> 0 votes
That's the sandbox version for D7, D8 full release is here https://www.drupal.org/project/other\_view\_filter

---
Tags: views
---


# Question
Title: Can't get page html after updating the page and flushing all caches
Once I updated a page via admin panel, I need to programatically flush all the caches, then get the page source code and write it to a file. I use the following code inside of my module:

```
function mymodulename_node_update($node) {
    drupal_flush_all_caches();
    $nid = $node->nid->value;
    $nodePath = \Drupal::service('path.alias_manager')->getAliasByPath('/node/'.$nid);
    $content = file_get_contents('https://mydomain.com'.$nodePath);
    file_put_contents(__DIR__ . '/test.html', $content);
}

```

But after a long time (longer that the time for normal flushing the caches via admin panel) I just receive the error

> "file\_get\_contents(https://mydomain.com/path-to-page): failed to open stream: HTTP request failed! in..."

and file writing is not executed.

If I try to get the content via cURL, the admin panel page never finishes loading.

What's wrong?

# Answer
> 1 votes
This is not a valid use case for drupal\_flush\_all\_caches(). This function is for changed or new code. For content you have to use cache tags.

In the rare case, where it is not possible to add correct cache tags to all render arrays, you can invalidate the tag `rendered`, which is added by default, even if no cache tags are specified:

```
\Drupal\Core\Cache\Cache::invalidateTags(['rendered']);

```

BTW entity operations are handled in transactions, so concurrent page request might not see the changes until they are committed to the database. In this case a cache clear (in any form) doesn't help.

**Edit: Adding an example for the latest comment.**

A nice solution would be a queue worker. It runs in the background, without that you have to wait for the admin panel to respond after a node save.

Example:

In the hook add a queue item with the entity ID:

*Media::postSave()*

```
\Drupal::queue('media_entity_thumbnail')->createItem(['id' => $translation->id()]);

```

Which is adding a queue item for this queue worker plugin:

*/modules/media/src/Plugin/QueueWorker/ThumbnailDownloader.php*

```
<?php

namespace Drupal\media\Plugin\QueueWorker;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Queue\QueueWorkerBase;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Process a queue of media items to fetch their thumbnails.
 *
 * @QueueWorker(
 *   id = "media_entity_thumbnail",
 *   title = @Translation("Thumbnail downloader"),
 *   cron = {"time" = 60}
 * )
 */
class ThumbnailDownloader extends QueueWorkerBase implements ContainerFactoryPluginInterface {

  /**
   * The entity type manager service.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * Constructs a new class instance.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   Entity type manager service.
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->entityTypeManager = $entity_type_manager;
  }

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

  /**
   * {@inheritdoc}
   */
  public function processItem($data) {
    /** @var \Drupal\media\Entity\Media $media */
    if ($media = $this->entityTypeManager->getStorage('media')->load($data['id'])) {
      $media->updateQueuedThumbnail();
      $media->save();
    }
  }

}

```

By default cron runs only every 3 hours. If you need the static HTML in a shorter time then trigger the cron task (which also runs the queues) from outside the website. See https://www.drupal.org/docs/user\_guide/en/security-cron.html

---
Tags: 8, hooks, caching
---


# Question
Title: "PHP Warning: require(drupal.php): failed to open stream"
I have created two fresh installations of Drupal, one version 8.9.17 and the other 9.2.3 and have been trying to get the Drupal console to work so that I can generate a module.

I used the following command in Composer and successfully downloaded it.

```
composer require drupal/console:~1.0 \
--prefer-dist \
--optimize-autoloader \
--sort-packages \
--no-update

```

Then run the following to avoid a Symphony conflict error.

```
composer update

```

This downloaded the relevant files and dependencies but when I try to access it from the command line (see below)

```
$ ../../../../vendor/bin/drupal

```

I get the following error.

> Warning: require(drupal.php): failed to open stream: No such file or directory in C:\xampp\htdocs\project\_name\vendor\bin\drupal(21) : eval()'d code on line 3 PHP Fatal error: require(): Failed opening required 'drupal.php' (include\_path='C:\xampp\php\PEAR') in C:\xampp\htdocs\project\_name\vendor\bin\drupal(21) : eval()'d code on line 3
>
> Fatal error: require(): Failed opening required 'drupal.php' (include\_path='C:\xampp\php\PEAR') in C:\xampp\htdocs\project\_name\vendor\bin\drupal(21) : eval()'d code on line 3

I looked on the Drupal console website and the advice was

> This can be caused by the ioncube loader extension

As i'm working with Xammp on Windows 10, ioncube isn't available by default in the php.ini file. What should I be looking for to solve this?

**EDIT** It has been pointed out to me that Drush now includes generating commands and should be used instead of Drupal console which appears to be obsolete.

# Answer
OK, so immediately after I posted the question I found the solution. I hope this helps anyone else in a similar situation.

The error is essentially saying that the drupal.php file cannot be found. So I went into the vendor/bin/drupal directory followed where the file was missing from:

```
vendor/drupal/console/bin/drupal

```

If you open that file, you will see the following code, which references the missing drupal.php file.

```
#!/usr/bin/env php
<?php

require 'drupal.php';

```

I compared this to Drush (which was working fine) and saw the Drush file was similar, but included the directory reference `__DIR__`. I updated the drupal.php file to the following and it now works as expected.

```
#!/usr/bin/env php
<?php

require __DIR__ . '/drupal.php';

```
> 1 votes

---
Tags: console
---


# Question
Title: Why do I have error related to unknown themes during synchronization?
When I try do synchronize from my local site to the remote one, I get this bunch of errors which prevent configuration to be imported.

It seems to be related to some themes (alpha\_responsive\_theme, elegant\_theme, mm, nucleus, tb\_sirate, zeropoint) but why?
I'm pretty sure I don't have these theme in my configuration: I have these folders in core/themes bartik, classy, engines, seven, stable, stark and onepage\_zymphonies\_theme which is set to be the default one (seven being the Administration theme).

Any idea why it happens?
And how to get rid of it?
Thanks in advance.

# Answer
> 0 votes
After trying CG Monroe's fisrt solution (add back in the themes, re-enable them and then uninstall them) with no success, I took a look to his last suggestion (Easy Uninstall project) which I couldn't see how to solve my problem. Finally, i used Drupal Console 'config:delete'. A bit tedious but effective.

# Answer
> 3 votes
At some point, the config you are using has had those themes enabled. Either from another site that you got the config set/ database from (e.g. the distro install?) or the themes were install/uninstalled/deleted on the dev machine (testing different themes?).

Since you seem to have the \*.settings files for the themes, these were probably installed and then deleted without uninstalling them. When a theme is uninstalled, this configuration information is removed.

To get rid of them, add back in the themes, re-enable them and then uninstall them.

An alternative is to use "drush cli" / the log messages to identify the configuration object names. Then use "drush cdel" to remove them from your dev box. NOTE: backup your db prior to this!

Or you can find the yaml files in the exported configuration and remove them. But you will have to do this each time you export to the remote site.

Another possible way is to try the easy uninstall project. Haven't used it but is seems to provide a GUI way to clean up the configuration.

*Soapbox Warning*

IMHO this is one of the great problems with Drupal 8 using a massive blob of config files. Developers will test stuff and decide not use it.. and it's impossible to find the cruft in the massive blob...

Features is now available for D8. It's a pain to set up, but your large blog can become a more manageable set of smaller config sets.

# Answer
> 1 votes
I recently had a similar problem:

In ThemeInstaller.php line 271:
The base theme stable cannot be uninstalled, because theme classy depends on it.

I was able to solve this with the following two drush commands: drush theme-uninstall classy drush theme-uninstall stable

---
Tags: 8, configuration
---


# Question
Title: How can I programmatically download webform results?
I want to download the results of certain webforms. I currently download them as CSV file by navigating to the download tab within the Drupal UI.

Is it possible to programmatically download the results?

# Answer
> 2 votes
The most easiest way to do it programmatically is via the commandline with drush:

```
drush webform:export contact_webform_name --destination=/var/test.csv

```

If you want to do it throug PHP you can use the WebformCommand directly. https://git.drupalcode.org/project/webform/-/blob/8.x-5.x/src/Commands/WebformCliService.php and look for the drush\_webform\_export function.

---
Tags: webforms
---


# Question
Title: How do I get rate and configuration values?
I'm stumped. How do you get values off of the shipment method config? For example I have a the Flat Rate shipping method "Express". I want to get the raw config form values stored on that shipping method. Such as rate, or a custom field I've provided.

I've plugged into the shipping methods to add a custom form field. But now need to retrieve it later on. The values aren't on the shipment entity.

```
    $shippingMethodId = $commerceOrder->shipments->entity->shipping_method->entity->shipping_method_id->value;
    $shipping_method_storage = \Drupal::entityTypeManager()->getStorage('commerce_shipping_method');
    $shipping_method = $shipping_method_storage->load($shippingMethodId);
    //$shipping_method->rate->value
    //$shipping_method->honeys_place_shipping_code->value

```

I've tried appending the values to the ShippingRate returned from calculateRates()

```
public function calculateRates(ShipmentInterface $shipment) {
    $rates = [];
    $rates[] = new ShippingRate([
      'shipping_method_id' => $this->parentEntity->id(),
      'service' => $this->services['default'],
      'amount' => Price::fromArray($this->configuration['rate_amount']),
      'description' => $this->configuration['rate_description'],
      'honeys_place_shipping_code' => $this->configuration['honeys_place_shipping_code'] // < -----
    ]);

    return $rates;
  }

```

This fires but does not make it into the shipment entity on the order later. I know I can retrieve the rate by accessing 'amount' off of the shipment entity. But I also need to retrieve custom values.

Is there a way to retrieve these values from config or get them onto the shipment entity object?

# Answer
> 2 votes
```
$shippingMethodId = $commerceOrder->shipments->entity->shipping_method->entity->shipping_method_id->value;
$shipping_method_storage = \Drupal::entityTypeManager()->getStorage('commerce_shipping_method');
$shipping_method = $shipping_method_storage->load($shippingMethodId);

$rate = $shipping_method->plugin->getValue()[0]['target_plugin_configuration']['rate_amount'];

```

You'll have to dig deeper on the plugin field from the shipping\_method entity.

```
$shipping_method->plugin->getValue()[0];
// This will return the following array below

```

---
Tags: commerce, 9
---


title: hook_forms() not working

text:
# Question
Title: hook_forms() not working
I'm trying to add a button to all node forms via callback in hook\_forms. I get a similar error. Can someone help.

```
Warning: call_user_func_array() expects parameter 1 to be a valid callback, class 'my_dossier_form' not found in drupal_retrieve_form() (line 844 of /var/www/includes/form.inc).
Notice: Array to string conversion in drupal_prepare_form() (line 1094 of /var/www/includes/form.inc).
Notice: Array to string conversion in drupal_prepare_form() (line 1108 of /var/www/includes/form.inc).
Notice: Array to string conversion in drupal_prepare_form() (line 1128 of /var/www/includes/form.inc).

```

Code

```
function my_dossier_forms($form_id, $args) {
  $forms = array();
  if ($types = node_type_get_types()) {
    foreach (array_keys($types) as $type) {
      $forms[$type . '_node_form']['callback'] = 'my_dossier_form';
    }
  }

  return $forms;
}

function my_dossier_form($form, &$form_state) {
  $form['delete'] = array(
    '#type' => 'submit',
    '#value' => t('Delete'),
    '#weight' => 10,
  );
  return $form;
}

```

# Answer
> 1 votes
What `node_type_get_types()` returns is an associative array of node type objects, keyed by the type. The description of the value returned is more understandable in the `_node_types_build()` documentation.

The hook implementation done from a Drupal core module, for example `comment_forms()`, helps to understand the correct code.

```
function comment_forms() {
  $forms = array();
  foreach (node_type_get_types() as $type) {
    $forms["comment_node_{$type->type}_form"]['callback'] = 'comment_form';
  }
  return $forms;
}

```

In your case, the code should be similar to the following one.

```
function my_dossier_forms($form_id, $args) {
  $forms = array();
  foreach (node_type_get_types() as $type) {
    $forms["my_dossier_node_{$type->type}_form"]['callback'] = 'my_dossier_form';
  }

  return $forms;
}

```

This code isn't much different from the code shown in the question, and it would not cause, alone, the *class 'my\_dossier\_form' not found* error.

The line 844 is the following one.

```
$form = call_user_func_array(isset($callback) ? $callback : $form_id, $args);

```

`$callback` is initialized from the following code.

```
if (!isset($forms) || !isset($forms[$form_id])) {
  $forms = module_invoke_all('forms', $form_id, $args);
}
$form_definition = $forms[$form_id];
if (isset($form_definition['callback arguments'])) {
  $args = array_merge($form_definition['callback arguments'], $args);
}
if (isset($form_definition['callback'])) {
  $callback = $form_definition['callback'];
  $form_state['build_info']['base_form_id'] = isset($form_definition['base_form_id']) ? $form_definition['base_form_id'] : $callback;
}

```

Line 844 could not think `my_dossier_form` is a class, except in the case `$callback` contains an array like `array('my_dossier_form', 'mymodule_form');`, or it contains a string like `'my_dossier_form::methodName'`.

The warnings about an array to string conversion means Drupal is getting an array when it expects a string, for example from the following line (line 1094).

```
    elseif (isset($form_state['build_info']['base_form_id']) && function_exists($form_state['build_info']['base_form_id'] . '_validate')) {

```

I would just avoid returning a form ID that starts with `$type` as that is probably creating conflicts with other modules implementing `hook_forms()`, which are expected to return a unique form ID each. See `module_invoke_all()` to understand what happens when two `hook_forms()` implementations return information for the same form ID.

```
  foreach (module_implements($hook) as $module) {
    $function = $module . '_' . $hook;
    if (function_exists($function)) {
      $result = call_user_func_array($function, $args);

      // In the case of hook_forms(), $result contains an array.
      if (isset($result) && is_array($result)) {
        $return = array_merge_recursive($return, $result);
      }

      elseif (isset($result)) {
        $return[] = $result;
      }
    }
  }

```

To be more exact, two modules returning values for the same form ID could at least cause the *call\_user\_func\_array() expects parameter 1 to be a valid callback, class '\[callback\]' not found* when they both set the value for the same callback. See the output of the following code, to understand what exactly happens.

```
$return["type_node_form"]["callback"] = "my_dossier_form";
$result["type_node_form"]["callback"] = "mymodule_form";
print_r(array_merge_recursive($return, $result));

```

The *callback* value becomes an array of two strings, which `call_user_func_array()` interprets as an array containing a class name and a method name.

```
Array
(
    [type_node_form] => Array
        (
            [callback] => Array
                (
                    [0] => my_dossier_form
                    [1] => mymodule_form
                )

        )

)

```

In this case, the conflict is with the Node module, which uses the following code for its `hook_forms()` implementation.

```
function node_forms() {
  $forms = array();
  if ($types = node_type_get_types()) {
    foreach (array_keys($types) as $type) {
      $forms[$type . '_node_form']['callback'] = 'node_form';
    }
  }
  return $forms;

```

As side note, the `hook_forms()` purpose is providing the same form builder for a group of forms whose ID follow a schema, such as in the case of comment edit forms, whose IDs are *comment\_node\_\[node type\]\_form*. When provided, the form builder callback (passed as *callback* value) should build the full form, not part of it.
The fact `my_dossier_form()` builds just a submission button, and `my_dossier_forms()` is using the same code used by `node_forms()` makes me think the code purpose is altering the node edit forms, which should be accomplished by implementing `hook_form_BASE_FORM_ID_alter()`. That's what the Book module does with `book_form_node_form_alter()`, which is invoked for the node edit form of every content type.

```
function book_form_node_form_alter(&$form, &$form_state, $form_id) {
  $node = $form['#node'];
  $access = user_access('administer book outlines');
  if (!$access) {
    if (user_access('add content to books') && (!empty($node->book['mlid']) && !empty($node->nid) || book_type_is_allowed($node->type))) {

      // Already in the book hierarchy, or this node type is allowed.
      $access = TRUE;
    }
  }
  if ($access) {
    _book_add_form_elements($form, $form_state, $node);

    // Since the "Book" dropdown can't trigger a form submission when
    // JavaScript is disabled, add a submit button to do that. book.css hides
    // this button when JavaScript is enabled.
    $form['book']['pick-book'] = array(
      '#type' => 'submit',
      '#value' => t('Change book (update list of parents)'),
      '#submit' => array(
        'book_pick_book_nojs_submit',
      ),
      '#weight' => 20,
    );
  }
}

```

If then the hook needs to use the content type name, that is available in `$form['#node']->type`.

---
Tags: 7, forms, hooks
---


title: Hide default value by view mode

text:
# Question
Title: Hide default value by view mode
I have an image field inside the content type 'Article'. I have two view mode: "teaser" and "full". I have a view page ('Homepage') showing all 'Article' nodes using "teaser" view mode.

* In the 'Homepage' view, I'd like to display a default image if the image field is not setted.
* In the "full" view mode I'd like to hide image field if is not set.

It's possible to do that?

# Answer
Here is what I would do.

Do not set a default image for your image field.

In views, instead of using a content: teaser use content: fields. Then in `No Results Behaviour` of your image field you put `<img src="/link/to/image.png">`

Then you create a "full" version using content: fields, which you don't do a `No Results Behaviour`.
> 0 votes

# Answer
I would create a preprocessor or add to existing one for node and check the view mode. If it is 'full', check to see if the image field has values/fid. If it does not, unset it from $content - which takes it out of the render array. You could also provide a .tpl file with a condition to render the specific field based on whether or not it has a value.

On the image field itself, you can upload a default image, which is what will display if no value is provided. That should solve the Homepage View display, Drupal should show the image when rendering if one is not provided by the user.

You can still construct Views with view modes, and in my opinion is far easier to work with than Views with fields.
> 0 votes

# Answer
If you are comfortable with twig, you can also make a custom template for your node teaser and use syntax like this:

```
{% if content.field_image.0 %}
   {{ content.field_image.0 }}
{% else %}
  <img src="/path/to/default/image.jpg" alt="Default image">
{% endif %}

```

The field name and path would need to be customized for your instance.
> 0 votes

---
Tags: views, media, entities
---


# Question
Title: view infinite scroll: how to exec a js function after loading items ?
I have the module "views infinite scrolling" installed on my site and it works. Now I want to exec a function when all loaded (Via ajax) items are loaded and showed to the user. How can I do this ? Thanks.

# Answer
> 1 votes
You should be able to use Drupal JS Behaviors. Views Infinite Scroll should fire off Drupal behaviors on an AJAX request. You would need to add custom JS to your pages that use infinite scrolling.

Here's an example where someone uses this to add a class to views rows loaded from Views Infinite Scroll.

```
Drupal.behaviors.myBehaviorName = {
  attach: function (context, settings) {
    // run your code here
  }
};

```

# Answer
> 1 votes
As @romain-ni says, the latest dev version supports raising a trigger when new items are loaded, however there are patches for previous version if for some reason you can't update to the latest version of the views\_infinite\_scroll package

**v8.x-1.6** https://www.drupal.org/project/views\_infinite\_scroll/issues/3227982

Once this is in place you can listen for that event in your own Javascript:

```
$(document).on('views_infinite_scroll.new_content', function (evt) {
  // Your code here
});

```

# Answer
> 0 votes
This code will show a visually-hidden button after all items have loaded.

```
Drupal.behaviors.infiniteScrollShowButton= {
  attach: function (context, settings) {
    // If all items have loaded, display the "show all" button
    if ($('.view-taxonomy-term .js-pager__items a').length === 0) {
      $('.view-taxonomy-term .view-footer a.button').removeClass('visually-hidden');
    }
  }
};

```

# Answer
> 0 votes
Un patch existe pour l'ajout d'un évènement jQuery au chargement de contenu : https://www.drupal.org/project/views\_infinite\_scroll/issues/3164903

On y ajoute deux triggers :

* "*views\_infinite\_scroll.new\_content*" : au chargement des éléments
* "*views\_infinite\_scroll.new\_content.appended*" : une fois que les éléments sont présents dans le DOM

Le premier trigger devrait intégrer la prochaine version stable ***8.x-1.8***, puisqu'il est déjà disponible dans la version ***8.x-1.x-dev***

Ensuite pour détecter l'évènement :

`$(document).on('views_infinite_scroll.new_content', (e) => {...})`

---
Tags: views
---


# Question
Title: How to programatically access the value of a taxonomy term reference?
I have a taxonomy term reference field attached to my user account.

I tried getting the value programatically in the same way as I get the value for text fields, but it doesn't work.

```
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\user\Entity\User;

$account = User::load($account->id());
// Returns the correct value.
$textfield = $account->get('field_textfield')->value;
// Does not return the correct value.
$termreference = $account->get('field_termreference')->value;

```

How am I supposed to get the value (name) of the referenced term programatically?

# Answer
As the taxonomy term is a reference the value (or label) is not stored in the user entity, just the target\_id. So you need to use:

`$termreference = $account->get('field_termreference')->target_id;`

This should be your code:

```
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\user\Entity\User;

$account = User::load($account->id());
// Returns the correct value.
$textfield = $account->get('field_textfield')->value;
// You need to use the target_id to access to the value.
$termreference = $account->get('field_termreference')->target_id;

```

Now you need to search the label:

```
$term = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->load($termreference);
$name = $term->getName();

```

Your taxonomy label (name) will be in `$name`.
> 15 votes

# Answer
There is a slightly cleaner way to do this. When calling `->get('field_name')` on a reference field it returns an instance of `EntityReferenceFieldItemList`. You can use this as an array and access the `target_id` and manually load the `\Drupal::entityTypeManger()` to subload, but that class actually has a method that does this for you:

```
$entity->get('field_tags')->referencedEntities();

```
> 3 votes

# Answer
field\_taxonomy-\>value; not work. Only -\>target\_id; But if you use the module Field Token Value, you could access to the name of field\_taxonomy saved as plain text like field\_taxonomy\_from\_token\_value-\>value;
> 1 votes

---
Tags: 8, entities, taxonomy-terms
---


# Question
Title: Editing paragraphs field settings returns 404 with php development server
I have this strange behaviour on my local drupal9 instance. I start it in the `web` folder with the build in php development server: `php -S localhost:8000`

Now if I try to edit any paragraphs field, eg. `/admin/structure/paragraphs_type/manual_news/fields/paragraph.manual_news.field_news_entries`

`[Fri Aug 13 12:47:39 2021] 127.0.0.1:55544 [404]: POST /admin/structure/paragraphs_type/manual_news/fields/paragraph.manual_news.field_news_entries - No such file or directory`

I get a 404 from the server. If it runs on a real webserver, it works.

Anybody knows how to debug / fix this?

I also opened https://www.drupal.org/project/paragraphs/issues/3228039 for this issue.

# Answer
> 1 votes
Clean URLs aren't supported when `php -S` is used to run the Drupal site because clean URLs require the .htaccess file or another file used from Nginx, which PHP doesn't use.

Either you don't use clean URLs (for example, /index.php?q=admin/structure/paragraphs\_type/manual\_news/fields/paragraph.manual\_news.field\_news\_entries), or you start the server with `php -S localhost:8888 .ht.router.php`, where .ht.router.php is a file that comes with Drupal 9. The router script also works around a bug in PHP, reported in If the trailing path that follows a script contains a dot PATH\_INFO is not set.

---
Tags: 9, paragraphs
---


# Question
Title: Add class to field parent with a preprocess hook in a custom module?
## Background:

I would like to alter the classes that are added to a rendered field in Drupal 9 in a custom module. I'm trying to use `hook_preprocess_field` and I'm not sure if this will work or if there is a better alternative.

based on: https://www.drupal.org/forum/support/module-development-and-code-questions/2017-03-31/add-class-to-field-in-drupal-8

### I have the following:

```
function my_module_preprocess_field__field_pricing_grid_items(&$vars)
{

  foreach(array_keys($vars['items']) as $delta)
  {
    $vars['items'][$delta]['attributes']->setAttribute('class', 'my-class');
  }
}

```

This works for adding a class but I can't figure out how to add a class to the parent div.

### Result

```
 <div class ="field__items">
         <div class ="field__item CUSTOM-CLASS"></div>
      </div>

```

### Desired result:

```
 <div class ="field__items CUSTOM-CLASS">
     <div class ="field__item></div>
  </div>

```

## Questions:

1. How can I add a class to the parent div? Is that possible with this hook?
2. Would an alternative approach of maybe overriding the field with a template in my module work? The theme template would be `field--field-pricing-grid-items.html.twig` Is there a way to override the theme with a module?
3. If neither of these approaches make sense what would be a possible way of achieving this.

# Answer
I was going with the wrong approach.

What worked:

I needed to use `hook_theme` in my `/my_module/my_module.module` file

```
function MY_MODULE_theme($existing, $type, $theme, $path) {
  return [
    'field__field_pricing_grid_items' => [
      'base hook' => 'field',
      'path' => $path . '/templates/field',
    ],
  ];
}

```

then it was easy to override `field.html.twig` with `/my_module/templates/field/field--field-pricing-grid-items.html.twig`
> 0 votes

---
Tags: theming, hooks
---


# Question
Title: Show a block only in in the front page and not in every pager page
I configured a block to be shown only on the front page, but the front page is actually a view of the last published nodes, with a pager, and the block is shown on each of its pages.

Is there any convenient way to show the block only once? I know it is possible to write a hook that checks if the *page* parameter is set and hide the block in that case.

What hook should I use to achieve this?

# Answer
> 0 votes
You could create a custom @Condition plugin, where the result is true when on the first page, and false whenever a URL parameter exists for page. Then you could set this context as the block context.

I wrote a tutorial on creating custom @Condition plugins here: https://www.jaypan.com/tutorial/custom-drupal-block-visibility-plugins-and-condition-plugin-api

---
Tags: blocks, 9
---


# Question
Title: Adding form placeholders to text form elements
Is there a way to add placeholders to a form with a module, or should this be done with a form template?

# Answer
> 40 votes
That's an HTML5 placeholder, you can just add it as an attribute to any element and HTML5 enabled browsers will react accordingly:

```
function MYMODULE_form_alter(&$form, &$form_state, $form_id) {
  if ($form_id == 'some_form') {
    $form['some_element']['#attributes']['placeholder'] = t('Placeholder text');
  }
}

```

A short recursive function like this might be handy if you want to automatically add placeholders for every textfield in a form (based on the field's title in this example):

```
function MYMODULE_auto_placeholders(&$element) {
  if ( isset($element['#type']) && $element['#type'] == 'textfield') {
    // set placeholder
    $element['#attributes']['placeholder'] = $element['#title'];
    // hide label
    $element['#title_display'] = 'invisible';
  }
  foreach (element_children($element) as $key) {
    MYMODULE_auto_placeholders($element[$key]);
  }
}

```

Then in your form alter function just call

```
MYMODULE_auto_placeholders($form);

```

That method will work for pretty much every textfield on a form, with the exception of those that are added in `#process` functions (like the alt and title textfields of an image field for example).

# Answer
> 20 votes
Just add the placeholder in the #attributes array for the form field element, such as in the following code.

```
   $form['my_textfield'] = array(
      '#type' => 'textfield',
      '#attributes' => array(
        'placeholder' => t('My placeholder text'),
      ),
    );

```

# Answer
> 17 votes
I tried Clive's answer:

```
function MYMODULE_form_alter(&$form, &$form_state, $form_id) {
  if ($form_id == 'some_form') {
    $form['some_element']['#attributes']['placeholder'] = t('Placeholder text');
  }
}

```

But to my surprise, I got the placeholder in wrapper of textfield, not textfield itself. Then I tried a variation as follows, it worked!

```
function MYMODULE_form_alter(&$form, &$form_state, $form_id) {
  if ($form_id == 'some_form') {
    $form['some_element']['und'][0]['value']['#attributes']['placeholder'] = t('Placeholder text');
  }
}

```

# Answer
> 8 votes
Stumbled upon this and thought Clive's answer seemed nice to automatically add placeholders.

Thou, a few minor changes are needed to get it right in drupal 8, so here's pretty much the same answer, but suitable for your drupal 8 theme.

```
/**
 * Implements hook_form_alter
 */
function THEME_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  add_placeholders($form);
}

/**
 * Add placeholders to suitable form elements -> textfield, password, email, textarea
 */
function add_placeholders(&$element) {
  if(isset($element['#type']) && in_array($element['#type'], ['textfield', 'password', 'email', 'textarea'])) {
    $element['#attributes']['placeholder'] = $element['#title'];
  }
  foreach(\Drupal\Core\Render\Element::children($element) as $key) {
    add_placeholders($element[$key]);
  }
}

```

# Answer
> 1 votes
I've found this solution here that worked for me:

```
/**

 * Implements template_preprocess_input().

 */

function MYTHEME_preprocess_input(&$variables) {

  // Set a placeholder for all search form elements.

  if ($variables['attributes']['type'] == 'search') {

    $variables['attributes']['placeholder'] = $variables['element']['#title'];

  }

}

```

# Answer
> 1 votes
Simply add the code for placeholder text

```
/**
 * Implements hook_form_alter
 */

function MODULENAME_form_alter(&$form, \Drupal\Core\Form\FormStateInterface
 $form_state, $form_id) {
  form['field_YOURFIELDNAME']['widget'][0]['value']['#placeholder'] = t('Some text');
}

```

# Answer
> 0 votes
If you want to target a form instance directly use hook\_form\_FORM\_ID\_alter. Might be tidier than using conditionals. My solution for targeting the search form block instance only.

```
function eyecare_form_search_block_form_alter(&$form, $form_state) {
  $form['keys']['#attributes']['placeholder'] = t('Search');
}

```

---
Tags: theming, forms
---


# Question
Title: Do I need to set up public files directory for multi-sites differently?
I had some Drupal installations which I merged into a multi-site installation.

When they were single installations, I created a *files* in the root directory and set the system permissions for the directory and its content to 755. When I ran the security review, I got no errors.

Now I set up them as multi-site; the *files* directory is still in the root folder, and inside there also the *site1*, *site2*, *site3* directories used from the single sites. When I set the system permissions to 755 and run the security review, I get an error that about some files in my public files directory which are writeable by the server.

Those files have been always writeable by the server. Otherwise, CSS/JavaScript aggregation and file uploading wouldn't work.

When I set the *files* folder in the root directory as well as *site1*, *site2*, *site3* to 555, the security review didn't report anything, but I got an error message from the Drupal status report because the public file system is not writeable from the server.

Do I need to set up the public files directory for multi-sites differently?

# Answer
To answer the question: no you do not need to set up those directories differently. The webserver obviously needs write access there for storing files. If the security\_review module has a problem with the dirs of the other sites then it is probably a bug in that module.

Anyhow: first click on details, maybe you find out more information there.

Then I had a look at the code, this is the method that detects the error:

https://git.drupalcode.org/project/security\_review/-/blob/8.x-1.x/src/Checks/FilePermissions.php#L48

It also tries to write a file in its own module directory (which it does not communicate to the user -\> not nice), so have a look into there and see if there is a file called "file\_write\_test\[...\]" or if there is a warning inside "IGNOREME.txt". If that is the case you need to remove write access to the modules directories for the webserver user (usually www-data)
> 1 votes

---
Tags: multi-sites, 9, security
---


# Question
Title: How do I query for all the entities for which a multi value field doesn't contain a specific value?
I have a multi value field which contains plain text. This is how it looks from the edit form.

>

I want to fetch entities that don't contain the *Expired* value. How can achieve it?

This is the code I wrote so far.

```
$query = \Drupal::entityQuery('taxonomy_term')->condition('vid', 'series');
$group = $query->orConditionGroup()
  ->notExists('field_multi_value')
  ->condition('field_multi_value', 'expired', 'NOT IN');
$query->condition($group);
$ids = $query->execute();

```

# Answer
To my knowledge you have to split it into 2 queries, because EntityQuery would still select a taxonomy\_term if it has another value that is not `expired`. No idea if that is a bug or by design (because technically the taxonomy\_term has a value that is NOT `expired`, in your example `Sometext`).

So the code would look like this:

```
$unwantedIds = \Drupal::entityQuery('taxonomy_term')
  ->condition('field_multi_value', 'expired')->execute();
$query = \Drupal::entityQuery('taxonomy_term')
  ->condition('tid', $unwantedIds ?: [-1], 'NOT IN');
$ids = $query->execute();

```
> 0 votes

---
Tags: entities, 9
---


# Question
Title: Allowing users to edit their webform submission
I am using Drupal 8.9.17 and Webform 6.0.4.

I have created a webform (registering for an event) that I am attaching to nodes (events). On the webform, I have checked "Allow users to update a submission using a secure token." And I have limited users to 1 submission per source entity.

All is good so far, but I would like the "Per user submission limit message" to say "You have already registered for this event" and then provide a link to view and edit their submission. I have attempted to do this using the \[webform\_submission:token-update-url\] token. And I set the permissions so that users can view and edit their submissions. However, the page for the URL that token spits out displays what appears to be an object defining the webform or submission (see below) rather than an editable form.

What do I need to do to get a link to an editable version of the user's previous submission?

```
[{"command":"settings","settings":{"pluralDelimiter":"\u0003","suppressDeprecationErrors":true,"ajaxPageState":{"libraries":"bartik\/messages,core\/drupal.dialog.ajax,webform\/webform.ajax,webform\/webform.element.details.save,webform\/webform.element.message,webform\/webform.theme.bartik,webform\/webform.theme.classy","theme":"bartik","theme_token":null},"ajaxTrustedUrl":{"form_action_p_pvdeGsVG5zNF_XLGPTvYSKCf43t8qZYSwcfZl2uzM":true},"user":{"uid":"316","permissionsHash":"34761c7696a50fc1957b53a0440e5fbc76a45d32fd40b21730cd04235e14a2c2"}},"merge":true},{"command":"add_css","data":"\u003Clink rel=\u0022stylesheet\u0022 media=\u0022all\u0022 href=\u0022\/sites\/default\/files\/css\/css_FOOEWHqjLXy_O63myzh8hUcoZuIExqFZ0eRqKYy-9RI.css\u0022 \/\u003E\n\u003Clink rel=\u0022stylesheet\u0022 media=\u0022all\u0022 href=\u0022\/sites\/default\/files\/css\/css_lEZM4fAqordMuJ1-dXqK0bKq2L4VyJvN1PXENL6vMdE.css\u0022 \/\u003E\n\u003Clink rel=\u0022stylesheet\u0022 media=\u0022all\u0022 href=\u0022\/core\/themes\/bartik\/css\/components\/messages.css?qxmvlx\u0022 \/\u003E\n"},{"command":"insert","method":"append","selector":"body","data":"\u003Cscript src=\u0022\/sites\/default\/files\/js\/js_E9vyQmkm5LzBBPbbSIF-YmVm0fz-3tfjNa8_AFRwPRI.js\u0022\u003E\u003C\/script\u003E\n","settings":null},{"command":"openDialog","selector":"#drupal-modal","settings":null,"data":"\u003Cspan id=\u0022webform-submission-basic-registration-with-guest-op-node-220-form-ajax-content\u0022\u003E\u003C\/span\u003E\u003Cdiv id=\u0022webform-submission-basic-registration-with-guest-op-node-220-form-ajax\u0022 class=\u0022webform-ajax-form-wrapper\u0022 data-effect=\u0022fade\u0022 data-progress-type=\u0022throbber\u0022\u003E\u003Cform class=\u0022webform-submission-form webform-submission-add-form webform-submission-basic-registration-with-guest-op-form webform-submission-basic-registration-with-guest-op-add-form webform-submission-basic-registration-with-guest-op-node-220-form webform-submission-basic-registration-with-guest-op-node-220-add-form\u0022 data-drupal-selector=\u0022webform-submission-basic-registration-with-guest-op-node-220-add-form\u0022 action=\u0022\/form\/basic-registration-with-guest-op?source_entity_type=node\u0026amp;source_entity_id=220\u0026amp;_webform_dialog=1\u0026amp;token=WeB5W4FaolOq0x60yuzFjYhSG2f8Yr6uO42GRk2hi5M\u0022 method=\u0022post\u0022 id=\u0022webform-submission-basic-registration-with-guest-op-node-220-add-form\u0022 accept-charset=\u0022UTF-8\u0022\u003E\n  \n    \u003Cdiv data-drupal-selector=\u0022edit-0\u0022 class=\u0022webform-message js-webform-message js-form-wrapper form-wrapper\u0022 id=\u0022edit-0\u0022\u003E\u003Cdiv data-drupal-messages\u003E\n            \n      \u003Cdiv class=\u0022messages__wrapper layout-container\u0022\u003E\n                            \u003Cdiv role=\u0022contentinfo\u0022 aria-label=\u0022Warning message\u0022 class=\u0022messages messages--warning\u0022\u003E\n                                          \u003Ch2 class=\u0022visually-hidden\u0022\u003EWarning message\u003C\/h2\u003E\n                                            You have already registered for this event.\u0026nbsp;http:\/\/skymallrats.com\/form\/basic-registration-with-guest-op?source_entity_type=node\u0026amp;source_entity_id=220\u0026amp;_webform_dialog=1\u0026amp;_wrapper_format=drupal_modal\u0026amp;token=zmHLhdHwo12LlVCmxhTREkEsusVfJLALFyO-TmaP69k\n                                      \u003C\/div\u003E\n                                  \u003C\/div\u003E\n      \u003C\/div\u003E\n\u003C\/div\u003E\n\u003Cinput autocomplete=\u0022off\u0022 data-drupal-selector=\u0022form-1dpd8qhjutpnuhks0feh8riqxhsbx3ykzfl9cfyqlqm\u0022 type=\u0022hidden\u0022 name=\u0022form_build_id\u0022 value=\u0022form-1dPd8QHJutpNuHks0FEh8rIQXHsBx3ykzfl9CFYqLqM\u0022 \/\u003E\n\u003Cinput data-drupal-selector=\u0022edit-webform-submission-basic-registration-with-guest-op-node-220-add-form-form-token\u0022 type=\u0022hidden\u0022 name=\u0022form_token\u0022 value=\u0022U9CWc1vN1c9ZZwMc33PMeaUtyX-_AA52W5hEgx6tdt8\u0022 \/\u003E\n\u003Cinput data-drupal-selector=\u0022edit-webform-submission-basic-registration-with-guest-op-node-220-add-form\u0022 type=\u0022hidden\u0022 name=\u0022form_id\u0022 value=\u0022webform_submission_basic_registration_with_guest_op_node_220_add_form\u0022 \/\u003E\n\n\n  \n\u003C\/form\u003E\n\u003C\/div\u003E","dialogOptions":{"modal":true,"title":"Second Test Event: Basic Registration with Guest Option"}}]

```

# Answer
> 0 votes
Just check "Limit users to one submission per webform/source entity" in the submission settings. Then:

> Please make sure authenticated users are allowed to 'view own submission' and 'edit own submission'.

Now the user can submit their data. After submitting when they visit the webform again, they will see their previous data and can update that.

---
Tags: 8, webforms, tokens
---


# Question
Title: How to redirect the user to an external website with POST data?
I want to implement an offsite payment method and everything is going well except the fact that I can't find how to redirect the user to the offsite payment gateway after submitting the cart checkout review

When the user submits the checkout review, the `orderSubmit` function is called. Inside this function I create the array with all the parameters required in the request, but I don't know how to send this request

```
public function orderSubmit(OrderInterface $order) {
    $fields = array(
      'version' => $this->configuration['version'],
      'TPE' => $this->configuration['tpe'],
      'date' => date('d/m/Y:H:i:s', time()),
      'montant' => round($order->getTotal(), 2).\Drupal::state()->get('uc_currency_code', 'USD'),
      'reference' => $order->id(),
      'texte-libre' => md5(uniqid(rand(), TRUE)),
      'mail' => $order->getEmail(),
      'lgue' => 'FR',
      'societe' => $this->configuration['company'],
      'url_retour' => Url::fromRoute('uc_cmic.cancel', array('absolute' => TRUE)),
      'url_retour_ok' => Url::fromRoute('uc_cmic.success', array('absolute' => TRUE)),
      'url_retour_err' => Url::fromRoute('uc_cmic.return', array('absolute' => TRUE)),
      'MAC' => "",
      'options' => ""
    );
    $fields = uc_cmcic_complete_request($this->configuration['security_key'], $fields); // This function fills the 'MAC' key by using the security key
    $host = UC_CMCIC_URL_CM_TEST;

//TODO send the request and redirect the user to the offsite payment gateway here
  }

```

What I tried to do and didn't work :

```
$response = \Drupal::httpClient()->request('POST', $host, [
  'form_params' => $fields,
]);

```

I tried some other variations of this too (like returning the `$response` after), which didn't work either

Question : What can I do to redirect the user to the offsite payment gateway while sending POST data in the request ?

# Answer
> 0 votes
You are defining an `orderSubmit()` method, which implies you are trying to write a payment method plugin that implements `PaymentMethodPluginInterface`.

That interface is not designed for offsite payment methods. Instead, you should be implementing `OffsitePaymentMethodPluginInterface` which has a `buildRedirectForm()` method exactly for the purpose of redirecting to the other site to complete payment.

The example for this is `PayPalPaymentsStandard` plugin which is part of the `uc_paypal` module bundled with Ubercart.

# Answer
> -1 votes
In D8 for redirection you need to use RedirectResponse Object & thats best way forward. However for improved security the same RedirectResponse object does not allow External URL redirections. For this You may want to use TrustedRedirectResponse. Sample below with assumption that you are trying to post data to the payment getway which can be added as a query string on the URL.

```
use Drupal\Core\Routing\TrustedRedirectResponse;

public function myController() {
  return new TrustedRedirectResponse($host.'?xyz=abc);
}

```

### PREFERRED

However the above suggestion does has security implications as the data is exposed on the URL. Below solution is a minor tweak from your code.

```
$response = \Drupal::httpClient()->post($host, [
        'form_params' => $fields,
        'headers' => [
          'Content-type' => 'application/x-www-form-urlencoded',
        ],
    ]);

```

You can try below if you would like to capture response (If its an XML / JSON)

```
$response = \Drupal::httpClient()->post($host, [
        'form_params' => $fields,
        'headers' => [
          'Content-type' => 'application/x-www-form-urlencoded',
        ],
    ])->getBody()->getContents();

```

---
Tags: 8, ubercart, http-request
---


# Question
Title: How can I remove some measure units from the Physical Fields widget?
I'm trying to alter the widget for the fields implemented by the Physical Fields module, using `hook_field_widget_form_alter()`.

I'm trying to remove some units from the select list, but there isn't a select list in `$element`.

How can I alter those units?

# Answer
> 2 votes
What the widget returns for the *Measurement* field is the rendering array built with the following code. (See MeasurementDefaultWidget::formElement().)

```
$element = [
  '#type' => 'physical_measurement',
  '#measurement_type' => $this->fieldDefinition->getSetting('measurement_type'),
  '#allow_unit_change' => $this->getSetting('allow_unit_change'),
  '#default_value' => $items[$delta]->getValue(),
] + $element;

if (!$this->getSetting('allow_unit_change')) {
   $element['#available_units'] = [$default_unit];
}

```

There isn't any option list, because that is built from the *physical\_measurement* form element class. (See `Measurement::processElement()`.)

```
$element['unit'] = [
  '#type' => 'select',
  '#options' => $units,
  '#default_value' => $default_value ? $default_value['unit'] : $unit_class::getBaseUnit(),
  '#title_display' => 'invisible',
  '#field_suffix' => '',
];

```

In `hook_field_widget_form_alter()`, the list of the allowed units are contained in `$element['#available_units']`, which could not be set when all the units for that measure are allowed.
The following code can be used to get all the units a *physical\_measurement* field uses (which are restricted from its measurement type).

```
use Drupal\physical\MeasurementType;

$unit_class = MeasurementType::getUnitClass($element['#measurement_type']);
$units = array_keys($unit_class::getLabels());

```

When `$element['#allow_unit_change']` is `FALSE`, which is used to mean it's not possible to change the unit associated with the field, `$element['#available_units']` contains the only allowed unit. In that case, `hook_field_widget_form_alter()` should not alter `$element['#available_units']`.

As side note, since the question is asking about removing measure units, the module code doesn't allow to add more units because the classes to handle them have code similar to the following one, used by the `VolumeUnit` class.

```
/**
 * {@inheritdoc}
 */
public static function getBaseFactor($unit) {
  self::assertExists($unit);
  $factors = [
    self::MILLILITER => '0.000001',
    self::CENTILITER => '0.00001',
    self::DECILITER => '0.0001',
    self::LITER => '0.001',
    self::CUBIC_MILLIMETER => '0.000000001',
    self::CUBIC_CENTIMETER => '0.000001',
    self::CUBIC_METER => '1',
    self::CUBIC_INCH => '0.00001638706',
    self::CUBIC_FOOT => '0.02831685',
    self::FLUID_OUNCE => '0.00002957353',
    self::US_GALLON => '0.0037854118',
  ];

  return $factors[$unit];
}

/**
 * {@inheritdoc}
 */
public static function assertExists($unit) {
  $allowed_units = [
    self::MILLILITER, self::CENTILITER, self::DECILITER, self::LITER,
    self::CUBIC_MILLIMETER, self::CUBIC_CENTIMETER, self::CUBIC_METER,
    self::CUBIC_INCH, self::CUBIC_FOOT, self::FLUID_OUNCE, self::US_GALLON,
  ];
  if (!in_array($unit, $allowed_units)) {
    throw new \InvalidArgumentException(sprintf('Invalid volume unit "%s" provided.', $unit));
  }
}

```

---
Tags: 8, entities
---


# Question
Title: Inbound path processor is not processing
I'm trying to use an Inbound Path Processor to redirect a path(to a webapp based on this tutorial), but Drupal is not picking it up.

I tried the following code to simply redirect every page on the site to `/happy`, but after rebuilding the cache, nothing happens; I can navigate the site normally and never get redirected.

**/mymodule/src/PathProcessor/MyPathProcessor.php**

```
namespace Drupal\mymodule\PathProcessor;

use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
use Symfony\Component\HttpFoundation\Request;

class MyPathProcessor implements InboundPathProcessorInterface {

  public function processInbound($path, Request $request) {
    return '/happy';
  }

}

```

Am I using this wrong? What do I need to do to get Drupal to use the path processor?

# Answer
> 3 votes
A path processor is a service tagged with *path\_processor\_inbound*. For example, this is the definition of the *path\_processor.files* path processor used by Drupal core.

```
path_processor.files:
  class: Drupal\system\PathProcessor\PathProcessorFiles
  tags:
    - { name: path_processor_inbound, priority: 200 }

```

Keep in mind that all the path processors are invoked. If the path set by a path processor isn't used from Drupal, another path processor could have changed it. Services like *path\_processor.files* use the priority for that reason: to avoid conflicts with other services of the same type which, in this case, could change the path already altered by another path processor.

---
Tags: routes, 9
---


# Question
Title: How to modify a rendered HTML string by adding custom code to setContent() in core in a module?
I want to override the HTML string returned by core using a custom module that replaces `$content['#markup']`, or the entire `$content` array returned by `HtmlResponse::setContent()`.

I can override `Response::setContent()` by calling `HtmlResponse::setContent()`. Exactly how do I add this to a custom module, for example implementing a service?

My goal is altering all the `<a>` tags to add a `<span>` tag to the ones that links to downloadable files.

# Answer
If you want to change the output of a form see hook\_form\_alter

If you want to change the output of controllers see this answer. You can also subscribe to other Kernel Events with that method and should be able to alter any output your site creates. (kernel.response is probably what you need)
> 0 votes

# Answer
If you want to act on the rendered HTML of a render array rather than the structured content array before rendering, use a #post\_render callback. See How to alter page content?
> 0 votes

---
Tags: 9
---


# Question
Title: Custom Submit Callback only triggering when Form is Valid when in Dialog
I'm trying to create a node in a dialog with the following link:

```
Enter the title of a registration form or <a class="use-ajax"
    data-dialog-options="{"width":800}"
    data-dialog-type="modal"
    href="/node/add/mycontenttype?enableAjaxSubmit=true">
    create a new registration form.
</a>

```

The callback `_mymodule_close_dialog()` runs perfectly when there are no validation errors. But when there is an error (e.g. the title is empty), JS just outputs an error `Uncaught TypeError: this.url is undefined`.

I can get rid of the JS error by preventing the form actions from becoming model buttons, but then the form just silently fails.

How can I show a form's error messages when creating a node in a dialog?

The callback:

```
/**
 * Implements hook_form_BASE_FORM_ID_alter() for \Drupal\node\NodeForm.
 */
function mymodule_form_node_mycontenttype_form_alter(&$form, FormStateInterface $form_state) {
    $enableAjaxSubmit = \Drupal::request()->query->get('enableAjaxSubmit');
    if (!empty($enableAjaxSubmit) && $enableAjaxSubmit === 'true') {
      $form['#attached']['library'][] = 'core/drupal.dialog.ajax';
      $form['#attached']['library'][] = 'core/jquery.form';

      $form['actions']['submit']['#attributes']['class'][] = 'use-ajax-submit';
      $form['actions']['submit']['#submit'][] = '_mymodule_close_dialog';
    }
}

use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\CloseModalDialogCommand;
use Drupal\Core\Ajax\PrependCommand;
use Drupal\Core\Ajax\AlertCommand;

function _mymodule_close_dialog(array &$form, FormStateInterface $form_state) {
  $response = new AjaxResponse();

  // if (!$form_state->getErrors()) {
    $response->addCommand(new CloseModalDialogCommand());
  // }

  $messages = ['#type' => 'status_messages'];
  $response->addCommand(new PrependCommand('.some-wrapper', $messages));

  $form_state->setResponse($response);
}

```

I followed:

## Update

Changed:

```
$form['actions']['submit']['#submit'][] = '_mymodule_close_dialog';

```

To:

```
$form['actions']['submit']['#ajax']['callback'] = '_mymodule_close_dialog'; // Rename function to remove `_`
$form['actions']['submit']['#ajax']['url'] = \Drupal\Core\Url::fromRoute('node.add', ['node_type' => $contentType]);
$form['actions']['submit']['#ajax']['options'] = [
  'query' => [
    \Drupal\Core\Form\FormBuilderInterface::AJAX_FORM_REQUEST => TRUE,
  ],
];

```

But getting a PHP error:

> `Symfony\Component\HttpKernel\Exception\HttpException: The specified #ajax callback is empty or not callable. in Drupal\Core\Form\FormAjaxResponseBuilder->buildResponse()`

When I `debug($triggering_element)` in the `FormAjaxResponseBuilder` file, the submit element did not have an `#ajax` key or the `use-ajax-submit` class. Thus, the triggering element is not ultimately altered.

Any ideas?

# Answer
> 0 votes
> and a bit of: https://www.drupal.org/project/drupal/issues/2934463#comment-13180158

You missed adding this part to your alter, which should fix your url undefined problem.

```
$form['actions']['submit']['#ajax']['url'] = \Drupal\Core\Url::fromRoute('node.add');
$form['actions']['submit']['#ajax']['options'] = [
  'query' => [
    Drupal\Core\Form\FormBuilderInterface::AJAX_FORM_REQUEST => TRUE,
  ],
];

```

---
Tags: 8, javascript, ajax, modal-dialogs
---


# Question
Title: Why Can't I Upgrade from 9.2.3 to 9.2.4 Using Composer?
If I check for outdated artifacts using:

```
composer outdated "drupal/*"

```

I can see Core needs updating:

```
drupal/core                       9.2.3             9.2.4
drupal/core-composer-scaffold     9.2.3             9.2.4
drupal/core-dev                   9.1.5             9.2.4
drupal/core-recommended           9.2.3             9.2.4

```

...

However, when I try to update:

```
composer update drupal/core-recommended --with-dependencies

```

I get the following messages:

```
Dependency "drupal/core" is also a root requirement, but is not explicitly allowed. Ignoring.
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Package container-interop/container-interop is abandoned, you should avoid using it. Use psr/container instead.
Package doctrine/reflection is abandoned, you should avoid using it. Use roave/better-reflection instead.
Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.
Package rector/rector-prefixed is abandoned, you should avoid using it. Use rector/rector instead.
Writing lock file
Generating autoload files
composer/package-versions-deprecated: Generating version class...
composer/package-versions-deprecated: ...done generating version class

```

This site was original built in Drupal 8, but has been upgraded to Drupal 9.

Any suggestions on how I might fix this issue?

My composer.json file is as follows:

```
{
    "name": "drupal/recommended-project",
    "description": "Project template for Drupal 8 projects with a relocated document root",
    "type": "project",
    "license": "GPL-2.0-or-later",
    "homepage": "https://www.drupal.org/project/drupal",
    "support": {
        "docs": "https://www.drupal.org/docs/user_guide/en/index.html",
        "chat": "https://www.drupal.org/node/314178"
    },
    "repositories": [
        {
            "type": "composer",
            "url": "https://packages.drupal.org/8"
        }
    ],
    "require": {
        "composer/installers": "^1.2",
        "drupal/address": "^1.9",
        "drupal/advagg": "4.x-dev@dev",
        "drupal/asset_injector": "2.x-dev",
        "drupal/charts": "^5.0@alpha",
        "drupal/coffee": "1.x-dev",
        "drupal/composer_deploy": "^1.6",
        "drupal/config_views": "^2.0",
        "drupal/core": "9.2.3 as 8.9.0",
        "drupal/core-composer-scaffold": "^9.1.5",
        "drupal/core-project-message": "^9.1.5",
        "drupal/core-recommended": "^9.1.5",
        "drupal/devel_entity_updates": "^3.0",
        "drupal/domain": "1.x-dev",
        "drupal/domain_site_settings": "1.x-dev",
        "drupal/dropzonejs": "^2.5",
        "drupal/drupalmoduleupgrader": "^1.7",
        "drupal/dynamic_asset": "1.x-dev",
        "drupal/email_registration": "^1.1",
        "drupal/exclude_node_title": "1.x-dev@dev",
        "drupal/feeds": "3.x-dev",
        "drupal/feeds_tamper": "^2.0",
        "drupal/field_css": "^2.0@RC",
        "drupal/field_formatter_class": "^1.5",
        "drupal/filefield_paths": "1.x-dev",
        "drupal/formblock": "^2.0@beta",
        "drupal/geocoder": "^3.17",
        "drupal/geophp": "^1.0",
        "drupal/group": "1.x-dev@dev",
        "drupal/gtranslate": "^1.13",
        "drupal/image_effects": "^3.1",
        "drupal/jquery_ui": "1.x-dev",
        "drupal/jquery_ui_sortable": "1.x-dev",
        "drupal/leaflet": "^2.1",
        "drupal/libraries": "^3.0@beta",
        "drupal/masquerade": "2.x-dev@dev",
        "drupal/media_directories": "^2.0",
        "drupal/media_library_form_element": "1.x-dev",
        "drupal/media_migration": "1.x-dev@dev",
        "drupal/menu_twig": "^1.4",
        "drupal/migrate_plus": "^5.1",
        "drupal/migrate_process_inline_images": "^1.0",
        "drupal/migrate_source_csv": "3.x-dev@dev",
        "drupal/migrate_tools": "5.x-dev@dev",
        "drupal/ng_lightbox": "^1.0@beta",
        "drupal/office_hours": "^1.3",
        "drupal/optimizedb": "1.x-dev@dev",
        "drupal/paragraphs": "^1.12",
        "drupal/pathauto": "^1.8",
        "drupal/php": "^1.1",
        "drupal/prepopulate": "^2.3",
        "drupal/quiz": "6.x-dev@dev",
        "drupal/recaptcha": "^3.0",
        "drupal/redirect": "^1.6",
        "drupal/search404": "^2.0",
        "drupal/shortcode": "2.0.x-dev@dev",
        "drupal/sitemap": "^2.0@beta",
        "drupal/theme_switcher": "^1.1",
        "drupal/ultimate_cron": "^2.0@alpha",
        "drupal/upgrade_rector": "1.x-dev@dev",
        "drupal/video": "^1.5@alpha",
        "drupal/video_embed_field": "2.x-dev@dev",
        "drupal/views_data_export": "1.x-dev@dev",
        "drupal/views_slideshow": "^4.8",
        "drupal/webform": "^6.0@alpha",
        "drupal/webform_migrate": "1.x-dev@dev",
        "drupal/weight": "^3.2",
        "drupal/xhprof": "^1.0@beta",
        "drush/drush": "^10.4",
        "geocoder-php/google-maps-provider": "^4.6",
        "google/apiclient": "^2.0",
        "phpstan/phpstan": "^0.12.81",
        "webmozart/assert": "^1.10"
    },
    "conflict": {
        "drupal/drupal": "*"
    },
    "minimum-stability": "dev",
    "prefer-stable": true,
    "config": {
        "sort-packages": true
    },
    "extra": {
        "drupal-scaffold": {
            "locations": {
                "web-root": "web/"
            }
        },
        "installer-paths": {
            "web/core": [
                "type:drupal-core"
            ],
            "web/libraries/{$name}": [
                "type:drupal-library"
            ],
            "web/modules/contrib/{$name}": [
                "type:drupal-module"
            ],
            "web/profiles/contrib/{$name}": [
                "type:drupal-profile"
            ],
            "web/themes/contrib/{$name}": [
                "type:drupal-theme"
            ],
            "drush/Commands/contrib/{$name}": [
                "type:drupal-drush"
            ],
            "web/modules/custom/{$name}": [
                "type:drupal-custom-module"
            ],
            "web/themes/custom/{$name}": [
                "type:drupal-custom-theme"
            ]
        },
        "drupal-core-project-message": {
            "include-keys": [
                "homepage",
                "support"
            ],
            "post-create-project-cmd-message": [
                "<bg=blue;fg=white>                                                         </>",
                "<bg=blue;fg=white>  Congratulations, you’ve installed the Drupal codebase  </>",
                "<bg=blue;fg=white>  from the drupal/recommended-project template!          </>",
                "<bg=blue;fg=white>                                                         </>",
                "",
                "<bg=yellow;fg=black>Next steps</>:",
                "  * Install the site: https://www.drupal.org/docs/8/install",
                "  * Read the user guide: https://www.drupal.org/docs/user_guide/en/index.html",
                "  * Get support: https://www.drupal.org/support",
                "  * Get involved with the Drupal community:",
                "      https://www.drupal.org/getting-involved",
                "  * Remove the plugin that prints this message:",
                "      composer remove drupal/core-project-message"
            ]
        }
    },
    "require-dev": {
        "drupal/core-dev": "^9.1.5",
        "kint-php/kint": "^3.3",
        "mglaman/drupal-check": "^1.1",
        "phpunit/phpunit": "^8.4.1"
    }
}

```

# Answer
> 8 votes
You have some really dirty hacks in your composer.json. A line like `"drupal/core": "9.2.3 as 8.9.0"` is a very bad idea and is always going to explode sooner or later. The intention usually is to upgrade Drupal core v9 while keeping v8-only/v9-incompatible modules, but trying to do something like this is just plain wrong.

1. Remove all lines `drupal/core***` except `drupal/core-recommended` and `drupal/core-composer-scaffold`. Those two will require all other stuff, the superfluent `drupal/core***` just make future upgrading harder.
2. In your other post there is the error message `...drupal/theme_switcher 1.1.0 requires drupal/core ^8.5...`. This module is incompatible with Drupal v9 (and maybe has been the cause for the very bad idea mentioned in earlier). Luckily there is now a `drupal/theme_switcher:^1.2` which is compatible with Drupal v9.
3. In my experience the most reliable way to update Drupal core is to explicitly require a new minimum version of core-recommended and core-composer-scaffold: `composer require drupal/core-recommended:^9.2.4 drupal/core-composer-scaffold:^9.2.4 --update-with-dependencies`.
4. In your special case you also need to update the v8 Theme switcher module to get a full set of v9 compatible packages: `composer require drupal/core-recommended:^9.2.4 drupal/core-composer-scaffold:^9.2.4 drupal/theme_switcher:^1.2 --update-with-dependencies`

# Answer
> -1 votes
You've locked `drupal/core-recommended` to version `9.1.5` in this line:

```
"drupal/core-recommended": "^9.1.5",

```

I believe you can fix this by running:

```
composer update drupal/core-recommended:^9.2. -W

```

Edit: Also, this doesn't look good to me, but I don't think it has anything to do with your problem:

```
    "drupal/core": "9.2.3 as 8.9.0",

```

I'm guessing someone did some aliasing for upgrade purposes or something. I think I'd try removing the `as 8.9.0` from that.

---
Tags: composer
---


# Question
Title: SimpleSAML_php authentication from 8.x-3.0 to 8.x-3.2 results in error
I updated SimpleSAML\_php authentication from 8.x-3.0 to 8.x-3.2.

Since then, whenever I click on the SSO login button, I get this error.

> Exception: Could not resolve 'saml:SP': no class named 'sspmod\_saml\_Auth\Source\_SP' or 'SimpleSAML\Module\saml\Auth\Source\SP'. in SimpleSAML\Module::resolveClass() (line 194 of /var/www/html/simplesamlphp/lib/SimpleSAML/Module.php).

I searched, but I couldn't find any information on it.

Everything is already set up (configurations, entityID in authsourse.php), but I still get this error.

# Answer
> 1 votes
Solved.

There was conflict because old version of simplesaml was present in custom folder, but when i updated via composer, new version got saved in contrib folder.

# Answer
> 0 votes
We've encountered the same issue today while upgrading Drupal 8 to 9 and upgrading the simplesaml\_php module to 3.2, using composer.

Solved by removing the entire simplesaml folder from the vendor folder `rm -rf vendor/simplesaml` and reinstalled again with `composer install`.

---
Tags: 8
---


# Question
Title: Using radio FormElement in a drupal 8 ListBuilder
I made a custom configuration entity, and I'm trying to set up radio buttons in the listBuilder. This is done in the core module language without any problems, but when I try to do the same trick with my custom entity I can't get any radio button. Instead the attributes are rendered in the html element.

```
    public function buildRow(EntityInterface $entity) {
      $row['label'] = $entity->label();
      $row['default'] = [
        '#type' => 'radio',
        '#parents' => ['site_default_language'],
        '#title' => t('Set @title as default', ['@title' => $entity->label()]),
        '#title_display' => 'invisible',
        '#return_value' => $entity->id(),
        '#id' => 'edit-site-default-language-' . $entity->id(),
      ];
      // Mark the right language as default in the form.
      if ($entity->id() == $this->languageManager->getDefaultLanguage()->getId()) {
        $row['default']['#default_value'] = $entity->id();
      }
      return $row + parent::buildRow($entity);
    }

```

Then I get this html element as a result without any errors in the logs:

```
<td #type="radio" #parents="active_scsp_server"
#title="..." #title_display="invisible"
#return_value="pre" #id="edit-active-scsp-server-pre" #default_value="pre">
</td>

```

# Answer
> 2 votes
To make the DraggableListBuilder work, the entity need to have a `weigth` key defined. Otherwise it will fail silently.

---
Tags: 8, entities, forms, configuration
---


title: How do I display a tour on a node?

text:
# Question
Title: How do I display a tour on a node?
On a Drupal 9 site, I installed the *Tour* and the *Tour UI* modules, and created a node.

How can I configure the *Tour UI* to show a tour on that node?

I don't know what to put in *Module name* and *Routes*.

>

# Answer
> 1 votes
The help text shows you what to put in the "Routes" box:

```
entity.node.canonical
- node:3

```

The Tour UI helps you create a Tour, but that Tour will be saved as a YAML file in your own custom module. Or I guess you could also add a tour to an existing module. Either way, you need some place to put the tour code. Most sites have a custom module to hold site-specific customizations of Drupal. So yes, you should be able to use "node" here if you want.

---
Tags: nodes, 9
---


# Question
Title: Create a view filter to find node which have no value in a specific field
I'm trying to create an exposed filter for view which allow to find node which have a specific field with no value. It's a field which lists taxonomy terms.

This is not possible with view. When I select to exposed the operator "IS NULL", the filter which is return by view make no sense as you can see on the following images

The filter after I choose this option:

In another issue on stackexchange, someone suggests to use better exposed filter and some people approve. But maybe because the issue was for Drupal 7 or for another type of field, this option is not available with the module for my field.

I create a custom filter which display a checkbox in filter which modify the query to display contents which have this field empty if check.

With EntityQuery, I know it's possible to make a query which return entities with a field empty with the function notExist on the field.

But in the case of the query for view, I have no idea how to indicate I want content which have no connection on this field. I guess I can have this with a subselect in the query but I have no idea how to construct it.

# Answer
> 0 votes
So, I finally found out how to do it with a left join and verify if it is null or not depending on the value of the checkbox (if checked, should be filled; if not, should be empty). Here is my filter:

```
<?php

namespace Drupal\my_module\Plugin\views\filter;

use Drupal\Core\Database\Query\Condition;
use Drupal\views\Plugin\views\filter\BooleanOperator;
use Drupal\views\Views;

/**
 * Filters to detect if a content has taxonomy term informed in field_tags.
 *
 * @ingroup views_filter_handlers
 *
 * @ViewsFilter("has_tags_filter")
 */
class HasTagFilter extends BooleanOperator {

  /**
   * Helper function that builds the query.
   */
  public function query() {
    $configuration = [
      'table' => 'node__field_tags',
      'field' => 'entity_id',
      'left_table' => 'node_field_data',
      'left_field' => 'nid',
      'operator' => '=',
    ];
    $join = Views::pluginManager('join')->createInstance('standard', $configuration);
    $this->query->addRelationship('node__field_tags', $join, 'node_field_data');

    if ($this->value == 1) {
      $this->query->addWhere('AND', 'node__field_tags.field_tags_target_id', '', 'IS NOT NULL');
    } else {
      $this->query->addWhere('AND', 'node__field_tags.field_tags_target_id', '', 'IS NULL');
    }
  }
}

```

# Answer
> 0 votes
1. Go to the view
2. Add filter for "Tags (field\_tags)"
3. Select "Is empty (NULL)" operator

Now only nodes with no tags will show

---
Tags: 8, views
---


# Question
Title: Validation doesn't show an error when the value entered in the field doesn't respect the minimum length
I am using the Field validation module to validate a text field using the Length rule with a minimum length of 1.

>

When the field is left empty, no validation error is shown.

>

Setting the field as required is not the solution for this specific case, as it causes problems with a module I am using.

How can I avoid users don't enter a value for that field?

# Answer
> 0 votes
I resolved the issue using the Clientside validation module, which has a sub-module that adds client side validation support for the *Field Validation* module.

---
Tags: 7, entities, javascript
---


# Question
Title: How do I grant to a role the permission to flush the cache?
I removed the *Administer site configuration* permission from a role, but I need users with that role to flush the cache from /admin/config/development/performance.

I know, there is `hook_menu_alter()` in Drupal 7, but what about Drupal 8?

# Answer
> 1 votes
You can alter the permission used to access a route, as described in Altering existing routes and adding new routes based on dynamic ones. In the specific case, the code used in `alterRoutes(RouteCollection $collection)` would be the following one.

Since the *administer site performance* permission is a custom one, it needs to be added as new permission by the module that implements the route subscriber class, as described in How to create a custom permission on Drupal 8 - 9.

For a module whose machine name is *mymodule*, the code would be similar to the following one.

### mymodule.permissions.yml

```
administer site performance:
  title: 'Administer site performance'
  description: 'Access the site performance settings page.'
  restrict access: true

```

### mymodule.services.yml

```
services:
  mymodule.route_subscriber:
    class: Drupal\mymodule\Routing\RouteSubscriber
    tags:
      - { name: event_subscriber }

```

### src/Routing/RouteSubscriber.php

```
namespace Drupal\mymodule\Routing;

use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;

/**
 * Listens to the dynamic route events.
 */
class RouteSubscriber extends RouteSubscriberBase {

  /**
   * {@inheritdoc}
   */
  protected function alterRoutes(RouteCollection $collection) {
    if ($route = $collection->get('system.performance_settings')) {
      $route->setRequirement('_permission', 'administer site performance');
    }
  }

}

```

If the users with that role should not access the settings in that page (*Caching* and *Bandwidth optimization*), but only be able to clear the cache, I would implement a custom route that shows a confirmation form and clear the cache (which means calling `drupal_flush_all_caches()`) when the user confirms the operation.

---
Tags: 8, caching
---


title: How can I make 3 different menus?

text:
# Question
Title: How can I make 3 different menus?
I need to do an Agenda in Drupal 7. The table for the events is more or less already done, but I would need to add 3 days on the top like:

Day 1 - Day 2 - Day 3

Depending on which day is clicked, it will show an agenda or the other.

How can I do this?

I'm adding the content using the *Agenda* content type I created, but I would need to create content for those 3 days.

# Answer
> 1 votes
The easiest way to accomplish this would be to utilize the **Taxonomy** module.

1. In your case, setup a new taxonomy with the name *Agenda Days*.
2. Add some of your terms to it: *Day 1*, *Day 2*, *Day 3*, etc.
3. Go back to your *Agenda* content type and add a taxonomy term reference field, configured to point at your new *Agenda Days* taxonomy.
4. Edit your content and select which Day they belong to.
5. Install and enable either the Views Menu Tabs or the Quick Tabs module.
6. Edit your view and add your new taxonomy field.
7. Set the views display mode to one provided by the module you selected, configured to use your taxonomy field for sorting.

---
Tags: 7, views, nodes
---


title: How to print in twig a Flag Link field

text:
# Question
Title: How to print in twig a Flag Link field
I have a views-view-unformatted.html.twig template working fine where I can print fields from a view of nodes.

Example: having i = 3, print row number 3 title

```
{{ rows[i]['content']['#row']._entity.title.0.value }}

```

This view has also a working relationship with flag. I see it working in the view preview.

How can I print the flag link inside views-view-unformatted.html.twig?

# Answer
Not being able to find better solution nor documetation, I built this working hack:

1. add a fake text field entity. IE: for articles: http://yoursite/admin/structure/types/manage/article/fields \> Add field \> Add text plain field
2. hook the view in a custom module:

```
function MYMODULE_views_pre_render(ViewExecutable &$view){
  if( $view->id() == 'VIEWID' ){
    foreach($view->result as $r) {
    // from https://www.drupal.org/comment/reply/295383/12504094
    $node = $r->_entity;
    $flag_link = [
      '#lazy_builder' => ['flag.link_builder:build', [
      $node->getEntityTypeId(),
      $node->id(),
      'FLAG',
    ]],
    '#create_placeholder' => TRUE,
  ];
  $flag_link = render($flag_link);
  $r->_entity->set('field_fake_flag_field', $flag_link); //.
    }
  }
}

```
3. print (finally) your flag link in the twig template:

{{ rows\[i\]\['content'\]\['#row'\].\_entity.field\_fake\_flag\_field.0.value }}

Twig is cool. It takes a while, sometimes.
> 1 votes

# Answer
I used `{{ fields.link_flag.content }}` in a views-view-fields--some\_view\_name.html.twig and that worked for me
> 0 votes

# Answer
I have found this

```
{{ view.style_plugin.getField(KEY, 'FIELD_NAME') }}

```

at this link https://dharmeshsavaliya.wordpress.com/2020/03/26/accessing-views-row-fields-inside-views-view-unformatted-html-twig-in-drupal-8/
> 0 votes

---
Tags: views, 8, theming, flags
---