# Question
Title: How to remove an uploaded file with JSON:API?
I have an image field on the User entity that saves to the private filesystem. The field is limited to one image.

I can POST an image file to the field using JSON:API in accordance with this change record.

However, I cannot figure out how to delete a posted file. I don't want to upload a new file to replace it; I want to remove the file that was uploaded from the user. How can I do that?

# Answer
Do you mean the reference to the field, or the actual file?

In the first case you could just PATCH the relative user, setting the field to empty. However the psychical file will remain on the server. To ensure its deletion you'll have to change the default Drupal behaviour: https://www.drupal.org/node/2891902

Please note in case the user field is a media, you'll need to actually delete the media and not only the reference to it on the user entity.

Otherwise, you could just do a DELETE against the file entity, e.g.

`DELETE yoursite.com/sonapi/file/file/ff861ea2-b4ac-459f-9e27-06f8c77e252a`

Please note you'll probably need to nevertheless PATCH the user entity and also DELETE the media entity to drop the dead references.
> 1 votes

# Answer
As @Giuseppe noted, if you want to delete the file and remove the reference to it from the entity, then you need to PATCH the entity and DELETE the file.

I implemented this by PATCHing the entity in the client and then using `hook_ENTITY_TYPE_presave()` to detect when the PATCH occurs (when $entity-\>original has a file reference and $entity does not), and then deleting the file within the presave hook. I think this might prove to be more reliable than trusting the client to do both file DELETE and entity PATCH.

Another issue to watch in this area is the ongoing effort to unify REST, JSON:API, and other file uploads.
> 0 votes

---
Tags: files
---


# Question
Title: How can I split the User name and password group in the register page?
I want to split the User name and password group in the register page into separate fields but they are wrapped into `"<div id="user_register_form"><h1>Register</h1>..."`

The open "div" is set as a prefix on the account name field but I can not find the close div.

# Answer
> 0 votes
You can use the Account field split module for this:

> About
>
> Currently the draggable field "Username and password" on user profile mashes together:
>
> ```
> Username
> Current password
> E-mail address
> Change Password
> Roles
> Status
> Notify user about new account (when admin create new account)
>
> ```
>
> This module aims to split "Username and password" into standalone fields for site builders who want to manage that fields separately.

---
Tags: forms, users
---


# Question
Title: How do I disable caching of a custom token?
I have created a custom token which reads values from the Apache Request Headers. It seems to work fine for my testing, but then when another user subsequently accesses the token, it uses my values. It seems that the token value is being cached. How do I disable caching on custom token values? NOTE: because of the use situation, I need to disable caching on the token value (or whole page), not just add a context.

I have searched for this and not found an answer. There are no cache related settings in hook\_token\_info nor hook\_tokens. I have seen references to using bubbleable metadata in caching (https://www.drupal.org/node/2528662), but I am not clear on what I need to put there to disable caching.

```
/**
 * Implements hook_token_info().
 */
function fred_token_info() {
  $info = [
    'types' => [
      'fred_type' => [
        'name' => t('fred type'),
      ],
    ],
    'tokens' => [
      'fred_type' => [
        'APACHE_HEADERS' => [
          'name' => 'apache_request_headers()',
          'description' => t('Request headers as sent through Apache...not strictly a superglobal, but useful. Works in the Apache, FastCGI, CLI, and FPM webservers.'),
          'dynamic' => TRUE,
        ],
      ],
    ],
  ];
  return $info;
}

/**
 * Implements hook_tokens().
 */
function fred_tokens($type, $tokens, array $data, array $options, \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata) {
  $replacements = [];
  if ($type == 'fred_type') {
    foreach ($tokens as $name => $original) {
      $tokenParts = explode(':',$name);
      if (count($tokenParts) >= 2) {
        switch ($tokenParts[0]) {
          case 'APACHE_HEADERS':
            $fredType =& $_SERVER;
            break;
        }
        $FredKey = $tokenParts[1];
      }
      else {
        return;
      }

      if (!isset($tokenParts[2]) || $tokenParts[2]!=='raw') {
        $replacements[$original] = filter_var($superglobalType[$superglobalKey], FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES|FILTER_FLAG_STRIP_LOW);
      }
    }
  }

  return $replacements;
}

```

# Answer
I eventually found an answer. Modify hook\_tokens to add the following line so that it executes prior to the computation of the tokens (I put it as the first line of the function):

```
\Drupal::service('page_cache_kill_switch')->trigger();

```

Be aware that this prevents anything on the affected page from being cached, whether the end user is logged in or not, but this is perfect for our use case.
> -2 votes

# Answer
You need to provide a cache context with your token. It's essentially a way to tell Drupal to 1) still cache your value but 2) cache a value per context. Drupal has some built-in context values, the one you want is the "user" context (i.e. cache a value per user).

In order to provide the context, `hook_tokens()` provides a `$bubbleable_metadata` object as the fifth parameter. This object is passed down as Drupal recurses through the token segments. Use this object to attach the cache context using `addCacheContexts()`.
> 3 votes

---
Tags: caching, tokens
---


# Question
Title: Creating content on via REST API using POST: proper formatting of json?
I'm trying to do a POST to create content on my site, but I get an *Access Denied for creating field: changed* even though I am an administrator role. When I remove the "changed" field in the JSON I get the *Website encountered an unexpected error* error. I assume my JSON format is incorrect. I got it from doing a GET on one of the published nodes, and just removed the nid and uuid values in the JSON.

Or is there a better way to get the proper format I need to create content or am I including too many fields in the JSON?

```
curl -u xxxxxx:yyyyyy -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "vid": [{"value": 349}],
    "langcode": [{"value": "en"}],
    "type": [{"target_id": "chats", "target_type": "node_type", "target_uuid": "1332c2f2-ecce-49fd-bff4-4bf37dd24f7b"}],
    "status": [{"value": true}],
    "uid": [{"target_id": 1, "target_type": "user", "target_uuid": "0e0c2c1d-ed85-4152-bbd6-3821005b07fd", "url": "\/user\/1"}],
    "title": [{"value": "tessssst"}],
    "created": [{"value": "2023-03-15T17:36:47+00:00", "format": "Y-m-d\\TH:i:sP"}],
    "changed": [{"value": "2023-05-09T19:40:40+00:00", "format": "Y-m-d\\TH:i:sP"}],
    "promote": [{"value": false}],
    "sticky": [{"value": false}],
    "default_langcode": [{"value": true}],
    "revision_translation_affected": [{"value": true}],
    "path": [{"alias": null, "pid": null, "langcode": "en"}],
    "publish_on": [],
    "unpublish_on": [],
    "body": [{"value": "<p>this is a test!<\/p>", "format": "full_html", "processed": "<p>this is a test!<\/p>", "summary": ""}],
    "field_chat_parent": [],
    "field_page": [{"value": 2}]
  }' \
  https://xxxx.yyy/node?_format=json

```

# Answer
> 2 votes
After digging around a few hours. This answer helped: https://drupal.stackexchange.com/a/283883/12585 -- Many of the answers as of 2023-May on the internet is for Drupal 8 and some of the parameters are deprecated like hal+json. This works for **Drupal 9.4** as of today.

I managed to first create a node using the most simplified amount of variables:

```
curl -u myuser:password -X POST \
  -H "Content-Type: application/json" \
  -d '{
  "type":[{"target_id":"page"}],
  "title":[{"value":"Hello World xxxx221"}],
  "body":[{"value":"<p>this is a test!<\/p>", "format": "full_html"}]
}' \
  https://xxxxxxxxx.com/node?_format=json

```

Pasting the above into the command line will just work and won't throw an error. The error I ran into was due to the fact I was copying all the json output from the initial query and pasting everything to the POST. Some of the things will need to be changed to make it work with POST for **Drupal 9**.

---
Tags: services, 9
---


title: Deleting a File from Website

text:
# Question
Title: Deleting a File from Website
I host my municipality's website (lionsbay.ca) through Drupal. On this website, residents can find council minutes and agendas. Upon request of a resident, she would like her information removed from a past agenda file that was posted in 2021, however, I cannot seem to delete the file through the website. I've tried tracing the file path but I cannot seem to find the direct source of the file's location. My guess is that the file is stored on a repository somewhere. Any help would be appreciated. Thanks in advance.

# Answer
> 1 votes
Files are generally attached to entities (nodes, webforms, etc.) So you need to find where the file is being used and delete that entity or remove the reference to the file.

If you're using core's Media to handle the files, you can use the Entity Usage module to determine where the files are referenced. If you're using a standard File field, you'll need to do the search on your own. (I would start by looking in the database in the Files table if you didn't have any other clues.)

Also, very important for issues where you are legally required to delete files, **Drupal will not delete unused files by default**. So you will need to use something like the File Delete module to search your system for unused files and remove them.

All of this assumes that the file upload was through Drupal; if you're using Google Forms or something to collect the files, the file won't be in Drupal and will be in whatever system you used to collect it.

---
Tags: files
---


# Question
Title: How can we resize a Media Image inserted using Ckeditor5?
D9.5 - Ckeditor 1 (v5)

We have Media Images and are using Ckeditor's "Insert Media" button in the WYSIWYG but we cannot resize the images.

Is this possible with Media images?

# Answer
> 2 votes
Yes, you can do this with the patch I built. There are patches for

* Drupal 9.5x
* Drupal 10.0x
* Drupal 10.1x
* Drupal 11.x Dev

https://www.drupal.org/project/drupal/issues/3359725

Once install patch, then visit text editor configuration page, enable `Use <img> tag for Media Image type` option in Filter settings \ Embed media

---
Tags: media, wysiwyg
---


# Question
Title: How to create a QueueWorker and run it without cron
Can someone please help me with an example on how to create a `QueueWorker` class, add items, and run it inside `hook_ENTITY_TYPE_presave()` or anywhere in a module file?

Basically, what I want is adding an item to the queue and run it soon after a node save, without waiting until the next cron runs.

# Answer
What you probably want is a different kind of queue worker. See https://mglaman.dev/blog/flush-and-run-using-kernelterminate-improve-page-speed-performance

A simple example:

*/src/EventSubscriber/TaskOnTerminateSubscriber.php*

```
<?php

namespace Drupal\mymodule\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\TerminateEvent;
use Symfony\Component\HttpKernel\KernelEvents;

/**
 * A subscriber running a task by request
 */
class TaskOnTerminateSubscriber implements EventSubscriberInterface {

  /**
   * TRUE if a task run is requested.
   *
   * @var bool
   */
  protected $runTask = FALSE;

  /**
   * Request a task run
   */
  public function runTask() {
    $this->runTask = TRUE;
  }

  /**
   * Run task if requested.
   *
   * @param \Symfony\Component\HttpKernel\Event\TerminateEvent $event
   *   The Event to process.
   */
  public function onTerminate(TerminateEvent $event) {
    if ($this->runTask) {
      // replace this with your long-running task
      sleep(10);
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    return [KernelEvents::TERMINATE => [['onTerminate', 150]]];
  }

}

```

*mymodule.services.yml*

```
services:
  mymodule.task_on_terminate_subscriber:
    class: Drupal\mymodule\EventSubscriber\TaskOnTerminateSubscriber
    tags:
      - { name: event_subscriber }

```

Trigger the task in hook\_ENTITY\_TYPE\_presave/insert/update:

```
\Drupal::service('mymodule.task_on_terminate_subscriber')->runTask();

```

runTask() could contain arguments, like the entity ID. You can also make the service store multiple tasks in an array class property, which would then be a queue stored in memory processed right after the response is flushed to the client.
> 5 votes

---
Tags: 9, queue
---


# Question
Title: Remove certain content types from search
There are certain content types such as book pages that I don't want to appear on the search results page. I found example code implementing this on drupal 7 here

```
/**
 * Implements hook_query_alter().
 * removes content types defined in the excluded_content_types array from search results
 */
function hook_query_alter(&$query) {
  $is_search = FALSE;
  $excluded_content_types = array(
      'product',
      'billboard',
    );
  foreach ($query->getTables() as $table) {
    if ($table['table'] == 'search_index') {
      $is_search = TRUE;
    }
  }
  if ($is_search) {
    foreach ($excluded_content_types as $content_type) {
      $query->condition('n.type', $content_type, '<>');
    }
  }
}

```

However I want to implement this in 8. Is there a way to exclude certain content types from search results?

UPDATE: I've also attempted to restrict the search types from within the search-result.html.twig however it does not seem like I have the content type variable available.

```
{{ attach_library('classy/search-results') }}
{{ title_prefix }}

{{ dump(info_split) }}

<h3{{ title_attributes.addClass('search-result__title') }}>
  <a href="{{ url }}">{{ title }}</a>
</h3>
{{ title_suffix }}
<div class="search-result__snippet-info">
  {% if snippet %}
    <p{{ content_attributes.addClass('search-result__snippet') }}>{{ snippet }}</p>
  {% endif %}
  {% if info %}
    <p class="search-result__info">{{ info }}</p>
  {% endif %}
</div>

```

When performing {{ dump(info\_split) }} it does not show content type as an available variable.

# Answer
Configuring search is easy with Search API. Excluding node types from indexing is one of its basic config options.

https://www.drupal.org/project/search\_api

When you set up your index and go to the configuration page, you can exclude bundles (types).
> 3 votes

# Answer
I had a similar issue and i managed it by writing

```
YOUR_THEME_NAME_preprocess_search_result(&$variables)

```

and by enabling advanced search for anonymous users. Also hide the options from users in

```
YOUR_THEME_NAME_form_search_form_alter(&$form, FormStateInterface $form_state, $form_id)

```

use these functions and `kint($variables)` and `kint($form)`. Hope it will help
> 1 votes

# Answer
We created a new search plugin to restrict indexing of specific CTs.

```
/**
 * Override standard core node search to exclude specific bundles from indexing.
 */
function mymodule_search_plugin_alter(array &$definitions): void {
  if (isset($definitions['node_search'])) {
    $definitions['node_search']['class'] = MyModuleNodeSearch::class;
  }
}

```

And just override **updateIndex** and **indexStatus** methods:

```
<?php

namespace Drupal\mymodule\Plugin\Search;

use Drupal\node\Plugin\Search\NodeSearch;

/**
 * Extend core node search to exclude specific content types from search.
 */
class MyModuleNodeSearch extends NodeSearch {

  const EXCLUDED_BUNDLES = ['article', 'basic_page'];

  public function updateIndex() {
    // ...
    // parent stuff.
    $query->condition('n.type', self::EXCLUDED_BUNDLES, 'NOT IN');
    // ... other query stuff.
  }

  public function indexStatus() {
    $total = $this->database->query('SELECT COUNT(*) FROM {node} [n] WHERE [n].[type] NOT IN ( :bundles[] )', [':bundles[]' => self::EXCLUDED_BUNDLES)->fetchField();
    $remaining = $this->database->query("SELECT COUNT(DISTINCT [n].[nid]) FROM {node} [n] LEFT JOIN {search_dataset} [sd] ON [sd].[sid] = [n].[nid] AND [sd].[type] = :type WHERE ([sd].[sid] IS NULL OR [sd].[reindex] <> 0) AND [n].[type] NOT IN ( :bundles[] )", [
      ':type' => $this->getPluginId(),
      ':bundles[]' => self::EXCLUDED_BUNDLES,
    ])->fetchField();

    return ['remaining' => $remaining, 'total' => $total];
  }
}

```

Nowadays there are lots of modules that allows you restrict indexing/showing specific CTs. Here is a module uses same approach as we: https://dgo.to/search\_exclude

BTW: just in case if you need programmatically reindex core search via drush then use this solution
> 0 votes

---
Tags: 8, search
---


# Question
Title: Showing parts of ajax-enabled forms in Dialog and using submit buttons in Dialog to change the form?
I'd like to achieve the following (d10):

In a form a list of custom drupal plugins will be displayed. Each of those plugins has a settings form array depending on its type that will be merged into the form as part of the list of plugins.

Users can add plugins by clicking an ajax-enabled add-button that shows a dialog popup with buttons for each plugin. This is similar to what paragraphs does when setting it to show an add button for each paragraph type - but in a popup/dialog.

When clicking an add button I'd like the corresponding plugin to be added to the list in the form via ajax including its settings form - without a page reload.

The problem I'm facing is this: Drupal uses jqueryui's dialog which renders its content outside of the form tag which prevents any submit buttons in the dialog from working since there is no form wrapper and no form\_id, build\_id,... hidden fields.

I checked what drupal core does in the block configuration and views forms. It seems like the core devs tried to find ways to achieve something similar but used page reloads (like when adding blocks) or made all changes semi-persistent (view configuration) which requires to store all changes in some kind of draft state.

Both ways to handle this seem like workarounds and create their own set of issues ux-wise and technically.

Am I missing some clever and simple way to implement something like this?

# Answer
After some more digging I found that the paragraphs module allows the option to show the add paragraph type buttons in a popup / dialog and then adds the form for the newly added paragraph, which is exactly what I wanted to do.

What they are doing is this:

* Create a container for all contents of the popup inside the form and add the js-hide class to hide it.
* Add a js behavior that listens on the click event of the add button that creates a deep clone of the popup container dom elements from inside the form and uses the copy as content for the popup.
* Also the behavior listens to the click event of each add button and simply closes the popup.
* Add regular #ajax settings to the add buttons including a callback. The cloned buttons in the popup trigger the exact same event handlers as the ones in the form, allowing to use all the regular ajax form techniques.

See paragraphs/js/paragraphs.modal.js and paragraphs/src/Plugin/Field/Widget/FieldWidget/InlineParagraphsWidget.php for how they do it.

Maybe this helps someone at some point save time :)
> 0 votes

---
Tags: forms, ajax, 10
---


title: Disable pre-populated fields in webform

text:
# Question
Title: Disable pre-populated fields in webform
I have form created with Webform module, some fields are enabled for pre-population, so in some cases I use URL parameters to pre-populate them.

The thing is that I need to forbid users to change pre-populated fields (ether to disable them, or to hide them).

I checked field conditions in Webform configuration, but there's no option for this.

How it's possible to resolve this?

# Answer
> 0 votes
When you edit an element, under form display you should have a list of options:

* Input hiding
* disabled
* read only
* prepopulate

I believe disabled should achieve what you require.

However, as the comment mentioned, there is no way to prevent anyone from amending the query string as they seem fit.

If you need the 'query string' to be 'secure' then you should be looking at a different solution entirely, e.g. you could store it in the session.

---
Tags: webforms, 10
---


# Question
Title: Add base fields to custom entity after install
I have a custom module with a custom entity which has its base fields specified in `baseFieldDefinitions`.

Now I want to add another base field to the entity but I can't see it in neither edit nor form display when applying the code below. I can see it gets added in the database and I have also tried just copy-pasting existing fields from baseFieldDefinitions() (which I know works) without luck. It seems it just doesn't update the frontend.

```
function word_update_9001() {
  $storage_definition = BaseFieldDefinition::create('string')
    ->setLabel(t('Temp Word'))
    ->setCardinality(4)
    ->setDisplayOptions('form', [
      'type' => 'string_textfield',
      'weight' => -5,
    ])
    ->setDisplayConfigurable('form', true);

  $update_manager = \Drupal::entityDefinitionUpdateManager();
  $update_manager->installFieldStorageDefinition('locked', 'word', 'word', $storage_definition);

```

Cache has been cleared several times.

# Answer
> 1 votes
You should do this in two steps. First make a new version of the entity class. Test it by reinstalling or, if you want to keep the data, by installing it on a different site. If the new entity version works and you can edit the entities including the new fields, then write the update hook. You can test the hook also on a second test site by installing first the old version and test the new code, before you apply it on production data.

# Answer
> 0 votes
This is for adding base field to any entity.

1. Implement hook\_base\_field\_info().
2. Update schema (you already did this).

https://bryanmanalo.com/what-drupal-base-field-and-when-do-you-need-create-one

---
Tags: 9
---


# Question
Title: Argument #1 ($callback) must be a valid callback, class Drupal\node\Entity\Node does not have a method "getCurrentUserId"
I upgraded from Drupal 8.9.20 to Drupal 9.59. All modules seem compatible (upgrade manager). The site is up and running, but if I try to add new content via node/add/article, this error appears.

> TypeError: call\_user\_func(): Argument #1 ($callback) must be a valid callback, class Drupal\node\Entity\Node does not have a method "getCurrentUserId" in call\_user\_func() (line 400 of /html/core/lib/Drupal/Core/Field/FieldConfigBase.php)
> #0 /html/core/lib/Drupal/Core/Field/FieldConfigBase.php(400): call\_user\_func('...', Object(Drupal\node\Entity\Node), Object(Drupal\Core\Field\Entity\BaseFieldOverride))
> #1 /html/core/lib/Drupal/Core/Field/FieldItemList.php(169): Drupal\Core\Field\FieldConfigBase-\>getDefaultValue(Object(Drupal\node\Entity\Node)) #2 /html/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php(271): Drupal\Core\Field\FieldItemList-\>applyDefaultValue()
> #3 /html/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php(129): Drupal\Core\Entity\ContentEntityStorageBase-\>initFieldValues(Object(Drupal\node\Entity\Node), Array)
> #4 /html/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php(94): Drupal\Core\Entity\ContentEntityStorageBase-\>doCreate(Array)
> #5 /html/core/lib/Drupal/Core/Entity/EntityForm.php(370): Drupal\Core\Entity\ContentEntityStorageBase-\>create(Array)
> #6 /html/core/lib/Drupal/Core/Entity/HtmlEntityFormController.php(73): Drupal\Core\Entity\EntityForm-\>getEntityFromRouteMatch(Object(Drupal\Core\Routing\RouteMatch), '...')
> #7 /html/core/lib/Drupal/Core/Controller/FormController.php(58): Drupal\Core\Entity\HtmlEntityFormController-\>getFormObject(Object(Drupal\Core\Routing\RouteMatch), '...')
> #8 \[internal function\]: Drupal\Core\Controller\FormController-\>getContentResult(Object(Symfony\Component\HttpFoundation\Request), Object(Drupal\Core\Routing\RouteMatch))
> #9 /html/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(123): call\_user\_func\_array(Array, Array)
> #10 /html/core/lib/Drupal/Core/Render/Renderer.php(580): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber-\>Drupal\Core\EventSubscriber{closure}()
> #11 /html/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(124): Drupal\Core\Render\Renderer-\>executeInRenderContext(Object(Drupal\Core\Render\RenderContext), Object(Closure))
> #12 /html/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(97): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber-\>wrapControllerExecutionInRenderContext(Array, Array)
> #13 /html/vendor/symfony/http-kernel/HttpKernel.php(169): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber-\>Drupal\Core\EventSubscriber{closure}()
> #14 /html/vendor/symfony/http-kernel/HttpKernel.php(81): Symfony\Component\HttpKernel\HttpKernel-\>handleRaw(Object(Symfony\Component\HttpFoundation\Request), 1)
> #15 /html/core/lib/Drupal/Core/StackMiddleware/Session.php(58): Symfony\Component\HttpKernel\HttpKernel-\>handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
> #16 /html/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(48): Drupal\Core\StackMiddleware\Session-\>handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
> #17 /html/core/modules/page\_cache/src/StackMiddleware/PageCache.php(106): Drupal\Core\StackMiddleware\KernelPreHandle-\>handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
> #18 /html/core/modules/page\_cache/src/StackMiddleware/PageCache.php(85): Drupal\page\_cache\StackMiddleware\PageCache-\>pass(Object(Symfony\Component\HttpFoundation\Request), 1, true)
> #19 /html/core/modules/ban/src/BanMiddleware.php(50): Drupal\page\_cache\StackMiddleware\PageCache-\>handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
> #20 /html/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(48): Drupal\ban\BanMiddleware-\>handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
> #21 /html/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(51): Drupal\Core\StackMiddleware\ReverseProxyMiddleware-\>handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
> #22 /html/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\Core\StackMiddleware\NegotiationMiddleware-\>handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
> #23 /html/core/lib/Drupal/Core/DrupalKernel.php(718): Stack\StackedHttpKernel-\>handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
> #24 /html/index.php(19): Drupal\Core\DrupalKernel-\>handle(Object(Symfony\Component\HttpFoundation\Request))
> #25 {main}

Do you know how to fix that?

# Answer
> 1 votes
Probably you are missing an update hook (did you run update.php on every update step?), but if this is the only problem it can be easily fixed:

```
drush cedit | grep core.base_field_override.node.*.uid

```

Now you see base field overrides of the uid field (one for each content type).

Enter the number of the first item listed. You have to enter it blindly because we grepped the prompt.

Correct this line:

```
default_value_callback: 'Drupal\node\Entity\Node::getDefaultEntityOwner'

```

Save the file and repeat the procedure for the other base fields.

You can also do this by copy-pasting the configuration in the admin config UI (single export and import).

---
Tags: 9, upgrading
---


# Question
Title: Getting error 'Redirects to external URLs are not allowed by default'
Getting the error after created this .htacces in root folder.

`RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} !^/web
RewriteRule ^(.*)$ /web/`

I want to redirect visitors to /web folder. I can login as Admin and enable modules, install themes and create content without any issues.

When a theme is installed, content created and publish it I get a blankpage with:

"Redirects to external URLs are not allowed by default, use \Drupal\Core\Routing\TrustedRedirectResponse for it."

I have trusted hostsettings configured like this in settings.php

`$settings['trusted_host_patterns'] = array(
 '^my-domain\.com$',
 '^my-domain\.com\.web$',
 '^.+\.my-domain\.com$',
 '^.+\.my-domain\.com\.web$',`

according to this issue uncomment `RewriteBase /` didn't work out either

Hope someone has a solution to this.

# Answer
If you still haven't figured this out, check this comment on d.o: https://www.drupal.org/project/drupal/issues/2612160#comment-11767977

It suggests a workaround by removing the subdir from the script name, adding to settings.php the following:

```
if (isset($GLOBALS['request']) and
'/subdirectory/index.php' === $GLOBALS['request']->server->get('SCRIPT_NAME')) {
$GLOBALS['request']->server->set('SCRIPT_NAME', '/index.php');
}

```
> 4 votes

# Answer
You should change move the content of web to the root directory, this a step by step to do it. https://tecnovatorexperts.com/articles/fixing-drupal-redirect-error-how-use-trustedredirectresponse-external-urls
> -1 votes

---
Tags: 8, redirection, .htaccess
---


title: Interactive Drush Script

text:
# Question
Title: Interactive Drush Script
I would like to write an interactive Drush script that *potentially* makes a change to all pages of a website. Let's say we would would like to change "Foo" to "Baz" on each page. We want to review each changed page before it is saved. The code would look something like this:

```
foreach(website as node) {
  if node->body->value contains "Foo" {
    change "Foo" to "Baz" and display the changed version of body for the user
    Prompt User: Commit change? Yes or No
    if "Yes", save the node
    }
}

```

It appears Drush is buffering output so that I can not interact with the user in this way. Is this possible?

# Answer
> 5 votes
Drush has built-in functions to handle I/O.

If you're using Drush \<9.x, there's the procedural `drush_confirm()` that will prompt a user for a response to a given message. If you're using 9.x+, any custom command class extending the `DrushCommands` class will have access to the `$this->io()->confirm` method which does the same thing with Symphony's CLI components.

See the latest Drush docs for more info on I/O.

# Answer
> 2 votes
I found the following 3 ways to prompt for user input in Drush which contains a solution.

```
if (drush_confirm('Print \'Hello world\' to the screen?')) {
  drush_print('Hello world!');
}
else {
  drush_user_abort();
}

```

# Answer
> 0 votes
On New drush versions, can you use the below, in a both command file and as drush script e.g. `drush scr /path/to/script.php`

```
use Drush\Exceptions\UserAbortException;

// Script to setup the round theme.
$do_run_if_no_input = FALSE;
$answer = $this->io()->confirm('Do you want to continue?', $do_run_if_no_input);
if (!$answer) {
    throw new UserAbortException("Command cancelled.");
}
$this->io()->writeln('Continuing...');

```

@see https://github.com/drush-ops/example-drush-extension/blob/master/ExampleCommands.php

---
Tags: 8, drush
---


# Question
Title: How to alter webform programmatically using hook_ENTITY_TYPE_prepare_form()
Using Webform 6.2.0-beta5 on D10.0.9.

I have created a custom webform with a module; this is working perfectly.

Now I need to make several changes to the webform; some values will be calculated within my custom module, used as the default value for fields, and then the fields will be hidden.

With this code I can see the $webform details:

```
function mymodule_webform_submission_prepare_form(WebformSubmissionInterface $webform_submission, $operation, FormStateInterface $form_state) {
  $webform = $webform_submission->getWebform();
  if ($webform->id() == 'custom_webform_id') {
    \Drupal::messenger()->addStatus($webform);
  }
}

```

How do I now, for example:

1. Add a value to a webform field
2. Hide that webform field from the user

This will have the effect of programmatically adding values to the webform fields.

Thanks

# Answer
> 0 votes
Goal: Add some hidden fields to a webform, calculate the values for those fields in custom code, and then submit them with the webform.

You could use a hook for this, but I think there's a better approach: standard Webform hidden fields with a custom token.

Webfoorm supports creating hidden fields in the UI, so just create the fields you need there.

Then, you can set the value of the hidden fields to a token-- this is where your custom code comes in. You can create tokens in a custom module, and then, after you rebuild the cache, your tokens will be available for use in webform.

Here's a tutorial about creating tokens, and the Examples module also has sample code for creating tokens.

---
Tags: webforms, 10
---


# Question
Title: Schema insert row only if primary key doesn't exist
I'm doing an exercise where I have a table called `visit_counter` with fields `uid` (primary key that stores the user id) and `user_visit_count` (sum of user's page visits). The purpose of the table is to store the user id and to count the times the user visits the page that loads the controller.

My problem is that my `if condition` is not working well. I'm trying to insert the row with the `uid` (primary key, user id), however, I need to execute the insert only after I have validated that the `uid` doesn't exist.

The following graphic is what I expect. To be able to check the stored values in the `visit_counter` table (sorry if the image is too big, I don't know how to resize it). In this exercise I'm forced to use `insert` or `update`.

Mymodule.install

```
/**
 * Implements hook_schema().
 */
function mymodule_schema() {
  $schema['visit_counter'] = [
    'description' => 'Descripción counter',
    'fields' => [
      'uid' => [
        'type' => 'int',
        'not null' => TRUE,
        'default' => 0
      ],
      'user_visit_count' => [
        'type' => 'int',
        'not null' => TRUE,
        'default' => 0
      ]
    ],
    'primary key' => ['uid'],
  ];

  return $schema;
}

```

src/Controller/MyModuleController.php.

```
<?php

namespace Drupal\mymodule\Controller;
use Drupal\Core\Controller\ControllerBase;

use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Database\Connection;

/**
 * Returns responses for MyModule Database routes.
 */
class MyModuleController extends ControllerBase {
  protected $database;
  protected $currentUser;

  public function __construct(Connection $database, AccountInterface $current_user) {
    $this->database = $database;
    $this->currentUser = $current_user;
  }

  public static function create(ContainerInterface $container) {
    return new static(
        $container->get('database'),
        $container->get('current_user'),
    );
  }

  /**
   * Builds the response.
   */
  public function build() {
    $connection = $this->database;
    $currentUser = $this->currentUser;
    $visit = 1;

    // Here I pretend to retrieve the row that contains the user uid
    $query = $connection->select('visit_counter', 't1')
        ->fields('t1', ['uid'])
        ->condition('uid', $uid, '=');
    $result = $query->execute();

    // Try to check result
    dpm($result);

    // if $result is false, then execute insert. But the problem is that it never execute it.
    if (!$result) {
      $insert = $connection->insert($dbTable)
        ->fields(['uid', 'user_visit_count']);

      $insert->values([
        'uid' => $uid,
        'user_visit_count' => $visit
      ]);

      $insert->execute();
    }

    $update = $connection->update($dbTable)
      ->expression('user_count', 'user_count + :visit', [':visit' => $visit]);
    $update->execute();

    $build['content'] = [
      '#type' => 'item',
      '#markup' => $this->t('It works!'),
    ];

    return $build;
  }

```

# Answer
The shown code creates a new row, if a row for that user ID does not exist, or updates the existing row. For that, there is the `Merge` class.

In the question case, the code to update the database row would be the following one.

```
$connection->merge('visit_counter')
  ->key('uid', $currentUser->id())
  ->fields(['user_visit_count' => $visit])
  ->expression('user_visit_count', 'user_visit_count + :inc', [':inc' => 1])
  ->execute();

```

`fields()` is used to insert a database row, if a row for that user ID does not exist; `expression()` is used to update the existing database row, if there is already a row for that user ID.
> 1 votes

---
Tags: database
---


# Question
Title: How do I use a hook to modify CiviCRM payment processor details in Webform?
I am trying to use `hook_form_alter()` to change the details of the CiviCRM Payment Processor for specific users (to change from live mode to test mode).

On Drupal 7 it's in `$form['submitted']` when *Page 2* is rendered. Many items are already there on *Page 1*, but this is not.

Are these somewhere else, or do I have to use a different mechanism, in Drupal 9?

# Answer
> 0 votes
You should use an extension. See here:

https://docs.civicrm.org/dev/en/latest/hooks/

But that's for CiviCRM hooks. Drupal hooks indeed still work in a .module file. Docs are here:

https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Form%21form.api.php/function/hook\_form\_alter/9

To see all of the various parts, you can use

```
$formValues = $form_state->getUserInput();
print_r($formValues);die;

```

once, in order to see all the values. You can then use

```
$form_state->setValue();

```

to set a value.

---
Tags: webforms, 9, civicrm
---


title: Get taxonomy terms

text:
# Question
Title: Get taxonomy terms
I wanted to retrieve taxonomy terms from a certain vocabulary in Drupal 8.

Apparently I can still do it using `taxonomy_get_tree` but it's deprecated.

I now have to use TermStorageInterface::loadTree

I am trying to access this function from a `Block` but I don't understand how to instantiate the `TermStorageInterface` class.

I tried accessing the function directly but it's not a static function :

```
TermStorageInterface::loadTree('categories')

```

I tried instantiating the class but it told me `Cannot instantiate interface Drupal\taxonomy\TermStorageInterface`

```
$test = new TermStorageInterface();

```

I don't understand how this class works and how I can access taxonomy links. I think I'm missing a big part of understanding how Drupal works.

# Answer
Replacing a deprecated function is in most cases trivial. Just look at it. There you can see this:

```
\Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree($vid, $parent, $max_depth, $load_entities);

```

If you are looking for a function that was already removed, search for it on Change records for Drupal core page. Pretty much every function that was removed should have more or less (usually more) detailed instructions on how to do it in Drupal 8 instead.

The storage class is an entity storage handler, that you get through the entity manager. In general, 99% of the classes in D8 are not meant to be created yourself, but as a service, or entity handler, plugin.

For example:

```
$vid = 'vocabulary_name';
$terms =\Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree($vid);
foreach ($terms as $term) {
 $term_data[] = array(
  'id' => $term->tid,
  'name' => $term->name
 );
}

```
> 58 votes

# Answer
This is what I use to create a list of tags:

```
  use Drupal\taxonomy\Entity\Term;
  use Drupal\Core\Link;
  use Drupal\Core\Url;

  $vocabulary_name = 'YOUR_VOCABULARY_NAME'; //name of your vocabulary
  $query = \Drupal::entityQuery('taxonomy_term');
  $query->condition('vid', $vocabulary_name);
  $query->sort('weight');
  $tids = $query->execute();
  $terms = Term::loadMultiple($tids);
  $output = '<ul>';
  foreach($terms as $term) {
      $name = $term->getName();;
      $url = Url::fromRoute('entity.taxonomy_term.canonical', ['taxonomy_term' => $term->id()]);
      $link = Link::fromTextAndUrl($name, $url);
      $link = $link->toRenderable();
      $rendered = \Drupal::service('renderer')->render($link);
      $output .='<li>'.$rendered.'</li>';
  }
  $output .= '</ul>';
  print $output;

```

Edit: updated the deprecated method render();
> 15 votes

# Answer
If you need term entity, you can use 'loadByProperties()'.

```
$vid = 'vocabulary_name';
/** @var \Drupal\taxonomy\Entity\Term[] $terms */
$terms =\Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties(['vid' => $vid]);

```
> 11 votes

# Answer
The error you are getting is because you are trying to create an instance of an interface, which is not something PHP allows. PHP interfaces describe the methods classes should implement for specific cases, but they cannot be used to create an object, for example with `new InterfaceName()`.

Now that `taxonomy_get_tree()` has been removed, and the entity manager service has been deprecated, you need to use the following code.

```
$terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree($vid, $parent, $max_depth, $load_entities);

```

There is not need to use `\Drupal::getContainer()` since `\Drupal` exposes a helper method to get the entity type manager service.
> 5 votes

# Answer
\Drupal::entityManager() is now deprecated so this worked for me

```
$vids = Vocabulary::loadMultiple();
foreach ($vids as $vid) {
  if ($vid->label() == 'YourVocab') {
    $container = \Drupal::getContainer();
    $terms = $container->get('entity.manager')->getStorage('taxonomy_term')->loadTree($vid->id());
    if (!empty($terms)) {
      foreach($terms as $term) {
        dsm($term->name);
      }
    }
    break;
  }
}

```
> 4 votes

# Answer
Load using machine name of the vocabulary (vid):

```
  $vid = 'name_of_your_vocabulary';
  $terms =\Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree($vid);
  foreach ($terms as $term) {
   $term_data[] = array(
    "id" => $term->tid,
    "name" => $term->name
   );
   dpm($term_data);
  }

```
> 4 votes

# Answer
I've just wrote a function, feel free to edit and use :) I needed the id of the term, but you can return whatever you want.

```
function checkTaxonomyTerm($vocab_name, $term_name){
    $query = \Drupal::entityQuery('taxonomy_term');
    $query->condition('vid', $vocab_name);
    $tids = $query->execute();
    $terms = Term::loadMultiple($tids);
    foreach($terms as $term) {
        $name = $term->getName();
        if($name == $term_name) {
            print_r($term->id());
            if (is_null($term->id())) {
                return null;
            }
            else{
                return array(true, $term->id());
            }
        }
        else {return addTaxonomyTerm($term->getVocabularyId(), $name);}
    }
}

```
> 2 votes

# Answer
This question have already been answered correctly, but if you need to get the translated name of your term, here's how I did it :

```
$values = [];
$lang_code = \Drupal::languageManager()->getCurrentLanguage()->getId();
$vocabulary_name = 'VOCABULARY_NAME_HERE';
$query = \Drupal::entityQuery('taxonomy_term');
$query->condition('vid', $vocabulary_name);
$query->sort('weight');
$tids = $query->execute();
$terms = Term::loadMultiple($tids);
foreach ($terms as $term) {
  $taxonomy_term_trans = \Drupal::service('entity.repository')->getTranslationFromContext($term, $lang_code);
  $values[$term->id()] = $taxonomy_term_trans->getName();
}

```
> 2 votes

# Answer
Here is the D8 example how to access taxonomy terms based on the vocabulary machine name:

```
$terms = \Drupal::entityManager()->getStorage('taxonomy_term')->loadTree('categories');
foreach ($terms as $term) {
  //$value = $term->get('field_example')->getValue();
  var_dump($term);
}

```

To load the whole entities, use: `loadTree('categories', 0, NULL, TRUE)`.
> 1 votes

# Answer
```
$vid = 'MACHINE_NAME_OF_VACABULARY';
$parent_tid = 0;//parent id
$depth = 2; //depth upto which level you want
$load_entities = FALSE;
$tree = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree($vid, $parent_tid, $depth, $load_entities);

foreach ($tree as $term) {
     $treeNames[] = array(
      'name' => $term->name
     );
}
dump($treeNames);

```
> 0 votes

# Answer
I have a custom function which works fine in Drupal 9 i.e

```
  /**
   * Get taxonomy term options for a form field.
   *
   * @param string $taxonomy_term_vid
   *
   * @return array
   */
  public function get_taxonomy_term_options_form($taxonomy_term_vid) {
    // Load taxonomy term data with vid.
    $term = \Drupal::service('entity_type.manager')->getStorage('taxonomy_term')->loadByProperties(['vid' => $taxonomy_term_vid]);

    // Create an array.
    $taxonomy_term_array = [];

    // Loop through field widget and sanitize.
    foreach($term as $key=>$value){
      $taxonomy_term_array[$term[$key]->tid->value] = $term[$key]->name->value;
    }

    return $taxonomy_term_array;
   }

```
> 0 votes

# Answer
if you want to load taxonomy terms with lower memory usage in Drupal 10, you can use the `TermStorageInterface::getQuery()` method to build a query that retrieves only the term IDs, and then use the `TermStorageInterface::load()` method to load each term one by one. Here's an example code snippet:

```
  $entity_type_manager = \Drupal::entityTypeManager();
  $vid = 'your_vocabulary_machine_name';
  $term_storage = $entity_type_manager->getStorage('taxonomy_term');
  $query = $term_storage->getQuery()
    ->condition('vid', $vid);
  $tids = $query->execute();

  foreach ($tids as $tid) {
    $term = $term_storage->load($tid);
    // Do something with the term.
  }

```

In this example, replace `'your_vocabulary_machine_name'` with the machine name of the vocabulary containing the terms you want to load. The `$query->execute()` method returns an array of term IDs that match the query, which you can then use to load each term individually with the `$term_storage->load($tid)` method.

This approach is more memory-efficient than using `TermStorageInterface::loadMultiple()` for large vocabularies, as it loads each term one by one instead of loading all terms into memory at once. However, it may be slower than loading multiple terms at once if the vocabulary is small or if you need to load a large number of terms.
> 0 votes

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


# Question
Title: How to extend duration of the the one time login link?
Is it possible to change the valid duration time of the one time login link that is send out by mail to the new user? I think by default it is 24 hours, ist it possible to change this to 72 hours?

# Answer
> 12 votes
For Drupal 7: In your `settings.php` try adding this:

`$conf['user_password_reset_timeout'] = '259200';`

# Answer
> 4 votes
In Drupal 8, you need to add the following line to your settings.php file:

`$config['user.settings']['password_reset_timeout'] = 259200;`

See the documentation for Global overrides:

> Drupal 8 retains the possibility of using the global $config overrides. The configuration system integrates these override values via the Drupal\Core\Config\ConfigFactory::get() implementation. When you retrieve a value from configuration, the global $config variable gets a chance to change the returned value.

# Answer
> 0 votes
One can do the same in Drupal 8 via Drush too.

* To get the current setting by default it is 86400 sec means 1 day

```
drush config:get user.settings password_reset_timeout

```
* To change the reset timeout duration for 2 weeks

```
drush config:set user.settings password_reset_timeout 1209600

```

# Answer
> 0 votes
Use User Password Reset Link Timeout module.

---
Tags: users
---


# Question
Title: How do I stop Fields being rendered twice for a node with Paragraph Types using UI Patterns?
I'm using UI Patterns within Paragraph Types which is resulting in the Fields being rendered twice in the theme. I'm using a sub theme of the bootsrap5 contrib theme. I have a custom layout twig:-

```
{#
/**
 * @file
 * Theme override to display a two-column layout.
 *
 * Available variables:
 * - in_preview: Whether the plugin is being rendered in preview mode.
 * - content: The content for this layout.
 * - attributes: HTML attributes for the layout <div>.
 */
#}

{%
    set classes = [
        'col-12',
        'col-md-9',
        'col-md-3',
    ]
%}

{% if content %}
    <div{{attributes.addClass('container')}}>
        <div{{attributes.addClass('row')}}>

            {% if content.first %}
                <div {{ region_attributes.first.addClass(classes).removeClass('col-md-9') }}>
                    {{ content.first }}
                </div>
            {% endif %}

            {% if content.second %}
                <div {{ region_attributes.second.addClass(classes).removeClass('col-md-3') }}>
                    {{ content.second }}
                </div>
            {% endif %}

        </div>

    </div>
{% endif %}

```

Screenshot of the result:-

The resulting HTML is below:-

```
<div class="field field--name-field-components field--type-entity-reference-revisions field--label-hidden field__items">
              <div class="field__item">

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'paragraph' -->
<!-- FILE NAME SUGGESTIONS:
   * paragraph--section--default.html.twig
   * paragraph--section.html.twig
   * paragraph--default.html.twig
   x paragraph.html.twig
-->
<!-- BEGIN OUTPUT from 'themes/custom/b5subtheme/paragraphs/templates/paragraph.html.twig' -->
  <div class="paragraph paragraph--type--section paragraph--view-mode--default">

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'pa_third_2thirds' -->
<!-- FILE NAME SUGGESTIONS:
   x pa-third-2thirds.html.twig
   x pa-third-2thirds.html.twig
-->
<!-- BEGIN OUTPUT from 'themes/custom/b5subtheme/templates/layouts/pa-third-2thirds.html.twig' -->

    <div class="container">
        <div class="container row">

                            <div class="col-12 col-md-3">

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'ds_entity_view' -->
<!-- BEGIN OUTPUT from 'modules/contrib/ds/templates/ds-entity-view.html.twig' -->

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'pattern_alert' -->
<!-- FILE NAME SUGGESTIONS:
   * pattern-alert--variant-warning--layout--paragraph--100056.html.twig
   * pattern-alert--variant-warning--layout--paragraph--ui-suite-test--default.html.twig
   * pattern-alert--variant-warning--layout--paragraph--default.html.twig
   * pattern-alert--variant-warning--layout--paragraph--ui-suite-test.html.twig
   * pattern-alert--variant-warning--layout--paragraph.html.twig
   * pattern-alert--variant-warning--layout.html.twig
   * pattern-alert--layout--paragraph--100056.html.twig
   * pattern-alert--layout--paragraph--ui-suite-test--default.html.twig
   * pattern-alert--layout--paragraph--default.html.twig
   * pattern-alert--layout--paragraph--ui-suite-test.html.twig
   * pattern-alert--layout--paragraph.html.twig
   * pattern-alert--layout.html.twig
   * pattern-alert--variant-warning.html.twig
   x pattern-alert.html.twig
-->
<!-- BEGIN OUTPUT from 'themes/custom/b5subtheme/templates/patterns/alert/pattern-alert.html.twig' -->

<div class="alert-warning alert-dismissible fade show alert">
    <h4 class="alert-heading">

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'field' -->
<!-- FILE NAME SUGGESTIONS:
   * field--default--paragraph--field-alert-heading--ui-suite-test.html.twig
   * field--default--paragraph--ui-suite-test.html.twig
   * field--default--paragraph--field-alert-heading.html.twig
   * field--default--field-alert-heading--ui-suite-test.html.twig
   * field--default--ui-suite-test.html.twig
   * field--default--field-alert-heading.html.twig
   * field--default.html.twig
   * field--theme-ds-field-default.html.twig
   * field--paragraph--field-alert-heading--ui-suite-test.html.twig
   * field--paragraph--field-alert-heading.html.twig
   * field--paragraph--ui-suite-test.html.twig
   * field--field-alert-heading.html.twig
   * field--string.html.twig
   x field.html.twig
-->
<!-- BEGIN OUTPUT from 'themes/contrib/bootstrap5/templates/field/field.html.twig' -->

  <div class="field field--name-field-alert-heading field--type-string field--label-above">
    <div class="field__label">Heading</div>
              <div class="field__item">This is the heading!</div>
          </div>

<!-- END OUTPUT from 'themes/contrib/bootstrap5/templates/field/field.html.twig' -->

</h4>

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'field' -->
<!-- FILE NAME SUGGESTIONS:
   * field--default--paragraph--field-simple-text--ui-suite-test.html.twig
   * field--default--paragraph--ui-suite-test.html.twig
   * field--default--paragraph--field-simple-text.html.twig
   * field--default--field-simple-text--ui-suite-test.html.twig
   * field--default--ui-suite-test.html.twig
   * field--default--field-simple-text.html.twig
   * field--default.html.twig
   * field--theme-ds-field-default.html.twig
   * field--paragraph--field-simple-text--ui-suite-test.html.twig
   * field--paragraph--field-simple-text.html.twig
   * field--paragraph--ui-suite-test.html.twig
   * field--field-simple-text.html.twig
   * field--string-long.html.twig
   x field.html.twig
-->
<!-- BEGIN OUTPUT from 'themes/contrib/bootstrap5/templates/field/field.html.twig' -->

  <div class="field field--name-field-simple-text field--type-string-long field--label-above">
    <div class="field__label">Message</div>
              <div class="field__item">Hello! I'm a message!</div>
          </div>

<!-- END OUTPUT from 'themes/contrib/bootstrap5/templates/field/field.html.twig' -->

      <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
  </div>

<!-- END OUTPUT from 'themes/custom/b5subtheme/templates/patterns/alert/pattern-alert.html.twig' -->

<!-- END OUTPUT from 'modules/contrib/ds/templates/ds-entity-view.html.twig' -->

                </div>

                            <div class="col-12 col-md-9">

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'ds_entity_view' -->
<!-- BEGIN OUTPUT from 'modules/contrib/ds/templates/ds-entity-view.html.twig' -->

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'pattern_card' -->
<!-- FILE NAME SUGGESTIONS:
   * pattern-card--variant-default--layout--paragraph--100057.html.twig
   * pattern-card--variant-default--layout--paragraph--ui-suite-card--default.html.twig
   * pattern-card--variant-default--layout--paragraph--default.html.twig
   * pattern-card--variant-default--layout--paragraph--ui-suite-card.html.twig
   * pattern-card--variant-default--layout--paragraph.html.twig
   * pattern-card--variant-default--layout.html.twig
   * pattern-card--layout--paragraph--100057.html.twig
   * pattern-card--layout--paragraph--ui-suite-card--default.html.twig
   * pattern-card--layout--paragraph--default.html.twig
   * pattern-card--layout--paragraph--ui-suite-card.html.twig
   * pattern-card--layout--paragraph.html.twig
   * pattern-card--layout.html.twig
   * pattern-card--variant-default.html.twig
   x pattern-card.html.twig
-->
<!-- BEGIN OUTPUT from 'themes/custom/b5subtheme/templates/patterns/card/pattern-card.html.twig' -->
<div class="card">

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'field' -->
<!-- FILE NAME SUGGESTIONS:
   * field--default--paragraph--field-card-media--ui-suite-card.html.twig
   * field--default--paragraph--ui-suite-card.html.twig
   * field--default--paragraph--field-card-media.html.twig
   * field--default--field-card-media--ui-suite-card.html.twig
   * field--default--ui-suite-card.html.twig
   * field--default--field-card-media.html.twig
   * field--default.html.twig
   * field--theme-ds-field-default.html.twig
   * field--paragraph--field-card-media--ui-suite-card.html.twig
   * field--paragraph--field-card-media.html.twig
   * field--paragraph--ui-suite-card.html.twig
   x field--field-card-media.html.twig
   * field--entity-reference.html.twig
   * field.html.twig
-->
<!-- BEGIN OUTPUT from 'themes/custom/b5subtheme/templates/overrides/field/field--field-card-media.html.twig' -->

  <div class="field field--name-field-card-media field--type-entity-reference field--label-above">
    <div class="field__label">Card image</div>
              <div class="field__item">

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'image_formatter' -->
<!-- BEGIN OUTPUT from 'themes/contrib/bootstrap5/templates/field/image-formatter.html.twig' -->

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'image' -->
<!-- BEGIN OUTPUT from 'themes/contrib/bootstrap5/templates/field/image.html.twig' -->
<img src="/sites/default/files/2023-01/turkeys.png" width="400" height="400" alt="turkeys" loading="lazy" typeof="foaf:Image" class="img-fluid">

<!-- END OUTPUT from 'themes/contrib/bootstrap5/templates/field/image.html.twig' -->

<!-- END OUTPUT from 'themes/contrib/bootstrap5/templates/field/image-formatter.html.twig' -->

</div>
          </div>

<!-- END OUTPUT from 'themes/custom/b5subtheme/templates/overrides/field/field--field-card-media.html.twig' -->

      <div class="card-header">

  </div>

    <div class="card-footer">

  </div>
    </div>

<!-- END OUTPUT from 'themes/custom/b5subtheme/templates/patterns/card/pattern-card.html.twig' -->

<!-- END OUTPUT from 'modules/contrib/ds/templates/ds-entity-view.html.twig' -->

                </div>

        </div>

    </div>

<!-- END OUTPUT from 'themes/custom/b5subtheme/templates/layouts/pa-third-2thirds.html.twig' -->

      </div>

<!-- END OUTPUT from 'themes/custom/b5subtheme/paragraphs/templates/paragraph.html.twig' -->

</div>
              <div class="field__item">

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'ds_entity_view' -->
<!-- BEGIN OUTPUT from 'modules/contrib/ds/templates/ds-entity-view.html.twig' -->

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'pattern_alert' -->
<!-- FILE NAME SUGGESTIONS:
   * pattern-alert--variant-warning--layout--paragraph--100056.html.twig
   * pattern-alert--variant-warning--layout--paragraph--ui-suite-test--default.html.twig
   * pattern-alert--variant-warning--layout--paragraph--default.html.twig
   * pattern-alert--variant-warning--layout--paragraph--ui-suite-test.html.twig
   * pattern-alert--variant-warning--layout--paragraph.html.twig
   * pattern-alert--variant-warning--layout.html.twig
   * pattern-alert--layout--paragraph--100056.html.twig
   * pattern-alert--layout--paragraph--ui-suite-test--default.html.twig
   * pattern-alert--layout--paragraph--default.html.twig
   * pattern-alert--layout--paragraph--ui-suite-test.html.twig
   * pattern-alert--layout--paragraph.html.twig
   * pattern-alert--layout.html.twig
   * pattern-alert--variant-warning.html.twig
   x pattern-alert.html.twig
-->
<!-- BEGIN OUTPUT from 'themes/custom/b5subtheme/templates/patterns/alert/pattern-alert.html.twig' -->

<div class="alert-warning alert-dismissible fade show alert">
    <h4 class="alert-heading">

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'field' -->
<!-- FILE NAME SUGGESTIONS:
   * field--default--paragraph--field-alert-heading--ui-suite-test.html.twig
   * field--default--paragraph--ui-suite-test.html.twig
   * field--default--paragraph--field-alert-heading.html.twig
   * field--default--field-alert-heading--ui-suite-test.html.twig
   * field--default--ui-suite-test.html.twig
   * field--default--field-alert-heading.html.twig
   * field--default.html.twig
   * field--theme-ds-field-default.html.twig
   * field--paragraph--field-alert-heading--ui-suite-test.html.twig
   * field--paragraph--field-alert-heading.html.twig
   * field--paragraph--ui-suite-test.html.twig
   * field--field-alert-heading.html.twig
   * field--string.html.twig
   x field.html.twig
-->
<!-- BEGIN OUTPUT from 'themes/contrib/bootstrap5/templates/field/field.html.twig' -->

  <div class="field field--name-field-alert-heading field--type-string field--label-above">
    <div class="field__label">Heading</div>
              <div class="field__item">This is the heading!</div>
          </div>

<!-- END OUTPUT from 'themes/contrib/bootstrap5/templates/field/field.html.twig' -->

</h4>

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'field' -->
<!-- FILE NAME SUGGESTIONS:
   * field--default--paragraph--field-simple-text--ui-suite-test.html.twig
   * field--default--paragraph--ui-suite-test.html.twig
   * field--default--paragraph--field-simple-text.html.twig
   * field--default--field-simple-text--ui-suite-test.html.twig
   * field--default--ui-suite-test.html.twig
   * field--default--field-simple-text.html.twig
   * field--default.html.twig
   * field--theme-ds-field-default.html.twig
   * field--paragraph--field-simple-text--ui-suite-test.html.twig
   * field--paragraph--field-simple-text.html.twig
   * field--paragraph--ui-suite-test.html.twig
   * field--field-simple-text.html.twig
   * field--string-long.html.twig
   x field.html.twig
-->
<!-- BEGIN OUTPUT from 'themes/contrib/bootstrap5/templates/field/field.html.twig' -->

  <div class="field field--name-field-simple-text field--type-string-long field--label-above">
    <div class="field__label">Message</div>
              <div class="field__item">Hello! I'm a message!</div>
          </div>

<!-- END OUTPUT from 'themes/contrib/bootstrap5/templates/field/field.html.twig' -->

      <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
  </div>

<!-- END OUTPUT from 'themes/custom/b5subtheme/templates/patterns/alert/pattern-alert.html.twig' -->

<!-- END OUTPUT from 'modules/contrib/ds/templates/ds-entity-view.html.twig' -->

</div>
              <div class="field__item">

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'ds_entity_view' -->
<!-- BEGIN OUTPUT from 'modules/contrib/ds/templates/ds-entity-view.html.twig' -->

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'pattern_card' -->
<!-- FILE NAME SUGGESTIONS:
   * pattern-card--variant-default--layout--paragraph--100057.html.twig
   * pattern-card--variant-default--layout--paragraph--ui-suite-card--default.html.twig
   * pattern-card--variant-default--layout--paragraph--default.html.twig
   * pattern-card--variant-default--layout--paragraph--ui-suite-card.html.twig
   * pattern-card--variant-default--layout--paragraph.html.twig
   * pattern-card--variant-default--layout.html.twig
   * pattern-card--layout--paragraph--100057.html.twig
   * pattern-card--layout--paragraph--ui-suite-card--default.html.twig
   * pattern-card--layout--paragraph--default.html.twig
   * pattern-card--layout--paragraph--ui-suite-card.html.twig
   * pattern-card--layout--paragraph.html.twig
   * pattern-card--layout.html.twig
   * pattern-card--variant-default.html.twig
   x pattern-card.html.twig
-->
<!-- BEGIN OUTPUT from 'themes/custom/b5subtheme/templates/patterns/card/pattern-card.html.twig' -->
<div class="card">

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'field' -->
<!-- FILE NAME SUGGESTIONS:
   * field--default--paragraph--field-card-media--ui-suite-card.html.twig
   * field--default--paragraph--ui-suite-card.html.twig
   * field--default--paragraph--field-card-media.html.twig
   * field--default--field-card-media--ui-suite-card.html.twig
   * field--default--ui-suite-card.html.twig
   * field--default--field-card-media.html.twig
   * field--default.html.twig
   * field--theme-ds-field-default.html.twig
   * field--paragraph--field-card-media--ui-suite-card.html.twig
   * field--paragraph--field-card-media.html.twig
   * field--paragraph--ui-suite-card.html.twig
   x field--field-card-media.html.twig
   * field--entity-reference.html.twig
   * field.html.twig
-->
<!-- BEGIN OUTPUT from 'themes/custom/b5subtheme/templates/overrides/field/field--field-card-media.html.twig' -->

  <div class="field field--name-field-card-media field--type-entity-reference field--label-above">
    <div class="field__label">Card image</div>
              <div class="field__item">

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'image_formatter' -->
<!-- BEGIN OUTPUT from 'themes/contrib/bootstrap5/templates/field/image-formatter.html.twig' -->

<!-- THEME DEBUG -->
<!-- THEME HOOK: 'image' -->
<!-- BEGIN OUTPUT from 'themes/contrib/bootstrap5/templates/field/image.html.twig' -->
<img src="/sites/default/files/2023-01/turkeys.png" width="400" height="400" alt="turkeys" loading="lazy" typeof="foaf:Image" class="img-fluid">

<!-- END OUTPUT from 'themes/contrib/bootstrap5/templates/field/image.html.twig' -->

<!-- END OUTPUT from 'themes/contrib/bootstrap5/templates/field/image-formatter.html.twig' -->

</div>
          </div>

<!-- END OUTPUT from 'themes/custom/b5subtheme/templates/overrides/field/field--field-card-media.html.twig' -->

      <div class="card-header">

  </div>

    <div class="card-footer">

  </div>
    </div>

<!-- END OUTPUT from 'themes/custom/b5subtheme/templates/patterns/card/pattern-card.html.twig' -->

<!-- END OUTPUT from 'modules/contrib/ds/templates/ds-entity-view.html.twig' -->

</div>
          </div>

```

# Answer
> 2 votes
So this turned out to be a face palm and a half!

The Field in question had the Display set to "Rendered Entity" with no "UI Limit".

Changing the "UI Limit" to 1 fixed it.

However, as this is a Paragraph Type the Display should really be "Paragraph Type".

Hope this helps anyone experiencing this problem!

---
Tags: paragraphs, bootstrap-theme
---


# Question
Title: Is there a way to add a Mapbox style to Drupal Leaflet?
I have recently upgraded to the latest version of Drupal, and the module 'Leaflet Mapbox' (https://www.drupal.org/project/leaflet\_mapbox) is no longer supported. I was wondering how I would go about adding a Mapbox style to the Drupal Leaflet map? I have tried following the Mapbox documentation, however, the leaflet map is already a container on drupal so this does not work.

# Answer
> 0 votes
The Leaflet Mapbox module has now been released for Drupal 8/9

---
Tags: views
---


# Question
Title: Creating taxonomy terms via REST API using POST: proper formatting of json?
On Drupal 9.4 here and I want to create taxonomy terms via the api. Is there a good example of what I need to POST to an API end point?

# Answer
Yes, make sure you have taxonomy REST services enabled at: **/admin/config/services/rest**

From there, you need to just POST to the following url: **mydomain.com/taxonomy/term?\_format=json**

The format of the json you need the following only: (fyi, its in javascript for a nodejs file running in the cli.)

```
const postData = JSON.stringify({
  vid: [{ target_id: 'vocabulary_name', target_type: 'taxonomy_vocabulary' }],
  name: [{ value: "some name here" }],
});

```

Just pass that data into https request. Keep in mind to add in your user / pass authentication.

```
const options = {
  hostname: 'mydomain.com',
  path: '/taxonomy/term?_format=json',
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Basic ' + Buffer.from('myuser:password').toString('base64'),
    'Content-Length': Buffer.byteLength(postData),
  },                                                                                };

```
> 0 votes

---
Tags: taxonomy-terms, services, 9
---


# Question
Title: Error with {{ page.highlighted }} while making a copy of Olivero
I am trying to learn Drupal 9 theming by copying Olivero theme into my themes folder.

I copied **web/core/themes/olivero** to **web/themes/olive** and then renamed all file names containing 'olivero' to replace it with 'olive'. Then I replaced all text inside all files from 'olivero' to 'olive'.

This gives me the error:

*The website encountered an unexpected error. Please try again later. TypeError: Drupal\Core\Render\Renderer::doTrustedCallback(): Argument #1 ($callback) must be of type callable, array given, called in /var/www/html/web/core/lib/Drupal/Core/Render/Renderer.php on line 788 in Drupal\Core\Render\Renderer-\>doTrustedCallback() (line 51 of core/lib/Drupal/Core/Security/DoTrustedCallbackTrait.php).*

I realized that the theme works if I remove `{{ page.highlighted }}` from the **web/themes/olive/templates/layout/page.html.twig**

Also, the theme seems to be working fine if I don't rename it and keep it olivero even if I move it to **web/themes/**

Can you please help me figure out how to fix this error and keep the `{{ page.highlighted }}` block?

# Answer
> 1 votes
Ok, I figured the issue out -- while renaming I failed to rename one file **web/themes/olivero/src/OliveroPreRender.php** because of the capitalization. Renaming the file + updating the class name fixes the problem.

# Answer
> 0 votes
You should not copy the theme to your local folder. If you want to modify a core theme, you should create a sub-theme of the theme you want to alter, and make your edits there. This means that any updates to the core module will flow down to your sub-theme.

# Answer
> -1 votes
Copying core themes and use it in `themes/` is a wrong way in Drupal. Rather You should create a custom theme having a base theme Olivero.

You should follow this theme where I use Olivero as base theme: https://github.com/priyadegwekar/drupal9/tree/priya/web/themes/custom/olivero\_sub\_theme

With this you can keep highlighted region too.

---
Tags: theming
---


# Question
Title: How can I patch a Composer dependency to make the dependency compatible with other dependency versions?
I'm trying to update my site from D9 to D10, I've had quite a lot of modules to go through, and for the most part a lot of them have releases which are stable for D10, but roughly 4-5 of them only have the auto-generated patch (which I've referenced in the composer.json file)

Except for some reason composer doesn't seem to acknowledge the local patched version when I'm trying to update Drupal Core

if I run `composer update -W` I get errors like this

```
- drupal/pdf[1.1.0, ..., 1.x-dev] require drupal/core ^8 || ^9 -> found drupal/core[8.0.0-beta6, ..., 8.9.x-dev, 9.0.0-alpha1, ..., 9.5.x-dev] but it conflicts with your root composer.json require (^10).

```

So then I ran `composer show drupal/pdf` to get

```
name     : drupal/pdf
descrip. : Display PDF file in Drupal without external readers and plugins.
keywords :
versions : * 1.1.0
type     : drupal-module
license  : GNU General Public License v2.0 or later (GPL-2.0-or-later) (OSI approved) https://spdx.org/licenses/GPL-2.0-or-later.html#licenseText
homepage : https://www.drupal.org/project/pdf
source   : [git] https://git.drupalcode.org/project/pdf.git 8.x-1.1
dist     : [zip] https://ftp.drupal.org/files/projects/pdf-8.x-1.1.zip 8.x-1.1
path     : /var/www/html/docroot/modules/contrib/pdf
names    : drupal/pdf

support
source : https://git.drupalcode.org/project/pdf

requires
drupal/core ^8 || ^9

```

Obviously I can see the bottom line appears to be what it's failing from, but I have already installed the patch locally via composer which fixes this, and I can even see that my locally installed file says

`core_version_requirement: ^9.3 || ^10`

Is there anything I have to do other than reference the patch file for the D10 upgrade for these custom modules?

# Answer
> 5 votes
`composer update` is run before patches are applied, which is why your patch is not working, as it is not seeing the patched version yet, and therefore finds incompatibilities.

See this link for information on how to deal with this issue: https://www.drupal.org/docs/develop/using-composer/using-drupals-lenient-composer-endpoint

---
Tags: composer, 10
---


# Question
Title: How can I debug an "access denied" on almost every webform?
**I checked every configuration section that I thought could affect access to webforms.** Except for the sample content named *Demo: Application* (/node/371 in my context), every webform - the URL prefix is /form/ - can be accessed just by an administrator, that is a unprivileged user gets an *Access denied* answer (*You are not authorized to access this page*). This is driving me nuts. I cannot tell since when this problem is occurring. Before realizing there is such an issue the Drupal instance has been upgraded from an 8 to a 9 version and then from 9 to 10, and later the Nodeaccess module has been installed - but I did configure it properly to let people other than administrator to view and modify forms.

In a separate test instance I disabled the Nodeaccess module: the outcome didn't change, as far as accessing the webform is concerned. So what is going on? **What may prevent any unprivileged authenticated user from accessing an arbitrary webform? How can I debug this?**

Thanks in advance!

Andrea

# Answer
The webform module alone has 11 places where it throws an access denied exception. There could be hundreds more in core, contrib or custom code.

To find it the easiest way would be a debugger stopping on exceptions.

If you don't have one you can throw a custom exception when any access denied exception is raised:

*vendor/symfony/http-kernel/Exception/AccessDeniedHttpException.php*

```
public function __construct(?string $message = '', \Throwable $previous = null, int $code = 0, array $headers = [])
{
    // add this line temporarily
    throw new \Exception("ACCESS DENIED: $message");
    parent::__construct(403, $message, $previous, $headers, $code);
}

```

Enable "All messages, with backtrace information" on */admin/config/development/logging* and you will see a backtrace on the screen under your custom exception pointing at the place where the access is denied.

When you are finished with debugging don't forget to remove the added code line.

If you find `AccessAwareRouter->checkAccess()` in the backtrace it's a routing/permission issue. Then this topic might help Debug "Access Denied"
> 2 votes

---
Tags: webforms, debugging
---


title: How can I add HTML links in e-mails?

text:
# Question
Title: How can I add HTML links in e-mails?
```
          $content_title = $translated_entity->getTitle();
          $url_alias = \Drupal::service('pathauto.alias_cleaner')
            ->cleanString($content_title);
          $content_link = <<<link
    <a href='/publications/$url_alias'>$url_alias</a>
    link;
          $params['node_title'] = 'Chaire ANMT – Nouvelle Publication : ' . $entity->label() . '';
          $params['message'] = <<<msg
    Nouvelle publication : $content_title $content_link. \n
    msg;
//some code
  $result = $mailManager->mail($module, $key, $subscriber, $langcode, $params, null, $send);

```

The email generated do not place the link next to the content title. How to fix that?

# Answer
> 4 votes
That is expected, when the Drupal mail back-end that comes with Drupal, implemented by the `PhpMail` class, is used. In fact, its `format()` method contains the following code.

```
  // Join the body array into one string.
  $message['body'] = implode("\n\n", $message['body']);

  // Convert any HTML to plain-text.
  $message['body'] = MailFormatHelper::htmlToText($message['body']);

  // Wrap the mail body for sending.
  $message['body'] = MailFormatHelper::wrapMail($message['body']);

```

`MailFormatHelper::htmlToText()` is the method that changes the HTML markup as you see.

The only way to avoid that is to use a different mail back-end or implement one in a custom module.

---
Tags: emails
---


# Question
Title: How do I hide the core version to the outside world?
I was bothered in knowing with whatcms.org a version can be given for drupal 8, even a minor version - no minor version, instead, when I tried with a drupal 7. Curiously enough in my case, I got a version slightly newer (just one minor version ahead), but I'm concerned anyway, should I not?

I'm thinking of every situation where a security update cannot be performed right away. I don't even know how can the version be guessed from outside. I issued a `tail -f` on the access log while pressing the *Detect CMS* button on whatcms.org, but, mysteriously, I got no log entry from there. Perhaps they cache the previous results on the same site not to perform a fetch each time the button is pressed with the same domain string input?

# Answer
> 1 votes
If you want to hide the Drupal version then you can use the following module:

https://www.drupal.org/project/remove\_http\_headers

# Answer
> 0 votes
whatcms.org does not seem to get the right Drupal version used on a site; for drupal.org, it reports it is using Drupal 7.92, while drupal.org is using Drupal 7.96, which is the version visible in its CHANGELOG.txt file.

While Drupal 8.x stopped to give detailed information about the changes in the recently created releases in its CHANGELOG.txt file, it is possible to understand whether a site is using Drupal 8 because that files contains the following line.

> For a full list of fixes in the latest release, visit: https://www.drupal.org/8/download

Drupal 9.0.x changed that line to the following one, more generic.

> For a full list of fixes in the latest release, visit: https://www.drupal.org/latest-release

There could be other publicly accessible files that can be used to understand what Drupal version is used, especially knowing which JavaScript libraries are used from each Drupal version (and which version of those JavaScript libraries). For example, the absence of the jQuery Once library, on a site that could be either be using Drupal 9 or Drupal 10, says the site is using Drupal 10 or any higher version; if the installed jQuery version is 3.6.3, the site could be using Drupal 9.5, 10.0.x, or 10.1.x, any release created after December 21, 2022.

You can alter the HTTP headers returned by a Drupal site, but sites could still gather information from the files they are still able to access, including any JavaScript or CSS file, which are necessary for a browser to correctly render Drupal pages and cannot be made inaccessible.

---
Tags: security
---


# Question
Title: How do I remove duplicate taxonomy field values in the paragraph display mode template?
How to remove duplicate values in this code, if they are in "{{ post.entity.name.value }}"?

```
{% block paragraph %}{% block content %}

    {% for post in paragraph.field_post %}
        {% if loop.last %}
            {% set separator = '' %}
        {% else %}
            {% set separator = ',' %}
        {% endif %}
        {{ post.entity.name.value }}{{ separator }}
    {% endfor %}

{% endblock %}{% endblock paragraph %}

```

I have a "Paragraph" type field. In the paragraph I have two fields: place of work (node reference) and position (taxonomy field).

Sometimes there is a need to display only the values of taxonomy fields on the site, and they can be repeated, because there may be several places of work. An employee can work in the same positions in several places. **How do I remove duplicates in the paragraph display mode template?**

As usual, I turned to ChatGPT, she gave me this solution

```
{% for post in paragraph.field_post %}{% set unique_names = post.entity.name.value|split(',')|batch(1)|map(item => item|unique)|join(',') %}{{ unique_names }}{% endfor %}

```

But there is a problem. The "unique" filter is not a standard filter in Twig. It can only be accessed by using third-party Twig extensions or Composer packages :(

# Answer
Since we output ready-made values in the tiwg templates and avoid complex code, the best solution in this situation turned out to be using the view module. I created the view I needed and set up the context, enabled aggregation, and then embedded it in the right place on the page.

Using the paragraph display mode template for displaying posts turned out to be not a flexible solution for me.
> 0 votes

---
Tags: 9, paragraphs
---


# Question
Title: How to disable browser autocomplete for the login form
I would like to remove auto fill user name and password while login or forgot password.

I have tried this but its not working:

```
if ($form_id == 'user-login-form') {
  $form['name']['#attributes']['autocomplete'] = 'off';
}

```

What am I doing wrong?

# Answer
> 0 votes
You can try:

```
$form['account']['name']['#attributes']['autocomplete'] = 'off';

```

# Answer
> 0 votes
Your problem has nothing to do with Drupal - this is a browser issue. "autocomplete" is simply a suggestion to the browser, and the browser does not have to abide by that suggestion. Modern browsers (for at least 6 years now) have ignored it for username/password fields.

Search the web for "autocomplete=off not working" and you will find explanations and work-arounds.

As far as the `hook_form_alter()` goes, the simplest thing is to look at the generated HTML page for that form and see if you successfully added the `autocomplete="off"` attribute to the form tag. If it's not there, then you chose the wrong form ID, or you chose the wrong form element, etc. But as I said, even when you get the `hook_form_alter()` correct, the browser will probably ignore this attribute and you will have to use one of the work-arounds if you want this to happen.

# Answer
> 0 votes
Many modern browsers do not support autocomplete="off" for login fields https://developer.mozilla.org/en-US/docs/Web/Security/Securing\_your\_site/Turning\_off\_form\_autocompletion#the\_autocomplete\_attribute\_and\_login\_fields

If you still want to implement for browsers that allow, it this is an option for Drupal 9.

```
function mymodule_form_alter(&$form, FormStateInterface $form_state, $form_id) {
   if ('user_login_form' == $form_id) {
    $form['#attributes']['autocomplete'] = 'off';
    $form['pass']['#attributes'] = ['autocomplete' => 'off'];
    $form['name']['#attributes'] = ['autocomplete' => 'off'];
  }
}

```

---
Tags: 8, forms
---


# Question
Title: Can a view be accessed if there's no page or block?
The following question may seem contrived, but I have custom access controls besides roles and permissions, so I can place view blocks on pages that are viewable by users in a list, for example.

If I create a view with a default and a block display that has "role: authenticated" access requirement, then I place the block on a page only admins can see, is there a way for regular "authenticated" users to see the view? For example, is there some AJAX URL they could enter to see it?

For security reasons, I need to know if there is some way they could see the data. If so, then I need to create a custom view access plugin.

# Answer
> 2 votes
If the view does not have a page display, then there is no path by which it can be accessed. It cannot be guaranteed that it won't be made available by some contributed module or custom module through an Ajax call, but by default, it won't be accessible to authenticated users if they are not able to access the page the block is on, even when the block itself is visible to authenticated users.

---
Tags: views, 10
---


# Question
Title: How do I compare a date field with the current time in a twig template?
I have a date field on a node – set to a custom date format (*c* which is ISO) in the Manage Display. I have a twig template checking to see if that date field comes before today, in which is should output *Past*. Outputting the fields/comparison values gets me the properly formatted dates I want to compare.

```
{{ content.field_closing_date.0 }}
{{ 'now'|date('c') }}

```

which outputs

```
2021-05-05T16:30:00-07:00
2021-05-14T12:01:53-07:00

```

My comparison is

```
{% if 'now'|date('c') > content.field_closing_date.0 %}
Past
{% endif %}

```

But it's not outputting *Past*. What gives? You can see that the todays date *does* come after the date field.

# Answer
> 1 votes
If you feel like a preprocess function is overload and if you can afford to ignore timezones, try converting both date values to **U**nix timestamps to make them properly comparable. And prefer raw node values over formatted content values:

```
{% if ('now'|date('U') > node.field_closing_date.0.value|date('U')) %}
  Past
{% endif %}

```

# Answer
> 0 votes
Adding the following code in the node template, you can compare the closing date value with the current date and print whatever you want based on that.

```
{% set current_date = "now"|date("Y-m-d") %}
{% set closing_date = node.field_closing_date.value|date("Y-m-d") %}

{% if closing_date < current_date %}
  Past
{% elseif closing_date > current_date %}
  Future
{% endif %}

```

# Answer
> -1 votes
This is my code to compare the current time with a date field for views twig tempplate

```
{% if content.field_expired_date.0['#markup']|date('U') >= "now"|date('U') %}
    Hello Future
{% endif %}

```

---
Tags: 8, theming, datetime
---


# Question
Title: How to invalidate custom block which use Cache Tags and Cache contexts for authenticated user?
I'm working on an application where authenticated user data mostly comes from an API. Performance is significantly better since I'm using the API cache. Before, to be sure to have up-to-date information, I used `'#cache' => ['max-age' => 0,]` but it's really not ideal.

I have a question, however, about the custom block cache for authenticated users.

If I add user context and tag it works fine for authenticated users.

```
return [
            '#theme' => 'lorem',
            '#datas' => $datas,
            '#cache' => [
                'contexts' => ['user'],
                'tags' => ['user:' . \Drupal::currentUser()->id(), 'lorem_custom_block:' . \Drupal::currentUser()->id(), 'lorem_custom_block_user:' . \Drupal::currentUser()->id()]
            ],
        ];

```

But one of my clients to test the application uses his Drupal account (therefore always the same Drupal user id) and changes a value of a custom field in his profile (an external id which is used to retrieve information with the API). In this case, I use a `hook_user_presave` and invalidate the custom block cache like this :

```
Cache::invalidateTags(['lorem_custom_block_user:' . $user_id]);

```

In this case, it does not work. The information displayed in the block is not up to date. I have to remove the context user on the block and it works.

```
return [
            '#theme' => 'lorem',
            '#datas' => $datas,
            '#cache' => [
                'tags' => ['user:' . \Drupal::currentUser()->id(), 'lorem_custom_block:' . \Drupal::currentUser()->id(), 'lorem_custom_block_user:' . \Drupal::currentUser()->id()]
            ],
        ];

```

Is this the normal behavior? it's as if there is a conflict between the context and the tag.

I thought invalidating the tag would be enough.

Maybe it doesn't make sense to use a context in my case. Can you give me your opinion?

# Answer
Blocks are plugins in Drupal, and plugins can be context aware. The key is that you add the context within the annotation:

```
/**
 * @Block(
 *  id = "mymodule_block1",
 *  label = "Block 1",
 *  admin_label = @Translation("Block 1"),
 *  context_definitions = {
 *    "current_user" = @ContextDefinition("entity:user"),
 *  }
 * )
 */

```

You can then retrieve the context in your code for the block.

```
$user = $this->getContextValue('current_user');

```

This *should* vary your block per user - and invalidate itself when the user changes. You can read more about "context aware plugins" where it explains how everything works behind the scenes, create your own, etc.
> 2 votes

---
Tags: blocks, caching
---


# Question
Title: Change the width of the off-canvas dialog
Layout Builder uses the off-canvas dialog. It comes out from the right side of the screen at a max width of 300px. We'd like to make it wider. I can do this with a powerful CSS rule, but is there a better Drupal way? Is there a setting I can't find?

# Answer
> 0 votes
You Can modify the initial width of the offCanvasDialog by setting the 'data-dialog-options'. We're doing that in the same\_page\_preview module with this implementation:

```
$form['toggle_preview_link'] = [
  '#type' => 'link',
  '#title' => t('Launch preview'),
  '#weight' => -1,
  '#url' => Url::fromRoute('same_page_preview.preview_pane', $route_parameters),
  '#attributes' => [
    'class' => ['use-ajax', 'button','button--primary', 'visually-hidden'],
    'data-dialog-type' => 'dialog',
    'data-dialog-renderer' => 'off_canvas',
    'data-dialog-options' => Json::encode([
      'width' => '50%',
      'classes' => [
        // @todo figure out how to override more than just this one.
        'ui-dialog' => 'same-page-preview-dialog',
      ],
    ]),
  ],
  '#attached' => [
    'library' => [
      'core/drupal.dialog.ajax',
    ],
  ],
];

```

This sets the initial width of the dialog to be 50% of the page.

Modifying the width of the dialog after it renders can be done manually by dragging with edge of the dialog. I'm also looking into modifying the width programmatically but don't have that figured out right now. I'll come back here if I do figure it out.

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


title: How to add a radar chart to Views?

text:
# Question
Title: How to add a radar chart to Views?
I'm using the Charts module to create a chart in my view (using its Views integration). But the supported chart type doesn't include **Radar chart**, which is what I would want it to be.

I found a sample of such chart at https://www.drupal.org/files/RadarGraph.png, which looks like so:

Is there a way I can get that to work?

# Answer
# Solution 1 - Using module Charts (with Views)

There is no out-of-the-box answer to your question to make this work with the Charts module, at least not as of today (yet). That's because "Radar chart" is not one of the currently supported chart types (those that are supported are: Pie, Bar, column, Line, Area and Scatter).

But if you're willing (and able) to complete a few extra steps, you should get it to work. Use the details below as a guide, but know also that there are so many options / features in either of the supported charting engines (libraries), i.e. Google Charts and Highcharts. Which is also why it's virtually impossible to support all those options out-of-the-box.

### Option 1

Refer to the issue about Stacking column/bar charts for an example of how you can do this with a custom module. Comment #3 in it contains a sample like so:

```
<?php
function MYMODULE_chart_alter(&$chart, $chart_id) {
  dsm($chart_id); // Need Developer module enabled for dsm to work
  if ($chart_id == 'MYCHARTVIEW__DISPLAY') { // Watch out for those double underscores
    $chart['#stacking'] = TRUE;
  }
}
?>

```

This should work for both Google Charts and Highcharts as the charting library (engine).

The remaining challenge for you is what exactly you should specify as chart\_id, and which specific charting option is needed for such "radar chart". My best bet, for Highcharts, is that it is what that library calls a "Polar Chart" (would be too easy for all those charting libraries to use at least the same terminology ...).

### Option 2

A possible alternative (or maybe refinement, depends on your needs) that you may want to look at is using the approach as described in the issue about Pass library-specific options via existing #raw\_options, AFTER you apply the patch in comment nr 4 of this issue. If you're using the most recent dev version of the Charts module (which is stable and includes various patches on top of the rc1 release), then this patch is already available (no more need to apply it). Be aware that this only works for Highcharts as of right now (waiting for a similar patch to become available for Google charts also).

Refer to the question about "https://drupal.stackexchange.com/questions/183018" (and its answers) for an interesting use-case of these "raw options".

# Solution 2 - Using module Forena (without Views)

The sample radar chart in your question is actually from the Supported chart types, one of the Community documentation pages from the Forena module.

The very same sample is also included in **SVG Graph Demo**, one of the samples that gets installed by the Forena module, such as in this demo site.

So such chart can ALSO be created with the Forena module, which uses the (free and open source) PHP SVG Graph library, without using Views. And by using the integration of Forena with the BEAN module (as explained in its documentation about Bean Integration, you can put such chart in a block that you'd put on your page where it fits your needs.

For (way) more details, head over to the Forena Documentation included with the module (or its equivalent in the demo site). Or have a look at various questions around here tagged with ... and add your own questions if needed (using this tag).

**Disclosure**: I'm a (co-)maintainer of Charts and Forena.
> 1 votes

# Answer
The easiest way is to install the charts module and activate it. Once this is done, you need to use the chart.js library. You have to modify the charts.charts\_types.yml file and add the code below at the end of the file:

```
radar:
  label: 'Radar'
  axis: 'xy'
  axis_inverted: false
  stacking: false

```

Then reload the module page with your browser and empty the caches.
> 0 votes

---
Tags: 7, views, charts
---


title: TypeError: realpath() PublicStream.php

text:
# Question
Title: TypeError: realpath() PublicStream.php
After installation of Drupal9 recommended project (using composer), I had this error on the homepage:

```
TypeError: realpath(): Argument #1 ($path) must be of type string, array given in realpath() (line 133 of core/lib/Drupal/Core/StreamWrapper/PublicStream.php).

```

I changed PHP 8.1 to 7.4, the error disappeared, but \[website\]/admin returned "no access rights to this page"

composer.json in project root:

```
{
    "name": "drupal/recommended-project",
    "description": "Project template for Drupal 9 projects with a relocated document root",
    "type": "project",
    "license": "GPL-2.0-or-later",
    "homepage": "https://www.drupal.org/project/drupal",
    "support": {
        "docs": "https://www.drupal.org/docs/user_guide/en/index.html",
        "chat": "https://www.drupal.org/node/314178"
    },
    "repositories": [
        {
            "type": "composer",
            "url": "https://packages.drupal.org/8"
        }
    ],
    "require": {
        "composer/installers": "^1.9",
        "drupal/core-composer-scaffold": "^9.5",
        "drupal/core-project-message": "^9.5",
        "drupal/core-recommended": "^9.5"
    },
    "conflict": {
        "drupal/drupal": "*"
    },
    "minimum-stability": "stable",
    "prefer-stable": true,
    "config": {
        "allow-plugins": {
            "composer/installers": true,
            "drupal/core-composer-scaffold": true,
            "drupal/core-project-message": true,
            "dealerdirect/phpcodesniffer-composer-installer": true
        },
        "sort-packages": true
    },
    "extra": {
        "drupal-scaffold": {
            "locations": {
                "web-root": "web/"
            }
        },
        "installer-paths": {
            "web/core": [
                "type:drupal-core"
            ],
            "web/libraries/{$name}": [
                "type:drupal-library"
            ],
            "web/modules/contrib/{$name}": [
                "type:drupal-module"
            ],
            "web/profiles/contrib/{$name}": [
                "type:drupal-profile"
            ],
            "web/themes/contrib/{$name}": [
                "type:drupal-theme"
            ],
            "drush/Commands/contrib/{$name}": [
                "type:drupal-drush"
            ],
            "web/modules/custom/{$name}": [
                "type:drupal-custom-module"
            ],
            "web/profiles/custom/{$name}": [
                "type:drupal-custom-profile"
            ],
            "web/themes/custom/{$name}": [
                "type:drupal-custom-theme"
            ]
        },
        "drupal-core-project-message": {
            "include-keys": [
                "homepage",
                "support"
            ],
            "post-create-project-cmd-message": [
                "<bg=blue;fg=white>                                                         </>",
                "<bg=blue;fg=white>  Congratulations, you’ve installed the Drupal codebase  </>",
                "<bg=blue;fg=white>  from the drupal/recommended-project template!          </>",
                "<bg=blue;fg=white>                                                         </>",
                "",
                "<bg=yellow;fg=black>Next steps</>:",
                "  * Install the site: https://www.drupal.org/docs/installing-drupal",
                "  * Read the user guide: https://www.drupal.org/docs/user_guide/en/index.html",
                "  * Get support: https://www.drupal.org/support",
                "  * Get involved with the Drupal community:",
                "      https://www.drupal.org/getting-involved",
                "  * Remove the plugin that prints this message:",
                "      composer remove drupal/core-project-message"
            ]
        }
    }
}

```

Drupal 9.5.9 PHP 7.4

# Answer
That is because `$settings['file_private_path']` is set to an array. It should be a string. That is something you have to correct in `settings.php`.
> 1 votes

---
Tags: 9
---