# Question
Title: Do I need to apply changes in default.settings.php to settings.php?
Recent updates to Drupal 7 have included changes in default.settings.php.

Do they need to be incorporated into settings.php manually, or does the code automatically look up in default.settings.php when it can't find something in settings.php? Apologies if this is in the documentation, I couldn't find it.

# Answer
Speaking specifically about the last change done for the *default.settings.php* file: No, you do not need to apply that change to the *setting.php* file, except in the case you need to change the default value used for the lock expiration timeout, which is 900 and which usually sufficient for any site.

The following is the patch for the change described in \[D7\] Cron lock time limit is too short and does not prevent multiple, concurrent cron runs.

```
diff --git a/includes/common.inc b/includes/common.inc
index 0f6ced800d..094db87f54 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -5532,7 +5532,8 @@ function drupal_cron_run() {
   drupal_alter('cron_queue_info', $queues);

   // Try to acquire cron lock.
-  if (!lock_acquire('cron', 240.0)) {
+  $cron_lock_expiration_timeout = variable_get('cron_lock_expiration_timeout', 900.0);
+  if (!lock_acquire('cron', $cron_lock_expiration_timeout)) {
     // Cron is still running normally.
     watchdog('cron', 'Attempting to re-run cron while it is already running.', array(), WATCHDOG_WARNING);
   }
diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php
index c521bc3b3a..4458ee47d0 100644
--- a/sites/default/default.settings.php
+++ b/sites/default/default.settings.php
@@ -821,3 +821,15 @@
  */
 # $conf['drupal_http_request_strip_sensitive_headers_on_host_change'] = TRUE;
 # $conf['drupal_http_request_strip_sensitive_headers_on_https_downgrade'] = TRUE;
+
+/**
+ * Cron lock expiration timeout:
+ *
+ * Each time Drupal's cron is executed, it acquires a cron lock. Older releases
+ * of Drupal set the default cron lock expiration timeout to 240 seconds. This
+ * duration was considered short, because it often caused concurrent cron runs
+ * especially on busy sites heavily utilizing cron.
+ *
+ * Use this variable to set a custom cron lock expiration timeout (float).
+ */
+# $conf['cron_lock_expiration_timeout'] = 900.0;

```

The last comment explains exactly what the purpose of that value is and why it was introduced.

> Each time Drupal's cron is executed, it acquires a cron lock. Older releases of Drupal set the default cron lock expiration timeout to 240 seconds. This duration was considered short, because it often caused concurrent cron runs especially on busy sites heavily utilizing cron.

If you do not notice any *Attempting to re-run cron while it is already running.* error in the Drupal log, then you do not need to add that line (`$conf['cron_lock_expiration_timeout'] = 900.0;`) to the *settings.php* file used for the site. Otherwise, you need to add it.

> does the code automatically look up in default.settings.php when it can't find something in settings.php?

No, it does not. Drupal only load the *settings.php* file, when running. Any change done to the *default.settings.php* file that is relevant for you need to be copied into the *settings.php* file used for the site. Keep in mind that, in some cases, the copied lines could need to be adapted for your use case.
> 1 votes

---
Tags: 7
---


title: How do I unset a link?

text:
# Question
Title: How do I unset a link?
I want to programmatically decide whether to render a link or not. This should apply to all links e.g anchor tags (no wysywigs) Not only menu links.

I tried using `hook_link_alter`.

```
/**
 * Implements hook_link_alter().
 */
function mymodule_link_alter(&$variables) {
  $my_logic = TRUE;
  if ($my_logic) {
    // Any way to not render the link?
    // I tried:
    // $variables = []; and unsetting keys with no luck
    // $variables['options']['#access'] = FALSE; With no luck.
    // I guess i could set a flag here and somewhere else process it, but where?
  }
}

```

I can alter the structure, but I don't see any way to unset the element. What I want is basically to not render the element if `$my_logic`.

I also tried to use a service with the *path\_processor\_outbound* tag, but I have the same problem. I can "access" all generated links but can just alter / modify the output of `processOutbound()` but not to not process.

I think I can´t use a custom access check because i don´t want the url´s to be inaccessible if directly requested and I rather don´t want to do the check in a template

# Answer
> 0 votes
I couldn't find a way to prevent the link from rendering, but I did find a couple of ways of hiding it, which was good enough in my use case.

* Add a 'hidden' class to the link (along with CSS to `display:none` it)
* Set the link text to an empty string.

```
  $item['options']['attributes']['class'][] = 'hidden';
  $item['text'] = "";

```

If your goal is just to tidy up a menu/hide a link rather than prevent access to the destination page then this might help.

---
Tags: uri, 9
---


# Question
Title: Run composer installed drupal from root (/) and not /web directory
How do I run a composer-installed Drupal site from / instead of /web?

The default install with `composer create-project drupal/recommended-project ./` will install Drupal in the /web directory. So when the site is uploaded to a host, the actual website root will be www.example.com/web. In most cases, the desired path to run the website is www.example.com/.

I have managed to do this, like this. And actually, I am asking if this is the definite correct way. (Clearly, I used my domain, not example.com.)

I have created a *.htaccess* file in the root with the following content.

```
RewriteEngine on
RewriteRule (.*) web/$1 [L]

```

I have uncommented the `RewriteBase` directive.

```
RewriteBase /web

```

I have added these lines in the *web/sites/default/settings.php* file.

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

```

I have also uncommented these lines in the *web/sites/default/settings.php* file to cover the trusted hosts issue.

```
$settings['trusted_host_patterns'] = [
  '^example\.com$',
  '^.+\.example\.com$',
];

```

Is this the correct way?

# Answer
> 3 votes
*web* is assumed to be the server document root directory. In some cases, the server document root directory is a different one, for example *public\_html*.

You could either change the server settings to make the server use *web* as server document root directory, or change the commands executed, if you cannot do that.

As reported in Using Composer to Install Drupal and Manage Dependencies / To do a modified install, instead of running `composer create-project drupal/recommended-project ./`, you need to:

* Run `cd <directory containing the server document root directory>`
* Run `composer create-project --no-install drupal/recommended-project ./`
* Change any reference to *web* found in the *composer.json* file with the server document root directory
* Run `composer install`

For example, with the Linux distribution I am using (Endeavour OS), the default server documentation directory (indicated by `httpd -S` as *Main DocumentRoot*) is /srv/http. To install Drupal in that directory, I would execute the following commands.

```
cd /srv
composer create-project --no-install drupal/recommended-project ./

```

After, I would edit the *composer.json* file to change any occurrence of *web* with *http*. In the case the directory *web* is indicated with its full path, I would change that accordingly. Then, I would run `composer install`.

# Answer
> 0 votes
> Is this the correct way?

No, it's not and it's a security risk.

Ideally, you would configure the web server to point the domain `www.example.com` to the sub-folder `/var/www/my-project/web` (or any other subfolder you have configured in composer.json). All web servers support this. If it's your own server configure a virtual host, hosting providers usually have a nice UI to manage these things.

Only if this is not possible use the legacy project:

```
composer create-project drupal/legacy-project .

```

It's important to use this project and not try to move the web folder yourself to root, because this project has extra protection for the vendor folder now being exposed in `www.example.com/vendor`.

This is the line that needs to be present in composer.json:

```
"require": {
    "drupal/core-vendor-hardening": "^10.0"

```

See https://github.com/drupal/legacy-project/blob/10.0.5/composer.json#L22

To be clear, the problem here is not renaming the subfolder, for example from /web to /public\_html. It's about the original question, running Drupal without a subfolder from the web server's root.

---
Tags: composer
---


# Question
Title: Export product image with Commerce Cart API module
I'm usng the "Commerce Cart API" module on my website to build a custom cart js layer. The problem i am facing is the purchased entity has a field\_image field and I would like get the url of that images instead of the file id.

The json response of cart?\_format=json looks like the following:

```
    [
  {
    "order_id": 9,
    "uuid": "a4604a10-f956-41e7-87f6-0d745b9345a4",
    "order_number": null,
    "store_id": 2,
    "order_items": [
      {
        "order_item_id": 12,
        "uuid": "33bca307-2ee1-4be4-9599-9ecf3604f83c",
        "order_id": 9,
        "purchased_entity": {
          "variation_id": 1181,
          "uuid": "8112b394-5008-4209-85dc-a9deaf5dfea1",
          "type": "default",
          "product_id": 470,
          "sku": "345",
          "title": "Glade Aerosol 330",
          "list_price": {
            "number": "1.000000",
            "currency_code": "ARS",
            "formatted": "ARS 1,00"
          },
          "price": {
            "number": "2.000000",
            "currency_code": "ARS",
            "formatted": "ARS 2,00"
          },
          "resolved_price": {
            "number": "4.000000",
            "currency_code": "ARS",
            "formatted": "ARS 4,00"
          },
          "field_imagenes": [
            122
          ]

```

In brief what i'm tring to get is field\_imagenes as an array of image urls. Is there a way to do it?

Thank you in advance!

# Answer
It's reasonable for you to move to Commerce API?

That module is based on JSONA:API, therefore you should be able to get the data of relationship entities quite easily, look at include\relationship example on docs.

Plus, the headless system on Drupal mostly prefer json:api, including commerce itself
> 0 votes

---
Tags: commerce, 9, uri
---


# Question
Title: 401 error when trying to retrieve OAuth access token
I'm trying to configure simple\_oauth to use with local Lando install of a Drupal 9 site with decoupled React frontend.

I followed the instructions at https://drupalize.me/tutorial/install-and-configure-simple-oauth?p=3003 and generated the necessary RSA keys in the terminal:

```
$ openssl genrsa -out private.key 2048
$ openssl rsa -in private.key -pubout -out public.key

```

...placing the keys into /drupal\_root/key, and specifying their paths at /admin/config/people/simple\_oauth. I opened both files in Gedit and they look standard

Then I created a new 'OAUTH' role, giving it permissions to add, edit and delete content.

I created a consumer, enabled the 'OAUTH' role, saved the configuration and copied its secret and UUID.

Then I followed the instructions at https://drupalize.me/tutorial/make-api-requests-oauth?p=3253 to make a POST request using Postman. The only difference is that instead of their request to http://localhost:8888/oauth/token, I sent it to my Lando url http://react1.lndo.site/oauth/token

I've checked the request headers and body key:values carefully (ensuring client\_id, client\_secret, username and password are correct), but I keep getting 401 responses that say "invalid\_client", "Client authentication failed"

Then I tried making the request with cURL and got the same error response:

```
$ curl -F 'grant_type=password' -F 'client_id=b7958f62-df63-4b95-9a55-20954178d788' -F 'client_secret=react2' -F 'username=oauthuser' -F 'password=password' -F 'scope=oauth' -X POST http://react1.lndo.site/oauth/token

{"error":"invalid_client","error_description":"Client authentication failed","message":"Client authentication failed"}

```

I also tried making the request via cURL using a different format and got a different error message:

```
$ curl -d '{"grant_type":"password", "client_id":"b7958f62-df63-4b95-9a55-20954178d788", "client_secret":"react2", "username":"oauthuser", "password":"password", "scope":"oauth"}' -H "Content-Type: application/x-www-form-urlencoded" -X POST http://react1.lndo.site/oauth/token

{"error":"invalid_grant","error_description":"The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.","hint":"Check the configuration to see if the grant is enabled.","message":"The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client."}

```

What am I missing here?

# Answer
> 0 votes
1. On postman try to remove the accept\content-type headers, for this request the defaults should be okay
2. When saving the oauth client, be sure that Chrome client don't autofill the "User" field with wrong data. The user field ***should be empty***. Obliviously, check also that the password you're using is correct.
3. Check if the permission of the key are correct. I don't remember the right permission set, but for test you could set them as 777 and in the db log should appear a message suggesting the right ones.
4. The scope "oauth" is valid? I mean, is it a valid role for your site?

---
Tags: 9, authentication-authorization
---


# Question
Title: trigger('RefreshView') problems with pagination
Colleagues, can you please advise: there is a views with enabled ajax and pagination, there is a form on the page (also ajax, but not views), which makes changes to the displayed views values. To update the views with the values after the form's ajax processing, I use $(".view-name").trigger('RefreshView'); Everything works fine, but there's a nuance - pagination, specifically, no matter which page we were on, Refresh always redirects to the first page. Any thoughts on where to look for a solution?

# Answer
This is a long time bug in views. I remember I just had a lot of trouble with it on a web site years ago. Take a look at this: https://www.drupal.org/project/drupal/issues/3100826
> 0 votes

---
Tags: 7, views, ajax
---


# Question
Title: Why does Drush crash with "SQLSTATE[HY000] [2002] No such file or directory"?
I have an error when i try to update my db or install a module with drush.

\[info\] Try to validate bootstrap phase 5 \[0.11 sec, 10.08 MB\] \[info\] Unable to connect to database with message: SQLSTATE\[HY000\] \[2002\] No such file or directory. More debug information is available by running `drush status`. This may occur when Drush is trying to bootstrap a site that has not been installed or does not have a configured database. In this case you can select another site with a working database setup by specifying the URI to use with the --uri parameter on the command line. See `drush topic docs-aliases` for details. \[0.11 sec, 10.31 MB\] \[info\] Could not bootstrap at phase 5 \[0.11 sec, 10.29 MB\]

this is my drush status. Drupal version : 9.5.4
Site URI : http://default
DB driver : mysql
DB hostname : localhost
DB port : 3306
DB username : root
DB name : DruCar
PHP binary : /opt/homebrew/Cellar/php@8.1/8.1.15/bin/php
PHP config : /opt/homebrew/etc/php/8.1/php.ini
PHP OS : Darwin
PHP version : 8.1.15
Drush script : /usr/local/bin/drush
Drush version : 11.5.1
Drush temp : /tmp
Drush configs : /Applications/XAMPP/xamppfiles/htdocs/DruCar/recommended-project/vendor/drush/drush/drush.yml Drupal root : /Applications/XAMPP/xamppfiles/htdocs/DruCar/recommended-project/web
Site path : sites/default

anyway, i cant install anything with drush en.

Thank you anticipate.

# Answer
> 1 votes
This is likely the well-known situation of using `localhost` for the database configuration, which is interpreted by some database extensions as an instruction to use a unix socket file, and then the unix socket file is not properly configured. The usual solution is to use `127.0.0.1`, the IPv4 address, instead of `localhost` in the database configuration. See https://serverfault.com/questions/295285/mysql-cannot-connect-via-localhost-only-127-0-0-1

Core has this issue, https://www.drupal.org/project/drupal/issues/2610858, which is attempting to improve error messaging about it, which may or may not impact Drush. Drush has had this question asked many, many times.

# Answer
> 1 votes
agre with answer from @cilefen

this is my example for local project. try doing the same, bottom line - use 127.0.0.1 instead of localhost in file

> settings.local.php

```
$databases['default']['default'] = array (
    'database' => 'yourproject_local',
    'username' => 'root',
    'password' => 'yourpassword',
    'prefix' => '',
    'host' => '127.0.0.1',
    'port' => '3306',
    'driver' => 'mysql',
);

$settings['trusted_host_patterns'] = array(
    '^subdomain\.test\.yourise\.local',
    '^yoursite\.dev\.local$',
    '^subdomain\.something\.yourhosting\.com$',
    '^example\.at$',
    '^www\.example\.at$',
);

```

---
Tags: drush
---


title: Alias listed but cannot be used

text:
# Question
Title: Alias listed but cannot be used
I am using Drupal 9.x now and using Drush 10.x. I have always used drush aliases to manage my D7 sites as it makes life so much easier with various environments etc.

To test the newer YAML file (Drush for my old D7 uses a php file), I created a single Drush alias for a uat environment. The file, local.site.yml, is located in my ~/.drush/sites directory. When I do a `drush sa` to check what aliases exist, I correctly (I think?!) get the following message:

```
'@local.uat':

root: /var/www/my_site/web
uri: 'https://mysite.mydomain.com'

```

So I think this tells me that the single alias has been found.

When I try to use the alias using `drush @local.uat status` it fails:

```
[preflight] The alias @local.uat could not be found.

```

It must be simple... I have tried using just @uat and @site.uat but nothing works (and I would not expect it to!). Any ideas?! I am running Ubuntu 22.04 and Drush 10.6.2 if that makes a difference.

# Answer
OK - I finally got to the bottom of this and it is working.

In order to get this to work all I needed to do was add the path of ANY alias file or files to *each project's* Drush config file i.e. `drush.yml` as well as my own local one.

You can name it whatever you like, `self.site.yml` or `foo.site.yml` if you wish, *there is no need for a special first name*, all you need is `myname.site.yml`. I have tested this and created two identical alias files with different names:

```
self.site.yml

foo.site.yml

```

Both or these files reside in my `~/.drush directory` but that is also not important *as long as the path is defined in drush.yml* in both your local drush installation AND in each project. Remember, you *may* have different versions of Drush per project.

When I do `drush sa`:

```
'@foo.foo':
  root: /var/www/my_site/web
  uri: 'https://mysite.mydomain.com

'@self.uat':
  root: /var/www/my_site/web
  uri: 'https://my_site'

```

Both `drush @uat status` and `drush @foo status` work equally well as they point to the same Drupal root.

The location of the sites aliases file also makes no difference. Changing the location of my sites alias path to 'bar' instead of drush or .drush is fine as long as the local and project drush.yml are aligned. This is how it looks in all my `drush.yml` files now:

```
  paths:
    alias-path:
      - '${env.HOME}/.drush/sites'

```

I could equally have used `${env.HOME}/.drush/sites`, `${env.HOME}/drush/sites` or `${env.HOME}/bar/sites`

I hope this helps others!
> 0 votes

# Answer
Add an alias file called **$PROJECT/drush/sites/self.site.yml**, where *$PROJECT is the project root (location of composer.json file).* The site alias file should be named `self.site.yml` because this name is special, and is used to define the different environments (usually remote) of the current Drupal site.

The contents of the alias file should look something like the example below:

```
# File: self.site.yml
live:
  host: server.domain.com
  user: www-admin
  root: /other/path/to/live/drupal
  uri: http://example.com
stage:
  host: server.domain.com
  user: www-admin
  root: /other/path/to/stage/drupal
  uri: http://stage.example.com

```

The top-level element names (live and stage in the example above) are used to identify the different environments available for this site. These may be used on the command line to select a different target environment to operate on by prepending an @ character, e.g. @live or @stage.

Following these steps, a cache:rebuild on the live environment would be:

```
  $ drush @live cache:rebuild

```
> 1 votes

# Answer
The filepath should be drush/sites/self.site.yml, and the contents should be the following (Indenting must be exact):

```
local.uat:
  root: /var/www/my_site/web
  uri: 'https://mysite.mydomain.com'

```

After which you will be able to call `drush @local.uat`.
> 0 votes

---
Tags: drush
---


# Question
Title: Deleted context link still exists in admin toolbar menu
I have deleted context but in admin toolbar it still showing . On main context admin page context doesn't exists. I have installed/uninstalled module - not helping

How do i remove that link from toolbar menu?

# Answer
Surely i got solved it...

After research and very useful comment on dropal.org I came across to the right answer. I have posted a bug on Admin toolbar project https://www.drupal.org/project/admin\_toolbar/issues/3348914 and one of the comment actually explained to me what happening. Here is a problem with the context menu builder. here is the link https://www.drupal.org/project/context/issues/3284359 on this bug

How do i got fixed ... i didn't applied the patch. I just recreated the contexts with the exact machine name and after saving deleted it .... That fixed my issue.
> 1 votes

---
Tags: 9, configuration, toolbar
---


# Question
Title: How to create and check block revisions?
How to create and check block revisions ? I can see the option at bottom while creating a block but when I click to see the revisions the link doesn't work.

# Answer
> 2 votes
The revisions checkbox on blocks doesn't do anything yet, in fact I think it may have been removed. Block revisioning is being proposed for Drupal 9's generic revision UI. For the latest, please see this post on Drupal.org.

# Answer
> 1 votes
This is possible in Drupal 10, according to this post on Drupal.org as of January 8, 2023.

---
Tags: 8, blocks
---


# Question
Title: Form field states with media fields that use media library widget and conditionally displaying visibility based on other media fields
I am trying to get some media fields to hide/show conditionally using $form fields #states API, based on other media fields (all the media fields are using the "Media Library" widget in form display.) I'm working with a node/add and edit for a content type in Drupal 9.5.4 and using hook\_form\_alter() to manage the states.

There are 3 media fields in total - if one has media added the other two should become invisible/hidden, but if the media that was added is removed then the other two fields should display again - which seems to be impossible (as far as showing them again).

After many hours of trial and error the only success I have achieved is hiding 2 of the 3 fields when the third one of them has media added.

* using `input[name="field_name[selection][0][target_id]` as the conditional controller which is problematic because technically that input doesn't exist in the form until media has been added thru the media library widget. So removing that media "will not" make the other media fields show again as the input is also removed from the form.

It was also kind of dumb luck that I managed to get that input to hide the 2 unused media fields on the form. As I was initially trying to write the #states like I was for some of the other (none media fields in the form).

For example: working with non-media field using the following worked great!

```
$form['field_business_name']['widget'][0]['value'][#states] = [
  'visible' => [
    'select[name="field_location_type"]' => ['value' => 1],
  ],
];

```

"but" trying to use the same format for the controller with a media field (using the media library widget) would not work.. For example:

```
$form['field_media_video_linked'][#states] = [
  'invisible' => [
    'input[name="field_media_video[selection][0][target_id]"]' => ['!value' => ''], // value not empty
  ],
];

```

However if the input was wrapped in another array (nested) it does work like this:

```
$form['field_media_video_linked'][#states] = [
  'invisible' => [
    ['input[name="field_media_video[selection][0][target_id]"]' => ['!value' => ''],],
  ],
];

```

But without the nesting it doesn't work?

That struck me as odd and was the only reason I found that to work because I added an `'or',` to the controller which required I wrap or nest the different control options in another array (as I was testing just using another input from a different "none-media" field to at least make sure I could use states to hide media fields, which does work)

So as of now using one media field to hide another media field works except if the control field/input is removed by clicking "Remove" on the media item the hidden media fields no-longer show and won't return to a visible state "and" I believe that is because when you remove an added media item the input `input[name="field_media_video[selection][0][target_id]"]` gets removed from the form as well and since it no-longer exists there is nothing to check that the value is now empty. Frustrating... So using an `'or',` with `'visible'` for `'input[name="field_media_video[selection][0][target_id]"]' => ['value' => ''], //value is empty` Does nothing!

I'm either addressing this the wrong way or there is a better way to work with states on media fields when the conditional controller is also a media field (that uses the media library widget) and I'd really like to keep using the media library widget, even tho I did find I could switch the widget type to "Inline Entity Form Simple" and I could then use `'input[name="field_media_video[0][field_media_video_file][0][fids]"]' => ['!value' => ''], //not empty` as the controller to make other fields hidden/invisible and then `'input[name="field_media_video[0][field_media_video_file][0][fids]"]' => ['value' => ''], //is empty` to then make them visible again, being the actual input is always in the form (whereas with Media Library widget no input exists until an item/file is added. If all else fails I'll have no choice but to switch the widget type to make this work but it's not what I want to do as there are added benefits to using the library widget as a user could select an uploaded file that "already exists" in their account versus uploading the same content to a new field.

I've spent hours looking through documentation and there is literally nothing for examples or use cases with media related fields when dealing with states.

So, is there a better way "or" what do I need to do to get this working with the "expected" results (keeping the field widgets as media library)?

I can provide additional information if needed, I use dpm() to get the $form, $form\_state and field info for the form on load.

# Answer
This is probably not the way to address media fields states, however it is a solution (or work around I was able to come up with).

Using hook\_form\_alter() the same hook I'm using to work with the media fields as conditional fields - I added a new field (input type radios) to the form to use as the controller which dictates the media fields visibility, disabled and required #states for media fields using the media library widget. That in combination with a little jquery magic and css got the job done effectively.

Here is exactly what I did (in hook\_form\_alter) I created and added the radios field to the forms I'm working with which will be css display none:

```
$form['field_media_set'] = [
  '#type' => 'radios',
  '#options' => [
    'upload' => 'Video Upload',
    'link' => 'Linked Video',
    'gallery' => 'Gallery Images',
    'other' => 'No Media',
  ],
  '#atributes' => [
    'name' => 'field_media_set',
  ],
  '#default_value' => 'other',
  '#weight' => 12,
];

```

I Didn't bother with any data storage for the field as it is just being used to control the 3 media fields using the media library widget in form display (video upload, linked video, and gallery images "which is actually 2 media fields, one for a main/poster image and the other to add up to 12 additional supporting images)

Next I created a new library and added it to the libraries.yml calling it post-library and listed a post.css and post.js file in the library like this:

```
post-library:
  version: 1.x
  js:
    js/post.js: {}

  dependencies:
    - core/jquery

  css:
    theme:
      css/post.css: {}

```

In the post.css file I added:

```
div#edit-field-media-set.form-radios {display: none;}

```

In the post.js I added:

```
function checkMediaFields() {

  var $uploadVideo = $('input[name="field_upload_video[selection][0][target_id]"]');
  var $linkVideo = $('input[name="field_link_video[selection][0][target_id]"]');
  var $gallery = $('input[name="field_gallery_poster_image[selection][0][target_id]"]');
  var $galImages = $('input[name="field_gallery_images[selection][0][target_id]"]');

  if ($uploadVideo.length || $linkVideo.length || $gallery.length || $galImages.length) {
    if ($uploadVideo.length) {
      $('input[name="field_media_set"][value="upload"]').prop('checked', true).change();
    }
    if ($linkVideo.length) {
      $('input[name="field_media_set"][value="link"]').prop('checked', true).change();
    }
    if ($gallery.length || $galImages.length) {
      $('input[name="field_media_set"][value="gallery"]').prop('checked', true).change();
    }
  } else {
    $('input[name="field_media_set"][value="other"]').prop('checked', true).change();
  }
}

$(document).ajaxComplete(function() {
  checkMediaFields();
});

```

As you can see from the js/jQuery there are 4 inputs (what's important to note here is those are the inputs that get added to the form once a media item has been added through the media library widget - "If no media is added those inputs DO NOT EXIST", and that is why those alone cannot be used as the control condition because if you remove the added media those inputs also get removed from the form.

Also note in the js/jQuery the use of "ajaxComplete" function, this is required to trigger the checkMediaFields functions which you can see checks for the existence of the inputs that get added to the form when media is added with a media field. Having that run after ajax completes is key because the media library form display widgets use ajax to upload or add the media in question to the form, without that the function to check for any media related inputs won't get triggered when a media field using media library widget adds new media to the form.

Next in the node-add and node-edit twig templates (example: page--node--add--contenttype-post.html.twig) I added the library I created above:

```
{{ attach_library('custom_module_name/post-library') }}

```

Now back in hook\_form\_alter() here is how I set up the conditional states for the media fields:

```
$form['field_upload_video']['#states'] = [
  'visible' => [
    [
      'input[name="field_media_set"]' => ['value' => 'other'],
    ],
    'or',
    [
      'input[name="field_media_set"]' => ['value' => 'upload'],
    ],
  ],
  'disabled' => [
    [
      'input[name="field_media_set"]' => ['value' => 'link'],
    ],
    'or',
    [
      'input[name="field_media_set"]' => ['value' => 'gallery'],
    ],
  ],
];
$form['field_link_video']['#states'] = [
  'visible' => [
    [
      'input[name="field_media_set"]' => ['value' => 'other'],
    ],
    'or',
    [
      'input[name="field_media_set"]' => ['value' => 'link'],
    ],
  ],
  'disabled' => [
    [
      'input[name="field_media_set"]' => ['value' => 'upload'],
    ],
    'or',
    [
      'input[name="field_media_set"]' => ['value' => 'gallery'],
    ],
  ],
];
$form['field_gallery_poster_image']['#states'] = [
  'visible' => [
    [
      'input[name="field_media_set"]' => ['value' => 'other'],
    ],
    'or',
    [
      'input[name="field_media_set"]' => ['value' => 'gallery'],
    ],
  ],
  'required' => [
    [
      'input[name="field_media_set"]' => ['value' => 'gallery'],
    ],
  ],
  'disabled' => [
    [
      'input[name="field_media_set"]' => ['value' => 'upload'],
    ],
    'or',
    [
      'input[name="field_media_set"]' => ['value' => 'link'],
    ],
  ],
];
$form['field_gallery_images']['#states'] = [
  'visible' => [
    [
      'input[name="field_media_set"]' => ['value' => 'other'],
    ],
    'or',
    [
      'input[name="field_media_set"]' => ['value' => 'gallery'],
    ],
  ],
  'required' => [
    [
      'input[name="field_media_set"]' => ['value' => 'gallery'],
    ],
  ],
  'disabled' => [
    [
      'input[name="field_media_set"]' => ['value' => 'upload'],
    ],
    'or',
    [
      'input[name="field_media_set"]' => ['value' => 'link'],
    ],
  ],
];

```

All of that combined "gets the job done!"

Things worth mentioning: Before using the created radios field "field\_media\_set" I tried a few other field types to use as the controller like a dropdown select field and a set of number input fields (which were intended to be used like the radios field - however those alternate form fields would only work once. What I mean by that is even tho the jQuery would update the fields in the event a previously added media element was "removed" the hidden media fields would not show again. Even when using `.change() or .keyup()` when jQuery made any changes to the controlling fields. For whatever reason only radio checkboxes works 100% of the time for this type of scenario.

There may be a better way (but with the limited documentation concerning the use of media fields using the media library widget as a conditional controller) this is what I came up with.

Hopefully this will help others in a similar situation.
> 0 votes

---
Tags: forms, media
---


# Question
Title: 401 error when trying to retrieve OAuth authorization code
I'm building a decoupled Next.js frontend to consume data from Drupal JSON:API on a local site installed with Lando. I want to use the simple\_oauth module to allow registered users to edit content on the front end.

My first target is requesting an Authorization Code, so I'm testing the request in Postman and with cURL. Both approaches are consistently giving me 401 "invalid\_client" error messages.

To the best of my knowledge, all the settings are correct including the required RSA keys, but I have yet to figure this out. Here's the cURL command I'm using:

```
curl -X GET \
'http://react1.lndo.site/oauth/authorize?client_id=b7958f62-df63-4b95-9a55-20954178d788&response_type=code&redirect_uri=http://react1.lndo.site&scope=oauth'

```

...where http://react1.lndo.site is my Drupal 9 Lando site url, b7958f62-df63-4b95-9a55-20954178d788 is the UUID of my consumer, and 'oauth' is the name of a role I created with permissions to allow registered users to add, edit and delete Article nodes (\*this role is selected in the 'Scopes' list of the consumer).

I've searched for hours including tons of questions to ChatGPT, but have yet to figure this out. Please help me get on the right track.

# Answer
Looking through the Simple OAuth code, a Consumer entity is loaded with the field client\_id having a value of the client\_id parameter from the URL. You said you are passing the UUID of the Consumer as the client\_id in your URL which would be incorrect, and explain why the system says your consumer is not found. You should view/observe/dump the consumer to determine the value of the client\_id field.
> 0 votes

---
Tags: 9, authentication-authorization
---


# Question
Title: How do I programmatically create a node or entity with an overridden layout?
I have a content type, `page`, that has layout builder enabled for the full view mode, with each node able to have its layout overridden.

I need to programmatically create a `page` node, and customize its layout-builder layout to show the following two blocks in a single-colum (vertically-stacked):

* field\_banner (entity reference field to an entity of type `banner`)
* body (node `body` field)

How can I create this node with an overridden layout programmatically?

# Answer
Layout is constructed from Section and SectionComponent entities, which are then added to the field with the ID of OverridesSectionStorage::FIELD\_NAME.

```
// Create the node, but don't save it just yet.
$node = \Drupal\node\Entity\Node::create([
  'type' => 'page',
  'title' => 'Custom Page',
  'body' => [
    'value' => 'This is the body of the node',
    'format' => 'full_html',
  ],
]);

// Load the field that stores the layout.
$layout_field = $node->get(\Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage::FIELD_NAME);

// Create a new section with a one-column layout.
$section = new \Drupal\layout_builder\Section('layout_onecol');
$region = 'Content';

// Create the configuration for the entity reference to field_banner
// on Nodes of type page.
$banner_configuration = [
  'id' => 'field_block:node:page:field_banner',
  'provider' => 'layout_builder',
  'label_display' => 0,
  'label' => 'Banner',
  'formatter' => [
    'label' => 'hidden',
    'type' => 'entity_reference_entity_view',
    'settings' => [
      'view_mode' => 'full',
    ],
    'third_party_settings'
  ],
  'context_mapping' => [
    'entity' => 'layout_builder.entity',
    'view_mode' => 'view_mode',
  ],
];

// Create the layout builder component (item/block).
$banner_component = new \Drupal\layout_builder\SectionComponent(\Drupal::service('uuid')->generate(), $region, $banner_configuration);

// Append the component to the section.
$section->appendComponent($banner_component);

// Create the configuration for the body field.
$body_config = [
  'id' => 'field_block:node:page:body',
  'provider' => 'layout_builder',
  'label' => 'Content',
  'formatter' => [
    'label' => 'hidden',
    'type' => 'text_default',
    'settings' => [],
    'third_party_settings' => [],
  ],
  'context_mapping' => [
    'entity' => 'layout_builder.entity',
    'view_mode' => 'view_mode',
  ],
];

// Create a new layout component.
$body_component = new \Drupal\layout_builder\SectionComponent(\Drupal::service('uuid')->generate(), $region, $body_config);

// Append the component to the section.
$section->appendComponent($body_component);

// Set the entire section to the layout field of the node.
$layout_field->appendItem($section);

// Save the node.
$node->save();

```

To determine the values to use for `$banner_configuration` and `$body_config`, I first created a node through the UI, then set it up with the layout builder components I wanted through the UI. Then, I got the NID of the new entity, and dumped it with Drush to determine the values in the layout field:

```
# 12345 is the Node ID of the node to dump:
drush ev "echo print_r(\Drupal::entityTypeManager()->getStorage('node')->load(12345)->toArray(), TRUE)"

```

I analyzed the dumped values in the field `layout_builder__layout` to determine what values to use for the configuration arrays to create the layout component configuration.
> 0 votes

---
Tags: layout-builder
---


# Question
Title: simple_oauth refresh token works only one time
I use the **\[simple\_oauth\]\[1\]** module to authenticate the user.

I have generated the first **access\_token** using the **grant\_type=password**.

then I have tried to get a new **access\_token** using my **refresh\_token** received from first request :

```
POST /oauth/token

grant_type:refresh_token
refresh_token:def50200d3c74fa2e41a886a6ff1...
client_secret:l0r3m1psum

```

the first time I execute the request it returns the new **access\_token** :

```
{
    "token_type": "Bearer",
    "expires_in": 300,
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJS..",
    "refresh_token": "def502002b9ce17870bc285427a6.."
}

```

the problem is the second time I run the same request it Doesn't works and I get **401 unauthorized** with this HTML body :

```
{
    "error": "invalid_request",
    "error_description": "The refresh token is invalid.",
    "hint": "Token has been revoked",
    "message": "The refresh token is invalid."
}

```

for the second request I have used the same **refresh\_token**(I'am sure it not expired I have set ttl to 78000 for test) :

```
POST /oauth/token

grant_type:refresh_token
refresh_token:def50200d3c74fa2e41a886a6ff1...
client_secret:l0r3m1psum

```

**grant\_type:refresh\_token** request will enable us to get new access\_token as long as refresh token not expired?

but it is like getting expires after just executing a request one time , if I generate a new **refresh\_token** using the **grant\_type:password** in my case the **grant\_type:refresh\_token** request works fine .

but in \[1\]: https://www.drupal.org/project/simple\_oauth

# Answer
> 1 votes
This is working as expected. You receive a new access token and refresh token. You must use those in subsequent requests.

---
Tags: authentication-authorization
---


# Question
Title: Highlight searched keyword using exposed filter views?
In a view and I added an exposed filter: **Search: Search Terms filter**

It works fine, but I'd like to highlight the searched keyword in the results.

How can I do it?

# Answer
> 7 votes
You could use something like highlight that finds and adds a class to all instances of a word on the page, and instead of feeding it a static word, you would use the contents of the search box.

You can find more information and links to other versions in the StackOverflow question, Highlight a word with jQuery.

# Answer
> 11 votes
Using search\_excerpt function you can highlight the searched keyword. It show the keyword in bold.

> Returns snippets from a piece of text, with certain keywords highlighted. Used for formatting search results.

An example is shown below,

for eg: //a body field is exposed here

in your views tpl file add the following code

```
 <?php foreach ($rows as $id => $row):

      $keys = $_GET['body_value'];
      $row = search_excerpt($keys, $row);
 ?>

```

it will highlight the searched word, see the screenshot below

# Answer
> 1 votes
If you are using views with Apache Solr with http://drupal.org/project/search\_api and http://drupal.org/project/search\_api\_solr

You can enable highlighting on the word.

# Answer
> 0 votes
```
function custom_views_pre_render(&$view) {
  if ($view->name == 'article') {
    $result = $view->result;
    foreach ($result as $i => $row) {
      $body = $view->result[$i]->field_body[0]['rendered']['#markup'];
      $strong = $view->exposed_data['body_value'];
      $view->result[$i]->field_body[0]['rendered']['#markup'] = str_replace($strong, '<strong>' . $strong . '</strong>', $body);
    }
  }
}

```

Example for **body** field. Into your custom module.

# Answer
> 0 votes
You could use a javascript plugin like mark.js.

Here's an example with jQuery :

```
$('.view-content').mark($('input').val());

```

---
Tags: search, views
---


# Question
Title: tempstore.private not working in openIDConnect client
I am getting some licence info via retrieveUserInfo in the openIDConnect custom client login process. Ideally i want to write it to the session but opted for the "tempstore.private" since it seems to be the drupal way.

in retrieveUserInfo I am getting the

```
$tempstore = \Drupal::service('tempstore.private')->get('sessionlike');

```

.. going through the licences and detecting the right ones and then i set:

```
 $tempstore->set('has_plus', true);

```

however retrieving the value in the custom block with

```
 $tempstore->get('has_plus')

```

comes up empty.

Do I need to apply the tempstore changes somehow ? Could not find anything on that.

# Answer
> 1 votes
> Ideally i want to write it to the session but opted for the "tempstore.private" since it seems to be the drupal way.

I think you've read some old blog post advertising Tempstore as replacement for the superglobal $\_SESSION. They were wrong, it was replaced by the Symfony Session object.

To make the right choice between Tempstore and Session you need to consider the use case. See What is the difference between \Drupal::request()-\>getSession() and \Drupal::service('user.private\_tempstore')?

The best Drupal way performance-wise would be adding a `session` cache context to the block. Blocks with this cache context are automatically lazy-loaded through BigPipe.

```
public function build() {
  $session = \Drupal::request()->getSession();
  $build = [
   '#plain_text' => $session->get('value'),
  ];
  $build['#cache']['contexts'][] = 'session';
  return $build;
}

```

This also helps with your comment, session data does survive a user login.

---
Tags: users, 9
---


# Question
Title: Routing for controller with multilingual enabled
On my Drupal 8 multilingual project, I have made a custom controller which create a basic page, but I can't figure out which parameter I must use in the routing. Is this `_locale`?

This is the definition of the route.

```
custom.route:
path: '{_locale}/portail'
defaults:
  _content: '\Drupal\my_module\Controller\DisplayController::display'
requirements:
_access: 'TRUE

```

This is the code I am using in the controller class.

```
public function display(Request $request, $locale) {
    var_dump($locale);die;
}

```

# Answer
> 4 votes
The language is detected and the prefix removed before routing. So you have a static route without parameter and in the controller you can get the prefix from the language manager:

```
  $langcode = $this->languageManager()->getCurrentLanguage()->getId();

```

# Answer
> 1 votes
To those like @liquidcms wondering how to do a multilingual path : you can make as many routes as languages, to the same controller.

```
custom.route.lang_one:
  path: '/path/in/this/language'
  defaults:
    _controller: '\Drupal\my_module\Controller\MyController::method'
  requirements:
    _permission: 'access content'

custom.route.lang_two:
  path: '/path/in/that/other/language'
  defaults:
    _controller: '\Drupal\my_module\Controller\MyController::method'
  requirements:
    _permission: 'access content'

```

And then you check the language in the controller as already mentioned by @4k4.

# Answer
> 0 votes
One controller with a single route, different paths in different languages:

`my_module.routing.yml`

```
custom.route:
  path: '/path-english'
  defaults:
    _controller: '\Drupal\my_module\Controller\MyController::method'
  requirements:
    _permission: 'access content'

```

This works for english (default language), but say we want `/path-deutsch` in German. We need to manipulate the path using a PathProcessor.

Set up the service in `my_module.services.yml`:

```
services:
  my_module.path_processor:
    class: Drupal\my_module\Path\PathProcessor
    arguments: ['@language_manager']
    tags:
      - { name: path_processor_inbound, priority: 50 }
      - { name: path_processor_outbound, priority: 50 }

```

The class is in`my_module/src/Path/PathProcessor.php`:

```
<?php

namespace Drupal\my_module\Path;

use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
use Drupal\Core\Render\BubbleableMetadata;
use Symfony\Component\HttpFoundation\Request;

/**
 * Rewrites inbound and outbound paths.
 */
class PathProcessor implements InboundPathProcessorInterface, OutboundPathProcessorInterface {

  /**
   * @var \Drupal\Core\Language\LanguageManagerInterface
   */
  protected $languageManager;

  /**
   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
   */
  public function __construct(LanguageManagerInterface $language_manager) {
    $this->languageManager = $language_manager;
  }

  /**
   * {@inheritdoc}
   */
  public function processInbound($path, Request $request) {
    if ($this->languageManager->getCurrentLanguage()->getId() === 'de' && $path === '/path-deutsch') {
      return '/path-english';
    }

    return $path;
  }

  /**
   * {@inheritdoc}
   */
  public function processOutbound($path, &$options = [], Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) {
    if ($path === '/path-english') {
      if ($options['language']->getId() === 'de') {
        return '/path-deutsch';
      }
    }

    return $path;
  }

}

```

`processInbound` rewrites the German path in the request to the route used by the controller.

`processOutbound` ensures that any generated links in a German context use the correct German path. (This doesn't work if you use two separate controller routes.)

```
// Will return '/path-deutsch'.
$url = Url::fromRoute('custom.route', ['language' => 'de'])->toString();

```

This means that the **language switcher block** etc. will automatically use the correct path.

---
Tags: 8, routes, i18n-l10n, page-controller
---


# Question
Title: On android, keyboard comes up momentarily and then disappears when a user clicks the search field (likely due to triggering a js resize event)
Related to this: https://stackoverflow.com/questions/51493956/android-keyboard-appears-and-disappears-when-clicking-search-input

We have some javascript that rearranges a couple elements in the page pretty dramatically depending on if we are on mobile or not (beyond what flex css could handle).

Unfortunately, on android (but not iphone), when our users click on the search field and the mobile keyboard comes up, it triggers a document resize which makes the search field lose focus and makes the keyboard disappear.

Reading the stack overflow link above, I see that one strategy is maybe to, when someone clicks on the search field suspend the resize event listener for half a second, but I'm not sure quite how to do that. The ID for the search field (an \<input) is #edit-search-api-fulltext.

Here's my JS so far: (I tried pasting a code snippet on here but it remove all carriage returns making it impossible to read) I'll try again as a comment.

# Answer
> 0 votes
I was able to solve this by momentarily pausing the resize event listener when the search box is clicked on (and the android keyboard is coming up).

```
var inputBox = document.getElementById("edit-search-api-fulltext");  // monitor search field at top of page for focus for android bugs
if (inputBox) {
  inputBox.addEventListener('focus', function() {
    searchBoxClicked();
  });
}

function searchBoxClicked() { // search bar focused
  window.removeEventListener('resize', WindowResizeCheck);

  setTimeout(() => {
    window.addEventListener('resize', WindowResizeCheck);
  }, 500);  // disable resize listener for half a second if user clicks on search box
}

var inputBox2 = document.getElementById("edit-search-api-fulltext--3");  // monitor search field at top of page for focus for android bugs
if (inputBox2) {
  inputBox2.addEventListener('focus', function() {
    searchBoxClicked2();
  });
}

function searchBoxClicked2() { // search bar focused
  window.removeEventListener('resize', WindowResizeCheck);
  setTimeout(() => {
    window.addEventListener('resize', WindowResizeCheck);
  }, 500);  // disable resize listener for half a second if user clicks on search box
}

```

---
Tags: theming
---


# Question
Title: Migrating URLs without titles into a link field (where the link title is optional) without them becoming NULL
I'm migrating URLs that don't have corresponding titles into a link field, and when I either don't define the title, or use the default\_value plugin to set the default value to '', the value actually gets set to NULL, instead of an empty string, which breaks my link formatter, that expects link titles to be strings (even if the strings are empty):

```
    process:
      field_link/title:
        - plugin: default_value
          default_value: ''

```

How can I insert the titles as an empty string, instead of NULL?

# Answer
> 4 votes
I found the solution was to create an empty string as a constant, in the migration source, and then reference that:

```
source:
  constants:
    empty: ''

process:
  field_link/title: constants/empty

```

---
Tags: migrations
---


# Question
Title: Set the canonical link rel via theme's template.php for a specific views page (not a node)
I do not want to use the D7 Metatag or the MetaTag Quick modules. How can I specify a single canonical link rel for a single URL which is not a node (a view page) via the theme's template.php?

I've tried this but it has a syntax error:

```
if (isset($_GET['q']) && preg_match("/^products\category/", $_GET['q']) {
  drupal_add_html_head(array(
    "#tag" => "link",
    "#attributes" => array(
      "rel" => "canonical",
      "href" => "https://domain.com/products/category",
    ),
  ), 'link_rel_canonical');
}

```

# Answer
As you said, you have a syntax error. You should be able to use this:

```
if (preg_match("/^products\/category/", $_GET['q'])) {
  drupal_add_html_head(array(
    "#tag" => "link",
    "#attributes" => array(
      "rel" => "canonical",
      "href" => "https://domain.com/products/category",
    ),
  )), 'link_rel_canonical');
}

```
> 0 votes

---
Tags: views, meta-tags
---


# Question
Title: Recommended way to use StringTranslationTrait in PHP8 enums
PHP8 has that new cool "enum" feature that should replace all class constants and would make coding easier and safer. One problem I encoutered is that many common Drupal traits (foremost the translation trait) have properties and therefore can not be used in enums.

So what I would like to do is

```
<?php

use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslatableMarkup;

enum Season: string {

  // Can't use this because that trait has a property
  // and enums can only use traits without properties.
  use StringTranslationTrait;

  case SUMMER = 'summer';
  case WINTER = 'winter';

  public function getLabel(): TranslatableMarkup {
    return match ($this) {
      Season::SUMMER => $this->t('summer'), // can't use OOP here :(
      Season::WINTER => $this->t('winter'),
    };
  }
}

```

I know that I can still use old-school procedural code

```
    return match ($this) {
      Season::SUMMER => t('summer'), // that's what we did 20 years ago
      Season::WINTER => t('winter'),
    };

```

but is there a "more beautiful" way? I'm on a D10/PHP8 project , procedural code is dicouraged, but traits with properties are very common in the Drupal ecosystem. Especially getting a translatable label of a constant is a very common use-case.

What is the recommended way of using/injecting Drupal traits (especially the StringTranslationTrait) into enums?

# Answer
> 1 votes
Following up on apaderno's comment, rather than using the Drupal API to create a `TranslatableMarkup` instance, you can instantiate the class yourself.

```
use Drupal\Core\StringTranslation\TranslatableMarkup;

enum Season: string {

  case SUMMER = 'summer';
  case WINTER = 'winter';

  public function getLabel(): TranslatableMarkup {
    return match ($this) {
      Season::SUMMER => new TranslatableMarkup('summer'),
      Season::WINTER => new TranslatableMarkup('winter'),
    };
  }
}

```

---
Tags: 10
---


title: phpunit can't find node permissions

text:
# Question
Title: phpunit can't find node permissions
We have a `Functional` test class derived from `BrowserTestBase` which is passing, but the output includes a deprecation notice.

> Adding non-existent permissions to a role is deprecated in drupal:9.3.0 and triggers a runtime exception before drupal:10.0.0. The incorrect permissions are "edit any page content". Permissions should be defined in a permissions.yml file or a permission callback. See https://www.drupal.org/node/3193348

The class's `$modules` array includes the `node` module, as well as a custom module whose implementation of `hook_install()` assigns permissions to roles. One of those permissions is the "edit any page content" permission. When the module is installed on the site the permissions are assigned correctly. My understanding of the Node module is that it creates and enables the Article and Page content types by default, exposing the permissions appropriate to those content types, and that is indeed what happens on the site. But `phpunit` can't find those permissions, even though it knows that loading of the Node module is required. Including a call to `$this->drupalCreateContentType()` in `setUp()` doesn't eliminate the notice. Presumably if I don't figure this out the tests will start failing when we try to move to Drupal 10. We could work around the problem by backing out the code to assign the permission in the hook, and just assign it manually, but that's kind of ugly. I could also hack the hook implementation to detect when it's running under `phpunit` (assuming that's possible), and skip that assignment in that case, but that's also hackier than I would like.

# Answer
> 0 votes
I believe I have figured out what we need to do, and it's a sort of a variation on the second of the two workarounds I identified in the original question above. As far as I can tell, my assumption that installing the `node` module automatically created and enabled the `article` and `page` content types was not valid. I now think that this magic is handled by the `drush site:install` command, probably in connection with the default profile it uses. Since `phpunit` can't take advantage of that magic, the install hook implementation really does have to figure out whether it's running as part of a test (by finding out if the content types have already been created) and if so, it must itself create the type whose permission it wants to install. After adding this to the top of that hook implementation:

```
  $types = \Drupal::entityTypeManager()
    ->getStorage('node_type')
    ->loadMultiple();
  if (!array_key_exists('page', $types)) {
    $type = NodeType::create(['type' => 'page', 'name' => 'Basic page']);
    $type->save();
  }

```

... the deprecation warning is gone. The permissions are brought into existence automatically when the content type is created, but the creation of the content type does not happen just by virtue of enabling the `node` module.

---
Tags: 9, testing
---


# Question
Title: How to get web server error logging output from phpunit functional tests?
The 500 errors generated during `phpunit` tests aren't showing up in the Apache error logs. Where are those errors logged?

# Answer
> 0 votes
I found out what's going on. Instead of letting Apache log the 500 error, `phpunit` is returning the error message as HTML markup to the call to `$this->drupalGet($path)`.

---
Tags: testing
---


title: Patch not working

text:
# Question
Title: Patch not working
I am trying to apply patch to the core file but none solution work for me. even not able commit core file changes due composer restriction

composer.json

```
{
"name": "pantheon-upstreams/drupal-project",
"description": "Install Drupal 9 with Composer on Pantheon.",
"type": "project",
"license": "MIT",
"repositories": [
    {
        "type": "composer",
        "url": "https://packages.drupal.org/8"
    },
    {
        "type": "composer",
        "url": "https://asset-packagist.org"
    },
    {
        "type": "path",
        "url": "upstream-configuration"
    },
    {
        "type": "package",
        "package": {
            "name": "woocommerce/flexslider",
            "version": "2.7.2",
            "type": "drupal-library",
            "source": {
                "url": "https://github.com/woocommerce/FlexSlider.git",
                "type": "git",
                "reference": "2.7.2"
            }
        }
    },
    {
  "type": "package",
  "package": {
    "name": "jsor/jcarousel",
    "version": "0.3.8",
    "type": "drupal-library",
    "dist": {
      "url": "https://github.com/jsor/jcarousel/archive/0.3.8.zip",
      "type": "zip"
    }
  }
},
{
  "type": "package",
  "package": {
    "name": "snake-345/jcarouselswipe",
    "version": "0.3.7",
    "type": "drupal-library",
    "dist": {
      "url": "https://github.com/snake-345/jcarouselswipe/archive/0.3.7.zip",
      "type": "zip"
    }
  }
}
],
"require": {
    "bower-asset/bootstrap": "^3.3",
    "bower-asset/jquery-simple-color": "^1.2",
    "bower-asset/spectrum": "^1.8",
    "composer/installers": "^1.9",
    "cweagans/composer-patches": "^1.7",
    "drupal/address": "^1.11",
    "drupal/admin_toolbar": "^3.1",
    "drupal/aos-aos": "^1.0",
    "drupal/better_exposed_filters": "^5.2",
    "drupal/better_search": "^1.6",
    "drupal/better_social_sharing_buttons": "^4.0",
    "drupal/bootstrap_barrio": "^5.5",
    "drupal/captcha": "^1.4",
    "drupal/color_field": "^3.0",
    "drupal/core-composer-scaffold": "^9.2",
    "drupal/core-recommended": "^9.2",
    "drupal/csv_serialization": "^2.1",
    "drupal/ctools": "^3.4",
    "drupal/devel_entity_updates": "^3.0",
    "drupal/diff": "^1.0",
    "drupal/disable_link_rel": "^2.0",
    "drupal/embed": "^1.6",
    "drupal/entity": "^1.4",
    "drupal/entity_browser": "^2.8",
    "drupal/entity_embed": "^1.2",
    "drupal/entity_reference_revisions": "^1.10",
    "drupal/extlink": "^1.6",
    "drupal/field_group": "^3.2",
    "drupal/field_permissions": "^1.2",
    "drupal/file_mdm": "^2.4",
    "drupal/flexslider": "^2.0",
    "drupal/fontawesome": "^2.23",
    "drupal/fontawesome_menu_icons": "^1.9",
    "drupal/googlemap_block": "2.x-dev@dev",
    "drupal/gtranslate": "^1.14",
    "drupal/hacked": "^2.0@beta",
    "drupal/header_and_footer_scripts": "^3.0",
    "drupal/honeypot": "^2.1",
    "drupal/hubspot": "^1.0@alpha",
    "drupal/inline_entity_form": "^1.0@RC",
    "drupal/jcarousel": "^6.0@alpha",
    "drupal/jquery_carousel": "^1.0",
    "drupal/lazy": "^3.11",
    "drupal/link_attributes": "^1.11",
    "drupal/menu_block": "^1.8",
    "drupal/menu_link_attributes": "^1.2",
    "drupal/metatag": "^1.21",
    "drupal/nofollowlist": "^2.0",
    "drupal/page_manager": "^4.0@beta",
    "drupal/paragraphs": "^1.15",
    "drupal/pathauto": "^1.11",
    "drupal/profile": "^1.4",
    "drupal/quick_node_clone": "^1.15",
    "drupal/recaptcha": "^3.0",
    "drupal/redirect": "^1.8",
    "drupal/redis": "^1.5",
    "drupal/responsive_menu": "^4.4",
    "drupal/schema_metatag": "^2.3",
    "drupal/search_api": "^1.28",
    "drupal/search_exclude_nid": "^2.0@alpha",
    "drupal/sophron": "^1.2",
    "drupal/state_machine": "^1.6",
    "drupal/token": "^1.7",
    "drupal/twig_tweak": "^3.1",
    "drupal/typed_data": "^1.0@beta",
    "drupal/views_autocomplete_filters": "^1.3",
    "drupal/views_autosubmit": "^1.4",
    "drupal/views_blogspot_archive": "^1.4",
    "drupal/views_conditional": "^1.5",
    "drupal/views_data_export": "^1.1",
    "drupal/views_infinite_scroll": "^2.0",
    "drupal/views_slideshow": "^4.8",
    "drupal/we_megamenu": "^1.13",
    "drupal/webform": "^6.1",
    "drupal/webform_spam_words": "^2.0",
    "drupal/youtube": "^2.0",
    "drush/drush": "^10",
    "jsor/jcarousel": "^0.3.8",
    "npm-asset/jquery": "^2.2",
    "oomphinc/composer-installers-extender": "^2.0",
    "pantheon-systems/drupal-integrations": "^9",
    "pantheon-upstreams/upstream-configuration": "self.version",
    "snake-345/jcarouselswipe": "^0.3.7",
    "wikimedia/composer-merge-plugin": "^2.0",
    "woocommerce/flexslider": "~2.0"
},
"require-dev": {
    "drupal/core-dev": "^9.2"
},
"conflict": {
    "drupal/drupal": "*"
},
"minimum-stability": "stable",
"prefer-stable": true,
"extra": {
    "drupal-scaffold": {
        "locations": {
            "web-root": "./web"
        },
        "allowed-packages": [
            "pantheon-systems/drupal-integrations"
        ],
        "file-mapping": {
            "[project-root]/.editorconfig": false,
            "[project-root]/pantheon.upstream.yml": false,
            "[project-root]/.gitattributes": false
        }
    },
    "installer-types": [
       "npm-asset",
       "bower-asset"
    ],
    "installer-paths": {
        "web/core": [
            "type:drupal-core"
        ],
        "web/libraries/{$name}": [
            "type:bower-asset",
            "type:npm-asset",
            "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"
        ],
        "web/private/scripts/quicksilver/{$name}/": [
            "type:quicksilver-script"
        ]
    },
    "composer-exit-on-patch-failure": true,
    "patchLevel": {
        "drupal/core": "-p2"
    },
    "merge-plugin": {
        "include": [
            "web/modules/contrib/webform/composer.libraries.json"
        ]
    }
},
"enable-patching": true,
"composer-exit-on-patch-failure": true,
"patches": {
     "drupal/core": {
        "Public files directory Not fully protected": "https://www.drupal.org/files/issues/2020-03-04/3117665-non_apache_servers_4.patch",
        "Illegal offset type in isset or empty core": "https://www.drupal.org/files/issues/2023-01-25/taxonomies-view-filter-core-fixes.patch",
        "view filter working core": "https://www.drupal.org/files/issues/2023-01-25/date-view-filter-core_0.patch"
      }
     },
"config": {
    "preferred-install": "dist",
    "sort-packages": true,
    "platform": {
        "php": "7.4"
    },
    "allow-plugins": {
        "composer/installers": true,
        "cweagans/composer-patches": true,
        "drupal/core-composer-scaffold": true,
        "dealerdirect/phpcodesniffer-composer-installer": true,
        "wikimedia/composer-merge-plugin": true,
        "oomphinc/composer-installers-extender": true
    }
}
}

```

.gitignore

```
# This file contains .gitignore rules that are often used with Drupal projects.
# Because .gitignore is specific to your site and its deployment processes, you
# may need to uncomment, add, or remove rules.

# Pantheon commits a settings.php for environment-specific settings.
# Place local settings in settings.local.php
/web/sites/*/settings.local.php
/web/sites/*/services*.yml
!/web/sites/*/services.pantheon.*.yml
!/web/sites/*/services.yml

# Ignore paths that contain user-generated content.
/web/sites/*/files
/web/sites/*/private

# Ignore SimpleTest multi-site environment.
/web/sites/simpletest

# Ignore custom modules managed by Composer.
#
# When a development team creates one or more custom modules that
# are intended for use on more than one site, the typical strategy
# is to register them in Packagist and give them the type
# `drupal-custom-module` instead of `drupal-module`. This will cause
# Composer to install them to the directory `modules/custom`.
# This will cause a build error on Pantheon unless this location
# is .gitignore'd as shown below.
#
# An alternate strategy for custom modules is to commit them
# directly to the repository of the site where they are used. This
# is commonly done with modules that are specific to just one site.
#
# Sites that have both Composer-managed custom modules and custom
# modules that are committed directly to the repository may ignore
# the `modules/custom` directory with the first rule shown below,
# and then allow the locations to be committed on a case-by-case
# basis by re-adding paths using `!` rules. A path that begins with
# a `!` will allow a previously-ignored path to be added to the
# repository. Note, however, that a path cannot be re-added if any
# of its parent directories are excluded. This is why we use the
# rule `/modules/custom/*` instead of `/modules/custom`. Alternately,
 # you might instead commit Git-tracked custom modules to some other
# path, such as /web/modules/<site-namespace>.
#
 # Sites that do not have any Composer-managed custom modules do
# not need to use any of the `modules/custom` rules below.
# ------------------------------------------------------------------
#/web/modules/custom/*
#!/web/modules/custom/module_in_repo

# Ignore directories generated by Composer
#
# See the "installer-paths" section in the top-level composer.json
# file.
# ------------------------------------------------------------------
/drush/Commands/contrib/
!/web/core/modules/web/views/src/Plugin/views/filter/Date.php
!/web/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php
/web/core/
/web/modules/contrib/
/web/themes/contrib/
/web/profiles/contrib/
 /web/libraries/
/web/private/scripts/quicksilver
/web/themes/contrib/bootstrap_barrio/css/components/forum.css
/web/themes/contrib/bootstrap_barrio/css/components/image-widget.css
!/web/libraries/flexslider
!/web/core/modules/web/views/src/Plugin/views/filter/Date.php
!/web/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php

# Generally you should only ignore the root vendor directory. It's important
# that core/assets/vendor and any other vendor directories within contrib or
# custom module, theme, etc., are not ignored unless you purposely do so.
/vendor/

# Ignore other scaffold files
#
# Ignore all files placed directly at the project root. Rely on
# .gitignore files placed by drupal/core-composer-scaffold to avoid
# build errors that arise if non-ignored files are scaffolded.
# ------------------------------------------------------------------
/.editorconfig
/.gitattributes

# The pantheon-systems/drupal-integrations project places a
# .drush-lock-update file at the project root.
/.drush-lock-update

# The drupal/core-composer-scaffold component will write .gitignore
# files to other locations where it scaffolds files that are not
# already ignored. We must also ignore these generated files.
/web/**/.gitignore

# Other common rules
# ------------------
# Ignore files generated by PhpStorm
#/.idea/

# Ignore .env files as they are personal
#/.env

```

# Answer
> 3 votes
`patches` needs to be placed inside the `extra` section (on the same level as `patchLevel`, `installer-types`, etc.), not on the root level.

```
{
    "name": "pantheon-upstreams/drupal-project",
    ...(trimmed)...
    "extra": {
         ...(trimmed)...
        "patchLevel": {
            "drupal/core": "-p2"
        },
        "patches": {
            "drupal/core": {
                "Public files directory Not fully protected": "https://www.drupal.org/files/issues/2020-03-04/3117665-non_apache_servers_4.patch",
                "Illegal offset type in isset or empty core": "https://www.drupal.org/files/issues/2023-01-25/taxonomies-view-filter-core-fixes.patch",
                "view filter working core": "https://www.drupal.org/files/issues/2023-01-25/date-view-filter-core_0.patch"
            }
        }
    }
}

```

---
Tags: 9
---


# Question
Title: Browser test reports assertion success as an error
I am running a Functional test in a class derived from `BrowserTestBase`. Line 105 of the test method has this assertion:

```
    $this->assertSession()->linkExists('10000003');

```

The test output says:

```
Behat\Mink\Exception\ExpectationException: Link with label 10000003 found.

```

identifying line 105 as the line where the exception occurred. If it found the link, why does it consider that an error?

# Answer
Aha! It appears that the `WebAssert` class is inconsistent in what it reports for testing errors. Sometimes it describes what *shouldn't* have happened: the previous error I got was described as

> Input "relationship-type" cannot take "2" as a value, ...

For this latest test, however, it did the opposite, describing not what went wrong, but what the test *wanted* to happen. Despite the output which seemed to say the link was found, when I captured the output I discovered that the link was, in fact, not there. Others may disagree, but I think a consistent approach would be a good thing here.

\[Edit: this is fixed in Drupal 10, which correctly reports what caused the failure.\]
> 0 votes

---
Tags: testing
---


# Question
Title: Is it possible to have a Search API Facet to expand the results? Basically, querying this facet OR this facet?
We have a Search API View (Solr) using facets and our nodes have two separate fields for the facets. One is an address field and the facet is built on the address US States and the other field is a text select list with values Virtual, Physical, National.

We need to build the facets in a way to allow the user to select a US State from facet 1 which narrows the results to node from that state.

Then, we need the second facet to **also include** nodes tagged as National even if they are not in the US state selected in the first facet.

It seems that the facets always narrow the results and I cannot find a way to make the facets work together as "this facet OR this other facet".

# Answer
## Search API "Or" Facet

I haven't tried this myself but there seems to be a Backend Search API feature **search\_api\_facets\_operator\_or** that fits with your description, although I don't know if it's available in all Drupal versions:

Search API Server backends and features

## Regular Views Filters Or

Under "Filter criteria" in your views there is a dropdown arrow next to the "Add" button.

When you click it you will be able to see the **And/Or Rearrange**. This will open up the interface to group your filters into different groups (if necessary) and to change the `Operator` from **And** to **Or**.
> 0 votes

---
Tags: search
---


# Question
Title: When a custom form is displayed in a custom template, ajax does not work
**What I want to do**

I want to display a custom form with a custom template and use ajax in it to change values in the form.

**Problem**

Custom template notation prevents ajax from working. I didn't change anything in php, just twig, and tried 2 patterns as shown below. In twig1, when I changed the value of "a", the value of "b" became "hogehoge" and the log was output. However, in twig2, the value of b was not changed even if the value of a was changed, and there was no "update" in the log. !!Both of the name of templates are "sampleexample.html.twig", and both of them are rendered.

SampleForm2.php

```
    <?php

namespace Drupal\sampleexample\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\Session\AccountProxyInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Ajax\AjaxResponse;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Core\Ajax\ReplaceCommand;

class SampleForm2 extends FormBase {

  /**
   * Current user account.
   *
   * @var \Drupal\Core\Session\AccountProxyInterface
   */
  protected $currentUser;

  /**
   * Node storage.
   *
   * @var \Drupal\node\NodeStorageInterface
   */
  protected $nodeManager;
  protected $taxonomanager;

  /**
   * {@inheritdoc}
   */
  public function __construct(
    EntityTypeManager $entity_type_manager,
    AccountProxyInterface $current_user
  ) {
    $this->currentUser = $current_user;
    $this->nodeManager = $entity_type_manager->getStorage('node');
  }

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

  /**
   * {@inheritdoc}
   */
  public function getFormId(){
    return 'sampleform2';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $form['#theme'] = 'sampleexample';
    $form['a'] = [
      '#type' => 'textfield',
      '#size' => '60',
      '#ajax' => [
        'callback' => '::myAjaxCallback', // don't forget :: when calling a class method.
        'disable-refocus' => FALSE, // Or TRUE to prevent re-focusing on the triggering element.
        'event' => 'change',
        'progress' => [
          'type' => 'throbber',
          'message' => $this->t('Verifying entry...'),
        ],
      ]
    ];
    $form['b'] = [
      '#type' => 'textfield',
      '#size' => '60',
    ];
    $form['actions']['#type'] = 'actions';
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Save data'),
    ];

    return $form;
  }

  function myAjaxCallback(array &$form, FormStateInterface $form_state, Request $request) {
    /** @var \Drupal\Core\Ajax\AjaxResponse $response */
    $response = new AjaxResponse();

    \Drupal::logger('b')->notice('update');
    $form['b']['#value'] = $this->t('hogehoge');
    $response
        ->addCommand(new ReplaceCommand(".form-item--b", $form['b']));
    return $response;

  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {

  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {

  }
}

```

twig1

```
{{ form_build_id }}
{{ form_token }}
{{ form_id }}

{{ form }}

```

twig2

```
{{ form_build_id }}
{{ form_token }}
{{ form_id }}

{{ form.a }}
{{ form.b }}
{{ form.actions.submit }}

```

**Points of concern** I checked if there is any difference in the dom displayed on the page when using twig1 and twig2 respectively. I found that

```
<input autocomplete="off" data-drupal-selector="form-if-9dbyv7ohy9odmuog4qlckk6aemou8w5bnljfhobi"
             type="hidden" name="form_build_id" value="form-If-9Dbyv7ohY9oDmUOG4qLCkk6AemOu8w5bnlJFHObI">
<input data-drupal-selector="edit-sampleform2-form-token" type="hidden" name="form_token"
            value="gjDpTprqywwq9hMaizsiaL6YU300vcJJ36FRW_o2sFM">
<input data-drupal-selector="edit-sampleform2" type="hidden" name="form_id" value="sampleform2">

```

And the callback is written differently.

twig1

```
"ajax":{"edit-a":{"callback":"::myAjaxCallback","disable-refocus":false,"event":"change","progress":{"type":"throbber","message":"Verifying entry..."},

```

twig2

```
"ajax":{"edit-a":{"callback":"Drupal\\sampleexample\\Form\\SampleForm2::myAjaxCallback","disable-refocus":false,"event":"change","progress":{"type":"throbber","message":"Verifying entry..."},

```

# Answer
The only variable available in a #theme form template is

```
{{ form }}

```

and this is the only thing you need to render. If you render single form elements or groups then include the rest of the `form` variable with the help of the `|without` filter.

A more complex example from the core Claro theme:

*core/themes/claro/templates/node-edit-form.html.twig*

```
{#
/**
 * @file
 * Theme override for a node edit form.
 *
 * Two column template for the node add/edit form.
 *
 * This template will be used when a node edit form specifies 'node_edit_form'
 * as its #theme callback.  Otherwise, by default, node add/edit forms will be
 * themed by form.html.twig.
 *
 * Available variables:
 * - form: The node add/edit form.
 *
 * @see claro_form_node_form_alter()
 */
#}
<div class="layout-node-form clearfix">
  <div class="layout-region layout-region--node-main">
    <div class="layout-region__content">
      {{ form|without('advanced', 'footer', 'actions') }}
    </div>
  </div>
  <div class="layout-region layout-region--node-secondary">
    <div class="layout-region__content">
      {{ form.advanced }}
    </div>
  </div>
  <div class="layout-region layout-region--node-footer">
    <div class="layout-region__content">
      <div class="divider"></div>
      {{ form.footer }}
      {{ form.actions }}
    </div>
  </div>
</div>

```

If you don't render the entire form variable then libraries like Ajax don't work.
> 1 votes

---
Tags: forms, ajax, 9
---


# Question
Title: Why is the service “@url_helper” not found?
I am trying to add the query string with the each URL access through the browser. To accomplish it, trying to implement that through the service. But getting no solution in Drupal 9.

mymodule.services.yml:

```
    services:
      weplant.userlibraryurl:
      class: Drupal\weplant\EventSubscriber\UserLibrarySubscriber
      arguments: ['@url_helper', '@request_stack']
      tags:
        - { name: event_subscriber }

```

UserLibrarySubscriber.php

```
<?php
namespace Drupal\weplant\EventSubscriber;

use Drupal\Core\Url;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Routing\UrlGeneratorInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class UserLibrarySubscriber implements EventSubscriberInterface {

  /**
   * The URL Helper service.
   *
   * @var \Drupal\Core\UrlHelper
   */
  protected $urlHelper;

  /**
   * The request stack.
   *
   * @var \Symfony\Component\HttpFoundation\RequestStack
   */
  protected $requestStack;

  /**
   * Constructs a new instance.
   *
   * @param \Drupal\Core\UrlHelper $url_helper
   *   The URL helper service.
   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
   *   The request stack.
   */
  public function __construct(UrlHelper $url_helper, RequestStack $request_stack) {
    $this->urlHelper = $url_helper;
    $this->requestStack = $request_stack;
  }

  /**
   * Modifies the URLs before they are processed.
   */
  public function modifyUrls(GetResponseEvent $event) {
    $request = $event->getRequest();
    $current_url = Url::fromUri($request->getUri());
    $query_params = $current_url->getOption('query') ?? [];
    $query_params['my_param'] = 'my_value';
    $current_url->setOption('query', $query_params);
    $modified_url = $this->urlHelper->toString($current_url);
    $request->server->set('REQUEST_URI', $modified_url);
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents() {
    $events[KernelEvents::REQUEST][] = ['modifyUrls'];
    return $events;
  }
}

?>

```

Error:

```
The website encountered an unexpected error. Please try again later.
Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException: The service "weplant.userlibraryurl" has a dependency on a non-existent service "url_helper". in Symfony\Component\DependencyInjection\Compiler\CheckExceptionOnInvalidReferenceBehaviorPass->processValue() (line 86 of /Users/test/Sites/auth8/vendor/symfony/dependency-injection/Compiler/CheckExceptionOnInvalidReferenceBehaviorPass.php).
Symfony\Component\DependencyInjection\Compiler\CheckExceptionOnInvalidReferenceBehaviorPass->processValue(Object, ) (Line: 83)
Symfony\Component\DependencyInjection\Compiler\AbstractRecursivePass->processValue(Array, ) (Line: 49)
Symfony\Component\DependencyInjection\Compiler\CheckExceptionOnInvalidReferenceBehaviorPass->processValue(Array) (Line: 92)
Symfony\Component\DependencyInjection\Compiler\AbstractRecursivePass->processValue(Object, 1) (Line: 49)
Symfony\Component\DependencyInjection\Compiler\CheckExceptionOnInvalidReferenceBehaviorPass->processValue(Object, 1) (Line: 83)
Symfony\Component\DependencyInjection\Compiler\AbstractRecursivePass->processValue(Array, 1) (Line: 49)
Symfony\Component\DependencyInjection\Compiler\CheckExceptionOnInvalidReferenceBehaviorPass->processValue(Array, 1) (Line: 47)
Symfony\Component\DependencyInjection\Compiler\AbstractRecursivePass->process(Object) (Line: 40)
Symfony\Component\DependencyInjection\Compiler\CheckExceptionOnInvalidReferenceBehaviorPass->process(Object) (Line: 94)
Symfony\Component\DependencyInjection\Compiler\Compiler->compile(Object) (Line: 762)
Symfony\Component\DependencyInjection\ContainerBuilder->compile() (Line: 1339)
Drupal\Core\DrupalKernel->compileContainer() (Line: 943)
Drupal\Core\DrupalKernel->initializeContainer() (Line: 482)
Drupal\Core\DrupalKernel->boot() (Line: 711)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)

```

# Answer
> 3 votes
The *url\_helper* service does not exist; that is why you get that error.
As the other answer said, there is the `UrlHelper` class, which is a class that implements static methods, and cannot be used to implement a service.

You do not need to use any method of that class, though. Since you already have a `Url` instance, you just need to call its `toString()` method.

```
public function modifyUrls(GetResponseEvent $event) {
  $request = $event->getRequest();
  $current_url = Url::fromUri($request->getUri());
  $query_params = $current_url->getOption('query') ?? [];
  $query_params['my_param'] = 'my_value';
  $current_url->setOption('query', $query_params);
  $request->server->set('REQUEST_URI', $current_url->toString());
}

```

(I will not comment on whether that is the correct code to use in that case.)

As a side note, you can verify which services are implemented by Drupal core by reading the content of some .services.yml files (whose content is also shown on api.drupal.org): The services that are part of Drupal core, but that are not implemented by a Drupal core module, are listed in core.services.yml; the services implemented by a Drupal core module are listed in a `<module machine name>.services.yml` file, for example user.services.yml (for the User module).

# Answer
> 1 votes
`UrlHelper` is not a service. It is a utility class with public static methods. Remove it from the arguments key in the custom service definition and remove it from the class constructor.

To use it, call static methods like `UrlHelper::someExistingMethod`. But `toString`, which you are trying to call on `UrlHelper`, does not exist.

---
Tags: 9, symfony-services
---


title: install Drush on shared server

text:
# Question
Title: install Drush on shared server
please i need to use drush on shared server

i allready use composer foe install drush,,

but if i use sudo command

sudo mv drush.phar /usr/local/bin/drush

, it is give me this massage

> -bash: sudo: command not found

i allready contact the server customer serves

and the answer was

we cannot support and sudo commnds

any IDEAS please for using drush on shaired server

# Answer
> 4 votes
Execute Drush out of the vendor directory as `vendor/bin/drush`. That is all you need to do. Shared hosting will never allow you to place executables in `/usr/local`.

# Answer
> -2 votes
i found the answer for my issue

we should use the command from the website directory

and use

> vendor/bin/drush

not only

> drush

after \> vendor/bin/drush we can put the drush command and injoy by using it

thanks every one for help

---
Tags: drush
---


# Question
Title: Creating a Custom Content Listing Page based on specific content type or taxonomy terms selected in the node
I am trying to create a content type that generates a list of content/nodes based on other content type or taxonomy terms selected within that node in the content type. I have a general content type for Articles and those are tagged with a taxonomy vocabulary for "Category".

This is within Drupal 9.

**What I've done so far:**

Create a new content type named "Custom List"

Add a field for an entity reference for Taxonomy Terms. Set the field to unlimited and selected the taxonomy vocabulary for Category. Then set the field to display as radio/checkboxes.

Created a View for Custom Lists as a Block. Set the block to display only on Custom List content types.

Set the View to list 10 items, with a contextual filter for Taxonomy Term ID, set to display all results.

**What I want to happen:**

When I create a new node for this Custom List content type, I will see the Categories taxonomy field I added as a list of checkboxes. I can check any number of taxonomy terms from this Categories vocabulary and the page will then display a content list of content that are only tagged with the terms I selected within this node. For example if I selected "News" and "Blog" as my terms, the list will only show content tagged with Category \> News and Category \> Blog.

Eventually I will be able to select Content Types as a filter on top of the taxonomy terms, but for now Im just trying to get the taxonomy filter working first.

**What is actually happening:**

The list generated still displays every content regardless of the term I selected when I created or edit the node. I have tried many things such as adding relationships from the Taxonomy Entity Reference field (Category) as well as the taxonomy term itself but to no avail.

Can someone help me with this issue or suggest a better or easier solution? Most likely my View isnt setup correctly as Im very inexperienced with contextual filters. I'm not even sure if I can achieve what Im trying to do with Contextual Filters. If there are any modules out there that can do what Im trying to do please advise or suggest them as well. Thank you all for your time.

# Answer
For the Content Type side, you've already done what needs to be done. In summary, a Content Type with an Entity Reference field pointing to Taxonomy Terms.

The Views side is where it gets tricky, but here's what you need to do:

1. You need a View of Nodes filtered by your "Custom List" content type.
2. You need to add a relationship from your Custom List nodes to the Category terms. To do this, add the relationship named:

```
Taxonomy term referenced from field_category

```

In the relationship window, update the **Administrative Title** to "Categories" so that it's less confusing. It will help in the next step.
3. Here's the tricky part: You need to add a relationship to all the Nodes that are using your Category taxonomy. To do this, add a relationship called:

```
Content using field_category (Relate each Content with a field_category set to the taxonomy term.)

```

In the relationship window, update the **Relationship** field to "Categories" (the name we set in Step 2). Also update the **Administrative Title** of this relationship to "Viewed Node". It will help in the next step.
4. To round things off, you need a contextual filter to filter "Viewed Node" to the current node's ID. To do this:

* Add a contextual filter. Filter by "Content", select the field named "ID".
    * Under **Relationship**, choose "Viewed Node" (the name we set in Step 3).
    * Under **When the filter value is NOT available**, select "Provide default value" and set the **Type** to "Content ID from URL".
5. Place the view block on your content type's page.

In summary, what the above did was link nodes to the terms, then terms back to nodes. Then, we just filtered the linked nodes to the current node. In SQL, it's something like the following pseudocode:

```
SELECT *
FROM nodes AS custom_list
INNER JOIN terms AS categories ON categories.id = custom_list.categories
INNER JOIN nodes AS viewed_nodes ON categories.id = viewed_nodes.categories
WHERE viewed_nodes.id = current_node.id

```

Or visually:

```
Nodes that appear on the view block
                v
            List nodes <- Category terms <- Nodes using the terms
                                                    ^
                                   Filter these by the current node's ID

```
> 0 votes

---
Tags: views, nodes, taxonomy-terms, 9
---


# Question
Title: Argument #1 ($config_factory) must be of type Drupal\Core\Config\ConfigFactoryInterface, array given
I'm writing a new module for the community and I have this error and I'm not understand what's going on.

The error:

> Mensaje TypeError: Drupal\view\_mode\_by\_owner\_role\Functors\ListOfNodeBundles::\__construct(): Argument #1 ($config\_factory) must be of type Drupal\Core\Config\ConfigFactoryInterface, array given, called in /var/www/html/web/core/lib/Drupal/Component/DependencyInjection/Container.php on line 259 en Drupal\view\_mode\_by\_owner\_role\Functors\ListOfNodeBundles-\>\__construct() (línea 20 de /var/www/html/web/modules/custom/view\_mode\_by\_owner\_role/src/Functors/ListOfNodeBundles.php)

My functor class:

```
<?php

namespace Drupal\view_mode_by_owner_role\Functors;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;

final class ListOfNodeBundles implements ContainerInjectionInterface {

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

  public function __construct(ConfigFactoryInterface $config_factory, EntityTypeManagerInterface $entity_type_manager, ){
    $this->view_mode_by_onwer_role = $config_factory->get('view_mode_by_owner_role.settings');
    $this->entityTypeManager = $entity_type_manager;
  }

  public static function create (ContainerInterface $container){
    return new static (
      $container->get('config.factory'),
      $container->get('entity_type.manager'),
    );
  }

  public function __invoke(): array {
    $nodes = $this->entityTypeManager->getStorage('node_type')->loadMultiple();

    $bundle_list = [];
    foreach ($nodes as $bundle) {
      $bundle_list[$bundle->id()] = $bundle->label();
    }

    return $bundle_list;
  }
}

```

My service.yml

```
services:
  view_mode_by_owner_role.functors_list_of_node_bundles:
    class: Drupal\view_mode_by_owner_role\Functors\ListOfNodeBundles
    arguments:
      - ['@config.factory', '@entity_type.manager']

```

And How I try to implement in a form:

```
<?php

namespace Drupal\view_mode_by_owner_role\Form;

use Drupal\view_mode_by_owner_role\Form\BasicSettingsForm;
use Drupal\Core\Form\FormStateInterface;
use Drupal\view_mode_by_owner_role\Functors\ListOfNodeBundles;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Configure View mode by owner role settings for this site.
 */
class SettingsChooseRoleForm extends BasicSettingsForm {

  /**
   * @var \Drupal\view_mode_by_owner_role\Functors\ListOfNodeBundles
   */
  private $listOfNodeBundles;

  public function __construct(ListOfNodeBundles $list_of_nodes_bundles){
    parent::__construct($config_factory);
    $this->listOfNodeBundles = $list_of_nodes_bundles;

  }

  public static function create(ContainerInterface $container){
    return new static(
      $container->get('view_mode_by_owner_role.functors_list_of_node_bundles'),
    );
  }
  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'view_mode_by_owner_role_settings_choosed_roles';
  }

  /**
   * {@inheritdoc}
   */
  protected function getEditableConfigNames() {
    return ['view_mode_by_owner_role.settings'];
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $listado = $this->listOfNodeBundles;
    $role_list = $this->listOfRoles();
    $config = $this->config('view_mode_by_owner_role.settings');

    $form['choosed_roles'] = [
      '#required' => TRUE,
      '#type' => 'checkboxes',
      '#options' => $role_list,
      '#title' => $this->t('Role list'),
      '#default_value' => $config->get('choosed_roles')
    ];

    // Add a submit button that handles the submission of the form.
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Submit'),
      '#description' => $this->t('Submit'),
    ];

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {

    $config = $this->config('view_mode_by_owner_role.settings')
    ->set('choosed_roles', array_filter($form_state->getValue('choosed_roles')))
    ->save();

    parent::submitForm($form, $form_state);
  }

}

```

I have read some questions, but I don't understand why I have this error.

# Answer
> 2 votes
The error is caused by how the service arguments are defined.

```
services:
  view_mode_by_owner_role.functors_list_of_node_bundles:
    class: Drupal\view_mode_by_owner_role\Functors\ListOfNodeBundles
    arguments:
      - ['@config.factory', '@entity_type.manager']

```

That service definition is telling Drupal to pass an array as first argument for the class constructor, when its first parameter is type-hinted as `Drupal\Core\Config\ConfigFactoryInterface`.

The correct definition for that service would be either one or the other of the following definitions.

```
services:
  view_mode_by_owner_role.functors_list_of_node_bundles:
    class: Drupal\view_mode_by_owner_role\Functors\ListOfNodeBundles
    arguments: ['@config.factory', '@entity_type.manager']

```

```
services:
  view_mode_by_owner_role.functors_list_of_node_bundles:
    class: Drupal\view_mode_by_owner_role\Functors\ListOfNodeBundles
    arguments:
      - '@config.factory'
      - '@entity_type.manager'

```

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


title: Should I "composerize" my installation?

text:
# Question
Title: Should I "composerize" my installation?
So; having been involved with a D7 / CiviCRM build a number of years ago, I started building a D10/CiviCRM build as a project to suggest to my local organisation.

I think I might have caused myself an issue though, as I installed D10 manually, then went on to install CiviCRM using composer.

* I have D10 installed on my VPS at /var/www/html/\[site\]
* I have CiviCRM installed at /home/\[user\]/

So the question really is; should I composerize D10 and move it to the home/\[user\] folder? Would using composer to update Civi break my D10 if I continued manually updating it?

# Answer
Yes, you should basically always use Composer to install Drupal 10+.

Midway through the Drupal 8 development cycle, composer use became mainstream, and now some contrib modules only support installation via composer. Even if you are not using such modules, the documentation will assume you have a composer installation and installing manually may lead to difficult to debug issues. It is also slower than using composer once you get the hang of it.

Although the above comments are about Drupal 10 in general, the CiviCRM docs also state that you should use composer to install Drupal.

As an aside, composer pairs well with git, so you can commit your `composer.lock` and use that to deploy to your VPS from your local machine, which is less error prone and faster (once set up) than manually updating.
> 1 votes

---
Tags: composer, 10
---