# Question
Title: How to dynamically manipulate canonical entity URLs
I've got a project with a multi-user / multi-domain environment. There is one "main" portal like `www.myproject.com` and for every registered user with a specific custom permission `has_personal_subdomain` there is a subdomain and a theme with his username, e.g. user `johndoe` has a subdomain `johndoe.myproject.com` and this domain is using his custom theme `johndoe`.

I've already set up all the domains and the theme switching part. All editing/administrative actions are always done on the main portal domain, also users with personal subdomains work on the main portal domain.

The problem are the canonical entity links: When editing nodes, editing is always done in admin theme on the main portal domain, so of course all canonical entity links also point to the portal domain. E.g. for users with a personal subdomain, the canonical links to `www.myproject.com/node/123` or `www.myproject.com/my-node-url-alias`, but I want `johndoe.myproject.com/node/123` or `johndoe.myproject.com/my-node-url-alias`.

What is the best way to dynamically manipulate the canonical entity view links? I'm not asking for a complete code, I'm just looking for the best starting point/hook/event/service to inject my custom logic.

What I want to avoid:
Create a million custom hooks for `hook_menu_local_tasks_alter` AND `hook_form_alter` AND `hook_views_pre_render` AND whatever else...

I do not care about metatags, SEO, duplicate URLs,... I just want a better editor experience with personalized entity view links pointing to the correct subdomain using the correct theme.

I know there is the OutboundPathProcessor, but this seems a bit over the top, as I only want to change canonical URLs and none of the admin links.

Is there a service specifically for generating canonical URLs I can decorate?

# Answer
> Is there a service specifically for generating canonical URLs I can decorate?

Yes, for a route based handling of outgoing canonical links you can use OutboundRouteProcessorInterface::processOutbound

For example like core implements the dynamic route `<current>`.

*/core/lib/Drupal/Core/RouteProcessor/RouteProcessorCurrent.php*

```
  public function processOutbound($route_name, Route $route, array &$parameters, BubbleableMetadata $bubbleable_metadata = NULL) {
    if ($route_name === '<current>') {
      if ($current_route = $this->routeMatch->getRouteObject()) {
        $requirements = $current_route->getRequirements();
        // Setting _method and _schema is deprecated since 2.7. Using
        // setMethods() and setSchemes() are now the recommended ways.
        unset($requirements['_method']);
        unset($requirements['_schema']);
        $route->setRequirements($requirements);

        $route->setPath($current_route->getPath());
        $route->setSchemes($current_route->getSchemes());
        $route->setMethods($current_route->getMethods());
        $route->setOptions($current_route->getOptions());
        $route->setDefaults($current_route->getDefaults());
        $parameters = array_merge($parameters, $this->routeMatch->getRawParameters()->all());
        if ($bubbleable_metadata) {
          $bubbleable_metadata->addCacheContexts(['route']);
        }
      }
      else {
        // If we have no current route match available, point to the frontpage.
        $route->setPath('/');
      }
    }
  }

```
> 2 votes

# Answer
I've finally a working version using `OutboundPathProcessor`.

(I was not able to use `OutboundRouteProcessor` as suggested by @4k4 because I did not know how to create absolute, cross-domain URL manipulations in there).

This is my code (`wt_dgm` is my module name):

`wt_dgm.services.yml`
(I need `@current_user` and `@config.factory` for my custom business logic, replace or remove by your needs)

```
  wt_dgm.path_outbound_host_processor:
    class: Drupal\wt_dgm\Path\OutboundHostProcessor
    arguments: [ '@current_user', '@config.factory']
    tags:
      - { name: path_processor_outbound, priority: 3000 }

```

`OutboundHostProcessor.php`

For my use case the key thing was using `$options['absolute']` and `$options['base_url']`, replace with your needs.

```
<?php

namespace Drupal\wt_dgm\Path;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\Url;
use Drupal\node\Entity\Node;
use Symfony\Component\HttpFoundation\Request;

class OutboundHostProcessor implements OutboundPathProcessorInterface {

  /**
   * @var $currentUser AccountProxyInterface
   */
  protected $currentUser;

  /**
   * @var $configFactory ConfigFactoryInterface
   */
  protected $configFactory;

  /**
   * @var $frontendCache array['username' => 'scheme:://username.dgmdomain.com']
   */
  protected $frontendCache;

  public function __construct(AccountProxyInterface $current_user, ConfigFactoryInterface $config_factory) {
    $this->currentUser = $current_user;
    $this->configFactory = $config_factory;
    $this->frontendCache = [];
  }

  /**
   * {@inheritdoc}
   */
  public function processOutbound($path, &$options = array(), Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) {
    try {
      $url = Url::fromUri('internal:' .  $path);
    }
    catch (\Throwable $throwable) {
      return $path;
    }
    if (!$url) {
      return $path;
    }

    $routeName = $url->getRouteName();
    if ($routeName != 'entity.node.canonical') {
      return $path;
    }

    $params = $url->getRouteParameters();
    if (!isset($params['node'])) {
      return $path;
    }

    $node = $params['node'];
    if (!($node instanceof Node) && is_numeric($node)) {
      $node = Node::load($node);
    }
    if (!($node instanceof Node)) {
      return $path;
    }

    $userDomain = $this->getCustomHost($request);
    if (!$userDomain) {
      return $path;
    }

    $options['absolute'] = TRUE;
    $options['base_url'] = $request->getScheme() . '://' . $userDomain;

    if ($bubbleable_metadata) {
      $bubbleable_metadata->addCacheContexts([
        'user',
      ]);
    }
    return $path;
  }

  protected function getCustomHost(Request $request) {
    // my custom logic here with current user etc
    if ($my_custom_yes) {
      return $myCustomHostname;
    }
    return NULL;
  }
}

```
> 1 votes

---
Tags: uri
---


# Question
Title: Config import for certain items appears to fail. How do I fix this?
After synchronizing configuration from dev to staging, I would expect all changes to be imported and for a message to display, advising "There are no configuration changes to import"

Most changes have been imported but I am left with six items which seemed to have failed import. Or a least do not clear from the list of items for import.

I do not understand why the active configuration appears not be be updated by staged.

As well importing all, I have also attempted to import each item separately, without success.

As well as using the UI have also tried drush cim. Drush confirms that the configuration was imported successfully. But if I run drush cim again, the same 6 files remain.

Why does it appear that the import is failing and what should I do to fix it? Is my active configuration messed up somehow?

A screenshot and some code to illustrate the problem.

**Via the UI**

**Via Drush**

```
    [easable]:sync$ drush cim
    +------------+-----------------------------------------------------+-----------+
    | Collection | Config                                              | Operation |
    +------------+-----------------------------------------------------+-----------+
    |            | block.block.schoolpwa_subtheme_bottommenu           | Update    |
    |            | block.block.schoolpwa_subtheme_mainnavigation       | Update    |
    |            | block.block.mainmenu2                               | Update    |
    |            | block.block.websiteswitcher                         | Update    |
    |            | core.entity_view_display.node.page.default          | Update    |
    |            | media_bulk_upload.media_bulk_config.pdf_bulk_upload | Update    |
    +------------+-----------------------------------------------------+-----------+

     Import the listed configuration changes? (yes/no) [yes]:
     >

     [notice] Synchronized configuration: update block.block.schoolpwa_subtheme_bottommenu.
     [notice] Synchronized configuration: update block.block.schoolpwa_subtheme_mainnavigation.
     [notice] Synchronized configuration: update block.block.mainmenu2.
     [notice] Synchronized configuration: update block.block.websiteswitcher.
     [notice] Synchronized configuration: update core.entity_view_display.node.page.default.
     [notice] Synchronized configuration: update media_bulk_upload.media_bulk_config.pdf_bulk_upload.
     [notice] Finalizing configuration synchronization.
     [success] The configuration was imported successfully.
    [easable]:sync$ drush cim
    +------------+-----------------------------------------------------+-----------+
    | Collection | Config                                              | Operation |
    +------------+-----------------------------------------------------+-----------+
    |            | block.block.schoolpwa_subtheme_bottommenu           | Update    |
    |            | block.block.schoolpwa_subtheme_mainnavigation       | Update    |
    |            | block.block.mainmenu2                               | Update    |
    |            | block.block.websiteswitcher                         | Update    |
    |            | core.entity_view_display.node.page.default          | Update    |
    |            | media_bulk_upload.media_bulk_config.pdf_bulk_upload | Update    |
    +------------+-----------------------------------------------------+-----------+

     Import the listed configuration changes? (yes/no) [yes]:
     >

```

# Answer
I've found that this sort of thing is because the config in your sync directory is not quite legitimate. My guess is that this is an issue on your dev and local environments too, not just staging.

I've found that the cause of this issue is usually a result of one of two things:

**1. Configuration files were manipulated manually and the configuration in the file isn't possible.**

An example, looking at your pastebin, the change for *core.entity\_view\_display.node.page.default.yml* is to remove the created field from the list of hidden fields on that display. However, there's no configuration for how the field should be displayed. *A field can't be not displayed and also not hidden.* To maintain the integrity of the config, the system defaults a field to hidden when it's not in either place.

**2. Database updates were run, updates that change configuration, but configuration was not exported on the dev environment after the update.**

An example, looking at your pastebin, it looks like some new configuration options (with appropriate default values) were added. It also looks like a module changed from working with `true` and `false` to `1` and `0`.

**To resolve this issue:**

First go to your primary development environment. Then get your latest database of record, clear cache, and deploy (database updates, then import your configuration). The result should be the same thing you're seeing on staging, i.e. some configuration will appear to be not fully synced. This is expected (for the reasons described above). Next re-export your configuration, commit, and re-deploy.

**To avoid this issue in the future:**

1. Don't manipulate config files directly. If you do, test the configuration locally to make sure you got it right (this includes after resolving merge conflicts in config files).
2. Always export configuration after running database updates. Sometimes the updates change configuration.
> 3 votes

---
Tags: 8, configuration
---


# Question
Title: Limit views exposed filter options to terms with associated nodes of content type X
I have a module which limits the options in a views exposed filter to terms which have associated nodes. The code is based on this answer: How can I limit the exposed filters to only display values for which content exists?

**Code:**

```
if (isset($form['area-of-law']['#options'])) {

  foreach ($form['area-of-law']['#options'] as $key => $value) {

    if (is_numeric($key)) {
      $query = \Drupal::database()->select('taxonomy_index', 'ti');
      $query->fields('ti', ['nid']);
      $query->condition('ti.tid', $key);
      $nodes = $query->execute()->fetchAssoc();

      if (!$nodes) {
        unset($form['area-of-law']['#options'][$key]);
      }
    }
  }
}

```

This works, but all node types are returned. I would like to limit the filter further to only show those of content type X.

# Answer
> 0 votes
```
  $query = \Drupal::database()->select('taxonomy_index', 'ti');
  $query->condition('ti.tid', $key);
  $query->join('node', 'n', 'n.nid = ti.nid'); // join node table
  $query->condition('n.type', 'x'); // filter by type
  $query->fields('n', ['nid']); // add filtered nids
  $nodes = $query->execute()->fetchAssoc();

```

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


title: Get list of available updates

text:
# Question
Title: Get list of available updates
I need to be able to easily get a list of available updates for a site.

I've come across the update\_get\_update\_list() function, but that only returns an array of " information about database updates that need to be run, and any updates that are not going to proceed due to missing requirements." I want the list that gets displayed on the admin/modules/update page, but can't seem to find any way to do that through the core update module, and want to avoid using grep on a drush up -n call (as that feels kind of hacky.)

# Answer
> 5 votes
Assuming you need this info programatically, you may be looking for `update_calculate_project_data()`:

> Calculates the current update status of all projects on the site.
>
> The results of this function are expensive to compute, especially on sites with lots of modules or themes, since it involves a lot of comparisons and other operations. Therefore, we cache the results into the {cache\_update} table using the 'update\_project\_data' cache ID. However, since this is not the data about available updates fetched from the network, it is ok to invalidate it somewhat quickly. If we keep this data for very long, site administrators are more likely to see incorrect results if they upgrade to a newer version of a module or theme but do not visit certain pages that automatically clear this cache.

The return value is:

> An array of installed projects with current update status information.

update\_calculate\_project\_data() requires an argument of all the available/enabled modules.

Running this...

```
module_load_include('inc', 'update', 'update.report');
$available = update_get_available(TRUE);
$data = update_calculate_project_data($available);

```

... will leave $data as an array of all available/enabled modules, with their corresponding existing\_version, recommended, and latest\_version info. If only the modules with updates are necessary, they can be filtered out by comparing those versions.

# Answer
> 5 votes
As far as I'm concerned there are three ways you can achieve this:

1. You can use the Drupal UI `/admin/reports/updates`
2. You can use Drush `drush ups`
3. You can use a Third Party Service Drupal Status.

I wrote an article that explains how to integrate multiple Drupal website with Drupal Status.

# Answer
> 3 votes
Did you look here `/admin/reports/updates`?

# Answer
> 2 votes
In Drupal 8 the right way of managing sites is using composer.

On top of that Drush from version 9 have deprecated all the up commands pm-update, pm-updatecode and pm-updatestatus

Give both points, if you need to get programatically the list of updated modules you can use composer:

`composer outdated "drupal/*"`

Drush is still used to get the list of security udpates:

`drush pm:security`

See for more informatio: https://www.drupal.org/docs/updating-drupal/updating-modules-and-themes-using-composer

---
Tags: 7, updating
---


# Question
Title: How to programmatically import a block to existing view?
I need to programmatically **import a block to existing view with its settings**.

**I know how to export the whole view and import it programmatically.** But I don't want to export whole view and then import whole view. In my view there is 25 blocks, so it gives me 4000 lines of code to import. I can copy export code only of my new block. But **how to import only this part of code to the existing view**? Is that possible? I can't find any solutions.

# Answer
The indicated "block" you point out in the UI, is internally called a "display". Displays are stored inside the view object in the `display` property (e.g. `$view->display`).

If you look at the views export code you should see the pattern of exported blocks (e.g. `/* Display: Block 1 */`, `/* Display: Block 2 */`, etc.) that's rendered by the `views::export()` method.

For each block (i.e. display) you should see the `$handler = $view->new_display(...)` followed by many `$handler->display...` lines that add the relevant fields/filters for that display.

To programmatically import a single display you would do something like the following:

```
$view = views_get_view('my_existing_view');
// @TODO: Add my new display handler code
$view->save();

```
> 1 votes

---
Tags: 7, views, importing-exporting
---


# Question
Title: On entity save, identify the user fields that have been changed
I have a need to track changes to users in a separate database, apart from Drupal. When a user changes their email address or their password, I need to communicate that change with a shadow table in MSSQL.

I've created an event and listener. The event is triggered by hook\_entity\_update. That works fine, but I don't know what was changed in that request. Is there a way to pass the value that was changed to that event listener?

# Answer
> 2 votes
Per the `hook_entity_update()` documentation:

> This hook runs once the entity storage has been updated. Note that hook implementations may not alter the stored entity data. Get the original entity object from $entity-\>original.

In your event listener, you can compare the email/password fields on `$entity` & `$entity->original` to check if they have changed.

---
Tags: 8, event-subscribers
---


# Question
Title: How to create arguments in path with regular page node instead of module, yet pass them to a module
I know how to create a URL with hook\_menu() in a module, but is it possible outside a module?

Here's what I'm needing/asking:

My front-end guy wants to create a page as a node using a Content Type via the Content menu. The URL for this content type needs to be something like www.somesite.com/some-page/arg1/arg2. Can this be done without a module defining the route with hook\_menu() in a module?

If so, in my module that needs to do the work, it needs to collect these args and use them to insert data into the DB.

Is it possible for him to create a path with arguments that I can get? If so, what would be the best way to get these args to my module that is going to work on the backend.

I could do this all day with hook\_menu() in module, but the front-end guy seems adamant that he can create a node and get the args from it's path. I don't understand how he can do that.

Thanks in advance. Cy

# Answer
https://www.drupal.org/project/pathauto will help you on your quest ... to do this make custom fields to collect the arguments then use tokens to replace them...

Edit:

but if your developer is using the front end to create content (nodes) its easy to create path arguments ... my idea was to make the fields to help generate the "path" ... then in your module you have to simply load the node then access the fields .. sure there is more ways to do this like look in to the path and explode out the "/" then use them but way easier just to get from the loaded node object.

other than that can you please up date your question to be more clear
> -1 votes

---
Tags: nodes
---


# Question
Title: How can I keep paragraphs of text in one cell when i import data from a CSV file without it making it all one big paragraph?
I'm using Migrate, Migrate Tools, Migrate CSV to import data from a csv file into my website.

One field I have is **Text (Formatted, Long)**, and this is where I import a bunch of information about my specific item.

If in my csv file I write out something like this in a cell (Using Alt+Enter to create a line break in the cell):

***This is paragraph 1** of a block of text. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut optime, secundum naturam affectum esse possit. Tu autem, si tibi illa probabantur, cur non propriis verbis ea tenebas? Sine ea igitur iucunde negat posse se vivere? Quod autem satis est, eo quicquid accessit, nimium est;*

***This is paragraph 2** of a block of text. Duo Reges: constructio interrete. Non enim quaero quid verum, sed quid cuique dicendum sit. Quod quidem iam fit etiam in Academia. Cum ageremus, inquit, vitae beatum et eundem supremum diem, scribebamus haec.*

It ends up importing like this into Drupal:

***This is paragraph 1** of a block of text. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut optime, secundum naturam affectum esse possit. Tu autem, si tibi illa probabantur, cur non propriis verbis ea tenebas? Sine ea igitur iucunde negat posse se vivere? Quod autem satis est, eo quicquid accessit, nimium est; **This is paragraph 2** of a block of text. Duo Reges: constructio interrete. Non enim quaero quid verum, sed quid cuique dicendum sit. Quod quidem iam fit etiam in Academia. Cum ageremus, inquit, vitae beatum et eundem supremum diem, scribebamus haec.*

Just one big block of text.

Does anyone know of anything special that I need to put into my Excel cell after each paragraph so that Drupal knows to break up each paragraph into its own `<p>` paragraph tag once imported from the saved .csv file?

I've gone through numerous sites/threads trying to find an answer but I can't quite seem to find anything explaining what I need to do in Excel prior to exporting as CSV to maintain the paragraph breaks.

*Note, after I export from excel to csv, i open the file in Notepad and resave as UTF-8 in order to import the content properly in Drupal.*

Is this something that is even possible at all?

# Answer
> 1 votes
If your CSV file *is* saved with new line characters then I expect it is also saved that way into the database. You can check this if you have access to the database. But you can also check this more or less on the Edit page of some of the content that has been imported.

Drupal uses Text formats to display the text. This means it can change what is being served from the database.

Check which Text format is being used on that field and then check the configuration for that format. You can check which Text format is being used on the Edit page, right below the text area element.

"Losing" new line characters can happen if you don't have a `Convert line breaks into HTML (i.e. <br> and <p>)` option turned on on the format that you apply to your field.

Try using that option on the Text format of the content you've imported, or maybe you have the "Plain text" format, since you're importing content, but make sure that this format has the option `Convert line breaks into HTML (i.e. <br> and <p>)` enabled.

You can find Text formats under `Configuration > Content authoring > Text formats and editors`.

Just make sure you don't save the node with a Text format chosen that doesn't have that option enabled (you will see all the paragraphs lumped together), because saving the node will override what is written in the database. Don't save if it doesn't look good, and try to find which Text format makes it look good, it will probably be the one that has the mentioned option enabled ;)

---
Tags: 8, migrations
---


# Question
Title: Filter for previous time periods in views
I'm trying to set up Grouped filters for `Content revision: Changed` in a view. I want the filter to work for Today, Yesterday, Last week, Last month, and Last year. The grouped filters makes that simple enough, and it looks like this:

But all the filters are not filtering correctly.

For the Today filter, this will work.

But When I try to use an `Is between` operator for any previous dates, I get an error like this

How do I filter for previous time periods in views?

---

The Today filter works with `Is greater than or equal to` `today midnight`. It's the `Is between` parameters that the view won't accept.

The existing `Today` filter query adds

```
 AND ((node_field_revision.changed >= 1597759242-68400))

```

to the query, which converts to

```
GMT: Tuesday, August 18, 2020 2:00:42 PM - 19 hours.

```

Setting the Yesterday filter with `Is less than or equal` to `yesterday` adds

```
AND ((node_field_revision.changed <= 1597759395-154800))

```

to the query, which converts to

```
GMT: Tuesday, August 18, 2020 2:03:15 PM - 43 hours

```

which means the filter will show values from yesterday and today. That's not a `yesterday` filter.

So `Is between` is the way to go, but any values I try, including yours @Yuseferi, aren't accepted in the UI and so can't adjust to the query accordingly. If the view would accept an `Is between` operator, the query would add something like this:

```
AND ((node_field_revision.changed <= 1597759956-(1597723200-1597636800))

```

Where

```
Now: 1597759956
Today: 1597723200
Yesterday: 1597636800

```

That would be a yesterday query, which Drupal could then adjust for GMT.

# Answer
> 0 votes
I would suggest

data between `today 1 sec ago` and `yesterday`

---
Tags: 8, views
---


# Question
Title: Migration with default values in the yml file
has the following value:
1. Firma
2. Landwirtschaft
3. Auto
4. Stadt
5. Traktor
6. Straße
7. Wald
8. Wiese
9. Park
10. Felder

The 10 values should be imported into Drupal, version 8.
In order not to have to read out these 10 terms from the database, I would like to "write these values into a YML import file" and import them that way.
How can you do that?

Here is the first draft of the YML file:

```
id: k1_import
label: 'Daten import'
migration_group: k1_migration
source:
  plugin: k1import
  key: migration_k1
process:
  title: Firma
  title: Auto
…

```

# Answer
got a tip from a friend, here are the links:

https://api.drupal.org/api/drupal/core%21modules%21migrate%21src%21Plugin%21migrate%21source%21EmbeddedDataSource.php/class/EmbeddedDataSource/9.0.x

https://www.drupal.org/docs/8/api/migrate-api/migrate-destination-plugins-examples/migrating-users

Here's my solution, in a nutshell, begin of the YML file:

```
id: k1_import
label: 'Daten import'
migration_group: k1_migration
source:
  plugin: embedded_data
  data_rows:
    -
        name_id: 1
        name: Firma
    -
        name_id: 2
        name: Landwirtschaft
    -
        name_id: 3
        name: Auto
    -
        name_id: 4
        name: Stadt
   ids:
     name:
          type: string
process:
    name: name
destination:
  plugin: 'entity:taxonomy_term'
  default_bundle: daten_import

```

End of the YML file.
> 0 votes

---
Tags: migrations
---


title: Add custom access check to user page

text:
# Question
Title: Add custom access check to user page
I would like to add an additional access condition to viewing user pages like `user/1`.

I looked at the following guidelines, but have not succeeded in coming up with a solution.

https://www.drupal.org/docs/drupal-apis/routing-system/altering-existing-routes-and-adding-new-routes-based-on-dynamic-ones

and

https://www.drupal.org/docs/8/api/routing-system/access-checking-on-routes#access-custom

The module code I have come up with this far - I can restrict all access to user profiles fine. But I fail at my attempt to get the account id of the page being requested. Once I have the account I can load the field I need to check.

my\_module.services.yml

```
services:
  my_module.route_subscriber:
    class: Drupal\my_module\Routing\RouteSubscriber
    tags:
      - { name: event_subscriber }

```

my\_module/src/Routing/RouteSubscriber.php

```
<?php

namespace Drupal\my_module\Routing;

use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;

/**
 * Listens to the dynamic route events.
 *
 * We deny access to profiles, if the user has not accepted the terms.
 *
 * See https://www.drupal.org/node/2187643
 *
 */
class RouteSubscriber extends RouteSubscriberBase {

  /**
   * {@inheritdoc}
   */
  protected function alterRoutes(RouteCollection $collection) {
    $path = \Drupal::service('path.current')->getPath();
    $path_args = explode('/', $path);
    // @todo: the right path does not seem to be accessible here - how do we get the user account id?
    // Always deny access to '/user/{uid}'.
    // Note that the second parameter of setRequirement() is a string.
    if ($route = $collection->get('entity.user.canonical')) {

      //$account = \Drupal\user\Entity\User::load($account_uid);
      // This effectively denies all access to user profiles
      $route->setRequirement('_access', 'FALSE');
    }
  }

}

```

EDIT: Based on the comments I tried the following, but I am not being restricted to paths like /user/1, so I guess something is still amiss:

My module structure now looks like this:

```
/my_module
  /src
    /Access
      my_moduleAccessCheck.php
    /Controller
      my_moduleController.php
    my_module.info.yml
    my_module.routing.yml
    my_module.services.yml

```

my\_moduleAccessCheck.php:

```
<?php

namespace Drupal\my_module\Access;

use Drupal\Core\Routing\Access\AccessInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Access\AccessResult;

/**
 * Checks access for displaying configuration translation page.
 */
class my_moduleAccessCheck implements AccessInterface {

  /**
   * A custom access check.
   *
   * @param \Drupal\Core\Session\AccountInterface $account
   *   Run access checks for this account.
   *
   * @return \Drupal\Core\Access\AccessResultInterface
   *   The access result.
   */
  public function access(AccountInterface $account) {
    // Check permissions and combine that with any custom access checking needed. Pass forward
    // parameters from the route and/or request as needed.
    // $account->hasPermission('do example things') && $this->someOtherCustomCondition()
    return AccessResult::forbidden();
  }

}

```

my\_moduleController.php:

```
<?php

namespace Drupal\my_module\Controller;

use Drupal\Core\Controller\ControllerBase;

/**
 * An example controller.
 */
class my_moduleController extends ControllerBase {

  public function sayHi() {
    dpm('Hi');
  }

}

```

my\_module.routing.yml

```
my_module:
  path: '/user/{uid}'
  defaults:
    _controller: '\Drupal\my_module\Controller\my_moduleController::sayHi'
    _title: ''
  requirements:
    _my_module_access_check: 'TRUE'

```

my\_module.services.yml

```
services:
  my_module.access_checker:
    class: Drupal\my_module\Access\my_moduleAccessCheck
    arguments: ['@current_user']
    tags:
      - # Required: Mark that this service is for access checks.
        name: access_check,
        # Optional: Specify which routes this applies to.
        applies_to: _my_module_access_check

```

# Answer
I ended up using hook\_ENTITY\_TYPE\_access():

```
use \Drupal\Core\Entity\EntityInterface;
use \Drupal\Core\Session\AccountInterface;
use \Drupal\Core\Access\AccessResult;
use \Drupal\Core\Access\AccessResultInterface;
/**
 *
 * Implements hook_ENTITY_TYPE_access().
 */
function my_module_user_access(EntityInterface $entity, $operation, AccountInterface $account) {

  // Deny access to user profile for other members if the user hasn't accepted the terms
  if (!$entity->id() === $account->id()
      || !$entity->hasField('field_profile_visibility')
      || $entity->get('field_profile_visibility')->value === NULL) {
    \Drupal::messenger()->addWarning(t('The profile is not visible because this user has not accepted the terms of this site yet.'));
    return AccessResult::forbidden();
  }
  return AccessResult::neutral();
}

```
> 1 votes

# Answer
The *entity.user.canonical* route has one parameter, `$user`. Route parameters can be retrieved from the route object and are upcast to their respective object. So, the user id will be upcast to the user object.

This technique is explained for nodes in the question How do I get the current node ID, but you can do the exact same thing with users.

For example:

```
$profile_user = \Drupal::routeMatch()->getParameter('user');
if ($profile_user instanceof \Drupal\user\UserInterface) {
  // $profile_user contains the $user object for the currently viewed profile.
}

```
> 0 votes

---
Tags: 8, users, routes
---


title: Composer file that is only additive

text:
# Question
Title: Composer file that is only additive
We have a Drupal 8.x instance maintained by one team and would like to be able to have another team add modules to this without overriding/removing what is already there. Is this possible?

# Answer
I think the best way would be using Composer and Git together:

* Use GIT and create feature branches and merge them into your production / develop branch
* Use $composer require drupal/module (keep the composer.json file in git repo)
> 1 votes

---
Tags: composer
---


# Question
Title: Set Views page title based on content being viewed via contextual filter
I have a view page that shows a single node based on a contextual filter that looks at an aliased path version of the title. For example my URL might be http://www.tacoland.com/resources/npr-story-about-tacos

and I want to set the page title to be NPR Story About Tacos (the node title).

I see how I can change the title with the value from the URL within the contextual filter settings, but I want it to be the proper node title.

My issue is basically like this one, but for D8: Views Page Title Based on URL

# Answer
* Add Title as field

+ ☑ Checkmark exclude from display
* For view title, put `{{ title }}`
> 2 votes

---
Tags: 8, views
---


# Question
Title: How do I uninstall and remove Drupal Console from my project?
1 year ago, I installed the Drupal Console module on my site.

This module seems to be completely abandoned, so I'm asking my question here.

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

I had installed this module with Composer and in my composer.json file I have this line :

```
"drupal/console": "~1.0"

```

In drupal `/admin/modules` I don't see any module with the name Console. How to remove this module ? Is it normal that there is nothing in Drupal ?

# Answer
> 3 votes
Drupal Console is not a Drupal module, is a shell for Drupal that is installed as a Composer package. This means it is not *installed* in Drupal so you don't have to uninstall it.

To remove it simply use `composer` and update your dependencies:

```
composer remove drupal/console
composer install

```

**Update**

You may also need to remove the Drupal Console Launcher if you have installed it. Drupal Console Launcher is an executable usually installed in your PATH that allows running Drupal Console without writing the complete path.

This process depends on the method used to install it. If you followed standard installation process just remove the launcher deleting it (Keep in mind that Drupal Console is named `drupal`):

```
rm /usr/local/bin/drupal

```

You probably need to be root to do it.

If the launcher is not there try to locate it using `whereis`:

```
$ whereis drupal
drupal: /usr/bin/drupal

```

Once you locate the file just remove it.

---
Tags: 8, composer, console
---


# Question
Title: Why do my custom elements not get rendered?
I've tried to create custom Webform elements, either radios or check-boxes. The elements are available in the Webform UI for administrator users; I can add them to webforms from the build section, but they are not getting rendered in the webform. I have no idea what I'm missing.

(Note: I have created custom elements whose type is textfield, selectbox, and autocomplete; they all work fine.)

The following is my custom src/Element and src/Plugin/WebformElement code.

### src/Element/MyCustomRadio.php

```
namespace Drupal\my_custom_element\Element;

use Drupal\Core\Render\Element\Radios;
use Drupal\Core\Render\Element\FormElement;
use Drupal\Core\Form\FormStateInterface;

/**
 * @FormElement("my_custom_element")
 *
 */
class MyCustomRadio extends Radios {

  /**
   * {@inheritdoc}
   */
  public function getInfo() {
    $class = get_class($this);
    return [
      '#input' => TRUE,
      '#size' => 60,
      '#process' => [
        [$class, 'processMyCustomRadio'],
        [$class, 'processAjaxForm'],
      ],
      '#element_validate' => [
        [$class, 'validateMyCustomRadio'],
      ],
      '#pre_render' => [
        [$class, 'preRenderMyCustomRadio'],
      ],
      '#theme' => 'input__my_custom_element',
      '#theme_wrappers' => ['form_element'],
    ];
  }

  public static function processMyCustomRadio(&$element, FormStateInterface $form_state, &$complete_form) {
    // Here you can add and manipulate your element's properties and callbacks.
    return $element;
  }

  public static function validateMyCustomRadio(&$element, FormStateInterface $form_state, &$complete_form) {
    // Here you can add custom validation logic.
  }

  /**
   * @param array $element
   * @return array
   */
  public static function preRenderMyCustomRadio(array $element) {
    $element['#attributes']['type'] = 'checkboxes';
    Element::setAttributes($element, ['id', 'name','value']);
    static::setAttributes($element, ['form-text', 'my-custom-element']);
    return $element;
  }

}

```

### src/Plugin/WebformElement/MyCustomRadio.php

```
namespace Drupal\my_custom_element\Plugin\WebformElement;

use Drupal\Core\Form\FormStateInterface;
use Drupal\webform\Plugin\WebformElement\Radios;
use Drupal\webform\Plugin\WebformElementBase;
use Drupal\webform\WebformSubmissionInterface;

/**
 * Provides a 'my_custom_element' element.
 *
 * @WebformElement(
 *   id = "my_custom_radio_element",
 *   label = @Translation("My Custom Radio"),
 *   description = @Translation("Provides a webform radio element."),
 *   category = @Translation("My Custom elements"),
 * )
 */
class MyCustomRadio extends Radios {

  /**
   * {@inheritdoc}
   */
  protected function defineDefaultProperties() {

    return [
      'multiple' => '',
      'size' => '',
      'minlength' => '',
      'maxlength' => '',
      'placeholder' => '',
    ] + parent::defineDefaultProperties();
  }

  /**
   * {@inheritdoc}
   */
  public function prepare(array &$element, WebformSubmissionInterface $webform_submission = NULL) {
    parent::prepare($element, $webform_submission);

  }

  /**
   * {@inheritdoc}
   */
  public function form(array $form, FormStateInterface $form_state) {
    $form = parent::form($form, $form_state);
    return $form;
  }

}

```

In the administrator panel, I have the option to add custom radios in my form.

>

In the front-end, custom radios are not rendered; only default radios and checkboxes are rendered.

>

# Answer
> 3 votes
For radios or checkboxes to work you need to call

```
\Drupal\Core\Render\Element\Checkboxes::processCheckboxes

```

or

```
\Drupal\Core\Render\Element\Radios::processRadios

```

Your example code, is extending Radios but trying to render checkboxes. I recommend extending Checkboxes and gradually override the default methods including \Drupal\Core\Render\Element\Checkboxes::processCheckboxes.

Below is an example

```
<?php

namespace Drupal\my_custom_element\Element;

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element\Checkboxes;

/**
 * Provides a form element for a set of My custom element.
 *
 * @FormElement("my_custom_element")
 */
class MyCustomElement extends Checkboxes {

  /**
   * {@inheritdoc}
   */
  public function getInfo() {
    $properties = parent::getInfo();
    // My custom properties.
    $class = get_class($this);
    $properties['#process'][] = [$class, 'processMyCustomElement'];
    $properties['#element_validate'] = [[$class, 'validateMyCustomRadio']];
    return $properties;
  }

  /**
   * {@inheritdoc}
   */
  public static function processCheckboxes(&$element, FormStateInterface $form_state, &$complete_form) {
    // You can override and extend this method.
    $element = parent::processCheckboxes($element, $form_state, $complete_form)
    return $element;
  }

  public static function processMyCustomRadio(&$element, FormStateInterface $form_state, &$complete_form) {
    // Here you can add and manipulate your element's properties and callbacks.
    return $element;
  }

  public static function validateMyCustomRadio(&$element, FormStateInterface $form_state, &$complete_form) {
    // Here you can add custom validation logic.
  }
}

```

---
Tags: 8, webforms
---


# Question
Title: taxonomy_term entity loadByProperties depth not found
The terms returned contain depth as a Term object attribute.

```
$vocabulary = 'category';
$entity_manager = \Drupal::entityTypeManager();
$entity_storage = $entity_manager->getStorage('taxonomy_term');
$terms = $entity_storage->loadTree($vocabulary);

```

However, when I try to load terms by depth, I get an error.

```
$entity_manager = \Drupal::entityTypeManager();
$entity_storage = $entity_manager->getStorage('taxonomy_term');
$terms = $entity_storage->loadByProperties([
  'vid' => $vocabulary,
  'depth' => 0,
]);

```

The error is the following.

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

What is the way to find attributes I can use on `loadByProperties()`?

# Answer
> What is the way to find attributes I can use on loadByProperties()?

I guess with Devel's Kint, loading one and then looking at the "iterator contents" tab

```
  $entity_manager = \Drupal::entityTypeManager();
  $entity_storage = $entity_manager->getStorage('taxonomy_term');
  $term = $entity_storage->load(1);
  kint($term);

```
> 2 votes

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


# Question
Title: How do I get the video media path in a theme?
I want to embed a video on a page.

How do I display the field\_media\_video\_embed\_field in my twig?

The complicated thing about this is that I am using a view inside tabs.

`{{ kint(programme.video) }}` gives me the following output.

>

In my theme I have used the following code.

```
$programmes = [];
$view = Views::getView('participating_programmes');
$view->execute();
foreach ($view->result as $row) {
  $video = $row->_entity->get('field_pvideo')->referencedEntities();
  $programmes[$nid]['video'] = $video;
}

```

Ideally I want to embed the video, but I would be happy if I could just get the field\_media\_video\_embed\_field and work from there.

# Answer
It's unclear whether you want to get one or multiple videos:

```
// get one video media entity
$video = $row->_entity->get('field_pvideo')->entity;
// get multiple video media entities
$videos = $row->_entity->get('field_pvideo')->referencedEntities();

```

Then in Twig for one video:

```
{{ programme.video.field_media_video_embed_field.value }}

```

or multiple videos:

```
{% for video in programme.videos %}
  {{ video.field_media_video_embed_field.value }}
{% endfor %}

```
> 1 votes

---
Tags: theming
---


# Question
Title: How to Write Browser Tests on Custom Theme?
I'm writing front end tests for a custom webform. These tests work fine when testing on the webform page of the form.

```
$this->drupalGet('/webform/custom_form');
$this->assertResponse(200);

```

However, testing the form using a custom theme and the page that the form is displayed on, does not work and the assertion fails returning a 404 response.

```
<?php

namespace Drupal\Tests\test_module\Kernel;

use Drupal\Tests\BrowserTestBase;
use Drupal\Tests\webform\Functional\Element\WebformElementBrowserTestBase;

class WebformValidationTest extends WebformElementBrowserTestBase {

  public static $modules = [
    'webform',
    'test_module',
  ];

  public static $testWebforms = ['custom_form'];

  protected $defaultTheme = 'custom_theme';

  /**
   * Test custom_form page from site with custom_theme theme install.
   *
   * @throws \Exception
   */
  public function testThemeFormPage() {
    $this->drupalGet('/page-that-displays-form');
    $this->assertResponse(200);
  }
}

```

I have also tried creating a submodule that imports the full config and installs all dependent modules from the site. This also did not work.

Is writing front end tests for specific nodes something that is possible with Drupal Browser Tests? If not, how does one write tests for webforms (validation, fields, display, etc)?

Thanks.

# Answer
I Would recommend reading https://www.previousnext.com.au/blog/introducing-drupal-testing-traits-drupal-extension-testing-existing-sites

in this article it explains using https://gitlab.com/weitzman/drupal-test-traits

in this way you can do...

```
use weitzman\DrupalTestTraits\ExistingSiteBase;

/**
 * Test webform on theme
 */
class WebformValidationTest extends ExistingSiteBase
{
    public function testThemeFormPage()
    {
        $this->drupalGet('/page-that-displays-form');
        // Assert stuff here.
    }
}

```
> 2 votes

# Answer
It is possible to get a custom theme to load during a test.

Checkout the webform\_bootstrap\_test\_theme included in the core Webform module. Make sure to includes all the needed blocks.

You can use the WebformNodeBrowserTestTrait to create a webform node during a test.

Still, it is worth checking out DrupalTestTraits.
> 1 votes

---
Tags: 8, webforms, testing
---


# Question
Title: How to render text including video embed field
I have a formatted text field, and am using the video\_embed module to allow users to embed video into that field, using the video\_embed\_wysiwyg submodule.

I am then trying to render the field in a block, but I simply get the JSON output in the text field:

> {"preview\_thumbnail":"/sites/default/files/styles/video\_embed\_wysiwyg\_preview/public/video\_thumbnails/A5jNGsBHHUE.jpg?itok=7fW8O9To","video\_url":"https://www.youtube.com/watch?v=\********","settings":{"responsive":1,"width":"854","height":"480","autoplay":1},"settings\_summary":\["Embedded Video (Responsive, autoplaying)."\]}

Here is my render array:

```
$text=check_markup($node->get('field_html_containing_video_content')->value, $node->get('field_html_containing_video_content')->format);

    $renderArray=[
      'content'=>[
        '#type'=>'inline_template',
        '#template'=>$text
      ]
    ];

    return $renderArray;

```

I've got the video embed filter enabled on the relevant text format, and have tried changing the order of the filters.

Any ideas what i am doing wrong here?

We are using a bootstrap subtheme.

Thanks for any help given.

# Answer
`check_markup()` is not able to handle attached metadata which some text filters need to work properly.

You have to use the render element `processed_text`:

```
$renderArray = [
  'content' => [
    '#type' => 'processed_text',
    '#text' => $node->field_html_containing_video_content->value,
    '#format' => $node->field_html_containing_video_content->format,
  ],
];

```

More info How to safely render node body on a custom variable?
> 3 votes

---
Tags: media, files, wysiwyg
---


# Question
Title: Admin UI doesn't see the correct PHP version
I have a local development site on Ubuntu and currently my system has **PHP 7.4** installed and I can see Drupal site using the correct version in terminal using `drush st` but if I browse to Admin UI and check the PHP version in status page it is showing different version.

In case it helps Initially the site was setup on **PHP 7.2** later on PHP version is updated on the system.

```
drush @local.local st

Drupal version   : 8.9.0
Site URI         : http://local.d8site.local
DB driver        : mysql
DB hostname      : localhost
DB port          : 3306
DB username      : drupal
DB name          : d8site_local
Database         : Connected
Drupal bootstrap : Successful
Default theme    : bartik
Admin theme      : seven
PHP binary       : /usr/bin/php7.4
PHP config       : /etc/php/7.4/cli/php.ini
PHP OS           : Linux
Drush script     : /var/www/local.d8site.local/vendor/drush/drush/drush
Drush version    : 10.2.2
Drush temp       : /tmp
Drush configs    : /var/www/local.d8site.local/vendor/drush/drush/drush
                  .yml
                  /var/www/local.d8site.local/drush/drush.yml
Install profile  : standard
Drupal root      : /var/www/local.d8site.local/web
Site path        : sites/local
Files, Public    : sites/local/files
Files, Temp      : /tmp

```

```
php --version
PHP 7.4.9 (cli) (built: Aug  7 2020 14:29:36) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.9, Copyright (c), by Zend Technologies
    with Xdebug v2.9.6, Copyright (c) 2002-2020, by Derick Rethans

```

# Answer
You have to configure the web server to use the PHP version you want.

For example:

```
sudo a2dismod php7.2
sudo a2enmod php7.4

sudo service apache2 restart

```
> 3 votes

---
Tags: 8, updating
---


# Question
Title: How can execute batch api over cron jobs
I am working on Drupal 8 and batch API. I created the batch api calling importingmatch and it should automatically importing every 1 hour.

My solution is use Drush for make it works without the browser, but I don't know how I can get the batch id.

Here is my code about the Batch API:

```
// Set up the Batch API
    $batch = array(
        'operations' => $operations,
        'finished' => 'importingmatch_finishedBatch',
        'title' => t('Import match'),
        'init_message' => t('Starting import match.....'),
        'progress_message' => t('Completed @current step of @total.'),
        'error_message' => t('Import match deletion has encountered an error.'),
    );

    batch_set($batch);

```

Currently, this batch is runing perfect if I access from my PC. Url: www.mysite.com/importing\_match I am not sure how make it working from the server Centos with Drush, I need the batch ID for execute: Drush batch-process

UPDATE: \- Interesting with the Command for Drush, I created custom Command from my module. Following from here: https://www.chapterthree.com/blog/how-to-create-custom-drush-commands. But it's still not working, it's showing:

```
The drush command '<name>' could not be found.

```

I changed from localhost to 127.0.0.1 from the settings.php but it's not helpful. Any idea?

# Answer
Instead of using Cron API you could provide your own custom Drush command and implement Batch API there. And then execute this Drush command using your server's crontab.

Following comes a sample command that implements a batch. Inside `module_name.drush.inc` put:

```
function module_name_drush_command() {
  $items = array();
  $items['migrate-batch-list'] = array(
    'description' => 'Migrate import match',
    'examples' => array(
      'drush migrate-batch-list' => 'Migrate import match',
    ),
  );
return $items;
}

function drush_module_name_migrate_batch_list() {
  drush_print('Migrate import match list.');

  $operations = array(...something here you want.);
  $batch = array(
    'operations' => $operations,
    'finished' => 'importingmatch_finishedBatch',
    'title' => t('Import match'),
    'init_message' => t('Starting import match.....'),
    'progress_message' => t('Completed @current step of @total.'),
    'error_message' => t('Import match deletion has encountered an error.'),
  );

  // Initialize the batch.
  batch_set($batch);

  // Start the batch process.
  drush_backend_batch_process();
}

```

**Note**: You can run this from terminal as `drush migrate-batch-list`
> 9 votes

# Answer
You don't. Batch API is to do long-running processing with user-interaction.

For cron, you either use queue API or you just process for N seconds/things in your hook\_cron() implementation, and in both cases, cron will be calling you repeatedly until it is eventually done.

If you want to process as fast as possible then either use a different queue processing that doesn't run on cron or make a custom script/drush command that you execute separate from cron, so that you don't block other cron jobs.
> 12 votes

# Answer
Sometimes it's just simply not possible to avoid batch process in cron jobs (user\_cancel using batch...). My case was to delete users periodically if they were inactive for a certain time. Here is how I solved it, it can give a good idea of how you can use it, I've stripped out the totally irrelevant parts leaving some guides for easier adaption.

```
function MODULE_cron() {
  [...]
  $to_delete_users = User::loadMultiple(/* User ids to be deleted */);

  $edit = [
    'user_cancel_notify' => FALSE,
  ];
  $failed_deletion_ids = [];
  foreach ($to_delete_users as $user) {
    [...]

    try {
      // Mark user for cancel.
      user_cancel($edit, $user->id(), 'user_cancel_reassign');

      // Drush check is failing, due wrong condition, however this may not
      // correct either, isset($batch['sets']) could be better.
      $batch =& batch_get();
      if (count($batch) > 0) {
        // Process the deletion with batch.
        // This is not ideal, but in this way we won't notify users that
        // wasn't deleted at the end also it doesn't break the whole process,
        // max the current loop will fail.
        if (function_exists('drush_backend_batch_process')) {
          // For drush.
          drush_backend_batch_process();
        }
        else {
          // For the rest.
          $batch['progressive'] = FALSE;
          batch_process();
        }
      }

      $mail_manager->mail(/* Parameters for mail sending */);
    }
    catch (Exception $e) {
      $failed_deletion_ids[] = $user->id();
      \Drupal::logger('cron')->error('Failed to cancel %uid user account. Please check the error message: %message in %file file at line %line and the backtrace: %backtrace NOTE: this error may not the original one!', [
        '%uid' => $user->id(),
        '%message' => $e->getMessage(),
        '%file' => $e->getFile(),
        '%line' => $e->getLine(),
        '%backtrace' => $e->getTraceAsString(),
      ]);
    }
    finally {
      // Reset batch.
      $batch = [];
    }
  }
  /* Some other stuff and make a report */
  [...]
}

```

There are two important things to note here.
First, if you using the drush function like it was suggested in the other answer, Behat can fail (if you have or the UI cron execution), because in that case drush is not loaded. That's why I've checked if it exists or not.
Second, if you want to run another batch process after a finished one, first you need to reset back to default the batch variable, or it will hang on the second run/call/loop.
This solution should also work when you call cron from the UI.
> 2 votes

---
Tags: 8, cron, batch-operations
---


# Question
Title: Reasons for using a webform node rather than just linking directly to the webform
Other than reuse, is there any reason why I should ***not*** link directly to a form in Drupal 8? I mean linking directly to the webform rather than to a node that incorporates the webform?

# Answer
> 3 votes
The main reason being you can't use any functionality that comes with a node being the page. E.g. add custom fields/paragraphs and allow users to edit them to customise the page. All of that would need to be done as blocks which isn't as efficent/easy for content loaders to manage

If you need to add metatags and other things like that also become more difficult.

And from the webform perspective it allows you to reuse the same form on multiple pages and the results are segmented to the webform node. Meaning if you need multiple copies of a form on your site you can have 1 form and reuse it on multiple pages rather than duplicating the actual webform itself

---
Tags: 8, nodes, webforms
---


title: Configure/Override EU Cookie Compliance

text:
# Question
Title: Configure/Override EU Cookie Compliance
I've installed eu\_cookie\_complience module (eu\_cookie\_compliance 8.x-1.9). The documentation is nearly non-existent, but as much as this module is used, I'm hoping to find someone that has solved these issues.

1. The big one: how does one set the `eu_only` parameter to true? Crazy, but there does not seem to be any option in the Admin panel. I could hack the contrib yaml or worse the module and force things. But that's a bad idea.
2. How can I hook into or override eu\_cookie\_compliance and specify my own IP lookup. Though it isn't documented, apparently there are three ways to check to see if the user is EU (the smart\_ip module, the geoip module, or geoip\_country\_code\_by\_name()) We already use IPSTACK and I want to leverage that.

# Answer
1. The settings form is hard-wired to only display the config field on the form if one of the following is installed: the Smart IP module, GeoAPI module, or the PHP `geoip` extension. Outside that you'll have to bypass the config form; if you're version controlling your config, make the change in the `eu_cookie_compliance.settings.yml` file; or use `drush cset` to make the change directly.
2. The hooks are documented in `eu_cookie_compliance.api.php`. You're wanting to use `hook_eu_cookie_compliance_geoip_match_alter()` that lets you alter the results from the `eu_cookie_compliance_user_in_eu()` function. The parameter `$geoip_match` is a keyed-array with `country` & `in_eu` keys that drive the logic.
> 3 votes

---
Tags: 8, configuration
---


title: How to load ConfigEntity by Field?

text:
# Question
Title: How to load ConfigEntity by Field?
I'm using ConfigEntity and on this Entity, there is a boolean Field (checkbox) called "Published".

Now I want to load all Entities with field Published = 1

Do you know how I can do that?

P.s. To load all Entities I'm using this line below:

```
$myEntities = \Drupal::entityTypeManager()->getStorage('my_entities')->loadMultiple();

```

If there is another best command, please let me know.

Thank you so much.

# Answer
> 1 votes
Try

```
$entity_manager = \Drupal::entityTypeManager();
$entity_storage = $entity_manager->getStorage('entity_name_id');
$entitys_published = $entity_storage->loadByProperties([
  'status' => 1, // published
])->loadMultiple();

```

---
Tags: entities
---


# Question
Title: Sending multiple attachments following the module instructions doesn't work
Through `drupal_mail('mymodule', ...)` I'm trying to send e-mails with four (4) attachments. When I send an e-mail, I is delivered instantly with the correct subject and body. However, I only get the first attachment, not the other three (3).

Details below. Does anyone have an idea what's going on here?

### What I've tried

So far I've tried several (not notable) combinations of headers, providing attachments as content of with filepath only, and some variations suggested in related Q&As. Attachment structure is per the documentation in the module at `mimemail.module::mimemail_prepare_message()` (~line 309).

The e-mails I'm sending are HTML emails, with attachments, routed through custom SMTP settings (sending through SendGrid).

The total size of the emails is well below the 8MB limit Drupal seems to impose.

In the bottom code block of my post I have added a `var_dump()` of my $message. This dump also contains the array `$message['params]`, which contains the $params I've passed to `drupal_mail('mymodule', ...)`.

### Technical details:

Installed mail modules:

Mail configuration, through Mail System module:

* Site-wide default MailSystemInterface class: `HTMLMailSystem__SmtpMailSystem`
* HTML Mail module class: `HTMLMailSystem__SmtpMailSystem`
* Mime Mail module class: `MimeMailSystem`

Custom mail hooks:

```
/**
 * Implements hook_mail().
 */
function mymodule_mail($key, &$message, $params)
{
    if (!empty($params['subject'])) {
        $message['subject'] = $params['subject'];
    }

    if (!empty($params['body'])) {
        $message['body'] = $params['body'];
    }

    if (!empty($params['attachments'])) {
        $message['attachments'] = $params['attachments'];
    }

    if (!empty($params['Cc'])) {
        $message['Cc'] = $params['Cc'];
    }
}

/**
 * Implements hook_mail_alter().
 */
function mymodule_mail_alter(&$message)
{
    if (!empty($message['params']['headers'])) {
        $message['headers'] = array_merge($message['headers'], $message['params']['headers']);
    }
}

```

A var\_dump of `$message`, called at the bottom of `mymodule_mail_alter()` (data partially faked to protect client):

```
array (size=12)
  'id' => string 'mymodule_mymail' (length=38)
  'module' => string 'mymodule' (length=10)
  'key' => string 'mymail' (length=27)
  'to' => string 'someone@example.com' (length=30)
  'from' => string 'test-someone@example.com' (length=35)
  'language' => string 'nl' (length=2)
  'params' =>
    array (size=4)
      'headers' =>
        array (size=2)
          'MIME-Version' => string '1.0' (length=3)
          'Content-Type' => string 'multipart/mixed; text/html; text/plain; charset=UTF-8; format=flowed; delsp=yes' (length=79)
      'subject' => string 'Title' (length=46)
      'body' =>
        array (size=1)
          0 => string '<p>HTML</p><p>Mail</p>'... (length=555)
      'attachments' =>
        array (size=4)
          0 =>
            array (size=4)
              'filecontent' => string 'test file' (length=16)
              'filename' => string 'test_mail.txt' (length=11)
              'filemime' => string 'text/plain' (length=10)
              'filepath' => null
          1 =>
            array (size=4)
              'filecontent' => string 'test file' (length=16)
              'filename' => string 'test_mail_2.txt' (length=13)
              'filemime' => string 'text/plain' (length=10)
              'filepath' => null
          2 =>
            array (size=4)
              'filecontent' => string 'test file' (length=16)
              'filename' => string 'test_file_3.txt' (length=32)
              'filemime' => string 'text/plain' (length=10)
              'filepath' => null
          3 =>
            array (size=4)
              'filecontent' => string 'test file' (length=16)
              'filename' => string 'test_mail.txt' (length=11)
              'filemime' => string 'text/plain' (length=10)
              'filepath' => null
  'send' => boolean true
  'subject' => string 'Title' (length=46)
  'body' =>
    array (size=1)
      0 => string '<p>HTML</p><p>Mail</p>'... (length=555)
  'headers' =>
    array (size=7)
      'MIME-Version' => string '1.0' (length=3)
      'Content-Type' => string 'multipart/mixed; text/html; text/plain; charset=UTF-8; format=flowed; delsp=yes' (length=79)
      'Content-Transfer-Encoding' => string '8Bit' (length=4)
      'X-Mailer' => string 'Drupal' (length=6)
      'Return-Path' => string 'test-someone@example.com' (length=35)
      'Sender' => string 'test-someone@example.com' (length=35)
      'From' => string 'test-someone@example.com' (length=35)
  'attachments' =>
    array (size=4)
      0 =>
        array (size=4)
          'filecontent' => string 'test file' (length=16)
          'filename' => string 'test_mail.txt' (length=11)
          'filemime' => string 'text/plain' (length=10)
          'filepath' => null
      1 =>
        array (size=4)
          'filecontent' => string 'test file' (length=16)
          'filename' => string 'test_mail_2.txt' (length=13)
          'filemime' => string 'text/plain' (length=10)
          'filepath' => null
      2 =>
        array (size=4)
          'filecontent' => string 'test file' (length=16)
          'filename' => string 'test_file_3.txt' (length=32)
          'filemime' => string 'text/plain' (length=10)
          'filepath' => null
      3 =>
        array (size=4)
          'filecontent' => string 'test file' (length=16)
          'filename' => string 'test_mail.txt' (length=11)
          'filemime' => string 'text/plain' (length=10)
          'filepath' => null

```

# Answer
We ran into the following article at the office: Let the Drupal send an email with attachment using Mimemail module.

Only one line in the code needed to be changed, I added 'filepath' to my attachment data. I first had `filepath => null`, but setting it to `$file_data->uri` fixed it:

```
$attachment_data[] = array(
    'filename' => $file_data->filename,
    'filemime' => $file_data->filemime,
    'filepath' => $file_data->uri,
);

```

Note: I had to remove `filecontent` from my attachment data. If you supply both `filecontent` and `filepath`, the same attachment is sent twice.
> 0 votes

# Answer
When attaching a file, you provide information about that file to Mime Mail using the keys: `filecontent`, `filename`, `filemime`, and `filepath` as shown above. The important part is to understand what these keys mean and what they are used for.

If you provide a `filepath`, then Mime Mail uses that to find the file and to extract its contents. If you DON'T provide a `filepath` (or if it is NULL), then you MUST store the contents of the file in `filecontent`.

`filename` and `filemime` are optional, and are just used to tell the recipient's mail client what to call the file and what the file contains. The name doesn't have to be the same as the "real" name of the file on your server.

If `filepath` is set but `filename` isn't, the "real" file name will be used. If `filecontent` is set but `filename` isn't, the file will be called "attachment.dat"

Likewise, if `filemime` isn't set, the mime type will be automatically computed based on the file extension.

There is one other important thing to know: Mime Mail automatically detects duplicate attachments and removes them. This is mostly used for images - themes often use the same image many times on a page, and rather than attaching many copies of the same image Mime Mail attaches the image only once (and uses that one copy many times). But this removal of duplicates is not only for images but for all attachments.

**The problem in the original post is because you attached the SAME file four times - Mime Mail detected that this was the same file and didn't try to include all four duplicates in your email.**

If you specify the file with `filepath`, Mime Mail identifies duplicates by comparing the paths - if the paths are the same, the files are duplicates. But if you specify the files using the `filecontent`, like you did in the original post, then Mime Mail compares the contents to determine if the files are duplicates. (Actually, it calculates a hash of the contents and compares the hashes, but that's effectively the same.)

As I said above, `filename` is just an optional name for the use of the recipient, it is not used to identify the file or determine if it is a duplicate.

To fix the example in the original post, just make the contents of the four files different, then you will get four attachments.
> 0 votes

# Answer
Use Drupal's Swift Mailer module, it does the task perfectly. I have used it to send multiple system reports in csv format.
> -1 votes

---
Tags: 7, mimemail
---


# Question
Title: How can I display an element only if any value has been selected from an entity select field?
I have a field on a webform (Drupal 8) that should be displayed only when a user has selected one or more options from an entity select field regardless which options are selected.

The available conditional logic options in webform do not seem to include such state. I have searched online and looked through the example\_element\_states.yml in the webform module and all select related examples specify a particular option value as the conditional logic.

Has anyone encountered and resolved such issue?

# Answer
> 4 votes
Got it! You need to check for a value greater than 0.

```
user:
  '#type': webform_entity_select
  '#title': User
  '#multiple': true
  '#target_type': user
  '#selection_handler': 'default:user'
  '#selection_settings':
    include_anonymous: false
    filter:
      type: _none
text:
  '#type': textfield
  '#title': Text
  '#states':
    visible:
      ':input[name="user[]"]':
        value:
          greater: '0'

```

I guess in PHP that must be:

```
$form['text']['#states'] = [
  'visible' => [
    ':input[name="user[]"]' => [
      'value' => [
        'greater' => "0",
      ]
    ]
  ]
];

```

---
Tags: webforms
---


# Question
Title: How do I handle exceptions in a service class?
I have implemented a Service which does stuff with a RESTful API:

```
services:
  api.clientname:
    class: Drupal\my_custom_module\Api\Clientname
    arguments: ['@http_client', '@messenger', '@logger.factory', '@uuid']

```

I'm injecting dependencies for `['@http_client', '@messenger', '@logger.factory', '@uuid']`:

* Guzzle HTTP Client
* messenger service to call `$this->messenger->addMessage()`,
* logger factory to call `$this->loggerFactory->get('my_custom_module')->debug($message, $variables)`,
* UUID service to generate UUID v4, per API specification.

I'm also using the `StringTranslationTrait` to get `$this->t()` working in my class.

All this is great so far. When it works, it works, and it logs success to the dblog. But it also needs to log failures. I'm having a problem with exception handling.

I'm trying to do something like this:

```
    while ($apiCallAttempts < API_CALL_LIMIT && $apiSuccess === FALSE) {
      $apiCallAttempts++;
      try {
        $accessToken = $this->obtainAccessToken($url, $postFields);
        $message = "Step 1: Access Token obtained from API endpoint";
        $this->loggerFactory->get('my_custom_module')->debug($message, []);
        $apiSuccess = TRUE;
      }
      catch (Exception $e) {
        // TODO: "watchdog_exception('my_custom_module', $e);".
        $variables = Error::decodeException($e);
        $this->loggerFactory->get('my_custom_module')->error('%type: @message in %function (line %line of %file).', $variables);
        if ($apiCallAttempts === API_CALL_LIMIT && $apiSuccess === FALSE) {
          // Set errorMessage for messenger service.
          $errorMessage = 'Error CLIENTNAME-01: Authentication API call failed';
          $message = 'Failed to sync %label with API. <br> @error after @limit attempts.';
          $variables = [
            '%label' => $form_id,
            '@error' => $errorMessage,
            '@limit' => strval($apiCallAttempts),
          ];
          $this->messenger->addMessage($errorMessage, 'error');
          $this->loggerFactory->get('dex_provider_hcp_forms')->error($message, $variables);
          return $accessToken;
        }
      }

```

but it doesn't work because:

> Fatal error: Trait 'Drupal\my\_custom\_module\Api\Exception' not found in /app/docroot/modules/custom/my\_custom\_module/src/Api/Clientname.php on line 66

After some Googling, I am very confused about how to add the global PHP `Exception` to my Service class namespace.

Removing a `use` declaration inside the class removed the "Trait not found" error.

The new error message was:

> Error: Class 'Drupal\my\_custom\_module\Api\Error' not found

**EDIT:** Both of these things were happening for the same reason. I was using `Exception` and `Error` inside my class without prepending them with a backward slash, causing PHP to assume that `Exception` was in the same namespace as the one I declared for the file. Using `\Exception` and `\Error` instead resolved the issue.

The new error message is:

> Error: Call to undefined method Error::decodeException()

# Answer
> 4 votes
When using unqualified class names like `Exception` and `Error`, and in the absence of a `use` statement to qualify the names, PHP assumes those classes are in the "current" namespace. The namespace of your service class appears to be `Drupal\my_custom_module\Api`, so that is where PHP will look for `Exception` and `Error`. When PHP does not find those classes, you will get an error.

To avoid this error, add a backslash before the class name for classes that are in the global namespace. So use `\Exception` and `\Error` instead. You do not need a use statement for either of these.

The error `Fatal error: Trait 'Drupal\my_custom_module\Api\Exception' not found in /app/docroot/modules/custom/my_custom_module/src/Api/Clientname.php on line 66.` is because you probably said `use Exception;` within the body of your class. The error `Error: Class 'Drupal\my_custom_module\Api\Error' not found` is because you referred to `Error` and not `\Error`.

Note that all these problems are just basic PHP issues - they are not caused by Drupal and do not involve Drupal at all. Specifically, it does not matter that you're in a service class, or that you're doing exception handling, etc.

---
Tags: 8, services
---


title: Image URLs aren't MIME encoded in email

text:
# Question
Title: Image URLs aren't MIME encoded in email
I'm using Drupal core 8.9.3 and Mime Mail 8.x-1.0-alpha3.

I have installed MimeMail and implemented mymodule\_mail hook.

The UI is set to use "Mime Mailer mailer". The UI for Mime Mail is configured to Not 'Link images only', and to use 'Full HTML'.

Emails are sent and display correct html with embedded image tags. However there is a problem with the image being MIME-encoded and included as a message attachment.

The below simplified email only has a paragraph tag enclosing an image tag with a test src as src="https://www.google.com/images/branding/googlelogo/1x/googlelogo\_color\_272x92dp.png".

Below is the raw email.

Please, any suggestions.

```
Date: Thu, 20 Aug 2020 01:46:23 +0000 (UTC)
Content-Transfer-Encoding: 7bit

This is a multi-part message in MIME format.

--e3e18d39bfaeb05c93e9fe993b85d9033ac7dadb1
Content-Type: multipart/alternative;
 boundary="caf5a2b1e29cb0704a73a98e3c656acf261f57860"
Content-Transfer-Encoding: 7bit

--caf5a2b1e29cb0704a73a98e3c656acf261f57860
Content-Type: text/plain; charset=utf-8
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

--caf5a2b1e29cb0704a73a98e3c656acf261f57860
Content-Type: text/html; charset=utf-8
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://ww=
w.w3.org/TR/html4/loose.dtd">
<html>
  <head>
    <meta http-equiv=3D"Content-Type" content=3D"text/html; charset=3Dutf=
-8">
    <title>Mime Mail message template</title>
  </head>
  <body id=3D"mimemail-body" class=3D"announce-contact-message">
    <div id=3D"center">
      <div id=3D"main">
        <p><img alt=3D"google" src=3D"https://www.google.com/images/brand=
ing/googlelogo/1x/googlelogo_color_272x92dp.png" /></p>

      </div>
    </div>
  </body>
</html>

--caf5a2b1e29cb0704a73a98e3c656acf261f57860--

--e3e18d39bfaeb05c93e9fe993b85d9033ac7dadb1--

```

# Answer
> MimeMail is not MIME encoding url images in email

That's correct. Mime Mail is still in alpha and that feature isn't working correctly yet. If this feature is important to you please participate in the issue queue at https://www.drupal.org/project/mimemail

Images are usually only delivered as attachments if they are not publicly accessible on the server. A complete link to an external image should work just fine in any email client.

But let me also note that there is something going on that you didn't mention - the Mime Mail module will NEVER use 7-bit encoding, and will NEVER use quoted-printable encoding. So it's quite possible that you're doing something that breaks the behaviour of Mime Mail.
> 0 votes

---
Tags: 8, mimemail
---


# Question
Title: How can I load the #totals property for the receipt theme in a custom module?
I am trying to manually output the customer's receipt on the checkout complete screen in a custom module.

I have the receipt printing nicely except for the "totals" variable.

My current custom code is the following.

```
$renderable = array(
      '#theme' => 'commerce_order_receipt',
      '#order_entity' => $order,
      '#billing_information' => NULL,
      '#shipping_information' => NULL,
      '#payment_method' => NULL,
      '#totals' => $this->orderTotalSummary->buildTotals($order), // <-- this is the problem
    );

```

```
 return array(
      '#theme' => 'custom_blocks_checkout_complete',
 '#order' =>  \Drupal::service('renderer')->render($renderable),
    );

```

As you can see, it's using `$this->orderTotalSummary->buildTotals($order)` to generate the #totals property and this was copied from the OrderReceiptMail class in the Order module. How can I get this property to populate from my custom module please?

# Answer
> 1 votes
Use the service

```
'#totals' => \Drupal::service('commerce_order.order_total_summary')->buildTotals($order),

```

---
Tags: commerce
---


title: Add class to {{ link }} twig attribute

text:
# Question
Title: Add class to {{ link }} twig attribute
I'm trying to apply a button class to the {{ link }} attribute in code below.

```
<span{{attributes}}><a class="usa-button"{{ link }}</span>

```

It displays correctly however the markup is not correct. I see an extra `<a=""` tag.

```
<a class="usa-button" <a="" href="http://sites.localhost/sites/default/files/Image%20styles%20for%20grid%20view_3.docx" type="application/vnd.openxmlformats-officedocument.wordprocessingml.document; length=14535">Image styles for grid view_3.docx</a>

```

Any ideas on how to apply class cleanly?

# Answer
> 4 votes
This is how to do it:

```
{{ link(item.title, item.url, { 'class': ['text-blue500', 'text-3xl'] }) }}

```

# Answer
> 2 votes
I think your easiest approach would be to modify the field template and add a wrapping span with classes you need.

**field--field-name.html.twig**:

```
{% for item in items %}
  <span{{ item.attributes.addClass('my-class') }}>{{ item.content }}</span>
{% endfor %}

```

or replace span and content with to add class directly to `<a>` tag:

```
<a href="{{ item.content['#url']}}" class="my-class">{{ item.content['#title'] }}</a>

```

# Answer
> 0 votes
You will have to rebuild the link(a href tag) with relevant attributes you require. You can add class, id, download or target blank as well. Refer definition below -

```
function THEME_preprocess_file_link(&$variables)
{

  $file = $variables['file'];
  $link_text = '';
  $options = array();
  $file_entity = $file instanceof File ? $file : File::load($file->fid);
  $url = file_create_url($file_entity
    ->getFileUri());
   // Use the description as the link text if available.
  if (empty($variables['description'])) {
    $link_text = $file_entity
      ->getFilename();
  }
  else {
    $link_text = $variables['description'];
    $options['attributes']['title'] = $file_entity
      ->getFilename();
  }

  $variables['attributes']->addClass(array('cta'));//Adding class to span
  $options['attributes']['download'] = TRUE;//Adding attribute to l
  $options['attributes']['target'] = array('_blank');//Adding attribute to l
  $variables['link'] = \Drupal::l($link_text, Url::fromUri($url, $options));

}

```

---
Tags: 8, theming
---


# Question
Title: Remove duplicate records from views feed display for node with multiple grouped terms from one vocabulary
I have an RSS feed display in a view, and one of the fields in the view is a multi-value taxonomy reference field. The feed settings have a "Grouping field" setting, and I've chosen that field (and I also have DISTINCT selected in the query settings), but I still get duplicate records.

When I run the query in MYSQL, I can see that it returns one record per term, so I'm guessing the grouping like that is handled in the display layer that an RSS view doesn't have (even though it does have a grouping setting).

That being said, what is a good place to filter out duplicate results? I saw the Views Distinct module, but it doesn't have a D8 release. In looking at the views hooks, it looks like hook\_views\_pre\_render would be the best place, because at that point the query is run and the $view object has the results.

If anyone has experience with this or a better way, I'm open to suggestions.

Thanks.

# Answer
> 2 votes
Here's what I came up with that does the job:

```
/**
 * Implements hook_views_query_pre_render().
 */
function video_export_views_pre_render(Drupal\views\ViewExecutable $view) {
  $unique_nids = $new_results = array();

  // Loop through results and filter out duplicate results.
  foreach($view->result as $index => $result) {
    if(!in_array($result->nid, $unique_nids)) {
      $unique_nids[] = $result->nid;
    }
    else {
      $new_results[] = $result;
    }
  }
  // Replace $view->result with new array. Apparently views requires sequentially keyed
  // array of results instead of skipping keys (e.g. 0, 2, 4, etc), so we can't just
  // unset the duplicates.
  $view->result = $new_results;
}

```

# Answer
> 2 votes
@wonder95, this doesn't seem to work for me since there are 2 errors in your code IMHO.

First, you should be filling up the `$new_results` array inside your `if` condition and not the `else` condition.

Secondly, `$view->result = $new_results;` has no affect on the final result set. You'll need to use unset instead.

Below is my final working code

```
function hook_views_pre_render(\Drupal\views\ViewExecutable $view) {
   if ($view->id() == 'my_view') {
     $unique_nids = array();
     foreach($view->result as $key => $result) {
       if(!in_array($result->nid, $unique_nids)) {
         $unique_nids[] = $result->nid;
       }
       else {
         unset($view->result[$key]);
       }
     }
   }
 }

```

# Answer
> 0 votes
Extending @NKP's answer, there's another situation where you might run into this problem, and that's when you are using a taxonomy term view for some reason (in my case I need to start with a term), and you add the related content as a relationship, but still filter by term ID as contextual filter.

If a node is tagged with more than one term, it might produce duplicates. In this case, this variation of the hook worked for me, in case anyone else has the same problem:

```
function HOOK_views_pre_render(\Drupal\views\ViewExecutable $view) {
  $view_id = $view->id();

  if ($view_id == 'YOUR_VIEW_MACHINE_NAME') {
    $filter_tid = $view->args[0]; // supposing the TID is your first contextual filter

    $view->result = array_filter($view->result, function ($result) use ($filter_tid) {
      return $result->tid == $filter_tid; // keep only the nodes related to a term matching the contextual filter
    });
  }
}

```

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


# Question
Title: Preprocessed var not being passed to twig template
I created a custom theme called wdocs as a sub theme of d8w3css. I made a template to override the menu template for the user account menu, calling it **menu--account.html.twig**. For my theme, I added the function `wdocs_preprocess_menu__account()` to my **wdocs.theme** file, and introduced a new variable with `$variables['tollfree_number'] = '555-1212'`; So far so good; in phpStorm I can set a breakpoint there are do see it is getting set in $variables. In my twig, I have some text, followed by `{{ tollfree_number }}`. The text gets displayed, but not the variable.

The problem is the twig calls a macro; I can access the var outside of the macro, but not within it. Googling suggested I pass context when calling the macro, so I changed the call to

```
{{ menus.menu_links(items, attributes, 0, menu_name, _context) }}

```

I also tried "context" (no underscore). And also tried dereferencing it with `{{ _context['tollfree_number'] }}`, but nothing seems to help. Where am I going wrong?

# Answer
You're correct in thinking that trying to access your var from within the macro is the issue. See Twig's macro documentation.

> But as with PHP functions, macros don’t have access to the current template variables.

You can pass your variable into the macro.

```
{{ menus.menu_links(items, attributes, 0, menu_name, tollfree_number) }}

```

Then you have two options, you can update the macro definition to add an extra parameter for your custom var.

```
{% macro menu_links(items, attributes, 0, menu_name, toll_free) %}
  {# etc.. #}
  {{ toll_free }}
{% endmacro %}

```

Or you can use the special varargs variable without updating the macro definition.

> If extra positional arguments are passed to a macro, they end up in the special `varargs` variable as a list of values.

Pass the var to the macro the same way, then access it like:

```
{% macro menu_links(items, attributes, 0, menu_name) %}
  {# varargs[0] has the first extra arg, varargs[1] has the second, etc #}
  {{ varargs[0] }}
{% endmacro %}

```

Using the special `_context` and `varargs` vars:

```
{{ menus.menu_links(items, attributes, 0, menu_name, _context) }}

{% macro menu_links(items, attributes, 0, menu_name) %}
  {{ varargs[0].tollfree_number }}
{% endmacro %}

```
> 4 votes

---
Tags: theming
---


# Question
Title: Accessing text field with multiple values in a twig file
Here is my use case: I have a content type which has a field referencing multiple other entities. Those entities have a text field with multiple values.

So I do

```
{% for ref in node.field_references %}

```

and then inside the loop

```
ref.entity.title.value

```

To have the `title` of the referenced entity. This works. Then I try to list the values in the text field:

```
for value in ref.entity.field_name_of_text_field

```

But without success. Is there something else that must be done first?

# Answer
Both loops iterate field items, so when you name the loop variables accordingly the code should be more understandable:

```
{% for ref_item in node.field_references %}
  {% for text_item in ref_item.entity.field_name_of_text_field %}
    {{ text_item.value }}
  {% endfor %}
{% endfor %}

```
> 1 votes

---
Tags: theming
---


# Question
Title: How to get instance of Module / Plugin / Widget of Vote Object in THEME_preprocess_node?
My drupal site is using the Fivestar Module or from the Vote Up Down Module.

Multiple of my content types are using various implementations of these as fields (specifically comments and my primary articles. Users can up/downvote comments and articles, and also give them ratings).

In my theme, I want to be able to access things like the vote count, and if the current user has voted, and ideally even the vote and reset links for custom display and front end functionality.

The default way how things are displayed doesn't work for my purposes, and creating specific views/blocks for each little snippet on my page, or manually overriding each template seems too cumbersome, and not flexible enough.

I'd much rather get all the vote info that I need in an array and pass it to my template, to be then displayed for full nodes, teasers, and comments.

How can I make use of the pre-formatted data that the modules are fetching and compiling from the database?

I see for example that `abstract class VoteUpDownWidgetBase extends PluginBase implements VoteUpDownWidgetInterface` has a method `public function build($entity)` which does exactly what I need to do. It gets all of the data about the vote field, and formats it for display in a template.

I don't see this equivalent from the Fivestar Module yet, but I'm sure I'll find something useful...

Either way: How can I access this method? What class do I have to get an instance of?

# Answer
Turns out, everything I need is in `$variables['content']` as long as the widgets/plugins that I'm trying to display are set to display as part of the view mode.

So in my specific case I can for example access `$variables['content']['field_upvotes']['#up_points']` and that contains the number of upvotes!

Easy enough! :-)
> 0 votes

---
Tags: 8, theming, plugins
---


title: submitForm() isn't called

text:
# Question
Title: submitForm() isn't called
Goal: Have a custom page that shows various elements including links, logos, images and a custom form to help the users reset their passwords (we have a custom reset password interface which sends the user a code so we are over-riding the default reset password interface).

Problem: The submit button is not firing the submitForm function on the reset password form.

Here is my code now:

In my module:

```
function example_theme($existing, $type, $theme, $path) {
  return array(
    'reset_password' => array(
            'variables' => array(
                'form' => NULL,
                'url'=> '',
                'main_logo_url' => '',
                'foot_logo_url' => '',
            )
        )
  );
}

```

In my routing:

```
example.reset_password:
  path: '/reset-password'
  defaults:
    _controller: '\Drupal\example\Controller\ResetPasswordController::resetPassword'
    _title: 'Reset password'
  requirements:
    _permission: 'access content'

```

In my Controller:

```
namespace Drupal\example\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Form\FormState;

class ResetPasswordController extends ControllerBase {
  public function resetPassword() {

    //code here that sets the variables in the return array

    $form_state = new FormState();
    $reset_password_form = \Drupal::formBuilder()->getForm('Drupal\example\Form\ResetPasswordForm', $form_state);

    return array(
      '#theme' => 'reset_password',
      '#form' => $reset_password_form,
      '#url' => $url,
      '#main_logo_url' => $main_logo_url,
      '#foot_logo_url' => $foot_logo_url,
    );
  }
}

```

in my form:

```
namespace Drupal\example\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;

class ResetPasswordForm extends FormBase {
  public function getFormId() {
    return 'reset_password';
  }

  public function buildForm (array $form, FormStateInterface $form_state) {
    \Drupal::logger('rp-form')->notice('hello from build Form');

    $form['#theme'] = 'reset_password';

    $form['step_one_user_exists'] = [
      '#type' => 'textfield',
      '#required' => TRUE,
      '#attributes' => array(
        'placeholder' => t('Enter here')
      )
    ];

    $form['actions'] = ['#type' => 'actions'];

    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Send Code'),
    ];

    return $form;
  }

  public function validateForm (array &$form, FormStateInterface $form_state) {
    \Drupal::logger('rp-form')->notice('hello from validate');
  }

  public function submitForm (array &$form, FormStateInterface $form_state) {
   \Drupal::logger('rp-form')->notice('hello from submit');
  }
}

```

and in my twig template:

```
// I have other elements on the page here
<div>
 {{ form.step_one_user_exists }}
</div>
<div>
 {{ form.actions.submit }}
</div>

```

As I said above, the problem is that the submitForm function from the form class is not firing when I click the submit button. The form shows up on the page, you can type in the text field, you can click the button, but then nothing happens. I have loggers in the buildForm (which logs), and in validateForm and submitForm (which do not log).

I want to use a controller because there are other variables needed on the page that change based on language and other factors, but am open to other methods of doing this (although I'd prefer to keep most of the logic out of the template).

Thanks!

# Answer
The code in the question is mixing three different types of templates:

1. Custom twig template in a controller

https://www.drupal.org/docs/theming-drupal/twig-in-drupal/create-custom-twig-templates-for-custom-module
2. Overriding form.html.twig

How to Create twig template files in drupal 8 for customizing user login,profile edit and forget password forms?
3. Adding a custom form template

How to render specific form elements from the \*.theme file

The confusing part of your question is that you are using the same template `reset-pasword.html.twig` for 1. and 3.

The issue discussed in the comments about the special form elements applies only to 2.

You can use all three template levels at the same time. Then they are nested inside of each other which you see when you enable twig debugging.

More info Custom registration twig template.
> 0 votes

---
Tags: 8, forms, page-controller
---


# Question
Title: Accessing a contrib module's code that has no service
I am doing custom work on a drupal site that uses particular installation profile. And the profile comes with a huge module that does a lot of work. Some of the classes I've been able to inject into my code.

I understand that interfaces aren't supposed to work this way, but it seems like, inside the module, the interface is passed as a parameter into a class's method and is assigned to a variable and that variable is an object. And from that an object is formed. I know I'm missing something here, I just don't know what.

This is an example of the code in the module:

```
public function userResult(OpignoModuleInterface $opigno_module) {
$step_info = opigno_learning_path_get_module_step($gid, $uid,
$opigno_module, $latest_cert_date);
$current_step_key = array_search($opigno_module->id(), array_column($steps, 'id'));
}

```

If I run it through Xdebug, $opingo\_module is an object. It appears to me that OpignoModuleInterface is assign to $opigno\_module. But I'm new to this. It becomes an object. And I really want that object for the custom code I'm creating.

So, this is how I configured my service:

```
    class: Drupal\ptc_after_course_redirect\RouteListener
    arguments: ['']
    tags:
      - { name: event_subscriber }

```

And then I passed the `OpignoModuleInterface` into the class's method just as they had. It didn't work, because there's no service associated with it. As you can see, the `argument` in the yaml file is empty. Nothing to put there.

Per @Clive, it does seem like an entity. Clive recommended this: `$this->entityTypeManager->getStorage('opigno_module')->load($id);`

But it comes back null.

# Answer
> 1 votes
There are two steps.

First you need OOP code to extend classes and override methods and properties. You can of course also rewrite a class from scratch implementing only the interface.

Second you need to implement your new code. If it is not a service which you can swap in the service container it is most times a plugin. Then the plugin manager for each plugin type controls which classes are used. In your case the plugin type is an entity type and its plugin manager is EntityTypeManager which provides a hook hook\_entity\_type\_alter() to replace the existing module code with your extended classes.

See How do I replace a plugin?

This is how to access class code of contrib modules. How these classes are instantiated is a different matter and there is no general answer, because some classes are when Drupal is bootstrapped, other only when a certain functionality is used. But by replacing the class code with your own you can be sure that your blueprint is used to create these instances.

---
Tags: 8
---


# Question
Title: Form in modal dialog: Template suggestions have weird random suffix
My custom module has the following form route in `mymodule.routing.yml`:

```
library_assets.add_asset_to_slideshow_modal:
  path: 'library/add-to-slideshow-modal/{library_asset_nid}'
  defaults:
    _form:  '\Drupal\library_assets\Form\AddAssetToSlideshow'
    _title: 'Add to Slideshow'
  requirements:
    _permission: 'access content'

```

And I have a link (in another form) which opens this form in a modal dialog:

```
$asset_display_title = $this->node->field_la_display_title->value;
$modal_form_url = Url::fromRoute('library_assets.add_asset_to_slideshow_modal', [
    'library_asset_nid' => $this->node->id(),
]);

$form['actions']['add_asset_to_slideshow'] = [
    '#type' => 'link',
    '#title' => $this->t('Add to Slideshow'),
    '#url' => $modal_form_url,
    '#attributes' => [
        'class' => [
            'use-ajax',
            'btn',
            'btn-blue',
            'w-100',
            'd-block',
        ],
        'data-dialog-type' => 'modal',
        'data-dialog-options' => json_encode(['title' => $asset_display_title, 'width' => 600]),
    ],
];

```

All of this is working – the form modal appears when I click on the "Add to Slideshow Link"; it validates correctly; the form submit handler works properly, etc.

But I'm having problems theming the form. When I inspect the contents of the modal to see the template suggestions, I see this:

```
<!-- FILE NAME SUGGESTIONS:
   * form--add-asset-to-slideshow--pv65ooQgrPE.html.twig
   x form.html.twig
-->

```

The `pv65ooQgrPE` part is different every time - it seems to be randomly generated (maybe a hash of some kind?).

I obviously can't create a template file if the filename is essentially random every time.

I tried omitting the suffix, and creating a template named `form--add-asset-to-slideshow.html.twig` – but that doesn't work.

What's going on here? Is this an intentional feature of Drupal? (If so, where is the documentation? I've been unable to find it.)

Most importantly, though: what's the proper way to theme my form, in this situation?

Thanks!

# Answer
> 2 votes
1. Create a .module in your module folder ex : **library\_assets/library\_assets.module**
2. in the .module file use this HOOK to create a new form suggestion that will be dynamically equal to **form--{form-id}.html.twig**

```
    <?php
/**
 1. Implement {module_name}_theme_suggestions_form_alter
 */
function library_assets_theme_suggestions_form_alter(array &$suggestions, array $variables) {
  //Get the form id
  // ! - you must replace the '-' with '_' in the suggestion name otherwise it won't work.
  $form_id = str_replace('-', '_', $variables['element']['#id']);

  // adding a new suggestion to the form template
  $suggestions[] =  'form__'.$form_id;
}

```
2. now clear the cache and inspect the code again and you'll find a new theme suggestion = **form--{form-id}.html.twig**

---
Tags: 8, theming, forms, modal-dialogs
---