title: Allow to login with e-mail

text:
# Question
Title: Allow to login with e-mail
As follow up to my previous issue, I think it would be nice to allow email address to be set as username for login. Or at least don't require username field and default it to use email address. This is a use case for Facebook login which allows email address be used as username for login.

When I try to run

```
{
"name": { "value": "test05@gmail.com" },
"mail": { "value": "test05@gmail.com" }
}

```

I am getting response

```
{
    "message": "Unprocessable Entity: validation failed.\nname: The username can not be an email address.\n"
}

```

# Answer
I use the Email Registration module.

Once enabled, during login the email address is required instead of the username. Usernames still cannot be email addresses, but usernames become irrelevant from a UX perspective.

From the module description:

> This module aims to solve some usability issues in managing accounts on a Drupal site.
>
> 1. On many sites the idea of a "username" is not necessary while the email is necessary. This module lets users register and login using only their email address and not worry about a username.
> 2. In general the fewer fields on a form the easier it is for people to finish the task. This module lets sites use just an email for registration form. How simple is just one form? So simple.
> 3. This module can be configured to allow a username as well for sites where that is important. In these cases, users get the benefit of being able to login with username or email which is helpful in case a user tries to login with their email (they are likely to remember their email even if they forgot their username).
> 1 votes

# Answer
My module Login with Email only should solve that:

> This simple Drupal 8 module allows users to log in **exclusively** with their email address from the standard login form.
>
> **Setup:**
>
> Just enable the module.
>
> **Recommended Modules:**
>
> This module is based on the Login Email or Username module: https://www.drupal.org/project/login\_emailusername
> 0 votes

# Answer
Use this module

https://www.drupal.org/project/mail\_login

This module enables users to login by email address with the minimal configurations. For configuring mail login go to "/admin/config/people/mail-login". Available configurations:

Enable login by email address: This option enables login by email address.
> 0 votes

---
Tags: 8
---


# Question
Title: How to let a boolean field on NULL if untouched?
Steps to reproduce my issue :

1- Add a boolean field to Article content type, not required, without touching default value

2- Edit an Article content without touching the boolean field.

3- Save the content.

4- The boolean field displays now FALSE value.

How to let this boolean field untouched and without any value (and thus hidden on full content display)?

# Answer
Not possible in the boolean field, it is possible to select the list field.

* Create a list(text) field. It should be a single value to act as a radio button.
* Change its widget from manage form display from the select list to checkbox/radio.

When you submit a node without touching the field it will have a null value.
> 1 votes

---
Tags: 9
---


title: How do you break up a .module file?

text:
# Question
Title: How do you break up a .module file?
I have a .module file that is nearly 1000 lines long. I have functions that i want to reuse from form submission in a cron job. What is the best way to abstract that to a better file?

# Answer
The `.module` file should contain only hook implementations. Other functions should be in classes as stateless static methods or as service object methods.

If the `.module` file contains only hook implementations and is still too big, use `require` statements to split up the file. Webform module uses this technique.
> 3 votes

---
Tags: 9
---


title: How do I render a radio inside a label?

text:
# Question
Title: How do I render a radio inside a label?
The drulma theme has a *form-element--type--radio.html.twig* template file that is rendering the radio input and then the label.

```
<div class="control js-form-item form-item js-form-type-radio form-item-age-bracket js-form-item-age-bracket">
  <input data-drupal-selector="edit-age-bracket-12-25" class="visually-hidden
    form-radio is-link radio" type="radio" id="edit-age-bracket-12-25"
    name="age_bracket" value="12-25">
   <label class="webform-options-display-buttons-label radio option">
    <!-- THEME DEBUG -->
    <!-- THEME HOOK: 'input__radio' -->
    <!-- FILE NAME SUGGESTIONS:
    x input--radio.html.twig
    x input--radio.html.twig
    * input.html.twig
    -->
    <!-- BEGIN OUTPUT from 'themes/contrib/drulma/templates/form/input--radio.html.twig' -->
     <!-- END OUTPUT from 'themes/contrib/drulma/templates/form/input--radio.html.twig' -->
12-25
  </label>
</div>

```

The template has 'children' enclosed within in the label

```
 <label{{ label_attributes }}>
  {{ children }}
  {{ label['#title'] }}
 </label>

```

How do I get the template to really print the radio enclosed in the label.

I have used 'buttons with flex' in webform as the display option for this widget. It looks nice, but does not work for me.

# Answer
While it is possible to include an `<input>` tag inside a `<label>` tag, it's not recommended because it can lead to unexpected behavior.

According to the HTML specification, the contents of a `<label>` tag should be a "textual label" that describes the purpose of the form control it's associated with. In other words, the label should be a description of the input field, not the field itself.

That being said, if you do include an `<input>` tag inside a `<label>` tag, the behavior can vary depending on the browser and the specific attributes used. Some browsers may display the input field as part of the label, while others may ignore the input tag entirely.

In most cases, it's better to use the `for` attribute to link the label to the corresponding radio input field using the `id` attribute, as this ensures proper behavior across different browsers and accessibility tools.
> 0 votes

---
Tags: theming
---


# Question
Title: How to create configurable plugins under a plugin manager?
I have a custom plugin type and annotation. The purpose of it is so people can develop their own clients for an API integration, and tell the system which plugin to use to perform these actions. I am able to load all defined plugins and add their sub forms on the main settings configuration form page I have implemented.

When the form submits, I can see the values go through to the appropriate plugin, but the values are not persisted nor in the database. I'm having trouble figuring out how to get this to save.

```
    $clients = $form_state->getValue('connections');

    if (!empty($clients)) {
      foreach ($clients as $key => $client) {
        $instance = $this->clientPluginManager->createInstance($key, []);
        $client_form_state = SubformState::createForSubform($form['connections'][$key], $form, $form_state);
        $instance->submitConfigurationForm($form['connections'][$key], $client_form_state);
      }
    }

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

```

In the plugin:

```
  /**
   * {@inheritdoc}
   */
  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
    $this->setConfiguration($form_state->getValues());
  }

  /**
   * {@inheritdoc}
   */
  public function setConfiguration(array $configuration) {
    $this->configuration = NestedArray::mergeDeep($this->defaultConfiguration(), $configuration);
  }

```

In my module (from looking around at other modules):

```
mymodule.default_client_configuration:
  type: config_entity
  label: 'Default client configuration'
  mapping:
    api_key:
      type: string
      label: 'The client API key.'
    hostname:
      type: string
      label: 'The hostname or base URI of the API.'

plugin.plugin_configuration.mymodule_client.*:
  type: mymodule.default_client_configuration

```

I am not entirely certain how the schema should be set. Do I also have to create a custom config entity class definition too? How does configuration make it from the form into config state?

# Answer
> 1 votes
To clarify the comment, it's true that instantiated plugins don't know from where they get the configuration, but you can add a method to the plugin manager to determine plugin id and configuration to instantiate with $this-\>createInstance().

To answer the question

*How to create configurable plugins under a plugin manager?*

with a minimal code example:

*PluginManager::getInstance*

```
  public function getInstance(array $options) {
    $settings = $this->configFactory->get('mymodule.settings');
    $plugin_id = $settings->get('default_client.plugin_id');
    $configuration = $settings->get('default_client.configuration');
    if ($plugin_id) {
      return $this->createInstance($plugin_id, $configuration);
    }
  }

```

A plugin by the way is not a form class. Even if it looks like this with build and submit methods, those are not fully working form functions. They are called by the corresponding methods of the parent form. The form instance is not the same as the plugin instance and the configuration is not saved to the database. You can do this in the parent form and by setting #tree you can usually submit the form structure as it is.

---
Tags: 9, configuration, plugins
---


# Question
Title: How do I use form #states greater than x?
I have a Drupal 8 site. How do I use `#states` to show an field if the value of another field is greater than one of my variables?

The code below is for =$chkval

```
$chkval = 4;
// This would be set by a database query

'#states' => array(
    'visible'=> array(
        ':input[name = 'actual_reps'] => array('value' => $chkval)
    )
)

```

# Answer
Unfortunately, Drupal does not support that.

> The following states may be applied to an element:
>
> * enabled
> * disabled
> * required
> * optional
> * visible
> * invisible
> * checked
> * unchecked
> * expanded
> * collapsed
>
> The following states may be used in remote conditions:
>
> * empty
> * filled
> * checked
> * unchecked
> * expanded
> * collapsed
> * value
>
> The following states exist for both elements and remote conditions, but are not fully implemented and may not change anything on the element:
>
> * relevant
> * irrelevant
> * valid
> * invalid
> * touched
> * untouched
> * readwrite
> * readonly
>
> When referencing select lists and radio buttons in remote conditions, a 'value' condition must be used:
>
> @code
>
> '#states' =\> array(
>
> // Show the settings if 'bar' has been selected for 'foo'.
>
> 'visible' =\> array(
>
> ```
> ':input[name="foo"]' => array('value' => 'bar'),
>
> ```
>
> ),
>
> ),
>
> @endcode

You can see state support value, but **value is array**, so can't use condition of value.

See the process states documentation page for more information
> 4 votes

# Answer
You can actually use this and regex by attaching the webform.states library to your form.

In your custom\_module.libraries.yml:

```
webform.states:
  version: VERSION
  js:
    /modules/contrib/webform/js/webform.states.js: {}
  dependencies:
    - webform/webform.behaviors
    - core/drupal.states

```

Then in your form, simply add:

```
$form['#attached']['library'][] = 'custom_module/webform.states';

```

Now you can use all of these:

```
  Drupal.states.Dependent.comparisons.Object = function (reference, value) {
    if ('pattern' in reference) {
      return (new RegExp(reference['pattern'])).test(value);
    }
    else if ('!pattern' in reference) {
      return !((new RegExp(reference['!pattern'])).test(value));
    }
    else if ('less' in reference) {
      return (value !== '' && parseFloat(reference['less']) > parseFloat(value));
    }
    else if ('less_equal' in reference) {
      return (value !== '' && parseFloat(reference['less_equal']) >= parseFloat(value));
    }
    else if ('greater' in reference) {
      return (value !== '' && parseFloat(reference['greater']) < parseFloat(value));
    }
    else if ('greater_equal' in reference) {
      return (value !== '' && parseFloat(reference['greater_equal']) <= parseFloat(value));
    }
    else if ('between' in reference || '!between' in reference) {
      if (value === '') {
        return false;
      }

      var between = reference['between'] || reference['!between'];
      var betweenParts = between.split(':');
      var greater = betweenParts[0];
      var less = (typeof betweenParts[1] !== 'undefined') ? betweenParts[1] : null;
      var isGreaterThan = (greater === null || greater === '' || parseFloat(value) >= parseFloat(greater));
      var isLessThan = (less === null || less === '' || parseFloat(value) <= parseFloat(less));
      var result = (isGreaterThan && isLessThan);
      return (reference['!between']) ? !result : result;
    }
    else {
      return reference.indexOf(value) !== false;
    }
  };

```

Like so:

```
'#states' => [
  'visible' => [
    ':input[name="custom_field"]' => ['value' => ['greater_equal' => '40']],
   ],
]

```
> 0 votes

---
Tags: forms
---


# Question
Title: Import a single configuration, producing errors - The configuration cannot be imported because it failed validation for the following reasons
Drupal 8.9.9

The configuration import for a single item import is not working. (I'm trying this on the same system to rule out any configuration mismatches between environments)

Steps

1. Create a simple view
2. Generate the single item export
3. Delete the view created in step 1
4. Import the configuraition created in step 2.

```
uuid: xxx
langcode: en
status: true
dependencies:
  config:
    - core.entity_view_mode.node.teaser
    - node.type.club_route
  module:
    - node
    - user
id: test_view
label: 'Test View'
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
display:
  default:
    display_plugin: default
    id: default
    display_title: Master
    position: 0
    display_options:
      access:
        type: perm
        options:
          perm: 'access content'
      cache:
        type: tag
        options: {  }
      query:
        type: views_query
        options:
          disable_sql_rewrite: false
          distinct: false
          replica: false
          query_comment: ''
          query_tags: {  }
      exposed_form:
        type: basic
        options:
          submit_button: Apply
          reset_button: false
          reset_button_label: Reset
          exposed_sorts_label: 'Sort by'
          expose_sort_order: true
          sort_asc_label: Asc
          sort_desc_label: Desc
      pager:
        type: some
        options:
          items_per_page: 10
          offset: 0
      style:
        type: html_list
      row:
        type: 'entity:node'
        options:
          view_mode: teaser
      fields:
        title:
          id: title
          table: node_field_data
          field: title
          entity_type: node
          entity_field: title
          label: ''
          alter:
            alter_text: false
            make_link: false
            absolute: false
            trim: false
            word_boundary: false
            ellipsis: false
            strip_tags: false
            html: false
          hide_empty: false
          empty_zero: false
          settings:
            link_to_entity: true
          plugin_id: field
          relationship: none
          group_type: group
          admin_label: ''
          exclude: false
          element_type: ''
          element_class: ''
          element_label_type: ''
          element_label_class: ''
          element_label_colon: true
          element_wrapper_type: ''
          element_wrapper_class: ''
          element_default_classes: true
          empty: ''
          hide_alter_empty: true
          click_sort_column: value
          type: string
          group_column: value
          group_columns: {  }
          group_rows: true
          delta_limit: 0
          delta_offset: 0
          delta_reversed: false
          delta_first_last: false
          multi_type: separator
          separator: ', '
          field_api_classes: false
      filters:
        status:
          value: '1'
          table: node_field_data
          field: status
          plugin_id: boolean
          entity_type: node
          entity_field: status
          id: status
          expose:
            operator: ''
            operator_limit_selection: false
            operator_list: {  }
          group: 1
        type:
          id: type
          table: node_field_data
          field: type
          value:
            club_route: club_route
          entity_type: node
          entity_field: type
          plugin_id: bundle
          expose:
            operator_limit_selection: false
            operator_list: {  }
      sorts:
        created:
          id: created
          table: node_field_data
          field: created
          order: DESC
          entity_type: node
          entity_field: created
          plugin_id: date
          relationship: none
          group_type: group
          admin_label: ''
          exposed: false
          expose:
            label: ''
          granularity: second
      title: 'Test View'
      header: {  }
      footer: {  }
      empty: {  }
      relationships: {  }
      arguments: {  }
      display_extenders: {  }
    cache_metadata:
      max-age: -1
      contexts:
        - 'languages:language_content'
        - 'languages:language_interface'
        - 'user.node_grants:view'
        - user.permissions
      tags: {  }
  page_1:
    display_plugin: page
    id: page_1
    display_title: Page
    position: 1
    display_options:
      display_extenders: {  }
      path: test-view
    cache_metadata:
      max-age: -1
      contexts:
        - 'languages:language_content'
        - 'languages:language_interface'
        - 'user.node_grants:view'
        - user.permissions
      tags: {  }

```

Error message:

```
The configuration cannot be imported because it failed validation for the following reasons:
Configuration block.block.bootstrap_comment_recent depends on the bootstrap theme that will not be installed after import.
Configuration block.block.bootstrap_search_form depends on the bootstrap theme that will not be installed after import.
Configuration block.block.bootstrap_system_main depends on the bootstrap theme that will not be installed after import.
Configuration block.block.bootstrap_user_new depends on the bootstrap theme that will not be installed after import.
Configuration block.block.bootstrap_user_online depends on the bootstrap theme that will not be installed after import.
Configuration block.block.color_glass_system_main depends on the color_glass theme that will not be installed after import.
Configuration block.block.eerrbootstrap_system_main depends on the eerrbootstrap theme that will not be installed after import.
Configuration block.block.eerr_system_main depends on the eerr theme that will not be installed after import.
Configuration block.block.multipurpose_zymphonies_theme_system_main depends on the multipurpose_zymphonies_theme theme that will not be installed after import.
Configuration block.block.tweme_system_main depends on the tweme theme that will not be installed after import.
Configuration bootstrap.settings depends on the bootstrap extension that will not be installed after import.
Configuration eerr2016.settings depends on the eerr2016 extension that will not be installed after import.
Configuration language.types depends on the Language module that will not be installed after import.
Configuration tweme.settings depends on the tweme extension that will not be installed after import.

```

Any help would be appreciated

# Answer
> 0 votes
I found the answer on Drupal.org:

To deal with this issue:

What I did in source website: run `drush cex` in config repository, remove \[non-existent theme\].settings.yml run `drush cim` run `drush cex` // to confirm export is clean deliver config files in target website What I did in target website run `drush cim` // or import all in synchronisation interface Now config import is OK.

# Answer
> 0 votes
Follow the post: https://www.drupal.org/project/drupal/issues/3144341

Deleting those configurations fix the issue.

```
drush config-delete "your-config-name"

```

---
Tags: 8, configuration, importing-exporting
---


# Question
Title: Site UUID in source storage does not match the target storage
When I run drush cim -y on my staging instance of my site I get the following error:

> Drupal\Core\Config\ConfigImporterException: There were errors validating the config synchronization. in Drupal\Core\Config\ConfigImporter-\>validate() (line 728 of /var/www/xeno/docroot/core/lib/Drupal/Core/Config/ConfigImporter.php).
> The import failed due for the following reasons: Site UUID in source storage does not match the target storage. Entities exist of type Paragraph and Paragraph type Image. These entities need to be deleted before importing.

This is what I did:

1. Copied the database from staging and pulled the latest code
2. Ran `drush cex -y`
3. Committed configuration
4. Went to staging and pull latest code
5. Ran `drush cim -y` and get the error stated above

When I run `drush cget system.site uuid` on local and staging I get the same uuid so I am not sure why I am getting this error.

How can I fix the error?

# Answer
> 3 votes
Actually, I was using SFTP deploy and the *system.site.yml* file wasn't present on the staging site. I just redeployed all files and the issue went away.

# Answer
> 12 votes
You can fix this by changing your local site UUID, with Drush, as described in one of the answers for How can I import the configuration on a different site?

I also needed to run a mix of `drush entity-updates -y` and installing specific modules with `drush en <module_name> -y` before the configuration objects would be imported on a new fresh database.

As per Support for automatic entity updates has been removed, you shouldn't use `drush entity-updates -y` anymore.

# Answer
> 12 votes
Traditionally, you would use the following before importing config:

```
drush cset system.site uuid "6ba9bxxx-xxxx-438x-9c8x-e5x7x3d0x347"

```

Or with drupal console:

```
$ drupal config:override

Enter configuration name []:
> system.site

Enter the configuration key [uuid]:
> uuid

Enter the configuration value:
> 6ba9bxxx-xxxx-438x-9c8x-e5x7x3d0x347

```

## A more generic solution

For people getting tired of doing this all over again. I finally got a fix on how to do this automatically. You would have to add a pre-command for config:import with drush to set the uuid. Read all about it here: https://stefvanlooveren.me/blog/fixing-systemsite-uuid-config-import-problem-drupal-8

# Answer
> 3 votes
Use `drush config-set "system.site" uuid <UUID value>`. You can confirm the value has been changed with `drush cedit system.site`.

To fix the shortcut error during the configuration import, use `drush ev '\Drupal::entityTypeManager()->getStorage("shortcut_set")->load("default")->delete();'`.

Manually edit the *system.site.yml* file, changing the UUID to match.

After that, the configuration import ran successfully on my port.

# Answer
> 2 votes
**Solved!** Using the Drush command. Get the site uuid from existing site the Set the UUID. That's it.

**Get UUID from Source:**

```
drush cget system.site uuid

```

**Set UUID to target:**

```
drush cset system.site uuid "c6a99459-912d-4502-b9d3-07dc6e5a240d"

```

# Answer
> 0 votes
Get UUID using: uuid:

```
drush cget system.site uuid

```

One need to paste the YML file back again to include UUID, this will resolve : /admin/config/development/configuration/single/import

---
Tags: 8, configuration
---


# Question
Title: What is the equivalent of field_view_field()?
I am working on some code where I need to get some fields from a node. For the body, I want to display the summary, or the trimmed body if the summary is not there.

I found an example on the web using field\_view\_field(), which seems to do what I want it to do: return the body field using the summary\_or\_trimmed formatter. But it seems this is a Drupal 7 thing. I can't seem to figure out what or if there is an equivalent.

The Drupal 7 example went something like this:

```
$x1 =  field_view_field('node', $node, 'body',
                                array('label'=>'hidden',
                                      'type' => 'text_summary_or_trimmed',
                                      'settings'=>array('trim_length' => 50)));

```

Note: I am trying to assemble text output for an email. I thought I could do this by making a custom display view mode, 'alt-teaser', that is like 'teaser' for my content type, but I don't see a way to get rid of the title (!), which I do not want. So that is why I now hope to get at individual fields, formatted.

```
 $viewBuilder = \Drupal::entityTypeManager()->getViewBuilder('node');
 $renderArray = $viewBuilder->view($node, 'alt_teaser');

```

# Answer
> 2 votes
Drupal's change records usually indicate when an API has been removed or changed. This one is documented: https://www.drupal.org/node/2208327

> In Drupal 8, those functions have been replaced by view() methods on, respectively, the FieldItemList object (i.e $entity-\>field\_name, "the list of field items") and the FieldItem object (i.e. $entity-\>field\_name\[$delta\], a specific item in the list). Their signatures have also been largely simplified.

---
Tags: views, 9
---


# Question
Title: How to migrate multi role to an user using migrate csv plugin
I have migrated bunch of users using the Migrate Source CSV and am succeeded in it, But I want multi-roles to be defined for single user.

Here is my config file,

Here is my following cases that I have tried out in a CSV file and it failed,

Is there is anything am missing out?

# Answer
> 3 votes
First put your all roles inside double quote separating by comma (,) and be sure that your all roles on csv file must be role's machine name.

then edit your config file something like this....

```
process:
  name: name
  mail: email
  pass: password
  roles:
    plugin: explode
    delimiter: ','
    source: roles
  status: status
destination:
  plugin: 'entity:user'
  md5_passwords: true

```

you have to use `plugin: explode` provided by drupal to import multiple roles separated by comma (,).

And if you are importing `password` also then use `md5_passwords: true`

# Answer
> 0 votes
I had the same issue those days and I came up with the following workaround (I don't think is the best but at least works!)

You roles should be comma separated in the CSV. Also, I had to make the match between the rids as in D7 the rid where numbers (eg 1,2,4) and in D8 it was strings (eg admin, default\_role, etc)

Create a custom module only for the migration and use `hook_user_presave` in your module file.

```
//check if the user has multiple roles comma separated
if( strpos($current_roles[1], ',') !== false )
{
  //split the roles
  $tmp = explode(', ', $current_roles[1]);
  foreach ($tmp as $role){
    //add each role separately to the user
    $user->addRole($role);
  }

  //remove the old roles added by the CSV (all user roles comma separated)
  $user->removeRole($current_roles[1]);
}
else{
  // Default role assigned to all other users - optional
  $user->addRole('default_role');
}

```

Also another way to do that is this but I didn't try it so I don't know if/how it works.

# Answer
> 0 votes
This is with ref to Drupal 8/9

Create your CSV with Roles column containing : **machine\_name** only seperated by only comma and no spaces around ex; **role\_update,role\_create**

Update CSV data accordingly, this is an example for reference

```
First Name Last Name Name      E-Mail     Roles
fname      lname     username  user@email role_update,role_create # No Space in the Cell of Roles data

```

Keep your CSV at public folder : **webroot/sites/default/files/User-List-D7.csv**

YML file should include the import in following way:

```
id: user_migration_csv
migration_tags: null
migration_group: Usermigration
label: 'User migration from CSV'
source:
  plugin: csv
  path: public://User-List-D7.csv
  header_row_count: 1
  ids:
    - ID
process:
  field_first_name: First Name
  field_last_name: Last Name
  name: Name
  mail: E-mail
  roles:
    plugin: explode
    delimiter: ','
    source: Roles

```

---
Tags: 8, migrations
---


# Question
Title: How to resend simplenews content for many times - Simplenews module
I use drupal 7 and simplenews module.I want to send a simplenews for several times.by default simple news module support onetime send to user.but i want to send simplenews to user for several times by UI . Is any way for resending support in simplenews module ?

Is any other module for edit contents before sending by mail and send it for several times to subscribers?

# Answer
> 0 votes
solution: simplenews Status Reset module .

# Answer
> 1 votes
I had a similar issue for Drupal 8. I created a module to do this: https://www.drupal.org/project/simplenews\_resend

This module adds a reset status link to the newsletter operations.

---
Tags: emails
---


title: How can I test Max-age on cache?

text:
# Question
Title: How can I test Max-age on cache?
I have a Controller using cache tags. I have just been asked a change that requires a `Max-age` also (show next event starting from current time); I explained that the only way to have this is with some delay, and we agreed for a max-age of 300.

To test the modification on my development machine, I set a shorter max-age of 60,

```
        return [
            '#theme' => 'mytheme',
            '#my_param' => $value,
             ...
            '#cache' => [
                'tags' => $cache_tags,
                'max-age' => 60,
            ],
        ];

```

I loaded the page just after a `drush cr`, and in the header I got a clear `X-Drupal-Cache: MISS`. I loaded it again, and I got a `HIT`. Then I waited for a minute... and I got a `HIT` again.

Should I be looking for something else? I noticed a `X-Drupal-Dynamic-Cache` header also, but it is always set to `MISS`.

EDIT: From the comments I understood that I cannot set a max-age on a page with anonymous access, so the question changes: is there any other workaround, apart from disabling cache on this page, that would have a big impact on performance?

# Answer
To extend on the comments. At this moment the only caching method implemented for anonymous traffic is cache tags. Context or max-age don't work. Cache tags are great for database content, this is the use case of Drupal out-of-the-box. But you can use cache tags for a time-based invalidation as well. In a cron job:

*mymodule.module*

```
use Drupal\Core\Cache\Cache;

/**
 * Implements hook_cron().
 */
function mymodule_cron() {
  $cache_tags = ['foo'];
  if (date('H') == 12 ) {
    Cache::invalidateTags($cache_tags);
  }
}

```

This invalidates the controller output every 24h at noon. Make sure to trigger cron at least every hour.

To achieve the max-age of 300, trigger cron every 5 min, or when events usually start in your schedule. Go to /admin/config/system/cron, get the link with the long hash code, and `wget` it in the cron table of your web host.

Then run the cron hook without condition:

```
function mymodule_cron() {
  $cache_tags = ['foo'];
  Cache::invalidateTags($cache_tags);
}

```

Don't forget to configure "Browser and proxy cache maximum age" in the Performance configuration page accordingly.
> 4 votes

---
Tags: caching
---


title: Turn off Twig cache

text:
# Question
Title: Turn off Twig cache
I did Disable Drupal 8 caching during development step by step ( also **finally rebuild the cache**) but yet my changes in `twig` files not take effects without rebuild the cache.

**sites/default/services.yml :**

```
parameters:
    session.storage.options: { gc_probability: 1, gc_divisor: 100, gc_maxlifetime: 200000, cookie_lifetime: 2000000 }
    twig.config: { debug: true, auto_reload: true, cache: false }

```

**sites/default/settings.php :**

```
 if (file_exists(__DIR__ . '/settings.local.php')) {
   include __DIR__ . '/settings.local.php';
 }

```

**sites/default/settings.local.php :**

```
$settings['container_yamls'][] = DRUPAL_ROOT . '/sites/development.services.yml';
$config['system.performance']['css']['preprocess'] = FALSE;
$config['system.performance']['js']['preprocess'] = FALSE;

```

**sites/development.services.yml :**

```
services:
  cache.backend.null:
    class: Drupal\Core\Cache\NullBackendFactory
parameters:
  twig.config:
    debug: true
    auto_reload: true
    cache: false

```

Where is the Problem?

# Answer
> 13 votes
I resolved my problem by following lines in settings.local.php.

```
# $settings['cache']['bins']['render'] = 'cache.backend.null';

```

duplicate the line and enable it (by remove # before it) and change `render` to `dynamic_page_cache` I mean the following line resolved your issue

```
 $settings['cache']['bins']['dynamic_page_cache'] = 'cache.backend.null';

```

in `Drupal >= 8.8` use

```
  $settings['cache']['bins']['render'] = 'cache.backend.memory';

```

# Answer
> 3 votes
In Drupal 8.8 adding this to sites/default/settings.local.php worked for me:

```
$settings['cache']['bins']['render'] = 'cache.backend.memory';

```

For some reasons when I try to add it as recommended ('cache.backend.**null**') I have the following error

```
Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException: You have requested a non-existent service "cache.backend.null". Did you mean one of these: "cache.backend.apcu", "cache.backend.memory", "cache.backend.php"?

```

# Answer
> 1 votes
For those who are struggling for configuring caching for Drupal 8, the following steps resolve the issue.

* Copy the example.settings.local.php which is located outside of the files folder into files folder with settings.local.php
* Uncomment `$settings['cache']['bins']['dynamic_page_cache'] = 'cache.backend.null';`
* Uncomment `$settings['cache']['bins']['page'] = 'cache.backend.null';` too

# Answer
> 0 votes
Now there is a **SUPER EASY** way to do this. Install the Mix module, and toggle the **Enable development mode** checkbox to **turn off/on twig cache and other caches with just a click**. No need to edit settings.php and \*services.yml files back and forth.

---
Tags: theming, 8, caching
---


# Question
Title: Font style isn't applied on PDF (entity print)
I'm using **Entity print** with my **webforms**. I've followed those steps https://www.drupal.org/node/2706755 to add the css into my PDF.

My classes are applied (color, padding...), but for some reason, the **fonts** are never applied on the generated PDF, but is applied on the debug : *https://.../print/pdf/webform\_submission/10/debug*

Font are not applied with CDN `@import url('https://fonts.cdnfonts.com/css/aphont');`

or with local font files

```
@font-face {
  font-family: "APHont";
  font-style: Italic;
  font-weight: 700;
  //URLs are OK
  src: url("./font/aphont-bold-italic.woff2") format("woff2"), url("./font/aphont-bold-italic.woff") format("woff");
}

body {
  font-family: 'APHont', 'Arial', serif; //even Arial is not applied
}

```

I'm using drupal 9.5.1, entity print 9.11 and DomPDF (i've been digging into others issues on entity print, but could'nt find any matching solution)

# Answer
> 0 votes
Works by doing this (thanks to this https://github.com/dompdf/dompdf/issues/1566#issuecomment-591955876) :

1. Using ttf
2. not apply font-family to the body
3. add `font-weight` to each tag you want to apply the font (h1, p...)

The result in css :

```
@font-face {
  font-family: "APhont";
  font-style: normal;
  font-weight: bold;
  src: url("./font/aphont-bold.ttf") format("truetype");
}

h1 {
  font-family: APhont, serif;
  font-weight: bold; //very important
}

```

---
Tags: webforms, importing-exporting
---


# Question
Title: How to set up drush on Cloudways / Digital Ocean?
I have been experimenting with Cloudways and Digital Ocean as a hosting solution.

I am attempting to set up Drush and have been following some instructions provided by GoDaddy.

The notes appear up to date and well written - See https://uk.godaddy.com/help/how-to-install-drush-on-cpanel-shared-hosting-12396 . I am not using cPanel but I do not think this should matter.

When entering

```
echo "alias drush='~/drush/drush'" >> .bashrc

```

The system returns

```
echo "alias drush='~/drush/drush'" >> .bashrc
bash: .bashrc: Permission denied

```

The same also happens with sudo

After continuing with rest of the instructions Drush is now installed but does not seem to be available for the public\_html directory. I'm guessing that the GoDaddy instructions are missing a step or are not quite compatible with this environment.

I have provided an example of a Features download (below) and as can be seen, the download is being placed in the drush directory and not sites/all/modules

```
[master_vkhgjqkyfv]:public_html$ drush dl features
Project features (7.x-2.8) downloaded to /home/master/.drush/features.

```

Can anybody say what I need to do to get this working properley?

# Answer
I'd recommend trying the official Drush installation instructions. Note that the first recommended installation method is to download the phar, which you might find easier to get working than the Composer-based instructions.

The GoDaddy instructions (at the time of this writing) recommend installing the latest unstable version of Drush. This is not recommended unless you are planning on frequently updating Drush, in which case you are better off installing via git rather than downloading an archive.
> 1 votes

# Answer
It is preinstalled so installing is not required if you install Drupal through single click installer.

If you have launched php stack and installed Drupal manually then you won't be able to install Drush.

This article might help you: https://support.cloudways.com/setting-up-drush-remote-aliases-for-drupal-sites-on-cloudways/
> 1 votes

# Answer
You need to define the full path of your .bashrc file

```
echo "alias drush='~/drush/drush'" >> ~/.bashrc

```
> 0 votes

# Answer
The support said that drush launcher is preinstalled by default but it wasn't on my newest instance. They promised me to check it out but nothing happened so far, so here is my workaround.

Cloudways doesn't allow to edit .bashrc but `.bash_aliases` is editable. I also needed some other customizations like downgrading composer. So I created a directory in `$HOME` and set up as `$PATH`.

```
cd ~
mkdir run
echo "export PATH=$PATH:/home/master/run" >> ~/.bashrc
cd run
wget -O drush.phar https://github.com/drush-ops/drush-launcher/releases/latest/download/drush.phar
chmod +x drush.phar
mv drush.phar drush

```
> 0 votes

# Answer
My solution for getting remote drush commands to work on a Cloudways server (since they restrict editing *~/.bashrc*) was to provide the path to the remote drush script as part of my local aliases file:

```
...
    'path-aliases' =>
            array (
            '%drush-script' => '/home/master/.composer/vendor/drush/drush/drush',
        ),
...

```

Everything worked normally after that.
> 0 votes

---
Tags: 7, drush
---


# Question
Title: How can I use Url::fromRoute() in a test?
If I crate the class for test like:

```
class ReturnHandlerTest extends OrderKernelTestBase {

  protected static $modules = [
    'commerce_payment',
    'commerce_payment_example',
  ];

  protected function setUp(): void {
    parent::setUp();
  }

}

```

And if I want to use

```
 Url::fromRoute('commerce_payment.checkout.return', $arguments)
   ->setOptions(['absolute' => true])
   ->toString()

```

In the test, I get this exception.

> Symfony\Component\Routing\Exception\RouteNotFoundException: Route "commerce\_payment.checkout.return" does not exist.

What should I do?

# Answer
> 1 votes
I should include the module "commerce\_checkout".

```
 protected static $modules = [
    'commerce_payment',
    'commerce_payment_example',
    'commerce_checkout',
  ];

```

---
Tags: testing
---


# Question
Title: How can I trigger a Drupal Core action without writing any PHP code?
On my D8 site I have configured an action of type "Send email" using the Drupal Core "Actions" module (module can be installed under "Administration \> Extend"). I want this Drupal Core action to trigger when a new comment is submitted.

In D7 there was another Drupal Core module named "Trigger" which allowed me to configure the desired trigger. In D8 there's no longer a "Trigger" module, nor can I find any other means to configure any Drupal Core action triggers. In the D8 docs I have found this not-very-helpful paragraph:

> In D7, there are triggers defined, supporting the core Trigger module. This is not supported in D8.

My general question therefore is: How can I configure a D8 site to trigger an action **that was defined via the Drupal Core module "Actions"** when a certain event occurs? Specifically in my case: How can I trigger a "send email" action when a comment is submitted?

As the title of this question mentions, I don't want to write PHP code to achieve this. I am willing to install a stable third-party module, though, **as long as it cooperates with the Drupal Core "Actions" module**.

In a wider sense, I am trying to understand what the Drupal Core "Actions" module does. To me, the module seems to be quite useless if as a site admin I cannot configure triggers for its actions.

# Answer
> 0 votes
You should have a look at ECA - event, conditions, actions: https://www.drupal.org/project/eca

---
Tags: 8, actions
---


title: Are media entity Ajax calls cached?

text:
# Question
Title: Are media entity Ajax calls cached?
In my Drupal 9 site with CKEditor 4 and I have created "media gallery" media type, which displays set of images (using slideshow module). So, when I click "insert media" CKEditor button for tool bar I have media selector popup, I can select and insert media gallery and that works well.

Only problem is that when media is inserted, that AJAX call is cached. So if I for example edit gallery, add another image or remove one and I go to insert that gallery in CKEditor I get old gallery, in state before my recent editing.

When I look at entity media it self it gets updated immediately, but AJAX call CKEditor makes is cached.

How can I disable that caching? Or force cache rebuild when gallery is saved?

**Update:**

I figured out that at: web/core/modules/media/src/Controller/MediaFilterController.php preview method is setting MaxAge value to 300 (5 minutes).

```
    // Note that we intentionally do not use:
// - \Drupal\Core\Cache\CacheableResponse because caching it on the server
//   side is wasteful, hence there is no need for cacheability metadata.
// - \Drupal\Core\Render\HtmlResponse because there is no need for
//   attachments nor cacheability metadata.
return (new Response($html, 200, $headers))
  // Do not allow any intermediary to cache the response, only the end user.
  ->setPrivate()
  // Allow the end user to cache it for up to 5 minutes.
  ->setMaxAge(300);

```

If I change parameter to 0 it starts working well. Now, is there a reason why this caching is needed and what would be the optimal way to solve this issue? I saw that this code has long history, yet since it was a separate module:

https://www.drupal.org/project/entity\_embed/issues/3182698

# Answer
Solved this by overriding the class and route that was using original's class method. So, I have route subscriber defined like:

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

```

Then I have route subscriber it self like:

```
<?php

namespace Drupal\my_module\Routing;

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

/**
 * Listens to the dynamic route events.
 */
class RouteSubscriber extends RouteSubscriberBase {

  /**
   * {@inheritdoc}
   */
  protected function alterRoutes(RouteCollection $collection) {
    // Overriding default MaxEge (5 minutes) value of API response.
    if ($route = $collection->get('media.filter.preview')) {
      $route->setDefault('_controller', '\Drupal\my_module\Controller\MyModuleMediaFilterController::preview');
    }
  }

}

```

And finally I have class override:

```
<?php

namespace Drupal\my_module\Controller;

use Drupal\filter\FilterFormatInterface;
use Drupal\media\Controller\MediaFilterController;
use Symfony\Component\HttpFoundation\Request;

/**
 * MediaFilterController override.
 */
class MyModuleMediaFilterController extends MediaFilterController {

  /**
   * Returns an HTML response containing a preview of the text after filtering.
   */
  public function preview(Request $request, FilterFormatInterface $filter_format) {
    // Overriding default MaxAge value of 300 (5 minutes).
    return parent::preview($request, $filter_format)->setMaxAge(0);
  }

}

```

Hopefully this will help someone and save some time. Maybe some condition can be added to preview override, like remove caching only for back-end or similar.
> 1 votes

---
Tags: media, 9, caching, wysiwyg
---


# Question
Title: Responsive Image Module is only selecting fallback image style
I recently tried implementing drupal 8's Responsive Images Module. I studied about it's basics and implemented according to the guides available online. But when i try to use it on any image on the site, its only selecting the Fallback Image Style. I have defined 5 breakpoints and 5 image styles for each breakpoint.

My breakpoint.yml is as follows:

```
investindia.big_screen:
  label: big screen
  mediaQuery: '(min-width: 1025px)'
  weight: 4
  multipliers:
    - 1x
investindia.tablet_landscape:
  label: tablet landscape
  mediaQuery: '(min-width: 769px)'
  weight: 3
  multipliers:
    - 1x
investindia.tablet:
  label: tablet portrait
  mediaQuery: '(min-width: 481px)'
  weight: 2
  multipliers:
    - 1x
investindia.smalltouch_landscape:
  label: smalltouch landscape
  mediaQuery: '(min-width: 321px)'
  weight: 1
  multipliers:
    - 1x
investindia.smalltouch:
  label: smalltouch portrait
  mediaQuery: '(min-width: 1px)'
  weight: 0
  multipliers:
    - 1x

```

Edit : Adding my responsive image style configuration for further clarification on the issue.

# Answer
> 1 votes
The posted responsive image configuration seems correct. Remember that you also need to change the image field formatter to "Responsive image". If you are using image fields on node entities, you can do this at

`/admin/structure/types/manage/MY_NODE_TYPE/display/MY_VIEW_MODE`

(If you are using image media entities instead of image fields, use the "Rendered entity" field formatter on the node, and switch to the "Responsive image" field formatter on the media entity at `/admin/structure/media/manage/MY_MEDIA_TYPE/display/MY_VIEW_MODE` )

# Answer
> 0 votes
Set the `mediaQuery` field to empty for your smallest breakpoint

```
investindia.smalltouch:
  label: smalltouch portrait
  mediaQuery: ''
  weight: 0
  multipliers:
    - 1x

```

Here's my configuration and it works:

```
THEME_NAME.small:
  label: small
  mediaQuery: ''
  weight: 0
  multipliers:
    - 1x
    - 2x
THEME_NAME.small-med:
  label: small-med
  mediaQuery: 'all and (min-width: 26em)'
  weight: 1
  multipliers:
    - 1x
    - 2x
THEME_NAME.small-large:
  label: small-large
  mediaQuery: 'all and (min-width: 41em)'
  weight: 2
  multipliers:
    - 1x
    - 2x
THEME_NAME.medium:
  label: medium
  mediaQuery: 'all and (min-width: 48em)'
  weight: 3
  multipliers:
    - 1x
    - 2x
THEME_NAME.large:
  label: large
  mediaQuery: 'all and (min-width: 61.25em)'
  weight: 4
  multipliers:
    - 1x
    - 2x

```

# Answer
> 0 votes
Might be worth checking the order of your filters in the text format if you are embedding these responsive images as Media items into text fields (such as Body).

I had problems because the 'Convert line breaks...' filter was after the Embed media and align images filters. Once I moved the 'Convert line breaks' filter above them, the correct HTML was generated and the correct responsive image was being rendered.

---
Tags: 8, theming
---


# Question
Title: How to hide the "Add new comment" link on the add new comment page
In Drupal 8+, the "Add comment" form can be shown on a separate page by going to **Manage fields** for the content type that has comments and unchecking the box *Show reply form on the same page as comments.*

This shows an **Add new comment** link on the node (in this case, an article):

The UX problem is that when you click the **Add new comment** link, you are taken to a new path (for example, `comment/reply/node/2/comment#comment-form`) but the **Add new comment** link is still there:

This is confusing because clicking the link merely returns you to the page you are already on, so it may appear that nothing has happened.

How can I remove this "Add new comment" link only on entities that have the comment form embedded (`/comment/reply/entity/entity_id`)?

# Answer
> 1 votes
```
/**
 * Implements hook_preprocess_HOOK().
 */
function mymodule_preprocess_links__node(&$variables) {
  if (\Drupal::routeMatch()->getParameter('field_name') === 'comment') {
    unset($variables['links']['comment-add']);
  }
}

```

# Answer
> 0 votes
Something like this should work (assuming you dont want it on the teaser too if its there):

```
function hook_preprocess_node(&$variables) {
  // Remove the "Add new comment" link on teasers or when the comment form is
  // displayed on the page.
  if ($variables['teaser'] || !empty($variables['content']['comments']['comment_form'])) {
    unset($variables['content']['links']['comment']['#links']['comment-add']);
  }
}

```

---
Tags: comments, 10
---


# Question
Title: Custom links.task for secondary tab loses primary tabs
I created a Views page and wanted to place it as a secondary task (tab) under admin/content.

I added an entry to my custom module's links.task like this:

```
mycustom.admin_orphaned_pages:
  title: Orphaned pages
  route_name: view.orphaned_pages.page_1
  parent_id: system.admin_content

```

and this works to now add a secondary task under the Admin Content page.

The issue is, when I go to this page, it loses the primary (and therefore other secondary) tabs. Is there something missing here?

# Answer
> -1 votes
I assume your configuration here is:

1. The system.admin\_content path is '/admin/content'
2. Your view's path is something **not** '/admin/content/{your path}'

=\> when you access '/admin/content', '/admin/comment' you will see your tab, but when you enter {your path}, the tabs content disapeared.

**Quick fix**: change your view path to '/admin/content/{your path}' (must include the system.admin\_content path as parent here)

Known issue for quick fix: Because your links.task.yml define 'parent\_id: system.admin\_content' as parent\_id, when you access '/admin/content/{your path}, both /admin/content and '/admin/content/{your path} will be set as active.

**=\> Completed fix**:

* set the path in your view to '/admin/content/{your path}' (or the path if you override system.admin\_content path + /{your path})
* change 'parent\_id: system.admin\_content' to 'base\_route: system.admin\_content'

---
Tags: views, 9
---


title: No route found for POST

text:
# Question
Title: No route found for POST
I've been working on a custom rest module for Drupal. I wrote the following Resource class.

```
    <?php

namespace Drupal\aker_platform\Plugin\rest\resource;

/**
 * Provides a License GET Resource
 *
 * @RestResource(
 *   id = "license_resource",
 *   label = @Translation("License Resource"),
 *   uri_paths = {
 *     "canonical" = "/aker/api/license/{id}",
 *     "https://www.drupal.org/link-relations/create" = "/aker/api/license",
 *
 *   }
 * )
 */

class LicenseResource extends ResourceBase {

  /**
   * Responds to entity GET requests.
   * @return \Drupal\rest\ResourceResponse
   */
  public function get($id) {
    // Bla bla code for GET requests
  }

  /**
   * Responds to entity POST requests.
   * @return \Drupal\rest\ResourceResponse
   */
  public function post($data) {
     // bla bla for post requests.
  }

}

```

The get route works well, i enabled the routes using Rest UI:

My custom skeleton structure:

```
src
 -Entity
   LicenseEntity.php
 -Plugin
   -rest
    -resource
     LicenseResource.php
aker_platform.info.yml

```

To test the endpoint, i'm using curl:

```
curl --header "Content-Type: application/json" \
  --request POST \
  --data-binary '{"username":"xyz","password":"xyz"}' \
  mydomain/aker/api/license?_format=json

```

Also the endpoint was added to Anonymous guests to perform the requests without authentication.

# Answer
> 2 votes
You need to set `Content-Type` to `application/vnd.api+json`.

> ## Headers
>
> The following headers are **required** on all POST request to get a proper JSON:API request and response.
>
> ```
> Accept: application/vnd.api+json
> Content-Type:application/vnd.api+json]
>
> ```

See: https://www.drupal.org/docs/core-modules-and-themes/core-modules/jsonapi-module/creating-new-resources-post#s-headers

---
Tags: 8, entities
---


title: Cannot apply patch; file not found

text:
# Question
Title: Cannot apply patch; file not found
There is a specialized fix to a contrib theme that I am trying to apply. Because this theme is in the .gitignore file, it's incumbent upon me to generate a patch that composer can include. Please note that the dependency "cweagans/composer-patches" is required. Pantheon is using the composer.lock file and I cannot get `composer update` to find the target file. I built the patch file using diff and modified its headers like other successful Drupal patches. In composer.json

```
"patchLevel": {
        "drupal/core": "-p2",
        "drupal/dxpr_theme": "-p1"
    },
"enable-patching": "true",
    "patches": {
      "drupal/dxpr_theme": {
        "header patch": "./patches/header.patch"
      }

```

and the headers of header.patch

```
index fakehash..fakehash1 100644
--- a/js/dist/dxpr-theme-header.js
+++ b/js/dist/dxpr-theme-header.js
@@ -4,7 +4,7 @@

```

Since there is no git commit with this theme I faked a couple of hashes but left the line in, thx for the comment @Clive. Ensuring that in context of this theme folder I was able to successfully run the patch using `patch -p1 < ../../../../patches/header.patch`

In context of the Drupal project I ran: `composer --verbose update ---lock`

> Applying patches for drupal/dxpr\_theme ./patches/header.patch (header patch) patch '-p1' --no-backup-if-mismatch -d 'web/themes/contrib/dxpr\_theme' \< '/Users/sam/apps/myproject/patches/header.patch' patching file js/dist/dxpr-theme-header.js Reversed (or previously applied) patch detected! Assume -R? \[n\] Apply anyway? \[n\] Skipping patch.

What's curious is that the file is updated but I find no evidence in composer.lock. Perhaps it is and I don't know how it's indicated? I have removed any file with .rej, .orig and reset the target file to before any patch attempts. Same state as my successful attempt at patch by itself, apart from composer.

As mentioned, if I can get composer to update this file after patching I can then see if the composer.lock file is applying this patch.

# Answer
It needs to be

```
"header patch": "./patches/header.patch"

```

and then run

```
composer update --lock

```

and it will apply the patch.
> 1 votes

---
Tags: composer
---


# Question
Title: Get Submission ID in ajax_callback hook / after submitAjaxForm
I'm looking to dig up the submission ID in code that I execute in the ajax\_callback hook. My code looks approximately like this:

```
function webform_to_datalayer_ajax_callback(&$form, FormStateInterface $form_state) {

  /** @var \Drupal\webform\WebformSubmissionForm $form_object */
  $form_object = $form_state->getFormObject();

  if ($form_state->hasAnyErrors()) {
    $response = $form_object->submitAjaxForm($form, $form_state);
  }
  else {
    $values = $form_state->getValues();
    /** @var \Drupal\webform\Ajax\WebformSubmissionAjaxResponse $response */
    $response = $form_object->submitAjaxForm($form, $form_state);

    /* Right here there should be a submission, right?
     * So how do I find the id?
     */
    $response->addCommand(new DatalayerCommand('form_submit', $form_object->getBaseFormId(), $values));
  }

  return $response;
}

```

I imagine that after the submitAjaxForm call the submission should have been created, so I need a way to find the sid, and add it to my Command.

# Answer
> So how do I find the id?

Try

```
$sid = $response->getWebformSubmission()->id();

```
> 1 votes

---
Tags: webforms, 9
---


# Question
Title: Add soft hyphen () in titles (in views) or other method to break words in a language-sensitive way
I have an Austrian site here (and the German language loves compound nouns).

A view produces a raster of 6 article teasers, but some titles exist of one single word of more than 30 characters - and no, it cannot be shortened because it is a proper name.

I'd like to be able to manually add  tags into these titles, as everything else will not be language-sensitive. CSS word-wrap or word-breaks are no options (not breaking at syllable boundaries). Also, adding an ellipsis is also not an option.

This is Drupal 8, so no "HTML Title" modules available.

The theme does a simple

```
{% if title %}
  <h1>{{ title }}</h1>
{% endif %}

```

I am aware of possible security implications and only want to allow this one tag. Alternatively, I am open to other suggestions fulfilling the stated need!

# Answer
> 5 votes
Our workaround inhouse: We've trained our editors to use Alt + 240 (hold alt key and press 2 4 0 on numeric pad) to manually add soft hyphens in title fields.

Is a bit weird because your editors don't see the hyphen in a text input field, but you can prove its existance by deleting it (backspace after such a hyphen will NOT delete the visible letter on first press)

# Answer
> 2 votes
I see two options:

1. Add some JS hyphenation library with a German dictionary included. After you attached the library to your page this probably fires on just some CSS class you need to have added to your titles. For example: `<h1 class="wordbreak-here">`.

Side note: More than one `<h1>` on a single page is bad for SEO.
2. Introduce a new text field (formatted) and print that as title. Then editors can add whatever markup you allow them to add.

You may want to have a look at Automatic Entity Label which lets you hide the normal node title field and retrieve that (maybe sanitized then) from your new title text (formatted) field.

---

Last but not least, search for "Drupal 8 markup node title" or something similar. I found HTML Titles for Drupal 8 under one of the first results. Sounds promising.

# Answer
> 0 votes
You can use the **css "hyphens"-property** to control the (automatic) hyphenation of text (for example for the h1-title):

```
h1 {
hyphens: auto;
}

```

It is language-sensitive, controlled by the lang-attribute of the parent element. You must correctly tag your content's language in order to obtain correct automatic hyphenation, for example by setting the lang-attribute to your document:`<html lang="de">`.

In most cases, your theme should set the attribute automatically, depending on the language of your website. You find the settings here:

```
/admin/config/regional/language

```

But just in case, you can globally change it by setting a variable ("de" is the ISO 639-1 Language Code for german):

```
$variables['html_attributes']->setAttribute("lang", "de");

```

Add the line of code to the function `[mytheme]_preprocess_html(&$variables)` in the file \[mytheme\].theme

More information about the css-property can be found here: https://www.w3.org/TR/css-text-3/#hyphenation

---
Tags: 8
---


# Question
Title: Manage config translations programmatically
My module creates and manages some config\_entities programmatically (ie. not through an EntityForm) in response to associated content entities being saved.

In order to handle multi-language content entities, I see two options:

* create one config entity and add translations for it
* create a unique config entity for each translation

The first seems cleaner - however, I don't see how to actually transparently manage translations for config entities. Translating an entity with the `config_translation` module appears to create a new entry in the `config` table with `collection = language.xx`, but I don't see how to access a translated entity explicitly (rather than it being automatically mapped by the site's interface language). Is this even possible?

# Answer
> 14 votes
The answer from @Smartsheet eng is correct if you want to *load* and read a specific translation as a config entity, see also How to progragrammatically get NodeType translated value in D8?.

However, changing them is different, because the config entity can't be saved like that, it doesn't understand what is a translation and what's not. It will just overwrite your default language with that data. See also https://www.drupal.org/project/drupal/issues/2910353.

To actually manage a translation of a config entity, you have to directly interact with the config system, you can see how in \Drupal\config\_translation\Form\ConfigTranslationFormBase::submitForm():

```
$config_translation = \Drupal::languageManager()->getLanguageConfigOverride($langcode, $config_name);

// Write the keys you want to translate on the config object.
$config_translation->save();

```

# Answer
> 8 votes
As far as I can see -- yes, this is possible but it's not as pretty as `$contentEntity->getTranslation($langcode);`. The full enchillada would be:

```
protected function getTranslatedConfigEntity(ConfigEntityInterface $configEntity, LanguageInterface $language) {
  $langcode = $language->id():
  /** @var \Drupal\Core\Config\Entity\ConfigEntityTypeInterface $configEntityType */
  $configEntityType = $configEntity->getEntityType();
  $name = $configEntityType->getConfigPrefix() . '.' . $configEntity->id();
  $translatedConfig = $this->languageManager->getLanguageConfigOverride($langcode, $name);
  $translatedConfig[$configEntity->getKeys('langcode')] = $langcode;
  $class = $configEntityType->getClass();
  return new $class($translatedConfig->get(), $configEntityType->id());
}

```

An alternative would be:

```
public function getTranslatedConfigEntity(ConfigEntityInterface $configEntity, LanguageInterface $language) {
  $currentLanguage = $this->languageManager->getConfigOverrideLanguage();
  $this->languageManager->setConfigOverrideLanguage($language);
  $translatedConfigEntity = $this->entityTypeManager
    ->getStorage($configEntity->getEntityTypeId())
    ->load($configEntity->id());
  $this->languageManager->setConfigOverrideLanguage($currentLanguage);
  return $translatedConfigEntity;
}

```

# Answer
> 0 votes
Adding translation from code (in my case update hook) should look something like:

```
$my_field_translation = \Drupal::languageManager()->getLanguageConfigOverride('de', 'field.field.node.my_conent_type.my_field_name');
$my_field_translation->set('label', 'German Label');
$my_field_translation->save();

```

To get exact name of config file ('field.field.node.my\_conent\_type.my\_field\_name') I added translation manually for field, exported config ("drush cex") and saw what config file was changed...

---
Tags: 8, configuration
---


# Question
Title: How do I debug A block ajax issue on a custom theme?
I'm receiving and ERROR " An error occurred while attempting to process /views/ajax?\_wrapper\_format=drupal\_ajax: ajax.$form.ajaxSubmit is not a function" when clicking on link in views block on exposed form using the taxonomy terms and better exposed filters.

I have tried already attach the views.ajax library to the exposed filter form. it not sorted the issue. Js aggregation also turned off.

I'm using Bootstrap Bario theme as a base and switching to it gives me same error. Core themes works fine.

How do i debug this issue or even possible solution for it?

# Answer
> 0 votes
I have discovered the issue:

In custom theme I have library which had dependency of jQuery which wasn't core library (that how i got html js css template from the themeforest) I have deleted it and as a dependancy set the core/jQuery in my theme.libraries.yml file. this is solved my issue. This might be helpful to anyone experiencing such an issue and as a step to debug same issue just check that libraries are dependant on core/jquery library

---
Tags: views, ajax, 9
---


title: List of fields to put on a REST call

text:
# Question
Title: List of fields to put on a REST call
I have a rest resource configured from Rest UI page (let's say /entity/user: POST); I would like to share the endpoint with others devs so they can call the API (create users for e.g.). **Where to get the list of fields to put on request body? also their structure (value or target\_id..)?**

Do you know a contrib module like this one that did the job for D7? https://www.drupal.org/project/rest\_api\_doc

Is there any programmatic practical way to have that? an API call or a drush command?

# Answer
I have discovered the module named OpenApi which provides a rest resource to describe the activated rest (and json) resources. It can be combined with two other modules OpenApi UI and ReDoc to have a nice user interface.
> 0 votes

---
Tags: 8
---


# Question
Title: is there a twig filter to correct the HTML?
the slice filter on html code can generate invalid html that can cause in cascade JS errors

```
 {% set text = paragraph.field_verbatim_text.value %}
 {% if text|striptags|length > 300 %}
    {{ text|slice(0, 310)|raw }} […]<a class="verbatim__suite" aria-label="{{ 'Read the full text'|t }}"
                                                       href="#verbatim-{{ paragraph.id() }}"
                                                       data-open="verbatim-{{ paragraph.id() }}">{{ 'Read more'|t }}</a>

```

# Answer
> 5 votes
Instead of the insecure `|raw` filter use the render element `processed_text`. This applies the filters of the text format and marks the filtered result as safe so that you don't need the `|raw` filter anymore:

```
{% set text_sliced = {
  '#type':   'processed_text',
  '#text':    paragraph.field_verbatim_text.value|slice(0, 310),
  '#format':  'basic_html',
} %}

{{ text_sliced }}

```

Most text formats should correct the HTML out of the box. If the text format the field uses now doesn't work, configure a custom text format for this purpose and enable the "Correct faulty and chopped off HTML" filter.

---

**Never output user input unfiltered**

The link in the comment is about the raw filter not being deprecated, a false alarm when you are confusing it with the verbatim tag. Nonetheless, the security issue is about bypassing the autoescape mechanism, in Drupal enabled by default for any template. One way is the raw filter, but there are many others, in Twig and PHP. Only do this if you are sure it's safe. User input should never be considered as safe.

---
Tags: theming
---


# Question
Title: Why precisely the twig raw filter is unsecure?
Why precisely the twig raw filter is insecure in the drupal context?

What would be an example of a potential threat ?

And what need to be done on the ckeditor field format side to protect potential malicious code, if the raw filter is used ?

# Answer
Twig templates escape special characters to prevent potential hacking. Twig raw puts out the data without escaping it, meaning that if it is user-supplied data, it's insecure and could be used for hacking.
> 4 votes

---
Tags: theming
---


title: Double click action in CKEditor 4?

text:
# Question
Title: Double click action in CKEditor 4?
I have D9.5 site with CKEditor 4 and media library module. In CKEditor, when I double click on some existing media I get popup to select new media to replace clicked one. After selecting new visually new one replaces old one. Old is not visible any more, but it's still there. I.e. if I click "Source" button I can see both in source. If I switch back to visual mode both are shown. Is this some kind of bug or configuration failure? I'm not finding any discussion on this issue so I wonder if maybe I didn't configure it correct.

# Answer
> 1 votes
I sounds like CKeditor is not cleaning up after it self. I would not rely on that method for replacing media, but rather delete the old media and then insert the new. That gives you a much better chance of having clean code. I would document the behavior with screen shots and send it it all the Ckeditor team so they can see if they have to update the function that cleans up the generated code. Also, if you can, I would see if you can duplicate the problem in other builds and other places, the more you can duplicated it the stronger case you will have for it being a bug :)

---
Tags: media, 9, wysiwyg
---


title: Format telephone numbers for values

text:
# Question
Title: Format telephone numbers for values
I have created a webform with telephone field, the submitted data is displayed in a views table, however the numbers in submitted data are in random format such as 1234567890, 234-987-9870, how do I configure the number field in views to be displayed in (123) 123 1234 format for the existing values and new submission.

# Answer
> 0 votes
If you are able to use Jquery here is a solution:

$('.phone').text(function(i, text) { return text.replace(/(\d\d\d)(\d\d\d)(\d\d\d\d)/, '$1-$2-$3'); });

http://jsfiddle.net/Xxk3F/1/

Answer found at: https://stackoverflow.com/questions/8760070/how-to-format-a-phone-number-with-jquery

---
Tags: views, webforms, 9
---


# Question
Title: How to check for content type in hook_form_alter
I see from the api and various posts that I can identify a specific form to change using hook\_form\_alter, like this: hook\_form\_FORM\_ID\_alter()

From experience, I know this can be done in the .theme file along these lines...

```
function MYTHEME_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  if($form_id == 'node_pm_form' || $form_id == 'FORMIDHERE') {
    $form['actions']['submit']['#value'] = t('Send');
  }

```

But I have a form that occurs on different node types and I want to change the submit button label based on the node type. I tried renaming the function like this... function `MYTHEME_form_alter__NODETYPE` and `MYTHEME_form_alter__NODETYPE__full`

That didn't work. I'm thinking form\_alter just doesn't accept node type in the function name, so I need some logic in the code to check for node type and condition results on that. But I don't yet know how to write that.

**I forgot to mention**: the node edit form ID's in my theme include the content type. But I want to alter the **comment edit form** based on what node type replying to. And all my comment forms have the same id.

# Answer
To get the content type on content type forms, you can get the form object, then the node from that, and finally the bundle (content type):

```
$form_object = $form_state->getFormObject();
$node = $form_object->getEntity()
$content_type = $node->bundle();

```

When altering a comment form, to get the entity type of the entity the comment is attached to, you have to fetch the commented entity before getting the bundle.

```
$form_object = $form_state->getFormObject();
$comment = $form_object->getEntity();
$node = $comment->getCommentedEntity();
$content_type = $node->bundle();

```

So your function would look something like

```
function HOOK_form_alter(array &$form, FormStateInterface $form_state, $form_id) {
  $comment_form_id = '[ENTER COMMENT FORM ID HERE]';
  if ($form_id == $comment_form_id) {
    $form_object = $form_state->getFormObject();
    $comment = $form_object->getEntity();
    $node = $comment->getCommentedEntity();
    $content_type = $node->bundle();
    if ($content_type == $some_content_type) {
      // Do stuff.
    }
  }
}

```
> 3 votes

---
Tags: 8, forms
---


# Question
Title: How can I remove test directories when deploying?
When deploying Drupal, there are a lot of files in `test` directories, none of which are needed in a production environment. There's an ongoing issue to Exclude test files from release packages, but how can I get rid of the test files now?

# Answer
After trying to configure `drupal-core-vendor-hardening` to do this, I realized that it's too much of a pain to add each individual module.

So I wrote a simple bash script instead.

```
cd "$my_git_root"
# Delete Drupal test directories.
# https://unix.stackexchange.com/a/89937
find . -type d -name tests -exec rm -rf {} +
# Delete testing and demo profiles.
find web/core/profiles -type d -name 'testing*' -exec rm -rf {} +
find web/core/profiles -type d -name 'nightwatch_testing' -exec rm -rf {} +
find web/core/profiles -type d -name 'demo_umami' -exec rm -rf {} +
# Delete README files.
find . -type f -name 'README*' -delete
find . -type f -name 'CHANGELOG*' -delete
# Delete patch records.
find . -type f -name 'PATCHES.txt' -delete
# Delete markdown files except licenses.
find web -type f -name "*.md" -not -name "LICENSE*" -delete
# Delete txt files except licenses.
find web -type f -name "*.txt" -not -name "LICENSE*" -not -name "COPYRIGHT*" -delete

```

Doing this, I managed to reduce my build size from 213 MB to 172 MB.
> 1 votes

# Answer
core-vendor-hardening can clean up test files and any other directories you want.

The only downside is that you have to make an entry per module in composer.json.

```
"extra": {
    "drupal-core-vendor-hardening": {
        "drupal/core": ["tests","profiles/demo_umami"],
        "drupal/json_field": ["tests"]
    },
}

```

The above example also shows how to delete the umami profile-- you need to insert the directory path. So, if you want to delete `tests` from all modules of core, you are looking at a very extensive list.

Note: To actually delete the files after modifying `composer.json`, you can delete the relevant directory and then run `composer.install`. If you change `composer.json` and then run `composer update`, the files will remain.
> 0 votes

---
Tags: deployment, 10
---


# Question
Title: How to access referenced entities programmatically
I have an entity (a node) with a term reference field. I'd like to access the referenced term entities programmatically, i.e. the fully loaded term objects.

Note that my question could equally apply to *any* referenced entity, e.g. node entities, user entities, etc.

Here's what I've got so far:

```
$node = \Drupal\node\Entity\Node::load($nid);

$term_ids = array();
$items = $node->field_tags->getValue();
foreach ($items as $item) {
  $term_ids[] = $item['target_id'];
}

$terms = \Drupal\taxonomy\Entity\Term::loadMultiple($term_ids);

```

It works, but I'm not very happy with it:

* It lacks abstraction (how am I supposed to know that the id of the referenced entity is in `$item['target_id']`?)
* It's not very generic (it works if the field references terms, but what if it references nodes or users?

Any better solution?

# Answer
> 6 votes
The following `$node->get('tags')->referencedEntities()` will return a list of all the referenced entities.

# Answer
> 1 votes
You can use the **get()** method of the field object to retrieve the referenced entities as fully loaded objects instead of just their IDs. Here's an example:

```
$node = \Drupal\node\Entity\Node::load($nid);

$terms = $node->field_tags->referencedEntities();

```

This will return an array of fully loaded term objects.

If you need to access the referenced entities of a field that can reference different types of entities (e.g. nodes, terms, users), you can use the **getTargetEntityTypeId()** method of the field object to determine the type of entity being referenced, and then use the appropriate entity type manager to load the referenced entities. Here's an example:

```
$node = \Drupal\node\Entity\Node::load($nid);

$field_name = 'field_example'; // Replace with your field name.
$field = $node->$field_name;

$entity_type_id = $field->getSetting('target_type');
$entity_type_manager = \Drupal::entityTypeManager();
$referenced_entities = $entity_type_manager->getStorage($entity_type_id)->loadMultiple($field->getValue());

```

This will load all the referenced entities of the field, regardless of their entity type.

Using this approach provides more abstraction and flexibility, making your code more generic and easier to maintain.

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


# Question
Title: Apply or expose a filter based on the value of a field in a referenced entity
I have a search\_api SOLR view of downloads on path /downloads that filters on current language and has a contextual filter on category so /downloads/foo shows downloads that have the category "foo" and /downloads/bar shows downloads with the category "bar".

I want to make it so I can have certain categories not filter on current language and instead show downloads of all languages and expose the language filter to the user. So I want to add a checkbox to the category taxonomy terms and have the language filter (and its exposedness) be dependent on the value of this checkbox.

Is there an existing module that could help me achieve this or will I have to resort to writing a custom filter plugin? Something like views\_dependent\_filters, but one that wouldn't have the dependency be on the value of another filter, but rather on a field value (via a relationship).

# Answer
There is no existing module that can directly help achieve this. However, it can be achieved by writing a custom filter plugin.

Here's a general outline of how this can be done:

1. Create a custom filter plugin that extends the existing search\_api filter plugin.
2. In the plugin, add a new option to the filter configuration form that allows the user to select whether or not the filter should be dependent on the value of the checkbox in the category taxonomy terms.
3. Use the Views API to add a relationship to the referenced entity (the category taxonomy term) in the view.
4. Use the relationship to get the value of the checkbox for the current category taxonomy term.
5. Based on the value of the checkbox, either apply or expose the language filter.

Here's an example of how the custom module code might look like:

```
use Drupal\views\Plugin\views\filter\SearchApiFilter;

/**
 * Provides a custom filter plugin for downloads view.
 *
 * @ViewsFilter("downloads_custom_filter")
 */
class DownloadsCustomFilter extends SearchApiFilter {

  /**
   * {@inheritdoc}
   */
  public function exposeOptions() {
    $options = parent::exposeOptions();
    $options['custom_dependent'] = array('default' => FALSE);
    return $options;
  }

  /**
   * {@inheritdoc}
   */
  public function query() {
    $dependent = $this->options['custom_dependent'];
    $term_field = $this->definition['term_field'];

    if ($dependent) {
      // Add relationship to the referenced category entity.
      $this->query->addRelationship('field_category', [
        'plugin_id' => 'standard',
        'base' => $this->definition['entity_type'],
        'field' => $term_field,
        'label' => $this->t('Category'),
      ]);

      // Get the value of the checkbox for the current category term.
      $checkbox_value = $this->query->addExpression("field_category.field_checkbox_value", 'checkbox_value');

      if ($checkbox_value == TRUE) {
        // Apply the language filter.
        parent::query();
      }
      else {
        // Expose the language filter to the user.
        $this->query->addExposedFilter('language');
      }
    }
    else {
      // Apply the language filter.
      parent::query();
    }
  }
}

```

After creating the custom module, you can enable it and add the new filter plugin to your downloads view. You can then configure the filter to be dependent on the value of the checkbox in the category taxonomy terms.

I hope this could help!
> 1 votes

# Answer
So I managed so solve my issue by implementing hook\_views\_pre\_view and hook\_form\_views\_exposed\_form\_alter & I set my filter to be exposed by default in the views settings.

In the hook\_views\_pre\_view, I check the view ID and display ID and make sure we're on a taxonomy page. I load the taxonomy term and check the field value, if it's not enabled, I load the filters, make sure it's set to exposed and then set it to not exposed and change the value. This didn't automatically remove the field from the exposed filters form, so I did that in the hook\_form\_views\_exposed\_form\_alter.

```
function MODULE_views_pre_view(\Drupal\views\ViewExecutable $view, $display_id, array &$args) {
  if ($view->id() == 'downloads' && $display_id == 'full') {
    // If we are on a taxonomy term page and it is set to not expose language.
    // Or we are not on a taxonomy term page.
    if (\Drupal::routeMatch()->getRouteName() == 'entity.taxonomy_term.canonical') {
      $term = \Drupal::routeMatch()->getParameter('taxonomy_term');
      if (isset($term->field_expose_language)) {
        $expose = $term->get('field_expose_language');
      }
    }
    if (!isset($expose) || $expose->value === '0' || $expose->value === NULL) {
      $filters = $view->display_handler->getOption('filters');
      // Set the exposed filter to not exposed and set it to the current language.
      if (isset($filters['search_api_language']) && $filters['search_api_language']['expose'] == TRUE) {
        $filters['search_api_language']['exposed'] = FALSE;
        $filters['search_api_language']['value'] = [
          '***LANGUAGE_language_interface***' => '***LANGUAGE_language_interface***'
        ];
        $view->display_handler->setOption('filters', $filters);
      }
    }
  }
}

function MODULE_form_views_exposed_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  if (\Drupal::routeMatch()->getRouteName() == 'entity.taxonomy_term.canonical') {
    $term = \Drupal::routeMatch()->getParameter('taxonomy_term');
    if (isset($term->field_expose_language)) {
      $expose = $term->get('field_expose_language');
    }
  }
  if ((!isset($expose) || $expose->value === '0' || $expose->value === NULL) && isset($form['language'])) {
    $form['language']['#value'] = \Drupal::languageManager()->getCurrentLanguage()->getId();
    $form['language']['#access'] = FALSE;
  }
}

```
> 0 votes

---
Tags: views
---


# Question
Title: How to remove unwanted div from view rendered block?
In D8 I've created a view and assigned it into a block,

1. I've created corresponding template for both view and block.
2. Most of the unwanted addition of div's and classes has been removed.

But the following empty div is still there in the rendered HTML

```
<div class="js-view-dom-id-e0cc1fe812f18c9b5d6660b04594c41b597ae5ee9b28cf43f718dc43b43a2f42">

```

Anyone experienced the same issue?

# Answer
Is the view empty? AFAIK that's the div where the view results should be (may somebody correct me if I'm wrong).

And here's some info in general about what this class is all about: https://www.drupal.org/node/2573529
> 0 votes

# Answer
Semantic Views makes unformatted styles, field row styles and other output more readily configurable without needing to override template files.

unfortunately it doesn't support drupal 8 yet.
> 0 votes

# Answer
I did it overriding the views-view.html.twig template.

In your theme (or module) just duplicate the default template (from /core/modules/views/templates) and name it views-view--MYVIEW.html.twig

In the code, comment the div in the top (and the closing in the bottom):

```
{#<div{{ attributes.addClass(classes) }}>#}
  {{ title_prefix }}
  {% if title %}
    {{ title }}
  {% endif %}
  ...
  {% if feed_icons %}
    <div class="feed-icons">
      {{ feed_icons }}
    </div>
  {% endif %}
{#</div>#}

```
> 0 votes

# Answer
The empty **div** with the **js-view-dom-id** class is added by Views in Drupal 8 as a placeholder for the view content, so it can be updated dynamically via AJAX without reloading the entire page. If you don't need this feature, you can disable it in the view settings by following these steps:

1. Edit the view and click on the "Advanced" section in the left sidebar.
2. Under the "Other" category, find the "Use AJAX" option and uncheck it.
3. Save the view and clear the cache.

This should remove the empty **div** from the rendered HTML. If it's still there, you may need to modify the view template and remove it manually. In your view template file, you can look for the **js-view-dom-id** class and remove the corresponding **div** element. Note that this may affect the functionality of the view if you rely on AJAX updates.
> 0 votes

---
Tags: views, blocks, 8
---


# Question
Title: How to get Webform Element value into Custom Token
I am currently working on a Drupal project that involves a Webform. I need to assign the value of a specific Webform element, particularly the IP address element, to a custom token. The goal is to use this token elsewhere in the site. Could you please provide guidance or steps on how to achieve this integration?

The steps I've taken so far:

```

use Drupal\webform\WebformSubmissionInterface;

function MODULE_token_info() {
// token info
}

function MODULE_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata, WebformSubmissionInterface $webform_submission = NULL) {

  // @var to return IP address of the submission.

  $ip = $webform_submission->getRemoteAddr();

  $replacements = [];
  if ($type == 'custom') {
    foreach ($tokens as $name => $original) {
      switch ($name) {

        case 'order-cart':
          $replacements[$original] = t('First Value');
          break;

        case 'city-token':
          $replacements[$original] = getCity($ip);
          break;

      }
    }
  }

  return $replacements;
}

```

However, the IP\_ADDRESS always returns NULL. I suspect that the $ip variable is not the correct method for retrieving the value of the Webform Submission IP Address

How to retrieve the Webform Submission IP Address value?

I'll appreciate the help, Thanks

# Answer
> 1 votes
The only tokens with access to a webform submission are the *webform\_submission* tokens defined in `webform_token_info()`. You can add new tokens implementing `hook_token_info_alter()` (for the token description) and `hook_tokens_alter()` (for the token value).

The relevant parts in `hook_tokens_alter()` are:

* Verify `$context['type']` is equal to `'webform_submission'`
* Verify `$context['data']['webform_submission']` is set

```
use Drupal\Core\Render\BubbleableMetadata;

function mymodule_tokens_alter(array &$replacements, array $context, BubbleableMetadata $bubbleable_metadata) {
  if ($context['type'] == 'webform_submission' && !empty($context['data']['webform_submission'])) {
    $webform_submission = $context['data']['webform_submission'];
    // Replace 'city' with the token name.
    $replacements[$context['tokens']['city']] = mymodule_get_city($webform_submission->getRemoteAddr());
  }
}

```

# Answer
> 1 votes
This is updated version cause of your comment below.

To get a webform element value into a custom token, you need to first create a custom token and then use the hook\_tokens function to assign a value to that token.

To do this, you can follow these steps:

Define your custom token using the **hook\_token\_info()** function. This function defines the name and description of your custom token.

```
function MODULE_token_info() {
  $info['tokens']['city-token'] = [
    'name' => t('City Token'),
    'description' => t('Replaces [city-token] with the city name.'),
  ];
  return $info;
}

```

Implement hook\_tokens function to replace the custom token with the desired value. This function takes several parameters, including the type of tokens being requested, the tokens themselves, the data used to replace the tokens, and the webform submission object.

```
function MODULE_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
  $replacements = array();
  if ($type == 'custom') {
    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'city-token':
          // Get the IP address element object from the submission.
          $webform_submission = $data['webform_submission'];
          $ip_element = $webform_submission->getElementData('ip_address');
          if ($ip_element) {
            // Get the value of the IP address element.
            $ip_address = $ip_element->getValue();
            $replacements[$original] = getCity($ip_address);
          }
          break;
      }
    }
  }
  return $replacements;
}

```

Note that the **$webform\_submission** parameter is passed in as part of the **$data** array. This allows you to access the webform submission object and retrieve the value of the desired element.

With these functions implemented, you should be able to use the **\[city-token\]** token in any text field within your webform and it will be replaced with the value of the city corresponding to the IP address provided in the **IP\_ADDRESS** element.

I hope this help!

---
Tags: 8, webforms, tokens
---


title: Limit the number of nodes per User

text:
# Question
Title: Limit the number of nodes per User
How to limit user for adding new nodes?

For example: Users can not Create/Add more than One post for specific Content Type.

# Answer
The Node Limit module does exactly that:

> The Node Limit module allows administrators to restrict the number of nodes of a specific type that roles or users may create.

Among its features are:

> * Per-role node limits
> * Per-user node limits
> * Per-time interval node limits
> * Any combination of the above
> 0 votes

# Answer
To limit users from adding more than one node for a specific content type, you can use the **hook hook\_form\_alter()** to alter the node form and disable the submit button if the user has already created a node for that content type. Here's an example code snippet:

```
function my_module_form_alter(&$form, &$form_state, $form_id) {
  global $user;
  if ($form_id == 'node_form' && $form['#node']->type == 'my_content_type') {
    $node_count = db_query("SELECT COUNT(*) FROM {node} WHERE type = :type AND uid = :uid", array(':type' => 'my_content_type', ':uid' => $user->uid))->fetchField();
    if ($node_count > 0) {
      $form['actions']['submit']['#attributes']['disabled'] = 'disabled';
    }
  }
}

```

In this code, you first check if the current form being altered is a node form for our desired **content type**. Then, we count the number of nodes that have been created by the current user for that content type. If the count is greater than zero, then disable the submit button.

To add a specific limit number for user role type, you can modify the above code to check for the user's role and apply different limits accordingly. Here's an example:

```
function my_module_form_alter(&$form, &$form_state, $form_id) {
  global $user;
  if ($form_id == 'node_form' && $form['#node']->type == 'my_content_type') {
    switch ($user->roles[3]) { // assuming the role ID we want to limit is at index 3
      case 'editor':
        $limit = 5; // limit editors to 5 nodes
        break;
      case 'contributor':
        $limit = 3; // limit contributors to 3 nodes
        break;
      default:
        $limit = 1; // default limit for all other users is 1 node
        break;
    }
    $node_count = db_query("SELECT COUNT(*) FROM {node} WHERE type = :type AND uid = :uid", array(':type' => 'my_content_type', ':uid' => $user->uid))->fetchField();
    if ($node_count >= $limit) { // use >= instead of > to allow users to edit existing nodes
      $form['actions']['submit']['#attributes']['disabled'] = 'disabled';
    }
  }
}

```

In this code, you first check the user's role and set a specific limit for each role. Then, it count the number of nodes created by the user for the desired content type and compare it with the limit. If the count is greater than or equal to the limit, it disable the submit button.

I hope this help!
> 1 votes

---
Tags: 7, nodes
---


# Question
Title: How to remove author field from the 'advanced' field group on the node edit form
I've got several node edit forms that, in most themes, display the 'advanced' group in a sidebar. I have other plans for the sidebar, though, so I don't want that behavior. I edited some twig and css to make the advanced group appear below the main form, but this is still pretty messy. In particular, we often have to adjust the author field, and I'd like it at the top all by its lovely self... not buried in a special section.

All of that to say, I see here how to add items to the 'advanced' group, but how can I remove author (and pretty much everything else) from it and just use the field group module to put fields where I want them? (Field group module currently doesn't let you take fields out of advanced)

I've done some things in MYTHEME.THEME with THEME\_node\_form\_alter (changing submit button label for example), but I can't seem to get it to move author out of the advanced group.

The module way would be fine, too, but is beyond my expertise without some help. (Seems like there would be a market for a "liberate the node form" module or "all fields moveable" module, etc.)

Added because code requested...

```
function MYTHEME_form_node_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
    $form['author']['#group'] = 'SOMETHING HERE?';
}

```

That isn't complete, so of course can't work. Looking at the api here, I see the author field is assigned to 'advanced' but I don't know how to assign it to something else. The relevant portion

```
$form['author'] = [
    '#type' => 'details',
    '#title' => $this
      ->t('Authoring information'),
    '#group' => 'advanced',
    '#attributes' => [
      'class' => [
        'node-form-author',
      ],
    ],
    '#attached' => [
      'library' => [
        'node/drupal.node',
      ],
    ],
    '#weight' => 90,
    '#optional' => TRUE,
  ];

```

# Answer
> 0 votes
This works... though it doesn't look quite right to me.

```
function MYTHEME_form_node_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
        $form['author']=['#group' => 'footer'];
    }

```

This sort of works, but is probably not a good idea. It removes author from 'advanced' but it doesn't seem to heed positioning in the "Manage form display" interface. And I get the impression fields are not supposed to be without a #group value.

```
function MYTHEME_form_node_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
    unset($form['author']['#group']);
}

```

---
Tags: nodes, forms, 9
---


# Question
Title: Advagg Bundler would NOT spit out same size CSS bundles
Trying to make sure Advagg bundler spits out two equal size CSS bundles as it makes a massive difference in initial server response time.

For some reason, Advagg will split files into 2 bundles (first one 4kb and second one 36kb).

On Dev server, it works perfectly with same settings. Both identical systems.

I have tried uninstalling/re-installing a few times with no luck.

On production site, it shows 88 groupings. On dev server (where everything works fine), it is showing 150 groupings.

I have spent hours and hours with no luck.

**Update:**

It appears that on production site, Advagg is trying to put all the system files or those added by modules in one file and theme CSS files in the second aggregate file. It then loads system CSS files aggregate first which becomes render blocking for the theme CSS files hence 4kb first file blocking 36kb second file.

This is not the intended functionality. Bundler logic allows for aggregation based on size or number of files.

How can I debug/fix this?

# Answer
> 0 votes
It appears that the only option is to add template CSS via drupal\_add\_js and give it a higher priority so that aggregates have similar sizes.

This is the code I added to template.php to get some files included in the aggregate.

```
  // loading font awesome separately to get it into aggregates

drupal_add_css(drupal_get_path('theme', 'mytheme') . '/font-awesome/css/font-awesome.min.css');

```

This is obviously not a solution but just a workaround the issue.

**Update:**

Disabling and enabling the base theme fixed the issue in entirety. Apparently just clearing the theme registry wasn't enough.

Hope this helps someone else with same issue.

---
Tags: 7
---


title: Custom access control in views

text:
# Question
Title: Custom access control in views
I have Views page with list of enities. The URL is /admin/group/12/members

There is a route in *mymodule.routing.yml*:

```
  mymodule_members_list:
  path: '/admin/group/{id}/members'
  requirements:
    _custom_access: '\Drupal\mymodule\Controller\GroupController::access'

```

The Group entity has field contains group administrator email (field\_admin\_email)

What I need is to allow administrator to only access a view with group/{id} associated with his email. (I have only problem with views access - it seems like views override my custom access method)

# Answer
> 5 votes
**Solved!**

Adding custom access to the Views is very easy: Just create own views access plugin by extending *Drupal\views\Plugin\views\access\AccessPluginBase* class.

Then implement custom access logic in *access* method and new rule should appear in **PAGE SETTINGS \> Access** popup window.

# Answer
> 0 votes
Use Views custom permissions module

---
Tags: views, 8, users
---


title: Statistics counter based on user role

text:
# Question
Title: Statistics counter based on user role
I need to track some basic usage statistics for a subset of my authenticated user roles in Drupal 9.

Is there a way to make the core statistics counter only count node views based on user roles? The three counters it provides are all I need, I just need them to only record certain user roles.

# Answer
The statistics module adds a js library to the node build. You can remove it if you don't want to count the node:

*mymodule.module*

```
/**
 * Implements hook_ENTITY_TYPE_view_alter() for node entities.
 */
function mymodule_node_view_alter(array &$build) {
  $roles = \Drupal::currentUser()->getRoles();
  if (!in_array('special_role', $roles)) {
    if (isset($build['#attached']['library'])) {
      $key = array_search('statistics/drupal.statistics', $build['#attached']['library']);
      if ($key !== FALSE) {
        unset($build['#attached']['library'][$key]);
      }
    }
  }
}

```

This only works if the roles are altering permissions. If not, add a cache context for the user roles:

```
$build['#cache']['contexts'][] = 'user.roles';

```
> 3 votes

---
Tags: nodes, 9
---