# Question
Title: Can I add elements to a field widget with AJAX?
I want to add fields depending on the first option selected. I also made a form with a similar behaviour but it wasn't a field widget.

I used this module as an example to make the form

I'm not sure if the way I implement this AJAX behavior should be different in a widget.

Can the behavior of this form be implemented in a field widget? I'm trying to look for examples of specifically field widgets implementing this but I still haven't found anything that could help \`

```
 public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
    $value = isset($items[$delta]->value) ? $items[$delta]->value : '';
    $element = [];

    // Gather the number of names in the form already.
    $num_names = $form_state->get('num_names');
    // We have to ensure that there is at least one name field.
    if ($num_names === NULL) {
        $name_field = $form_state->set('num_names', 1);
        $num_names = 1;
    }

    $element['breakpoints'] = [
      '#type' => 'select',
      '#title' => $this->t('Breakpoint group'),
      '#options' => $this->getBreakpointsGroups(),
      '#default_value' => $value,
      '#empty_option' => $this->t('-Select a breakpoint group-'),
    ];

    $element['images_fieldset'] = [
        '#type' => 'fieldset',
        '#title' => $this->t('Load images for each breakpoint'),
        '#prefix' => '<div id="breakpoint-wrapper">',
        '#suffix' => '</div>',
    ];

    for ($i = 0; $i < $num_names; $i++) {
        $element['images_fieldset']['image'][$i] = [
            '#type' => 'image',
            '#title' => $this->t('Image'),
            '#name' => "Name"
        ];
    }

    $element['images_fieldset']['submit'] = [
        '#type' => 'submit',
        '#title' => 'Submit',
        '#submit' => ['::submitForm'],
        '#value' => $this->t("submit"),
        '#ajax' => [
            'callback' => '::loadImageFields',
            'wrapper' => 'breakpoint-wrapper',
        ],
    ];

    return ['value' => $element];
}

/**
 * Returns a list of breakpoints.
 *
 * @return array
 *   An associative array of breakpoints, suitable to use as form
 *   options.
 */
protected function getBreakpointsGroups() {
    $breakpoints = \Drupal::service('breakpoint.manager')->getGroups();
    $breakpoint_names = array_keys($breakpoints);
    $breakpoint_group = array();
    foreach ($breakpoint_names as $name) {
        $breakpoint_group += [$name => $name];
    }
    return $breakpoint_group;
}

protected function submitForm($form, &$form_state) {
    $state = $form_state->getTriggeringElement();
    $breakpoint= $state['#value'];
    $array = array();

    $breakpoint = \Drupal::service('breakpoint.manager')->getBreakpointsByGroup($breakpoint);
    $breakpoints_name = array_keys($breakpoint);

    foreach ($breakpoints_name as $breakpoint_definition) {
        $mediaQuery = $breakpoint[$breakpoint_definition]->getMediaQuery();
        array_push($array,$mediaQuery);
    }

    $name_field = $form_state->get('num_names');
    $form_state->set('num_names',count($array));

    $form_state->setRebuild();

}

public function loadImageFields(array $form, FormStateInterface $form_state) {
    return $form['images_fieldset'];
 }

```

}

# Answer
> 0 votes
The point is, you can't modify Drupal's internal form structure in an Ajax callback. So you'll need a submit handler, either for the field or for the complete widget, do the form modifications there and add a $form\_state-\>setRebuild().

Ajax callbacks basically modify the HTML in the browser, that's all they do. They are called after the validation and submission has run.

---
Tags: forms, ajax
---


title: How do I get the bundle name by route?

text:
# Question
Title: How do I get the bundle name by route?
I would like to pass variables to twig that depend on certain routes or node types (bundles) (a user page, a comment, a page or a custom content type). I used the name of the route (`entity.comment.edit_form`, `entity.user.canonical`) for that purpose.

Now I need to exclude some node types (i.e. I can't use just `entity.node.canonical` for all cases, I need to create a special condition for certain types of nodes). How do I get node type (bundle?) by route name/object?

My code is in the `.theme` file in `HOOK_preprocess_page(&$variables)`

UPD: Okay, I have discovered that there is actually `$variables['node']` which contains what I need in `$variables['node']->getType()`

But let's say I have just the route, how do I get an ID from it and load a node?

# Answer
> 1 votes
Drupal is providing such a variable for the html template:

```
/**
 * Implements hook_preprocess_HOOK() for HTML document templates.
 */
function node_preprocess_html(&$variables) {
  // If on an individual node page or node preview page, add the node type to
  // the body classes.
  if (($node = \Drupal::routeMatch()->getParameter('node')) || ($node = \Drupal::routeMatch()->getParameter('node_preview'))) {
    if ($node instanceof NodeInterface) {
      $variables['node_type'] = $node->getType();
    }
  }
}

```

This code works in your `HOOK_preprocess_page(&$variables)` as well.

UPD: `getParameter('node')` gets already a loaded node. So you don't need to get the ID to load the node, but if you need the ID then use `getRawParameter('node')`.

---
Tags: 9
---


# Question
Title: how to extend existing drush command class
Can anyone please let me know how to extend existing drush command class.

I am writing a custom module which provides custom content entity and created a devel generate plugin similar to ContentDevelGenerate.

It is working as expected to generate dummy content from UI . I would like to create a drush command as well for the devel generator plugin. Similar to DevelGenerateCommands content method I would like to add additional method to generate my custom content entity.

My drush command class as follows :

```
<?php

namespace Drupal\omdb_api\Commands;

use Drupal\devel_generate\Commands\DevelGenerateCommands;

/**
 * Class to create omdb api entity drush commands.
 */
class OmdbApiEntityDevelGenerateCommands extends DevelGenerateCommands {

  /**
   * Create omdb api entity items by drush command.
   *
   * @command devel-generate:omdb-api
   * @aliases dgen:omdb-api, devel-generate-omdb-api
   * @pluginId omdb_api_entity_devel_generate
   * @validate-module-enabled omdb_api
   *
   * @param int $num
   *   Number of omdb api entity items to generate.
   * @param array $options
   *   Array of options as described below.
   *
   * @option kill Delete all omdb api entity items before generating new omdb api entity.
   * @option feedback An integer representing interval for insertion rate logging.
   * @option skip-fields A comma delimited list of fields to omit when generating random values.
   * @option languages A comma-separated list of language codes
   */
  public function omdbApiEntities($num = 50, array $options = ['kill' => FALSE, 'feedback' => 1000]) {
    $this->generate();
  }

  /**
   * Wrapper for calling the plugin instance generate function.
   */
  public function generate() {
    $instance = $this->getPluginInstance();
    $instance->generate($this->getParameters());
  }

}

```

OmdbApiEntityDevelGenerateCommands If i paste this method directly in DevelGenerateCommands class, command is working as expected. So it seems I am missing something to extend the DevelGenerateCommands class.

```
# drush.services.yml
services:
  omdb_api.devel_generate_commands:
    class: \Drupal\omdb_api\Commands\OmdbApiEntityDevelGenerateCommands
    tags:
      - { name: develgenerate.command }

```

# Answer
In order to fix the issue I need to update my `drush.services.yml` as below :

```
services:
  omdb_api.devel_generate_commands:
    class: \Drupal\omdb_api\Commands\OmdbApiEntityDevelGenerateCommands
    arguments: ['@plugin.manager.develgenerate']
    tags:
      -  { name: drush.command }

```
> 2 votes

---
Tags: drush, 9, dependency-injection
---


# Question
Title: Changing the PHP memory limit in settings.php is not reflected in phpinfo()
Of the various methods to increase the PHP memory limit, the only one I can use for a certain site is `ini_set('memory_limit', 'XXXM');` in settings.php.

This value does not appear to change the memory\_limit shown in phpinfo(). However, I know it is actually being modified because the site breaks without it.

Is there another way to view the current memory limit OR the actual memory limit ceiling?

# Answer
> 3 votes
You are probably calling phpinfo() before settings.php. What if you use the built-in phpinfo() linked in the Status Report?

http://www.example.com/admin/reports/status/php

---
Tags: 7
---


# Question
Title: How can I add an external (CDN) CSS style sheet to a theme?
I'm trying to add Font Awesome to a Bootstrap subtheme I have made.

Here is the code I am using in `subtheme.libraries.yml`, which is based on the documentation for hosting external JS libraries in Drupal 8:

```
font-awesome:
  remote: https://fortawesome.github.io/Font-Awesome/
  version: 4.5.0
  license:
    name: MIT
    url: https://fortawesome.github.io/Font-Awesome/license/
    gpl-compatible: true
  css:
    https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css: { type: external, minified: true }

```

Unfortunately this results in a WSOD. The error log gives these two errors:

> Warning: constant(): Couldn't find constant CSS\_HTTPS://MAXCDN.BOOTSTRAPCDN.COM/FONT-AWESOME/4.5.0/CSS/FONT-AWESOME.MIN.CSS in Drupal\Core\Asset\LibraryDiscoveryParser-\>buildByExtension() (line 143 of /srv/bindings/code/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php).

And:

> Warning: Illegal string offset 'weight' in Drupal\Core\Asset\LibraryDiscoveryParser-\>buildByExtension() (line 143 of /srv/bindings/928c941920f8443d9e50a3d908967fee/code/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php).

# Answer
> 2 votes
If you have the same code from your question in your subtheme.libraries.yml file, but it isn't showing up, you still need to add that library to your subtheme.info.yml file

subtheme.info.yml

```
name: Subtheme Name
type: theme
description: 'A theme description.'
core_version_requirement: ^8 || ^9
base theme: bartik
libraries:
  - subthemename/global-styling
  - subthemename/font-awesome
  - subthemename/bootstrap

```

subtheme.libraries.yml

```
global-styling:
  css:
    theme:
      css/styles.css: {}
  js:
    js/scripts.js: {}

font-awesome:
  remote: https://fortawesome.github.io/Font-Awesome/
  version: 4.5.0
  license:
    name: MIT
    url: https://fortawesome.github.io/Font-Awesome/license/
    gpl-compatible: true
  css:
    theme:
      https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css: { type: external, minified: true }

bootstrap:
  remote: https://getbootstrap.com/docs/5.1/
  version: 5.1
  license:
    name: MIT
    url: https://getbootstrap.com/docs/5.1/about/license/
    gpl-compatible: true
  css:
    theme:
      https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css: { type: external, minified: true }
  js:
    https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js:  { type: external, minified: true }

```

Put another way, **you first need to specify the library names in your info.yml file then define them in your libraries.yml file**.

# Answer
> 2 votes
My understanding was that for a sub theme you defined the CSS as such:

```
global-styling:
  css:
    theme:
      css/style.css: {}

```

...as discussed here: https://www.drupal.org/node/2165673

# Answer
> 1 votes
This worked for me, as of the 8.0.4 core release:

```
googlefonts-ptsans:
version: 1.0
license:
  name: google fonts
  url: https://www.google.com/fonts#AboutPlace:about
  gpl-compatible: true
css:
  base:
    https://fonts.googleapis.com/css?family=PT+Sans:400,400italic,700italic,700: {}

```

---
Tags: 8
---


# Question
Title: How do I delete/remove a broken configuration?
I'm using Layout Builder to manage the display of a content type.
I was playing around with the config and adding different things, and something I added caused the page to crash and now I get this error:

> The website encountered an unexpected error. Please try again later.Drupal\Core\Entity\EntityMalformedException: The "node" entity cannot have a URI as it does not have an ID in Drupal\Core\Entity\EntityBase-\>toUrl() (line 191 of core/lib/Drupal/Core/Entity/EntityBase.php). node\_tokens('node', Array, Array, Array, Object)

This error persists every time I click "Manage Display" for that content type; I rebuilt the cache with Drush and cleared my browser cache, but I still get the error when I go to edit the display settings. I get the error when I log in with a different browser, too, and also when I revert the config.

I am working on this site locally, so I can pull from the dev server, but is there another way to reset my "unsaved changes" locally so that I don't have to wait for the pull to complete?

# Answer
> 1 votes
Layout builder stores in-progress modifications to the layout config in the `key_value_expire` table. Whenever Layout Builder is loaded (e.g. Manage Display, Page Manager, etc.), it first checks that table if there is an in-progress entry for the layout you're about to edit. If it exists, it tries to load that, otherwise it loads the one saved in config.

The problem is, that in-progress config may contain bad data (e.g. a block that was added but has bad implementation) or outdated data (e.g. a block that you were working on, but whose implementation has already been deleted). This can cause Layout Builder to fail loading. As far as I know, Drupal has no way of guarding or recovering from this from the admin side nor does a cache rebuild help.

One way to recover is to load a copy of the DB prior to your changes, but that may take forever and blow away any other work in progress you have. Another is to dive into the `key_value_expire` table and look for that in-progress entry, delete it, and reload Layout Builder.

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


# Question
Title: Upgrade fails with InvalidArgumentException
After upgrading from Drupal 8.9 to Drupal 9.3, I get this fatal error when I run the database updates. The remaining updates aren't executed. It looks like the Multiversion Uninstall command needs to be run, but it's not installed.

> system module - Update entity\_revision\_metadata\_bc\_cleanup
>
> Failed: InvalidArgumentException: Drupal\multiversion\Entity\Storage\Sql\ContentEntityStorage does not implement Drupal\Core\Entity\ContentEntityStorageInterface in Drupal\Core\Entity\ContentEntityType-\>checkStorageClass() (line 52 of public/core/lib/Drupal/Core/Entity/ContentEntityType.php).

# Answer
Removing all rows Drupal 9.3's key\_value table containing 'multiversion' allowed the database update to complete successfully.

```
select * from key_value where value like '%multiversion%';

delete from key_value where value like '%multiversion%';

```
> 1 votes

---
Tags: 9, upgrading
---


title: Error while updb, how to debug it?

text:
# Question
Title: Error while updb, how to debug it?
I've updated opigno\_lms to 2.29 version without any problems by composer and then "drush updb", then installed 3.0.2 version through composer, but "drush updb" gives me:

> \[notice\] Update started: opigno\_statistics\_update\_9001 \[error\] Argument 2 passed to Drupal\Core\Config\CachedStorage::write() must be of the type array, bool given, called in /home/mine/praca/gitowanie/opigno\_project/web/modules/contrib/opigno\_statistics/opigno\_statistics.install on line 83 \[error\] Update failed: opigno\_statistics\_update\_9001

Anybody has solution for that? Or idea how to debug it? Every other update goes nice, but this one breaks everything... I tried already plenty of times, but it fails all the time, on 2 different servers.

# Answer
If you look for a way to effectively debug `updb`, then try to use Xdebug. If you have problems configuring it for command line scripts, you can always run updates via `update.php` in your browser.

When you set a breakpoint on `opigno_statistics_update_9001`, you will see exactly where the faulty argument comes from.
> 3 votes

---
Tags: 9, updating
---


# Question
Title: How do I debug Drupal\Component\PhpStorage\FileStorage::createDirectory()?
On my site, I am getting this warning.

> Alert: User warning: mkdir(): Permission Denied in Drupal\Component\PhpStorage\FileStorage-\>createDirectory() (line 123 of core/lib/Drupal/Component/PhpStorage/FileStorage.php).

I want to debug or get the exact path and directory where Drupal is trying to create the directory.

I tried changing the file permissions for sites/default/files with `chmod -R 777` path, without success.

How can I debug it?

# Answer
> 1 votes
The standard debugger for PHP is Xdebug. Install it and set a breakpoint at that line. If for some reason you cannot use Xdebug, `\Drupal::logger('my_module')->notice($message)` or `error_log()` the filename to be written and examine the logs.

---
Tags: files, 9, debugging
---


# Question
Title: Cannot set subscriptions_status on migration from csv
Modules used: Simplenews and Migrate.

I have a single newsletter, to which I want to subscribe all users imported from the csv file.

This is my csv file content:

```
id,email,date_submitted

```

This is my yml import file

```
langcode: en
status: true
dependencies: {  }
id: test_subscriber
class: null
field_plugin_method: null
cck_plugin_method: null
migration_tags:
   - CSV
migration_group: test
label: 'Test Subscriber'
source:
  plugin: csv
  path: sites/default/import/subscriber.csv
  delimiter: ','
  enclosure: '"'
  header_row_count: 1
  track_changes: true
  ids:
    - id
process:
  type:
    plugin: default_value
    default_value: simplenews_subscriber
  created: date_submitted
  mail: email
  status:
    plugin: default_value
    default_value: 1
  subscriptions:
    plugin: sub_process
    source: subscriptions
    process:
      target_id:
        plugin: default_value
        default_value: my_newsletter
      status:
        plugin: default_value
        default_value: 1
      timestamp: date_submitted
      source:
        plugin: default_value
        default_value: website
destination:
  plugin: 'entity:simplenews_subscriber'
migration_dependencies:
  required: {  }
  optional: {  }

```

I can successfully import all the user with email and timestamp but the users are not subscribed to the newsletter (my\_newsletter).

In database I have the table "simplenews\_subscriber\__subscriptions but the columns: "entity\_id", "subscription\_target\_id", "subscription\_status", "subscription\_timestamp" and "subscriptions\_source" doesn't get populated.

If I change the subscription setting in the yml file as follow:

```
    subscriptions:
      target_id:
        plugin: default_value
        default_value: my_newsletter
      status:
        plugin: default_value
        default_value: 1
      timestamp:
        plugin: default_value
        default_value: date_submitted
      source:
        plugin: default_value
        default_value: website

```

In database table "simplenews\_subscriber\__subscriptions, the columns: "entity\_id" and "subscription\_target\_id" get populated but the columns: "subscription\_status", "subscription\_timestamp" and "subscriptions\_source" are filled with null instead of data.

The end result is a list of user unsubscribed to my\_newsletter (from subscriber edit page the my\_newsletter checkbox is unchecked)

Thanks in advance to anybody willing to give me a tip.

# Answer
> 0 votes
I finally was able to solve the issue. Instead of this yml section code

```
subscriptions:
  target_id:
    plugin: default_value
    default_value: my_newsletter

```

I changed to this code

```
subscriptions/target_id:
  plugin: default_value
  default_value: my_newsletter

```

Here the full code

```
langcode: en
status: true
dependencies: {  }
id: test_subscriber
class: null
field_plugin_method: null
cck_plugin_method: null
migration_tags:
  - CSV
migration_group: test
label: 'Test Subscriber'
source:
  plugin: csv
  path: sites/default/import/subscriber.csv
  delimiter: ','
  enclosure: '"'
  header_row_count: 1
  track_changes: true
  ids:
    - id
process:
  type:
    plugin: default_value
    default_value: simplenews_subscriber
  created: date_submitted
  mail: email
  status:
    plugin: default_value
    default_value: 1
  subscriptions/target_id:
    plugin: default_value
    default_value: my_newsletter
  subscriptions/status:
    plugin: default_value
    default_value: 1
  subscriptions/timestamp:
    plugin: default_value
    default_value: date_submitted
  subscriptions/source:
    plugin: default_value
    default_value: website
destination:
  plugin: 'entity:simplenews_subscriber'
migration_dependencies:
  required: {  }
  optional: {  }

```

---
Tags: migrations, 9
---


# Question
Title: How to prevent changing specific module installed status during config syncs?
I know there are methods for preventing specific configurations from being exported but how do you prevent specific module's installed/un-installed status from being exported?

For example, I'd like dev,stage\_file\_proxy to be enabled on dev but not prod. And the reverse for mem\_cache.

Also, is there a way to prevent the performance page settings (CSS, JS aggregation) from being exported?

# Answer
> 3 votes
You can use`$settings['config_exclude_modules']` key in settings.php to exclude modules.

For example Refer example.settings.php

---
Tags: configuration
---


# Question
Title: After I upgraded the site, some admin pages get a "Field storage definition for 'uuid' could not be found." error
Upgrading Drupal 8 to Drupal 9 went smooth and without any issues, but now when I visit admin/reports/status or admin/modules/uninstall, I get this exception.

> Drupal\Core\Field\FieldException: Field storage definition for 'uuid' could not be found. in Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema-\>getEntitySchema() (line 968 of core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php).

Searching for the error gives a few results of the same issue, but without solution. I've ran database updates (nothing pending), cleared caches, and tried to disable various contributed modules without success.

# Answer
> 1 votes
> I've ran database updates (nothing pending), cleared caches and tried disabling a various contrib modules without success.

A systematic approach would be to find out the entity type and with this information the module which is causing the error.

Change the code line throwing the exception to include the entity type ID:

*(line 971 of core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php):*

```
throw new FieldException("Field storage definition for '$field_name' in '$entity_type_id' could not be found.");

```

If you don't need the entity data or you are able to restore it, then uninstall the module before you upgrade Drupal and reinstall it later. If this is not an option you have to check the issue queue for a fix in the database update hooks of the module.

# Answer
> -1 votes
I was able to track down the issue. The previous admin had installed some core test modules (enity\_test) that were corrupt. Forcing removing the module resolved the issue.

---
Tags: 9, upgrading
---


title: Clear field value when edit node

text:
# Question
Title: Clear field value when edit node
I have a List (text) field in a content type that will determine the importance of the content whether it is Major or Minor and that field is set to be required. I need to force content editors to make that selection each time they edit a content page. Is there a way to clear the data from that field upon edit event?

Note: I'm on Drupal 9.2.18

# Answer
> 1 votes
HOOK\_form\_alter should be ok, then empty the field default value.

```
$form[YOURFIELD]['default_value'] = '';

```

---
Tags: forms, hooks
---


# Question
Title: How can I override a displayed node with the content rendered by a custom module?
I have a path /application, which is rendered by a custom module (through a combination of theme hook/controller/routing/twig). It's basically a mini-application.

I also have a Drupal node /data, which is a Drupal node with content loaded by Drupal.

However, I need to load the contents of /application at /data. I tried setting the routing of my custom module to use /data instead of /application, but Drupal still loads the Drupal node content at /data rather than the custom module content. The catch is, this particular application uses fields from that node, so I don't want to delete the node. I just want to change what gets rendered when you view the node, but leave it active and editable.

What's the best way to accomplish this?

# Answer
> 2 votes
Since /data is a path alias for a node, Drupal shows that node instead of showing what a controller associated to that path returns. If you were to set a node path alias to /admin/config/people/accounts, Drupal would show that node instead of the accounts setting page.

If you want to change the render array used for a node, you need to implement `hook_ENTITY_TYPE_view()`. You can compare `$entity->id()` with the node ID to which you want add data in its render array.

```
use \Drupal\Core\Entity\EntityInterface;
use \Drupal\Core\Entity\Display\EntityViewDisplayInterface;

/**
 * Implements hook_ENTITY_TYPE_view().
 */
function mymodule_node_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
  if ($entity->id() == 3) {
    // Change $build.
  }
}

```

You can also find the node ID given its path alias, for example with code similar to the following one.

```
use \Drupal\Core\Entity\EntityInterface;
use \Drupal\Core\Entity\Display\EntityViewDisplayInterface;

/**
 * Implements hook_ENTITY_TYPE_view().
 */
function mymodule_node_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
  $path = \Drupal::service('path_alias.manager')->getPathByAlias('/data');

  if (preg_match('/node\/(\d+)/', $path, $matches)) {
    if ($entity->id() == $matches[1]) {
      // Change $build.
    }
  }
}

```

See How can I get the node ID from a path alias? which also explains when to use the *path\_alias.manager* or the *path.alias\_manager* service.

(As a side note, if I were to use this code in sites where the content is exported from a site and imported in those sites, I would compare the node UUID with the value I expect for the node I need to handle. This works only when the node UUIDs are kept when importing content.)

---
Tags: routes, 9
---


# Question
Title: How to use a referenced entity's values in a TableSort?
I have a custom entity with a field which is defined as follows.

```
$fields['file'] = BaseFieldDefinition::create('file')
  ->setDescription('Reference to the built-in core File entity type.')
  ->setLabel('File')
  ->setRequired(TRUE)
  ->setSetting('file_extensions', 'pdf rtf doc docx')
  ->setSetting('max_filesize', '20MB')
  ->setSetting('description_field', TRUE)
  ->setDisplayOptions('form', ['type' => 'file'])
  ->setDisplayOptions('view', ['type' => 'file']);

```

I have no problem getting to the fields of the referenced `File` entity from instances of my own entity type. So for example, these lines work as expected.

```
$filename = $my_entity->file->entity->filename->value;
$username = $my_entity->file->entity->uid->entity->name->value;

```

When I try to use the fields of the referenced `File` entity with a `TableSort`, which I want to plug into an entity query on my custom entity type, the code throws an exception.

```
$header['name'] = [
  'data' => 'File Name',
  'field' => 'file.entity.filename',
  'specifier' => 'file.entity.filename',
];
$header['user'] = [
  'data' => 'Uploaded By',
  'field' => 'file.entity.uid.entity.name',
  'specifier' => 'file.entity.uid.entity.name',
];

// …

$query->tableSort($header);

```

> Uncaught PHP Exception Drupal\Core\Entity\Query\QueryException: "'file' not found"

For the syntax of the `$header` array I'm largely relying on clues I got from Tablesort with EntityQuery, in which Berdir recommends that we look at the `load()` and `buildHeader()` methods of the `UserListBuilder` class. Unfortunately, that example appears to be using field values which are stored directly in the base table, so I'm falling back on the assumption that, since we're in entity query land, the same syntax described in `QueryInterface::condition()` would be expected.

I've looked at a number of the open issues, such as Table sort ignores "field", always adds header title as order query parameter and Tablesort cleanup, but I didn't see anything which shed any light on the problem.

# Answer
There's a bug in core, and for the time being a workaround is:

```
'specifier' => 'file.target_id.entity.filename',

```

The reason for this is the entity query relationship syntax started out as join support, it predates the entity reference support in core and as such while `$entity->file->target_id->entity` is not supported by core, `file.target_id.entity` is supported by entity query to indicate it should join the entity specified by the `file` field and the entity id should be what is specified by `target_id`. It's a bit clunky but at the time, it was what worked.

It *should* be able to figure out `file.entity.filename` as well because the main property name for the `file` field happens to be `target_id` and so it is able to expand it for you into `file.target_id.entity.filename`. However, the class in case `Drupal\Core\Entity\Query\Sql\Tables` is extremely convoluted and this expansion has a bug for the shared table case which will be fixed in https://www.drupal.org/project/drupal/issues/3278083
> 2 votes

# Answer
The failure described above appears to be the result of a bug in the `FileItem` plugin. Even though the field type plugin claims it is an entity reference (the `@FieldType` annotation says `category = @Translation("Reference")`), and in all other respects that is true, the support for the documented entity query syntax is broken.

Until the bug is fixed, the workaround would be to give up the specialization implemented by the `FileItem` field type and use a plain `entity_reference` field instead. With that change the `TableSort` code above works correctly.
> 1 votes

---
Tags: database, 9
---


# Question
Title: How can I use a different dialog for flag removal than for setting the flag?
My project has a special flag with an entity reference field in it to nodes of a content type I'll call here "bucket."

How it works:

* The flag "add to bucket" appears on nodes of type Article
* If the user wants to bucket the node, they click the 'add to bucket' flag
* The 'add to bucket' flag is configured to display a dialog (at the moment, not modal)
* The dialog allows them to select a bucket; on submission of the dialog, the flagging is created with a relationship to the selected bucket

All of that works great (views allows the user to see Articles in each bucket)

**The problem arises when the user decides to remove** the flagging from the node.

Flag module uses the same dialog, only with a 'remove' button added to it. Since it's the same dialog used to add the flagging, it has the entity reference field with the list of buckets... but this is meaningless. Clicking 'remove' deletes the flagging regardless of what 'bucket' it's related to.

The dialog also has a "save" button on it that doesn't make sense in that context.

I'd like users to not see the entity reference field when removing the flag... and also not see the save button.

**Option 1**: maybe there is a setting in the flag configuration for this and I am going to facepalm . . . but I am not seeing the setting, if it exists. (Fatigue blindness? I have done it before!)

**Option 2**: use CSS to hide those two elements. Problem: there does not appear to be classes to distinguish the removal context from the add context. Maybe I'm missing something there? I can see that in my theme form.html.twig is handling individual form elements, but I don't see what template is generating the dialog. With that info, maybe some logic can go into the template to create some classes for the unflagging?

**Option 3**: get it to use a different form for the unflagging. ... How to do that?

Maybe there's a module that can do this, or a combination of them? I haven't found one.

If I'd need to code a module, any advice on the general approach? (Note: I've coded maybe two tiny modules before, mostly cut and paste, so... skill level for that is probably "below novice." So use small words, lol)

# Answer
I have a site that basically has the same setup as yours: some flags have fields, and I use the **Field entry form** link type to let the user set the fields on flagging.

However, setting the fields on unflagging makes no sense, so I want a direct unflag link.

Setup

1. Go to the admin edit page for your flag at `/admin/structure/flags/manage/`.
2. For **Link Type**, use **Field Entry Form**.

This will let users edit the fields when they flag, but not show the fields when unflagging.

I'm pretty sure that's all you have to do. This is definitely working on my site, so if this is not enough, let me know and I'll dig through my code and see what I changed. (I wrote it a couple years ago, so I don't remember it so well.)
> 0 votes

---
Tags: flags
---


# Question
Title: how to order grouped fields in the sidebar on a node form
On my old Drupal 7 site, I was able to group a set of fields and display them as a tab in the sidebar. I'm moving it to Drupal 8 and using the adminimal theme. I created groups and am using the details sidebar widget. I'd like my custom groups to appear at the top of the sidebar. I've assigned a weight of -5 in Manage Form Display, but they still appear in the middle of the sidebar (even after clearing caches). Is there a way to control the order of groups in the sidebar, or am I maybe using the wrong widget? Thanks.

I have tried using Vertical Tabs Config, but even after clearing caches, the order is unchanged.

# Answer
Using weight -99 makes it appear on top in my installation.

Weight -10 moves it nicely below the 'author' box, which is where I want it.
> 0 votes

---
Tags: forms
---


# Question
Title: Batch API drush command running once then shows Solr error
I am trying to create a drush command to run a batch process. When I run the command, the batch runs just once, and I get this error :

> in Drupal\search\_api\_solr\SolrConnector\SolrConnectorPluginBase-\>handleHttpException() (line 1026 of /var/www/html/web/modules/contrib/search\_api\_solr/src/SolrConnector/SolrConnectorPluginBase.php). Drupal\search\_api\_solr\SearchApiSolrException: Solr endpoint http://solr:8983/ not found (code: 404

This is my code, am I doing something wrong? Thanks for your help:

```
class Drush9CustomCommands extends DrushCommands {

  use StringTranslationTrait;
  private $entityTypeManager;
  protected $logger;
  protected $batchService;

  public function __construct(EntityTypeManagerInterface $entityTypeManager, LoggerInterface $logger, BatchService $batch_service) {
    parent::__construct();
    $this->entityTypeManager = $entityTypeManager;
    $this->logger = $logger;
    $this->batchService = $batch_service;
  }

  /**
   * Update Node.
   *
   * @command update:node
   * @aliases update-node
   *
   * @usage update:node foo
   *   foo is the type of node to update
   */
  public function updateNode() {

    $batch = array(
      'title' => t('Exporting'),
      'operations' => array(
        array([$this->batchService,'processMyNode'], array()),
      ),
      'finished' => [$this->batchService,'processMyNodeFinished'],
    );
    batch_set($batch);

    drush_backend_batch_process();
  }
}

```

Batchservice.php

```
class BatchService implements ContainerInjectionInterface {

  use StringTranslationTrait;
  protected $messenger;

  public function __construct(MessengerInterface $messenger) {
    $this->messenger = $messenger;
  }

  /**
   * {@inheritDoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('messenger')
    );
  }

  /**
   * Batch process callback.
   *
   * @param int $id
   *   Id of the batch.
   * @param string $operation_details
   *   Details of the operation.
   * @param object $context
   *   Context for operations.
   */
  public function processMyNode( &$context) {
    if (!isset($context['sandbox']['total'])) {
      // Get node experience ids.
      $query = \Drupal::entityTypeManager()->getStorage('node')->getQuery();
      $nids = $query
        ->condition('type', 'mynode')
        ->range(0,1000)
        ->accessCheck(FALSE)
        ->execute();

      $context['sandbox']['total'] = count($nids);
      $context['sandbox']['node_ids'] = $nids;
      $context['sandbox']['current'] = 0;
    }
    $node_ids = array_slice($context['sandbox']['node_ids'], $context['sandbox']['current'], 25);

    foreach ($node_ids as $nid) {
      $node = \Drupal::entityTypeManager()->getStorage('node')->load($nid);
      $node->set('field_my_field', 'my field value');
      $node->save();
    }

    $context['sandbox']['current'] += count($node_ids);

    \Drupal::logger('test')->notice($context['sandbox']['current'] . ' Experiences passed / ' . $context['sandbox']['total']);

    if ($context['sandbox']['total'] == 0) {
      $context['sandbox']['#finished'] = 1;
    }
    else {

      $context['sandbox']['#finished'] = ($context['sandbox']['current'] / $context['sandbox']['total']);
    }
  }

  /**
   * Batch Finished callback.
   *
   * @param bool $success
   *   Success of the operation.
   * @param array $results
   *   Array of results for post processing.
   * @param array $operations
   *   Array of operations.
   */
  public  function processMyNodeFinished($success, array $results, array $operations) {

    if ($success) {
      $message = \Drupal::translation()->formatPlural(count($results), 'One post processed.', '@count posts processed.');
    }
    else {
      $message = t('Finished with an error.');
    }
    \Drupal::logger('teset')->notice($message);
  }

}

```

# Answer
Search API Solr module cannot connect to the configured Solr server instance each time the script saves a node. The configured Solr instance is http://solr:8983/. You must configure a reachable instance.
> 1 votes

# Answer
Adding to @cilefen's answer, there was en error in the code : it should be $context\['finished'\] instead of $context\['sandbox'\]\['#finished'\].After modifying this, the batch was running successfully.
> 2 votes

---
Tags: drush, batch-operations
---


# Question
Title: How do I make the exposed filter values available as tokens for text areas
How do I display (or render in `twig`) the search term, entered in the search field. When you enter a search term and click on the search button, you will go to the search results page (view).

In the header of that view I want to display something like:

> Your search of \[mysearcherm\] has x number of results.

I build the search field with a `views exposed form` and `Search API.`

**Update:** In the textarea of the views header I want to insert the *search term* as replacement token, but it's not available?

# Answer
> 1 votes
You can show the relevant search term in `Text area` when you add it to the `Header` option in Views.

When you tick the `Use replacement tokens from the first row` option you will see the Replacement patterns tokens available, and below that global replacement tokens.

You can use something like:

```
<div class="messages messages--status">Your search of {{ name }} has [view:total-rows] result(s). </div>

```

Although you'll probably want to take care of plurals more intelligently and also use a twig conditional to not show anything if the Exposed form isn't used.

# Answer
> 1 votes
You could use the global replacement pattern for the current page query parameter.

E.g. for */search?search\_term\_field\_name=foo*:
`[current-page:query:search_term_field_name]`

---
Tags: views, search
---


# Question
Title: What is the correct way to get the rendered fields values in a block view twig template?
I have a block on the main page of my website that displays most recent news stories from the *News* page (3 latest entries). This is a view, here is its settings:

I'm trying to make this block look better. As this is a view, I use *block--views-block--news-block-1.html.twig* template file for my purpose. All I wanted was to get the rendered field values in it to wrap each of them in a *\<div\>* with its own css class. I tried many times and through trial and error finally I wrote the following code:

```
{%
  set classes = [
    'block',
    'block-' ~ configuration.provider|clean_class,
    'block-' ~ plugin_id|clean_class,
  ]
%}
<div class="container">
    <section{{ attributes.addClass(classes) }}>
        {{ title_prefix }}
            <h2 class="article_title">District News</h2>
        {{ title_suffix }}
        {% block content %}
            <ul class="content article_list">
                {% for row in content['#view'].result %}
                    <li class="article_item">
                        <div class="article_item-header">
                            <div class="article_release_date">
                                {{ content['#view'].field['published_at'].value(row)|date("d.m.Y") }}
                            </div>
                            {{ content['#view'].field['title'].getItems(row) }}
                        </div>
                        <div class="article_description">
                            {{ content['#view'].field['body'].getItems(row) }}
                        </div>
                    </li>
                {% endfor %}
            </ul>
        {% endblock %}
        <div class="article_read_more">
            <a href="/news">View All</a>
        </div>
    </section>
</div>

```

It works satisfactorily: I see the markup and variables in this block on the main page exactly as I designed them. But I can't say that it works just perfectly because when I go to "Recent log messages", I see errors coming from this *.twig* template file. The error message is: `User error: "raw" is an invalid render array key in Drupal\Core\Render\Element::children() (line 97 of core/lib/Drupal/Core/Render/Element.php)`. When I delete my twig file, the error doesn't occur anymore. So my question is: What is the correct way to achieve the same markup without such errors? Maybe my approach is totally wrong and I should change it. Or maybe my approach is correct, but there is a little mistake in my code? Anyway, I'd like to know why do I get such error in the log and how can I avoid it. Please, help me.

# Answer
> 0 votes
I think trying to theme fields inside the block is not a best practice.

The approach mentioned in the comments by **sonfd** is a very good one.

Otherwise, you can theme each field in the view by overriding the field template like:

```
views-view-field--{views machine name}--{page or block machine name}--{field name}.html.twig

```

This would be a better approach.

Hope this answers your question.

---
Tags: 8, theming
---


# Question
Title: Overriding Drupal.ajax.prototype is not working
I am trying to override Drupal.ajax.prototype.beforeSend like this:

```
(function($) {
  // In mymodule.js, which appears after ajax.js in the page build
  Drupal.ajax.prototype.beforeSend = function(xmlhttprequest, options) {
    console.log('overridden!')
  }
})(jQuery);

```

The file that contains the code is listed below core's ajax.js in the page's source, yet it's the console.log I put inside core's ajax.js beforeSend that is showing up.

What else is to it? Has anything been changed in Drupal? Using version 9.3. Can't override any of those functions.

# Answer
> 1 votes
There are two javascript objects:

1. `Drupal.ajax`
2. `Drupal.Ajax`

You have to override the prototypes in `Drupal.Ajax`. For each Ajax object declared in Drupal settings an instance of it will then be stored in `Drupal.ajax.instances`.

---
Tags: javascript, ajax, 9
---


# Question
Title: How to login first time in the login panel
I have finally managed to install aegir on a vps. I have also set the dns of my domain and I can i can finally see my login panel. The problem is that I don’t have username and password. The documentation says I should have a temporary link to set the password but I can’t find that temporary link. I tried to reset the password but obviously I can’t receive any mail because I have never done any login so far. There is any file that I can check to find username and password please?

# Answer
> 1 votes
You can always generate another temporary login link. This recipe will log you into the VM, switch you to the Aegir user, and then generate the link.

1. `ssh aegir.example.com`
2. `sudo -sHu aegir`
3. `drush @hostmaster user-login`

The output will be a URL, which you can open in your browser.

---
Tags: aegir
---


title: How do I add styles to regions?

text:
# Question
Title: How do I add styles to regions?
In Drupal layout builder I may have a section with several drag and drop regions. Each region can contain multiple components (fields for example). I would like to add a style to the region - not the section or the component. EG. In the below picture I would like the editor to be able to add a red border to the drag and drop region containing Field 5 and Field 6.

I have looked at the Layout Styles module and it only seems to cover sections and components. Is there a solution in core (possibly an active patch) or contrib to allow the editor to control region styles?

# Answer
Layouts are just plugins that by default use `\Drupal\Core\Layout\LayoutDefault` as its implementation. The built-in two-column and three-column layouts extend from `MultiWidthLayoutBase` which extends from `LayoutDefault`. Their implementation is custom, allowing them to define that width distribution select whenever you use them.

You can use this same approach to add custom configuration to your layout, and allow a site-builder to customize it whenever they use it (like adding an input that accepts classes for a specific region). To do this, you'll need to do the following:

1. Define a class extending `LayoutDefault`.
2. Define your layout in `.layouts.yml`, with its `class` pointing your class in Step 1.
3. In this custom class, define the additional configuration properties and their form inputs for whatever you want configurable (e.g. add a textfield for adding classes for a specific region)
4. In the layout's twig, find and consume that property to customize the region (e.g. print the class)
5. Repeat 3 and 4 for other things you want configurable in the layout.

See https://www.drupal.org/docs/drupal-apis/layout-api/how-to-register-layouts#using-class-key for details on how to define a layout with custom configuration.
> 2 votes

---
Tags: layout-builder
---


# Question
Title: How to access weight of a paragraph in twig?
I use paragraphs with my content type. I need to show weight (order) value of each paragraph in my twig template. I can change weight with select box when I edit the node, but I can't figure out how to get it in twig.

I thought it would be something like this:

```
function ws_preprocess_paragraph(&$variables)
{
    $par = $variables['paragraph'];
    $myweight = $par->weight;
}

```

Please help)

# Answer
> 0 votes
The weight of a paragraph is not stored in the paragraph itself, but in the host field.

So if you need to get the weight of a paragraph from within the paragraph (e.g. from within `hook_preprocess_paragraph()`), you have to go upwards to the parent entity and find your the weight there:

```
function MYMODULE_preprocess_paragraph(&$variables) {
  $paragraph = $variables['paragraph'];
  $parent = $paragraph->getParentEntity();
  // get all paragraph IDs sorted by weight
  $weights = array_map( function($a) {return $a['target_id'];}, $parent->get($paragraph->get('parent_field_name')->value)->getValue());
  // find the weight of my paragraph ID
  $weight = array_search($paragraph->id(), $weights);
  // ...do stuff with $weight... e.g. assign it to a Twig variable
  // $variables['weight'] = $weight;
}

```

Please note that the weight is easier to get if you use the paragraph host field template instead (see @4k4's comment below the original question):

> Use {{ loop.index0 }} in a field template like field--field-paragraph.html.twig

# Answer
> 0 votes
I'm sorting fields in paragraph template (paragraph.html.twig) in a next way:

```
{% set items = content|sort((a, b) => a['#weight'] <=> b['#weight']) %}

```

Note: that there could be "odd" fields like "\_layout\_builder", so to avoid rendering items without weight just use condition:

```
{% for item in items %}
  {% if item['#weight'] is defined %}
    {{ item }}
  {% endif %}
{% endfor %}

```

# Answer
> -1 votes
You'll find it here for its fields:

```
$variables['content']['FIELD_NAME']['#weight'];

```

and then again in every item of a field:

```
$variables['content']['FIELD_NAME'][0]['#weight'];
$variables['content']['FIELD_NAME'][1]['#weight'];
$variables['content']['FIELD_NAME'][2]['#weight'];
$variables['content']['FIELD_NAME'][..]['#weight'];

```

---
Tags: 8, theming, paragraphs
---


# Question
Title: How to display a comma-separated list of multiple field values?
How can I render for example each taxonomy terms value as an inline comma-separated list (or add between each of them a "/" Character) in my field template `field--node--field-tags.html.twig` file or even visually with css?

# Answer
> 1 votes
You can use CSS to accomplish this.

For example:

### HTML

```
<div class="field-items">
  <div class="field-item">
  Apple
  </div>
  <div class="field-item">
  Orange
  </div>
  <div class="field-item">
  Lemon
  </div>
</div>

```

### CSS

```
.field-item {
  float: left;

}

.field-item:after {
  content: ","; right: 3px; position: relative;
}

.field-item:last-child:after {
    content: none;
}

.field-item:last-child:before {
    content: " and ";
}

.field-item:first-child:before {
    content: none;
}

```

# EXAMPLE DEMO

# Answer
> 8 votes
While it's possible to use CSS for this I think it's better to avoid it. I've simplified the field twig file to illustrate, but it would look something like this:

```
{% for item in items %}
  {% if loop.last %}
    {% set separator = '' %}
  {% else %}
    {% set separator = '/' %}
  {% endif %}
  {{ item.content }}{{ separator }}
{% endfor %}

```

# Answer
> 1 votes
The https://drupal.stackexchange.com/a/238069/5501 should work.

However if you need to remove all markup from the output and ensure no whitespaces/new lines exists in it then it become tricky:

```
items|safe_join(', ')|preg_replace('/[\n\r]/', '')|replace({'  ': ''})|striptags|trim)

```

Let's break down the above:

* safe\_join filter iterates the list and trigger render/toString method to get item output, then joins all items by comma + whitespace
* preg\_replace filter used to remove all new lines
* replace filter used to remove double whitespaces that were left from item's markup
* striptags filter removes all tags in output
* trim filter removes spaces from the start/end of the string

Note: `preg_replace` is used from Twig Tweaks module

# Answer
> 0 votes
I combined Classy's field.html.twig with Rene's suggestion, in the file field--node--field-tags.html.twig:

```
{%
    set classes = [
    'field',
    'field--name-' ~ field_name|clean_class,
    'field--type-' ~ field_type|clean_class,
    'field--label-' ~ label_display,
]
%}
{%
    set title_classes = [
    'field__label',
    label_display == 'visually_hidden' ? 'visually-hidden',
]
%}
{% if label_hidden %}
    {% if multiple %}
        <div{{ attributes.addClass(classes, 'field__items') }}>
            {% for item in items %}
                {% if loop.last %}
                    {% set separator = '' %}
                {% else %}
                    {% set separator = ', ' %}
                {% endif %}
                <span{{ item.attributes.addClass('field__item') }}>{{ item.content }}</span>{{ separator }}
            {% endfor %}
        </div>
    {% else %}
        {% for item in items %}
            {% if loop.last %}
                {% set separator = '' %}
            {% else %}
                {% set separator = ', ' %}
            {% endif %}            <span{{ attributes.addClass(classes, 'field__item') }}>{{ item.content }}</span>{{ separator }}
        {% endfor %}
    {% endif %}
{% else %}
    <div{{ attributes.addClass(classes) }}>
        <div{{ title_attributes.addClass(title_classes) }}>{{ label }}</div>
        {% if multiple %}
            <div class="field__items">
        {% endif %}
        {% for item in items %}
            {% if loop.last %}
                {% set separator = '' %}
            {% else %}
                {% set separator = ', ' %}
            {% endif %}
            <span{{ item.attributes.addClass('field__item') }}>{{ item.content }}</span>{{ separator }}
        {% endfor %}
        {% if multiple %}
            </div>
        {% endif %}
    </div>
{% endif %}

```

# Answer
> 0 votes
You don't even need to do this on a theme level, you could use a display suite block field embedding a view of comma separated terms, if you wanted.

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


# Question
Title: How do I include composer.libraries.json in root composer.json without using the wikimedia/composer-merge-plugin plugin?
In the recent update to Composer 2.0 the advice is to get rid of the wikimedia/composer-merge-plugin, which was even removed from the dependencies of Drupal core.

There is documentation on how to include custom module's composer.json in the root composer.json, for example Managing dependencies for a custom project, but I cannot find documentation on how to include composer.libraries.json in the root composer.json.

Can anyone explain how to get the Webform module compatible with Composer 2.0?

# Answer
here is a specific way how to do it for webform module.

1. run composer require wikimedia/composer-merge-plugin
2. in composer add

> ```
>   "merge-plugin": {
>             "include": [
>                 "web/modules/contrib/webform/composer.libraries.json"
>             ]
>         },
>
> ```

3. run composer update --lock and verify the files

Reference https://www.drupal.org/docs/8/modules/webform/webform-frequently-asked-questions/how-to-use-composer-to-install-libraries

Reference 2 https://www.drupal.org/docs/8/modules/webform/webform-cookbook/using-the-composer-merge-plugin-to-download-libraries-with

Github issue https://github.com/wikimedia/composer-merge-plugin/issues/184
> 1 votes

# Answer
The alternate ways are documented. I think the one you would prefer is:

> Execute `drush webform:composer:update`, to update your Drupal installation's composer.json to include the Webform module's selected libraries as repositories.
> 1 votes

---
Tags: webforms, composer
---


# Question
Title: How can I disable revisioning on specific node field
I currently use hook\_entity\_extra\_field\_info() and hook\_ENTITY\_TYPE\_view() to create a custom field displayed in nodes that generates content dynamically via PHP.

However, Drupal 9 seems to be tracking changes to the output of this field each time I save a node revision, which I don't want because that field's content is generated dynamically and is already under version control in a custom module.

Is there a way to tell Drupal in one of those hooks, or anywhere else in the configuration, not to track revisions for a specific node field?

# Answer
As the content of your field is dynamically generated, it sounds like you need a computed field. Which will remove the issue you are running into. A computed field is one that is generated dynamically, rather than through user input. As it is a field, it can be managed like any other field in Drupal (though integration with Views requires some additional efforts). Fields are also cached with Drupal's various cache APIs.

To create a computed field, first extend `Drupal\Core\Field\FieldItemList`, `use` the `Drupal\Core\TypedData\ComputedItemListTrait`, and implement the `computeValue()` method:

```
namespace Drupal\[EXAMPLE]\Plugin\Field;

use Drupal\Core\Field\FieldItemList;
use Drupal\Core\TypedData\ComputedItemListTrait;

class SomeDynamicField extends FieldItemList {

  use ComputedItemListTrait;

  /**
   * {@inheritdoc}
   */
  protected function computeValue() {
    $values = some_function_to_get_an_array_of_values();
    foreach ($values as $index => $value) {
      $this->list[$delta] = $this->createItem($delta, $value);
    }
  }
}

```

Next, this field needs to be added to each entity type that you need, in hook\_entity\_base\_field\_info\_alter():

```
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;

/**
 * Implements hook_entity_base_field_info_alter().
 */
function EXAMPLE_entity_base_field_info_alter(&$fields, EntityTypeInterface $entity_type) {
  // Add/alter entity types as necessary.
  $applicable_entity_types = ['node'];
  if (in_array($entity_type->id(), $applicable_entity_types)) {
    $fields['my_computed_field'] = BaseFieldDefinition::create('string')
      ->setName('example_field')
      ->setLabel(t('Example Calculated Field'))
      ->setDescription(t('An example of a calculated field'))
      // Set the field as a computed field.
      ->setComputed(TRUE)
      // Set the class that generates the field value(s).
      ->setClass('\Drupal\[EXAMPLE]\Plugin\Field\Computed\SomeDynamicField');
  }
}

```
> 0 votes

---
Tags: nodes
---


title: Translate untranslated strings

text:
# Question
Title: Translate untranslated strings
I downloaded and enabled translations for my Drupal core and modules using these Drush commands:

```
drush dl l10n_update && drush en -y $_
drush language-add lt && drush language-enable $_
drush l10n-update-refresh
drush l10n-update

```

I got almost everything translated, and in "Administration » Configuration » Regional and language" I see that I have almost 60% of translations to Lithuanian.

However, not everything is translated. For example, harmony forum module thread reply;

```
    <ul class="post-links links--inline"><li class="show_replies first"><a href="/forumas/" id="post-1-show-replies" class="post-show-replies ajax-processed" data-thread-id="1" data-post-id="1">2 replies</a></li>
<li class="reply"><a href="/forumas/post/add?field_harmony_thread=1&field_harmony_post_is_reply_to=1" id="post-1-reply" title="Reply directly to this post" class="reply-link" data-thread-id="1" data-post-id="1">Atsakyti</a></li>
<li class="reply_as_new_thread"><a href="/forumas/thread/add?field_harmony_thread_cont_from=1" id="post-1-reply-as-new" title="Create a new thread as a reply to this post" class="reply-link" data-thread-id="1" data-post-id="1">Reply as a new thread</a></li>
<li class="flag-harmony_likes last"><span><span class="flag-wrapper flag-harmony-likes flag-harmony-likes-1">
      <a href="/forumas/flag/flag/harmony_likes/1?destination=thread/1&token=zOnI9qp9K92XqmzqfCM8P-0n7f_FEMQKzumwX_A4xb4" title="" class="flag flag-action flag-link-toggle flag-processed" rel="nofollow"><span class="count">(0)</span> <span class="text">Like</span></a><span class="flag-throbber"> </span>
    </span>
</span></li>
</ul>

```

and user profile statistics

```
<div class="field field--name-field-harmony-thread-count field--type-number-integer field--label-above">
<div class="field__label">Thread count: </div>
<div class="field__items">
</div>
<div class="field field--name-field-harmony-post-count field--type-number-integer field--label-above">
<div class="field__label">Post count: </div>
<div class="field__items">
</div>

```

How can I edit these strings to be translated?

# Answer
> 1 votes
Here is a custom function I use in update hooks, to translate some missing translations, just in case you want to do this programmatically:

```
function translateString($source_string, $translated_string, $langcode) {
  $storage = \Drupal::service('locale.storage');
  $string = $storage->findString(array('source' => $source_string));
  if (is_null($string)) {
    $string = new SourceString();
    $string->setString($source_string);
    $string->setStorage($storage);
    $string->save();
  }
  // If exists, replace
  $translation = $storage->createTranslation(array(
    'lid' => $string->lid,
    'language' => $langcode,
    'translation' => $translated_string,
  ))->save();
}

```

To implement it you can do the following (i.e. in a update hook):

```
function my_module_update_8000() {
  translateString(
    'The original String',
    'My translated String to german thats why the next parameter is "de"',
    'de'
  );
}

```

# Answer
> 0 votes
Identify what template it is using with Themer. Override it and use t() function to wrap those strings needed to be translated.

# Answer
> 0 votes
My solution:

I tried Devel Themer module as suggested, but I was not able to find function source to copy and change it for my needs. After unsuccessfull tries to add my translations via template.php and by creating custom tpl.php in templates folder of my theme, I decided to use this method:

> *! Before proceeding make sure that you have your non-english language downloaded and enabled as default in "Administration » Configuration » Regional and language » List"*

---

# 1. Download and enable:

## 2. In

*Administration » Configuration » Regional and language » Multilingual settings » Strings* choose "English" as source language.

## 3. In

*Administration » Configuration » Regional and language » Translate interface » Translate* search for your string, which you want to translate and limit search to Views, Fields or Blocks.

## 4. Translate

all occurrences of your untranslated strings.

## 5. Go to

*Administration » Configuration » Regional and language » Translate interface » Strings* , choose all text groups and click "Refresh strings" to clean up translations, which can overlap.

---

That's it!

> P.S. You can also use *Administration » Configuration » Regional and language » Translate interface » Extract* to choose any module or theme, extract untranslated strings to .po file and then Import that file back when translated. This potx module scans for strings wrapped with t() function.

---
Tags: drush, i18n-l10n
---


title: Enable error logging from settings.php

text:
# Question
Title: Enable error logging from settings.php
I am facing a *The website encountered an unexpected error.* error while setting up the site.

Most of the documentation I found online suggest to enable logging in the *setting.php* file.

I added this line in *site/default/settings.php*.

```
$config['system.logging']['error_level'] = 'verbose';

```

I added the following code.

```
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);

```

I added the following lines to the same *settings.php* file, at the bottom.

```
if (file_exists(__DIR__ . '/settings.local.php')) {
  include __DIR__ . '/settings.local.php';
}

```

I copied the *sites/example.sites.php* file to *sites/default/* and renamed it *settings.local.php*, as suggested on https://catwolf.org/qs?id=180d30e9-9dcb-4e96-b030-7df34c77f5ab&x=x and https://www.drupal.org/forum/support/post-installation/2018-07-18/enable-drupal-8-backend-errorlogdebugging-mode.

I restarted Apache and reloaded the site, but I see the same error shows again without any log.

I am migrating a Drupal 7.84 site from CentOS 6.10 to CentOS 7.9. The httpd passes from version 2.2.15 to version 2.4.6; it also has PHP 7.03 and MariaDB 10.3.

Did I miss anything, or do I have to do anything else to enable the logging?

# Answer
Here's the official manual for Drupal 7: https://www.drupal.org/docs/7/creating-custom-modules/show-all-errors-while-developing

At the end of your `sites/default/settings.php`, add the following lines:

```
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
$conf['error_level'] = 2;

```

Please pay extra attention to the last line, as it is specific to D7. Your code was for D8/D9.

Of course those changes can be also made to `settings.local.php`, but I would try to do it after making sure that `$conf['error_level'] = 2;` works at all at the end of `settings.php`.
> 1 votes

# Answer
Please try the following:

* in the webroot, rename `.htaccess` to `.htaccess.old` -- Apache 2.4 has changed syntax in some directives that may be causing the error before PHP is even reached. Also check apache log files ( usually `/var/log/httpd/error.log`, see your VirtualHost for other paths )
* check your PHP file logs, since some errors never make it to the screen even with all the above listed directives -- on CentOS these are normally under `/var/log/php*`

One of the above should have a log entry on what went wrong.
> 1 votes

---
Tags: 7, debugging, log-messages
---


# Question
Title: After update to PHP 8, module no longer works
I have updated my Drupal from 8 to 9 on PHP 7.4. Now I am updating to PHP 8. Everything runs so far, only with a self-built module I get now an Error message:

```
The website encountered an unexpected error. Please try again later.
Error: Attempt to assign property "format" on null in copy_fields_from_model_contract_node_edit_node_prepare_form() (line 57 of modules/custom/copy_fields_from_model_contract_node_edit/copy_fields_from_model_contract_node_edit.module).

```

This concerns exactly this line:

```
$node->body->format = 'full_html';

```

The code of my module is not elegant, but it did its job as desired. Basically, I copy from another node the content of the body field into a body field of another node. The code of the whole module looks like this:

```
<?php

use Drupal\node\NodeInterface;
use Drupal\Core\Form\FormStateInterface;

/**
* Implements hook_ENTITY_TYPE_prepare_form() for node entities.
*/
function copy_fields_from_model_contract_node_edit_node_prepare_form(NodeInterface $node, $operation, FormStateInterface $form_state)
{

//TODO Build NID as array and go through with loop

  $arr_vertraege = array(6766, 6769, 6768, 6775, 6767, 6776, 6777, 6778, 6779, 6780, 6781, 6782);

  foreach ($arr_vertraege as $mustervertraege) {

    $nid = $mustervertraege;
    $node_storage = \Drupal::entityTypeManager()->getStorage('node');
    $node = $node_storage->load($nid);

    if ($nid == $mustervertraege) {

      $getcontract = $node->field_vertragsinhalt->value;
    }

  //TODO Works without ($operation == 'default')

  if ($operation == 'edit') {
    /*dpm("Modul an edit");*/
    //TODO Shall always pull the current node and nid. But does not work yet with node creation. Error: Call to a member function id() on null in

    $node = \Drupal::routeMatch()->getParameter('node');
    $nid = $node->id();
    $node_storage = \Drupal::entityTypeManager()->getStorage('node');
    $node = $node_storage->load($nid);

    if ($node->getType() == 'buch_anlegen') {
      if ($node->field_vertrag_updaten->value == '1') {
        //  dpm($node->field_vertrag_updaten->value);
        //var_dump($node);

        if ($mustervertraege == $arr_vertraege[0]) {
          $node->field_vertragsinhalt->value = $getcontract;
          $node->body->format = 'full_html';
          // don't save the node yet, it will be saved after submitting
        }
      }
      if ($node->field_vertrag_updaten_2->value == '1') {
        //  dpm($node->field_vertrag_updaten->value);
        //var_dump($node);

        if ($mustervertraege == $arr_vertraege[1]) {
          $node->field_vertragsinhalt_2->value = $getcontract;
          $node->body->format = 'full_html';
          // don't save the node yet, it will be saved after submitting
        }
      }
      if ($node->field_vertrag_updaten_3->value == '1') {
        //  dpm($node->field_vertrag_updaten->value);
        //var_dump($node);

        if ($mustervertraege == $arr_vertraege[2]) {
          $node->field_vertragsinhalt_3->value = $getcontract;
          $node->body->format = 'full_html';
          // don't save the node yet, it will be saved after submitting
        }
      }
      if ($node->field_vertrag_updaten_4->value == '1') {
        //  dpm($node->field_vertrag_updaten->value);
        //var_dump($node);

        if ($mustervertraege == $arr_vertraege[3]) {
          $node->field_vertragsinhalt_4->value = $getcontract;
          $node->body->format = 'full_html';
          // don't save the node yet, it will be saved after submitting
        }
      }
      if ($node->field_vertrag_updaten_5->value == '1') {
        //  dpm($node->field_vertrag_updaten->value);
        //var_dump($node);

        if ($mustervertraege == $arr_vertraege[4]) {
          $node->field_vertragsinhalt_5->value = $getcontract;
          $node->body->format = 'full_html';
          // don't save the node yet, it will be saved after submitting
        }
      }
      if ($node->field_vertrag_updaten_6->value == '1') {
        //  dpm($node->field_vertrag_updaten->value);
        //var_dump($node);

        if ($mustervertraege == $arr_vertraege[5]) {
          $node->field_vertragsinhalt_6->value = $getcontract;
          $node->body->format = 'full_html';
          // don't save the node yet, it will be saved after submitting
        }
      }
      if ($node->field_vertrag_updaten_7->value == '1') {
        //  dpm($node->field_vertrag_updaten->value);
        //var_dump($node);

        if ($mustervertraege == $arr_vertraege[6]) {
          $node->field_vertragsinhalt_7->value = $getcontract;
          $node->body->format = 'full_html';
          // don't save the node yet, it will be saved after submitting
        }
      }
      if ($node->field_vertrag_updaten_8->value == '1') {
        //  dpm($node->field_vertrag_updaten->value);
        //var_dump($node);

        if ($mustervertraege == $arr_vertraege[7]) {
          $node->field_vertragsinhalt_8->value = $getcontract;
          $node->body->format = 'full_html';
          // don't save the node yet, it will be saved after submitting
        }
      }
      if ($node->field_vertrag_updaten_9->value == '1') {
        //  dpm($node->field_vertrag_updaten->value);
        //var_dump($node);

        if ($mustervertraege == $arr_vertraege[8]) {
          $node->field_vertragsinhalt_9->value = $getcontract;
          $node->body->format = 'full_html';
          // don't save the node yet, it will be saved after submitting
        }
      }
      if ($node->field_vertrag_updaten_10->value == '1') {
        //  dpm($node->field_vertrag_updaten->value);
        //var_dump($node);

        if ($mustervertraege == $arr_vertraege[9]) {
          $node->field_vertragsinhalt_10->value = $getcontract;
          $node->body->format = 'full_html';
          // don't save the node yet, it will be saved after submitting
        }
      }
      if ($node->field_vertrag_updaten_11->value == '1') {
        //  dpm($node->field_vertrag_updaten->value);
        //var_dump($node);

        if ($mustervertraege == $arr_vertraege[10]) {
          $node->field_vertragsinhalt_11->value = $getcontract;
          $node->body->format = 'full_html';
          // don't save the node yet, it will be saved after submitting
        }
      }
      if ($node->field_vertrag_updaten_12->value == '1') {
        //  dpm($node->field_vertrag_updaten->value);
        //var_dump($node);

        if ($mustervertraege == $arr_vertraege[11]) {
          $node->field_vertragsinhalt_12->value = $getcontract;
          $node->body->format = 'full_html';
          // don't save the node yet, it will be saved after submitting
        }
      }
    }
  }
  }
}

```

Does anyone have a tip for me? I would be very grateful.

Greetz Bavra

P.S.: Complete error message from the logs:

```
Message    Error: Attempt to assign property "format" on null in copy_fields_from_model_contract_node_edit_node_prepare_form() (line 57 of /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/web/modules/custom/copy_fields_from_model_contract_node_edit/copy_fields_from_model_contract_node_edit.module)
#0 [internal function]: copy_fields_from_model_contract_node_edit_node_prepare_form(Object(Drupal\node\Entity\Node), 'edit', Object(Drupal\Core\Form\FormState))
#1 /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/web/core/lib/Drupal/Core/Entity/EntityForm.php(397): call_user_func_array('copy_fields_fro...', Array)
#2 /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/web/core/lib/Drupal/Core/Entity/EntityForm.php(129): Drupal\Core\Entity\EntityForm->prepareInvokeAll('node_prepare_fo...', Object(Drupal\Core\Form\FormState))
#3 /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/web/core/lib/Drupal/Core/Entity/ContentEntityForm.php(287): Drupal\Core\Entity\EntityForm->init(Object(Drupal\Core\Form\FormState))
#4 /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/web/core/lib/Drupal/Core/Entity/EntityForm.php(96): Drupal\Core\Entity\ContentEntityForm->init(Object(Drupal\Core\Form\FormState))
#5 [internal function]: Drupal\Core\Entity\EntityForm->buildForm(Array, Object(Drupal\Core\Form\FormState))
#6 /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/web/core/lib/Drupal/Core/Form/FormBuilder.php(531): call_user_func_array(Array, Array)
#7 /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/web/core/lib/Drupal/Core/Form/FormBuilder.php(278): Drupal\Core\Form\FormBuilder->retrieveForm('node_buch_anleg...', Object(Drupal\Core\Form\FormState))
#8 /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/web/core/lib/Drupal/Core/Controller/FormController.php(73): Drupal\Core\Form\FormBuilder->buildForm(Object(Drupal\node\NodeForm), Object(Drupal\Core\Form\FormState))
#9 [internal function]: Drupal\Core\Controller\FormController->getContentResult(Object(Symfony\Component\HttpFoundation\Request), Object(Drupal\Core\Routing\RouteMatch))
#10 /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/web/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(123): call_user_func_array(Array, Array)
#11 /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/web/core/lib/Drupal/Core/Render/Renderer.php(564): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
#12 /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/web/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(124): Drupal\Core\Render\Renderer->executeInRenderContext(Object(Drupal\Core\Render\RenderContext), Object(Closure))
#13 /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/web/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(97): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array)
#14 /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/vendor/symfony/http-kernel/HttpKernel.php(158): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
#15 /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/vendor/symfony/http-kernel/HttpKernel.php(80): Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object(Symfony\Component\HttpFoundation\Request), 1)
#16 /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/web/modules/contrib/redirect_after_login/src/RedirectMiddleware.php(46): Symfony\Component\HttpKernel\HttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#17 /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/web/core/lib/Drupal/Core/StackMiddleware/Session.php(58): Drupal\redirect_after_login\RedirectMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#18 /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/web/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(48): Drupal\Core\StackMiddleware\Session->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#19 /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/web/core/modules/page_cache/src/StackMiddleware/PageCache.php(106): Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#20 /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/web/core/modules/page_cache/src/StackMiddleware/PageCache.php(85): Drupal\page_cache\StackMiddleware\PageCache->pass(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#21 /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/web/core/modules/ban/src/BanMiddleware.php(50): Drupal\page_cache\StackMiddleware\PageCache->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#22 /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/web/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(48): Drupal\ban\BanMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#23 /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/web/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(51): Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#24 /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#25 /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/web/core/lib/Drupal/Core/DrupalKernel.php(708): Stack\StackedHttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#26 /home/bavramor/data/Webdesign/Webseiten/landwehrcie-backup-drupal9/web/index.php(19): Drupal\Core\DrupalKernel->handle(Object(Symfony\Component\HttpFoundation\Request))
#27 {main}

```

# Answer
> 0 votes
Most likely there are nodes that do not have a body set, thus `$node->body` returns `null`.

Since using PHP8, you can try the null safe operator by simply doing:

```
$node->body?->format = 'full_html';

```

( pay close attention the the `?->` operator which means "if null just abort the chain" )

If the node that exhibits the issue does have a body, please update the question.

*Edit:* Corrected position of null safe operator as per comments.

---
Tags: 9
---


title: Get Webform Category Name in Preprocess

text:
# Question
Title: Get Webform Category Name in Preprocess
I am trying to get the Category name (from the General form settings) for webforms in a preprocess function, ultimately to add as a class to the webform wrapper.

**MYMODULE.module**:

```
function MYMODULE_preprocess_webform(&$variables) {
  kint($variables);
}

```

Right now I don't see a "category" value in the array structure.

How do you get this value in preprocess? Maybe I need a different hook?

# Answer
Not quite clear on the question actually. So you're saying you want to get the settings value from webform element "Category" list before it's submitted and set it as your form wrapper?

Here's some tip I recently learned, hope it helps.

```

// get all webform submissions id from my_webform
$query = \Drupal::entityQuery('webform_submission')->condition('webform_id', 'my_webform');
$result = $query->execute();

// load all submission data from webform submission storage
$webformStorage = \Drupal::entityTypeManager()->getStorage('webform_submission');
$submissions = $webformStorage->loadMultiple($result);

```

If you want to get the submission values form webform, then you can use **WebformSubmission** class.

If your want to get the element settings value that user sees from your category list, then you'll need **Webform** class to load the options array from the webform element.

```
use Drupal\webform\Entity\WebformSubmission;
use Drupal\webform\Entity\Webform;

// get sid from your preprocess or elsewhere
$webform_submission = WebformSubmission::load($sid);

// This will return the data array of submitted values from sid given.
$data = $webform_submission->getData();

// Load webform with bundle type id, normally it's your webform machine name
$webform = Webform::load($sid->bundle());

// Get element definition in webform element
$select_list_element = $webform->getElement('key_name');

// for options in Select list. this may vary depending on what element type you're using
$select_list = $select_list_element['#options'];

```
> 0 votes

# Answer
A couple approaches I found to accomplish this:

**MYMODULE.module:**

```
/**
 * Implements hook_form_alter().
 */
function MYMODULE_form_alter(&$form, FormStateInterface $form_state, $form_id) {

  if ($form['#webform_id']) {
    $webformCategory = strtolower($form_state->getFormObject()->getWebform()->get('category'));
    array_push($form['#attributes']['class'], 'webform-category-' . $webformCategory);
  }
}

```

^ There may be better ways to verify we have a webform other than `if ($form['#webform_id'])` to prevent errors from `->getWebform` and `->getEntity` methods on non-webform forms.

**MYCUSTOMHANDLER.php:**

```
class CustomHandler extends WebformHandlerBase {
  public function alterForm(array &$form, FormStateInterface $form_state, WebformSubmissionInterface $webform_submission) {
    $webformCategory = strtolower($webform_submission->getWebform()->get('category'));
    array_push($form['#attributes']['class'], 'webform-category-' . $webformCategory);
  }
}

```

This works well but would need to be included in each handler to apply this class to all webforms in question.
> 0 votes

# Answer
The webform isn't there yet in the preprocess function you need to load it first.

```
use Drupal\Component\Utility\Html;

/**
 * Implements hook_preprocess_HOOK().
 */
function MYMODULE_preprocess_webform(&$variables) {
  /** @var \Drupal\webform\WebformInterface $webform */
  $webform = \Drupal::service('entity_type.manager')->getStorage('webform')->load($variables['element']['#webform_id']);
  if (!empty($webform->toArray()['category'])) {
    $category = $webform->toArray()['category'];
    $variables['attributes']['class'][] = Html::cleanCssIdentifier('category-' . strtolower($category));
  }
  else {
    $variables['attributes']['class'][] = 'category-none';
  }
}

```
> 0 votes

---
Tags: 8, webforms
---


# Question
Title: Dynamically change the "access denied page" title and body
This is not a question about changing or preprocessing the twig template of the 403 page.

I have a controller with an `access` function called by the `_custom_access` key of my route.

In some circumstance, this function is returning `AccessResult::forbidden("a good reason");` which lead to my default `403` page.

Most of the case, this is the behavior I would like to have, but in few cases, I would like to have another title and another message for this page like "You can not access because you eat too much chocolate"

Should I create a new custom error route and send a new response to this route from my `access` function? (hint: I tried without success)

Any idea?

**Edit to add:** Thanks to 4k4, I have added a custom `ExceptionHtmlSubscriber` extending `DefaultExceptionHtmlSubscriber` in order to override the `on403()` method.

Here is my code:

```
class TXSExceptionHtmlSubscriber extends DefaultExceptionHtmlSubscriber {
  /**
   * {@inheritdoc}
   */
  public function on403(ExceptionEvent $event) {
    $my_condition=extract_from_event($event);
    if($my_condition){
      // I am not sure it is what should be done here
      $this->makeSubrequest($event, '/my_custom_error_path/my_custom_error_code', Response::HTTP_FORBIDDEN);
    }else {
      parent::$this->on403($event);
    }
  }
  /**
   * {@inheritdoc}
   */
  protected static function getPriority() {
    return 1000;
  }
}

```

So far, I have 2 questions:

1-Unfortunately my custom `on403()` method is never called (on the other hand, the `on403()` method `DefaultExceptionHtmlSubscriber` is called): What I am missing?

2-Let say that my custom `on403()` method is called: what should be the code to redirect the user on my custom `403` page containing my custom message (ie: it could be nice to display the reason of the AccessResult `AccessResult::forbidden("a good reason");`

# Answer
Access functions are only for calculating access results. You can't do anything else because this function is called multiple times, not only for incoming routes but also for outbound links.

To set a response use `on403()` of an ExceptionHtmlSubscriber. You can also set the result of a sub-request if you want a themed 403 message. See How can we catch the acces denied exception?

**Edit: Answering further questions**

> 1-Unfortunately my custom on403() method is never called (on the other hand, the on403() method DefaultExceptionHtmlSubscriber is called): What I am missing?

You need to register the service in *mymodule.services.yml*:

```
services:
  mymodule.exception_subscriber:
    class: Drupal\mymodule\EventSubscriber\MymoduleExceptionHtmlSubscriber
    tags:
      - { name: event_subscriber }

```

> 2-Let say that my custom on403() method is called: what should be the code to redirect the user on my custom 403 page containing my custom message (ie: it could be nice to display the reason of the AccessResult AccessResult::forbidden("a good reason");

You find the message in the exception object:

```
public function on403(ExceptionEvent $event) {
  // ...
  if ($my_condition) {
    $message = $event->getThrowable()->getMessage();
    $event->setResponse(new Response($message, 403));
  }
}

```

BTW you don't need to call `parent::on403($event)` because the parent registered with a lower priority will be called automatically when your event subscriber doesn't set a response.
> 1 votes

# Answer
If your eventsubscriber is not working yet, try updating the services yml file and adding the constructor to the class.

```
services:
  mymodule.exception_subscriber:
    class: Drupal\mymodule\EventSubscriber\MymoduleExceptionHtmlSubscriber
    tags:
      - { name: event_subscriber }
    arguments: ['@config.factory', '@http_kernel', '@logger.channel.php', '@redirect.destination', '@router.no_access_checks', '@access_manager']

```
> 0 votes

---
Tags: routes
---


# Question
Title: Webform Conditional based on previous webform entry
I have a webform that we have people fill out two times - for a pre-test and a post-test and I am looking for a way to determine if a pre-test has been taken an if so, set a specific value on form element.

Currently we have people manually select whether they are taking the pre-test or post-test in a webform element, but they are not doing so very accurately. Given we have the data to know that, I am hoping to be able to determine that automatically in a webform field.

Here are some more details:

* We gather a unique participant id (c\_ID)
* Based on that participant ID I am hoping to validate if a previous webform submission with that participant ID exists.
    + **If not**: Provide a value of "pre-test"
    + **If so**: Provide a value of "post-test"

Does anyone have any recommendations on how something like this could be done?

# Answer
Configure the select element containing "pre-test" and "post-test" options to have "Prepopulate" checked. Remember the machine name of the element, that will be the query parameter for the succeeding steps (e.g. `submission_type`)

Then create a block with a textfield asking for a participant ID. On submit, it would look for a webform submission already containing the ID. Then one of two things can happen:

* If a submission doesn't exist yet, redirect the user to the webform with the "pre-test" pre-selected (e.g. `?submission_type=pre-test`)
* If a submission already exists, redirect the user to a webform with "post-test" pre-selected. (e.g. `?submission_type=post-test`)

Then pop this block wherever you want to start this workflow, like on some page. As an added bonus, you could make the block ask for the query parameter name used in the redirect during its configuration. That way, you can match the webform element name without having to dive into code later should you rename it.

I also recommend putting a warning saying the field was pre-filled based on participant ID's existence. Prefills are easy to overlook.

---

Alternatively, just provide two links to the webform, one with `?submission_type=pre-test` bolted onto its url and the other with `?submission_type=post-test`. Same effect minus the participant ID checking and custom block (assuming the user chooses the right link).
> 1 votes

---
Tags: webforms
---


# Question
Title: Warning: file_put_contents(): Filename cannot be empty
After updating Drupal 8 to Drupal 9, the following error is displayed on the website page:

> Warning: file\_put\_contents(): Filename cannot be empty in /var/www/html/web/modules/contrib/devel/src/DevelDumperManager.php on line 108

Although it is not in the logs. But there is a similar one:

> Warning: file\_put\_contents(): Filename cannot be empty in Drupal\search\_api\_solr\_devel\Logging\SolariumRequestLogger-\>preExecuteRequest() (line 148 of /var/www/html/web/modules/contrib/search\_api\_solr/modules/search\_api\_solr\_devel/src/Logging/SolariumRequestLogger.php)

Perhaps these errors are somehow related. How I do fix these errors?

I tried debugging and this is what xdebug shows for the first error:

# Answer
> 0 votes
The site missing some or all of the Devel module settings. In this case the one missing is `debug_logfile`. I think the simplest way forward would be to uninstall Devel-related modules and reinstall them so their configurations may be restored. If you are using configuration management, you could add the settings to the exported configuration then re-import them. The are in `devel.settings.yml` in the Devel module.

---
Tags: 9
---


# Question
Title: Set a <front> page for different languages?
Is there a way to specify the front page per language of a Drupal 9 site? I am looking for an interface that allows this to be set by a user.

# Answer
If you want to allow user input, it's not an easy task. I would do the following:

1. Create a custom form (not a configuration form), using the Form API, that allows the user to input the path to the front page. https://www.drupal.org/docs/drupal-apis/form-api/introduction-to-form-api

How you work this form to manage the language is up to you - you could either use the current interface language, or you could add an additional input for language on the form.
2. In the submit handler for the form, save the submitted values to the Drupal State, using the State API. The State API is environment-specific storage, it is not exported/imported with configuration between environments. https://www.drupal.org/docs/8/api/state-api/overview
3. Dynamically override the `system.site.page.front` configuration, using the value retrieved from the state for the current language. https://www.drupal.org/docs/drupal-apis/configuration-api/configuration-override-system#s-providing-overrides-from-modules
> 0 votes

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


# Question
Title: How to add an unique wrapper id to a widget element?
I made a widget that "generates" fields based on the option chosen of a select field. I got most of the logic done:

1- An option is chosen from this field:

```
 $element['value'] = [
  '#type' => 'select',
  '#options' => $this->getBreakpointsGroups(),
  '#default_value' => $value,
  '#ajax' => [
    'callback' => [$this,'updateBreakpoint'], // don't forget :: when calling a class method.
    'event' => 'change',
    'wrapper' => $wrapper_id, // This element is updated with this AJAX callback.
    'method' => 'html',
  ]
];

```

2-Then the function updateBreakpoint is executed and it proceeds to update the form. First I extract the wrapper value in case there are multiple widget forms so it doesn't target another instance of the same form.

```
    $state = $form_state->getTriggeringElement();
    $wrapper_id = $state['#ajax']['wrapper'];

```

3- After making some changes to the form I return the changes and use an AJAX command

```
    $ajax_response = new AjaxResponse();
    $ajax_response->addCommand(new HtmlCommand("div#{$wrapper_id}",  $form['image_fieldset']));
    return $ajax_response;

```

The wrapper is defined inside the formElement function in the following way:

```
    $wrapper_id = Html::getUniqueId('wrapper');

```

The problem is that no change is made. When I define $wrapper\_id as a string without any unique id functions ('wrapper' for example) the changes are made. I'm confused as to why. I made sure to check the value of the wrapper persisted in all the form.

# Answer
> 0 votes
If you have multiple wrappers on the same page you can't rely solely on Html::getUniqueId. You need to add as much predictable information as available.

For example in a single field widget element you can add the field name, the field parents and the field delta:

```
$field_name = $this->fieldDefinition->getName();
$parents = $form['#parents'];
$prefix = implode('-', array_merge($parents, [$field_name, $delta]));

$wrapper_id = $prefix . '-my-wrapper';

```

---
Tags: forms, webforms, ajax
---


# Question
Title: Hide the term id on an autocomplete widget
How can I hide the term ID on an autocomplete widget?

# Answer
There is no built-in option to hide term ID from entity\_autocomple form element. Its behavior is simple, if there is a term that match then shows (TID).
> 2 votes

# Answer
Here is the trick to hide Reference EntityID using Javascript/jQuery.
This is not a proper way to hide it, but it works.

```
jQuery("body").find('.form-autocomplete').change(function() {
            var val = jQuery(this).val();
            var match = val.match(/\((.*?)\)$/);
            if (match) {
                jQuery(this).data('real-value', val);
                jQuery(this).val(val.replace(' ' + match[0], ''));
            }
        });

```
> 1 votes

# Answer
For Drupal 8 I would adjust your code a bit to remove the number after selecting the suggested value, not after user click outside the input:

```
    jQuery("body").find('.form-autocomplete').on('autocompleteclose', function(event, node) {
  var val = jQuery(this).val();
  var match = val.match(/\((.*?)\)$/);
  if (match) {
    jQuery(this).data('real-value', val);
    jQuery(this).val(val.replace(' ' + match[0], ''));
  }
});

```
> 1 votes

# Answer
Similar to other jQuery-related answers, you could add an function and call it for each form field or an entire form:

```
    function autocomplete_tid_remove(field){

        var autocomplete_tid = $(field);

        var autocomplete_tid_stripped = $(autocomplete_tid).val().split(" ")[0];

        $(field).val(autocomplete_tid_stripped);

    }

    var selector = "[**put form id here**]";

    autocomplete_tid_remove(selector + " .form-autocomplete");

```
> 0 votes

# Answer
Here is a complete js file for removing TID's onload *and* onchange

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

 Drupal.behaviors.my_module = {
   attach: function(context) {

  // Remove TID's onload.
  Drupal.my_module.remove_tid();

  // Remove TID's onchange.
  jQuery('body').find('.form-autocomplete').on('autocompleteclose', function() {
    Drupal.my_module.remove_tid();
  });
}
};

Drupal.my_module = {
  remove_tid: function () {
    let field_autocomplete = jQuery('body').find('.form-autocomplete');
    let autocomplete_tid_stripped = field_autocomplete.val().split(' ')[0];
    field_autocomplete.val(autocomplete_tid_stripped);
  },
};

})(jQuery, Drupal, drupalSettings);

```
> 0 votes

# Answer
I have done small changes in code and working fine for me. thank's

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

  Drupal.behaviors.common = {
    attach: function(context) {
      // Remove TID's onload.
      Drupal.common.remove_tid();

      // Remove TID's onchange.
      jQuery('body').find('.form-autocomplete').on('autocompleteclose', function() {
        Drupal.common.remove_tid();
      });
    }
  };

  Drupal.common = {
    remove_tid: function () {
      var field_autocomplete = jQuery('body').find('.form-autocomplete');
      field_autocomplete.each(function (event, node) {
        var val = $(this).val();
        var match = val.match(/\((.*?)\)$/);
        if (match) {
          $(this).data('real-value', val);
          $(this).val(val.replace(' ' + match[0], ''));
        }
      });

    }
  };
})(jQuery, Drupal, drupalSettings);

```
> 0 votes

# Answer
For more than a taxonomy term, I made a slight change in the code shown by another answer.

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

  Drupal.behaviors.common = {
    attach: function(context) {
      // Remove TID's onload.
      Drupal.common.remove_tid();

      // Remove TID's onchange.
      jQuery('body').find('.form-autocomplete').on('autocompleteclose', function() {
        Drupal.common.remove_tid();
      });
    }
  };

  Drupal.common = {
    remove_tid: function () {
      var field_autocomplete = jQuery('body').find('.form-autocomplete');
      field_autocomplete.each(function (event, node) {
        var val = $(this).val();
        var match = val.match(/\s\(.*?\)/g);
        if (match) {
          $(this).data('real-value', val);
          $(this).val(val.replace(/\s\(.*?\)/g, '' ));
        }
      });
    }
  };
})(jQuery, Drupal, drupalSettings);

```
> 0 votes

# Answer
In full JS :

```
(Drupal => {
  Drupal.behaviors.autocompleteRemoveTid = {
    attach: context => {
      // Remove TID's onload.
      Drupal.autocompleteRemoveTid.removeTid(context);

      // Remove TID's onchange.
      context.querySelectorAll(".form-autocomplete").forEach(element => {
        element.addEventListener("change", () => {
          Drupal.autocompleteRemoveTid.removeTid(context);
        });
      })
    }
  };

  Drupal.autocompleteRemoveTid = {
    removeTid: context => {
      context.querySelectorAll(".form-autocomplete").forEach(element => {
        let val = element.value;
        const match = val.match(/\(([0-9]+)\)$/);
        if (match) {
          element.value = val.replace(" " + match[0], "");
        }
      });
    }
  };
})(Drupal);

```
> 0 votes

---
Tags: 8
---


title: Display paragraphs field as a table?

text:
# Question
Title: Display paragraphs field as a table?
Is there a way to display the output of a multiple value paragraphs field in a node as a table? The paragraphs has three fields in it, and I would like to display with labels in the header row, and content rows containing the data for each row in the paragraph.

# Answer
> 1 votes
You can template paragraph types. In the template, you build the values into whatever output fits your needs. Create a new template type, and loop through the values to create the table.

How to theme paragraphs: https://www.drupal.org/docs/contributed-modules/paragraphs/theming-paragraphs-in-drupal-8

---
Tags: paragraphs
---