# Question
Title: Properly override template_preprocess_book_navigation function in Drupal 8 Book module
I'm trying to find what is the proper way to override the **template\_preprocess\_book\_navigation** function from the Drupal 8's core Book module. I want to add only two lines (bolded), but don't know how this should be done in "Drupal 8 way". Should I create a custom module or add something in the mytheme.theme file?

Thank you and be healthy!

```
**function template_preprocess_book_navigation(&$variables)** {
  $book_link = $variables['book_link'];

  // Provide extra variables for themers. Not needed by default.
  $variables['book_id'] = $book_link['bid'];
  $variables['book_title'] = $book_link['link_title'];
  $variables['book_url'] = Url::fromRoute('entity.node.canonical', ['node' => $book_link['bid']])->toString();
  $variables['current_depth'] = $book_link['depth'];
  $variables['tree'] = '';

  /** @var \Drupal\book\BookOutline $book_outline */
  $book_outline = \Drupal::service('book.outline');

  if ($book_link['nid']) {
    $variables['tree'] = $book_outline->childrenLinks($book_link);

    $build = [];

    if ($prev = $book_outline->prevLink($book_link)) {
      $prev_href = Url::fromRoute('entity.node.canonical', ['node' => $prev['nid']])->toString();
      $build['#attached']['html_head_link'][][] = [
        'rel' => 'prev',
        'href' => $prev_href,
      ];
      $variables['prev_url'] = $prev_href;
      $variables['prev_title'] = $prev['title'];
    }

    /** @var \Drupal\book\BookManagerInterface $book_manager */
    $book_manager = \Drupal::service('book.manager');
    if ($book_link['pid'] && $parent = $book_manager->loadBookLink($book_link['pid'])) {
      $parent_href = Url::fromRoute('entity.node.canonical', ['node' => $book_link['pid']])->toString();
      $build['#attached']['html_head_link'][][] = [
        'rel' => 'up',
        'href' => $parent_href,
      ];
      $variables['parent_url'] = $parent_href;
      $variables['parent_title'] = $parent['title'];
      **$variables['prev_depth'] = $prev['depth'];**
    }

    if ($next = $book_outline->nextLink($book_link)) {
      $next_href = Url::fromRoute('entity.node.canonical', ['node' => $next['nid']])->toString();
      $build['#attached']['html_head_link'][][] = [
        'rel' => 'next',
        'href' => $next_href,
      ];
      $variables['next_url'] = $next_href;
      $variables['next_title'] = $next['title'];
      **$variables['next_depth'] = $next['depth'];**
    }
  }

  if (!empty($build)) {
    \Drupal::service('renderer')->render($build);
  }

  $variables['has_links'] = FALSE;
  // Link variables to filter for values and set state of the flag variable.
  $links = ['prev_url', 'prev_title', 'parent_url', 'parent_title', 'next_url', 'next_title'];
  foreach ($links as $link) {
    if (isset($variables[$link])) {
      // Flag when there is a value.
      $variables['has_links'] = TRUE;
    }
    else {
      // Set empty to prevent notices.
      $variables[$link] = '';
    }
  }
}

```

# Answer
> 4 votes
You can put a template\_preprocess() function in a custom module or your theme `mytheme.theme` file.

It depends on what you want to achieve.

If this preprocess function is specific to the theme (for example, the results are used in a theme template), then you should put it in `mytheme.theme`.

But, if you put the code in `mytheme.theme` and then change themes, your book navigation will no longer behave as expected.

So, if the preprocess function does general processing that you might want to use in multiple themes, and it doesn't do anything theme-specific, then it's better to put it in a custom module. (This is probably your use case.)

---
Tags: theming
---


title: How to get pathInfo in twig?

text:
# Question
Title: How to get pathInfo in twig?
I want get the pathInof in my template twig :

How i can reach pathInfo please in my template ?

# Answer
> 1 votes
The screenshot is a render element of a link which gets printed in the template completely. What you find inside is for internal usage and is most times protected anyway.

To get this request parameter in your template build your own variable in preprocess:

```
$variables['path_info'] = \Drupal::request()->getPathInfo();

```

You might also need to add a cache context if your template depends on this information:

```
$variables['#cache']['contexts'][] = 'url.path';

```

Then print it in Twig:

```
{{ path_info }}

```

This is the general approach. In this specific case if you are interested in the current path (including the base path) then you could simply use a Twig function:

```
{{ path('<current>') }}

```

---
Tags: 8, views, theming
---


# Question
Title: How can I create a recommended project in the root directory?
My server consists only one Drupal site and therefore I would like to install it directly under `D:\home\site\wwwroot`

However this seems impossible as the new command `composer create-project drupal/recommended-project:~8.8.0 mysite` requires some sort of a folder name like 'mysite'.

All I want is Drupal composer.json stuff to be installed in `D:\home\site\wwwroot` and 'web' folder to be installed in `D:\home\site\wwwroot\web`

Is this possible at all?

# Answer
If wwwroot is empty you can simply run the following command from inside this directory.

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

```

The `.` at the end says: current directory.
> 1 votes

# Answer
In some shared hosting platforms, you cannot change the root folder.

You have an optional "drupal/legacy-project" option that creates the legacy folder organization.

“composer create-project drupal/legacy-project wwwroot”

https://www.drupal.org/docs/develop/using-composer/starting-a-site-using-drupal-composer-project-templates

I have some websites on gandi.net (fr) shared hosting and it defaults the root folder to "subdomain.domain.tld/htdocs".

* I run the "drupal/recommended-project" composer command
* Rename the “web” folder to “htdocs”
* Edit the composer.json file to replace “web/” by “htdocs/”
* Run “composer update”

And voilá.

In the end, the “subdomain.domain.tld” folder looks like this:

* config/sync/ (created manually, outside of the Drupal installation directory, defined in settings.php)
* htdocs/
* private/ (created manually, outside of the Drupal installation directory, defined in settings.php)
* vendor/
* composer.json
* composer.lock

Edit: If you are unable to run the composer command in your shared hosting, do it locally on a LAMP (or Acquia Dev Desktop) and upload the compressed archive to your host.
> 0 votes

---
Tags: 8, composer
---


# Question
Title: How can I block an abusive person who endlessly tries to register more accounts?
I have a free site with open registration; anyone can register and I am not able to change that. However, accounts must be approved by an administrator after completing a profile, so I have access to a variety of info to identify duplicate users (date of birth, "self introduction" comment, email address, photos, reported location in profile, geolocated IP, IP address, etc.)

Recently the site has become subject to the attacks of a single user who is extremely abusive and seems to have unlimited time to register multiple accounts. These accounts can be blocked relatively easily, but it's a lot of work for the administrator (me) and I'm afraid I will miss one among the legitimate accounts (this user registers about 6 accounts per day).

I have already:

* banned the user's known IP addresses
* blocked registration from known domains that are used for "disposable" email addresses
* added a special view to help me identify accounts based on known patterns of access from the user

Is there anything I can do with cookies, etc. to make it even more difficult for this user to register?

This is a Drupal 7 site.

**EDIT:** In my case, this is a human user, not a bot. But the bot answers are useful for those who are being specifically targeted by spammers.

# Answer
**Instead of trying to prevent the user from registering, which is a game for trolls and hackers alike, make the site annoying for him.** Make him want *to leave*, as opposed to making him want to *win the trolling game*.

What I've done in the past, not specific to Drupal, is to let the user register. I then sabotaged his specific account:

1. I sprinkled `if ($someuser) { sleep(rand($base, $base*2)) }` in the code, where `$base` would increase daily. I made sure that he had lots of timeouts.
2. I would drop N% of his posts, where N started above 50% and only increased from there. If the post had certain keywords, it would be dropped immediately.
3. I would randomly drop CSS rules so the page would display poorly. Again, the severity would increase in time.

This required more effort on my part, but as per Sun Tzu's advice I was **conditioning the user to hate my site**. I wanted him to feel not that he was annoying us, but rather that he should be annoyed by the delays and apparent bugs. It took less than a week for him to give up on us.

**EDIT**: In the comments, user rooby mentions the Misery module which is based on the same concept:

1. **Delay**: Create a random-length delay, giving the appearance of a slow connection. (by default this happens 40% of the time)
2. **White screen**: Present the user with a white-screen. (by default this happens 10% of the time)
3. **Wrong page:** Redirect to a random URL in a predefined list. (by default this happens 0% of the time)
4. **Random node**: Redirect to a random node accessible by the user. (by default this happens 10% of the time)
5. **403 Access Denied**: Present the user with an "Access Denied" error. (by default this happens 10% of the time)
6. **404 Not Found**: Present the user with a "Not Found" error. (by default this happens 10% of the time)
7. **Forms don't submit**: Redirect back to the form during validation to prevent submission. (by default this happens 60% of the time) Note: Occasionally certain forms validate based on which button was pressed, this won't work in those cases.
8. **Crash IE6**: If the user is using Internet Explorer 6, this will crash their browser. (by default this happens 0% of the time)
9. **Spam**: Replace node content with a set word. (by default this happens 10% of the time)
10. **Logout**: Log the user out. (by default this happens 10% of the time)
> 51 votes

# Answer
**Email The Abuser's ISP - They *Will* Put An End To It!**

You have a log of his account creation times, and the IP addresses used to register, right? You also have a log of all of his harassing comments as well? **Send these logs to their ISP**.

You can find the ISP by doing an IP lookup either directly with ARIN at https://whois.arin.net/ui or another service which simply uses them for a whois. Most users will have an actual ISP come up for the owner of the IP, but some (large corporations typically) will own the IP themselves. Either way, you'll find out who it is that owns the IP, and from there you can look them up and get an abuse email address (sometimes they put an actual abuse email address in the WHOIS information, which is great) to send your email to. Be kind and courteous with the ISP. Don't write an epic novel - keep it simple. Something like, "This ip has been harassing myself and users of my website, please see the logs" will suffice. They see thousands of these a day, they don't want to read a sob story.

I used to be a Network Engineer at an ISP. We received *countless* harassment reports for this very kind of behavior. The moment a subscriber receives the phone call, they stop about 90% of the time. If it does continue, legal action can ensue which means most ISPs take very serious action.

**Your aggressor is probably a kid living at home with his parents**, which means when his parents' internet account is threatened to be terminated, they'll be incredibly upset with him for his behavior.

I've personally taken this route and it does work, even if the aggressor is not in your country. ISPs do NOT take kindly to users being abusive on their network.

**Side Note**

There's the possibility he is using a Proxy. However, it is very unlikely he used a proxy upon initial registration unless he fully intended (from the moment he registered) to troll on your website. If none of the IPs actually point to a legitimate ISP where you can actually send an abuse email, try sending it to whatever Proxy service he's using.

People have this crazy idea that simply because someone's using a Proxy, they're undetectable. That's false. Proxy services typically follow the same rules everyone else does (of course there are exceptions). When they get an abuse report, they take care of it just like an ISP does.

On the incredibly small chance that this user is behind a Proxy that doesn't play nice, I'd follow the advice of the other answerers. Seriously though, that's so rare that most website owners will never see an attack from someone who's seemingly "undetectable".
> 32 votes

# Answer
On your question, you do not mention having a Captcha. Perhaps, this anoying user is a bot? My guess is that you already have a captcha and that user is actually human.

But... if that's not the case, I would recommend:

## reCAPTCHA

> Uses the Google reCAPTCHA web service to improve the CAPTCHA system and protect email addresses.
> 17 votes

# Answer
## Hellban / shadowban

As blocking a person from creating new accounts to replace blocked ones is nearly impossible, a somewhat popular solution to this problem is the so-called hellban (http://www.urbandictionary.com/define.php?term=hellban) or shadowban, where you implement a feature that isolates certain accounts from everyone else without making it obvious that they have been banned.

For example, you would make sure that they feel like their experience is genuine and they can keep using the same accounts, but make a condition that any of their posts or activities are visible only to hellbanned accounts and any accounts (or not-logged-in situations) that share IP and/or cookies with the hellbanned account. If the user continues to troll but gets no response, then they will leave eventually.
> 9 votes

# Answer
At No Sssweat's suggestion, I'm putting this as an answer instead of a comment so more people see it.

Drupal.org/project/misery does quite a good job of annoying the crap out of a user with about eight different methods. Random time outs, log outs, random redirects etc. and the frequency is changeable.

An easy version of dotancohen's nice write up to the problem.
> 7 votes

# Answer
It is actually impossible.

You cannot stop a person from registering again and again. The only thing you can do is harden the registration process and double/triple-checking their ID (through email address **and** google account **and** facebook account **and** LinkedIn account **and** phone number, and so on) and correlate each person with a specific ID that aggregate all these details.

The specific person can either pick another network through web proxy, create many @email, open several facebook/google/LinkedIn account and so on, but you will never be able to stop this person to register again (unless you check its DNA).

**Your only choice is to harden the registration process.**
> 6 votes

# Answer
How about using a second factor? In your registration, say you'll send an SMS to their phone or an email and ask they confirm their registration by clicking a link and sending the info back. You'll acquire a harder to spoof identifier than IP address and it may be easier to filter him out that way. Obviously will require some small amount of scripting...
> 6 votes

# Answer
As suggested by No Sssweat, you can use reCAPTCHA. But if incase you don't want to annoy legitimate users, then you can try Honeypot module. It gives the option of having an extra field which shouldn't be filled (AFAIK bots may fill all fields) or have time restriction.

> Documentation reads as,
>
> Honeypot uses both the honeypot and timestamp methods of deterring spam bots from completing forms on your Drupal site (read more here). These methods are effective against many spam bots, and are not as intrusive as CAPTCHAs or other methods which punish the user \[YouTube\].

Another option is Spambot module, it prevents spam by verifying registration attempts against the Stop Forum Spam.

> Spambot protects the user registration form from spammers and spambots by verifying registration attempts against the Stop Forum Spam (www.stopforumspam.com) online database. It also adds some useful features to help deal with spam accounts.
> 4 votes

# Answer
There is an answer to the question - albeit that the community may find this offensive. I don't know why, save cost, that the path has not been taken more often.

Send a cease and desist letter to the individual who is trespassing on your site, and who is violation Title XVIII of the US Code, Tampering with Computers, Fraud and Abuse.

If they don't cease, pony up the cash and sue. In the mid 1990's I had the first case in the WDMO where the sole ISP for the Kansas City area was hacked by 3 teens. I contacted the families (they had rooted one of the servers and were stupid enough to store their application to join "Warez" groups with their real names and addresses in a hidden subdirectory) and asked them to stop the adolescent boys from doing this. They refused.

In a 54 page complaint I had to explain: ISPs, Internet, exceeding access, etc. to a newly appointed judge.

I was more than an advocate, an account of mine had been accessed (thank god, not a client account - The Rust List) and I was ticked off. I charged the parents (only way to go where defendants were minors in a civil action) and stated that the parents had placed a dangerous instrumentality into the hands of minors without adequate supervision and that the minors had engaged in the transfer of stolen software, and kiddie porn (Yep, 16 year old boys taking pictures of their 16 year old GF's is "kiddie porn"). I hit the homeowner's policies and I had a remedy that the courts in those days would not even consider: a lifetime ban on the use of computers for the teenagers.

It got settled within a week of the transfer of discovery.

You have tried everything else: hammer this little shit with a walletectomy. Be careful to pick an attorney who knows what this is and not one associated with a "big firm" where you will be billed to death.

Consider how a court order precluding the jerk from accessing your site might work: S/He's banned - you find an IP address of his/her violating the Court Order - send it to your attorney and s/he files a motion with the Court showing violation of the Order and the Court will respond with a Show Cause Order why the defendant should not be held in contempt of court.

An attack from other sites/IP addresses start coming in - tell your counsel - and the response can include mirroring the defendants computers, an injunction stopping the defendant from all access to the Internet, and, upon finding that the defendant violated the order by proxy, the Court will impose sanctions.

Ultimately, the jerk may have confined himself to forever be blocked from the Internet. If you are truly pissed off after all of this and you want to be certain that the defendant is off the Internet, hire a PI to follow the defendant for a week or ten days (not cheap) and if s/he is accessing the Internet from a Starbucks - or McD's you have them: back to the judge.

The ultimate Order is beyond belief and will only happen with overwhelming evidence that the defendant had routinely violated the court's orders: no smart phones, no VOIP, no cable or sat TV (Internet is available and s/he has shown a total disregard for the law, so nothing that they can hack to gain access is permitted), no Internet (it would be nice if it were a lifetime ban - likely not), no Internet of things, and ultimately, no computing devices.

If the defendant was making their living in the tech sector - they have ditches to dig and burgers to flip.

That's how to end this with the nuclear option.
> 3 votes

# Answer
I like the misery model, but I would add to randomly send him to a page with really loud annoying messages and other times send him to really bad porn sites. Anything that might get him in trouble or embarrassed if he is in public.
> 2 votes

# Answer
I have written a module specifically for this kind of situations. It is called Spaces Enforced and can be found here, and works by blocking any registering person whose name doesn't contain at least 1 space. It can be further configured and you can select what character needs to appear how many times. It also has a succession rate of close to 100%
> 2 votes

# Answer
Disable registration form /user login page Account Settings - /admin/config/people/accounts -\> under Who can register accounts?, check only admin
> 0 votes

---
Tags: users
---


# Question
Title: how to insert data to the database table with hook_update_N
i have just create a table inside the file mymodule.install , the database was created successfully but not for the hook\_update\_N , 8001,8002 and 8003 , they tell me **'\[success\] No pending updates.'** so the hook hook\_update\_N , 8001,8002 and 8003 does not insert anything

can anyone tell me why please ?

this is my code

```

/**
 * Installs the database schema.
 */
function mymodule_install() {
  drupal_install_schema('mymodule_post');
}

/**
 * Uninstalls the database schema.
 */
function mymodule_uninstall() {
  drupal_uninstall_schema('mymodule_post');
}

/**
* Creates the tables using the schema API.
*/
function mymodule_schema() {
  $schema['mymodule_post'] = array(
    'description' => 'description ',
    'fields' => array(
      'pid' => array(
        'description' => 'post id',
        'type' => 'serial',
        'not null' => TRUE,
      ),
      'title' => array(
        'description' => 'title',
        'type' => 'text',
        'not null' => TRUE,
      ),
      'body' => array(
        'description' => 'body',
        'type' => 'text',
        'not null' => TRUE,
      ),
      'created' => array(
              'description' => 'created',
              'not null' => TRUE,
              'mysql_type' => 'timestamp',
            ),
    ),
    'primary key' => ['pid'],
  );
  return $schema;
}

function mymodule_update_8001() {

  //some codes
}

function mymodule_update_8002() {

  //some codes
}

function mymodule_update_8003() {
 //some codes
}
?>

```

# Answer
You have to copy the table definition in a `hook_update_n` implementation. You can see an example of the this in 8.9.x Layout Builder layout\_builder.install file. https://git.drupalcode.org/project/drupal/-/blob/8.9.x/core/modules/layout\_builder/layout\_builder.install#L68

```
/**
 * Implements hook_schema().
 */
function layout_builder_schema() {
  $schema['inline_block_usage'] = [
    'description' => 'Track where a block_content entity is used.',
    'fields' => [
      'block_content_id' => [
        'description' => 'The block_content entity ID.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ],
      'layout_entity_type' => [
        'description' => 'The entity type of the parent entity.',
        'type' => 'varchar_ascii',
        'length' => EntityTypeInterface::ID_MAX_LENGTH,
        'not null' => FALSE,
        'default' => '',
      ],
      'layout_entity_id' => [
        'description' => 'The ID of the parent entity.',
        'type' => 'varchar_ascii',
        'length' => 128,
        'not null' => FALSE,
        'default' => 0,
      ],
    ],
    'primary key' => ['block_content_id'],
    'indexes' => [
      'type_id' => ['layout_entity_type', 'layout_entity_id'],
    ],
  ];
  return $schema;
}

/**
 * Create the 'inline_block_usage' table.
 */
function layout_builder_update_8602() {
  $inline_block_usage = [
    'description' => 'Track where a block_content entity is used.',
    'fields' => [
      'block_content_id' => [
        'description' => 'The block_content entity ID.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ],
      'layout_entity_type' => [
        'description' => 'The entity type of the parent entity.',
        'type' => 'varchar_ascii',
        'length' => EntityTypeInterface::ID_MAX_LENGTH,
        'not null' => FALSE,
        'default' => '',
      ],
      'layout_entity_id' => [
        'description' => 'The ID of the parent entity.',
        'type' => 'varchar_ascii',
        'length' => 128,
        'not null' => FALSE,
        'default' => 0,
      ],
    ],
    'primary key' => ['block_content_id'],
    'indexes' => [
      'type_id' => ['layout_entity_type', 'layout_entity_id'],
    ],
  ];
  Database::getConnection()->schema()->createTable('inline_block_usage', $inline_block_usage);
}

```

See the `inline_block_usage` definition is the same in `layout_builder_schema()` and `layout_builder_update_8602()`.

`layout_builder_update_8602()` manually creates the table via `Database::getConnection()->schema()->createTable('inline_block_usage', $inline_block_usage);`
> 1 votes

---
Tags: hooks
---


title: Webform submitted data in views

text:
# Question
Title: Webform submitted data in views
I am using the Webform and displaying it using the Views module.

Unfortunately, I couldn't get the submitted data of the webform in views.

Any suggeestions?

# Answer
> 2 votes
you need to add new view 'admin/structure/views/add' and into Show select webform submission continue and edit ... you can get all the submission list into the view.

Add field webform submission data:value

Then select webform node and field you want.

You can see the fields like this

View result

Try it.

# Answer
> 0 votes
Create a new view for your submissions, for example, lets create a table.

Add a new field, and look for "webform submission data: value". Once you have selected that, you will need to enter the name, or the nid of your webform, and then select a field to show that value.

If you cant see the "webform submission data: value" field right away, you may need to add a relationship to your view for that specific webform.

# Answer
> 0 votes
Create a new view by selecting webform submissions data and rendered submissions instead of fields under Display format. Or follow the process from this : - how-display-webform-submissions-views-drupal-7

# Answer
> 0 votes
If your needs are more complex, you may use Data and/or Webform MySQL Views modules.

# Answer
> 0 votes
In Drupal 8, you need to install the "Webform Views" module to have the webform submission data show up in "Views".

---
Tags: 7
---


# Question
Title: How can I programmatically get a checkbox value?
I need to check if "Close this ticket after replying" checkbox is checked or not. The value $ab is not getting displayed. This is a comment type created from **Structure \> Comment Type**.

```
function helper_ticket_comment_submit(&$form, FormStateInterface $form_state)
{
          $ab = $form_state ->getValue('field_close_this_ticket');
           \Drupal::logger('CheckBoxValue')->notice($ab);

 }

```

> Error:

# Answer
Install the Devel module and then use its `dpm()` function to print a variable to the message area of your page. So inside your submit handler put `dpm($form_state->getValue('field_close_this_ticket'));` and you'll see what the error message already is telling you: It's an array. While the logger expects a string. What you are looking for is inside `$form_state->getValue('field_close_this_ticket')['value']`, note the `['value']` at the end.

```
function helper_ticket_comment_submit(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {

  if ($form_state->hasValue('field_close_this_ticket') && $form_state->getValue('field_close_this_ticket')['value'] == TRUE) {

    \Drupal::service('messenger')->addMessage(t('Ticket closed'));
  }
}

```
> 1 votes

---
Tags: 8, forms
---


# Question
Title: How can I reset render cache on an entity for current user in hook_entity_view_alter()?
I have Commerce products for which I'm showing or hiding the add-to-cart link, if that product is or isn't in the current cart. In my code:

```
function mymodule_commerce_product_view_alter(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display) {
    $myservice = \Drupal::service('myservice');
    if ($myservice->isProductInCurrentCart($entity->id()) {
        $build['add_to_cart_link']['#access'] = FALSE;
    }
}

```

That works fine, however, when the product entities are rendered, they retain their cache and will continue to display, or not display, the add-to-cart link until the cache is cleared.

In my view\_alter hook, how can I clear the cache for the current user for that entity before it's rendered? Can I set a cache tag?

# Answer
> 0 votes
At first I disabled the cache for the link, but the entity was cached, preventing the link from being shown or hidden. Therefore, I disabled the cache for the entity and that worked.

In the alter hook:

```
$build['#cache']['max-age'] = 0

```

The entity is now rendered with the proper state of the cart link.

---
Tags: 8, commerce, caching
---


# Question
Title: Views filter that alternates content types?
Does anyone know if it's possible to have a view that alternates between content types? Aka show 1 of content type news and then 1 of content type article repeating?

# Answer
> 0 votes
Not automatically. You could custom code such a solution, maybe an advanced Pager for showing different number of results for each CT.

The usual way to solve this problem is to create different Attachment or Block Displays, each for a different Content Type, show them in the same Region and adjust the CSS styles to make them look like they are part of the same "box".

---
Tags: 8, views
---


# Question
Title: Exclude entities currently referenced on a node with entity browser
I have content type that references a custom entity and uses an entity browser widget to select them.

I want to create a view for the entity browser that excludes entities that have already been selected on the current node so that they're not selected twice.

Not sure if this can be done with default functionality or it needs a custom module

# Answer
You are listing entities as your base type, but you want to filter based on the current node.

You need to introduce the information about the nodes that already reference (all) entities, and also what the current node is.

You do that by using the "backwards" Relationship, the one that goes from Entities back to the nodes that reference them. This Relatinship will be named something like `Content using field_entity_reference` and its Description will be `Relate each Content with a field_entity_reference set to the content.`

It will *not* be named "Content referenced from field\_entity\_reference", that one is the "forward" Relationship.

You can **Require** this Relationship because you want the results that *do* have a reference from a node.

Then add the Contextual filter `Content: ID`, make it **use the Relationship**, and configure
`When the filter value is NOT available`
**Provide default value**
**Content ID from URL**
`When the filter value IS available or a default is provided`
**Specify validation criteria**
**Content**
**Your CT**

Configure the other options as needed, but in the end:

`More`
**Exclude**

So we found all nodes that reference entities in our results, and used the Contextual filter to exclude the results related to the current node.
> 0 votes

---
Tags: entities
---


title: Sub-menu items in twig

text:
# Question
Title: Sub-menu items in twig
How can I theme all the menu levels individually in menu.html.twig?

I need to give different markup to the 2nd (child) and 3rd (grandchild) level, and in the twig I only have parent and child.

```
  {% if item.below %}
    {{ menus.menu_links(item.below, attributes.removeClass('nav'), menu_level + 1) }}
  {% endif %}

```

Whatever I put in this is duplicated for the child and grandchildren.

I thought that `menu_level + 2` might get it. What does `menu_level +` do?

---

**EDIT: Thanks to Hudri's tip below I did the following** Thanks Hudri!

```
  {% if menu_level == 0 and item.is_expanded %}
    <li{{ item.attributes.addClass(item_classes) }}>
    <a href="{{ item.url }}" class="dropdown-toggle nav__title" data-target="" data-toggle="dropdown">{{ item.title }} </a>
  {% elseif menu_level == 1 %}
    <li{{ item.attributes.addClass(item_classes) }}>
    {{
      link(
        item.title,
        item.url,
        item.attributes.removeClass(item_classes),
        item.attributes.setAttribute('title', '')
      )
    }}
  {% elseif menu_level == 2 %}
    <li{{ item.attributes.addClass(item_classes) }}>
    {{
      link(
        item.title,
        item.url,
        item.attributes
      )
    }}

```

# Answer
> 4 votes
`menu_links` is a Twig macro, that is called recursively. That macro is located in `menu.html.twig`. In there you can do stuff based on current `menu_level`.

```
{% if menu_level == 0 %}
  <ul class="menu--main">
{% else %}
  <ul class="menu--sub">
{% endif %}

```

Notice that this macro is called recursivly, so if you are adding a classes like `{% set attributes = attributes.addClass('menu--level' ~ menu_level) %}`, this additional class is also stored and passed down recursivly!

Also take a look at `core/themes/classy/templates/navigation/menu.html.twig` to get an example of a full menu template file.

# Answer
> 1 votes
Use macro instead, that's better for recursive twig

# Answer
> 0 votes
This is an example code of macro.

{% macro menu_links(items, attributes, menu_level) %}

  {% if items %}
    {% if menu_level == 0 %}

    {% else %}

    {% endif %}
    {% for item in items %}
      {%
        set classes = [
          'menu-item',
          item.is_expanded ? 'menu-item--expanded',
          item.is_collapsed ? 'menu-item--collapsed',
          item.in_active_trail ? 'menu-item--active-trail',
        ]
      %}

        {{ link(item.title, item.url) }}
        {% if item.below %}
          {{ menus.menu_links(item.below, attributes, menu_level + 1) }}
        {% endif %}

    {% endfor %}

  {% endif %}
{% endmacro %}```

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


# Question
Title: How can I check in Twig if an advanced theme setting option is unchecked?
I've a list of advanced theme option(checkbox) to not display, such as search bar.

I did many research about custom advanced theme setting, but all the samples are `{% if remove_searchbar %}{% endif %}`. This render if the option is checked but I want the opposite.

How do I make a statement for **if an option is unchecked**? I really don't want to do `{% if remove_searchbar %}{% else %}{% endif %}` or make everything checked by default.

# Answer
The following answer is credited to @leymannx.

```
{% if not remove_searchbar %}
  do something
{% endif %}

```

After some research I found twig syntax. You also can find the "if statement" syntax and samples on Twig Symfony website.
> 0 votes

---
Tags: 8, theming
---


# Question
Title: Is there a cli equivalent of the web ui with underspecification?
# background

I run `drush cget markdown.settings` and see:

```
dependencies:
  module:
    - markdown
parser:
  id: michelf/php-markdown-extra
  render_strategy:
    type: none
  settings:
    code_attr_on_pre: false
    code_class_prefix: ''
    empty_element_suffix: ' />'
    enhanced_ordered_list: true
    fn_backlink_class: footnote-backref
    fn_backlink_html: '↩'
    fn_backlink_label: ''
    fn_backlink_title: ''
    fn_id_prefix: ''
    fn_link_class: footnote-ref
    fn_link_title: ''
    hard_wrap: false
    hashtag_protection: false
    no_entities: false
    no_markup: false
    omit_footnotes: false
    predef_abbr: {  }
    predef_titles: {  }
    predef_urls: {  }
    tab_width: 4
    table_align_class_tmpl: ''

```

I visit https://drupal8.example.org/admin/config/content/markdown

In the "Parser" tab, I select `CommonMark (1.5.3)`.

In the "Render Strategy" tab, I select `Filter Output`.

I save the changes.

I run `drush cget markdown.settings` and see:

```
dependencies:
  module:
    - markdown
parser:
  id: league/commonmark
  render_strategy:
    type: filter_output
    allowed_html: '<a href hreflang> <abbr> <blockquote cite> <br> <cite> <code> <div> <em> <h2> <h3> <h4> <h5> <h6> <hr> <img alt height src width> <li> <ol start type=''1 A I''> <p> <pre> <span> <strong> <ul type>'
    plugins:
      - markdown
      - bootstrap
  settings:
    allow_unsafe_links: true
    enable_em: true
    enable_strong: true
    html_input: allow
    max_nesting_level: 0
    renderer:
      block_separator: \n
      inner_separator: \n
      soft_break: \n
    unordered_list_markers:
      - '-'
      - '*'
      - +
    use_asterisk: true
    use_underscore: true

```

Notice that `parser.render_strategy.allowed_html` and `parser.render_strategy.plugins` were added and that `parser.settings` has changed, even though I did not edit them in the web UI.

---

I revert the changes by visiting: https://drupal8.example.org/admin/config/content/markdown

In the "Parser" tab, I select `PHP Markdown Extra (1.9.0)`.

In the "Render Strategy" tab, I select `None`.

I save the changes.

I run `drush cget markdown.settings` and see the original settings:

```
dependencies:
  module:
    - markdown
parser:
  id: michelf/php-markdown-extra
  render_strategy:
    type: none
  settings:
    code_attr_on_pre: false
    code_class_prefix: ''
    empty_element_suffix: ' />'
    enhanced_ordered_list: true
    fn_backlink_class: footnote-backref
    fn_backlink_html: '↩'
    fn_backlink_label: ''
    fn_backlink_title: ''
    fn_id_prefix: ''
    fn_link_class: footnote-ref
    fn_link_title: ''
    hard_wrap: false
    hashtag_protection: false
    no_entities: false
    no_markup: false
    omit_footnotes: false
    predef_abbr: {  }
    predef_titles: {  }
    predef_urls: {  }
    tab_width: 4
    table_align_class_tmpl: ''

```

Notice that `parser.render_strategy.allowed_html` and `parser.render_strategy.plugins` were removed and that `parser.settings` has changed back, even though I did not edit them in the web UI.

# the problem

However, these associated changes do not happen if I change with drush:

```
$ drush cset markdown.settings parser.id league/commonmark
$ drush cset markdown.settings parser.render_strategy.type filter_output
$ drush cget markdown.settings
dependencies:
  module:
    - markdown
parser:
  id: league/commonmark
  render_strategy:
    type: filter_output
  settings:
    code_attr_on_pre: false
    code_class_prefix: ''
    empty_element_suffix: ' />'
    enhanced_ordered_list: true
    fn_backlink_class: footnote-backref
    fn_backlink_html: '↩'
    fn_backlink_label: ''
    fn_backlink_title: ''
    fn_id_prefix: ''
    fn_link_class: footnote-ref
    fn_link_title: ''
    hard_wrap: false
    hashtag_protection: false
    no_entities: false
    no_markup: false
    omit_footnotes: false
    predef_abbr: {  }
    predef_titles: {  }
    predef_urls: {  }
    tab_width: 4
    table_align_class_tmpl: ''
$

```

In particular, notice that `parser.render_strategy.allowed_html` and `parser.render_strategy.plugins` were not added and that `parser.settings` has not changed.

Using the web UI, these associated changes do not have to be specified explicitly.

Is there a way to make similar under-specified config changes by cli?

For clarification, I use `drush` simply as an example of a cli. Using other non-interactive commandline tools would be fine.

# Answer
Using Form API and Configuration are two fundamentally different things:

1. A module configuration forms in Drupal are (usually) provided by that module extending ConfigFormBase. A “Form” is just a specially-crafted render array that on submit (and validate) calls methods on the Form controller class. A module author can do whatever they want in that submit/validate based on user's input. This can include calling set/get on Config objects as they need to, and then calling save. **Form submit methods and callbacks can do any number of complexity rather than a simple operation on a config object.**
2. The Drush config:set (cset) command sets a value to a single config key. It does not do any logic or form submission. One needs to know exactly the configuration that needs to be saved. **The config commands are doing create, read, update, and delete on the config object defined by the provided key.**

The answer is **no**. There is not a way for Drush to determine what the module is actually doing in its form submit.

In order to replicate what a module does in logic, then the same logic must be traced and values changed accordingly before trying to save the configuration.
> 3 votes

# Answer
This appears to update ancillary settings the same as the web UI:

```
drush php << 'EOD'
  use \Drupal\Core\Form\FormState;
  use \Drupal\Core\Render\RenderContext;

  $v['parser']['id'] = 'league/commonmark';
  $v['parser']['render_strategy']['type'] = 'filter_output';

  #$v['parser']['id'] = 'michelf/php-markdown-extra';
  #$v['parser']['render_strategy']['type'] = 'none';

  $s = new FormState();
  $s->setValues($v);
  $c = new RenderContext();
  $r = \Drupal::service('renderer');
  $r->executeInRenderContext($c, function() use ($s) {
    \Drupal::formBuilder()->submitForm(
      '\Drupal\markdown\Form\SettingsForm',$s
    );
  });
EOD

```
> 1 votes

---
Tags: 8, drush, configuration, external-scripts
---


title: Create language locales in D8

text:
# Question
Title: Create language locales in D8
I am attempting to create/enable the UI to allow users to create translations in D8, and I need to create locales (e.g. `en-us`, `en-gb`, `en-ca` for English, `es-mx`, `es-es` for Spanish, etc). I've gone through the directions I've been able to find, and I am not seeing where to add specific locales after adding the base languages (at `admin/config/regional/language`). Or in the `Translate` tab on content items. Where do I go to enable specific locales?

# Answer
To use locales separately, each has to be added as a custom language.

1. `/admin/config/regional/language/add`
2. Choose **Custom language**.
3. Enter the locale that you want as the **Language code**.
4. Enter the **Language name**.
> 0 votes

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


# Question
Title: How to redirect an anonymous user to the login form after a 403 error?
I want to redirect an anonymous user to the login form if such user encounters a 403 error.

I have created event subscriber and this is my code, but I end up on loop on the current page.

```
/**
   * Redirect anonymous user to login page if he encounters 404 or 403
   * response.
   *
   * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $response
   *   The created response object that will be returned.
   * @param string $event
   *   The string representation of the event.
   * @param \Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher $event_dispatcher
   *   Event dispatcher that lazily loads listeners and subscribers from the dependency injection
   *   container.
   */
  public function checkLoginNeeded(GetResponseEvent $response, $event, ContainerAwareEventDispatcher $event_dispatcher) {
    $routeMatch = RouteMatch::createFromRequest($response->getRequest());
    $route_name = $routeMatch->getRouteName();
    $is_anonymous = \Drupal::currentUser()->isAnonymous();
    $is_not_login = $route_name != 'user.login';

    if ($is_anonymous && $route_name == 'system.403' && $is_not_login) {
      $query = $response->getRequest()->query->all();
//      $query['destination'] = $routeMatch->getRouteObject()->getPath();
      $query['destination'] = \Drupal::url('<current>');
      $login_uri = \Drupal::url('user.login', [], ['query' => $query]);
      $returnResponse = new RedirectResponse($login_uri, Response::HTTP_FOUND);
      $response->setResponse($returnResponse);
    }
  }

```

---

I think this is related to the fact that the response already contains destination(current uri) and system.404 and system.403 have some high priority that prevent me to override this.

# Answer
I've seen that this question was never answered how to do this programmatically. The code actually does work, when placed in an Exception Subscriber:

*/src/EventSubscriber/RedirectOn403Subscriber.php:*

```
<?php

namespace Drupal\mymodule\EventSubscriber;

use Drupal\Core\EventSubscriber\HttpExceptionSubscriberBase;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;

class RedirectOn403Subscriber extends HttpExceptionSubscriberBase {

  protected $currentUser;

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

  protected function getHandledFormats() {
    return ['html'];
  }

  public function on403(ExceptionEvent $event) {
    $request = $event->getRequest();
    $is_anonymous = $this->currentUser->isAnonymous();
    $route_name = $request->attributes->get('_route');
    $is_not_login = $route_name != 'user.login';
    if ($is_anonymous && $is_not_login) {
      $query = $request->query->all();
      $query['destination'] = Url::fromRoute('<current>')->toString();
      $login_uri = Url::fromRoute('user.login', [], ['query' => $query])->toString();
      $returnResponse = new RedirectResponse($login_uri);
      $event->setResponse($returnResponse);
    }
  }

}

```

*mymodule.services.yml:*

```
services:
  mymodule.exception403.subscriber:
    class: Drupal\mymodule\EventSubscriber\RedirectOn403Subscriber
    tags:
      - { name: event_subscriber }
    arguments: ['@current_user']

```
> 21 votes

# Answer
The easiest way is to browse to `/admin/config/system/site-information` and fill in the field that reads `Default 403 (access denied) page` with `user/login`
> 14 votes

# Answer
Just looking at the title of this question (= **How to redirect user to login form from 403?**), there are 2 options to do so, without any custom code involved, as detailed below.

# Option 1: Use the CustomError module

The CustomError module allows the site admin to create custom error pages for HTTP status codes 403 (access denied) and 404 (not found), without creating nodes for each of them. Some more details about its features (from its project page):

> * Configurable page title and descriptions.
> * There are no author and date/time headers as with normal nodes.
> * Any HTML formatted text can be be put in the page body.
> * The error pages are themable.
> * Users who are not logged in and try to access an area that requires login will be redirected to the page they were trying to access after they login.
> * Allows custom redirects for 404s.

You'll probably be interested mostly in the part about "*Users who are not logged in and try to access an area that requires login will be redirected to the page they were trying to access after they login.*".

For D8 there is a 8.x-1.x-dev version for this module available also, more details about it can be found in Issue # 2219227.

# Option 2: Use the Rules module

Assume the path of the "Default 403" page is set to `no_access` (via admin). Then create a rule using the Rules module, with as **Event** something like "After visiting node `no_access`". So that the entire rule would look something like so:

* **Events:** After visiting node `no_access`
* **Conditions:**

1. *User has role(s)* \- `Parameter: User: [site:current-user], Roles: anonymous user`
    2. *NOT Text comparison* \- `Parameter: Text: [site:current-page:url], Matching text: user/login`
* **Actions:** *Page redirect* \- `Parameter: URL: user/login`

Should you want to do so, you could even add another **Action** to also display some (informational) message in the Drupal message area, with something like "You tried to visit a page for which login is required ...".

True, it might require you to enable an extra contributed module (Rules). But, as indicated by its growing popularity also, that module is probably already enabled in mostly any site (similar to the Views module), because there are dozens of use-cases for this module.

For D8 there is a 8.x-3.x-alfa1 version for this module available also, more details about its D8 version can be found in Issue # 2574691, which includes a link to the Rules issue "Rules 8.x Roadmap"

If the above doesn't help, then you might want to check if the reason for your loop (or "some high priority" as in your question) cannot be prevented/explained by something similar to the answer to the question "How to specify a Rules event like "Content is 'going to be' viewed"?".
> 10 votes

# Answer
I guess the solution to your problem is simple. You can easily create a custom page for `404` and `403` then inside this page, redirect anonymous users to the `/user` page.

You can use this code

```
function YOURTHEME_preprocess_page(&$vars) {
   $header = drupal_get_http_header('status');
   if ($header == '404 Not Found') {
       $vars['theme_hook_suggestions'][] = 'page__404';
   }
}

```

Whenever 404 occurs your `page--404.tpl.php` will be used. In this page use this code

```
if(user_is_logged_in() == false){
       /// You can do anything here.
}

```

The How to make custom 403 and 404 page in Drupal explains another method of creating a page for `403` and `404`.
> 4 votes

# Answer
The easiest way to do that is to use the Redirect 403 to User Login module (a dev version of it exists for D8). Here is a quote about it (from its project page):

> Redirect the HTTP 403 error page to the Drupal /user/login page with an optional message that reads:
>
> "Access denied! You must login to view this page."
>
> Also, the desired page is appended in the url query string so that, once login is successful, the user is taken directly where they were originally trying to go.
> 3 votes

# Answer
(1) create a page node with an alias like "/my403.html"

(2) go to /admin/config/system/site-information and set "/my403.html" as 403-Errorpage

(3) go to /admin/structure/block and add the "User login" block (Sec. Forms) to the main content region. Restrict block's appearance to page "my403.html" and role "guest".

That's it.

If you need more control, create a controller for your 403 page and add the UserLoginForm by hand.

Best regards, Rainer
> 0 votes

# Answer
Take a look at Redirect 403 to User Login module can help you.
> 0 votes

# Answer
function THEME\_preprocess\_page(&$variables) {

global $base\_url;

$is\_anonymous = \Drupal::currentUser()-\>isAnonymous();

$route\_name = \Drupal::routeMatch()-\>getRouteName();

$is\_not\_login = $route\_name != 'user.login';

if ($is\_anonymous && $route\_name == 'system.403' && $is\_not\_login) {

```
$response = new RedirectResponse( $base_url . '/user/login' );
$response->send();

```

}

}
> -1 votes

---
Tags: 8, routes, redirection
---


# Question
Title: How can I delete a poorly named vocabulary?
I've created a couple of vocabularies programmatically, and accidentally included some spaces in the machine names (caused by bad data in an input CSV). I can't delete these vocabularies with `Vocabulary::load($vid)->delete();`, even though I can see the vocabularies names with
`dsm(taxonomy_vocabulary_get_names());`, while `load($vid)` returns `NULL`.

Any ideas on how I can delete these vocabularies? I was able to delete all the terms in a vocabulary, but the reference remains under `admin/structure/taxonomy`.

# Answer
> 1 votes
Where are you calling that code? Is the `$vid` really right? You say it has spaces in it? Did you try to delete them from the UI?

Under /admin/structure/taxonomies where you have all vocabularies listed, open the operations dropdown and click "Edit vocabulary".

On the next page click "Delete".

# Answer
> 1 votes
Try to delete it from UI if that doesn't work. Install devel\_generate module which is included in devel module as a sub module and select option to generate vocabulary and give count as 0. Don't forget to select delete existing vocabulary. It will delete existing vocabulary which will solve your problem.

# Answer
> 1 votes
To clarify - deleting the vocabulary through the UI allowed some of the menu choices, but not others.

I fixed it by doing a database dump and edited the dump with a search and replace (my vocabulary to my\_vocabuary) and then importing the edited dump before deleting with the UI.

Not an elegant solution, but it worked.

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


# Question
Title: How to programmatically apply a fee/promotion?
I am trying to use the new commerce\_fee module to programmatically apply a fee during admin payment flow. I did a simple form\_alter to the commerce\_payment\_add form to have a list of my Fee entities show up as checkboxes. This allows staff to add the specific fee if they choose.

I also set:

```
$form['payment']['amount']['#default_value']['number'] = $form['payment']['amount']['#default_value']['number'] + $form_state->getValues()['fee_amount'];

```

so that the fee is added in to the next step as the amount which will be charged to the customer's CC.

But the Fee is not properly added to my Order. I have seen Commerce documentation and a post here re: this but they do not work. At the moment I have a #submit added to the payment\_add\_form which follows some of the code I have seen; but no luck:

```
function _sia_add_fee_to_order (&$form, $form_state) {
  // its crazy that i need to figure out the Order from the URL; why isn't it in the form??
  $url = $_SERVER['REQUEST_URI'];
  $bits = explode('/', $url);
  $order_id = $bits[4];

  $order = Order::load($order_id);
  $fee = current(Drupal\commerce_fee\Entity\Fee::loadMultiple());

  $price = $fee->getPlugin()->getConfiguration()['amount'];
  $splitter = \Drupal::getContainer()->get('commerce_order.price_splitter');
  $amounts = $splitter->split($order, new Price($price['number'], $price['currency_code']));

  foreach ($order->getItems() as $order_item) {
    $order_item->addAdjustment(new Adjustment([
      'type' => 'fee',
      'label' => $fee->getName(),
      'amount' => $amounts[$order_item->id()],
      'source_id' => $fee->id(),
    ]));
  }

  $fee->apply($order);
  $order->recalculateTotalPrice();
  $order->save();
}

```

The post I saw here left out all the commerce documentation steps of adding adjustments to each line item and simply had the $fee-\>apply() function. Neither of theses work.

ADDED: More info about commerce\_fee: https://www.drupal.org/project/commerce/issues/2903716, https://github.com/lisastreeter/commerce\_fee

This is basically the commerce "promotion" module but redone to be more specific to fees. The question i have posted could be asked re: promotions as well; as should apply equally to either.

# Answer
> 0 votes
After a lot of trial and error I thought of moving the code out of the form submit function and into an event subscription; in particular, i moved it to the commerce\_order.order.paid event.

So I ended up with this in an existing event subscriber call back I already had:

```
  public function placeOrder($event) {
    $order = $event->getOrder();

    $fee = current(\Drupal\commerce_fee\Entity\Fee::loadMultiple());
    $price = $fee->getPlugin()->getConfiguration()['amount'];
    $splitter = \Drupal::getContainer()->get('commerce_order.price_splitter');
    $amounts = $splitter->split($order, new Price($price['number'], $price['currency_code']));
    foreach ($order->getItems() as $order_item) {
      $adjustments = $order_item->getAdjustments();
      $adjustments[] = new Adjustment([
        'type' => 'fee',
        'label' => $fee->getName(),
        'amount' => $amounts[$order_item->id()],
        'source_id' => $fee->id(),
      ]);
      $order_item->setAdjustments($adjustments);
      $order_item->save();
    }
  }

```

This code includes code to split the fee up among all the items; which may or may not be what you want (although doesn't seem like it could ever be a bad idea).

---
Tags: 8, commerce
---


title: Changing the Title of a Blog

text:
# Question
Title: Changing the Title of a Blog
How can I change the title of my blog? It's found on the main page of the blog above the previews of the posts. I am attaching a screen shot.

I want to change "The Scrapbooked Bible Study" (on the left above the first blog post preview) to "Winning the Victory." Thank you.

# Answer
That page seems to be a Views page, but there is no way of telling from the screenshot if you're using a special module to build those page or simply Views.

If it's Views then you can Edit the particular View, it has the `Title` option for the Page, that's where you change it.

You can find list of all Views under Structure on the top menu.
> 2 votes

---
Tags: configuration
---


# Question
Title: Create a node when the user is activated
Drupal Version: 8.92

Part 1. I am trying to create a node when the user is activated by the administrator for the first time. However, I am not able to find the right event/hook to execute the code. I tried "user\_activated" but it is not working.

```
function my_module_user_activated(\Drupal\user\UserInterface $user) {
  \Drupal\node\Entity\Node::create([
    'type' => 'my_node',
    'title' => $user->getDisplayName(),
    'uid' => $user->id(),
  ])->save();
}

```

Any pointers?

PS: I don't want to create the node using hook\_user\_insert().

Part 2. If I need to delay the node creation to user validating the email by clicking a URL, what hook should I try?

Thank You! Uri

# Answer
> 1 votes
Modified code based on the pointers from No Sssweat. The working code is below for any future references.

```
function <MY-MODULE>_user_presave(\Drupal\user\UserInterface $user) {
  if (($user->isActive()) and ($user->getLastAccessedTime() == 0 ) and ( $user->hasRole('role_name'))){
  \Drupal\node\Entity\Node::create([
    'type' => 'my_content_type',
    'title' => $user->getDisplayName(),
    'uid' => $user->id(),
  ])->save();
  }

```

}

---
Tags: hooks
---


# Question
Title: How to load my module's css file after my theme's css file?
In my theme, I have css files loaded through `mytheme.libraries.yml` and `mytheme.info.yml` file.

In my module I have added a css file like this

```
function mymodule_page_attachments(array &$attachments) {
  $attachments['#attached']['html_head'][] = [
    [
      '#tag' => 'link',
      '#attributes' => [
        'rel' => 'stylesheet',
        'media' => 'all',
        'href' => path_to_css_file,
      ],
    ],
    'mymodule_css',
  ];
}

```

But when visit a page, I see the my module's css file is loaded first and then the theme's css files.

I want to load my module's css file at the end of html head or just after my theme's css files.

How will I do that?

# Answer
You can define the priority for the css to load , Add weight key for the CSS file in the \*.libraries.yml , the lesser the weight the higher the priority.

```
  cuddly-slider:
    version: 1.x
      css:
        theme:
          css/cuddly-slider.css: {weight: -50}

```
> 1 votes

# Answer
I think you should add your module's CSS via a `m̀y_module.libraries.yml` instead.

And just for the sake of completeness: You could also implement hook\_library\_info\_alter and move your module's library after your theme's library. The already suggested solution using weight is preferable though.

If you need conditional logic for adding the module's CSS, the hook is the way to go.
> 1 votes

---
Tags: 8, theming
---


title: drupal/core vs. drupal/core-recommended

text:
# Question
Title: drupal/core vs. drupal/core-recommended
I've got a question regarding `drupal/core` and `drupal/core-recommended`. So `drupal/core-recommended` uses fixed versions of the packages, which the corresponding core version was tested with. And yes it does make sense that this prevents bugs introduced by version differences of let's say different versions of Symfony packages for example.

But on the other hand, let's say you install a dev version of a module or any other third-party library with composer, which explicitly needs a different version of a Symfony component than `drupal/core-recommended`, these locked versions of the core dependencies will cause trouble, as the third party library/the module could not get installed due to version conflicts.

So how would one deal with this kind of situation when using `drupal/core-recommended`?

# Answer
It's "core-recommended" as in "recommended to avoid having dependency-related trouble with core."

Drupal core (and some of the contributed modules, but not all) has a variety of automated tests that are run to make sure that everything is working as expected. However, Drupal also needs dependencies to run. If you use different versions of the dependencies that were not tested, there might be a bug and the tests might not pass. `core-recommended` contains the specific dependencies that were used to test a given version of Drupal core.

By using `core-recommended`, you are guaranteed to get the same dependencies that were used to release that version of Drupal core, and Drupal core is only released when those tests pass. So, you can be more confident when you are deploying your site because you know that your combination of core + dependencies is verified to work according to the tests.

> let's say you install a dev version of a module or any other third party library with composer, which explicitly needs a different version of a Symfony component than drupal/core-recommended,

It's relatively rare for a contributed module to need a specific version of a Symfony component. When you do need to use such a module and you switch from `core/recommended` to `core`, you might experience some Symfony-related or other dependency-related bugs because some of the dependencies may require changes in core that have not been made yet. So when you do this, you are now responsible for running the tests, and if you encounter bugs you may have to search the core issue queue and apply patches, etc.

As for dev modules, it's never **recommended** to install dev modules on production, so whenever you install a dev module, you are taking responsibility for testing into your own hands.

TLDR: **Recommended** means "recommended because it passed the tests." These are the *recommended* dependencies because they are known to work, but of course you can try to use others if you like. But, if you don't use the recommended dependencies and then you find a bug and think, "hey, core is broken, let's file an issue!", you should first check with `core-recommended` to make sure the issue isn't specific to the contrib module/dependency that required you to remove `core-recommended`.
> 13 votes

# Answer
Let's look at the README of `drupal/core-recommended`. This basically explains it all.

> Require this project instead of the drupal/core subtree split in order to guarentee that all of the dependencies from `drupal/core` will be included in your Drupal site at exactly the same version that was tested with the version of Drupal you are currently using.
>
> The consequences of not using this project is that Drupal's dependencies may "float up" to more recent versions than were tested with Drupal when you use Composer to upgrade your Drupal site. Occasionally, new dependency versions introduce bugs in Drupal. While these sorts of errors are usually corrected fairly quickly, it can still be quite disruptive to be one of the first people encountering one of these bugs. Using the recommended dependencies avoids this problem by only using dependency versions that have already been tested with Drupal.

Also in the docs on Using Composer to Install Drupal and Manage Dependencies.

> Run `composer require drupal/core-recommended` to install Drupal core into your preferred directory structure. You could also require the `drupal/core` package, if you did not want to lock all of Drupal core's dependencies to known-good versions.
> 3 votes

---
Tags: composer
---


# Question
Title: Checking whether an arbitrary field is valid for user entity
In my module I have a form where I want to give an admin the ability to configure which user field they want to put certain data in. In the context of the admin form, I'd like to verify that the field actually exists.

If I have a given user, I can probably use:

```
$userEntity->hasField('custom_field')

```

What is the correct way to do this when I don't have any specific user entity? After all, fields should be the same for any user.

# Answer
> 1 votes
Yes, for users you can't configure different bundles like content types for nodes. So fields are the same for any user.

If you don't have an existing user you can create one temporarily:

```
use Drupal\user\Entity\User;

$field_exists = User::create()->hasField('custom_field');

```

---
Tags: 8, entities, users
---


# Question
Title: Unable to change the widget for a webform coponent
When editing a webform component (7.x-4.23) selecting a different widget type (e.g. from email to hidden) has no effect.

# Answer
> 0 votes
Disabling module Hide Submit Button (7.x-2.4) restored the webform functionality.

---
Tags: webforms
---


# Question
Title: Delegate user administration to a limited set of users
We are currently using the excellent `domain` module (allows segmenting users by domain) in and the `user_import` module (allows uploading users via CSV) in Drupal 7.

We've got a new requirement where we want certain authorised users to be able to perform CSV uploads and edit user details for other users within their `domain`, but limited to no other admin function within the site (or other user details).

Without re-implementing each function (edit users, upload CSV, etc) in a new module (my fallback plan), is this possible within the Drupal permission / authorisation framework?

My gut feeling and exploration so far is no, it isn't. But I am no Drupal expert...

# Answer
> 0 votes
I was never able to find a workaround for this. I ended up replicating the UI outside of the admin panel and reused a collection of the import functions (I also abandoned the users making "manual" edits.)

---
Tags: 7, users, authentication-authorization
---


title: Conditional fields on block add form

text:
# Question
Title: Conditional fields on block add form
I have a custom block type created through the UI and I used the configuration files to export it to a module. My custom block type has two fields and I want users to be able to fill out one or the other, but not both.

Is there way to implement that logic through the UI? If not, how can I create the custom block type in code and add this logic?

# Answer
i just found this module that is supposed to do that

https://www.drupal.org/project/conditional\_fields

I believe a form alter function on a module should do the trick however i haven't implemented anything like it.

Good luck.
> 0 votes

---
Tags: blocks
---


# Question
Title: How can I add a contextual filter with taxonomy term name as entity reference?
I have a content type "award" which have a taxonomy (product) as entity reference.

I want to add a contextual filter by term name on my view. I tried to use a contextual filter on "taxonomy term name" but it's not working, looks like i'm doing something wrong

I added the field in advanced \> relations, that way i managed to make a working filter on the taxonomy term name i wanted, but i need a contextual filter

here is the view :

here is the contextual filter configuration

# Answer
> 1 votes
The direct Product (which is a Taxonomy reference in your case) Contextual filter only works with term IDs, not their names. So you can make it work only if you choose the term ID in the validation settings.

To use the term name as the contextual filter you have to add the Relationship for the taxonomy reference field, which you seem to have done.

Now you can add the Term **Name** Contextual filter, from the Taxonomy term Category. (Remove the Contextual filter for the Product field that you currently have.) You didn't have the Term Name available under Contextuals before you added the Relationship. That Contextual will use the Relationship automatically.

When you add the Term **Name** contextual you will be able to set up the term name Validation, and you'll need to enter the term names in path arguments and in Views Preview field to test it.

---
Tags: 8, views
---


# Question
Title: I need to group a view by first initial (A,B,C) but both A and a are considered separate groups
How can I get views to group upper and lowercase together?

I have the title field added twice in the view and the second instance under REWRITE RESULTS I have "Trim this field to a maximum length" of 1. My view is grouped by this second field (so the first letter of the title.)

I have tried glossary mode with a contextual filter and that also didn't yield results.

Do I have to convert the field with strtoupper in a view template to achieve this?

# Answer
> 2 votes
In Drupal 8, a workaround is with twig. Choose the option to *Override the output of this field with custom text* and then use twig to capitalize.

```
{{ my_field|upper }}

```

---
Tags: 7, views
---


# Question
Title: Unable to specify different grid columns settings for page and attachment
I am trying to build a views grid display where I have two rows of 3 items each, and then one row of of four items. To me, the logical approach would be to use a primary display in a grid format, and then an attachment, since the layout is slightly different. However, this does not work, because it is impossible to have different grid settings for the primary display and the attachment.

For instance, I set the number of `columns` to 3 for the page display. However, when I create the attachment display, set the `columns` to 4, and attach it to the page display, when I go back to the page display, the columns in the grid settings is set to 4. In order to get 4 items in a row in the attachment without affecting the parent display, I have to use the `unformatted` format and do theming.

Is there a way to have separate grid settings for an attachment vs the parent display? There is no option in the grid settings for overriding for just that display.

# Answer
> 0 votes
You *can* apply different `Format` to different Views Displays.

In your Attachment Display you have to click on the **Format** option, *not* on the **Settings** link. This will open the modal dialouge where you change the Format for the Attachment, but before you save make sure you change the **For** option to **This attachment (override)**.

The default option for the **For** is "All displays" so if you change it under Attachment, it will automatically change on your Page Display as well, unless you override it like I described above.

Just remember that the overriding option appears on the modal dialogue where you're choosing the option, not on the "Settings" modal that depends on which option is set.

---
Tags: 8, views
---


# Question
Title: Looking for a solution to add taxonomy after user submitted a node?
I want users to be able to publish content without having to associate it with a specific taxonomy term. They only have access to taxonomy tags.

I would like to be able to assign content to categories (taxonomy vocabulary/terms) on the backend as an admin or let another role have permission to do that.

Basically on a given content type, i don't authors to see the taxonomy term reference field when adding/editing a node

How should I go about this ?

# Answer
> 0 votes
There is more than one module for dealing with Taxonomy permissions. Some work on Vocabulary basis, others deal with terms directly. Search the modules, test them out and find out which ones suit your needs: Search Taxonomy Content Access Control for Drupal 8

In your case it may be appropriate to use the Field Permissions module.

# Answer
> 0 votes
Limit access to a taxonomy reference field to users with a certain role: Field Permissions module or custom code

Set a permission for the field and limit access to whatever role you want. (Give "edit" permission to the admin, but "view" permission to everyone.) Then users who do not have the role won't see the field for editing, although they will still be able to see the tags on the node itself.

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


# Question
Title: Show form message only on initial load, but not after submit
I'm often using `Drupal::messenger()` to show status/warning messages *before* I submit the form. But those messages are usually counter-intuitive once the form has been submitted. How can I add a message to a form that is only rendered *before* the form has been submitted.

My typical code looks like this:

```
function buildForm() {
  /**
   * ...build form...
   */
  $showWarning = $this->doSomeChecks();

  /**
   * tried to use isSubmitted() to prevent the message after submitting, but does not work
   */
  if ($showWarning  && !$form_state->isSubmitted()) {
    $this->messenger()->addWarning('Some info you should know before submitting this form.');
  }
}

function submitForm(array &$form, FormStateInterface $form_state) {
  /**
   * ...do stuff...
   */
  $this->messenger()->addMessage('Successfully done something');

  /**
   * tried to use deleteByType() to remove message from buildForm after submitting, but does not work
   */
  $this->messenger()->deleteByType($this->messenger()::TYPE_WARNING);
}

```

I know I could use `$form_state->setRedirect($customPage)` in `submitForm()`, but I don't want the overhead of creating a new page just to hide a simple warning message.

Is there no simple way to show a message only once on the initial form load, but not after submit?

# Answer
Try

```
if (empty($form_state->getUserInput())) {
  $this->messenger()->addWarning('Some info you should know before submitting this form.');
}

```

See the discussions here: Avoid repetitive API calling in form rebuild while submitting
> 2 votes

# Answer
This is my current version working for my simple usecase. Since `buildForm()` is triggered multiple times during a single submit request, the combined check `empty($form_state->getUserInput()) && !$form_state->isRebuilding()` allowed me to detect if the form was initially loaded/not submitted yet.

```
class MyConfirmForm extends ContentEntityConfirmFormBase {

  public function getQuestion() {
    return $this->t('Do you want to do stuff?');
  }

  function buildForm(array $form, FormStateInterface $form_state) {
    $form = parent::buildForm($form, $form_state);

    $showWarning = $this->doSomeChecks();

    if ($showWarning && empty($form_state->getUserInput()) && !$form_state->isRebuilding()) {
      $this->messenger()->addWarning('Some info you should know before submitting this form.');
    }

    return $form;
  }

  public function submitForm(array &$form, FormStateInterface $form_state) {
    parent::submitForm($form, $form_state);

    $result = $this->doStuff();

    if ($result) {
      $this->messenger()->addMessage('Successfully done something');
      $form_state->setRebuild(TRUE);
    }
  }
}

```

I'm leaving the question open though, because @4k4 indicated in comments above that using `$form_state->setRebuild()` inside `submitForm()` is not a good practice.
> 0 votes

# Answer
You can try

```
$request = \Drupal::request()->server;
$http_referer = $request->get('HTTP_REFERER');
$showWarning = TRUE;
if ($http_referer) {
  $host = $request->get('HTTP_HOST');
  $request_scheme = $request->get('REQUEST_SCHEME');
  $current_url = str_replace($request_scheme . '://' . $host, '', $http_referer);
  $url_object = \Drupal::service('path.validator')->getUrlIfValid($current_url);
  $route_name = $url_object->getRouteName();
  if ($route_name === 'custom.router') {
    $showWarning = FALSE;
  }
}

if ($showWarning &&  $this->doSomeChecks()) {
  this->messenger()->addWarning('Some info you should know before submitting this form.');
}

```
> 0 votes

---
Tags: forms, status-messages
---


# Question
Title: How to access backbone model from custom javascript outside of view
I was wondering if there is a way to access backbone model variable outside the scope of the backbone model/view.This is the backbone model:

```
function (Backbone, Drupal) {
  Drupal.addToCart.AddToCartModel = Backbone.Model.extend({
    defaults: {
      defaultVariation: '',
      selectedVariation: '',
      attributes: {},
      renderedAttributes: {},
      injectedFields: {},
      variations: {},
      variationCount: 0,
      quantity: 1,
      unit_box: 1,
      type: 'commerce_product_variation_attributes'
    },
    initialize: function initialize() {
      this.set('variationCount', Object.keys(this.get('variations')).length);
      this.set('selectedVariation', this.getVariation(this.get('defaultVariation')));
    },
    // ... some getter and setter
  });
})(Backbone, Drupal);

```

And this is the view:

```
(function ($, Backbone, _, Drupal) {
  Drupal.addToCart.AddToCartView = Backbone.View.extend({
    initialize: function initialize() {
      var _this = this;

      var defaultVariation = this.model.getVariation(this.model.getDefaultVariation());
      _.each(this.model.getAttributes(), function (attribute, i) {
        var attributeFieldName = 'attribute_' + attribute.id;
        if (defaultVariation.hasOwnProperty(attributeFieldName)) {
          _this.selectedAttributes[attributeFieldName] = defaultVariation[attributeFieldName];
        }
      });
      this.render();
    },

    events: {
      'click .form-submit': 'addToCart',
      'change .attribute-widgets input[type="radio"]': 'onAttributeChange',
      'change .attribute-widgets select': 'onAttributeChange',
      'change .variations-select select': 'onVariationTitleChange',
    },
    // ... events on...
    onAttributeChange: function onAttributeChange(event) {
      Drupal.detachBehaviors();
      this.selectedAttributes[event.target.name] = event.target.value;
      var selectedVariation = this.model.getResolvedVariation(this.selectedAttributes);
      this.model.setSelectedVariation(selectedVariation.uuid);
      var injectedFields = this.model.getInjectedFieldsForVariation(selectedVariation.uuid);
      Object.values(injectedFields).map(function (injectedField) {
        $('.' + injectedField.class).html(injectedField.output);
      });
      Drupal.attachBehaviors();

      $('.banner.wishlist').click(function () {
        $.ajax({
          url: Drupal.url('wishlistupdate'),

          method: 'POST',
          data: JSON.stringify([{
              'variation': $(this).attr('data-variation')
            }]),
          contentType: 'application/json;',
          dataType: 'json'
        }).done(function (data) {
          // <--- HERE IS WHERE I WANT TO ACCESS BACKBONE MODEL TO ALTER SOME PROPERTIES
      });
     });
    },
// ... some other events subscribers
});
  Drupal.addToCart.AddToCartView.prototype.selectedAttributes = {};
})(jQuery, Backbone, _, Drupal);

```

And this is the theme settings:

```
(function ($, _, Drupal, drupalSettings) {
  Drupal.theme.addToCartButton = function () {
    return drupalSettings.theme.commerce_cart_flyout_add_to_cart_button;
  };
  //... some other themes functions
  Drupal.theme.addToCartVariationSelect = function (_) {
    return function (args) {
      var template = _.template(drupalSettings.theme.commerce_cart_flyout_add_to_cart_variation_select);
      return template(args);
    };
  }(_);

  Drupal.addToCart = {};
  Drupal.behaviors.addToCart = {
    attach: function attach(context) {
      $(context).find('[data-product]').once('flyout-add-to-cart').each(function (k, el) {
        var model = new Drupal.addToCart.AddToCartModel(drupalSettings.addToCart[el.dataset.product]);
        new Drupal.addToCart.AddToCartView({ el: el, model: model });
      });
    }
  };
})(jQuery, _, Drupal, drupalSettings);

```

What I want is to access backbone model variables on ajax done method, but I didn't find a way to do it.

# Answer
> 1 votes
After some debugging I have found I can access drupalSettings object and It has a property addToCart with all the data I need.

---
Tags: 8, commerce, javascript, ajax
---


# Question
Title: Proper way to alter field value from submit handler
I added extra field "field\_password\_changed" to user account and I want to store there time of latest password change (simple input field).

Now, I added custom submit handler to catch user account edit action and currently it looks like this:

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

  $form_state->set('field_password_changed', 'test22');

  $form['field_password_changed']['en'][0]['value']['#value'] = '54321';
  $form['field_password_changed']['widget'][0]['value']['#default_value'] = 'My new value';
  $form['field_password_changed']['widget'][0]['value']['#value'] = 1;

  $form["field_password_changed"]["#default_value"] = 'ffff';

}

```

Basically I tried every suggestion from this page: https://www.drupal.org/forum/support/module-development-and-code-questions/2013-08-01/using-hook\_form\_alter-to-set-a-field

And not even one is working form me.

What is the proper way to change form field value in custom submit handler?

# Answer
> 4 votes
Consider using the Force Password Change module which can already keep track of that.

---

First, add your custom submit handler to the beginning of the submit handlers array and not to the end, to ensure you "inject" the data before the user entity gets saved.

```
/**
 * Implements hook_form_FORM_ID_alter().
 */
function MYMODULE_form_user_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  array_unshift($form['actions']['submit']['#submit'], 'user_form_submitted');
}

```

Second, depending a little bit on what field type exactly you added to the user entity, the form value needs to be an array of values. In the sample below I used a simple plain textfield to store the data.

```
function user_form_submitted(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
  $form_state->setValue('field_password_changed', [['value' => 'test22']]);
}

```

Third, you somehow need to check if the password got changed at all or if just the user form got submitted. Again, have a look at the `force_password_change.module` to get an idea of what exactly you may need to check for.

# Answer
> 1 votes
@leymannx's answer is correct and by following his instructions and also by looking into "Force password change" module code I came to following solution:

```
function user_form_submitted(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
  $current_pass = $form_state->getValue('current_pass');
  if($current_pass && $current_pass != $form_state->getValue('pass')) {
    $form_state->setValue('field_password_changed', [['value' => date("Y-m-d")]]);
  }
}

```

In case someone else needs similar feature.

---
Tags: 8, webforms
---


# Question
Title: User Role creation in module overridden by configuration import
I'm creating a user Role in a .yml file like `user.role.myrole.yml`. This works fine when this module is installed manually and the Role is created.

When I enable this module via configuration import though, only the roles defined in this configuration set are installed and not what is in the config/install folder of that module.

Tried putting that file in config/optional folder and writing a hook\_install() for the user creation also with the same results.

I would like to have this only in the module, the module may or not be installed via config, so I need this role only when this module is enabled. How can this be achieved?

Thank you

# Answer
Docs for installing default configuration in a custom module

However, putting a role in the configuration of a module is probably a bad idea. I can't think of an example of a major contrib module that does this.

Imagine this situation: you install the module, and then the role gets enabled. Next, the user adds some other permissions to the role (created by the module) unrelated to your module. Then the user uninstalls the module. Now what should happen? Usually, the expectation is that module config will be cleaned up on uninstall, but this won't happen for roles.

What you probably want to do is add some permissions and allow the user of the module to add those to the role of the user's choice.
> 1 votes

---
Tags: 8, users, configuration
---


# Question
Title: how to display a media description in a twig template?
i want to display the media description available in the file field of a media ( media of type video, audio, image, etc...), it is an option to activate:

> Enable Description field
>
> The description field allows users to enter a description about the uploaded file.

i already print other fields that i want in the media twig template, but i can not find the value of that "description" field attribute, because it's not a field, but some sort of attribute of the file field.

# Answer
> 1 votes
I got the media description in the preprocess

```
function template_preprocess_media(array &$variables) {

  $bundle = $media->Bundle();

  if($bundle == 'file') {
    $field_media_file = $variables['content']["field_media_file"];

    if($field_media_file[0]["#description"]){
      $desc = $field_media_file[0]["#description"];
    } else{
      $desc = $variables['name'];
    }
    $variables['description'] = $desc;
  }
}

```

---
Tags: 8, theming, media
---


# Question
Title: How to programmatically create a custom token in a module
How would you go about programmatically creating a token? I wish to add some custom tokens for my module.

# Answer
> 7 votes
In Drupal 6, you use `hook_token_values()`.

This hook will allow you to create tokens. You can create them in the global scope or you can use an object like a node, or a user to seed the values.

You should also use `hook_token_list()` to explain what your tokens are.

The token.api documentation is quite clear.

```
function my_user_token_values($type, $object = NULL, $options = array()) {
  if ($type == 'user') {
    $user = $object;
    $tokens['name']      = $user->name;
    $tokens['mail']      = $user->mail;
    return $tokens;
  }
}

```

I won't X post the whole thing but that should give you a high level idea.

# Answer
> 21 votes
In Drupal 7 the code for handling tokens is part of the Drupal core module.

The hooks the token modules need to implement are:

* hook\_token\_info() is the hook that provides information about the tokens implemented by a module.
* hook\_tokens() is the hook that needs to be implemented to provide the actual values that replace the tokens.

Other modules can alter the token implementation provided from a module using hook\_token\_info\_alter() and hook\_tokens\_alter().

Differently from the Token module, the code in Drupal core allows to create the content of a token only when strictly necessary. In Drupal 6, the Token module would ask to the modules implementing tokens all the values for their token using `hook_token_values()`; this means that a module can compute the value for a token which is then not required for the tokens being replaced. In Drupal 7, the implementation of `hook_tokens()` receives `$tokens`, an array of tokens to be replaced, as argument; the module is then able to compute the value of a token, knowing it will be used.

The function that in Drupal 7 is used to replace the tokens with their value is token\_replace(), which is the only function used to replace the tokens with their values.

Other difference between the Token module for Drupal 6 and the code in Drupal 7 are:

* In Drupal 7, \[node:author\] returns the name of the author; \[node:author:mail\] returns the email address associated with the author of a node, and \[node:author:url\] returns the URL of the user profile for the node author. In other words, it is possible to use \[node:author:xyz\], where "xyz" is one of the tokens returned for a user object.
* In Drupal 7, there are not raw tokens; the implementation of `hook_tokens()` get a parameter that tells to the hook when the content of the token needs to be sanitized; when the token value doesn't need to be sanitized, the content is not passed to the functions `check_plain()` or `filter_xss()`.
* In Drupal 7, there isn't any function that show the list of the available tokens. If a module needs to show the list of available tokens, it must build the list of the tokens itself and show it in the description of a form field; alternatively, it can use the theme function still available in the Token module.

# Answer
> 11 votes
I wanted to add a new token to the *Site information* section of tokens, called *City name*. This is how I did it in Drupal 7.

```
 /**
 * Implements hook_token_info().
 */
function my_module_token_info() {

  // Add tokens.
  $site['city_name'] = array(
    'name' => t('Token Name'),
    'description' => t('Token Description'),
  );

  return array(
    'tokens' => array(
      'site' => $site,
    ),
  );
}

/**
 * Implements hook_tokens().
 */
function my_module_tokens($type, $tokens, array $data = array(), array $options = array()) {
  $replacements = array();

 if ($type == 'site') {
    foreach ($tokens as $name => $original) {
      switch ($name) {
        case 'city_name':
          $city_name = variable_get('city_name');
          $replacements[$original] = $options['sanitize'] ? check_plain($city_name) : $city_name;
          break;
      }
    }
  }

  // Return the replacements.
  return $replacements;
}

```

# Answer
> 6 votes
**For Drupal 8, example using the node object:**

You can put tokens in your module at mymodule.tokens.inc using hook\_token\_info() to register them and hook\_tokens() for the the replacement data.

If you want to create a custom token for an existing token type, such as for nodes, you'll need to put your token within the subarray within hook\_token\_info(). Refer to node.tokens.inc in the node module to see what you're building off of.

**mymodule.tokens.inc:**

```
<?php

use Drupal\Core\Render\BubbleableMetadata;
use Drupal\image\Entity\ImageStyle;

/**
 * Implements hook_token_info().
 */
function mymodule_token_info() {
  $info = array();

  $info['tokens']['node']['custom_title'] = [
    'name' => t("Custom Title"),
    'description' => t("a custom node title token"),
  ];
  // Return them.
  return $info;
}

/**
 * Implements hook_tokens().
 */
function mymodule_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {

  $replacements = array();
  if ($type == 'node') {
    foreach ($tokens as $name => $original) {
      // Find the desired token by name
      switch ($name) {
        case 'custom_title':
          $node = $data['node'];
          $replacements[$original] = $node->label();
          break;
      }
    }
  }
  // Return the replacements.
  return $replacements;
}

```

# Answer
> 5 votes
**For Drupal 8**

```
// We need to include the needed class for tokens.

use Drupal\Core\Render\BubbleableMetadata;

/**
 * Implements hook_token_info().
 */
function modulename_token_info() {
  $info = array();
  // Add any new tokens.
  $info['tokens']['customtokentype']['customtoken'] = t('Telling drupal that you define custom token');
  // Return them.
  return $info;
}

/**
 * Implements hook_tokens().
 */
function modulename_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
  $replacements = array();
  $simple = $data["customanything"];
  if ($type == 'customtokentype') {
    foreach ($tokens as $name => $original) {
      // Find the desired token by name
      switch ($name) {
        case 'customtoken':
          $new = $simple;
          $replacements[$original] = $new;
          break;
      }
    }
  }
  // Return the replacements.
  return $replacements;
}

```

Getting the value of tokens in your function requires code similar to the following one.

```
$token = \Drupal::token();
$message_html = "hello my custom token is replaced see it here [customtokentype:customtoken]";

// Token data.
$data = array('customanything' => $tosendtotokens);
$message_html = $token->replace($message_html, $data);

```

# Answer
> 1 votes
In `my_module.tokens.inc` place two functions like this:

```
<?php

/**
 * Implements hook_token_info().
 */
function my_module_token_info() {
  $info['tokens']['node']['my_custom_title'] = [
    'name' => t("Custom title"),
    'description' => t("The first 10 characters of the title"),
  ];
  return $info;
}
/**
 * Implements hook_tokens().
 */
function my_module_tokens($type, $tokens, array $data, array $options, \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata) {
  $replacements = [];
  if ($type == 'node') {
    foreach ($tokens as $name => $original) {
      // Find the desired token by name
      switch ($name) {
        case 'my_custom_title':
          $node = $data['node'];
          $replacements[$original] = substr($node->getTitle(), 0, 10);
          break;
      }
    }
  }
  return $replacements;
}

```

---
Tags: tokens
---


title: Programatically set the language

text:
# Question
Title: Programatically set the language
I'm trying to automate the setup of Drupal multi-site installations as much as possible, we're using each multi-site for a separate country so would like the languages localised for each.

Is there a way to programmatically set the default language? It normally defaults to English. For a Spanish multi-site I would like to set it to Spanish.

The default language seems to be set in SelectLanguageForm.php .Since this happens so early on in the installation process, I can't use `hook_form_alter()` because no modules, profiles, or themes would be loaded at that point.

The default language will be defined basing on the subdomain where Drupal is going to be installed.

# Answer
You should consider using Drush (or Bash running Drush) for that. With `drush site:install` you can set the default language via `--locale`.

The following command will (re)install Drupal using the `expert` install profile and set default language to Ukrainian.

```
drush si expert --locale=uk

```

Source: https://drushcommands.com/drush-9x/site/site:install/
> 0 votes

# Answer
```
drush config:set system.site default_langcode en

```
> 0 votes

---
Tags: 8, i18n-l10n, installation
---


# Question
Title: In Views, when using a filter on a datetime field, what is the time zone of the offset?
When I use an offset like "-4 hours" in a Views filter on a datetime field, which timezone is used?

**More details**

In Views, when I add a filter for a **datetime** field, I get the following options for **Value type**:

* A date in any machine readable format. CCYY-MM-DD HH:MM:SS is preferred.
* An offset from the current time such as "+1 day" or "-2 hours -30 minutes"

On my site, users can set their own timezones. (/admin/config/regional/settings *Users may set their own time zone*)

So there are potentially three timezones in play:

* In the database, the strings for **datetime** are stored in the UTC timezone
* The site timezone is set to Tokyo
* The user timezone is set to Berlin

So, which of these timezones does the offset use?

# Answer
For datetime fields, it uses the timezone for the user viewing the page. If no user timezone is set, it uses the default site timezone.

See the relevant comment from *Drupal\datetime\Plugin\views\filter\Date.php*:

> Date-only fields do not have a time zone associated with them, so the filter input needs to use UTC for reference. Otherwise, use the time zone for the current user.

```
/**
 * Get the proper time zone to use in computations.
 *
 * Date-only fields do not have a time zone associated with them, so the
 * filter input needs to use UTC for reference. Otherwise, use the time zone
 * for the current user.
 *
 * @return string
 *   The time zone name.
 */
protected function getTimezone() {
  return $this->dateFormat === DateTimeItemInterface::DATE_STORAGE_FORMAT
    ? DateTimeItemInterface::STORAGE_TIMEZONE
    : date_default_timezone_get();
}

```
> 2 votes

---
Tags: views, datetime
---


# Question
Title: Get the user ID for a newly created user account
I'm trying to import user data from a JSON file. So far I've managed to import user entities with some fields. The next step would be to retrieve the user ID.

The code I wrote is the following one.

```
$users_json = file_get_contents("../import/users.json");
$users_array = json_decode($users_json, true);

foreach ($users_array as $user) {
  if(!empty($user["roles"])) {
    $values = array(
      'name' => $user["name"],
      'pass' => '',
      'status' => $user["status"],
      'mail' => $user["e_mail"],
      'field_first_name' => $user["first_name"],
      'field_last_name' => $user["last_name"],
      'field_nationality' => $user["nationality"],
      'field_introduction' => $user["introduction"],
      'field_about_me' => $user["about_me"],
      'field_current_job' => $user["current_job"],
      'field_website' => $user["website"],
    );

    $storage = \Drupal::entityTypeManager()->getStorage('user');
    $userEntity = $storage->create($values);
    if ($userEntity->save() == 1) {
      echo "User " . $userEntity->id . " saved!\n\n";
    }
    else {
      echo "User " . $userEntity->id . " updated!\n\n";
    }
  }
}

```

However, when I try to print the user ID, I get no data.

I've just started to use Drupal API, so I'm not very familiar with it. Is there something I'm doing wrong?

# Answer
You don't have access to an id property. You need to use the id() method instead, e.g. `$userEntity->id()`. See the documentation for User.

For example:

```
echo "User " . $userEntity->id() . " saved!\n\n";

```
> 2 votes

---
Tags: 8, migrations, importing-exporting
---


# Question
Title: How do I fix the error given from "drush en nodejs -y"?
When I run `drush en nodejs -y`, I get this error.

> In PmCommands.php line 247: Unable to install modules nodejs due to missing modules nodejs.

How do I fix that error?

I am using Drush 10.2.2 and Composer 1.10.9.

# Answer
`drush en` doesn't download the modules anymore. That must be done with Composer.

```
composer require drupal/nodejs
drush en nodejs -y

```

If `composer require drupal/nodejs` was executed, the module could have been saved in the wrong directory, for example in the vendor directory.

In that case, I would check the *composer.json* file to verify it doesn't instruct Composer to install the modules in the wrong directory. In *composer.json* there should be a line similar to the following one, under *installer-paths*.

```
"modules/contrib/{$name}": ["type:drupal-module"]

```

If the \*composer.json file contains lines similar to the following lines, check that *web-root* is using the correct web root directory. (Some servers could use *public\_html* instead of *web*.)

```
"drupal-scaffold": {
    "locations": {
        "web-root": "web/"
    }
}

```
> 3 votes

---
Tags: drush
---