# Question
Title: How can I get the href value from a link?
I have a link I render in a template file with the following markup.

```
{{ item.caption.link }}

```

I want to show only the `href` value. None of the following worked for me.

```
{{ item.caption.link[0]['#url'] }}
{{ item.caption.link.0.url }}
{{ item.caption.link[0]['#url']|render }}

```

# Answer
I think it might be better if you pass just href value as a variable to Twig and not the complete HTML markup.

Put HTML in Twig and just fill it with the values (href value in that case) you need.
> 0 votes

# Answer
First look for the available attributes with this tutorial on XDebug.

Another method to inspect a variable is to use the `dd()` function. See this question to install it. Put the following in your template, it will print all the available attributes of your link :

```
{{ dd(item.caption.link) }}

```

Try the following :

```
{{ item.caption.link.0['#url'] }}

```

If this doesn't work, you should access at least to the `uri`. See this question to get an URL from the URI.
> 0 votes

---
Tags: theming, 10
---


title: How to load a CSS file from a URL?

text:
# Question
Title: How to load a CSS file from a URL?
I have a raw github page with CSS:

```
https://raw.githubusercontent.com/USER/REPOSITORY/main/website/style.css

```

How to load it in my website to effect the website globally?

I tried this in `/core/themes/olivero/olivero.libraries.yml` but no change occured after cache rebuild.

```
global-styling:
  version: VERSION
  css:
    base:
      css/base/fonts.css: {}
      css/base/base.css: {}
      css/base/utility.css: {}
      css/base/https://raw.githubusercontent.com/USER/REPOSITORY/main/website/style.css: {}

```

# Answer
> 1 votes
External files can be used, but for a CSS file the definition is similar to the following one.

```
https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css: { type: external, minified: true }

```

`type: external` is necessary to make Drupal know that is an external file; `minified: true` is used only when the file is minified.

As comments already said, it is a bad idea to change a Drupal core file because those changes are lost all the times Drupal is updated. Instead, it is better to create a custom theme, which can also use the core theme as parent theme.

---
Tags: theming, 10
---


# Question
Title: Set cacheability on a custom route returning XML
I'm working on fixing a custom module that returns XML from a certain route. Essentially what is described in the answers of this question.

Currently this route isn't being cached. I've tried to switch to using `CacheableResponse` and setting cache tags but received the error "The controller result claims to be providing relevant cache metadata, but leaked metadata was detected"

At this point I feel a bit lost. How can I adjust caching for this route so it's cached until the returned XML changes?

Here's my current response:

```
/**
 * Class CustomEntityFeedController.
 *
 * @package Drupal\my_module\Controller
 */
class CustomEntityFeedController extends ControllerBase {
    public function feed() {
        // Query for custom entities, loop through them, and build XML output.
        $xml = $xml_content_header . $xml_content . $xml_content_footer;
        $response = new Response();
        $response->headers->set('Content-Type', 'xml');
        $response->setContent($xml);
    }
}

```

# Answer
So you did already manage to return a cacheable response object, otherwise you wouldn't be able to produce this error message. The cause of the error is bubbled-up metadata, for example from an entity query with access checks or from a rendered URL. For each, you find solutions by searching for the error message. If not, you can put the entire XML processing in its own render context. See How to get cache metadata from nested render array when returning response?
> 1 votes

# Answer
Instead of using a Response object, you need to use a `\Drupal\Core\Cache\CacheableResponse` object. The `CacheableResponse` class extends the `Response` class; it adds the `addCacheableDependency()` method can be used as in the following code.

```
$response = new CacheableResponse($user->label());
$response->addCacheableDependency($user);

```

What passed to `addCacheableDependency()` can be any object from which the cached data depends.
> 0 votes

---
Tags: routes, caching
---


title: External CSS file isn't loaded

text:
# Question
Title: External CSS file isn't loaded
I am trying to load an external CSS file to globally effect my website.

I have tried the following in `/core/themes/olivero/olivero.libraries.yml` but no change occured after cache rebuild.

```
global-styling:
  version: VERSION
  css:
    base:
      css/base/fonts.css: {}
      css/base/base.css: {}
      css/base/utility.css: {}
      css/base/https://raw.githubusercontent.com/USER/REPOSITORY/main/website/style.css: { type: external, minified: false}

```

Removing `css/base/` from before the URL doesn't matter.

Why does the external CSS file isn't loaded?

# Answer
This:

```
global-styling:
  version: VERSION
  css:
    base:
      css/base/fonts.css: {}
      css/base/base.css: {}
      css/base/utility.css: {}
      css/base/https://raw.githubusercontent.com/USER/REPOSITORY/main/website/style.css: { type: external, minified: false}

```

Should be this:

```
global-styling:
  version: VERSION
  css:
    base:
      css/base/fonts.css: {}
      css/base/base.css: {}
      css/base/utility.css: {}
      //raw.githubusercontent.com/USER/REPOSITORY/main/website/style.css: { type: external, minified: false}

```

Make sure you clear the registry when you're done. If it doesn't load, look at the HTML source, and show us what is being loaded.
> 0 votes

---
Tags: theming
---


# Question
Title: How can I change the place of the js and css files generated by advagg to another place
How can I change the place of the js and css files generated by advagg to another place, for example in a folder at the root of the site.

Thanks for your help

# Answer
The assets output location is configurable as of Drupal 10.1.0.
> 0 votes

---
Tags: css-js-aggregation
---


# Question
Title: Content type with 3 paragraphs fields: How can I output to different parts of page.html.twig?
I have a content type called (*My Page*) with 3 paragraph types, for a slider to display images, for text to display text, and for sidebar content. (The sidebar is a `<div>`, not a Drupal region/block).

Using the *page.html.twig* template, I am able to output the content to `{{ page.content }}`. However, it dumps all the content of all 3 paragraph types in one place.
I'd like to be able to separate the content as in this screenshot.

>

Paragraph type 1 should be rendered in `<header>`, paragraph type 2 in `<main>`, and paragraph type 3 in `<div id="sidebar">`.

The *page.html.twig* template is the following. (I simplified the section responsible for outputting the paragraph type content.)

{% block content %}

\*****I'd like to paragraph type 1 to output here\**** {{ page.content }} \*****I'd like to paragraph type 2 to output here\**** \*****I'd like to paragraph type 3 to output here\**** {% endblock %} \`\`\`

I use the Barrio subtheme.

If I can just figure out how to separate the content across the 3 `<div>`, that would be great.

Is there a conditional logic I could use?

```
if paragraph type 1 machine name  = X
{{ page.content}}
if paragraph type 2 machine name = Y
{{ page.content}}
if paragraph type 3 machine name = Z
{{ page.content}}

```

This has me stumped as a newbie.

# Answer
> 3 votes
The`page.html.twig` isn't the twig which prints the content of a node, but rather one which prints the markup of the entire page.

To avoid confusion, I'm taking the `article` node bundle as example. If you want to customize its content you should override`node--article--full.html.twig` or `node--article.html.twig` for all the displays of the bundle.

Inside you can use

```
 {% block content %}
  <header> {{content.field_paragraph_1}}</header> {# the field name of the pragraph #}
     <main>
         {{ content | without('field_paragraph_1, field_paragraph_1) }}
     </main>

  <div id="sidebar">{{content.field_paragraph_3}}</div>
 {% endblock %}

```

You should always print all the `content` renderable array inside a `node` twig, use the `without` filter to avoid to print twice the fields you have manually inserted in other lines of the template.

---
Tags: theming, 9, paragraphs
---


title: Create custom field for shipping method

text:
# Question
Title: Create custom field for shipping method
I'm trying to add a new field called Opening Hours for commerce\_shipping\_method bundle. I'm having hard time finding examples how to do this and those I tried didn't really work for me. In fact, not even adding a simple numeric field didn't work for me.

I have 2 YML files in a custom module where I define the testing numeric field.

### custom\_module.field\_instance.yml

```
langcode: en
status: true
dependencies:
  config:
    - field.storage.commerce_shipping_method.field_opening_hours_x
id: commerce_shipping_method.commerce_shipping_method.field_opening_hours_x
field_name: field_opening_hours_x
entity_type: commerce_shipping_method
bundle: commerce_shipping_method
label: 'Test Numeric Field'
description: 'A test field'
required: false
default_value: {  }
default_value_callback: ''
settings: {  }
third_party_settings: {  }
deleted: false

```

### custom\_module.field\_storage.yml

```
langcode: en
status: true
dependencies:
  module:
    - commerce_shipping
id: commerce_shipping_method.field_opening_hours_x
field_name: field_opening_hours_x
entity_type: commerce_shipping_method
type: decimal
module: number
settings:
  precision: 2
locked: false
cardinality: 1
translatable: true
indexes: {  }
persist_with_no_fields: true
custom_storage: false

```

When I install this module and go to edit my shipping method ( /admin/commerce/shipping-methods/manage/2?destination=/admin/commerce/shipping-methods ), the extra field is not present.

Any help or pointers to the right docs on how to do this would be appreciated.

# Answer
After some help from the slack commerce community and reviewing a Drupal article on field definitions, I came up with the following resolution:

## my\_commerce.module

Here is where the definition that's shown on form page goes. It didn't work if I simply used it in the update hook (second file below), so I had to duplicate the definition in my .module file as well.

```
/**
 * Implements hook_entity_base_field_info().
 */
function my_commerce_entity_base_field_info( EntityTypeInterface $entity_type ) {
  $fields = array();

  // add opening hours text area field to commerce shipping methods
  if ($entity_type->id() === 'commerce_shipping_method') {
    $fields['opening_hours'] = BaseFieldDefinition::create( 'text_long' )
      ->setLabel( t( 'Opening Hours' ) )
      ->setDescription( t( 'You can provide opening hours to physical pickup/payments points.' ) )
      ->setRevisionable(true )
      ->setTranslatable( true )
      ->setSetting( 'target_type', 'commerce_shipping_method' )
      ->setSetting( 'handler', 'default' )
      ->setSetting( 'handler', 'default' )
      ->setDisplayOptions('form', array(
        'type' => 'text_textarea',
        'settings' => array(
          'display_label' => true,
        ),
      ))
      ->setDisplayOptions('view', [
        'label' => 'hidden',
        'type' => 'text_long',
        'weight' => 0,
      ])
      ->setCardinality( 1 )
      ->setDisplayConfigurable( 'form', true );
  }

  return $fields;
}

```

## my\_commerce.install

While the .module file will show the Opening Hours textarea on the opening hours form, this part handles saving the value for that field into the database.

```
/**
 * Add opening hours field to all shipping methods.
 */
function my_commerce_update_9006() {
  $field_storage_definition = BaseFieldDefinition::create( 'text_long' )
    ->setLabel( t( 'Opening Hours' ) )
    ->setDescription( t( 'You can provide opening hours to physical pickup/payments points.' ) )
    ->setRevisionable(true )
    ->setTranslatable( true )
    ->setSetting( 'target_type', 'commerce_shipping_method' )
    ->setSetting( 'handler', 'default' )
    ->setDisplayOptions('form', array(
      'type' => 'text_textarea',
      'settings' => array(
        'display_label' => true,
      ),
    ))
      ->setDisplayOptions('view', [
        'label' => 'hidden',
        'type' => 'text_long',
        'weight' => 0,
      ])
    ->setCardinality( 1 )
    ->setDisplayConfigurable( 'form', true );

  \Drupal::entityDefinitionUpdateManager()
    ->installFieldStorageDefinition('opening_hours', 'commerce_shipping_method', 'commerce_shipping_method', $field_storage_definition);
}

```
> 0 votes

---
Tags: commerce
---


# Question
Title: Why can’t I print Webform submission values in an Twig-templated HTML mail?
This is further to https://www.drupal.org/project/webform/issues/2946976.

I'm trying to use a Twig template for the HTML email that is sent upon webform submission; I'm using Symfony Mailer for the HTML.

I have the twig template working, by:

1. Selecting Twig template as the formatter for the email body
2. Naming my twig file webform-email-message-html--WEBFORM-ID.html.twig and placing it in my theme folder.

Now I can't get the webform\_submission values to appear in the rendered email. I've tried:

1. `{{ webform_token('[webform_submission:values:field_name]', webform_submission, [], options) }}`
2. `{{ data.field_name }}`
3. `{webform_submission:values:field_name }}`

I have the following code in my .module file:

```
function my_module_preprocess_webform_email_message_html(&$variables) {
  /** @var \Drupal\webform\WebformSubmissionInterface $webform_submission */
  $webform_submission = $variables['webform_submission'];
  $variable['data'] = $webform_submission->getData();
}

```

When I use the three options to render the field value within Webform's internal Twig template handler, they work fine, but when I use them in my custom Twig file, they are blank.

Help is appreciated.

Thanks

# Answer
I have a webform with an email handler that use a twig template for the body field - not a separate twig file. Here's an example of one of the emails - it renders the image (vs a link to the image file).

Star Choice is an image select field. All other webform elements are text-based.

```
<h1>Congratulations!</h1>
<p>You've received a Shooting Star Award from {{ webform_token('[webform_submission:values:your_name]', webform_submission) }}!</p>
<p>{{ webform_token('[webform_submission:values:additional_comments_sent_in_email_]', webform_submission) }}

<table style="width:1200px; padding:20px; text-align:center; border: 10px solid #D4AF37">
<tr>
<td>

<table style="width:1150px; padding:20px; text-align:center; border: 5px solid #000">

<tr>
<td colspan="2">

<span style="font-family:'Brush Script MT', cursive; color:#003399;font-size:86px;">Shooting Star Award</span>
{{ webform_token('[webform_submission:values:star_choice]', webform_submission) }}

<h2 align="center">{{ webform_token('[webform_submission:values:award_recipient_name]', webform_submission) }}</h2>
<hr style="height:2px;border-width:0;color:gray;background-color:gray">
<p><strong>is hereby receiving this Shooting Star Award for</strong></p>
<p>{{ webform_token('[webform_submission:values:award_reason]', webform_submission) }}<br />
{{ webform_token('[webform_submission:values:award_comments_included_on_the_certificate_]', webform_submission) }}</p>
</td>
</tr>
<tr>
  <td><img src="https://intranet.epa.gov/sites/default/files/2020-09/seal.png" height="200" /></td>
  <td>
  <p style="font-size:24px;">PRESENTED BY: {{ webform_token('[webform_submission:values:your_name]', webform_submission) }}</p>
<hr style="height:2px;border-width:0;color:gray;background-color:gray">
<p style="font-size:24px;">ON THIS DAY: {{ webform_token('[webform_submission:created]', webform_submission) }}</p>

<hr style="height:2px;border-width:0;color:gray;background-color:gray">
</td>
</tr>
</table>
</td>
</tr>
</table>

```
> 0 votes

---
Tags: webforms
---


# Question
Title: How do I run cron as an authenticated user?
On my site, user profiles are not public; the anonymous user does not have permission to access them. However, I want to send a view containing a list of member profiles in an email with Rules Scheduler. I am attaching the view to the email with `views_embed_view()`. However, the view content is not displayed because Rules Scheduler use cron, which embeds the view as anonymous user.

So my first thought was to somehow run cron as an admininistrator, but the Drupal documentation states this:

> **Running cron as an authenticated user (Drupal 6 and earlier only)**
>
> If triggering cron.php via the methods above, cron tasks will run as the anonymous user. For most cases this should be fine, but if Drupal cron has been customized to include tasks that must run as a certain site user (e.g. custom logic has been implemented in `hook_cron()` that requires special user permissions), the following script may be useful. This script authenticates a user of your choosing before calling cron.php.
>
> **Note that as of Drupal 7, cron always runs as an anonymous user, so this will not work in Drupal 7 and later versions!**

It doesn't give the reason why, but I assume this is some kind of security issue, and there is no alternative listed. Is it flat-out impossible to run cron as an admin in Drupal 7+?

# Answer
I don't think this is possible w/o some shenanigans. If you look at `drupal_cron_run()`, you will see that it changes the user to anonymous (around line 5337 in includes/common.inc):

```
// Force the current user to anonymous to ensure consistent permissions on
// cron runs.
$original_user = $GLOBALS['user'];
$GLOBALS['user'] = drupal_anonymous_user();

```

The only way around this would be to implement a `hook_cron()` and alter its weight so that it gets called first, and have it swap in the user that you want (which can be a little tricky in some cases. This may also cause problems with some cron hooks, like `search_cron()`, which assume the anonymous user, and end up with weird results (in this case, there may be a security problem where privileged information is revealed to the anonymous user).
> 2 votes

# Answer
Had the same issue in D10 so I'm adding this for reference for people using Drupal 8, 9, 10.

```
// Call the account switcher service.
$accountSwitcher = \Drupal::service('account_switcher');
// Switch to the admin user.
$accountSwitcher->switchTo(new \Drupal\Core\Session\UserSession(['uid' => 1]));

// Render view.
$view = \Drupal\views\Views::getView('VIEW_ID');
$view->setDisplay('DISPLAY_ID');
$view->execute();
$rendered = $view->render();
$output = \Drupal::service('renderer')->render($rendered);

// Switch back to the old session.
$accountSwitcher->switchBack();

```
> 2 votes

# Answer
Given that you've said you're calling `views_embed_view()` somewhere in order to attach the view to the email, you could try following these instructions on Safely Impersonating Another User.

For Drupal 7 this would mean wrapping your `views_embed_view()` inside the following snippet:

```
global $user;
$original_user = $user;
$old_state = drupal_save_session();
drupal_save_session(FALSE);
$user = user_load(1);

// Insert your views_embed_view() code here.

$user = $original_user;
drupal_save_session($old_state);

```

Note that this example impersonates the admin user. You may wish to use a different user ID in the `$user = user_load(1);` line.
> 0 votes

---
Tags: 7, cron
---


title: CKeditor 5 generic block level styles

text:
# Question
Title: CKeditor 5 generic block level styles
Is it possible to define generic block level styles in CKeditor5, instead of targeting a specific HTML tag?

E.g. I can define this in editor.full\_html.twig

```
settings:
  plugins:
    ckeditor5_style:
      styles:
        -
          label: 'looks like header 1'
          element: '<p class="h1">'
        -
          label: 'looks like header 2'
          element: '<p class="h2">'

```

But with this setting an editor can not style e.g. an `<h3>` to look like an `.h2`

Now I could add every single combination as style

```
settings:
  plugins:
    ckeditor5_style:
      styles:
        -
          label: 'P looks like header 1'
          element: '<p class="h1">'
        -
          label: 'H2 looks like header 1'
          element: '<h2 class="h1">'
        -
          label: 'H3 looks like header 1'
          element: '<h3 class="h1">'
        -
          label: 'H4 looks like header 1'
          element: '<h4 class="h1">'
        -
          label: '...(exaggerated example 300 lines later) H1 looks like header 6...'
          element: '<h1 class="h6">'

```

and I could live with the massive YAML config, but the styles combo in the admin UI gets equally polluted and flooded with so many disabled options that it becomes unusable.

Is there a way to define a style as "generic block level style"? E.g. a style `.h1` that can be applied to any block tags like `<p>` and `<h2>` and `<h3>` and `<h4>` etc?

In CK4 a `p.h4` was also available/mouse-clickable when the cursor was inside a `<h3>`. I don't know to to migrate that.

# Answer
Currently it seems that this feature is not included in ckeditor5.
Take a look at https://github.com/ckeditor/ckeditor5/issues/12771.
I would recommend to add the styles in the YAML config once and hide the disabled styles in the panel, like described in this feature request https://github.com/ckeditor/ckeditor5/issues/12770.

```
.ck.ck-style-panel .ck-style-grid .ck-style-grid__button.ck-disabled {
    display: none; /* Add this */
}
```
> 1 votes

---
Tags: wysiwyg
---


title: How can I confirm a path is internal?

text:
# Question
Title: How can I confirm a path is internal?
In my theme's theme file I'm using preprocess\_node to create variables for a node based on the section of my site its in, and that's figured out by checking the path's alias.

So for example a path like: /events/this\_is\_my\_node

It'll see that this node is in the events section of the site and the variables provided to the twig template facilitate theming the node page based on the section it's in.

Everything works great if the node and the node that's the section's landing page have their aliases generated, but if they don't, I get an error like:

```
UnexpectedValueException: External URLs do not have internal route parameters. in Drupal\Core\Url->getRouteParameters() (line 583 of core/lib/Drupal/Core/Url.php).

```

So basically I'm trying to figure out a conditional I can use so that I'm only getting Route parameters from a path it recognizes as being internal and should something go wrong and a path alias isn't set I don't get a WSOD.

I've tried using UrlHelper::isExternal but that doesn't seem to help as it just seems to look at the path structure to determine if its external or not vs actually check the page.

This is my code:

```
function THEME_preprocess_node(&$variables) {
  $route_name = \Drupal::routeMatch()->getRouteName();
  $variables['#cache']['contexts'][] = 'route.name.is_layout_builder_ui';
  $internal_path = \Drupal::service('path.current')->getPath();
  $current_path = \Drupal::service('path_alias.manager')->getAliasByPath($internal_path);
  $variables['current_path'] = $current_path;
  $variables['internal_path'] = $internal_path;
  $int_exp = explode("/", $current_path);
  $alias = \Drupal::service('path_alias.manager')->getPathByAlias($int_exp[1]);
  $is_layout = false;
  if ($route_name ) {
    $node = $variables['node'];
    // check if layout builder is active
    if (isset($int_exp[3])) {
      if ($int_exp[3] == 'layout' or $int_exp[1] == 'layout_builder')
      {
        $is_layout = true;
      }
    }

    if (isset($internal_path) && !$is_layout) {
      $params = Url::fromUri("internal:/" . $alias)->getRouteParameters();
      $entity_type = key($params);
      if (isset($entity_type)) {
        $node = \Drupal::entityTypeManager()->getStorage($entity_type)->load($params[$entity_type]);
        $variables['section_nid'] = $node->nid->value;
        $variables['is_site_section'] = false;
        if ($node->type->target_id == 'sections') {
          $variables['is_site_section'] = true;
          $variables['current_section'] = $node->field_section_machine_name->value;
          $variables['current_section_title'] = $node->title->value;
        }
      }
    }

  }
}

```

# Answer
> 1 votes
You should not check for internal. An url like `internal:/robots.txt` is internal, but it is no route and has no route parameters.

So before you do `Url::getRouteParameters()` you should check for Url::isRouted()

---
Tags: theming, hooks, 9, path-aliases
---


# Question
Title: How do I set a Node's Paragraph field in a different language programmatically?
I have a node that has different translations already and I'm trying to populate Paragraph fields in different languages programmatically. I can edit the node and update the paragraph fields manually and they work correctly, so I know the translation configuration is setup correctly.

However, when using the code below, when I create a new paragraph in, say Dutch, and save it to the translated node object it **always** overwrites the English translation with the Dutch one.

For example:

English Node-\>MY\_PARAGRAPH\_VALUE = "English Paragraph Text"
...run code...
English Node-\>MY\_PARAGRAPH\_VALUE = "Now has Dutch Paragraph text "

```
<?php
  /*
   * $node is an existing Node entity with empty multi-value paragraph
   *  fields to populate in different language below
   */
  $lang_code = 'nl';
  $lang_node = $node->getTranslation($lang_code); // copy En node into Dutch Node

  /*
   * Using a Paragraph View to verify,
   * this part works and creates a paragraph with the correct language
   */
  $paragraph = Paragraph::create([
      'type' => 'images_descriptions',
      'field_product_image' => $img[$key],
      'field_product_description' => $desc[$key],
      'langcode' => $lang_code,
  ]);
  $paragraph->save();

  $current[] = [
      'target_id' => $paragraph->id(),
      'target_revision_id' =>$paragraph->getRevisionId(),
  ];

  $lang_node->set('field_images_descriptions', $current);
  $lang_node->save();

?>

```

# Answer
> 2 votes
The paragraphs ERR field only contains one (pertinent) bit of info - the relationship to the paragraph entity. The field itself shouldn't be translated, because every time you set it, you're setting the paragraph for every translation of that node.

To provide translations, you translate the paragraph entity itself, e.g.

```
$node->field_images_descriptions->entity
  ->addTranslation($lang_code, [
    'field_product_image' => $img[$key],
    'field_product_description' => $desc[$key],
  ])
  ->save();

```

# Answer
> 0 votes
this is the right solution for me :

```
 $paragraph = Paragraph::create(['type' => 'statistics',]);
$paragraph->set('field_title', " $title $element->year");
$paragraph->set('field_unit', " $unit");
$paragraph->set('field_count', $element->file_count);
$paragraph->addTranslation( $lang, [
  'field_title' => " $titleEn $element->year",
  'field_unit' => $unitEn,
  'field_count' => $element->file_count,
]);
$paragraph->isNew();
$paragraph->save();

```

---
Tags: 8, i18n-l10n
---


title: Validation with hook_form_alter()

text:
# Question
Title: Validation with hook_form_alter()
In Drupal 8, for the Comment entity, I want to validate an integer field (stored as field\_nieuwe\_waarde\_value). In each comment posted for a node, it needs to be greater than the value for the previous comment.

The code doesn't give any errors, but the validation doesn't work.

```
use Drupal\Core\Form\FormStateInterface;

       /**
        * Implements hook_form_alter().
        */

       function waarde_validatie_form_alter(&$form, $form_state, $form_id) {
         if ($form_id == 'comment_waarde_van_dit_voorwerp__form'){
           $form['#validate'][] = 'start_drupal_waarde_voorwerp_form_validate';
         }
       }

       /**
        * Validation form for the waarde_voorwerp form.
        *
        * Check if the value is > then the last submitted value
        */
       function start_drupal_waarde_voorwerp_form_validate($form, FormStateInterface &$form_state) {
         // Look for the just submitted value.
         $nieuwste_waarde = $form_state->getValue('field_nieuwe_waarde');
         // Look for all early submitted values.
         $alle_waarden = $form_state->getUserInput('field_nieuwe_waarde');
         // Search for the last submitted value.
         $laatste_bod = end($alle_waarden);
         if ($nieuwste_waarde <= $laatste_waarde) {
           $form_state->setErrorByName( t('Gelieve hoger dan'), $laatste_waarde, t('in te stellen'));
         }
       }

```

# Answer
Adding a validation handler in hook\_form\_alter() is the Drupal 7 method of adding validation for a field. It will still work in Drupal 8, however the preferred method of adding validation to Field API Field in Drupal 8 is to add a Constraint to the field. Constraints will be applied when the Node is created, no matter the means of creation. Adding a validation handler to the form only validates the field when the node is created using the Form API - IE through a browser. If a node is created by any other means - IE through an API or programatically, the validation is not applied. Constraints are applied no matter the method of creating the node.

Documentation on how to add a constraint can be seen here: https://www.drupal.org/docs/8/api/entity-validation-api/providing-a-custom-validation-constraint

See Method 2 - adding constraints to fields added to an entity you haven't defined, or fields added through the UI
> 3 votes

# Answer
Based in the second comment "The function is working and validating. Its printing after validating, but it is not getting the values from getUserInput", we can conclude that the problem is about the `getUserInput` returned values. This is the only answer we can give to the question.

Maybe the given form fields, used as arguments, are not set? We cannot check that for you unless you put here also the form definition.

PS: `$form_state->setErrorByName()` receives only 2 arguments. But validation should still work even with these wrong arguments.
> 0 votes

---
Tags: 8, forms, comments
---


# Question
Title: How to access validated values after AJAX callback when fields are nested in a container?
I have a custom form where I have a search field and button. After the users clicks the button, I load some items based on what they've entered into the search field.

The *problem* that I'm having is that when my form is being rebuilt after the AJAX callback is fired, there are no values in `$form_state->values` so I can't access them with `$form_state->getValue()`. However, I do see the input in `$form_state->input`. When I view the documentation for `$form_state->getUserInput()`, it states:

> These are raw and unvalidated, so should not be used without a thorough understanding of security implications. In almost all cases, code should use self::getValues() and self::getValue() exclusively.

That makes sense to me, and I'd love to, but the values aren't there. However, I did notice that if I remove the container from my example below, so that all of my fields are directly at the root of the form array, the values **are** in `$form_state->values` in `::buildSearchResults()` after AJAX rebuild. Why is that the case? What am I missing here?

My code:

```
public function buildForm(array $form, FormStateInterface $form_state) {
  // Note that I see the same issue even when setting #tree to FALSE.
  $form['container'] = [
    '#type' => 'container',
    '#tree' => TRUE,
  ];

  $form['container']['search_field'] = [
    '#type' => 'textfield',
    '#title' => $this->t('Search field'),
  ];

  $form['container']['ajax_button'] = [
    '#type' => 'button',
    '#value' => t('Search'),
    '#name' => 'ajax_button',
    '#limit_validation_errors' => [],
    '#ajax' => [
      'callback' => [static::class, 'myCallbackFunction'],
      'wrapper' => 'search_results',
      'event' => 'click',
    ],
  ];

  $form['container']['search_results'] = [
    '#type' => 'container',
    '#attributes' => [
      'id' => 'search_results',
    ],
    'items' => $this->buildSearchResults($form_state),
  ];

  return $form;
}

public static function myCallbackFunction(array &$form, FormStateInterface $form_state): array {
  // I think I need this here?
  $form_state->setRebuild();
  return $form['container']['search_results'];
}

public function buildSearchResults(FormStateInterface $form_state) {
  // This is always empty at this stage. :(
  // In fact, the values array is totally empty.
  // Though I can see the values in $form_state->input.
  // And, as I stated earlier, if I drop the container from my
  // form and put all fields at the root of the form, the values
  // property is not empty and I can access my field values there.
  $search_text = $form_state->getValue(['container', 'search_field'], '');

  return views_embed_view('view_id', 'display_id', $search_text);
}

```

**Update 1:** @4uk4's answer inspired some more testing. It seems that even if I remove the AJAX functionality so that the button triggers my form's `submitForm()` method, the values **are still missing** from the `$form_state->values` array. After retesting this example code, I can no longer reproduce my issue at all.

**Update 2:** I believe the behavior @4uk4 was describing was causing me to see some false positives here when testing the example code. When I tested with my real code, I'd see my issue *always*, on first AJAX rebuild, with no triggering element set and on all subsequent rebuilds. However, I think I was only ever testing this example code with to the first rebuild, the rebuild with no triggering element and no values were processed - I'd see no values in `$form_state->values` (what I was expecting, given my tests with my actual code), but if I had ever tested long enough for the second rebuild, the actual AJAX submission, I'd have seen the field values in `$form_state->values`.

# Answer
> 1 votes
In the first Ajax request fired, the form is rebuilt with empty values and should return exactly the same form build as the original build. The only difference is that the raw input data is present, but in normal cases ignore that and rebuild exactly the same form. This form build is needed to submit the input data and fill values and triggering element. Only after that, the Ajax rebuild is triggered. To be sure you have the official Ajax rebuild check the triggering element in $form\_state.

The second Ajax request runs more as expected, the form object is retrieved from the cache and the form build function is no longer needed to reconstruct the previous form build.

See this change record https://www.drupal.org/node/2501435

You don't need `$form_state->setRebuild()` in an Ajax callback, Ajax forms are rebuilt automatically. This only makes sense in a #submit callback to have a fallback in case Ajax is disabled and you still want to show the rebuilt form.

# Answer
> 1 votes
Ultimately, my issue was caused by my use of `#limit_validation_errors` on my button (something that I had very unfortunately omitted from my original question's example code).

My example code in the original post is just a small portion of a larger form. My goal was to avoid validating the whole form when a user is trying to AJAX load some additional form content in one section.

After removing the `#limit_validation_errors` key from my button altogether, I started to see the values in `$form_state->values` as expected per @4uk4's answer. But this was forcing me to fill all the unrelated, but ultimately required for final form submission, fields before loading in the new options based on input. To fix this, I referenced How to correctly use #limit\_validation\_errors element of Form API in Drupal 7 and updated to limit validation errors to only my elements rather than removing validation from the form as a whole.

```
$form['container']['ajax_button'] = [
  '#type' => 'button',
  '#value' => t('Search'),
  '#name' => 'ajax_button',
  // I reference my container here because I want to validate all fields
  // within my container.
  '#limit_validation_errors' => [['container']],
  // Alternate approach where to validate fields individually.
  // '#limit_validation_errors' => [['container', 'search_field'], ['container', 'some_other_field']],
  '#ajax' => [
    'callback' => [static::class, 'myCallbackFunction'],
    'wrapper' => 'search_results',
    'event' => 'click',
  ],
];

```

As an aside, this feels like a pretty terrible name for this key. With its current name, it sounds like it's limiting only the errors, not limiting validation entirely. Had this key been named \`#limit\_validation', I might not have spent half a day on this issue. :(

---
Tags: forms, ajax
---


# Question
Title: How can I access the user email associated to an object that implements the \Drupal\user\UserInterface interface?
If a method of a class returns an object that implements the `\Drupal\user\UserInterface` interface, how can I access the user email associated to that object?

# Answer
The object you have is a user entity object.

The user entity class has a `getEmail()` method, that you can use to get the user's email.

For more info look here, User::getEmail().
> 2 votes

# Answer
Since the `\Drupal\user\UserInterface` interface extends the `\Drupal\Core\Session\AccountInterface` interface, which makes available the `getEmail()` method, you can use it to get the email associated with the returned object.
For example, if you stored the returned object in `$account`, you can access its email address with `$account->getEmail()`.

Do not confuse `\Drupal\Core\Session\AccountInterface::getEmail()` with `\Drupal\user\UserInterface::getInitialEmail()`. The latter returns the email address used when the account was registered, which can be later changed. To get the current email address, you need to call `\Drupal\Core\Session\AccountInterface::getEmail()`.
> 0 votes

---
Tags: 8, users
---


# Question
Title: Set new paragraphs to unpublished by Default
Is there a way, to set new Paragraphs to unpublished per Default?

For Content Types, I can set that new Nodes are not published per Default (Structure -\> Content types -\> Tab 'Edit' -\> Widget 'Publishing options' -\> Group 'Default options'. Is there a way to enable the same behaviour for paragraphs?

I use Drupal 9.5.9 and Paragraph 8.x-1.15

If I add a new paragraph to a page, I don't want it to be published. I can unpublish it manually, yes, but I as the Page is multilingual, this leads to a problem: I would like to have the Published-Field translated, so that a Paragraph is published in one language but not in another. If I have to unpublish manually, only the paragraph in the default language (where I added it) gets unpublished, but in all translations it is still published. Therefor I have to unpublish the same paragraph in all translated languages as well, which is a) tedious b) prone to errors and c) has them published in an not yet edited state for some time until I get to this language.

This is why I currently have the publish-field not translated, which is not my desired configuration. I insert the new paragraph, set it to unpublished, edit all its translations and then publish all translations at once. If newly inserted paragraphs would be unpublished per Default, this would solve my problem.

When editing Paragraph Types, there is no Publishing-Options-Widget as on Content Types. I could not find any relevant configuration for Paragraph Module, no setting on the 'Paragraphs (stable)'-Widget on the encompassing Content Types 'Manage Form Display'.

Thanks a lot for any hint... Kind regards, mullzk

# Answer
> 2 votes
As I could not find any other way, i made my own small module with just one hook:

```
<?php
use Drupal\Core\Entity\EntityPublishedInterface;

function paragraph_default_status_paragraph_create(\Drupal\Core\Entity\EntityInterface $entity) {
  if ($entity instanceof EntityPublishedInterface) {
    $entity->setUnpublished();
  }
}

```

This of course depends on the 'published'-field be enabled in the manage-form-display of all paragraphs, otherwise they will remain unpublished for all eternity :)

---
Tags: paragraphs
---


# Question
Title: webform contact form gives access denied for anonymous users
I have a few webforms on my **D9** site. They all work for anonymous site visitors except for the contact form, which gives an "access denied" error.

The permissions under the form's Settings-\>Access tab are the same as for other forms that do work.

The form's access permissions are set to allow anonymous / authenticated / admin users to create and view/ update own submissions:

I did search for the solution but what I found didn't help. I tried the debugging code from https://drupal.stackexchange.com/a/315766 and get the following messages:

The website encountered an unexpected error. Please try again later.

*Exception*: ACCESS DENIED: in *Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException-\>\__construct()* (line *28* of */path/to/vendor/symfony/http-kernel/Exception/AccessDeniedHttpException.php*).

```
Drupal\Core\Http\Exception\CacheableAccessDeniedHttpException->__construct() (Line: 118)
Drupal\Core\Routing\AccessAwareRouter->checkAccess() (Line: 95)
Drupal\Core\Routing\AccessAwareRouter->matchRequest() (Line: 112)
Symfony\Component\HttpKernel\EventListener\RouterListener->onKernelRequest()
call_user_func() (Line: 142)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch() (Line: 145)
Symfony\Component\HttpKernel\HttpKernel->handleRaw() (Line: 81)
Symfony\Component\HttpKernel\HttpKernel->handle() (Line: 58)
Drupal\Core\StackMiddleware\Session->handle() (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle() (Line: 191)
Drupal\page_cache\StackMiddleware\PageCache->fetch() (Line: 128)
Drupal\page_cache\StackMiddleware\PageCache->lookup() (Line: 82)
Drupal\page_cache\StackMiddleware\PageCache->handle() (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle() (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle() (Line: 23)
Stack\StackedHttpKernel->handle() (Line: 718)
Drupal\Core\DrupalKernel->handle() (Line: 19)

```

I don't know what this all means. Where do I go from here? Thank you.

# Answer
This is probably due to submission limits, as laid out by the module maintainer.

> You need to uncheck 'Limit total to one submission per webform/source entity' (/admin/structure/webform/manage/contact/settings/submissions) and set \[the\] submission limit to 1.
> 0 votes

---
Tags: webforms, 9
---


title: How do I implement clipboard.js?

text:
# Question
Title: How do I implement clipboard.js?
How to create a "Copy" button to copy a text in Drupal 8?

I downloaded the following library in the folder `/libraries` :

https://github.com/zenorocha/clipboard.js

Here is my `bootstrap_subtheme_front_office.info.ym`l file :

```
name: Bootstrap Subtheme Front Office
type: theme
description: 'SASS starter kit for a Bootstrap Barrio SubTheme.'
# version: VERSION
core: 8.x
core_version_requirement: ^8 || ^9
base theme: bootstrap_barrio

libraries:
  - bootstrap_subtheme_front_office/bootstrap
  - bootstrap_subtheme_front_office/global-styling
  - bootstrap_subtheme_front_office/clipboardjs
libraries-override:
  bootstrap_barrio/global-styling: true
  bootstrap_barrio/file: true
regions:
  navigation_collapsible_first: 'Navigation (Collapsible first)'
  navigation_collapsible_left: 'Navigation (Collapsible left)'
  navigation: 'Navigation'
  navigation_collapsible_right: 'Navigation (Collapsible right)'
  navigation_collapsible_second: 'Navigation (Collapsible second)'
  breadcrumb: 'Breadcrumb'
  header: 'Top Bar'
  sidebar_first: 'Primary'
  highlighted: 'Highlighted'
  help: 'Help'
  content: 'Content'
  sidebar_second: 'Secondary'
  footer: 'Footer'

# Information added by Drupal.org packaging script on 2020-10-11
version: '5.0.2'
project: 'bootstrap_subtheme_front_office'
datestamp: 1602444143

```

I declared the library in this file `bootstrap_subtheme_front_office.libraries.yml` :

```
bootstrap:
  js:
    /libraries/bootstrap/dist/js/bootstrap.bundle.min.js: {}
  css:
    component:
      /libraries/bootswatch/dist/flatly/bootstrap.min.css: {}
global-styling:
  version: VERSION
  js:
    js/popper.min.js: {}
    js/bootstrap.min.js: {}
    js/barrio.js: {}
    js/custom.js: {}
    js/back-to-top.js: {}
  css:
    component:
      css/style.min.css: {}
  dependencies:
    - core/jquery
    - core/drupal
clipboardjs:
  version: VERSION
  js:
    /libraries/clipboard.js/dist/clipboard.min.js: {}

```

I created this HTML code :

```
<button class="btn" data-clipboard-text="Just because you can doesn't mean you should — clipboard.js">
  Copy to clipboard
</button>

```

I have checked the source code of my pages and the library is loaded :

When I click on the button, it does not "Copy". How to make this library work with Drupal 8 ?

https://clipboardjs.com/

There is a step and I don't know what to do in Drupal :

> Now, you need to instantiate it by passing a DOM selector, HTML element, or list of HTML elements.
>
> `new ClipboardJS('.btn');`

# Answer
> 0 votes
I suggest you do the following:

1. Install the Clipboard.js Drupal module and enable it.
2. If you installed it with Composer, you can skip this step. If not, have the clipboard.js file located at `/libraries/clipboard.js/dist/clipboard.js`. Because that's where the module expects it: https://git.drupalcode.org/project/clipboardjs/-/blob/2.0.0/clipboardjs.libraries.yml#L27
3. Now attach the library either in `hook_preprocess_page` to every page or via `{{ attach_library('clipboardjs/clipboardjs') }}` directly in Twig.
4. Now have either an `<a>`, an `<input>` or a `<button>` with `class="clipboardjs-button"`. Because that's how the module initiates the library: https://git.drupalcode.org/project/clipboardjs/-/blob/2.0.0/js/clipboard.js#L15.

And you are done. And you can follow up adding the Clipboard.js-specific attributes.

# Answer
> 0 votes
If you're still interested in a solution, I have found the following pages that help in https://github.com/zenorocha/clipboard.js :

Under the demo sub-directory: https://github.com/zenorocha/clipboard.js/blob/master/demo/

You can find for instance "function-text.html" and "target-textarea.html" among others...

So, I have copied the content from those files and paste them in my page, from there I started modifying to what I have and what I need, for instance, from file "constructor-node.html" I have copied:

(Text between * is what I have modified, eg.: \*Click to copy\*...)

```
   <!-- 1. Define some markup -->
    <div id="btn" data-clipboard-text="*The text I want to be copied*">
      <span>*Click to copy*</span>
    </div>

    <!-- 2. Include library -->
    <script src="*https://myserver.com/mydrupal/libraries*/dist/clipboard.min.js"></script>

    <!-- 3. Instantiate clipboard by passing a HTML element -->
    <script>
      var btn = document.getElementById('btn');
      var clipboard = new ClipboardJS(btn);

      clipboard.on('success', function (e) {
        console.info('Action:', e.action);
        console.info('Text:', e.text);
        console.info('Trigger:', e.trigger);
      });

      clipboard.on('error', function (e) {
        console.info('Action:', e.action);
        console.info('Text:', e.text);
        console.info('Trigger:', e.trigger);
      });
    </script>

```

This way it worked for me.

Good luck!!

By the way, the DOM Selector is the function that selects the element that triggers the action, I have just learned from here: https://blog.bitsrc.io/dom-selectors-explained-70260049aaf0

---
Tags: 8, theming, javascript
---


# Question
Title: How can I fix the "Predis\Client not found" error?
I have a project based on Drupal 7. I installed PHP 7.4, Redis, and php-redis on Ubuntu. I get a *Class 'Predis\Client' not found* error.

The values in the *setting.php* file for the module are the following.

```
$conf['redis_client_interface']           = 'Predis';
$conf['cache_backends'][]                 = 'sites/all/modules/redis/redis.autoload.inc';
$conf['cache_class_cache']                = 'Redis_Cache';
$conf['cache_class_cache_menu']           = 'Redis_Cache';
$conf['cache_class_cache_drupal_get_filename'] = 'Redis_Cache';
$conf['cache_class_cache_bootstrap']      = 'Redis_Cache';
$conf['cache_class_cache_path']           = 'Redis_Cache';
$conf['cache_class_cache_field']          = 'Redis_Cache';
$conf['cache_class_cache_views']          = 'Redis_Cache';
$conf['cache_class_cache_admin_menu']     = 'Redis_Cache';
$conf['lock_inc']                         = 'sites/all/modules/redis/redis.lock.inc';

```

When I run `drush cc` the same error is show. For solving that, I re-installed Redis and php-redis, but that hasn't fixed the error.

# Answer
> 0 votes
If you want to use the redis client interface `Predis` you have to install the Predis library. Composer is the recommended install method, alternatively for Drupal 7 you can still use the library folder. See README.Predis.txt in the modules folder.

If you want to use the Ubuntu package **php-redis** then change the redis client interface to `PhpRedis`. See README.PhpRedis.txt in the modules folder.

# Answer
> 0 votes
For Drupal 8 and above, Composer based installation, need to RUN below command

**composer require predis/predis**

---
Tags: 6, caching
---


# Question
Title: Adding role to a user after login - after simplesamlphp/externalauth
**Adding more context to my question**

here is the scenario: a) the organization has different departments b) actual Drupal roles are not assigned through simplesaml role attribute but through simplesaml attribute: IsUnit\_A =\> value: yes or no c) all members of UnitA \>\> 'isUnit\_A=yes', get 'unitA' role; which is assigned through suggestion #1 of the below answer "Assign them via the module's configuration". such as: `unitA:IsUnit_A,=,Yes` d) all non-UnitA members are assigned 'noUnitA' role such as: `noUnitA:IsUnit_A,=,No` e) Exceptions: using the module's configuration as well we grant exceptions to a few users; for example, some users from other departments are granted 'unitA' role. For example: `unitA:uid,=,user21`

So, my goal is to once all roles have been assigned according to the "Automatic role population from simpleSAMLphp attributes" in simplesaml configuration, then using a hook check if the current user has both roles, then remove one: noUniteA.

The problem with the simplesaml hooks is that they evaluate the current user pre rendering what is specified in 'Automatic role population from simpleSAMLphp attributes'

Do you happen to know if there is any hook that it's triggered after SAML roles are assigned through 'Automatic role population from simpleSAMLphp attributes' is rendered and before page content is loaded?

**\- - - - - - - - - - - - - - -**

I've successfully created a custom module calling the 'hook\_user\_login' to add a role to the current user if the condition is met.

**Environment**:

*Drupal Version:* 9.5.10 | *PHP Version:* 8.1.17 | *Database Version:* 8.0.34

In my local everything runs to satisfaction;

```
<?php

use Drupal\user\Entity\User;

/**
 * Implements hook_user_login().
 *
 * Custom query function to remove role after the user logged in.
 */
function remove_role_user_login($account){

    // load current user
    $loaded_user = User::load(\Drupal::currentUser()->id());

    $current_user = \Drupal::currentUser();

    // get current user's roles
    $array_roles = $current_user->getRoles();

    $roles = implode(", ", $array_roles );

    if (in_array("role_1", $array_roles) && in_array("role_2", $array_roles)) {
       // adding some logging for debugging purpoes
        \Drupal::logger('remove_role')->notice("role_1 and role_2 are in roles");
        \Drupal::logger('remove_role')->notice($roles);
        // it has both roles, so remove 'role_2'
        $loaded_user->removeRole('role_2');
        $loaded_user->save();

    }
    // leaving the else statement for dubigging purposes
    else {
        \Drupal::logger('remove_role')->notice("role_1 and role_2 are not in roles");
        \Drupal::logger('remove_role')->notice($roles);
    }
}

```

However, on the server where authentication goes through **SimpleSamlphp** script changes get overwritten with the roles assigned by SimpleSamlphp

I can confirm that the hook\_user\_login() is triggered because notices are recorded in database log ('drush ws')

Which hook shall I use to apply changes after SimpleSaml authentication?

Thanks

**UPDATE** I was not able to find a solution to dynamically change user's roles. I ended up applying content access by role using content hook; not ideal because is more demanding for the application (it has to check on every page), but it go the job done.

# Answer
You can:

1. Assign them via the module's configuration, if they are listed by the Identity Provider. Go to `admin/config/people/simplesamlphp_auth/sync` and configure `Automatic role population from simpleSAMLphp attributes` according the instruction.
2. Use `hook_simplesamlphp_auth_user_roles_alter`
3. `hook_simplesamlphp_auth_user_attributes` should also actually work with the user roles

The hooks are well documented inside `simplesamlphp_auth.api.php`

```
/**
 * Hook to alter the roles assigned to a SAML-authenticated user.
 *
 * Whenever a user's roles are evaluated this hook will be called, allowing
 * custom logic to be used to alter or even completely replace the roles
 * evaluated.
 *
 * @param array &$roles
 *   The roles that have been selected for the current user
 *   by the role evaluation process.
 * @param array $attributes
 *   The SimpleSAMLphp attributes for this user.
 */
function hook_simplesamlphp_auth_user_roles_alter(&$roles, $attributes) {
  if (isset($attributes['roles'])) {
    // The roles provided by the IdP.
    $sso_roles = $attributes['roles'];

    // Match role names in the saml attributes to local role names.
    $user_roles = array_intersect(user_roles(), $sso_roles);

    foreach (array_keys($user_roles) as $rid) {
      $roles[$rid] = $rid;
    }
  }
}

/**
 * Hook to alter a Drupal user account after SAML authentication.
 *
 * Allows other modules to change fields or properties on the Drupal account
 * after a user logged in through SimpleSAMLphp. This can be used to add
 * map additional SAML attributes to Drupal user profile fields.
 *
 * @param \Drupal\user\UserInterface $account
 *   The Drupal account that can be altered.
 * @param array $attributes
 *   The SimpleSAMLphp attributes for this user.
 *
 * @return \Drupal\user\UserInterface|bool
 *   The altered Drupal account or FALSE if nothing was changed.
 */
function hook_simplesamlphp_auth_user_attributes(\Drupal\user\UserInterface $account, $attributes) {
  $saml_first_name = $attributes['first_name'];
  if ($saml_first_name) {
    $account->set('field_first_name', $saml_first_name);
    return $account;
  }
  return FALSE;
}

```
> 2 votes

---
Tags: authentication-authorization
---


# Question
Title: Extract image meta data and set field values on Media upload
I'm extracting IPTC and EXIF data when the user uploads an image using the Media Library UI.

Using `hook_form_alter` I can get the URI of the uploaded file in the `media_library_add_form_upload` form, and extract the data from it. So far, so good.

However, the form does not (yet?) contain the other fields attached to the media type (e.g. title, description, copyright) so I can't set the values.

How can I set the values in the form that is shown after I've selected and upload an image?

# Answer
> 0 votes
The form fields are nested under `$form['media'][0]`. Having extracted data from the uploaded file, we can set the data as default values on the fields.

```
/**
 * Implements hook_media_form_alter().
 */
function ds_media_exif_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  if ($form_id !== 'media_library_add_form_upload') {
    return;
  }

  if (!array_key_exists('upload', $values)) {
    return;
  }

  $upload_id = $values['upload'][0];
  $file = File::load($upload_id);
  $uri = $file->getFileUri();

  // Extract exif, iptc data here.
  $data = extractFileData($uri);

  // Title defaults to the file name.
  if (isset($data['title'])) {
    $form['media'][0]['fields']['name']['widget'][0]['value']['#default_value'] = $data['title'];
    // Set alt field to title.
    $form['media'][0]['fields']['field_media_image']['widget'][0]['#default_value']['alt'] = $data['title'];
  }

  // Some more fields: field_description and field_copyright.
  $form['media'][0]['fields']['field_description']['widget'][0]['#default_value'] = $data['description'] ?: '';
  $form['media'][0]['fields']['field_copyright']['widget'][0]['value']['#default_value'] = $data['copyright'] ?: '';
}

```

---
Tags: media, 9
---


# Question
Title: The website encountered an unexpected error after the media module was uninstalled. The "media" entity type does not exist
This is a Drupal 9.5.10. After uninstalling the Media module. The website encountered an unexpected error. The error message only shows a little information, as I know. It mentioned the "media" entity type does not exist.

The website does not have the Media Entity module. Of course, the Media module and Media types(audio, document, image, remote video, and video) should not be there because I just uninstalled it. So what does it mean? Do you know if the error message below relates to another module besides the Media? It's hard to know what needs to be fixed. How do I fix this?

If I enable the Media module by running `drush en media`, it will return the site, but I want to uninstall the Media module.

```
The website encountered an unexpected error. Please try again later.
Drupal\Component\Plugin\Exception\PluginNotFoundException: The "media" entity type does not exist. in Drupal\Core\Entity\EntityTypeManager->getDefinition() (line 139 of core/lib/Drupal/Core/Entity/EntityTypeManager.php).

Drupal\Core\Entity\EntityTypeManager->getDefinition('media') (Line: 128)
Drupal\Core\Entity\EntityDisplayBase->__construct(Array, 'entity_view_display') (Line: 178)
Drupal\Core\Entity\Entity\EntityViewDisplay->__construct(Array, 'entity_view_display') (Line: 564)
Drupal\Core\Entity\EntityDisplayBase->__wakeup()
session_start() (Line: 192)
Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage->start() (Line: 162)
Drupal\Core\Session\SessionManager->startNow() (Line: 110)
Drupal\Core\Session\SessionManager->start() (Line: 57)
Symfony\Component\HttpFoundation\Session\Session->start() (Line: 54)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 106)
Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 85)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 718)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)

Fatal error: Uncaught Drupal\Component\Plugin\Exception\PluginNotFoundException: The "media" entity type does not exist. in /Applications/XAMPP/xamppfiles/htdocs/localhost/web/core/lib/Drupal/Core/Entity/EntityTypeManager.php:139 Stack trace: #0 /Applications/XAMPP/xamppfiles/htdocs/localhost/web/core/lib/Drupal/Core/Entity/EntityFieldManager.php(219): Drupal\Core\Entity\EntityTypeManager->getDefinition('media') #1 /Applications/XAMPP/xamppfiles/htdocs/localhost/web/core/lib/Drupal/Core/Entity/EntityFieldManager.php(196): Drupal\Core\Entity\EntityFieldManager->buildBaseFieldDefinitions('media') #2 /Applications/XAMPP/xamppfiles/htdocs/localhost/web/core/lib/Drupal/Core/Entity/EntityFieldManager.php(346): Drupal\Core\Entity\EntityFieldManager->getBaseFieldDefinitions('media') #3 /Applications/XAMPP/xamppfiles/htdocs/localhost/web/core/lib/Drupal/Core/Entity/EntityDisplayBase.php(405): Drupal\Core\Entity\EntityFieldManager->getFieldDefinitions('media', 'audio') #4 /Applications/XAMPP/xamppfiles/htdocs/localhost/web/core/lib/Drupal/Core/Entity/EntityDisplayBase.php(304): Drupal\Core\Entity\EntityDisplayBase->getFieldDefinitions() #5 /Applications/XAMPP/xamppfiles/htdocs/localhost/web/core/lib/Drupal/Core/Entity/EntityDisplayBase.php(541): Drupal\Core\Entity\EntityDisplayBase->toArray() #6 [internal function]: Drupal\Core\Entity\EntityDisplayBase->__sleep() #7 [internal function]: session_write_close() #8 {main} thrown in /Applications/XAMPP/xamppfiles/htdocs/localhost/web/core/lib/Drupal/Core/Entity/EntityTypeManager.php on line 139

```

# Answer
> 3 votes
This looks like you still have a display mode object stored in the user session referencing the media entity type. Unserializing this object throws the error message. Log out and log in, if more users are affected then truncate the sessions table.

---
Tags: media, 9
---


# Question
Title: Changing order of execution of hook_preprocess_html
I have 2 custom modules in Drupal 9, let's called them module A and module B.

Each module has a fully working hook\_preprocess\_html function defined in the .module file. The hook\_preprocess\_html function in module A runs first, followed by the function in module B.

How can I update Module A so the hook\_preprocess\_html function in there runs after the one in module B?

I have tried to implement hook\_module\_implements\_alter(&$implementations, $hook) but it is not even showing the hook\_preprocess\_html hook. This preprocess\_html seems not to apply to the module\_implements\_alter hook.

Many thanks for you help!

# Answer
> 0 votes
`hook_module_implements_alter()` is invoked only for those hooks that are invoked via `ModuleHandler::alter()`, `ModuleHandler::invokeAll()`, and `ModuleHandler::invokeAllWith()`. All those methods call `ModuleHandler::getImplementationInfo()`, which calls `ModuleHandler::buildImplementationInfo()`, which contains the following code. (See the line after the *Verify implementations that were added or modified.* comment.)

```
  // Allow modules to change the weight of specific implementations, but avoid
  // an infinite loop.
  if ($hook != 'module_implements_alter') {
    // Remember the original implementations, before they are modified with
    // hook_module_implements_alter().
    $implementations_before = $implementations;

    // Verify implementations that were added or modified.
    $this->alter('module_implements', $implementations, $hook);

    // Verify new or modified implementations.
    foreach (array_diff_assoc($implementations, $implementations_before) as $module => $group) {
      // If an implementation of hook_module_implements_alter() changed or
      // added a group, the respective file needs to be included.
      if ($group) {
        $this->loadInclude($module, 'inc', "{$module}.{$group}");
      }

      // If a new implementation was added, verify that the function exists.
      if (!function_exists($module . '_' . $hook)) {
        throw new \RuntimeException("An invalid implementation {$module}_{$hook} was added by hook_module_implements_alter()");
      }
    }
  }

```

`Registry::processExtension()` creates the list of preprocess hooks implemented for a single theme hook with the following code.

```
foreach ($prefixes as $prefix) {
  if (isset($info['template']) && function_exists($prefix . '_preprocess')) {
    $info['preprocess functions'][] = $prefix . '_preprocess';
  }
  if (function_exists($prefix . '_preprocess_' . $hook)) {
    $info['preprocess functions'][] = $prefix . '_preprocess_' . $hook;
  }
}

```

`$prefixes` is initialized from the following code. In particular, see the `if ($type == 'module') { /* … */ }` part, which is the one that allows to modules to add their own variable preprocessors (a.k.a. preprocess hooks).

(I left only the relevant comment to make the code shorter. All the comments can be read in the linked documentation page, which shows also the method code.)

```
if ($type == 'module') {
  $prefixes[] = 'template';

  // Add all modules so they can intervene with their own variable
  // preprocessors. This allows them to provide variable preprocessors
  // even if they are not the owner of the current hook.
  $prefixes = array_merge($prefixes, $module_list);
}
elseif ($type == 'theme_engine' || $type == 'base_theme_engine') {
  $prefixes[] = $name . '_engine';
  $prefixes[] = $theme;
}
else {
  $prefixes[] = $name;
}

```

$module\_list is initialized with `$module_list = array_keys($this->moduleHandler->getModuleList());`

The code that invokes the preprocess hooks does not use the methods from the `ModuleHandler` class I listed earlier, since it builds the list of preprocess hooks to invoke using `Registry::processExtension()`, which means `hook_module_implements_alter()` is not invoked for preprocess hooks.

The list of modules returned by `ModuleHandler::getModuleList()` is ordered by the modules weight, though. This means that changing a module weight will change the order preprocess hooks are invoked.

# Answer
> 0 votes
Is there a hook\_preprocess\_hook in the list? As the doc says hook\_module\_implements\_alter will only show the base hooks and hook\_preprocess\_html is a variant of hook\_preprocess\_hook

So you would need to change the weight for hook\_preprocess\_hook

---
Tags: hooks, 9
---


# Question
Title: Custom module routing is going to page not found
I have a custom module that i'd like to display my webform on a certain page but it keeps sending me to a 'Page not found'.

folder structure is;

```
modules/custom/custom_webform_display
-custom_webform_display.info.yml
-custom_webform_display.module
-custom_webform_display.routing.yml
--src
---Controller
----CustomWebformDisplayController.php
--templates
---custom-webform-display-page.html.twig

```

info.yml

```
name: 'Custom Webform Display'
type: module
description: 'Displays a webform in a custom page template.'
core_version_requirement: ^9 || ^10
package: Custom
dependencies:
  - webform:webform

```

.module

```
<?php

use Drupal\Core\Render\Markup;

/**
 * Implements hook_theme().
 */
function custom_webform_template_theme($existing, $type, $theme, $path) {
  return [
    'custom_webform_template_page' => [
      'variables' => [
        'webform' => NULL,
        'webform_content' => NULL,
      ],
      'template' => 'custom-webform-template-page',
      'path' => $path . '/templates',
    ],
  ];
}

/**
 * Page callback for the custom webform template page.
 */
function custom_webform_template_page_callback($webform_id) {
  $webform = \Drupal::entityTypeManager()->getStorage('webform')->load($webform_id);

  if ($webform) {
    // Ensure the webform content is a render array before rendering.
    $webform_content = $webform->getSubmissionForm()->render();
    $webform_content = Markup::create($webform_content);

    $build = [
      '#theme' => 'custom_webform_template_page',
      '#webform' => $webform,
      '#webform_content' => $webform_content,
    ];
    return $build;
  } else {
    // If the webform with the given ID does not exist, return a 404 page.
    throw new \Symfony\Component\HttpKernel\Exception\NotFoundHttpException();
  }
}

```

.routing.yml

```
custom_webform_display.display:
  path: '/custom-webform'
  defaults:
    _controller: '\Drupal\custom_webform_display\Controller\CustomWebformDisplayController::displayWebform'
  requirements:
    _permission: 'access content'

```

.page.html.twig

```
{#
/**
 * @file
 * Default template file for displaying a webform in a custom page template.
 *
 * Available variables:
 * - webform: The rendered webform.
 */
#}

<div class="custom-webform-display">
  {{ webform }}
</div>

```

Controller.php

```
<?php

namespace Drupal\custom_webform_display\Controller;

use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\Response;

/**
 * Controller for displaying a webform in a custom page template.
 */
class CustomWebformDisplayController extends ControllerBase {

  /**
   * Displays the webform in a custom page template.
   *
   * @return \Symfony\Component\HttpFoundation\Response
   *   The rendered webform.
   */
  public function displayWebform() {
    // Load the webform by its ID. Replace 'your_webform_id' with the actual webform ID.
    $webform_id = 'contact';
    $webform = \Drupal\webform\Entity\Webform::load($webform_id);

    // Check if the webform exists and is open.
    if ($webform && $webform->isOpen()) {
      // Render the webform.
      $form = \Drupal::formBuilder()->getForm($webform);
      $rendered_form = \Drupal::service('renderer')->render($form);

      // Create a custom page template and replace 'custom_webform_display_page' with your template name.
      $output = [
        '#theme' => 'custom_webform_display_page',
        '#webform' => $rendered_form,
      ];

      return new Response(\Drupal::service('renderer')->render($output));
    }

    // If the webform doesn't exist or is closed, display an error message or redirect to another page.
    return new Response($this->t('The webform is not available at the moment.'));
  }

}

```

# Answer
> 0 votes
I changed my displaywebform function to just this and it now works;

```
  public function displayWebform() {

      $webform = \Drupal::entityTypeManager()->getStorage('webform')->load('contact');
      $webform = $webform->getSubmissionForm();

      return $webform;
    }

```

---
Tags: routes, 9
---


# Question
Title: Update a field without changing the node
I need to change a field in a node without altering the 'changed' timestamp. I know this was possible in Drupal 7 using `field_attach_update()`, but that has been removed since then.

I could use an update query and change the field directly in the database, but I was wondering if there is a better way (or a replacement for `field_attach_update()`).

# Answer
The functionality is available on `SqlContentEntityStorage` but it's `protected`. Bother! said Pooh and *reflected* on his poor Drupal coding practices:

```
    $storage = $this->entityTypeManager->getStorage($entity->getEntityType());
    if ($storage instanceof SqlContentEntityStorage) {
      // assemble the list of fields you want to save in $names.
      (new \ReflectionObject($storage))
        ->getMethod('doSaveFieldItems')
        ->invoke($storage, $entity, $names);
      $storage->resetCache([$entity->id()]);
    }

```
> 1 votes

# Answer
Let's try this code:

```
function MODULE_NAME_node_presave(Drupal\node\NodeInterface $node) {
  $node->set('FIELE_NAME', 'FIELD_VALUE');
}

```

OR

```
use Drupal\node\NodeInterface;

function hook_entity_presave(Drupal\Core\Entity\EntityInterface $entity)   {
  if ($entity instanceof NodeInterface) {
    $entity->title->value = 'testing';
  }
}

```
> 0 votes

---
Tags: 8, nodes
---


title: How can I choose an attribute?

text:
# Question
Title: How can I choose an attribute?
A product has *Size* and *Pickup Location* as attributes; *Size* can be *S*, *M*, *L*, and *XL*, while *Pickup Location* gives 10 choices.

The *Pickup Location* value does not affect the product price; that is only influenced by *Inventory* and *Size*.

When a product is created, I have to create 40 variations, even though the price changes only when *Size* has a different value. The *Pickup Location* value is just shown as reference in *Add to cart*.

How can I choose the *Pickup Location* attribute?

I am using Drupal 10 and Commerce 8.x-2.36.

>

>

>

>

>

# Answer
We typically only recommend using product attributes for those aspects of a family of product variations that differentiate one from another. Size is a reasonable attribute, because logically you would group together the various size variations as a single product. However, pickup location is more of a fulfillment detail - I wouldn't use product attributes for this.

What you do *instead* really depends on your business requirements. For example, if all products have the same potential pickup locations, it would be as simple as adding a `List (text)` field to your order item instead and displaying it on the Add to Cart form mode. If your options vary based on product and / or location inventory, then you'll need to pair that with custom code to filter the options as needed. (Alternately, if it's one pickup location for an entire order, you also could make this a field on the order itself, but again, really depends on your business requirements.)
> 0 votes

---
Tags: commerce, 10
---


# Question
Title: How to create a custom content entity without canonical url?
I've created a custom content entity with `drush generate module-content-entity`, which works great. However, I want my custom entities NOT to have a canonical url. Drush automatically creates those in the annotation of the entity class:

```
/**
 * Defines the chunk entity class.
 *
 * @ContentEntityType(
 *   id = "chunk",
 *   ...
 *   links = {
 *     "add-form" = "/admin/content/chunk/add/{chunk_type}",
 *     "add-page" = "/admin/content/chunk/add",
 *     "canonical" = "/chunk/{chunk}",
 *     "edit-form" = "/admin/content/chunk/{chunk}/edit",
 *     "delete-form" = "/admin/content/chunk/{chunk}/delete",
 *     "collection" = "/admin/content/chunk"
 *   },
 *   ...
 * )
 */

```

I've tried to remove the canonical entry from the annotation, but this resulted in numerous errors left and right, because a lot of core and contrib modules assume that canonical URLs always exist. In my case most of those errors are coming from other modules calling functions that end up in `EntityBase::toUrl`, which fails with a WSOD due the missing canonical entry.

Even though this seems to be mandatory (@see EntityTypeInterface), some others - e.g. Paragraphs module - somehow managed to remove the canonical link, without causing errors everywhere.

Can anybody give me some hints how to **safely** remove the canonical link from my custom content entity? I do not want to turn the canonical links into 403's or 404's, I'd prefer those links not ever being rendered at all (hide "View" primary tab, prevent links when rendering entity labels, etc).

# Answer
> 0 votes
It is actually really easy, except removing all references to canonical from the boilerplate code I just needed to override one single function `toUrl` in my custom entity class:

`src/Entity/MyContentEntity.php`

```
/**
 * Defines the chunk entity class.
 * Note the missing "links = { canonical }" entry in the annotation
 *
 *
 * @ContentEntityType(
 *   id = "chunk",
 *   ...
 *   links = {
 *     "add-form" = "/admin/content/chunk/add/{chunk_type}",
 *     "add-page" = "/admin/content/chunk/add",
 *     "edit-form" = "/admin/content/chunk/{chunk}/edit",
 *     "delete-form" = "/admin/content/chunk/{chunk}/delete",
 *     "collection" = "/admin/content/chunk"
 *   },
 *   ...
 * )
 */
class Chunk extends ContentEntityBase implements ChunkInterface {
  /**
   * this prevents WSOD when 3rd party modules call $entity->toUrl
   */
  public function toUrl($rel = 'canonical', array $options = []) {
    if ($rel == 'canonical') {
      return Url::fromUri('route:<nolink>')->setOptions($options);
    }
    else {
      return parent::toUrl($rel, $options);
    }
  }
}

```

The other stuff here is only needed if the entity is based on an auto-generated boilerplate code from `drush generate` or similar:

`src/Form/MyContentEntityForm.php`

```
/**
 * The following change is only necessary if you use boilerplate code from "drush generate" or similar
 */
class ChunkForm extends ContentEntityForm {
  public function save(array $form, FormStateInterface $form_state) {
    ...
    // change the following line
    //$form_state->setRedirect('entity.chunk.canonical', ['chunk' => $entity->id()]);
    // to something of your choice
    $form_state->setRedirect('entity.chunk.edit-form', ['chunk' => $entity->id()]);
  }

}

```

`my_module.links.task.yml`:

```
# If existing, remove the following boilerplate code from "drush generate" or similar

entity.chunk.view:
  title: View
  route_name: entity.chunk.canonical
  base_route: entity.chunk.canonical

```

# Answer
> 1 votes
Best way to do it is to define your own `html` `route_provider`

In your `@ContentEntityType` definition:

```
"route_provider" = {
  "html" = "Drupal\chunk\Routing\ChunkHtmlRouteProvider",
}

```

And the `/chunk/src/Routing/ChunkHtmlRouteProvider.php` itself:

```
<?php

namespace Drupal\chunk\Routing;

use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\Routing\AdminHtmlRouteProvider;

class ChunkHtmlRouteProvider extends AdminHtmlRouteProvider {

  protected function getCanonicalRoute(EntityTypeInterface $entity_type) {
    return $this->getEditFormRoute($entity_type);
  }

}

```

---
Tags: entities
---


title: View with reference entity type

text:
# Question
Title: View with reference entity type
I have a View entity reference type and I want to get the fields along with fields I m using relationship Field to cal referenced type entities

I have content type with name product Field 1: title Field 2: description Field 3: listing & it's a paragraph entity refenced field

I created a View and called fiels But Field 3 contain multiple fields

But saw the title & description fields repeated number of time entity type refenced occurs

Let say 1 have 10 node

Node:1

Title 1 Description 1 Listing 1

Title 1 Description 1 Listing 2

Title 1 Description 1 Listing 3

Node:2

Title 2 Description 2 Listing 1

Title 2 Description 2 Listing 2

........ 10

Now I want

Title 1 Description 1 Listing 1 Listing 2 Listing 3

Title 2 Description 2 Listing 1 Listing 2 ........

# Answer
Select "Unformatted list" as your format. You can then use "Grouping fields" under the settings to group by both the title and description. You may need to play with CSS and/or Twig to get the exact output you want.
> 0 votes

---
Tags: views, 10
---


title: Updated Google Analytics breaks website

text:
# Question
Title: Updated Google Analytics breaks website
I uninstalled the Google Analytics module and installed a newer version for Drupal 8. Now the website crashes with this exception error.

> Uncaught PHP Exception Symfony\Component\Routing\Exception\RouteNotFoundException: "Route "view.google\_analytics\_summary.page\_1" does not exist." at /opt/drupal/web/core/lib/Drupal/Core/Routing/RouteProvider.php line 208

I have no idea how to fix this since I can't log in on the site. I also can't use Drush because the site is on a ECS server that doesn't give me access to the terminal.

# Answer
I ended up running update.php to force the cache to rebuild correctly. There wasn't an analytics update but I got luck there was another update and when it finished running it recompiled the code and fixed the issue.
> 0 votes

---
Tags: google-analytics
---


# Question
Title: How to split Webform in multi menus/tabs?
Is it possible to split my webform into several menus/tab?

Note: aim is not to obtain a multi step webform but a webform with several sections reachable easily by selecting a tab.

# Answer
There's the Webform Navigation module which allows you to let users navigate back and forth between steps of a multi-step form via the progress bar. How you design these steps in the progress bar is up to you. If you want them look like **tabs**, style the steps to look like tabs. That's all.

> The goal of this module is to provide the ability to navigate forward and backward through a web form using the Webform Progress tracker (aka wizard progress bar) supplied by the webform module on multi-page web forms. It also allows for the ability to bypass validation when the user presses the "next" button as they navigate through the form.

Alternatively use collapsible fieldsets to contain sections of your webform.
> 2 votes

# Answer
Or you can have

* one `radios` element set to display as buttons (type radio, options display: buttons horizontal; these are your tabs)
* each tabpanel a `fieldset` or `container`
* each tabpanel a condition to only show if the right tab is selected in the radios element (make sure not to 'clear values when hidden')
* then css the buttons into tabs
> 0 votes

---
Tags: webforms
---


# Question
Title: Display Suite ignores hook_preprocess_node()
It seems like the *Display Suite* module completely ignores `hook_preprocess_node()` and `hook_process_node()`. Is this the case?

If it is so, what is the way to make minor changes to the final rendering with access to all the node variables?

# Answer
Display Suite does indeed hijack the node rendering to a point where the default preprocess functions may not be used.

The module comes with a set of its own preprocess functions that are documented in ds.api.php.

In this case, `hook_ds_pre_render_alter()` can be used instead of the `hook_preprocess_node()` to modify the output via the complete render array. Also, the `$entity` object is available with all values.
> 14 votes

# Answer
I was able to trigger it using `hook_preprocess_ds_entity_view()`:

```
function mytheme_preprocess_ds_entity_view(&$variables) {
  $variables['node'] = $variables['content']['#node'];
  $variables['view_mode'] = $variables['content']['#view_mode'];

  mytheme_preprocess_node($variables);
}

```
> 1 votes

---
Tags: nodes, theming, display-suite
---


# Question
Title: Can I populate a select form element with options in separate file?
I have a text file which is a list of 100s of rows. Which I am trying to turn in to a select list.

I have created a module, in mymodule/src/form/mymoduleform.php. I am trying to load the items from the file and turn the file content into an array which can be used as options for the form element.

The form element is created by the following code.

```
$form['mymodule_select'] = [
  '#type' => 'select',
  '#title' => $this->t('Select element'),
  '#options' =>  selectlist_options(),
];

```

`selectlist_options()` is implemented using the following code.

```
function selectlist_options() {
  $options = [];
  $result = file('options.txt');

  foreach ($result as $key => $value) {
    $options[$key] = $options['name'];
  }

  return $options;
}

```

Is this the correct approach?

# Answer
> Is this the correct approach?

The code shown in the question needs to be first fixed: In the `foreach()` loop, it does not set `$options[$key]` to any value read from the file because it does not use `$value`.

Supposing that the *options.txt* file is contained in the same directory containing the .module file, the code to get the file path is the one shown by @4uk4.

The code you should use for `selectlist_options()` becomes the following one.

```
function selectlist_options() {
  $options = [];
  $result = file('options.txt');

  foreach ($result as $key => $value) {
    $options[$key] = $value;
  }

  return $options;
}

```

On Drupal, strings used in the user interface must be translatable, while strings read from a file are not translatable. Furthermore, the files in the directory containing modules cannot be altered by the web server nor PHP. This means that, if the *options.txt* file is in the directory containing the module, its content is probably changed when the module is updated.
Instead of using a text file that is updated when the module is updated, it would be better to set directly the options with the code that initializes `$form['mymodule_select']`. In this case, the options would be translatable too.
> 1 votes

# Answer
To read the file mymodule/options.txt in PHP use:

Drupal 8 + 9:

```
$file_path = drupal_get_path('module', 'mymodule') . '/options.txt';
$result = file($file_path);

```

Drupal 10 (works since Drupal 9.3):

```
$file_path = \Drupal::service('extension.list.module')->getPath('mymodule') . '/options.txt';
$result = file($file_path);

```

See change record https://www.drupal.org/node/2940438.
> 0 votes

---
Tags: 8
---


# Question
Title: Custom field type that has an array property won't save unless serialized, otherwise get "This value should be of the correct primitive type" error
I have a custom field type with two properties I am storing on it. One stores a string. The other I want to store an array. The array should be serialized in the field value table of the database, but it should be an array when exported to YAML (default value setting of the field instance configuration).

I am having trouble getting this to work. In my head it would be similar to 'allowed formats' option on text long instances.

The field value schema:

```
field.value.myfieldtype:
  type: mapping
  label: Default value
  mapping:
    view_id:
      type: string
      label: View ID
    facets:
      type: sequence
      label: Facet list
      sequence:
        type: string
        label: 'Facet machine name'

```

The field properties are defined as:

```
  /**
   * {@inheritdoc}
   */
  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {

    $properties['view_id'] = DataDefinition::create('string')
      ->setLabel(t('A Drupal View.'))
      ->setRequired(TRUE);

    $properties['facets'] = DataDefinition::create('string')
      ->setLabel(t('A list of facets selected by the user.'))
      ->setRequired(FALSE);

    return $properties;
  }

```

In the custom form widget I made, I have massageFormValues like this:

```
  /**
   * {@inheritdoc}
   */
  public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
    $facet_list = !empty($values[0]['facets']) ? array_filter($values[0]['facets']) : [];
    $values[0]['facets'] = serialize($facet_list);
    return parent::massageFormValues($values, $form, $form_state);
  }

```

If I call `array_keys` instead of `serialize`, or pass anything that is not a string, I get an error on save:

```
This value should be of the correct primitive type.

```

If I use `serialize`, it saves fine and I can see the serialized value in the tuple in the database. However, if I set a 'Default Value' on the field instance and run a configuration export, I get this:

```
default_value:
  -
    view_id: 'search:content_listing'
    facets: 'a:1:{s:16:"content_category";s:16:"content_category";}'

```

What I would expect is:

```
default_value:
  -
    view_id: 'search:content_listing'
    facets:
      - content_category
      - (other selected options)
      - (other selected options)

```

What am I missing?

# Answer
> 2 votes
Instead of serializing, I changed the schema of the property to store as JSON for sake of the storage and export since serialized strings are discouraged.

---
Tags: entities, 9, configuration
---


# Question
Title: Disable core path based breadcrumb only for the frontend theme
I want to disable path based breadcrumbs if the current route is not a registered admin route. The front end of the site is driven entirely by Menu Breadcrumb, but in some cases the URL path is generated dynamically with Pathauto based on a combination of field values. This results in sometimes the parent paths generated cause path based breadcrumb in core to return a breadcrumb when it should not (if the current page is not in the menu, Menu Breadcrumb is skipped). At the same time, I do not want breadcrumbs in the admin interfered with.

Is the way to support both cases to decorate the core PathBasedBreadcrumb service? Here is what I came up with:

My module service definition:

```
services:
  mymodule.breadcrumb:
    class: Drupal\mymodule\PathBasedBreadcrumbBuilderOverride
    decorates: system.breadcrumb.default
    decoration_priority: 10
    public: false
    arguments: [ '@router.request_context', '@access_manager', '@router', '@path_processor_manager', '@config.factory',  '@title_resolver', '@current_user', '@path.current', '@path.matcher', '@router.admin_context']

```

The class:

```
/**
 * Defines a class to build path-based breadcrumbs.
 */
class PathBasedBreadcrumbBuilderOverride extends PathBasedBreadcrumbBuilder {

  /**
   * The admin context service.
   *
   * @var \Drupal\Core\Routing\AdminContext
   */
  protected $adminContext;

  /**
   * {@inheritdoc}
   */
  public function __construct(RequestContext $context, AccessManagerInterface $access_manager, RequestMatcherInterface $router, InboundPathProcessorInterface $path_processor, ConfigFactoryInterface $config_factory, TitleResolverInterface $title_resolver, AccountInterface $current_user, CurrentPathStack $current_path, PathMatcherInterface $path_matcher = NULL, AdminContext $admin_context = NULL) {
    parent::__construct($context, $access_manager, $router, $path_processor, $config_factory, $title_resolver, $current_user, $current_path, $path_matcher);
    $this->adminContext = $admin_context;
  }

  /**
   * {@inheritdoc}
   */
  public function applies(RouteMatchInterface $route_match) {
    return $this->adminContext->isAdminRoute();
  }

}

```

`applies` in that case only works from the admin. I am getting the result I would expect to get (**no** breadcrumb in the frontend of the site unless the current page is in the menu, letting Menu Breadcrumb be the only manager).

Are there other ways of doing this, or is this the simplest way without interfering with core functionality?

# Answer
> 3 votes
Normally it's unnecessary to decorate a tagged service. You can register the extended core service class again with a different priority (in your case between the path based and the menu-based breadcrumb builder) and so avoid conflicts with different modules extending the same service because you don't need to swap the service in the container.

**Example for "Disable core path based breadcrumb only for the frontend theme"**

If you only want to disable the path-based breadcrumb under certain conditions then put the condition in applies() and return an empty breadcrumb:

*/src/MyModuleBreadcrumbBuilder.php*

```
<?php

namespace Drupal\mymodule;

use Drupal\Core\Breadcrumb\Breadcrumb;
use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
use Drupal\Core\Routing\AdminContext;
use Drupal\Core\Routing\RouteMatchInterface;

class MyModuleBreadcrumbBuilder implements BreadcrumbBuilderInterface {

  /**
   * The admin context service.
   *
   * @var \Drupal\Core\Routing\AdminContext
   */
  protected $adminContext;

  public function __construct(AdminContext $admin_context) {
    $this->adminContext = $admin_context;
  }

  /**
   * {@inheritdoc}
   */
  public function applies(RouteMatchInterface $route_match) {
    return !$this->adminContext->isAdminRoute($route_match->getRouteObject());
  }

  /**
   * {@inheritdoc}
   */
  public function build(RouteMatchInterface $route_match) {
    $breadcrumb = new Breadcrumb();
    return $breadcrumb;
  }

}

```

And tag the service with a priority higher than zero but lower than any other breadcrumb builder you don't want to override:

*mymodule.services.yml*

```
services:
  mymodule.breadcrumb:
    class: Drupal\mymodule\MyModuleBreadcrumbBuilder
    arguments: ['@router.admin_context']
    tags:
      - { name: breadcrumb_builder, priority: 5 }

```

---
Tags: 9, breadcrumbs
---