# Question
Title: How to delete all nodes of a given content type?
I have several thousand nodes of a particular content type. Using the web interface (example.com/admin/content), I can only delete about 50 at a time. How can I quickly delete them?

# Answer
> 32 votes
There is a module for that (TM).

See Bulk Delete.

That will use the Batch API to delete the nodes to avoid timeout or memory issues when deleting thousands of nodes with a single call to node\_delete\_multiple().

Bulk Delete is an abandoned module. See for alternatives:

# Answer
> 32 votes
In Drupal 8 one way is use the entityQuery() method with the EntityStorageInterface::delete() method:

```
$result = \Drupal::entityQuery("node")
    ->condition("type", "YOUR_CONTENT_TYPE_NAME")
    // If the update is being executed via drush entityQuery will only
    // return the content uid 0 have access to. To return all set
    // accessCheck to false since it defaults to TRUE.
    ->accessCheck(FALSE)
    ->execute();

$storage_handler = \Drupal::entityTypeManager()->getStorage("node");
$entities = $storage_handler->loadMultiple($result);
$storage_handler->delete($entities);

```

If you need to apply other filters/conditions you can check the QueryInterface interface page

***EDIT*** (Other way, thanks to @4k4):

```
$storage_handler = \Drupal::entityTypeManager()->getStorage("node");
$entities = $storage_handler->loadByProperties(["type" => "YOUR_CONTENT_TYPE_NAME"]);
$storage_handler->delete($entities);

```

If you want to test the code you can use:

```
drush php-eval '$storage_handler = \Drupal::entityTypeManager()->getStorage("node"); $entities = $storage_handler->loadByProperties(["type" => "article"]); $storage_handler->delete($entities);'

```

This will delete all your articles.

# Answer
> 28 votes
Looking at the Devel Generate module for inspiration, here is its "content kill" function `devel_generate_content_kill`:

> ```
> function devel_generate_content_kill($values) {
>   $results = db_select('node', 'n')
>               ->fields('n', array('nid'))
>               ->condition('type', $values['node_types'], 'IN')
>               ->execute();
>   foreach ($results as $result) {
>     $nids[] = $result->nid;
>   }
>
>   if (!empty($nids)) {
>     node_delete_multiple($nids);
>     drupal_set_message(t('Deleted %count nodes.', array('%count' => count($nids))));
>   }
> }
>
> ```

So I would try either using Devel Generate to delete all nodes but create no new ones, or use example.com/devel/php to call `devel_generate_content_kill(array('node_types' => array('my_node_type')));` directly.

# Answer
> 15 votes
If you want to do it purely through the UI, you can use the devel\_generate module.

1. Navigate to the "Generate Content" menu in "Configuration" (admin/config/development/generate/content).
2. Select the content types you want to delete.
3. Make sure the checkbox next to "Delete all content in these content types before generating new content" is checked.
4. Set the number of nodes you'd like to generate to "0".

This way, no nodes will be generated and all nodes of the selected types will be deleted.

# Answer
> 11 votes
You can do this is in Drupal 7 using the Execute PHP Code part of the Devel module by entering:

```
$result= db_query("SELECT nid FROM {node} AS n WHERE n.type = 'TYPE'");
foreach ($result as $record) {
  node_delete($record->nid);
}

```

# Answer
> 10 votes
Create a file with below code in root of drupal installation and execute the file.

```
<?php
  require_once './includes/bootstrap.inc';
  drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

  $aquery= db_query("SELECT nid FROM {node} AS n WHERE n.type = 'company'");
  while ($row = db_fetch_object($aquery)) {
    node_delete($row->nid);
  }
?>

```

# Answer
> 9 votes
Do this in terminal, if you use Drush and the delete all module:

```
 drush delete-all [content-type-machine-name]

Examples:
 drush delete-all article             Delect all article nodes.
 drush delete-all all                 Delete nodes of all types.
 drush delete-all --reset             Delete nodes of all types, and reset node, revision and comment counters.
 drush delete-all users               Delete users.

Options:
 --reset                              Reset counter for node, revision and comment tables.
 --roles                              pick roles

```

# Answer
> 5 votes
Views Bulk operations provides a BatchAPI enabled, configurable node admin screen that allows filtering by type, selection of all nodes matching your search criteria, etc.

That's my hands-down solution in Drupal 6 - besides batch delete, you can bulk-edit nodes and do a bunch of other stuff.

It looks like the Drupal 7 version isn't ready yet - but I'd be watching that module for a D7 release.

# Answer
> 5 votes
With Devel module, using drush:

```
drush genc 0 --types=article --kill

```

Or in UI as explained here: http://befused.com/drupal/delete-nodes-devel

# Answer
> 4 votes
Another snippet is:

```
$query = db_query("SELECT n.nid FROM {node} n WHERE n.type = 'TO_BE_DELETED'");
while ($n = db_fetch_object($query))
{
     node_delete($n->nid);
}

```

where `TO_BE_DELETED` is the content type to be deleted.

# Answer
> 3 votes
I use Delete all module, it works fine with D8 and provides very useful drush commands. For example, to delete all content of the `article` content type:

```
drush delete-all article

```

# Answer
> 1 votes
You can try Delete all module, navigate to 'admin/content/delete\_content' and you will be presented with a form for deletion of content belonging to certain content types.

Regards

# Answer
> 1 votes
If you have Migrate module enabled you can use:

```
$ drush migrate-wipe <content-type>

```

See Typical migrate commands using Drush.

# Answer
> 0 votes
This module is used to delete all content and/or users from a site. This is mainly a developer tool, which can come in handy in several case

https://www.drupal.org/project/delete\_all

as will as the Bulk Delete module will delete any nodes of a certain node-type using the batch api. It is advised to use the Views Batch Operations module (VBO) for a small number of nodes. But if you have to delete 10.000 nodes this module might be a better option.

https://www.drupal.org/project/bulkdelete

# Answer
> 0 votes
Delete all nodes of content type programmatically here is a helper function:

```

function _delete_all_nodes_of_type($type = '') {
  // Return all nids of nodes of type.
  $nids = db_select('node', 'n')
    ->fields('n', array('nid'))
    ->condition('n.type', $type)
    ->execute()
    ->fetchCol(); // returns an indexed array
  if (!empty($nids)) {
    node_delete_multiple($nids);
    drupal_set_message(format_plural(count($nids), count($nids) . ' node Deleted.', count($nids) . ' nodes Deleted.'));
  }
}

```

# Answer
> 0 votes
Not sure if this is fast but if you wanted to get feedback about which node it was deleting you could do something like this:

```
<?php

$nids = \Drupal::entityQuery('node')->condition('type','output')->execute();
$nodes =  \Drupal\node\Entity\Node::loadMultiple($nids);
foreach ($nodes as $n) {
    echo $n->id() . "\n";
    $n->delete();
}

```

You can rush it with something like `drush scr del.php`. The nid would be displayed right before it was deleted.

# Answer
> 0 votes
This is the **most direct** way but it has issues when you have a **considerable amount of data**.

```
$storage_handler = \Drupal::entityTypeManager()->getStorage("node");
$entities = $storage_handler->loadByProperties(["type" => "YOUR_CONTENT_TYPE_NAME"]);
$storage_handler->delete($entities);

```

To avoid this problem you could **retrieve every node id** by applying an entity query search and then doing **each node deletion** inside a for-each loop.

```
$query = \Drupal::entityQuery('node')->condition('type', 'YOUR_CONTENT_TYPE_NAME');
$nids = $query->execute();
$nodes = \Drupal::entityTypeManager()->getStorage('node')->loadMultiple($nids);
foreach ($nodes as $n) {
  $n->delete();
}

```

You can even play around adding any conditions to the query by keep calling the method `condition('field_to_evaluate', 'value')` (API docs).

# Bonus

To execute from drush cli:
```
drush eval "
  \$query = \\Drupal::entityQuery('node')
    ->condition('type', 'YOUR_CONTENT_TYPE_NAME');
  \$nids = \$query->execute();
  \$nodes = \\Drupal::entityTypeManager()->getStorage('node')->loadMultiple(\$nids);
  foreach (\$nodes as \$n) {
    \$n->delete();
    echo 'Deleted node ' . \$n->id() . PHP_EOL;
  }
"

```

If you run into memory issues try to add `php -d memory_limit=-1` before the drush command

### Happy coding!

# Answer
> 0 votes
In newer versions of Drush, you can use the `entity:delete` command to bulk delete nodes of a specific content type. No contributed modules or custom functions are required.

```
drush entity:delete node --bundle=content_type_machine_name

```

Another use of this command is to delete specific nodes. For example, to delete nodes 14, 16, 382, and 596, structure your command like this:

```
drush entity:delete node 14,16,382,596

```

More information can be found in the Drush documentation.

# Answer
> -2 votes
I ended up using db\_delete, no modules required:

```
<?php
  db_delete('node')
    ->condition('type', 'MY_CONTENT_TYPE')
    ->execute();
?>

```

**Edit/Warning:** See Berdir's comment below. This method does not clean up all data related to the nodes.

---
Tags: nodes
---


title: Removing data from an entity type

text:
# Question
Title: Removing data from an entity type
I am trying to remove a problematic payment gateway module and getting the following message when trying to uninstall it:

> There is data for the bundle Authorize.net (Accept.js) on the entity type Payment. Please remove all content before uninstalling the module.

How do I clear data for the entity type? Do I need to edit the database directly?

# Answer
> 1 votes
The issue is that the Authorize.Net module defines a `PaymentType` plugin that serves as the bundle for `Payment` entities created by the Accept.js payment gateway. I don't recall *why* we decided to use a custom payment type there, but so long as payment entities exist in your database of that bundle, you will not be able to uninstall the Authorize.Net module.

If you're able to do so without harming your store, you would need to delete all of those payments. I wouldn't recommend deleting them directly from the database, as it's not a simply search and delete operation for entities. In the past we've solved tasks like this by writing a simple drush command or other server-side script to load and batch delete entities.

The alternative would be to attempt to convert them to the default payment type. I don't know if that will break anything off the top of my head, but you can give it a shot or see if anyone in the Drupal Commerce Slack knows what side effects that might generate.

---
Tags: commerce, 9, payment-gateways
---


# Question
Title: Dynamically changing the href on a link with data-dialog-type = modal does not work
For reference I am working on this issue for the Focal Point module...

I have a link whose href is updated dynamically using js based on a particular user interaction (moving the focal point on an image). The link may start as /admin/focal\_point/preview/10/21x68 but after a user moves the focal point to another part of the image it is changed to /admin/focal\_point/preview/10/75x28. In attempting to open this preview page open in a modal (as opposed to a new window) I'm finding that clicking the link loads the preview as though the link had not been changed. Any idea how to tell the ajax logic that loads modals to acknowledge the changed href?

# Answer
Presumably, the JS used to generate the dynamic href is on the page that the modal opens? If I'm not mistaken, iframes do not run their attached scripts by default. So this page may be helpful:

https://stackoverflow.com/questions/251420/invoking-javascript-code-in-an-iframe-from-the-parent-page?utm\_medium=organic&utm\_source=google\_rich\_qa&utm\_campaign=google\_rich\_qa

Since your requested iframe is on the same domain as your parent page, there shouldn't be any CORS issue.
> 0 votes

# Answer
Try to re init the javascript. Once your web page is loaded javascript will add all the elements to your DOM (Document Object Model). So when you add something new with your ajax, the DOM doesn't know that you have added something new and it will work with the elements as before. So you may have to re init your javascript and tell the DOM that you have added new elements. If you are running an ajax in a 'onclick' function, the re init should be as below.

```
$(document).on("click","#elementID",function() {
    //your ajax script here
});

```

This is how you tell DOM that it should re add the element (`#elementID`) to itself.
> 0 votes

# Answer
This is an old post, but I just ran across this same problem. As you saw, once a modal link is on the page and has been processed by the Ajax JavaScript, changing the href of the link will not change the actual URL loaded into the modal window. To dynamically change the URL used by the modal, you must also:

1. Unbind the existing "click" handler.
2. Reset the "once" on your link.
3. Call bindAjaxLinks() again to make the link use a modal.

Example code for changing a link with the ID "my-link":

```
$('#my-link')
  // Change the link href.
  .attr('href', '/my-new-url')
  // Unbind the old Ajax click handler.
  .unbind('click');
// Remove the once() so it will get processed again.
once.remove('ajax', '#my-link');
// Attach the Ajax action to this link.
Drupal.ajax.bindAjaxLinks(document.body);

```

I updated the Ajax Dialog Boxes documentation on d.o with this info, too:

https://www.drupal.org/docs/develop/drupal-apis/ajax-api/ajax-dialog-boxes#s-changing-the-href-of-an-existing-modal-link
> 0 votes

---
Tags: 8, javascript, media
---


title: Composer not updating modules

text:
# Question
Title: Composer not updating modules
I have a Drupal 8.9.16 website and when I try running `composer update` to update the modules, I get this output

```
$ composer update
Loading composer repositories with package information
Updating dependencies
Nothing to modify in lock file
Installing dependencies from lock file (including require-dev)
Nothing to install, update or remove
Package container-interop/container-interop is abandoned, you should avoid using it. Use psr/container instead.
Package doctrine/reflection is abandoned, you should avoid using it. Use roave/better-reflection instead.
Generating autoload files
38 packages you are using are looking for funding.
Use the `composer fund` command to find out more!

```

So it says *Nothing to install, update or remove*.

Then I look at the list of available updates in the Available updates page (/admin/reports/updates) and most of them have updates.

Looking at the composer.json file, I see it listing the updated version.

For example:

* Drupal's Available updates page says I have *Address 8.x-1.8* installed and recommends version *8.x-1.9*.
* The *composer.json* file shows under require: `"drupal/address": "^1.9"`, which I assume means composer says to update to version 1.9.

I was getting this problem in Composer 1, so I updated to Composer 2 but that hasn't changed anything.

```
$ composer --version
Composer version 2.1.3 2021-06-09 16:31:20

```

Below is the content of my composer.json file.

```
{
    "name": "root/chesla",
    "require": {
        "composer/installers": "^1.7",
        "drupal/core": "^8.8",
        "drupal/address": "^1.9",
        "drupal/addtoany": "^1.12",
        "drupal/admin_toolbar": "^2.0",
        "drupal/ajax_form_entity": "^1.0-alpha1",
        "drupal/backup_migrate": "^4.1",
        "drupal/allowed_formats": "^1.2",
        "drupal/block_class": "^1.3",
        "drupal/captcha": "^1.1",
        "drupal/context": "^4.0-beta2",
        "drupal/ctools": "^3.2",
        "drupal/devel": "^2.1",
        "drupal/diff": "^1.0",
        "drupal/entity": "^1.1",
        "drupal/exclude_node_title": "^1.1",
        "drupal/features": "^3.8",
        "drupal/field_group": "^3.0",
        "drupal/media_entity": "^1.8",
        "drupal/imce": "^2.2",
        "drupal/libraries": "^3.0-alpha1",
        "drupal/mailsystem": "^4.3",
        "drupal/image_widget_crop": "^2.3",
        "drupal/markup": "^1.0-beta1",
        "drupal/menu_block": "^1.6",
        "drupal/metatag": "^1.13",
        "drupal/mimemail": "^1.0-alpha2",
        "drupal/module_filter": "^3.1",
        "drupal/paragraphs": "^1.11",
        "drupal/pathauto": "^1.6",
        "drupal/recaptcha": "^2.5",
        "drupal/typed_data": "^1.0-alpha4",
        "drupal/rules": "^3.0.0-alpha5",
        "drupal/sharethis": "^2.0-beta3",
        "drupal/simplenews": "^1.0-beta1",
        "drupal/sitemap": "^1.5",
        "drupal/token": "^1.7",
        "drupal/views_bootstrap": "^4.2",
        "drupal/views_fieldsets": "^3.3",
        "drupal/yoast_seo": "^1.7",
        "drupal/migrate_upgrade": "^3",
        "drupal/migrate_plus": "^4",
        "drupal/migrate_tools": "^4.5",
        "drupal/bootstrap": "^3.21",
        "drupal/context_layout": "^1.0-beta2",
        "drupal/crop": "^2.1",
        "drupal/entityqueue": "^1.0",
        "drupal/views_slideshow": "^4.7",
        "drush/drush": "^10.3",
        "drupal/bootstrap_simple_carousel": "^1.43",
        "drupal/carousel_block": "^1.10",
        "drupal/slick": "^2.3",
        "drupal/webform": "^6.0",
        "drupal/core-composer-scaffold": "^8.9"
    },
    "repositories": {
        "drupal": {
            "type": "composer",
            "url": "https://packages.drupal.org/8"
        }
    }
}

```

I just noticed that I have drush listed. I don't use it and don't know how to use it.

```
$ drush --version
Drush Launcher Version: 0.6.0
Drush Commandline Tool 10.3.6

```

I am trying to use composer here to update. I can install modules (did webforms the other day).

Thank you.

# Answer
> 1 votes
Running `composer update` isn't a recommended way to update.

Here you can find a, step by step, recommended way to update your codebase on local and on production.

After a codebase update running a database update is the key here. If you use drush run `drush updb`. If you don't use drush you can visit `[YOUR DOMAIN]/update.php` in any browser.

NOTE: if you visit your site `[YOUR DOMAIN]/update.php` you also might want to change `settings.php`

in `settings.php` set the default `FALSE` to `TRUE` in:

`$settings['update_free_access'] = FALSE; // default`

# Answer
> 0 votes
Try this:

> composer update drupal/address --with-all-dependencies

or

Delete the file composer.lock and Try this:

> composer update

# Answer
> 0 votes
Same problem here (composer updates not being reflected in the Drupal's Available Updates). The problem was as @Hometoy said, two directories. In my case, one was /modules and the other /modules/contrib. Deleting the module inside /modules and keeping the newer version in /modules/contrib did the trick.

---
Tags: 8, composer
---


# Question
Title: Extra template name suggestions for main-menus based on regions
In the first look, the process of solving this problem maybe seems very easy, but this problem makes me really confused.
Imagine that I want to have two main-menu one in the header (primary menu/ top bar) and another in the footer region of my site with different style and template.
By default, there is no file suggestion that differentiates which twig file to be used for a menu based on the region.
That's why I follow below article to add **block region** as the id of main-menu and make block's template name suggestion unique and dedicated for next customization.

Drupal 8: Theming menus by KristianKaa

```
function THEMENAME_preprocess_block(&$variables) {
  $variables['content']['#attributes']['block'] = $variables['attributes']['id'];
}

function THEMENAME_theme_suggestions_menu_alter(array &$suggestions, array $variables) {
  if (isset($variables['attributes']['block'])) {
    $hook = str_replace(array('block-', '-'), array('', '_'), $variables['attributes']['block']);
    $suggestions[] = $variables['theme_hook_original'] . '__' . $hook;
  }
}

```

But the following HTML comments, are the theme hook suggestions added by the debug function, for the Main navigation blocks after adding those codes.

**Header section:**

```
<!-- FILE NAME SUGGESTIONS:
   * menu--main.html.twig
   * menu--main--mainnavigation-2.html.twig
   x menu.html.twig
-->

```

**Footer section:**

```
<!-- FILE NAME SUGGESTIONS:
   * menu--main.html.twig
   * menu--main--mainnavigation-2.html.twig
   x menu.html.twig
-->

```

As you can see from the suggestions, there is nothing to distinguish the two menus from one another based on regions.
I think this template name suggestion hasn't any efficiency in this case because what's that suggested isn't semantic.

So my main question is: How can I add extra filename suggestions for main-menu based on regions not block Id? Or
How to get the current region inside the `Hook_theme_suggestions_menu_alter`, to add my own suggestion?

I read below posts also but all of those add block Id as twig suggestions for menus:
Additional twig suggestions for menus
How to create a custom Twig Template for Menu Block
How to make a theme hook suggestion for menu according to the region?

# Answer
> 3 votes
You should install the Menu Block module, which will let you dictate the name of the instance of the menu for easy theming. It is then up to you to give it a name.

TBH you don't want to get in the game of trying to manipulate region suggestions based on what it may or may not contain.

# Answer
> 0 votes
in Drupal 9.5 for some reason i was struggling to pass variable from the block preprocess function: My solution was by $GLOBALS so in Preprocess block I'm getting the region from configuration variable

```
/**
 * Implements hook_preprocess_block().
 *
 */
function THEMENAME_preprocess_block(array $variables)
{

  if (isset($variables['elements']['#id'])) {

    // Access the block configuration.
    $region = $variables['configuration']['region'];

    if (!empty($region)) {
      $variables['content']['#attributes']['region'] = $region;
      $GLOBALS['my-region'] = $region;

    }
  }
}

```

Than getting this in hook\_theme\_suggestions\_menu\_alter()

```
/**
 * Implements hook_theme_suggestions_HOOK_alter().
 */
function THEMENAME_theme_suggestions_menu_alter(array &$suggestions, array $variables)
{

  //dpm($variables);
  $menu_id = $variables['attributes']['id'];
  $region = $GLOBALS['my-region'];
  if (isset($menu_id)) {
    $suggestions[] = 'menu__' . $menu_id;
  }
  if (isset($region)) {
    $suggestions[] = 'menu__' . $region;
  }
}

```

so output i have :

```
<!-- FILE NAME SUGGESTIONS:
   x menu--account.html.twig
   * menu--header-form.html.twig
   * menu--block-system-menu-block-account--2.html.twig
   x menu--account.html.twig
   * menu.html.twig
-->

```

I Hope it will help someone :)

---
Tags: 8, navigation, theming
---


# Question
Title: Multiple local sites in subdirectories using nginx
I want to install both Drupal 9 and Drupal 10 on my localhost. I had based Nginx config on Nginx Drupal recipe and following is the part of Nginx config that I'm struggling with:

```
server {
    listen 80;
    root /var/www/drupal10;
    index index.php;

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

    location @rewrite {
        rewrite ^ /drupal10/index.php;
    }
}
... and then the rest of configuration as per nginx drupal recipe

```

Similarly for nginx configuration of drupal9 subdirectory:

```
server {
    listen 80;
    root /var/www/drupal9;
    index index.php;

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

    location @rewrite {
        rewrite ^ /drupal9/index.php;
    }
}
... and then the rest of configuration as per nginx drupal recipe

```

But I'm getting "404 Not Found". What could be the correct configuration?

# Answer
> 0 votes
Resolved with following configuration:

```
server {
  listen 80 default_server;

  root /var/www;

  index index.php;

  location /drupal9 {
    alias /var/www/drupal9;
    try_files $uri /drupal9/index.php;
  }

  location /drupal10 {
    alias /var/www/drupal10;
    try_files $uri /drupal10/index.php;
  }

# Additional settings for PHP and Drupal from nginx drupal recipe[1]
# ...

```

---
Tags: 10
---


# Question
Title: How do I upgrade from drupal 9 to 10 when dynamic entity reference 3.0 and up require drupal 10 first?
I am trying to upgrade my drupal 9 site to drupal 10. I installed the Upgrade Status module and it's only complaints are I need to upgrade linkchecker and dynamic entity reference. The problem is that to upgrade linkchecker to 2.0 I need to upgrade dynamic entity reference to at least 3.0, but dynamic entity reference 3.0 and up all require drupal 10.

How do I upgrade those two so that I can upgrade drupal 9 to drupal 10 if dynamic entity reference requires drupal 10? It seems like a loop to me.

Thanks!

# Answer
That situation is documented on Drupal.org.
> 0 votes

---
Tags: 9
---


title: Multi Domain Robots.txt

text:
# Question
Title: Multi Domain Robots.txt
I have multiple sites running on a single code base in Drupal 8. Is there any possible way to add different robots.txt file for each site?

# Answer
If you are under apache, you can use mod\_rewrite to deliver others robots.txt :

```
  RewriteCond %{HTTP_HOST} ^domain1.com$
  RewriteRule ^robots.txt$ robots.domain1.com.txt [L]
  RewriteCond %{HTTP_HOST} ^domain2.com$
  RewriteRule ^robots.txt$ robots.domain2.com.txt [L]

```

Note you may also generate your robots.txt in some controller for each site depends on config and fallback to default if not defined.
> 0 votes

# Answer
The RobotsTXT module lets you have a different robots.txt for each site in a multisite setup. It also gives you the chance to edit via the UI.
> 0 votes

# Answer
1. You need to have 2 modules:

2. Enable RobotsTxt and Domain Configuration UI modules.
3. On the RobotsTxt configuration form (https://\[your\_website\]/admin/config/search/robotstxt) you should see a "Enable domain configuration" button. Just click on the button.
4. After, you will see a "Domain Configuration" container at the top of the RobotsTxt configuration page where you can switch between domains and apply related configuration.
> 0 votes

---
Tags: multi-sites
---


# Question
Title: How do I link to a custom form mode in a (node) tab?
My issue is my form is too big and too slow, with nested paragraphs, media entities and so on, the biggest issue is the metatag-module with its 100s of fields.

So I thought I just add a new form display mode, add the metatag-field only there and somehow link to it from some .links.tasks.yml with `base_route: entity.node.canonical`

Alas, it's apparently not that easy. I have no idea what route\_name to use, if there even is any (but what good is a form mode if I can't use it?)

I could add a custom .routing.yml with

```
  defaults:
    _entity_form: node.metatags

```

but then, for that to work, I apparently need to somehow get it into the list of forms in Drupal\node\Entity\Node -\> handlers -\> form

Google is quite useless, because all I find are various examples about the display mode. quick\_node\_clone wasn't really helpfull as an example, because they also only use the default form mode.

Does anyone have any idea? This should be easy, if only I knew how...

# Answer
> 0 votes
What I ended up doing, since nobody seems to be answering questions around here anymore:

```
<?php

use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Url;

/**
 * Implements hook_entity_form_display_alter().
 */
function meta_tab_entity_form_display_alter(EntityFormDisplayInterface &$form_display, array $context) {

  if (isset($context['entity_type']) && isset($context['bundle'])) {
    /** @var \Drupal\Core\Entity\EntityDisplayRepository $entity_display_repo */
    $entity_display_repo = \Drupal::service('entity_display.repository');
    $form_mode_opts = $entity_display_repo->getFormModeOptionsByBundle($context['entity_type'], $context['bundle']);
    $route_name = \Drupal::routeMatch()->getRouteName();
    if ($route_name === "entity.node.metatags_form" && isset($form_mode_opts['metatag_form'])) {
      $form_display = $entity_display_repo->getFormDisplay($context['entity_type'], $context['bundle'], 'metatag_form');
    }else{
      $form_display = $entity_display_repo->getFormDisplay($context['entity_type'], $context['bundle'], 'default');
    }
  }
}

function meta_tab_entity_operation(EntityInterface $entity) {
  $operations = [];

  // Only add an operation for node entities.
  if ($entity->getEntityTypeId() !== 'node') {
    return $operations;
  }

  if (!_quick_node_clone_has_clone_permission($entity)) {
    return $operations;
  }

  $operations['metatags'] = [
    'title' => t('Metatags'),
    'weight' => '100',
    'url' => Url::fromRoute('entity.node.metatags_form', ['node' => $entity->id()]),
  ];

  return $operations;
}

```

more or less copied from https://www.drupal.org/project/entity\_form\_mode/, obviously + routing.yml-file and links.task.yml

yes, it's ugly and hardcoded to machine\_name = metatags\_form, but at least it works. I can't spend another day on some minor taks that would have taken 30 minutes in the good old drupal7 days. I'm really getting sick of this over engineered piece of \**** and Symfony.

---
Tags: nodes, forms, 10
---


# Question
Title: how to get the default value for a multivalue field
I'm using the Mulitvalue Field module (https://www.drupal.org/project/multivaluefield) and I created a form like this and it successfully sets the various emails into the configuration file, but when i try to get the default value it, gets an array back. how do i get the default value for this multivalue field to display it in the form?

```
$form['comment']['comment_admin_notify_mailto'] = array(
  '#type' => 'multivalue',
  '#title' => $this->t('Comments Topics And Post Approvers'),
  '#cardinality' => MultiValue::CARDINALITY_UNLIMITED,
  'comment_admin_notify_mailto' => [
    '#type' => 'textfield',
    '#title' => $this->t('Person to send notifications for Comments, Topics and Posts'),
    '#default_value' =>  vista_moderation_admin_notify_variable_get_site_email(),
  ],
);

```

this is the function code:

```
function vista_moderation_admin_notify_variable_get_site_email() {

  $moderation_config = \Drupal::config('vista_moderation_admin_notify.settings');
  $site_mail = $moderation_config->get('comment_admin_notify_mailto');
  if (empty($site_mail)) {
    $site_config = \Drupal::config('system.site');
    $site_mail = $site_config->get('mail');
  }
  return $site_mail;

```

# Answer
i found the answer in the comments of the module which is the documentation :

```
 /**
   * Sets the default value for the child elements.
   *
   * @param array $elements
   *   The elements array.
   * @param array $value
   *   An array of values, keyed by the children element name.
   */
  public static function setDefaultValue(array &$elements, array $value): void {
    // @todo Handle nested elements.
    foreach (Element::children($elements, FALSE) as $child) {
      if (isset($value[$child])) {
        $elements[$child]['#default_value'] = $value[$child];
      }
    }
  }

```
> 0 votes

---
Tags: forms
---


# Question
Title: Is it possible to migrate data into multiple translations of entities?
I'm running a multilingual migration of events (which are stored as nodes) that have keywords on them where there are keywords (stored as terms). The keywords should be stored in a multilingual tags vocabulary.

The source is a REST API, and what I have for the keyword data looks like this:

```
array (
 'id' => 'yso:p360',
 'alt_labels' =>
 array (
 ),
 'created_time' => '2014-06-23T11:37:29.198000Z',
 'last_modified_time' => '2014-06-23T11:37:29.198000Z',
 'aggregate' => false,
 'deprecated' => false,
 'n_events' => 11709,
 'data_source' => 'yso',
 'image' => NULL,
 'name' =>
 array (
   'fi' => 'kulttuuritapahtumat',
   'en' => 'cultural events',
   'sv' => 'kulturevenemang',
 ),
 '@id' => 'https://api.hel.fi/linkedevents/v1/keyword/yso:p360/',
 '@context' => 'http://schema.org',
 '@type' => 'Keyword',
)

```

Is there some way to use for example EntityGenerate to create all the translations of the tags when importing the original languages of the events that has these tags?

Note:

I would like to avoid migrating all the keywords first. We only import a subset of the events, and we are only interested in the keywords that are used by those events.

The normal way of D8 migrations with multilingual sources is to create a configuration for each translation. This doesn't work in our case, as I chose to use entity\_generate for the tags. If you have another suggestion, I am interested.

# Answer
> 1 votes
I'm missing some information to fully understand your problem, but to migrate an object which has many translation you need: Create the initial entity in the source language of your choice and add translations to this entity. For example, for a taxonomy term:

```
$default_language = "en";
$other_languages = ["fi", "sv"];

$term = \Drupal\taxonomy\Entity\Term::create([
  'vid' => 'my_vocabulary',
  'name' => $my_importer_object['name'][$default_language],
]);

$term->save();

foreach ($other_languages as $language) {
  $term_object = [
    'vid' => 'my_vocabulary',
    'name' => $my_importer_object['name'][$language],
  ];
  $term->addTranslation($language, $term_object);

  $term->save();
}

```

If it's a taxonomy term, it will be referenced in your object like an entity reference, with its `tid`. The link between the translated version of the term and the translated version of the node will be the language. If the term exists in the language of the node, Drupal 8 will used it automatically. If the term isn't translated in the language, Drupal will use the original language of the term, in which it had been initially created.

# Answer
> 0 votes
Here is an example media migration config file that demonstrates creating translations of media entities based on the original default (English) entity. Keep in mind this relies on other migrations (files, default media entities in default language) and is not a complete example. I just wanted to highlight the actual config I used to create the translations.

The key piece is in the destination plugin, the "translations: true" attribute. Combined with the process plugin for looking up "mid" so we get the right source entity to translate:

```
langcode: en # Langcode of the config, nothing to do with the entities
status: true

id: file_json_to_media_es
migration_group: files_to_media
label: 'Import files to Media entities (ES)'
source:
  plugin: url
  data_fetcher_plugin: http
  urls:
    - 'https://example.com/files.json' # A JSON file as source, in my example
  data_parser_plugin: json
  item_selector: '[*]'
  fields:
    -
      name: id
      label: 'ID'
      selector: id
    -
      name: path_name
      label: 'Path Name'
      selector: /language/es/path_name
    -
      name: title
      label: Title
      selector: /language/es/title
  ids:
    id:
      type: integer

  # Use the ID as the key unique migration identifier.
  keys:
    - id

process:
  mid:
    - plugin: migration_lookup
      migration: file_to_media_english
      source: id #this is a custom id field in the source JSON, this can be any unique key you have in your setup
  langcode:
    - plugin: default_value
      default_value: es
  name: title
  field_media_file/target_id:
    - plugin: migration_lookup
      migration: file_import_es # a migration that created the files
      source: id # same, just needs to be your unique id field as needed
destination:
  plugin: 'entity:media'
  default_bundle: resource # Whatever your media bundle is
  translations: true # this is the important attribute!
migration_dependencies:
  required:
    - file_to_media_english # the default media entities
    - file_import_es # the file migration

```

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


# Question
Title: How to execute a drush command with an answer to a prompt
When I run `drush it` to sync up my taxonomy content, I am prompted to specify the style:

```
What import style would you like?:
[0] Full
[1] Safe
[2] Force

```

Is there a way I can include my answer to this prompt in the original drush command, so the prompt is answered automatically? I'm asking because I need to add this command to my ci/cd pipeline.

I tried it like this:

`drush it --style=0`

But that results in 'The "--style" option does not exit'.

# Answer
> 1 votes
Assuming this is the `it` command from the Structure sync module, you can use the `choice` option, e.g.

```
drush it --choice=full

```

Other options are `safe` and `force` (all lowercase).

For future reference you can run `drush <command> --help` to list the options for any command.

---
Tags: drush, configuration, 10
---


# Question
Title: How to know what is the most viewed node without installing a module?
How to know what is the most viewed node withotut installing a module?

Is there a core way to do that months (more than one year) after enabling the statistics module?

# Answer
> 0 votes
You can refer the below code to get most viewd node by using preprocess function in your `.theme` file.get statistics count for all required nodes.

Save Node name and count in a array, then later check array for largest count.

```
function [THEME_NAME]_preprocess_node(&$variables) {
//get all node ids
$nids = \Drupal::entityQuery('node') ->condition('status', 1) ->condition('type', 'YOUR-NODE-TYPE') ->execute();
$nodes = \Drupal\node\Entity\Node::loadMultiple($nids);
// now with foreach loop save each node name and count in array
foreach ($nids as $nid){
$node_name = Node::load($nid); $node_details->field_FIELD_NAME->value;
  $statistics = statistics_get($variables['node']->id());
  $variables['node_stats'] = \Drupal::translation()->formatPlural($statistics['totalcount'], '1 view', '@count views');
}

```

# Answer
> 0 votes
You can use the core statistics module to get the count of views for each node or you can write simple code to get same result..

```
function getMostViewedNodeBetweenDates($start_timestamp, $end_timestamp) {
  // Query the database for the node with the highest view count.
  $query = \Drupal::entityQuery('node')
    ->condition('status', 1) // Published nodes.
    ->condition('type', 'article'); // Change 'article' to the desired content type.

  // Add a condition for the date range.
  $query->condition('created', [$start_timestamp, $end_timestamp], 'BETWEEN');

  // Sort the nodes by view count in descending order.
  $query->sort('statistics_counter_totalcount', 'DESC');
  $query->range(0, 1); // Retrieve only one result, the most viewed node.

  // Execute the query and get the result.
  $nids = $query->execute();

  // If we have results, load the most viewed node.
  if (!empty($nids)) {
    $most_viewed_nid = reset($nids);
    $most_viewed_node = \Drupal\node\Entity\Node::load($most_viewed_nid);
    return $most_viewed_node;
  }

  // No nodes found.
  return null;
}

```

You can then call this function in your custom code to get the most viewed node:

```
$start_timestamp = strtotime('2023-01-01');
$end_timestamp = strtotime('2023-12-31');

$mostViewedNode = getMostViewedNodeBetweenDates($start_timestamp, $end_timestamp);

if ($mostViewedNode) {
  // Do something with $mostViewedNode.
  $title = $mostViewedNode->getTitle();
  $viewCount = $mostViewedNode->get('statistics_counter_totalcount')->value;
  // ...
}
else {
  // No most viewed node found in the specified date range.
}

```

I hope this answers your question.

---
Tags: 10
---


# Question
Title: Adding response headers to media response
I have a custom module which implements hook\_ENTITY\_TYPE\_access to control access to particular PDFs (media objects). When the user doesn't have access they are redirected to a login/register page. This works successfully.

I now need to send specific HTTP response headers (cache-control, no-store) along with the response when the user does have access to the document.

From what I have been able to find, to get access to the response object and add headers I need to subscribe to the response event. This seems excessive, I'm subscribing to the response event for every request, when I only need the to set headers for a very small number of requests.

Do I need to subscribe to the response event, is there a way to get hold of the response object when processing hook\_ENTITY\_TYPE\_access?

Or, is there another way to set 'Cache-control: no-store' without needing to access the response object?

Is it a fair assumption, that when hook\_ENTITY\_TYPE\_access has been called and allowed access, that any later response must be for the particular media object. I.e., I need some way for the hook\_ENTITY\_TYPE\_access handler and the response event subscriber to communicate, e.g. in the hook handler, retrieve my event subscriber service object and set some flag on it - to indicate it should add headers to the response event.

Or, Should I be looking at this another way?

# Answer
Yes, there is a way to communicate via cache metadata. If you add cache metadata to the access results the router adds it to a CacheableAccessDeniedHttpException. When issuing the redirect response to the login/register page you can check if the exception class is an instance of CacheableDependencyInterface and get the cache metadata to set the response headers.
> 0 votes

---
Tags: media, caching
---


# Question
Title: How to import unknown number of images with drupal feeds from xml file?
I am trying to import a gallery of images for a specific content type. The XML file looks like this:

```
 <img1>
 https://xml.....
 </img1>
 <img2>
 https://xml.....
 </img2>
 <img3>
 https://xml.....
 </img3>
 <img4>
 https://xml.....
 </img4>
 <img5>
 https://xml.....
 </img5>

```

The node has a different and unknown number of images. I'm using QueryPath's XML parser to import the contents to the node, but I don't know how to import all the images. Some nodes have up to 50 images.

# Answer
> 0 votes
I switched from QueryPath's XML parser to XPath, and used :

`//*[starts-with(local-name(), "img")]`

Thus, all images starting with 'img', regardless of the number after it, are imported without a problem.

---
Tags: feeds, 10
---


# Question
Title: I am having a problem implementing a custom command
I am running Drupal 9 on DDEV. I am following detailed docs I have found online for implementing a custom drush command, but having no luck.

Here is my drush.services.yml in the root of my custom module, sp\_retrieve, and my class file, RetrieveContentCommands.php in src/Commands.

When I clear cache and try "drush list", my command is not listed. When I try to run the command I get "There are no commands defined in the sp\_retrieve namespace."

```
services:
  sp_retrieve.commands:
    class: \Drupal\sp_retrieve\Commands\RetrieveContentCommands
    arguments: ['@entity_type.manager', '@logger.factory', '@logger.dblog']
    tags:
      - { name: drush.command }

```

```
namespace Drupal\sp_retrieve\Commands;

use Drush\Commands\DrushCommands;

class RetrieveContentCommands extends DrushCommands {
   /**
   * A custom Drush command to archive a user.
   *
   * @command sp_retrieve:archive_user
   *
   * @aliases archive_user
   *
   * @usage sp_retrieve:archive_user
   *
   * @param string $userId
   * @param string $deactived
   * @param string $reason
   *
   *
   */
   public static function archiveUser(string $userId, $deactived = FALSE, $reason = '') {
      var_dump("foo");
    }
}

```

# Answer
> 2 votes
Your command methods must be instance members, not static. Remove the `static` keyword and you should be able to continue

```
public function archiveUser(string $userId, $deactived = FALSE, $reason = '')

```

You have also defined several arguments for the service, but not implemented the constructor to receive them - that will probably throw an error once the `static` keyword is removed and the service is picked up, so worth fixing now.

It's also worth bearing in mind that it's now recommended to use PHP attributes rather than docblock annotations to define commands. This is available from Drush 12 onwards.

---
Tags: drush
---


title: Using password recommendations code

text:
# Question
Title: Using password recommendations code
I've got a Drupal 9 site where I'm adding a form that will add a user to the Drupal site and also to an external database. I'd like to be able to use the same mechanism that you see in the *Add user* form which shows recommendations for the password, and removes individual recommendation items as the entered password passes them.

Is there an API for using Drupal Core's code for this in my own forms?

# Answer
> 5 votes
> Is there an API for using Drupal Core's code for this in my own forms?

Using the *password\_confirm* form element, you automatically get the JavaScript code that does what you described. The function that adds the necessary JavaScript code is `user_form_process_password_confirm()`, but you do not need to call it, as that is a process hook that is automatically invoked for that form element.

```
function user_form_process_password_confirm($element) {
  $password_settings = [
    'confirmTitle' => t('Passwords match:'),
    'confirmSuccess' => t('yes'),
    'confirmFailure' => t('no'),
    'showStrengthIndicator' => FALSE,
  ];

  if (\Drupal::config('user.settings')->get('password_strength')) {
    $password_settings['showStrengthIndicator'] = TRUE;
    $password_settings += [
      'strengthTitle' => t('Password strength:'),
      'hasWeaknesses' => t('Recommendations to make your password stronger:'),
      'tooShort' => t('Make it at least 12 characters'),
      'addLowerCase' => t('Add lowercase letters'),
      'addUpperCase' => t('Add uppercase letters'),
      'addNumbers' => t('Add numbers'),
      'addPunctuation' => t('Add punctuation'),
      'sameAsUsername' => t('Make it different from your username'),
      'weak' => t('Weak'),
      'fair' => t('Fair'),
      'good' => t('Good'),
      'strong' => t('Strong'),
      'username' => \Drupal::currentUser()->getAccountName(),
    ];
  }

  $element['#attached']['library'][] = 'user/drupal.user';
  $element['#attached']['drupalSettings']['password'] = $password_settings;

  return $element;
}

```

There is no need to add any specific CSS class to the form element, as that is already done from `PasswordConfirm::processPasswordConfirm()`.

```
  $element['pass1'] = [
    '#type' => 'password',
    '#title' => t('Password'),
    '#value' => empty($element['#value']) ? NULL : $element['#value']['pass1'],
    '#required' => $element['#required'],
    '#attributes' => [
      'class' => [
        'password-field',
        'js-password-field',
      ],
      'autocomplete' => [
        'new-password',
      ],
    ],
    '#error_no_message' => TRUE,
  ];
  $element['pass2'] = [
    '#type' => 'password',
    '#title' => t('Confirm password'),
    '#value' => empty($element['#value']) ? NULL : $element['#value']['pass2'],
    '#required' => $element['#required'],
    '#attributes' => [
      'class' => [
        'password-confirm',
        'js-password-confirm',
      ],
      'autocomplete' => [
        'new-password',
      ],
    ],
    '#error_no_message' => TRUE,
  ];

```

To use a *password\_confirm* form element, you just need to define the form element using code similar to the following one. (It is the code used by the User module for the account password.)

```
$form['account']['pass'] = [
  '#type' => 'password_confirm',
  '#size' => 25,
  '#description' => $this->t('To change the current user password, enter the new password in both fields.'),
];

```

As side note, the *password\_confirm* form element must not be confused with the *password* form element, which shows a single text field to enter a password, without the second text field to confirm it. It does not show the strength indicator either.

---
Tags: users
---


# Question
Title: How can I use a unique non-administrative theme for the admin role?
I have an admin role in my website and I want that based on that role, the non-administrative theme would be different than the default one. For example, if the installed and default non-administrative theme for all users is Olivero, then the installed and default non-administrative theme for me as an admin should be something else, for example a contributed theme I have downloaded.

How could I do that?

# Answer
Luckily, there are a couple of modules that can help you. I've used the Theme Negotiation by Rules module in several projects to provide functionality similar to your requirements.

> Usage: As a site builder, visit the Appearance page and click the Theme rules tab. Use the Add theme rule action button to add new rules.

Then, you can define conditions, such as the user role, path (patterns), etc., to indicate the alternative theme to use.
> 1 votes

---
Tags: 10
---


# Question
Title: Context condition by role with reaction by theme causes website to partially crash
I have setted a Context **condition** by role (admin) with **reaction** of using a theme different than the default one, and my problem is that it caused my website to partially crash; I as an admin get the following error in all pages:

> An unexpected error occurred. Please try again later

The website **keeps working** for non admin role users (i.e anonymous users, regular registered users, etc.) but I as an admin can't use it.

Flushing all caches with Drush didn't help.

## Watchdog

In the SQL watchdog table I only see a few **"Page not found"** errors without further detailing.

`vendor/bin/drush watchdog:show`

```
------- -------------- ---------------- ---------- -------------------------------------------------------------------------------------------
  ID      Date           Type             Severity   Message
 ------- -------------- ---------------- ---------- -------------------------------------------------------------------------------------------
  85934   16/Dec 17:21   php              error      Twig\Error\SyntaxError: Unknown "endif" tag. in Twig\Parser->subparse() (line 37 of
                                                     /home/USERNAME/WEB_APPLICATION_ROOT/example.com/web/themes/contrib/basic/templates/layout/html.html.twig).

```

The theme I made the Context reaction about is named Basic, so this is probably related.

## Things I consider

### 1. Programatically unset the Context reaction somehow

Putting some PHP like one of these somewhere, but where?

```
<?php
/**
* Implementation of hook_context_load_alter().
*/
function custom_module_context_load_alter(&$context) {
  if ($context->name == 'context_name') {
    if (arg(0) == "view_path") //on friends view now
    dpm($context->reactions['block']['blocks']); //find your block name
    unset($context->reactions['block']['blocks']['block_name']); //unset block condition
  }
}
?>

```

or:

```
<?php
function mymodule_update7101() {

  $result = db_select('context', 'c')
    ->fields('c')
    ->condition('name', 'machine_name_of_context_to_edit', '=')
    ->execute()
    ->fetchAssoc();
  $conditions = unserialize($result['conditions']);
  $reactions = unserialize($result['reactions']);

  unset($reactions['block']['blocks']['block_name_to_remove']);
  unset($conditions['path']['values']['condition_path_url_to_remove']);
  $conditions['path']['values']['condition_path_url_to_set'] = 'condition_path_url_to_set';

  db_update('context')
    ->fields([
      'conditions' => serialize($conditions),
      'reactions' => serialize($reactions),
    ])
    ->condition('name', 'machine_name_of_context_to_edit', '=')
    ->execute();
}
?>

```

Code credits

### 2. Merely removing Context module altogether

If simply removing Context module altogether as follows would help, then I don't have any problem whatsoever to do that, but I am just afarid that it would do more harm than good.

```
vendor/bin/drush un context
composer remove 'drupal/context:^5.0@RC'

```

So what can be done in that case besides recovering data from a yesterday backup?

# Answer
> 0 votes
As indicated by `vendor/bin/drush watchdog:show` the problem was in a twig file of the Basic theme because when I have changed in `/themes/contrib/basic/templates/layout/html.html.twig` from this:

```
<!DOCTYPE html>
  <html{{ html_attributes }}>
{% endif %}

```

to that:

```
<!DOCTYPE html>
  <html{{ html_attributes }}>

```

I could then finally use my website as an admin and with Basic theme as a non-administrative theme unique to me as an admin.
I then went to `/admin/structure/context` and manually removed the particular condition-reaction discussed here.

---
Tags: 10
---


# Question
Title: How to programmatically add the exact name of the field in the form?
I have used hook\_entity\_base\_field\_info in my custom module to add a field to the user entity:

```
$fields['phone'] = BaseFieldDefinition::create('telephone')
  ->setName('phone')
  ->setLabel(t('Phone number'))
  ->setDescription(t('User phone number information.'))
  ->setRequired(TRUE)
  ->setRevisionable(TRUE)
  ->setTranslatable(FALSE)
  ->setSetting('max_length', 15)
  ->setDisplayOptions('form', [
    'type' => 'telephone_default',
    'weight' => -3,
    'settings' => [
      'placeholder' => '123-456-7890',
    ],
  ])
  ->setDisplayOptions('view', [
    'type' => 'telephone_default',
    'weight' => -3,
    'label' => 'above',
  ])
  ->setDisplayConfigurable('view', TRUE)
  ->setDisplayConfigurable('form', TRUE);

```

But in the user form, the name of the field is name="phone\[0\]\[value\]" as follows:

```
<input data-drupal-selector="edit-phone-0-value" aria-describedby="edit-phone-0-value--description" type="tel" id="edit-phone-0-value" name="phone[0][value]" value="" size="30" maxlength="256" placeholder="123-456-7890" class="form-tel required form-element form-element--type-tel form-element--api-tel" required="required" aria-required="true">

```

I want my field name to be like name="phone" and id="edit-phone". How can i make my phone field name like email field:

```
<input data-drupal-selector="edit-mail" aria-describedby="edit-mail--description" type="email" id="edit-mail" name="mail" value="" size="60" maxlength="254" class="form-email required form-element form-element--type-email form-element--api-email" required="required" aria-required="true">

```

I also tried to create field in install yml file, but the result was the same.

# Answer
> 0 votes
Since the question compares the form element added for a *telephone* entity field with the form element used for the account email, and wonders why in the latest case the form element name is simply *mail*, I will answer this part.

The *Email address* form element is not added as entity field, but it is a normal form element added by `AccountForm::form()`.

```
  $form['account']['mail'] = [
    '#type' => 'email',
    '#title' => $this->t('Email address'),
    '#description' => $this->t('The email address is not made public. It will only be used if you need to be contacted about your account or for opted-in notifications.'),
    '#required' => !(!$account->getEmail() && $user->hasPermission('administer users')),
    '#default_value' => !$register ? $account->getEmail() : '',
  ];

```

If you add a new email entity field (for example, *Secondary email*), its HTML element name would be similar to the one you noticed for the telephone field.

```
<input data-drupal-selector="edit-field-secondary-email-0-value" type="email" id="edit-field-secondary-email-0-value" name="field_secondary_email[0][value]" value="" size="60" maxlength="254" placeholder="" class="form-email form-element form-element--type-email form-element--api-email">

```

This does not happen just for the email form element, but also for the *Username* field, which is added from `AccountForm::form()` too.

```
  $form['account']['name'] = [
    '#type' => 'textfield',
    '#title' => $this->t('Username'),
    '#maxlength' => UserInterface::USERNAME_MAX_LENGTH,
    '#description' => $this->t("Several special characters are allowed, including space, period (.), hyphen (-), apostrophe ('), underscore (_), and the @ sign."),
    '#required' => TRUE,
    '#attributes' => [
      'class' => [
        'username',
      ],
      'autocorrect' => 'off',
      'autocapitalize' => 'off',
      'spellcheck' => 'false',
    ],
    '#default_value' => !$register ? $account->getAccountName() : '',
    '#access' => $account->name->access('edit'),
  ];

```

The HTML markup added for that form element is the following one, on Drupal 10.2.

```
<input class="username form-text required form-element form-element--type-text form-element--api-textfield" autocorrect="off" autocapitalize="none" spellcheck="false" autocomplete="off" data-drupal-selector="edit-name" aria-describedby="edit-name--description" type="text" id="edit-name" name="name" value="admin" size="60" maxlength="60" required="required" aria-required="true">

```

It could be possible to get a simpler HTML element name for a telephone entity field, by adding a form element that overrides the one used by the entity field, but only when the form element is added by an entity form class like the `AccountForm` class. Anything added after, for example in a `hook_form_alter()`/`hook_form_FORM_ID_alter()` implementation, would not have any effect.

---
Tags: entities, 10
---


# Question
Title: How configure the field permissions of a custom entity?
I'm on D10.x.x and I've created a custom content entity - code below.

Those entities are created programmatically after an event, and one is assigned to each one of the site' users.

The users with authenticated role have the permission to edit their own entities; however they should be able to edit only a sub-set of the entity's fields.

I can't figure how to implement that access check.

From my example, the entity has like `uid`, `status`, `created`, etc.

How could I limit one role to edit only `status` or `title` field?

```
<?php

declare(strict_types=1);

namespace Drupal\my_module_notification\Entity;

use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\my_module_notification\MyModuleNotificationUserInterface;
use Drupal\user\EntityOwnerTrait;

/**
 * Defines the my_module_notification_user entity class.
 *
 * @ContentEntityType(
 *   id = "my_module_notification_user",
 *   label = @Translation("User notification"),
 *   label_collection = @Translation("user notifications"),
 *   label_singular = @Translation("user notification"),
 *   label_plural = @Translation("user notifications"),
 *   label_count = @PluralTranslation(
 *     singular = "@count user notification",
 *     plural = "@count user notifications",
 *   ),
 *   handlers = {
 *     "list_builder" =
 *   "Drupal\my_module_notification\MyModuleNotificationUserListBuilder",
 *     "views_data" = "Drupal\views\EntityViewsData",
 *     "access" =
 *   "Drupal\my_module_notification\MyModuleNotificationUserAccessControlHandler",
 *     "form" = {
 *       "add" = "Drupal\my_module_notification\Form\MyModuleNotificationUserForm",
 *       "edit" = "Drupal\my_module_notification\Form\MyModuleNotificationUserForm",
 *       "delete" = "Drupal\Core\Entity\ContentEntityDeleteForm",
 *       "delete-multiple-confirm" =
 *   "Drupal\Core\Entity\Form\DeleteMultipleForm",
 *     },
 *     "route_provider" = {
 *       "html" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider",
 *     },
 *   },
 *   base_table = "my_module_notification_user",
 *   admin_permission = "administer my_module_notification_user",
 *   entity_keys = {
 *     "id" = "id",
 *     "label" = "title",
 *     "uuid" = "uuid",
 *     "owner" = "uid",
 *   },
 *   links = {
 *     "collection" = "/admin/content/notification-user",
 *     "add-form" = "/notification-user/add",
 *     "canonical" = "/notification-user/{my_module_notification_user}",
 *     "edit-form" = "/notification-user/{my_module_notification_user}/edit",
 *     "delete-form" = "/notification-user/{my_module_notification_user}/delete",
 *     "delete-multiple-form" =
 *   "/admin/content/notification-user/delete-multiple",
 *   },
 *   field_ui_base_route = "entity.my_module_notification_user.edit_form",
 * )
 */
final class MyModuleNotificationUser extends ContentEntityBase implements MyModuleNotificationUserInterface {

  use EntityChangedTrait;
  use EntityOwnerTrait;

  /**
   * {@inheritdoc}
   */
  public function preSave(EntityStorageInterface $storage): void {
    parent::preSave($storage);
    if (!$this->getOwnerId()) {
      // If no owner has been set explicitly, make the anonymous user the owner.
      $this->setOwnerId(0);
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function baseFieldDefinitions(EntityTypeInterface $entity_type): array {
    $fields = parent::baseFieldDefinitions($entity_type);
    $fields += self::ownerBaseFieldDefinitions($entity_type);

    $fields['title'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Title'))
      ->setRequired(TRUE)
      ->setSetting('max_length', 255)
      ->setDisplayOptions('form', [
        'type' => 'string_textfield',
        'weight' => -5,
      ])
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayOptions('view', [
        'label' => 'hidden',
        'type' => 'string',
        'weight' => -5,
      ])
      ->setDisplayConfigurable('view', TRUE);

    $fields['uid'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Author'))
      ->setSetting('target_type', 'user')
      ->setDefaultValueCallback(self::class . '::getDefaultEntityOwner')
      ->setDisplayOptions('form', [
        'type' => 'entity_reference_autocomplete',
        'settings' => [
          'match_operator' => 'CONTAINS',
          'size' => 60,
          'placeholder' => '',
        ],
        'weight' => 1,
      ])
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'author',
        'weight' => 1,
      ])
      ->setDisplayConfigurable('view', TRUE);

    $fields['my_module_notification'] = BaseFieldDefinition::create('entity_reference')
      ->setLabel(t('Notification'))
      ->setRequired(TRUE)
      ->setCardinality(1)
      ->setSetting('target_type', 'my_module_notification')
      ->setDisplayOptions('form', [
        'type' => 'options_select',
        'weight' => 2,
      ])
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'entity_reference_label',
        'weight' => 2,
      ])
      ->setDisplayConfigurable('view', TRUE);

    $fields['status'] = BaseFieldDefinition::create('list_string')
      ->setLabel(t('Status'))
      ->setSettings([
        'allowed_values' => [
          MyModuleNotificationUserInterface::UNREAD => t('Unread'),
          MyModuleNotificationUserInterface::READ => t('Read'),
          MyModuleNotificationUserInterface::ARCHIVED => t('Archived'),
        ],
      ])
      ->setDefaultValue('unread')
      ->setRequired(TRUE)
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'string',
        'weight' => 3,
      ])
      ->setDisplayOptions('form', [
        'type' => 'options_select',
        'weight' => 3,
      ])
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayConfigurable('view', TRUE);

    $fields['created'] = BaseFieldDefinition::create('created')
      ->setLabel(t('Authored on'))
      ->setDescription(t('The time that the my_module_notification_user was created.'))
      ->setDisplayOptions('view', [
        'label' => 'above',
        'type' => 'timestamp',
        'weight' => 10,
      ])
      ->setDisplayConfigurable('form', TRUE)
      ->setDisplayOptions('form', [
        'type' => 'datetime_timestamp',
        'weight' => 10,
      ])
      ->setDisplayConfigurable('view', TRUE);

    $fields['changed'] = BaseFieldDefinition::create('changed')
      ->setLabel(t('Changed'))
      ->setDescription(t('The time that the my_module_notification_user was last edited.'));

    return $fields;
  }

  /**
   * {@inheritdoc}
   */
  public function getType(): string {
    return $this->bundle();
  }

  /**
   * {@inheritdoc}
   */
  public function getTitle(): string {
    return $this->get('title')->value;
  }

  /**
   * {@inheritdoc}
   */
  public function setTitle($title): static {
    $this->set('title', $title);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getCreatedTime(): int {
    return $this->get('created')->value;
  }

  /**
   * {@inheritdoc}
   */
  public function setCreatedTime($timestamp): static {
    $this->set('created', $timestamp);
    return $this;
  }

}

```

# Answer
**\- Create an Access Control Handler.**

```
  // src/MyModuleNotificationUserAccessControlHandler.php

namespace Drupal\my_module_notification;

use Drupal\Core\Entity\EntityAccessControlHandler;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;

/**
 * Defines the access control handler for the MyModuleNotificationUser entity type.
 */
class MyModuleNotificationUserAccessControlHandler extends EntityAccessControlHandler {

  /**
   * {@inheritdoc}
   */
  protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) {
    // Check the operation being performed.
    switch ($operation) {
      case 'update':
        // Check if the user has the 'edit title' or 'edit status' permission.
        if (
          ($account->hasPermission('edit title') && $entity->access('update', $account, TRUE)) ||
          ($account->hasPermission('edit status') && $entity->access('update', $account, TRUE))
        ) {
          return AccessResult::allowed();
        }
        return AccessResult::forbidden();

      // Add additional cases for other operations if needed.

      default:
        // For other operations, fall back to the default checkAccess method.
        return parent::checkAccess($entity, $operation, $account);
    }
  }
}

```

**\- Define Permissions my\_module\_notification.permissions.yml**

**\- Then use the Access Control Handler in my\_module\_notification.module**

```
/**
 * Implements hook_entity_type_alter().
 */
function my_module_notification_entity_type_alter(array &$entity_types) {
  // Update the access control handler for MyModuleNotificationUser entities.
  $entity_types['my_module_notification_user']->setHandlerClass('access', MyModuleNotificationUserAccessControlHandler::class);
}

```
> 0 votes

# Answer
Since the entity defined from your module has an access handler (`Drupal\my_module_notification\MyModuleNotificationUserAccessControlHandler`), it is sufficient to implement `checkFieldAccess()` in that class, similarly to what the `NodeAccessControlHandler` class does with its `checkFieldAccess()` method. Do not confuse it with the `checkAccess()` and `access()` methods, which are called for operations on the entity itself, not on the entity fields.

```
  // Only users with the administer nodes permission can edit administrative
  // fields.
  $administrative_fields = [
    'uid',
    'status',
    'created',
    'promote',
    'sticky',
  ];
  if ($operation == 'edit' && in_array($field_definition->getName(), $administrative_fields, TRUE)) {
    return AccessResult::allowedIfHasPermission($account, 'administer nodes');
  }

  // No user can change read only fields.
  $read_only_fields = [
    'revision_timestamp',
    'revision_uid',
  ];
  if ($operation == 'edit' && in_array($field_definition->getName(), $read_only_fields, TRUE)) {
    return AccessResult::forbidden();
  }

  // Users have access to the revision_log field either if they have
  // administrative permissions or if the new revision option is enabled.
  if ($operation == 'edit' && $field_definition->getName() == 'revision_log') {
    if ($account->hasPermission('administer nodes')) {
      return AccessResult::allowed()->cachePerPermissions();
    }
    return AccessResult::allowedIf($items->getEntity()->type->entity->shouldCreateNewRevision())->cachePerPermissions();
  }
  return parent::checkFieldAccess($operation, $field_definition, $account, $items);

```

That method is called by the `fieldAccess()` method implemented by the `EntityAccessControlHandler` class, which usually is the parent class used by access control handlers.

```
  $account = $this->prepareUser($account);

  // Get the default access restriction that lives within this field.
  $default = $items ? $items->defaultAccess($operation, $account) : AccessResult::allowed();

  // Explicitly disallow changing the entity ID and entity UUID.
  $entity = $items ? $items->getEntity() : NULL;
  if ($operation === 'edit' && $entity) {
    if ($field_definition->getName() === $this->entityType->getKey('id')) {
      // String IDs can be set when creating the entity.
      if (!($entity->isNew() && $field_definition->getType() === 'string')) {
        return $return_as_object ? AccessResult::forbidden('The entity ID cannot be changed.')->addCacheableDependency($entity) : FALSE;
      }
    }
    elseif ($field_definition->getName() === $this->entityType->getKey('uuid')) {
      // UUIDs can be set when creating an entity.
      if (!$entity->isNew()) {
        return $return_as_object ? AccessResult::forbidden('The entity UUID cannot be changed.')->addCacheableDependency($entity) : FALSE;
      }
    }
  }

  // Get the default access restriction as specified by the access control
  // handler.
  $entity_default = $this->checkFieldAccess($operation, $field_definition, $account, $items);

  // Combine default access, denying access wins.
  $default = $default->andIf($entity_default);

  // Invoke hook and collect grants/denies for field access from other
  // modules.
  $grants = [];
  $this->moduleHandler()->invokeAllWith('entity_field_access', function (callable $hook, string $module) use ($operation, $field_definition, $account, $items, &$grants) {
    $grants[] = [
      $module => $hook($operation, $field_definition, $account, $items),
    ];
  });

  // Our default access flag is masked under the ':default' key.
  $grants = array_merge([':default' => $default], ...$grants);

  // Also allow modules to alter the returned grants/denies.
  $context = [
    'operation' => $operation,
    'field_definition' => $field_definition,
    'items' => $items,
    'account' => $account,
  ];
  $this->moduleHandler()->alter('entity_field_access', $grants, $context);
  $result = $this->processAccessHookResults($grants);
  return $return_as_object ? $result : $result->isAllowed();

```
> 0 votes

---
Tags: entities
---


# Question
Title: How to debug Drupal especially when no webserver logs and PHP logs are available?
In one case I got in all webpages of my website this exception (error):

> An unexpected error occurred. Please try again later

At first, I didn't know how to debug this but after doing some reading I have checked the SQL table `watchdog` and there I only saw a few **"Page not found"** errors without further detailing, but after running `vendor/bin/drush watchdog:show` I got this

```
------- -------------- ---------------- ---------- -------------------------------------------------------------------------------------------
  ID      Date           Type             Severity   Message
 ------- -------------- ---------------- ---------- -------------------------------------------------------------------------------------------
  85934   16/Dec 17:21   php              error      Twig\Error\SyntaxError: Unknown "endif" tag. in Twig\Parser->subparse() (line 37 of
                                                     /home/USERNAME/WEB_APPLICATION_ROOT/example.com/web/themes/contrib/basic/templates/layout/html.html.twig).

```

This helped me to debug the problem by editing the aforementioned Twig file.

How to debug Drupal especially when no webserver logs and PHP logs are available? From `watchdog` table? From `drush`? from both?

# Answer
> 2 votes
The error message is so generic you can't even tell whether it's a PHP or Drupal error, both look the same. The error message is displayed by all production websites when something goes wrong. It doesn't tell any details not to divulge non-public information. You would check the log files if you get a report of such an error in a production environment.

In a dev environment, you usually want to see the error messages on the screen immediately.

For Drupal visit /admin/config/development/logging and set

**Error messages to display:**

* All messages, with backtrace information

## Set full error messages for a specific IP only

In the comments it was asked what can be done in a production environment to not show error messages to all site visitors while debugging the error. An easy option would be to override the UI config value via settings.php

*web/sites/default/settings.php*

```
if ($_SERVER['REMOTE_ADDR'] == '192.168.12.34') {
  $config['system.logging']['error_level'] = 'verbose';
}

```

and restrict it to your IP so that everybody else is still seeing the generic error message as configured in UI.

## Drupal 10.2.x

Updated a site to the just released Drupal 10.2.0 and noticed the error logging has changed. You get now more information from the error logs with the default production settings. See the change record https://www.drupal.org/node/3340781

---
Tags: debugging
---


# Question
Title: Trait 'Drupal\entity\Form\EntityDuplicateFormTrait' not found, while setting up existing site
I'm in the process of attempting to setup a development environment for a Drupal site that's been provided by a vendor. From what I've been told, the sites been setup and running on our UAT (Ubuntu) machine following a pretty simple set of directions:

* git clone
* restore database
* start services
* `drush cim -y`
* clear cache

(these may not be the actual steps, just want I was give).

The repository is updated with all composer dependencies, so my understanding is just pulling the same code and attempting to start it should work. So from my understanding (albeit limited composer/PHP experience) there should be no need for `composer install/update` as the folder structure should not need any changes.

I was instructed (or hinted) that setting up using Lando would be better for our team, than attempting to:

* setup individual services
* using AMPPS or something along those lines

which makes sense. I don't think there is anything really wrong with the Lando installation, but who knows at this point (PHP environments are not my strong suit).

Here is a copy of my lando.yml:

* composer 1 is required, we were given dependencies that don't work with composer 2
* Drush is working with this configuration

```
name: drupal8
recipe: drupal8
config:
  webroot: docroot
  composer_version: '1.10.17'
tooling:
  drush:
    service: appserver
    cmd: php /app/vendor/drush/drush/drush --root=/app/docroot

```

After following the steps above:

* Git clone
* lando start (at this point lando has no issues)
* lando db-import db/db-extract.sql (again no issues)
* `lando drush cim -y`

I get the following (which is the same as if I go to the browser):

```
> lando drush cim -y
PHP Fatal error:  Trait 'Drupal\entity\Form\EntityDuplicateFormTrait' not found in /app/docroot/modules/contrib/commerce/modules/number_pattern/src/Form/NumberPatternForm.php on line 16

Fatal error: Trait 'Drupal\entity\Form\EntityDuplicateFormTrait' not found in /app/docroot/modules/contrib/commerce/modules/number_pattern/src/Form/NumberPatternForm.php on line 16
 [warning] Drush command terminated abnormally.

```

I found the following:

https://www.drupal.org/project/commerce/issues/3051402 (from 2015)

Which points at the issue being dependency related. So essentially we have:

* drupal/commerce being installed as a dependency of drupal/commerce\_print. It's not actually listed in the project main dependencies (unsure if this is an issue). It's being installed as `drupal/commerce:2.24.0`
* drupal/entity and drupal/state\_machine were configured as versions `dev-1.x` which (the link above) said was not good. I've since updated ONLY these two so that they are being installed as `drupal/entity:1.0.0-rc2` and `drupal/state_machine:1.0.0`

After doing so, I'm still receiving the issue regarding `PHP Fatal error: Trait 'Drupal\entity\Form\EntityDuplicateFormTrait' not found in /app/docroot/modules/contrib/commerce/modules/number_pattern/src/Form/NumberPatternForm.php on line 16` even though I can confirm that it is actually available with `1.0.0-rc2`.

I'm assuming again that the DB is up to date, since it was provided to us to match the current repository (which I can only assume is right since again, it's working on UAT with none of these changes).

From what I've seen/read, these types of issues:

* Are not actually what the problem is, and it's actually something else but throwing this nonesensical error. If this is the case, how does one go about finding the actual error?
* There are still issues with dependencies installed, but again the UAT environment works out of the box with git clone, so why wouldn't these versions (in composer.lock) work locally?

Some other things that I've confirmed:

* I'm able to connect to SQL through `lando drush sqlc`, so I assume that the `$databases['default']['default']` is configured correctly. Please correct me if I'm wrong, but I've provided the database configuration settings from my `local.settings.php` file:

```
$lando = json_decode(getenv('LANDO_INFO'), TRUE);
$databases['default']['default'] = [
  'database' => $lando['database']['creds']['database'],
  'username' => $lando['database']['creds']['user'],
  'password' => $lando['database']['creds']['password'],
  'prefix' => '',
  'host' => $lando['database']['internal_connection']['host'],
  'port' => $lando['database']['internal_connection']['port'],
  'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql',
  'driver' => 'mysql',
  // For whatever reason this won't work, even on docker/Lando.  Future kens problem.
  //'unix_socket' => '/opt/bitnami/mysql/tmp/mysql.sock',
];

```

I've attempted setting up the project without restoring the database. So following the steps:

* git clone
* lando start
* drupal8.lndo.com:8000

which would always fail on the third step of the installation. It would result in the dependency of `bartik_bartic_menu` or something like that. I can start from scratch and provide more information if it's required.

Any help would be appreciated; I apologize in advance if I've overlooked providing something or completely missed the mark on following some basic steps on getting Drupal up and running.

**Edit**

I'm unsure if this matters, but here are the PHP modules installed in the Lando environment. If it's at all possible that there is PHP Trait or Autoloading functionality that doesn't work due to them (default Lando Drupal8 recipe):

```
[PHP Modules]
apcu
bcmath
bz2
calendar
Core
ctype
curl
date
dom
exif
fileinfo
filter
ftp
gd
gettext
hash
iconv
imagick
imap
intl
json
ldap
libxml
mbstring
memcached
mysqli
mysqlnd
OAuth
openssl
pcntl
pcre
PDO
pdo_mysql
pdo_pgsql
pdo_sqlite
Phar
posix
readline
redis
Reflection
session
SimpleXML
soap
sodium
SPL
sqlite3
standard
tokenizer
xml
xmlreader
xmlwriter
Zend OPcache
zip
zlib

[Zend Modules]
Zend OPcache

```

**Composer Autoload**

One other thing I've noticed while looking around, there are files generated:

* `vendor/autoload.php`
* `docroot/autoload.php`

But there is no `autoload` section within the `composer.json` file. Although I'm assuming it works, or else there would probably be way more earlier Cannot find class errors?

In comparison to what is working, there is no `autoload` section in the working `composer.json` so I'm not sure, here is the EXTRA section, which provides some drupal specific configuration (I guess) if it's helpful:

* setup.sh does some stuff with Docker when a .dockerfile is there (not the case)
* The rest, I have zero idea.

```
    "extra": {
        "scripts-dev": {
            "post-install-cmd": [
                "sh ./scripts/tools/setup.sh"
            ]
        },
        "drupal-scaffold": {
            "locations": {
                "web-root": "./docroot"
            },
            "file-mapping": {
                "[web-root]/.htaccess": {
                    "mode": "replace",
                    "path": "docroot/.htaccess",
                    "overwrite": false
                }
            }
        },
        "merge-plugin": {
            "include": [
                "docroot/modules/custom/*/composer.json"
            ],
            "recurse": true,
            "replace": false,
            "ignore-duplicates": false,
            "merge-dev": true,
            "merge-extra": false,
            "merge-extra-deep": false,
            "merge-scripts": false
        },
        "installer-paths": {
            "docroot/core": [
                "type:drupal-core"
            ],
            "docroot/modules/contrib/{$name}": [
                "type:drupal-module"
            ],
            "docroot/profiles/contrib/{$name}": [
                "type:drupal-profile"
            ],
            "docroot/themes/contrib/{$name}": [
                "type:drupal-theme"
            ],
            "docroot/libraries/{$name}": [
                "type:drupal-library"
            ],
            "drush/contrib/{$name}": [
                "type:drupal-drush"
            ]
        },
   }

```

**Edit May 10**

I was able to get the basic/standard install going by removing the Database configuration from the `settings.php` file and running the install from scratch. Once this was completed I replaced the autogenerated database config with the above config for lando; after restarting the site basic/standard site is up and running.

I then ran `lando db-import` as mentioned above and that completed successfully.

Sadly, I'm still back to the error message (above).

**Edit May 10 x 2**

Ok so apparently `lando composer install` works "correctly" and there are no issues, but when looking into the structure a little more (I apologize, I should have done this way earlier) the `docroot/modules/contrib/entity` folder is empty.

I'm not sure if this is an issue with:

* Composer
* Composer drupal scaffold
* Etc.

If I delete the `/vendor` folder, which this module isn't even a part of, it will do the full install again, but it again leaves the `docroot/modules/contrib/entity` folder empty.

Looking in the repository, there isn't even a `docroot/modules/contrib/entity` folder. After:

* Removing the entity folder
* Re-running `lando composer install` the entity folder is now populated and the `EntityDuplicateFormTrait` is there

After attempting to clear cache or access the site I'm now getting the error

```
PHP Fatal error:  Uncaught Drupal\Component\Plugin\Exception\PluginNotFoundException: The "state" plugin does not exist. Valid plugin IDs for Drupal\Core\Field\FieldTypePluginManager are: address, address_country, address_zone, color_field_type, comment, commerce_plugin_item:commerce_condition, commerce_plugin_item:commerce_promotion_offer, commerce_plugin_item:commerce_shipping_method, commerce_remote_id, commerce_adjustment, commerce_price, commerce_shipment_item, commerce_tax_number, datetime, daterange, entity_reference_revisions, file, file_uri, google_map_field, image, layout_section, link, metatag, list_float, list_integer, list_string, path, physical_dimensions, physical_measurement, redirect_source, telephone, text, text_long, text_with_summary, webform, yoast_seo, boolean, changed, created, decimal, email, entity_reference, float, integer, language, map, password, string, string_long, timestamp, uri, uuid in /app/docroot/core/lib/Drupal/Component/Plugin/Discovery/DiscoveryTrait.php:53

```

**Edit May 10x3**

So after playing around a little more, it looks like NONE of the dependencies in the provided composer.json file that are listed as `1.x-dev` have been installed. They are also missing in the git repository, even though every other single dependency folder is there and complete.

So somehow:

* They are not in the .gitignore file
* They are all listed as `1.x-dev` regardless of whether this is good practice or not (which I doubt)
* And they are all missing in the repository and require an extra delete/install step to get them working.

I've done a quick search for this, but can't find anything meaningful in the short time, if someone can point me in the right direction I'd be greatful.

# Answer
> 1 votes
This may not be the appropriate answer, but the environment is up and running now (partially due to my own inexperience) but I thought I should share. So the problem was indeed as simple as running `lando composer install` a number of times.

* Checkout project from git. The git project contains ALL the vendor dependencies **except** the three: `drupal/poll`, `drupal/state_machine` and `drupal/entity`. Composer is git cloning modules instead of download them?
* Running `lando composer install` updates these with blank folders and says that it completes successfully

from here, I needed to:

* Individually delete the `docroot/modules/contrib/entity|poll|state_machine` directories and run `lando composer install` again

at this point I had these three folders containing their respective projects. Finally I was able to run `lando drush cr` without errors. After which, I was able to access the drupal site home page.

I have more testing to do, in figuring out how to streamline the dev environment and `settings.php` file(s) but at lease this issue seems fixed.

# Answer
> 0 votes
If you are not installing commerce via composer, then you still need the dependencies to commerce

Commerce has a composer.lock file

```
    "require": {
    "php": "^8.0",
    "ext-bcmath": "*",
    "drupal/core": "^9.3 || ^10",
    "drupal/address": "^1.7",
    "drupal/entity": "^1.0",
    "drupal/entity_reference_revisions": "~1.0",
    "drupal/inline_entity_form": "^1.0@RC",
    "drupal/profile": "^1.2",
    "drupal/state_machine": "^1.5",
    "drupal/token": "^1.7",
    "commerceguys/intl": "^2.0.2"
},

```

The composer merge plugin can be used, with this exerpt from composer.json showing the included composer file

```
    "extra": {
    "merge-plugin": {
        "include": [
            "web/modules/patched/commerce/composer.json"
        ]
    },

```

---
Tags: commerce, composer
---


# Question
Title: Finding all nodes not referenced by a menu
I see this question was asked for Drupal 7, but the menu\_link table doesn't exist in Drupal 9.

Is there a SQL query for listing all nodes that are not assigned to a menu?

# Answer
> 0 votes
I ended up installing Menu Entity Index https://www.drupal.org/project/menu\_entity\_index which added a database table and views integration. This allowed me to set up a view listing all nodes of specific content types not in selected menus.

Once that module has been enabled and configured, the table it adds is created. I used the SQL query below.

```
$query = 'SELECT * FROM `node_field_data` t1
WHERE (t1.status = 1) AND
    (t1.type = "article"
    OR t1.type = "page"
    OR t1.type = "content_type_x"
    OR t1.type = "webform") AND
NOT EXISTS (SELECT 1 FROM menu_entity_index t2 WHERE t2.target_id = t1.nid)
ORDER BY "node_field_data_created" DESC';

```

This selects all node data where the content type is article, page, etc. and the node.nid column is not found in the target\_id column of the menu entity index table.

# Answer
> 2 votes
In Drupal 9 can use this 'menu\_link\_content\_data' table to find whether node has any reference as menu:

Snippet code for finding all nodes not referenced by a// Get all nodes.

```
$nids = \Drupal::entityQuery('node')
      ->execute();
if (isset($nids) && !empty($nids)) {
  foreach($nids as $nid){
    // Check nid has reference as menu
    $rst =  \Drupal::database()->select('menu_link_content_data', 'mn')
      ->fields('mn')
      ->condition('mn.link__uri', 'entity:node/'.$nid, '=')
      ->execute()
      ->fetchAll();
    // If nid has no reference as menu then insert nid in $nodes[]
    if(empty($rst)){
     $nodes[] = $nid;
    }
  }
}

```

---
Tags: database, 9, navigation
---


# Question
Title: How can I use material symbols in the search bar submit input?
# Background

I have a Drupal 10 site with a search bar that appears on the front page. Using this search bar redirects the user to a search page with a similar search bar, underneath which are the results. By default, both of these search bars just have the word "search".

I added the material-icons module and have successfully used material symbols on other parts of the site, but I do not know how to do this with the submit input in the search bar.

I found a similar question where the user added a fontawesome icon, and the solution (to use unicode) is not applicable for me.

# Attempts

## Attempt 1

I had a partially successful attempt by creating a module with a function that alters the search form:

```
<?php
use Drupal\Core\Render\Markup;

function search_bar_icon_form_search_block_form_alter(&$form, &$form_state) {
  // Set a placeholder for the search input
  $form['keys']['#attributes']['placeholder'][] = t('Search');

  // Replace the submit button with a Material Icon
  $icon_html = '<span class="material-symbols-rounded">search</span>';
  $form['actions']['submit']['#markup'] = Markup::create($icon_html);

  // Optionally, you can hide the original submit button text
  $form['actions']['submit']['#value'] = '';
}

```

This solution is not ideal because html input does not allow children, so instead, it places the span as the input's sibling. This requires CSS to pad the empty input and translate the sibling over the empty button. This solution feels messy and overly complicated, so I wanted to explore better options before I go with this.

## Attempt 2

I attempted to create a twig template in my custom theme to override the way that the search bar is being generated. To do this, I went into the `core/modules/search/templates` to locate the default template, however there was only one template named `search-result.html.twig` which renders single search results, not the search bar itself. I cannot find the correct default twig template so I don't know what to name my custom one to override the styling.

My goal with this attempt is to replace the input with a button that can hold an icon if possible.

# Summary

I feel like adding an icon shouldn't be so complicated and I must be missing something obvious. What is the best way to theme my search bar so that it shows a material symbols magnifying glass instead of the word "search"?

# Answer
> 0 votes
I found a way to do this in a relatively simple way.

This answer presumes you are using the Material Icons module, although I configured this module to use Rounded Material Symbols instead. I also created a custom module and `form_search_block_form_alter` to alter the search block form and add in this icon.

## Custom Module

The form alter code is housed in a custom module comprised simply of an `info.yml` file, and the `.module` file containing the code.

```
.
└── web/
    └── modules/
        └── custom/
            └── search_bar_icon/
                ├── search_bar_icon.info.yml
                └── search_bar_icon.module

```

Once the module is created, make sure you install it in the Extend menu.

## Form Alter

In order for an element to use a Material Symbol, it must be assigned a `material-symbols` class, and the content of the element must be the name of the required icon. In my case the search `form-submit` button already had "search" as the content, which is the name of the material symbol I wanted to use, so all I had to do was add the `material-symbols-rounded` class to this button.

```
<?php
use Drupal\Core\Render\Markup;

function search_bar_icon_form_search_block_form_alter(&$form, &$form_state) {
  // Set a placeholder for the search input
  $form['keys']['#attributes']['placeholder'][] = t('Search');

  // Add a class to the submit button
  $form['actions']['submit']['#attributes']['class'][] = 'material-symbols-rounded';
}

```

This resulted in the "search" text being replaced by the Material Symbols magnifying glass.

---
Tags: theming, 10
---


# Question
Title: "Change value" action in Views Bulk Operations?
I'm moving from D7 to D10. One of my favourite and much needed things in D7 was "Change value" action in the VBO administration view. It was quite efficient way to editing many nodes at once.

I think I have checked everything here in D10 and I haven't found any easy solution.

Could you help me, please? Thanks!

Drupal core 10.2.0 Views Bulk Operations (VBO) 4.2.5

Szy.

# Answer
Almost immediately after posting I've found the module:

https://www.drupal.org/project/views\_bulk\_edit
> 1 votes

---
Tags: views, 10
---


title: Indexing PDF files without java

text:
# Question
Title: Indexing PDF files without java
Is there a way to index PDF files so that they can be searched (the text inside the PDF) without using java? Our host refuses any software that requires java, and every module I've found has required it. Since the host refuses anything that uses java, I haven't been able to find a solution that works.

# Answer
> 0 votes
In theory, you should be able to use a combination of the following modules to index PDF content, without any external dependencies:

The third module needs a patch to make it compatible with Drupal 10. If you don't already have it in your composer.json, you'll need to install and allow the Drupal Lenient composer plugin to require the module without errors.

There are some simple instructions on setting up the attachments module here, you would choose the "PHP" extractor instead of "Solr".

It's worth bearing in mind that more advanced search solutions aren't usually based on PHP/SQL, for performance reasons. If you're indexing a lot of documents, you might see some degradation. If that happens you'll probably need to consider implementing/purchasing a more purpose-built solution (like a Solr instance).

---
Tags: search
---


# Question
Title: Drush 12 Installation in Hosting Environments
I'm running a Drupal hosting server, where the client's installation is below the directory

```
/var/www/<client-id>

```

The full path to any of the client's websites is

```
/var/www/<client-id>/htdocs/<drupal root>/web

```

The client is provided with a login to his *client-id* directory, where .bashrc lives, which sets the PATH to the vendor/bin directory -- but you can provide only one path per client, when a different PATH per *drupal root* would be needed.

**How can I set up drush or bash to use the proper *drupal root* comfortably?**

I know I could provide a script at *drupal root*, but if the client jumps between various *drupal root* installations, it might become cumbersome to also run the script before calling drush in the other *drupal root*.

Before 12, things where focused on *drupal root* and everything worked without any issues, as long as the user was in `<drupal root>/web`

---

Upgrade after I found out what bugged me in the first place:

With drush \< 12, I could issue a drush command in any directory at or below 'web-root'. With a path ./vendor/bin, I will constrain myself to use drush from web-root only.

Is there a better way to solve this properly in a multi-site client installation as described above?

# Answer
AFAIK Drush is built to work from `./drupal_root`, not from `./drupal_root/web`.

I'm using a similar setup to your's (not nested as deep though), my *current directory relative* path variable using the leading dot `./vendor/bin/` still works inside the respective drupal project roots:

My .bashrc looks like `PATH="/some/other/stuff:./vendor/bin:$PATH"`
> 2 votes

---
Tags: drush
---


# Question
Title: Trying to display my media field image in WYSIWYG via twig
So I have several images that need to be displayed under a certain image style. I basically made a custom media display view mode, selected an image in the WYSIWYG for a particular page using this display, and now I want to add a class in Twig...

I made a media.html.twig file, renamed it to the custom image style I created in Drupal, and now I'm trying to get the image to display along with the class. In Media \> Manage Display, I set the format of my image to URL to image. Now trying to at least print the image before I add the class in TWIG.

{{media.field\_media\_image.entity.uri.value}}

This code is returning public://image.jpg, but not giving me the full image URL nor printing the exact image I need.

Help appreciated.

# Answer
For get full image url try this syntax this is worked for me:

```
{{ file_url(media.field_media_image.entity.fileuri) }}

```
> 0 votes

---
Tags: wysiwyg
---


# Question
Title: Impossible to change admin password, script gives me different hash every time (???)
Context: Drupal 9.14 installed on a small EC2 server for testing

After a composer update, my admin password stopped working (first strange event).

I decided to use the password-hash.sh to recreate one and update it directly in my MySQL database. But... it didn't work. And I noticed this strange behavior. The script is giving me a different hash each time I'm using it (if the same logic is used at login, no wonder I can't log)

```
ubuntu$ ./core/scripts/password-hash.sh "vincent" "vincent" "vincent" "vincent"

password: vincent               hash: $S$EopakPbMJyAFAGO.3sKQy5xNBSJ9Msv87Ox4fNw8hpx2SO36HWX/

password: vincent               hash: $S$EJyg6.fdWP5F7.nVhoZ7dcw/P5yxz4UEP7fFQ2yhoQu/NwzxFhu1

password: vincent               hash: $S$EO.OiLMVtIU2RwRddeXXDr.Yw288BsXK3kPIR0LrRzYbgrzltzD8

password: vincent               hash: $S$E78EyKNvURFUqEIiukjFpWR3mgSsIRyOt1PLDQGmFyIXbw5zVGvB

```

Is it normal? If not, how to solve this?

# Answer
> 0 votes
`password-hash.sh` is used to generate a hash from a plaintext password.
Each time it is executed, it returns a different value because it uses a different salt each time. That is not a problem, since the salt is part of the hash itself, which means that Drupal core is able to verify the password matches the hash.

---
Tags: authentication-authorization
---