# Question
Title: How do I extend the schema of an existing config_object?
*Module A* defines the following schema in module\_a.schema.yml.

```
module_a.settings.api:
  type: config_object
  label: API settings
  mapping:
    store:
      type: string
      label: Store code
    credentials:
      type: mapping
      label: Credentials
      mapping:
        username:
          type: string
          label: Username
        password:
          type: string
          label: Password

```

*Module B* extends the *Module A* configuration form and defines additional schema properties in module\_b.schema.yml.

```
module_a.settings.api:
  mapping:
    additionl_one:
      type: mapping
      label: Additional one
      mapping:
        additionl_one_sub:
          type: label
          label: Additional one sub

```

The definition from module\_b.schema.yml isn't picked up by Drupal. I'm checking with `config_inspector` and proving it by the fact that the new properties can't be translated.

Available articles/tutorials out there are doing the same thing but seems like this is either outdated or never worked:

# Answer
> 2 votes
Use `hook_config_schema_info_alter()`.

```
function module_b_config_schema_info_alter(&$definitions) {
  $definitions['module_a.settings.api']['mapping']['additionl_one'] = [...];
}

```

# Answer
> 0 votes
I think that I have found a solution, it seems that you need to add 'type: config\_object' when extending:

```
module_a.settings.api:
  type: config_object
  mapping:
    additionl_one:
      type: mapping
      label: Additional one
      mapping:
        additionl_one_sub:
          type: label
          label: Additional one sub

```

---
Tags: 8, configuration
---


# Question
Title: table styles not applied by bootstrap sub-theme
I'm writing a custom sub-theme of `bootstrap` with Drupal 9, and have noticed that table elements in my forums do not render properly:

This is despite me selecting "Bordered Table" in the configuration settings:

The next thing I tried was using Chrome's "inspect" feature to manually add `class = "table"` to the table element, which caused it render much more nicely;

I don't see any options to apply that class to table elements, either in the forum settings nor in the bootstrap settings.

I have a few questions to help me (and hopefully others) understand what's happening here;

1. Is adding the `class = "table"` to table elements the right approach?
2. Assuming it is, what's the right way to apply this class to table elements? (I've not averse to writing php code, I've just never written a theme before so I don't know where to start)
3. What's the point of those bootstrap table settings - they don't seem to do anything?

**EDIT:**

As per leymannx's suggestion, I've enabled twig debugging and looked at the resulting html:

```
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'forums' -->
<!-- FILE NAME SUGGESTIONS:
   * forums--containers--0.html.twig
   * forums--0.html.twig
   * forums--containers.html.twig
   x forums.html.twig
-->
<!-- BEGIN OUTPUT from 'core/modules/forum/templates/forums.html.twig' -->

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'forum_list' -->
<!-- BEGIN OUTPUT from 'core/modules/forum/templates/forum-list.html.twig' -->
<table>
  <thead>
    <tr>
      <th>Forum</th>
      <th>Topics</th>
      <th>Posts</th>
      <th>Last post</th>
    </tr>
  </thead>
  <tbody>
      <tr>
      <td>
                          <div title="No new posts">
            <span class="visually-hidden">No new posts</span>
          </div>
          <div><a href="/forum/1">General discussion</a></div>
                      <div></div>
                        </td>
              <td>
          0
                  </td>
        <td>0</td>
        <td>

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'forum_submitted' -->
<!-- BEGIN OUTPUT from 'core/modules/forum/templates/forum-submitted.html.twig' -->
  n/a

<!-- END OUTPUT from 'core/modules/forum/templates/forum-submitted.html.twig' -->

</td>
          </tr>
    </tbody>
</table>

<!-- END OUTPUT from 'core/modules/forum/templates/forum-list.html.twig' -->

```

# Answer
I opened issue #3190017 Apply table settings to core's forum list table where I added the preprocess plugin and the forum list template with the adjustments to respect the theme settings. You now can change the table settings and revisit `/forum` to see how the changes are applied. You can simply use the patch via Composer if you want.
> 1 votes

# Answer
`leymannx` suggested twig debugging, which was super helpful. The solution I came up with was to copy the `core/modules/forum/templates/forum-list.html.twig` file into `mytheme/templates` and then modify it to take the table theming elements from the `system/table.html.twig` file in bootstrap.

Here's the end result:

```
{#
/**
 * @file
 * Default theme implementation to display a list of forums and containers.
 *
 * Available variables:
 * - forums: A collection of forums and containers to display. It is keyed to
 *   the numeric IDs of all child forums and containers. Each forum in forums
 *   contains:
 *   - is_container: A flag indicating if the forum can contain other
 *     forums. Otherwise, the forum can only contain topics.
 *   - depth: How deep the forum is in the current hierarchy.
 *   - zebra: 'even' or 'odd', used for row class.
 *   - icon_class: 'default' or 'new', used for forum icon class.
 *   - icon_title: Text alternative for the forum icon.
 *   - name: The name of the forum.
 *   - link: The URL to link to this forum.
 *   - description: The description field for the forum, containing:
 *     - value: The descriptive text for the forum.
 *   - new_topics: A flag indicating if the forum contains unread posts.
 *   - new_url: A URL to the forum's unread posts.
 *   - new_text: Text for the above URL, which tells how many new posts.
 *   - old_topics: A count of posts that have already been read.
 *   - num_posts: The total number of posts in the forum.
 *   - last_reply: Text representing the last time a forum was posted or
 *     commented in.
 * - forum_id: Forum ID for the current forum. Parent to all items within the
 *   forums array.
 *
 * @see template_preprocess_forum_list()
 *
 * @ingroup themeable
 */
#}
{% if responsive %}
  <div class="table-responsive">
{% endif %}
{%
  set classes = [
    'table',
    bordered ? 'table-bordered',
    condensed ? 'table-condensed',
    hover ? 'table-hover',
    striped ? 'table-striped',
    sticky ? 'sticky-enabled',
  ]
%}
<table{{ attributes.addClass(classes) }}>
  <thead>
    <tr>
      <th>{{ 'Forum'|t }}</th>
      <th>{{ 'Topics'|t }}</th>
      <th>{{ 'Posts'|t }}</th>
      <th>{{ 'Last post'|t }}</th>
    </tr>
  </thead>
  <tbody>
  {% for child_id, forum in forums %}
    <tr>
      <td{% if forum.is_container == true %} colspan="4"{% endif %}>
        {#
          Enclose the contents of this cell with X divs, where X is the
          depth this forum resides at. This will allow us to use CSS
          left-margin for indenting.
        #}
        {% if forum.depth > 0 %}{% for i in 1..forum.depth %}<div class="indent">{% endfor %}{% endif %}
          <div title="{{ forum.icon_title }}">
            <span class="visually-hidden">{{ forum.icon_title }}</span>
          </div>
          <div><a href="{{ forum.link }}">{{ forum.label }}</a></div>
          {% if forum.description.value %}
            <div>{{ forum.description.value }}</div>
          {% endif %}
        {% if forum.depth > 0 %}{% for i in 1..forum.depth %}</div>{% endfor %}{% endif %}
      </td>
      {% if forum.is_container == false %}
        <td>
          {{ forum.num_topics }}
          {% if forum.new_topics == true %}
            <br />
            <a href="{{ forum.new_url }}">{{ forum.new_text }}</a>
          {% endif %}
        </td>
        <td>{{ forum.num_posts }}</td>
        <td>{{ forum.last_reply }}</td>
      {% endif %}
    </tr>
  {% endfor %}
  </tbody>
</table>
{% if responsive %}
  </div>
{% endif %}

```

The table now seems to render sensibly;
> 1 votes

---
Tags: theming, bootstrap-theme
---


# Question
Title: View inside display suite block field - contextual filter
using display suite's block field, which i chose a view block.. so when i have view page ('parent' view) it list many nodes. each node has a field for block view ('child' view).

how do i get child view to filter its results by the node id it is being displayed with in parent view? like a contextual filter, but not getting nid from url, but the node the block field is part of.

i like using display suite layouts, but need to rewrite the author field link. so i figure id make view to show the author and rewrite, then insert it into the node view mode as field. but i cant filter the child field how id like.

# Answer
Use the Views Field View Module, which allows you to insert other views as fields.

* Add a field of **Content: nid**. In settings, hide from display.
* Add field of **Global: View** (child view). In settings, click on Available tokens and put the nid token as your filter.
> 0 votes

# Answer
You should use Display Suite **Dynamic Field** instead of Block Field.

It allows you to select a view and pass the appropriate contextual filter arguments.

From Dynamic Field documentation:

> A dynamic field can be used in combination with the Views Content Pane submodule of Ctools to pass contextual arguments to a view. Steps:
>
> * Install Ctools, Views, and Display Suite.
> * Enable the Views Content Panes module and Views UI.
> * Create a new view. Add a contextual filter--for example, node NID. Add a content pane display. Configure the "arguments" argument of the display to enable the exposed filter you added. For example, select "From context" as the source of the argument and "Content ID" as the context.
> * Create a dynamic field.
> * Under manage display for a given content type, configure the dynamic field to use the views content pane you configured.
> 4 votes

# Answer
I just hit this use case in 2020 with Drupal 8. Here's the recipe I used with Display Suite and Views.

Create your DS Block Field using what will be the "child view." A critical setting is the configuration of that field. Set the content to "Node from URL." The default of "None" works fine for a regular node display, but not in a child View.

I also made a custom Display Mode for the content type using DS's 1-col layout and displaying only that block field. This isn't necessary strictly, but is useful if your parent View is field based, as I tend to make them. DS fields aren't true fields, and so aren't recognized by Views. However, there is an option to display a rendered entity as a field in field-based View. I chose that option and set the display mode to use my custom set up with the single field to get around that issue.

My parent View, as noted is field-based. I bring in the desired fields for the main rows with one of those fields being the rendered entity using my display mode. Works like a charm! I have no doubt you could do this with a Content or DS-Content based View, but I haven't experimented with it. I just prefer field based Views.

And if anyone's about to say, "But you can just use Views' grouping functionality," yes that's true, but the markup produced by that out of the box is not very nice when you want several fields "grouped" per row and a simple sublist.
> 0 votes

---
Tags: views, display-suite
---


# Question
Title: How do I generate taxonomy terms with drush 10/drupal 9.1.x
I'm trying to use drush to generate some taxonomy terms for a sandbox/development instance. This might be an error report by I'm a drush noob so I wanted to ask first.

When I run `drush devel-generate:terms --bundles=tags 10` I would expect drush to generate some terms for me, instead I get an error message like this:

```
#10 0.555  [error]  TypeError: count(): Argument #1 ($value) must be of type Countable|array, null given in count() (line 377 of /opt/drupal/web/modules/contrib/devel/devel_generate/src/Plugin/DevelGenerate/TermDevelGenerate.php) #0 /opt/drupal/web/modules/contrib/devel/devel_generate/src/Plugin/DevelGenerate/TermDevelGenerate.php(377): count(NULL)
#10 0.555 #1 /opt/drupal/web/modules/contrib/devel/devel_generate/src/Plugin/DevelGenerate/TermDevelGenerate.php(197): Drupal\devel_generate\Plugin\DevelGenerate\TermDevelGenerate->generateTerms(Array)
#10 0.555 #2 /opt/drupal/web/modules/contrib/devel/devel_generate/src/DevelGenerateBase.php(83): Drupal\devel_generate\Plugin\DevelGenerate\TermDevelGenerate->generateElements(Array)
#10 0.555 #3 /opt/drupal/web/modules/contrib/devel/devel_generate/src/Commands/DevelGenerateCommands.php(282): Drupal\devel_generate\DevelGenerateBase->generate(Array)
#10 0.555 #4 /opt/drupal/web/modules/contrib/devel/devel_generate/src/Commands/DevelGenerateCommands.php(159): Drupal\devel_generate\Commands\DevelGenerateCommands->generate()
#10 0.555 #5 [internal function]: Drupal\devel_generate\Commands\DevelGenerateCommands->terms('10', Array)
#10 0.555 #6 /opt/drupal/vendor/consolidation/annotated-command/src/CommandProcessor.php(257): call_user_func_array(Array, Array)
#10 0.555 #7 /opt/drupal/vendor/consolidation/annotated-command/src/CommandProcessor.php(212): Consolidation\AnnotatedCommand\CommandProcessor->runCommandCallback(Array, Object(Consolidation\AnnotatedCommand\CommandData))
#10 0.555 #8 /opt/drupal/vendor/consolidation/annotated-command/src/CommandProcessor.php(176): Consolidation\AnnotatedCommand\CommandProcessor->validateRunAndAlter(Array, Array, Object(Consolidation\AnnotatedCommand\CommandData))
#10 0.555 #9 /opt/drupal/vendor/consolidation/annotated-command/src/AnnotatedCommand.php(311): Consolidation\AnnotatedCommand\CommandProcessor->process(Object(Symfony\Component\Console\Output\ConsoleOutput), Array, Array, Object(Consolidation\AnnotatedCommand\CommandData))
#10 0.555 #10 /opt/drupal/vendor/symfony/console/Command/Command.php(255): Consolidation\AnnotatedCommand\AnnotatedCommand->execute(Object(Drush\Symfony\DrushArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#10 0.555 #11 /opt/drupal/vendor/symfony/console/Application.php(1027): Symfony\Component\Console\Command\Command->run(Object(Drush\Symfony\DrushArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#10 0.555 #12 /opt/drupal/vendor/symfony/console/Application.php(273): Symfony\Component\Console\Application->doRunCommand(Object(Consolidation\AnnotatedCommand\AnnotatedCommand), Object(Drush\Symfony\DrushArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#10 0.555 #13 /opt/drupal/vendor/symfony/console/Application.php(149): Symfony\Component\Console\Application->doRun(Object(Drush\Symfony\DrushArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#10 0.555 #14 /opt/drupal/vendor/drush/drush/src/Runtime/Runtime.php(118): Symfony\Component\Console\Application->run(Object(Drush\Symfony\DrushArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#10 0.555 #15 /opt/drupal/vendor/drush/drush/src/Runtime/Runtime.php(49): Drush\Runtime\Runtime->doRun(Array, Object(Symfony\Component\Console\Output\ConsoleOutput))
#10 0.555 #16 /opt/drupal/vendor/drush/drush/drush.php(72): Drush\Runtime\Runtime->run(Array)
#10 0.555 #17 /opt/drupal/vendor/drush/drush/drush(4): require('/opt/drupal/ven...')
#10 0.555 #18 {main}.
#10 0.555 TypeError: count(): Argument #1 ($value) must be of type Countable|array, null given in /opt/drupal/web/modules/contrib/devel/devel_generate/src/Plugin/DevelGenerate/TermDevelGenerate.php on line 377 #0 /opt/drupal/web/modules/contrib/devel/devel_generate/src/Plugin/DevelGenerate/TermDevelGenerate.php(377): count(NULL)
#10 0.555 #1 /opt/drupal/web/modules/contrib/devel/devel_generate/src/Plugin/DevelGenerate/TermDevelGenerate.php(197): Drupal\devel_generate\Plugin\DevelGenerate\TermDevelGenerate->generateTerms(Array)
#10 0.555 #2 /opt/drupal/web/modules/contrib/devel/devel_generate/src/DevelGenerateBase.php(83): Drupal\devel_generate\Plugin\DevelGenerate\TermDevelGenerate->generateElements(Array)
#10 0.555 #3 /opt/drupal/web/modules/contrib/devel/devel_generate/src/Commands/DevelGenerateCommands.php(282): Drupal\devel_generate\DevelGenerateBase->generate(Array)
#10 0.555 #4 /opt/drupal/web/modules/contrib/devel/devel_generate/src/Commands/DevelGenerateCommands.php(159): Drupal\devel_generate\Commands\DevelGenerateCommands->generate()
#10 0.555 #5 [internal function]: Drupal\devel_generate\Commands\DevelGenerateCommands->terms('10', Array)
#10 0.555 #6 /opt/drupal/vendor/consolidation/annotated-command/src/CommandProcessor.php(257): call_user_func_array(Array, Array)
#10 0.555 #7 /opt/drupal/vendor/consolidation/annotated-command/src/CommandProcessor.php(212): Consolidation\AnnotatedCommand\CommandProcessor->runCommandCallback(Array, Object(Consolidation\AnnotatedCommand\CommandData))
#10 0.555 #8 /opt/drupal/vendor/consolidation/annotated-command/src/CommandProcessor.php(176): Consolidation\AnnotatedCommand\CommandProcessor->validateRunAndAlter(Array, Array, Object(Consolidation\AnnotatedCommand\CommandData))
#10 0.555 #9 /opt/drupal/vendor/consolidation/annotated-command/src/AnnotatedCommand.php(311): Consolidation\AnnotatedCommand\CommandProcessor->process(Object(Symfony\Component\Console\Output\ConsoleOutput), Array, Array, Object(Consolidation\AnnotatedCommand\CommandData))
#10 0.555 #10 /opt/drupal/vendor/symfony/console/Command/Command.php(255): Consolidation\AnnotatedCommand\AnnotatedCommand->execute(Object(Drush\Symfony\DrushArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#10 0.555 #11 /opt/drupal/vendor/symfony/console/Application.php(1027): Symfony\Component\Console\Command\Command->run(Object(Drush\Symfony\DrushArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#10 0.555 #12 /opt/drupal/vendor/symfony/console/Application.php(273): Symfony\Component\Console\Application->doRunCommand(Object(Consolidation\AnnotatedCommand\AnnotatedCommand), Object(Drush\Symfony\DrushArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#10 0.555 #13 /opt/drupal/vendor/symfony/console/Application.php(149): Symfony\Component\Console\Application->doRun(Object(Drush\Symfony\DrushArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#10 0.555 #14 /opt/drupal/vendor/drush/drush/src/Runtime/Runtime.php(118): Symfony\Component\Console\Application->run(Object(Drush\Symfony\DrushArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#10 0.555 #15 /opt/drupal/vendor/drush/drush/src/Runtime/Runtime.php(49): Drush\Runtime\Runtime->doRun(Array, Object(Symfony\Component\Console\Output\ConsoleOutput))
#10 0.555 #16 /opt/drupal/vendor/drush/drush/drush.php(72): Drush\Runtime\Runtime->run(Array)
#10 0.555 #17 /opt/drupal/vendor/drush/drush/drush(4): require('/opt/drupal/ven...')
#10 0.555 #18 {main}
#10 0.555 TypeError: count(): Argument #1 ($value) must be of type Countable|array, null given in count() (line 377 of /opt/drupal/web/modules/contrib/devel/devel_generate/src/Plugin/DevelGenerate/TermDevelGenerate.php).
#10 0.560  [warning] Drush command terminated abnormally.

```

Dockerfile to reproduce:

```
FROM drupal:9.1.0

RUN composer require -n \
  drush/drush:10.3.6 \
  drupal/devel:4.0.1

RUN apt-get update; apt-get install -y sqlite3
RUN mkdir /db; chown www-data:www-data /db
USER www-data

RUN cd /opt/drupal && drush site-install -y \
    --db-url=sqlite:///db/drupal.sqlite \
    --db-su=root \
    --db-su-pw=password \
    --account-name=admin \
    --account-pass=pass
USER root

RUN drush en -y devel devel_generate
RUN drush devel-generate:terms --bundles=tags 10

```

What makes me suspicious that I've done something wrong is the "*Argument #1 ($value) must be of type Countable|array, null given*" message. Have I missed an argument?

I looked at the output of `drush devel-generate:terms --help` doesn't seem to suggest I've done anything wrong;

```
Create terms in specified vocabulary.

Arguments:
  [num] Number of terms to generate. [default: 50]

Options:
  --kill                        Delete all terms in these vocabularies before generating new ones.
  --bundles[=BUNDLES]           A comma-delimited list of machine names for the vocabularies where terms will be created.
  --feedback[=FEEDBACK]         An integer representing interval for insertion rate logging. [default: 1000]
  --languages[=LANGUAGES]       A comma-separated list of language codes
  --translations[=TRANSLATIONS] A comma-separated list of language codes for translations.
  --min-depth[=MIN-DEPTH]       The minimum depth of hierarchy for the new terms. [default: 1]
  --max-depth[=MAX-DEPTH]       The maximum depth of hierarchy for the new terms. [default: 4]

Aliases: gent, devel-generate-terms

```

# Answer
Seems like the version of devel\_generate you're using isn't fully compatible with PHP 8.0.

This issue fixed the problem: https://gitlab.com/drupalspoons/devel/-/issues/356

Replacing `drupal/devel:4.0.1` in the docker file with the dev version containing the patch (`drupal/devel:4.x-dev#c8d48e2dab1d92a74fd17b5a91d005476e91b9b0`) resolves the issue.
> 4 votes

---
Tags: taxonomy-terms, drush, 9
---


# Question
Title: How can I programmatically change the active theme?
How can I programmatically change the active Drupal 8 theme?

In Drupal 6, we used the following code.

```
global $custom_theme;
$custom_theme = 'garland';

```

In Drupal 7, we used `hook_custom_theme()`.

In Drupal 8, what's the correct way to do this?

# Answer
> 31 votes
In Drupal 8, you use theme negotiators, which essentially are services using a specific tag. See the theme negotiators implemented by Drupal, to understand exactly how they work; the example given in the change record is not updated.

### user.services.yml

```
  theme.negotiator.admin_theme:
    class: Drupal\user\Theme\AdminNegotiator
    arguments: ['@current_user', '@config.factory', '@entity_type.manager', '@router.admin_context']
    tags:
      - { name: theme_negotiator, priority: -40 }

```

### AdminNegotiator.php

```
<?php

namespace Drupal\user\Theme;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Routing\AdminContext;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Theme\ThemeNegotiatorInterface;

/**
 * Sets the active theme on admin pages.
 */
class AdminNegotiator implements ThemeNegotiatorInterface {
  use DeprecatedServicePropertyTrait;

  /**
   * {@inheritdoc}
   */
  protected $deprecatedProperties = ['entityManager' => 'entity.manager'];

  /**
   * The current user.
   *
   * @var \Drupal\Core\Session\AccountInterface
   */
  protected $user;

  /**
   * The config factory.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  protected $configFactory;

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

  /**
   * The route admin context to determine whether a route is an admin one.
   *
   * @var \Drupal\Core\Routing\AdminContext
   */
  protected $adminContext;

  /**
   * Creates a new AdminNegotiator instance.
   *
   * @param \Drupal\Core\Session\AccountInterface $user
   *   The current user.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   The config factory.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   * @param \Drupal\Core\Routing\AdminContext $admin_context
   *   The route admin context to determine whether the route is an admin one.
   */
  public function __construct(AccountInterface $user, ConfigFactoryInterface $config_factory, EntityTypeManagerInterface $entity_type_manager, AdminContext $admin_context) {
    $this->user = $user;
    $this->configFactory = $config_factory;
    $this->entityTypeManager = $entity_type_manager;
    $this->adminContext = $admin_context;
  }

  /**
   * {@inheritdoc}
   */
  public function applies(RouteMatchInterface $route_match) {
    return ($this->entityTypeManager->hasHandler('user_role', 'storage') && $this->user->hasPermission('view the administration theme') && $this->adminContext->isAdminRoute($route_match->getRouteObject()));
  }

  /**
   * {@inheritdoc}
   */
  public function determineActiveTheme(RouteMatchInterface $route_match) {
    return $this->configFactory->get('system.theme')->get('admin');
  }

}

```

The code is pretty easy to understand: The `applies()` method returns `TRUE` when the current route is the one for which your module wants to change the theme; the `determineActiveTheme()` method returns the theme machine name of the theme to apply.

See also ThemeNegotiator::determineActiveTheme() should not require a RouteMatch to be passed in for a possible change on the arguments received from the methods used by theme negotiators; if that patch is applied, you will need to change your theme negotiator code too.
That issue's status has been *Needs work* since May 10, 2018; it has been also moved to Drupal 9.2.x on December 28, 2018. Drupal 8.9.x could not get that change back-ported and it's probably safe to say that a Drupal 8.x module needs to use the `RouteMatchInterface $route_match` parameter for `applies()`.

# Answer
> 4 votes
We have also a very nice and clean solution at https://git.drupalcode.org/project/mailsystem/blob/HEAD/src/MailsystemManager.php#n60

```
 // Switch the theme to the configured mail theme.
$mail_theme = $this->getMailTheme();
$current_active_theme = $this->themeManager->getActiveTheme();
if ($mail_theme && $mail_theme != $current_active_theme->getName()) {
  $this->themeManager->setActiveTheme($this->themeInitialization->initTheme($mail_theme));
}

```

# Answer
> 3 votes
Another solution to this question is using the Theme Switcher module. it's compatible with Drupal 8 and 9 that developed by themeKey module developer.

> Theme Switcher module allows you to create theme-switching rules which allow automatic selection of a theme based on Drupal 8 Conditions system. Because of this it can also be easily extended to support additional custom conditions exposed by other modules. In Drupal 8 conditions are no longer defined through a hook and they are plugins now.
>
> With this module you can apply a specific theme to a unique node, use different themes based on the page language, use contrib conditions like a domain to apply one theme or another. The best of all is that you can do al of the above at once!

---
Tags: 8
---


# Question
Title: Are service autowiring and class autoresolving not anymore supported?
In Drupal 8, I needed to define services via the mymodule.services.yml file only, if I wanted to use them via the service container. I could also autowire them and the classes were automatically resolved.

```
services:
  my_module.twitter_feed:
    class: Drupal\my_module\TwitterFeed
    autowire: true

  domcrawler.crawler:
    class: Symfony\Component\DomCrawler\Crawler
    public: false

```

In Drupal 9, it looks like I must define all my custom classes inside the mymodule.services.yml file. Drupal 9 seems not to be able to identify my classes without an entry within the mymodule.services.yml file. There seems to be no autoresolving anymore. Symfony 4 and 5 are able to do that and Symfony 3.x (Drupal 8) was definitely able to autowire services and find classes by its own. Drupal seems to have its own service container, which suppresses that functionality.

Defining every single PHP class within a \*.services.yml file is a huge amount of work for upgrading and maintaining my project. I cannot believe that Drupal 9 requires this work, because the Symfony framework already does that automatically.

Do you know any way to re-enable PHP class autowiring and autoresolving in an upgrade from Drupal 8 to 9?
I hope that anybody else was struggling with autowiring during an upgrade from Drupal 8 to 9 and knows any solution.

# Answer
I found out, that in Drupal 9 some autowiring "magic" from Drupal 8 was removed. Drupal also overrides the Symfony dependency injection by its own logic. Therefor you must write your own service provider class in your module to autowire your service containers (basically all PHP classes, except e.g. Drupal Plugins or Drupal Entities, which Drupal 9 resolves by its own).

Like this (path: modules/custom/my\_nice\_module/MyNiceModuleServiceProvider.php):

```
<?php
declare(strict_types=1);

namespace Drupal\my_nice_module;

use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceProviderBase;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\Finder\Finder;

class MyNiceModuleServiceProvider extends ServiceProviderBase {

  public function register(ContainerBuilder $container) {
    $containerModules = $container->getParameter('container.modules');
    $finder = new Finder();

    $foldersWithServiceContainers = [];

    $foldersWithServiceContainers['Drupal\my_nice_module\\'] = $finder->in(dirname($containerModules['my_nice_module']['pathname']) . '/src/')->files()->name('*.php');

    $foldersWithServiceContainers['Drupal\my_nice_module\Transformer\\'] = $finder->in(dirname($containerModules['my_nice_module']['pathname']) . '/src/Transformer/')->files()->name('*.php');
    $foldersWithServiceContainers['Drupal\my_nice_module\Transformer\RegionalCouncilMeeting\\'] = $finder->in(dirname($containerModules['my_nice_module']['pathname']) . '/src/Transformer/RegionalCouncilMeeting/')->files()->name('*.php');

    $foldersWithServiceContainers['Drupal\my_nice_module\DomCrawler\\'] = $finder->in(dirname($containerModules['my_nice_module']['pathname']) . '/src/DomCrawler/')->files()->name('*.php');
    $foldersWithServiceContainers['Drupal\my_nice_module\MongoDBFetcher\\'] = $finder->in(dirname($containerModules['my_nice_module']['pathname']) . '/src/MongoDBFetcher/')->files()->name('*.php');

    $foldersWithServiceContainers['Drupal\my_nice_module\Importer\\'] = $finder->in(dirname($containerModules['my_nice_module']['pathname']) . '/src/Importer/')->files()->name('*.php');
    $foldersWithServiceContainers['Drupal\my_nice_module\Importer\MongoDB\\'] = $finder->in(dirname($containerModules['my_nice_module']['pathname']) . '/src/Importer/MongoDB/')->files()->name('*.php');
    $foldersWithServiceContainers['Drupal\my_nice_module\Importer\RegionalCouncilMeetingsArchive\\'] = $finder->in(dirname($containerModules['my_nice_module']['pathname']) . '/src/Importer/RegionalCouncilMeetingsArchive/')->files()->name('*.php');

    $foldersWithServiceContainers['Drupal\my_nice_module\Entity\\'] = $finder->in(dirname($containerModules['my_nice_module']['pathname']) . '/src/Entity/')->files()->name('*.php');
    $foldersWithServiceContainers['Drupal\my_nice_module\Extractor\\'] = $finder->in(dirname($containerModules['my_nice_module']['pathname']) . '/src/Extractor/')->files()->name('*.php');
    $foldersWithServiceContainers['Drupal\my_nice_module\Service\\'] = $finder->in(dirname($containerModules['my_nice_module']['pathname']) . '/src/Service/')->files()->name('*.php');
    $foldersWithServiceContainers['Drupal\my_nice_module\DrushCommandFacade\\'] = $finder->in(dirname($containerModules['my_nice_module']['pathname']) . '/src/DrushCommandFacade/')->files()->name('*.php');

    foreach ($foldersWithServiceContainers as $namespace => $files) {
      foreach ($finder as $fileInfo) {
        $class = $namespace
          . substr($fileInfo->getFilename(), 0, -4);
        // don't override any existing service
        if ($container->hasDefinition($class)) {
          continue;
        }
        $definition = new Definition($class);
        $definition->setAutowired(TRUE);
        $container->setDefinition($class, $definition);
      }
    }
  }

}

```

Here is a good presentation about this: https://events.drupal.org/sites/default/files/slides/drupalcon.2019.autowiring.pdf

The Autoservices Drupal module can also be a good example for your own solution: https://gitlab.com/upstreamable/drupal-autoservices
> -3 votes

---
Tags: symfony-services
---


title: Why is my form handler not running?

text:
# Question
Title: Why is my form handler not running?
I've created a custom module which creates a form and shows it in a block but whenever I submit it, it just refreshes the page and doesn't seem to execute the submit handler at all.

I'm going crazy trying to get this to work.

I've stripped the code right back but it's still not working.

```
<?php

// declare block info
function paypoint_block_info() {
    $blocks['paypoint_block'] = array(
        'info' => t('Paypoint Payment Form'), // block admin title
        'cache' => DRUPAL_NO_CACHE,
    );

    return $blocks;
}

// declare block functions
function paypoint_block_view($delta = '') {
    switch ($delta) {
        case 'paypoint_block':
            $block['content'] = drupal_get_form('paypoint_form');
            return $block;
            break;
    }

}

function paypoint_form($form, &$form_state) {
    $form['#attributes'] = array('class' => 'content-page-form');

    $form['strForename'] = array(
        '#type' => 'textfield', //you can find a list of available types in the form api
        '#title' => 'Forename',
        '#size' => 30,
        '#maxlength' => 37,
        '#required' => TRUE, //make this field required
    );

    $form['submit'] = array(
        '#type' => 'submit',
        '#value' => t('Make a payment'),
        '#attributes' => array('class' => array('btn', 'btn-default')),
    );

    return $form;
}

function paypoint_form_validate($form, &$form_state) {

}

function paypoint_form_submit($form, &$form_state) {
    drupal_set_message($form_state['values']['strForename']);
}

```

The validate function seems to get called, just not the submit one. I put `print_r($form)` in the validate function and got this: http://pastebin.com/Yt6T6Cr8

# Answer
Your code looks fine to me. I think you might be on to something with your last comment, there might be something interfering with the hook.

One thing you might wanna do is run module\_implements and look for the submit hook. That should give you a list of modules that implement that hook. At the very least you can check to see if there is anything strange going on with those modules.
> 1 votes

# Answer
Maybe, you need to point the submit handler in the form function clearly ?

```
$form['#submit'][] = 'paypoint_form_mysubmit';

```
> 0 votes

# Answer
Just wanted to add that if you overload POST with too many form fields, exceeding your PHP max\_post\_size, PHP will purge POST. Since that includes submission information, no submission will occur. I had a custom form running in a loop off a database query. If it returns too many rows, the form is too large and cannot be submitted. Doesn't look like that was the problem in this case, but wanted to put it here since people searching for a solution may be running into this.
> 0 votes

---
Tags: 7, forms
---


# Question
Title: How are better exposed filters - checkboxes value - assigned
I have been wrestling with this for a number of days. I apologize if this has been asked elsewhere, I am not able to find the fix, or maybe I am just not thinking in an incorrect way to make this work. One of my exposed filters searches using "Content:Has Taxonomy Term", I use the dropdown not autofill. With that created, I pull up my settings for better exposed filters. I expose my filter as checkboxes. the checkboxes that the Better Exposed Filters creates adds a number value to each of the checkboxes, not the taxonomy term name. Is there a way I can manipulate the output of the Better Exposed filter to show the name as the value, not id?

1. Here I create add a filter/filter criteria. you can see on the left I added a Content Has Taxonomy term (exposed)
2. Here you can see the criteria. I have clicked the "expose the filter to visitors" to allow the visitor to select how to filter. I click a couple options in the select list to show different predetermined options. and not shown, I select the option "limit list to selected items"
3. from the highlighted 1st image above, shows settings for Better Exposed Filters. I click the setting option, and for the display of that criteria, and for that filter, display filter as checkboxes.
4. After that, I can preview my checkboxes here.
5. If you inspect each of those checkboxes, you can see the value assigned to the checkboxes, is a number.

I believe it to be the Taxonomy Term ID?...

6. In this next screenshot, you can see the value from the checkbox represented as the same id.

This is where I get stuck... I want to show the term as the taxonomy term name, not id.

thanks in advance for your help!

# Answer
> 1 votes
This has nothing to do with Exposed filter settings, it's to do with what information is stored in the Taxonomy term Entity Reference field on your Content Type, and it's the term ID, not the term name.

So when you add the most ready-available Taxonomy Fields/Filters to your **Content** Views you will get the term ID.

Other Taxonomy term information (term name included) is stored in another database table and you have to add the Relationship for that particular field in order to pull information from that table into Views.

What you want is to add the Relationship for the Field on your content that stores the Taxonomy term reference. You can require the Relationship if you only want to see nodes in results where that field isn't empty.

After you add that Relationship you will get more options under Fields and Filters, one of those options is the Taxonomy term **Name**. They will use the Relationship by default since they can't be added to the Views without it.

Add the **Name** Filter to your Views and expose it. Adjust the Vocabulary settings and other options to your particular need.

---
Tags: taxonomy-terms
---


title: Remove reply link in comments

text:
# Question
Title: Remove reply link in comments
How can you remove the reply button under each comment in Drupal 8? I understand there is an option "Show reply form on the same page as comments", but I am not trying to remove this form. Only the reply button.

How can the reply button be removed?

# Answer
You could use the `hook_comment_links_alter()`

```
use Drupal\comment\CommentInterface;

/**
 * Implements hook_comment_links_alter().
 *
 * Unsets the "Reply" link of a comment.
 *
 * @param array &$links
 *   A renderable array representing the comment links.
 * @param \Drupal\comment\CommentInterface $entity
 *   The comment being rendered.
 * @param array &$context
 *   Various aspects of the context in which the comment links are going to be
 *   displayed, with the following keys:
 *   - 'view_mode': the view mode in which the comment is being viewed
 *   - 'langcode': the language in which the comment is being viewed
 *   - 'commented_entity': the entity to which the comment is attached
 */
function mymodule_comment_links_alter(array &$links, CommentInterface $entity, array &$context) {
  // Unset the "Reply" link
  if(isset($links['comment']['#links']['comment-reply'])) {
    unset($links['comment']['#links']['comment-reply']);
  }
}

```
> 3 votes

# Answer
Go to Structure \>\> Comment types and under Manage display, move "Links" to Disabled section.

Save and the Reply link for each comment will disappear.
> 0 votes

# Answer
The forms in Drupal 8 are entities, through the administration of the form you can manipulate the fields and in the presentation of the type of content you can show it, the button can be deleted from the entity of the comments.
> -1 votes

---
Tags: 8, comments
---


# Question
Title: How can I add a CC field to webform email send?
Does anyone have a solution to adding a CC field to the `/node/[nid]/webform/emails/xxx` interface?

I have a client that wants to use a decently complex conditional email send solution in Drupal 7 webform, where based on various select lists, it will send an email to two places.

1. "Real\_Person@domain.com"
2. "Ticket\_System@domain.com"

Allowing a real person to be notified and respond via REPLY-ALL to the email, and having the ticket system be updated with the communication in real time.

The ticket system can't handle getting a reply-to with both addresses in the single "TO:" line.

`to: Real_Person@domain.com,Ticket_System@domain.com` breaks the system.

Has anyone solved this? built a module? Have guidance on how to solve?

Thank you!

# Answer
I have done some similar thing to send the email to another email-address. You can use the following solution for changing the content of the email or to add an attachment or things like this...

```
/**
 * Implements hook_mail_alter().
 */
function MYMODULE_mail_alter(&$message) {
  // Check message id
  if ($message['id'] == '...') {
    $email_key = 'new_email_key_if_you_want';

    // Block email which is sending right now.
    $message['send'] = FALSE;

    // send original email
    drupal_mail('MYMODULE', $email_key, $message['to'], language_default(), $message['params'], $message['from']);

    // send copy of the email.
    drupal_mail('MYMODULE', $email_key, 'ticket_system@domain.com', language_default(), $message['params'], $message['from']);
  }
}

```

You can add this in an own module, in the `MYMODULE.module`.
> 0 votes

# Answer
I have done this by adding the below code to my custom module. Use hook\_mail\_alter().

```
function mymodule_mail_alter(&$message) {
   if ($message['id'] == 'webform_submission') {
     if ($message['params']['node']->nid == 'webform_node_id') {
    $message['headers']['CC'] = 'yourccmailaddress@gmail.com';
     }
   }
 }

```
> 0 votes

---
Tags: 7, webforms, emails
---


title: Custom token not being replaced

text:
# Question
Title: Custom token not being replaced
In my\_module I have the following:

```
function my_module_token_info()
{
  $info = [];
  $info['types']['my_module_tokens'] = [
      'name' => 'My Module Tokens',
      'description' => t('Tokens specific to my module.'),
  ];
  $info['tokens']['my_module_tokens']['my_token'][] = 'For my token in a node body';

  return $info;
}

function my_module_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
  $replacements = [];

  if ($type == 'my_module_tokens') {
    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'my_token':
          $replacements[$original] = 'test';
          break;

        default:
          break;
      }
    }
  }

  return $replacements;
}

```

In a node body I have \[my\_module\_tokens:my\_token\]

Now, the token info is found and processed, and appears in the token list. The implementation of hook\_tokens does not fire unless I add the following:

```
function my_module_preprocess_node(&$variables) {
  $token = Drupal::token();
  $token->replace("[my_module_tokens:my_token]", array('node'=>$variables['node']), array('clear' => TRUE));
  }
}

```

The token information and replacement text are sent off to the tokens logic, but the result is that the token in the body text is unchanged.

# Answer
You need to add a way to process the token. The Token Filter module provides a ckeditor filter plugin to do this when the field is rendered.

From the module's README:

> Install the module as any other module. Visit the text format administration page at /admin/config/content/formats/filters and edit a text format. Check the 'Replaces global tokens with their value' filter and save the text format.

> When editing a form where this text format is used in a field, you can type global tokens that will be replaced when the field is rendered.
> 1 votes

# Answer
Ah, got it. I had my brain in backwards. Token replacement is on node save, not when it's rendered. Calling token replace in hook\_node\_presave did it.
> -2 votes

---
Tags: tokens, 9
---


# Question
Title: Why is config for my view going to config/local?
We use config\_split to slice off parts of the site config to other config folders (local, dev, stage, etc). This more or less works as expected.

I have one view which always exports to config/local even though there is nothing I can see in the config\_split configuration which is suggesting it do this.

Any ideas why it fails to export to the correct folder (config/sync)?

# Answer
As is usually the case, 30 min after posting I figure it out.

My issue was caused by the "Include dependent configuration" option being enabled for the split configuration.
> 2 votes

---
Tags: 8
---


# Question
Title: How can I get hold of the Composer object?
I'm trying to get the file location of something in an installed package.

https://stackoverflow.com/questions/15714809/get-filesystem-path-of-installed-composer-package says I can get that from the Composer object.

But in Drupal, how do I get the Composer object?

# Answer
> 1 votes
Usually, I'm doing this using ReflectionClass.

Take this example: to load the `cacert file` which comes inside one of composer packages:

* First, locate **any** class in that package using ReflectionClass.
* Secondly, get the package path using `dirname` bypassing the file name.

Code example:

```
// Load certificate file.
$reflection = new \ReflectionClass(\GSRequest::class);
$packagePath = dirname($reflection->getFileName());
$gsRequest::setCAFile(realpath($packagePath . '/cacert.pem'));

```

---
Tags: composer
---


# Question
Title: 0% of the site has been indexed. There are 400 items left to index
Drupal core search doesn't re-index the site and it always shows this message.

> 0% of the site has been indexed. There are 453 items left to index.

What settings do I need to change to make Drupal re-index the site properly?

# Answer
You need to run cron. Because building the search engine can take time, Drupal does it on cron runs (the scheduling system).

First, to make sure everything is OK, run cron manually from `/admin/config/system/cron` to ensure that the search items get indexed.

Then set up cron to run automatically following the instructions in the documentation.
> 1 votes

---
Tags: search
---


# Question
Title: Webform POST handler, add API/header creds?
Using the Remote POST handler in Webform 5, is there a way to add basic auth / API key to the request leveraging tokens or referencing a const? I'd like to find a way not to hardcode actual values.

# Answer
Just adding an answer in addition to @Ryan Hartman's comment. Extend the `RemotePostWebformHandler` class and its `::buildConfigurationForm()` method:

```
/**
 * Custom Webform submission remote post handler.
 *
 * @WebformHandler(
 *   id = "custom_remote_post",
 *   ...
 * )
 */
class CustomRemotePostWebformHandler extends RemotePostWebformHandler {

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
    $form = parent::buildConfigurationForm($form, $form_state);

    $form['api_key'] = [
      '#type' => 'textfield',
      '#title' => $this->t('API Key'),
      '#description' => $this->t('The API key for basic auth.'),
      '#default_value' => $this->configuration['api_key'] ?? NULL,
    ];

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
    parent::submitConfigurationForm($form, $form_state);

    $this->configuration['api_key'] = $form_state->getValue('api_key') ?: NULL;
  }

  ...

```

Alternatively implement the form element provided by the Key module so you can securely store sensitive secret keys.
> 1 votes

---
Tags: 8, webforms
---


# Question
Title: Bearer Token in url is not parsed by route
I Installed Drupal 9.1.0 on a VPS running on Ubuntu 20.4 and using Nginx 1.18.0, with Composer 2.0.8.

I installed two modules (Token and Devel) and a theme (Barrio) using Composer. I can enable the modules without problem, but when I try to install Barrio and set it as default theme, I get an access denied error.

I tried to enable other themes, but I always get the same error.

I cleared the cache and installed the theme with Drush. Still, setting the theme as default one causes an error. In this case, the error in the log is the following one.

```
Path: /admin/appearance/default?theme=THEME_NAME&token=TOKEN. Drupal\Core\Http\Exception\CacheableAccessDeniedHttpException: 'csrf_token' URL query argument is missing. in Drupal\Core\Routing\AccessAwareRouter->checkAccess() (line 117 of /PATHTOSITE/core/lib/Drupal/Core/Routing/AccessAwareRouter.php).

```

...:: UPDATE ::...

I used to start debugging to check where exactly the problem happens and realized that in line 57 of /Drupal/Core/Access/CsrfAccessCheck.php where code says :

$request-\>query-\>get('token', '')

there results become '' , which means $request-\>query-\>token is empty. debugging backward I looked at many files including :

```
Drupal/Core/Access/AccessArgumentsResolverFactory.php where it produces $request for csrfCheck
Drupal/Component/Utility/ArgumentsResolver.php which is used in above file

```

and finally I got to the place where problem is started:

Drupal/Core/Routing/RouteMatch::createFromRequest

this method can not recognize the token in url and cause all other problems, but I still don't know what cause this method failure

could it be my Nginx configurations?

Nginx config is:

location / { try\_files $uri $uri/ /index.php?query\_string; }

One more thing , when I use dsm on request, token can be find in $request-\>requestUri (is shown as /admin/appearance/install?theme=stark&token=qI5\_B99oNr4TWdNfu5aTdd01NezSpPKOZje8HCVOMEs), But $rquest-\>query\["query\_string"\] is ""

# Answer
> 0 votes
Finally after spending few days doing debugging and searching I noticed that the error was due to my Nginx configuration , this configuration from Nginx website was what solved the problem for me:

```
server {
    server_name example.com;
    root /var/www/drupal/MYSITE/web;

    location = /favicon.ico {
      log_not_found off;
      access_log off;
    }

    location = /robots.txt {
      allow all;
      log_not_found off;
      access_log off;
    }

    # Very rarely should these ever be accessed outside of your lan
    location ~* \.(txt|log)$ {
      allow 192.168.0.0/16;
      deny all;
    }

    location ~ \..*/.*\.php$ {
      return 403;
    }

    location ~ ^/sites/.*/private/ {
      return 403;
    }

    # Block access to scripts in site files directory
    location ~ ^/sites/[^/]+/files/.*\.php$ {
      deny all;
    }

    # Allow "Well-Known URIs" as per RFC 5785
    location ~* ^/.well-known/ {
      allow all;
    }

    # Block access to "hidden" files and directories whose names begin with a
    # period. This includes directories used by version control systems such
    # as Subversion or Git to store control files.
    location ~ (^|/)\. {
      return 403;
    }

    location / {
      try_files $uri /index.php?$query_string;
    }

    location @rewrite {
      rewrite ^ /index.php;
    }

    # Don't allow direct access to PHP files in the vendor directory.
    location ~ /vendor/.*\.php$ {
      deny all;
      return 404;
    }

    # Protect files and directories from prying eyes.
    location ~* \.
    (engine|inc|install|make|module|profile|po|sh|.*sql|theme|twig|tpl(\.php)?
    |xtmpl|yml)(~|\.sw[op]|\.bak|\.orig|\.save)?$|/(\.(?!well-
    known).*|Entries.*|Repository|Root|Tag|Template|composer\.
    (json|lock)|web\.config)$|/#.*#$|\.php(~|\.sw[op]|\.bak|\.orig|\.save)$ {
      deny all;
      return 404;
    }

    # In Drupal 8, we must also match new paths where the '.php' appears in
    # the middle, such as update.php/selection. The rule we use is strict,
    # and only allows this pattern with the update.php front controller.
    # This allows legacy path aliases in the form of
    # blog/index.php/legacy-path to continue to route to Drupal nodes. If
    # you do not have any paths like that, then you might prefer to use a
    # laxer rule, such as:
    #   location ~ \.php(/|$) {
    # The laxer rule will continue to work if Drupal uses this new URL
    # pattern with front controllers other than update.php in a future
    # release.
    location ~ '\.php$|^/update.php' {
      fastcgi_split_path_info ^(.+?\.php)(|/.*)$;
      # Ensure the php file exists. Mitigates CVE-2019-11043
      try_files $fastcgi_script_name =404;
      # Security note: If you're running a version of PHP older than the
      # latest 5.3, you should have "cgi.fix_pathinfo = 0;" in php.ini.
      # See http://serverfault.com/q/627903/94922 for details.
      include fastcgi_params;
      # Block httpoxy attacks. See https://httpoxy.org/.
      fastcgi_param HTTP_PROXY "";
      fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
      fastcgi_param PATH_INFO $fastcgi_path_info;
      fastcgi_param QUERY_STRING $query_string;
      fastcgi_intercept_errors on;
      # PHP 5 socket location.
      #fastcgi_pass unix:/var/run/php5-fpm.sock;
      # PHP 7 socket location.
      fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
    }

    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
      try_files $uri @rewrite;
      expires max;
      log_not_found off;
    }

    # Fighting with Styles? This little gem is amazing.
    location ~ ^/sites/.*/files/styles/ {
      try_files $uri @rewrite;
    }

    # Handle private files through Drupal. Private file's path can come
    # with a language prefix.
    location ~ ^(/[a-z\-]+)?/system/files/ {
      try_files $uri /index.php?$query_string;
    }

    # Enforce clean URLs
    # Removes index.php from urls like www.example.com/index.php/my-page -->
    www.example.com/my-page
    # Could be done with 301 for permanent or other redirect codes.
    if ($request_uri ~* "^(.*/)index\.php/(.*)") {
      return 307 $1$2;
    }
}

```

---
Tags: 9, routes
---


title: How to render full profile2?

text:
# Question
Title: How to render full profile2?
I am tring to load a full profile2.

I a trying the following code

```
<?php
$uid = user_load($node->uid);
$profile2_p = profile2_load_by_user($uid, 'profile2_profile');
print render($profile2_p);
?>

```

I get an error saying object can't be used as array.

If I print\_r $profile2\_p then I get an array relating to the correct profile2 entity. How can I render this entity?

Thanks

# Answer
> 2 votes
Looks like you can get a renderable array using profile2\_view():

```
/**
 * View a profile.
 *
 * @see Profile::view()
 */
function profile2_view($profile, $view_mode = 'full', $langcode = NULL, $page = NULL) {
  return $profile->view($view_mode, $langcode, $page);
}

```

https://git.drupalcode.org/project/profile2/-/blob/7.x-1.x/profile2.module#L1084

See also that modules hook\_user\_view() which does the same:

https://git.drupalcode.org/project/profile2/-/blob/7.x-1.x/profile2.module#L399

```
/**
 * Implements hook_user_view().
 */
function profile2_user_view($account, $view_mode, $langcode) {
  foreach (profile2_get_types() as $type => $profile_type) {
    if ($profile_type->userView && $profile = profile2_load_by_user($account, $type)) {
      if (profile2_access('view', $profile)) {
        $account->content['profile_' . $type] = array(
          '#type' => 'user_profile_category',
          '#title' => $profile_type->getTranslation('label'),
          '#prefix' => '<a id="profile-' . $profile->type . '"></a>',
        );
        $account->content['profile_' . $type]['view'] = $profile->view($view_mode);
      }
    }
  }
}

```

With a single entity in your code, you can:

```
render($profile2_p->view($some_view_mode));

```

---
Tags: 7, entities
---


title: How do I update from 9.0.10 to 9.1.0?

text:
# Question
Title: How do I update from 9.0.10 to 9.1.0?
I have tried to update drupal 9.0.10 to 9.1 with composer but I cannot:

```
composer outdated 'drupal/*'
 drupal/core             9.0.10 **9.1.0** Drupal is an open source content manage...
 drupal/core-recommended 9.0.10 **9.1.0** Locked core dependencies; require this ...

```

I have executed "composer update drupal/core-recommended --with-all-dependencies" and this is the result

```
loading composer repositories with package information
Updating dependencies
Nothing to modify in lock file
Installing dependencies from lock file (including require-dev)
Nothing to install, update or remove
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.
Generating autoload files

```

This is my composer.json

```
{
    "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.9",
        "drupal/bootstrap": "^3.23",
        "drupal/core-composer-scaffold": "^9",
        "drupal/core-project-message": "^9",
        "drupal/core-recommended": "^9",
        "drupal/metatag": "^1.13",
        "drupal/migrate_plus": "^5.1",
        "drupal/migrate_tools": "^4.5|^5",
        "drupal/migrate_upgrade": "^3.2",
        "drupal/pathauto": "^1.8",
        "drupal/redirect": "^1.6",
        "drush/drush": "^10.3"
    },
    "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"
            ]
        }
    }
}

```

# Answer
> 4 votes
With version 9.1.0 (release notes) Drupal Composer templates no longer use minimum-stability `dev` as in the past this occasionally produced unexpected results, such as upgrading to newer unstable releases. This is what's currently happening to you.

Raised minimum stability will not affect existing projects; this has to be done manually. For production websites `stable` is recommended.

So in you composer.json replace `"minimum-stability": "dev"` with `"minimum-stability": "stable"` and then simply require core again: `composer require drupal/core-recommended`. This will give you the latest stable release.

From then on for updating core simply run `composer update drupal/core 'drupal/core-*' --with-all-dependencies` as recommended under Updating Drupal core via Composer.

---
Tags: composer, upgrading
---


title: add a link to admin menu bar

text:
# Question
Title: add a link to admin menu bar
I want to add the link to tags i.e. /admin/structure/taxonomy/tags directly to admin menu so that it appears next to "Content" link present on admin menu. How to do it?

# Answer
You can achieve using below step:

* Go to Administration -\> Structure -\> Menus
* Click on 'Add links' in 'Management' menu row
* Add 'Menu link title' and 'Path' for menu item.
* Set 'Parent link' for the menu item, so it would display in correct position.
* Save the form

Update:

By default, `Management menu` be only dispaly to `administrator` or the user which has permission to view it.

If you want to set any specific visibility permission, you can use Menu item visibility module.

> This module exposes configurable and extendable visibility settings for menu links. For example, if you want to create a 'My account' link that points to /user, both anonymous and registered users have access to the /user path, so both will see the link, even if anonymous users do not technically have an account. Using this module will allow you to only have your 'My account' link visible to registered users, and hide it from anonymous users.
> 15 votes

# Answer
In drupal 8 here is how you would attach a menu-link to the toolbar. Add a file my\_module.links.menu.yml to your module:

```
my_module.admin_settings_form:
  title: 'MyModule Settings'
  route_name: my_module.admin_settings_form
  description: 'Custom settings'
  parent: system.admin
  weight: 15

```

Source: https://stefvanlooveren.me/blog/add-menu-item-drupal-toolbar
> 4 votes

# Answer
One thing I would like to add to this, make sure to go in Taxonomy page and drag the item all the way to the bottom, the hierarchy can be confusing, and it won't show even if you choose --administration as parent item
> 0 votes

---
Tags: navigation
---


# Question
Title: How can I save multiple (checkboxes) values in a configuration entity?
I have a standard config entity in Drupal 8. I'm trying to save multiple values in the form of checkboxes.

The form itself looks like this:

As you can see it's nothing too special about it.

If I try to save it, the following exception occurs:

```
InvalidArgumentException: The configuration property user_role.anonymous doesn't exist. in Drupal\Core\Config\Schema\ArrayElement->get() (line 74 of core/lib/Drupal/Core/Config/Schema/ArrayElement.php).

Drupal\Core\Config\StorableConfigBase->castValue('user_role.anonymous', 'anonymous') (Line: 211)
Drupal\Core\Config\StorableConfigBase->castValue('user_role', Array) (Line: 212)
Drupal\Core\Config\Config->save() (Line: 280)
Drupal\Core\Config\Entity\ConfigEntityStorage->doSave('pl_n_6467f20669ddf097337c47aab87e3c', Object) (Line: 392)
Drupal\Core\Entity\EntityStorageBase->save(Object) (Line: 259)
Drupal\Core\Config\Entity\ConfigEntityStorage->save(Object) (Line: 364)
Drupal\Core\Entity\Entity->save() (Line: 637)
Drupal\Core\Config\Entity\ConfigEntityBase->save() (Line: 130)
Drupal\pl_notifications\Form\PLNotificationsConfigurationForm->save(Array, Object)
call_user_func_array(Array, Array) (Line: 111)
Drupal\Core\Form\FormSubmitter->executeSubmitHandlers(Array, Object) (Line: 51)
Drupal\Core\Form\FormSubmitter->doSubmitForm(Array, Object) (Line: 585)
Drupal\Core\Form\FormBuilder->processForm('p_l_notifications_configuration_add_form', Array, Object) (Line: 314)
Drupal\Core\Form\FormBuilder->buildForm('p_l_notifications_configuration_add_form', Object) (Line: 74)
Drupal\Core\Controller\FormController->getContentResult(Object, Object)
call_user_func_array(Array, Array) (Line: 123)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 574)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 124)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array) (Line: 97)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
call_user_func_array(Object, Array) (Line: 144)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 64)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 57)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 99)
Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 78)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 50)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 656)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)

```

Which makes perfect sense since I haven't defined that field in the `ConfigEntityType`.

However, I can't define it since there may be other roles added later on or rather some other roles removed.

# Answer
> 4 votes
Config (schema) defines two basic types of complex/array definitions.

Mapping: When you have limited, known set of keys, each with a specific type, e.g. a few strings, integers, booleans and so on.

Sequence: When you have a unlimited list of values that all have the same type but with unknown or numeric keys. Each element must have the same type. The top level in a config entity and simple config is always a mapping (the type there is a config\_object or config\_entity or so, but those all extend from mapping)

In your example, you would define the user roles as type sequence in config schema, and as a standard php array in your config entity class.

You can also combine this in any way you can imagine, you have a mapping, one key is a list, each element is a mapping, and so on.

# Answer
> 1 votes
In case anybody has this problem too.

I ended up serializing the values and saving them as a single field.

I don't know if that is the best approach for it but it works for now.

# Answer
> 1 votes
I just ran into the same situation with a similar error. The correct resolution for this problem is, that you have to **create a config/schema/YOURMODULE.schema.yml file for (especially!) multi-value settings first**.

Drupal will otherwise fall back to a "wrong" way of storing these settings if it doesn't know how to handle them. Other scalar settings seem to be handled with a better fallback if a schema file doesn't (yet) exist.

Typically I created the schema file after I had successfully implemented the module, that's not possible in this case.

Here's my code example for a filter with a "checkboxes" setting:

```
@Filter(
 *   id = "cookie_content_blocker_extras_element_filter",
 *   title = @Translation("Cookie content blocker filter: Wrap elements automatically"),
 *   description = @Translation("Place this filter before 'Cookie content blocker filter'. Wraps selected HTML elements with external content like iframes, embed, object, link, img in <cookiecontentblocker> automatically."),
 *   type = Drupal\filter\Plugin\FilterInterface::TYPE_MARKUP_LANGUAGE,
 *   settings = {
 *     "handle_dom_elements" = {},
 *     "handle_dom_elements_exclude_classes" = "no-cookie-content-blocker",
 *     "src_allowed_local" = 1,
 *     "src_allowed_whitelist" = "",
 *   },
 * )

```

*/config/schema/cookie\_content\_blocker\_extras\_element\_filter.schema.yml*:

```
filter_settings.cookie_content_blocker_extras_element_filter:
  type: filter
  label: 'Cookie content blocker element filter'
  mapping:
    handle_dom_elements:
      type: sequence
      label: 'HTML element types to auto-block'
      sequence:
        type: string
        label: 'HTML element type'
    handle_dom_elements_exclude_classes:
      type: string
      label: 'Exclude elements by class'
    src_allowed_whitelist:
      type: string
      label: 'Whitelist external sources (Domains)'
    src_allowed_local:
      type: boolean
      label: 'Whitelist local source'

```

After clearing the cache with this schema.yml the error is gone. Horrible story :D

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


# Question
Title: How can I compare a view token with a string?
I have a view token {{ field\_category }} == Content: Category I need a condition like if field category == "Foo & Bar" do something else do something

I wrote code

```
         {% if field_category == "Foo & Bar" %}

             string1
         {% else %}

            string 2

        {% endif %}

```

But it always goes for the else part even if the token value is "Foo & Bar"

Please help to find a solution

# Answer
> But it always goes for the else part even if the token value is "Foo & Bar"

This is because by default Views adds classes to your field:

Which means `field_category` is actually equal to `<div class="some-class">Foo & Bar</div>`

and since `<div class="some-class">Foo & Bar</div>` is not equal to `Foo & Bar` your conditional always goes to the else.

If you uncheck the "Add default classes" in your category field settings, your conditional should work now. Unless... your field is linking to something so it would still be `<a href="/some-url">Foo & Bar</a>` and not equal.

As 4k4 commented, you can instead use `field_category__value` to get the raw value and not have to uncheck the "Add default classes".
> 3 votes

---
Tags: 8
---


title: How can I achieve this functionality?

text:
# Question
Title: How can I achieve this functionality?
I am a new vibe drupal developer and I have created content type as an event which displays on home page upcoming event as blogs i have created a simple blog event using view home page but I don't know how to achieve these using view or other modules. plz correct me if i am wrong thanks in advance

Expected result when a user clicks these of any event it displays image and title description main square

| image | event title |
| --- | --- |
| One | event description |

title: 1 jan(Date)
description:event description

title: 2 jan(Date)
description:event description

title: 3 jan(Date)
description:event description

# Answer
I assume you have already added the image and event description, etc. as fields for the event content type.

For custom content types, normally you control the display using the **Manage Display** link from Structure -\> Content Types -\> Edit.

You can rearrange the fields how you want in the UI, and you can also use a Twig theme template to customize the specific HTML. Core also provides the Layout Builder module to make something more complex.

It's also possible to use Views to overwrite the default content page, but it is usually easier to use Twig templates and/or Layout Builder. If you do want to use Views, take a look at the core Taxonomy Term display to see how Views can be used to override a path for a custom content type.
> 0 votes

---
Tags: 8, views
---


# Question
Title: How can I redirect user, while showing them a status message?
I'm working on a custom module, in which I want to redirect users, while showing them a status message. What I've tried so far only works for authenticated users but I want to redirect anonymous users. I've tried the below solutions so far. What else can I do?

```
\Drupal::messenger()->addStatus($config->get('mymessage');
$redirect = new \Symfony\Component\HttpFoundation\RedirectResponse($login->toString());
$redirect->send();

```

```
$request = \Drupal::request();
$session = $request->getSession();
$session->set('msg', $config->get('girisyapmesaji'));
$session->save();
\Drupal::messenger()->addStatus($session->get('msg'));
$redirect = new RedirectResponse($login->toString());
$redirect->send();

```

# Answer
> 2 votes
In general, never send responses from custom code. See How do I set a cookie using $response-\>headers-\>setCookie() in a form submission handler?

In this case you are sending a redirect response to the client while Drupal builds the real response containing the cookie to the session where the message gets stored. The real response never reaches the client because you've already sent them away.

If you don't have a controller to return the redirect you can use an event subscriber to set the response the Drupal kernel returns. See this code example https://drupal.stackexchange.com/a/274494/47547.

In your case you would set a status message instead of a custom cookie:

*/src/EventSubscriber/MymoduleSubscriber.php*

```
<?php

namespace Drupal\mymodule\EventSubscriber;

use Drupal\Core\Routing\RouteMatchInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpFoundation\RedirectResponse;

/**
 * mymodule event subscriber.
 */
class MymoduleSubscriber implements EventSubscriberInterface {

  /**
   * The route match.
   *
   * @var \Drupal\Core\Routing\RouteMatchInterface
   */
  protected $routeMatch;

  /**
   * Constructs event subscriber.
   *
   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
   *   The route match.
   */
  public function __construct(RouteMatchInterface $route_match) {
    $this->routeMatch = $route_match;
  }

  /**
   * Kernel request event handler.
   *
   * @param \Symfony\Component\HttpKernel\Event\RequestEvent $event
   *   The request event.
   */
  public function onKernelRequest(RequestEvent $event) {
    if (!$event->isMainRequest()) {
      return;
    }
    if (!$this->routeMatch->getRouteName() == 'entity.node.canonical') {
      return;
    }
    $request = $event->getRequest();
    if ($this->routeMatch->getRawParameter('node') == '1') {
      \Drupal::service('page_cache_kill_switch')->trigger();
      if ($some_condition) {
        \Drupal::messenger()->addStatus('My message');
        $redirect = new RedirectResponse('/foo/bar', 302);
        $event->setResponse($redirect);
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    return [
      KernelEvents::REQUEST => ['onKernelRequest', 31],
    ];
  }

}

```

*mymodule.services.yml*

```
services:
  mymodule.event_subscriber:
    class: Drupal\mymodule\EventSubscriber\MymoduleSubscriber
    arguments: ['@current_route_match']
    tags:
      - { name: event_subscriber }

```

---
Tags: 8, redirection
---


title: Reuse fields programmatically

text:
# Question
Title: Reuse fields programmatically
Conceptually and for performance purposes it is advisable to reuse existing fields in Drupal. This is very easy to do manually, but my question is, how can I do this programmatically? I have the following code:

```
$all_bundle_fields = \Drupal::service('entity_field.manager')->getFieldDefinitions('taxonomy_term', $vid);

if (!isset($all_bundle_fields['field_image'])) {
  FieldStorageConfig::create(array(
    'field_name' => 'field_image',
    'entity_type' => 'taxonomy_term',
    'type' => 'entity_reference',
    'settings' => [
      'target_type' => 'media',
    ],
    'cardinality' => 1
  ))->save();

  FieldConfig::create(array(
    'field_name' => 'field_image',
    'entity_type' => 'taxonomy_term',
    'bundle' => $vid,
    'label' => 'Logo',
    'description' => 'Logo to represent the term.',
    'required' => FALSE,
  ))->save();
}

```

In this example, I extract all the fields from the vocabulary and only create the field if and only if it does not exist previously in this vocabulary. The above code works.

What does not work here is if that field has already been created for another entity or vocabulary. This is what I would like to find out in order to make a complete reuse of the fields.

Thanks in advance.

# Answer
> 0 votes
Until someone tells me how an existing field can be reused I have got a remedy. As my "need" was to reuse fields that I have been creating in various vocabularies, what I have done is the following:

1. Create a function that goes through all the existing vocabularies and checks if a certain field, passed by parameter, exists.
2. If so, it returns me the name of the vocabulary where it already exists. This saves me from creating the corresponding database table.
3. Then I check if the vocabulary where the field exists is my own vocabulary. In case it is not, I link it.

This would be the final result. First, the function:

```
function existsField($field) {
  $vocabularies = taxonomy_vocabulary_get_names();

  foreach($vocabularies as $vocabulary) {
     $all_bundle_fields = \Drupal::service('entity_field.manager')->getFieldDefinitions('taxonomy_term', $vocabulary);
     if (isset($all_bundle_fields[$field])) {
        return $vocabulary;
     }
  }
}

```

Next, the final code:

```
$vocabulary = existsField('field_image');

if (empty($vocabulary)) {
  FieldStorageConfig::create(array(
     'field_name' => 'field_image',
     'entity_type' => 'taxonomy_term',
     'type' => 'entity_reference',
     'settings' => [
        'target_type' => 'media',
     ],
     'cardinality' => 1
  ))->save();
}

if (empty($vocabulary) || $vocabulary != $vid) {
  FieldConfig::create(array(
     'field_name' => 'field_image',
     'entity_type' => 'taxonomy_term',
     'bundle' => $vid,
     'label' => 'Logo',
     'description' => 'Logo to represent the term.',
     'required' => FALSE,
  ))->save();
}

```

---
Tags: taxonomy-terms
---


# Question
Title: Webform default validation message altering
I am working with drupal8 site.Here i want to override the default "The email address %mail is not valid" email format validation message.I have checked with hook\_form\_alter to implement this.But no result.Is it possible to alter this message?

# Answer
> 1 votes
For Drupal 8, Webform uses Drupal's default Email Form Element. You'll have to override this element info and add your own validation message there.

See https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Render%21theme.api.php/function/hook\_element\_info\_alter/8.5.x

# Answer
> 0 votes
I would prefer creating a new webform handler which has custom validation for a specific field instead of using form alter.

I have created a gist for you place it to the correct folder (mymoduleName/src/Plugin/WebformHandler) clear cache and than attach the webform handler to your webform.

https://gist.github.com/benzo11/9e947c3732a5c90c749f8b8842c0baa3

Second is, that you create a new webform element which extends WebformEmailMultiple and overriding validateWebformEmailMultiple function(you can just copy paste it and change the text). But you will have to add a new webform element in webform.

I hope it can help you!

---
Tags: 8, webforms
---


# Question
Title: How to display configuration updates when deleting fields programatically
I am working on a module that adds it's needed fields automatically when you enable **it's function** in the UI for specific entity types. It also deletes it's needed fields when you disable it's function for an entity type. (I'm not talking about when you enable the module)

We are doing this with the following on a checkbox form that displays the entity types that can have the function:

```
foreach ($values['product_variations'] as $key => $value) {
  foreach ($neededFields as $neededField => $data) {
    $field_storage = FieldStorageConfig::loadByName('commerce_product_variation', $neededField);
    if (!$field_storage) {
      FieldStorageConfig::create([
        'entity_type' => 'commerce_product_variation',
        'field_name' => $neededField,
        'type' => $data['field_type'],
        'settings' => $data['settings'],
        'cardinality' => $data['cardinality'],
      ])->save();
    }
    $field = FieldConfig::loadByName('commerce_product_variation', $key, $neededField);
    if (!$value) {
      if ($field) {
        $field->delete();
      }
      continue;
    }

```

This works fine, except that (from what I understand) Fields API automatically deletes the FieldStorageConfig when the last instance of a field is deleted. This would not be a big deal except when the FieldStorageConfig is deleted programmatically, any view that has these enabled fields will be deleted along with it. Whereas if you delete the fields manually though the UI, the view will just be disabled as this was added in this issue.

Even if I were to address this behavior when deleting programmatically, I would still like to display the message when there are changes to the configuration as we are still removing field instances which can result in the loss of data.

> **Are you sure you want to delete the field?**
> This action cannot be undone.

> **Configuration Updates**
> The listed configuration will be updated:

Is there an EventSubscriber to display this message, or would I need to copy the source code for this and add it to my module?

# Answer
> 1 votes
As discussed in the comments the solution, in most cases, is to add the field storage to the config/install folder of the module specifying `persist_with_no_fields: true`.

*core/modules/block\_content/config/install/field.storage.block\_content.body.yml*

```
langcode: en
status: true
dependencies:
  module:
    - block_content
    - text
id: block_content.body
field_name: body
entity_type: block_content
type: text_with_summary
settings: {  }
module: text
locked: false
cardinality: 1
translatable: true
indexes: {  }
persist_with_no_fields: true
custom_storage: false

```

Then the configuration updates will be displayed when the module is uninstalled.

Sometimes the dependencies on the module disappear after configuration saves because Drupal is optimizing duplicate dependencies. Then enforce the dependency for this configuration item. See How to properly manage field types for a content type created by a module?

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


# Question
Title: How can I remove self-closing source tags when rendering HTML markup?
I use this markup in a node body.

```
<video>
  <source src="myvideo.mp4" type="video/mp4" />
  my title
</video>

```

The following markup is instead rendered in the node view.

```
<video>
  <source src="myvideo.mp4" type="video/mp4" />
    my title
  </source>
</video>

```

`</ source>` is added to the markup, and renders the full HTML not valid.

How can I avoid `</ source>` is added?

# Answer
> -1 votes
I use this code for replace \< /source\> in theme

```
  $content_this =  render($content['body']);
  print str_replace("</source>","",$content_this);

```

in node.tpl.php

---
Tags: 7, nodes
---


# Question
Title: How do you transfer ownership of a Drupal contrib module to someone else?
I own the S3FS and Date iCal Drupal contrib modules, though I abandoned development on them quite a while ago, due to moving all my work to another platform. However, I am still on the record as the "owner" of these modules, despite there being other maintainers who have taken over.

There's no obvious way to remove myself as owner. I can't uncheck any of my permissions from the Maintainers listing. There doesn't appear to be any UI that lets me designate an existing maintainer as the new owner.

What do I do?

# Answer
> 0 votes
You can either:

* Open an issue on the Drupal.org project ownership queue, asking to make the new project owner a user on drupal.org that you know be willing to become the new project owner
* Open an issue on the same queue, asking to make the new project owner the maintainer who did more commits or who was more active in following and answering the project issues
* Open an issue in the same queue, stating you are seeking for a new project owner, using *Needs maintainers* as *Component* value

Once you open the issue queue, the procedure will be handled by Drupal.org site moderators.

As alternative, you could simply set the *Maintenance status* field of the project to *Seeking new maintainer* and (eventually) the *Development status* to *No further development*. Users who need the project, and who are able to develop code for a project, seeing you set that status, could ask to be the new project owner or maintainer, if they are interested.

Finally, if you think you won't maintain anymore a project hosted on drupal.org and you won't use Drupal, you could delete your account on drupal.org. Authenticated user are now able to delete their account on drupal.org, for any reason they could have.

---
Tags: community-official-site
---


# Question
Title: How can I add custom client-side validation to a Webform field using advanced custom properties?
**The setup:**

I have a Drupal 8 form created manually, and I would like to recreate it using Webform module.

So far, I have managed to recreate everything (required fields and submission handler), but there is a last bit missing: the client-side validation that triggers on change.

The initial form have fields defined as follows:

```
$form['first-name'] = array(
  '#type' => 'textfield',
  '#title' => $this->t('First name'),
  '#required' => true,
  '#required_error' => $this->t('This is a required field. Please enter your first name.'),
  '#ajax' => [
    'callback' => '::validateFnameAjax',
    'effect' => 'fade',
    'event' => 'change',
    'progress' => FALSE,
  ],
);

```

Calling validation functions like this one:

```
public function validateFnameAjax(array &$form, FormStateInterface $form_state) {
  $response = new AjaxResponse();
  $errorValidation = '<div class="form-item-message form-item__error-message"><small class="text-danger"></small></div>';
  $successValidation = '<div class="form-item-message form-item__success-message"><small class="text-success"></small></div>';
  // Assert the name field has been included
  if (!$form_state->getValue('first-name') || empty($form_state->getValue('first-name'))) {
    $message = $this->t('This is a required field. Please enter your first name.');
    $response->addCommand(new InvokeCommand('.staff-register-form .js-form-item-first-name .form-text', 'removeClass', ['is-valid']));
    $response->addCommand(new InvokeCommand('.staff-register-form .js-form-item-first-name .form-text', 'addClass', ['is-invalid']));
    $response->addCommand(new ReplaceCommand('.js-form-item-first-name .form-item-message', $errorValidation));
    $response->addCommand(new HtmlCommand('.js-form-item-first-name .text-danger', $message));
  }
  else {
    $message = $this->t('Your first name is valid.');
    $response->addCommand(new InvokeCommand('.staff-register-form .js-form-item-first-name .form-text', 'removeClass', ['is-invalid']));
    $response->addCommand(new InvokeCommand('.staff-register-form .js-form-item-first-name .form-text', 'addClass', ['is-valid']));
    $response->addCommand(new ReplaceCommand('.js-form-item-first-name .form-item-message', $successValidation));
    $response->addCommand(new HtmlCommand('.js-form-item-first-name .text-success', $message));
  }
  return $response;
}

```

In the new webform function I have changed lines one and six, but the rest of the code is the same.

New line one:

```
  public function validateFnameAjax(WebformSubmissionInterface $webform_submission) {

```

New line 6:

```
  if (!$webform_submission->getElementData('first_name') || empty($webform_submission->getElementData('first_name'))) {

```

**The challenge:**

Implement the same behaviour: trigger the custom validation when the content in the field changes (before submission).

**Current status:**

I have tried to include the Ajax properties using the Advanced tab of the edit field UI as you can see in the following screenshots:

But I get this error in the log:

> Symfony\Component\HttpKernel\Exception\HttpException: The specified #ajax callback is empty or not callable. in Drupal\Core\Form\FormAjaxResponseBuilder-\>buildResponse() (line 67 of /var/www/drupalvm/web/core/lib/Drupal/Core/Form/FormAjaxResponseBuilder.php).

Does anyone know what am I doing wrong to get this error, and how to make this client side validation work?

# Answer
> 0 votes
Although this answer does not provide the solution to add custom client-side validation to a Webform field using advanced custom properties, it provides a working alternative to add client-side validation to a Webform field.

Given that Webform UI comes with the option to add custom CSS and Javascript to a Webform, I have made use of this option to add my custom validation.

# Answer
> 0 votes
I think @ygoex has a very good working alternative. Just a few things that catches my attention when reading the problem. I noticed the red star on your screenshot of the elements(YAML). Did you save your changes? If yes did you also reinstall or rebuild the cache (if it is Yaml you useably have to reconstruct the configuration). I know, maybe some dumb remarks, but if you are a bit like me.....

Secondly (forgive me if I make no sense, I'm not a Drupal guru), the poundsign # is used in render arrays to recognize key-properties (https://www.drupal.org/docs/drupal-apis/render-api/render-arrays). So maybe excluding the # give the right result?

Edit: added "secondly" and changed some typo's

---
Tags: 8, ajax, webforms
---


# Question
Title: My module development snapshot is never updated
I made some changes to my module on Drupal.org and pushed them up; I have seen that Drupal.org updated the development snapshot. Shortly after, I made some more changes and pushed them up, but this time it has not been updated.

I do see my commit when I look at *View commits*.

I was told that Drupal.org updates the development snapshots every 12 hours; but it has been already 24 hours since the last `git push` I did.

```
git push origin 7.x-1.x
Everything up-to-date

```

# Answer
> 0 votes
Drupal.org shows a link to your drupal.org account for each commit you do only when Git has been set to use the same email associated to your drupal.org account.
If the email you are using with Git is different from the email you are using to get emails about your drupal.org account, you can add that email on https://www.drupal.org/user/1327432/edit/email-addresses. In this way, the commits you do for a module are associated to your drupal.org account which also means, for example, that the *Maintainers for Slack to Drupal* block that appears on https://www.drupal.org/project/slack\_to\_drupal show the exact number of commits you did and when.

>

The new email associated to the drupal.org account doesn't need to be made the default one, which means it would be used for every communication from Drupal.org. It just needs to be added to your account, so drupal.org associate the email to an account.

For the other commits to be associated to your account, you need to open an issue on the Drupal.org infrastructure queue asking to associate your commits to your account, similar to Request to associate commits with my account.

---
Tags: community-official-site
---


# Question
Title: How do I change the default country on the billing address form in Commerce 2?
I want to set the default country when customers check out in Commerce 2. How do I do that?

# Answer
> 2 votes
You need to edit the form display for the *Customer* **Profile Type**.

You can find it in the Admin menu under "Users", or go directly there by this path:

`/admin/config/people/profiles/manage/customer/form-display`

Then, on the widget for the address field, click the gear to set the default country.

# Answer
> 1 votes
Path for me was: /admin/config/people/profile-types/manage/customer/fields

No gear to click, but there was an edit button:

Clicking the \[edit\] button dumps you off here: /admin/config/people/profile-types/manage/customer/fields/profile.customer.address

Scroll to the bottom and change this:  ... to whatever you need the default country to be.

---
Tags: 8, commerce
---