# Question
Title: Problem with text format and base64 inline images
I am using Drupal 8.6.13 text field with Full HTML text format for a field where we want to have content with inline base64 images (due to the synchronization with Gather Content).

Synchronization works well and images content is being saved in a Drupal text field but "data:" part is somehow filtered out - most probably by the Text format filter.

Long text field's source content:

```
<img alt="null" src="data:image/png;base64,...." />

```

becomes

```
<img alt="null" src="image/png;base64,...." />

```

after saving.

Limit allowed HTML tags and correct faulty HTML is active for Full HTML text format. We need this to limit heading types. If I disable "Limit allowed HTML tags" option then it works as expected.

Allowed tags are: `<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type class> <ol start type> <li class> <dl> <dt> <dd> <h4 id> <s> <sup> <sub> <img src data alt data-entity-type data-entity-uuid data-align data-caption> <table> <caption> <tbody> <thead> <tfoot> <th> <td> <tr> <hr> <p> <button>`

Any idea how to prevent `data:` from being filtered out of the `src` attribute of an image tag and keep "Limit allowed HTML tags" option active?

# Answer
> 1 votes
I believe I tracked this down. With the filter formatter you mentioned, it calls Xss::filter() on the text. Xss::filter() looks at the src attribute and will strip any protocol that have not been allowed. It sees anything XYZ: as a protocol, in this case data:. I added - data to my docroot/sites/default/services.yml filter\_protocols value and stopped the data: from being stripped. I don't know if this opens any other security concerns or not.

---
Tags: 8, input-formats
---


# Question
Title: How can I print a custom message for unpublished nodes upon submission?
Structure \> Content type \> ContentX \> Edit \> Unchecked published.

Structure \> People \> Permission \> Anonymous users have access to create node for Content Type "ContentX".

An anonymous user goes to the page ../node/add/geo and fill up the form and be able to save the unpublished node. However, the user goes not get any message stating that the node is saved. I like to display a message to the user that user is saved and it is pending approval.

Based on my research, I need Rules module.

Enabled the Rules module for D8.

In Rules module, I like to configure it to display a message to the user when the unpublished node is saved.

Here I am not sure what to enter:

How would I configure the settings in Rules module to display a message when a node from a specific content type is saved but not published?

# Answer
First, you should be using the current -dev version of Rules - that will make it much easier to do what you want.

Second, for the "Node is of type" condition, switch to the data selector for the first entry then select "node" from the data selector. This is the data object "node" that was created. For the content type, enter the machine id of the content type using the direct input mode (the default).
> 1 votes

---
Tags: 8, rules
---


# Question
Title: Load id of paragraph in hook_preprocess_layout using load($id) method
I want to load the paragraph id in theme preprocess.

```
function theme_preprocess_layout(&$variables) {
  $ID = $variables['content']['#paragraph'] ???; "contents - protected 'id' => array(1) = 68"
  $variables['attributes'] = new Drupal\Core\Template\Attribute($variables['attributes']);
  $variables['attributes']->addClass($ID);}

```

It gives me the available method "load($id)" but I don't know how to implement this method.

# Answer
> 0 votes
Thanks to Beau for the help. The method is simply

`$ID = $variables['content']['#paragraph']->id();`

---
Tags: theming, 8
---


# Question
Title: how cache a view that has a field that is calculated on the fly
```
in a view, one field has the following computed value, which takes a lot of time to return results from the database:

```

`<?php $term = taxonomy_term_load($data->tid); print count_term_get_node_count($term); ?>`

```
parent term 1: 23
parent term 2: 45

```

when one clicks on the parent term 1 link, she is seeing the count of nodes of the children taxonomy terms of parent term 1:

1st child of parent term 1: 33 2nd child of parent term 1: 89 and so on.

How could i cache the results of the whole view, i would like to run the count\_term\_get\_node\_count with a cron job, every night, and calculate the results for each term that exists in the vocabulary?

I tried with the views custom cache module, but i am not getting the results i need, something is not working well.

# Answer
> 0 votes
I solved a similar problem a little differently. You can create a computed field in the taxonomy and recalculate it either with a cron job for all terms, or after adding/editing a node for specific terms of this node. In the settings of the calculated field, select "stored in the database". And in the View display the value of this field, stored in database.

---
Tags: 7, views, caching
---


title: Print machine name in script

text:
# Question
Title: Print machine name in script
I want to include a tracking script in my Drupal website and inside the script I want to include things like, title of page, description etc. In other words, I need to include the machine names inside the script.

But how can I do that. I need to change the %TEXT% to %MACHINE\_NAME%. The script is here:

```
<script type="application/ld+json">
{
"@context": "http://schema.org",
"@type": "Event",
"location": {
"@type": "Place",
"name": "%company-name%",
"logo": "%logo-url%",
"alternateName": "Universe Science Park",
"address": {
"@type": "PostalAddress",
"streetAddress": "%address%",
"postalCode": "%postalcode%",
"addressLocality": "%city%",
"addressRegion": "%region%",
"addressCountry": "%country%"
}
},
"name": "%event-title%",
"description": "%event-descriptions%",
"startDate": "%startdate-and-time%",
"endDate": "%enddate-and-time%",
"image": [
"https://example.com/photos/1x1/photo.jpg",
"https://example.com/photos/4x3/photo.jpg",
"https://example.com/photos/16x9/photo.jpg"
],
"inLanguage": "%language%"
}
</script>

```

# Answer
> 1 votes
I found a solution for this. If somebody ever comes across same problem here is how you can manage it.

1. Download the module: https://www.drupal.org/project/tracking\_code
2. After installing and activating go to Structure -\> Tracking Code Snippets
3. Add tracking code
4. Here you can use tokens and add machine names

This is very simple and helped me alot!

I hope this will help you too. Cheers

# Answer
> 0 votes
You are going to want to put together a new custom module.

At first your only going to need a few files:

* modules/custom/tracking/
    + js/
        - tracking.js
    + tracking.info.yml
    + tracking.libraries.yml

Define a new library for your javascript to live in, and add dependencies for `core/jquery` and `core/drupalSettings`. You can then create the file and follow the Javascript API to implement access to `drupal.settings` to get some of these items from Drupal directly.

Your other variables can be derived by JS or you can write your own PHP in the future if you need to send a new setting and expose it to the library. At this point your enabled to do whatever you would like to do.

**Sources:**

---
Tags: 7
---


# Question
Title: Problems rendering radio buttons after an ajax call
I'm going to be linking this D.O issue.

I found that when changing an attribute other radio buttons would not update properly. Specifically that when there was two possible options for these buttons but each were only available if another option in the form was set, changing the first attribute resulted in the second one not being rendered as checked. If you went to add that item to the cart the proper variation would be added so this seems to be just visual.

I was able to use xdebug to find that the default value of the buttons is set correctly.
I've been digging through the rendering trace of this element but am getting quite lost.

Does anyone know why after an ajax call a radio button would be rendered incorrectly and not be marked as checked?

# Answer
A common mistake with AJAX is presuming that an AJAX callback is "aware" of the form's state. In reality, the form definition that AJAX works with is the original form instance. You will have to set the form values accordingly in your AJAX callback prior to returning the re-rendered markup. The `$form` argument that you receive in your callback function is always a cached copy of the *original* form.

So, to track what is going on in your form across AJAX callbacks, need to stash data server-side (e.g. in \Drupal::state()) or use the values stored in the `$form_state` argument. A good explanation of Drupal's form handling can be found here (thanks Jaypan!): https://www.drupal.org/forum/support/module-development-and-code-questions/2012-02-24/update-form-state-in-ajax-callback#comment-5648474
> 0 votes

---
Tags: 8
---


# Question
Title: How to set in my own module dependency on a composer package?
I have some of my own modules written on Drupal 7 that use composer packages. These composer packages are downloaded with the module Composer Manager.

How can I set explicitly the dependency on a composer package on my own module?

Should I describe it somehow on my module's .info-file or should I do it via a hook?

It's because if I don't do it, the Composer Manager can remove these packages from the site on the next updating. Unfortunately, I couldn't find any example.

# Answer
You can add dependencies in your module.

Navigate to your module's root folder. Run `composer init`. When you get to the question `Would you like to define your dependencies (require) interactively`, say yes. You can then declare any composer libraries your module depends on.

You can also manually create a `composer.json` file that looks something like this:

```
{
    "name": "uniquenamespace/packagename",
    "description": "This module does some stuff",
    "type": "drupal-module",
    "authors": [
        {
            "name": "Some Guy",
            "homepage": "https://www.example.com/some_guy",
            "role": "Maintainer"
        }
    ],
    "require": {
        "somevendor/somepackage": "^1.9"
    },
    "license": "GPL-2.0-or-later"
}

```

Run `composer validate` to check that your `composer.json` file is valid.
> 1 votes

---
Tags: 7, composer
---


title: Block user upon one webform submission

text:
# Question
Title: Block user upon one webform submission
I'm using Drupal 8, and I'm trying to block a user after submitting a certain webform. I have the below scenario:

* Only authenticated user can submit and use the webform
* Webform submission is limited to once per user
* Users can save drafts of the webform and continue filling it later on
* Rules action or otherwise should take place when the user has logged in

I tried to use Rules with rules\_webform module but to no avail. The actions in rules are all there, but I need a condition to detect if the user has submitted the webform completely, not saved it as a draft only.

Appreciate any help given

Thanks

# Answer
> 1 votes
The best way to do this is to write your own webform handler and attach it to the webform's `postSave()` method. You can also experiment and try another method if needed from `WebformHandlerBase`.

Webform handlers are not documented well, but they should be because they are quite useful.

**modules/custom/mymodule/src/Plugin/WebformHandler/MyModuleWebformHandler.php**

```
<?php

namespace Drupal\mymodule\Plugin\WebformHandler;

use Drupal\webform\Plugin\WebformHandlerBase;
use Drupal\webform\WebformSubmissionInterface;

/**
 * Webform example handler.
 *
 * @WebformHandler(
 *   id = "mymodule_webform_handler",
 *   label = @Translation("My Module Webform Handler"),
 *   category = @Translation("My Module"),
 *   description = @Translation("Block users after submission"),
 *   cardinality = \Drupal\webform\Plugin\WebformHandlerInterface::CARDINALITY_UNLIMITED,
 *   results = \Drupal\webform\Plugin\WebformHandlerInterface::RESULTS_PROCESSED,
 *   submission = \Drupal\webform\Plugin\WebformHandlerInterface::SUBMISSION_OPTIONAL,
 * )
 */

class MyModuleWebformHandler extends WebformHandlerBase
{

  /**
   * {@inheritdoc}
   */
  public function postSave(WebformSubmissionInterface $webformSubmission, $update = TRUE)
  {
    if (!$webformSubmission->isDraft()) {
      $webformSubmission->getOwner()->block();
    }
  }

}

```

After getting that setup, enable your new module (don't forget an info.yml file) and then edit your webform settings \> handlers to add this to that particular form.

---
Tags: 8, webforms
---


# Question
Title: Extract email addresses from composite field type using Computed Twig
I am using a custom composite field type to gather multiple pieces of data from a user. This custom composite field type contains a field which gathers the email addresses entered.

What I am trying to do is use a Computed Twig field to extract the email addresses entered within the custom composite field type and use an email handler to send a confirmation email to all those email addresses, however, I am having real trouble trying to reference a field inside a composite field type. I have used multiple pieces of twig code, but all I seem to be able to reference is the values from all the fields in the custom composite and not a specific field.

I am really new to twig, so do not understand the full capabilities yet, but does anyone know how to reference a field value inside a composite field type, using twig?

# Answer
I'll provide 2 links as an answer, but it has really been answered by others.

1. Write custom webform handler:

How to create a custom Webform handler to submit to a SOAP API

2. Use `hook_form_alter()` to add a custom submit handler.

Add a custom submission handler to a form

Drupal 8 version of `hook_form_alter()`: https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Form%21form.api.php/function/hook\_form\_alter/8.8.x
> 1 votes

---
Tags: theming, webforms, emails
---


# Question
Title: Out of the several possible ways, what is the best practice for getting a field value for an entity?
There are many different ways to get a field value from an entity. For example, a text field:

```
$entity->get('title')->value;
$entity->get('title')->get(0)->get('value')->getValue();
$entity->get('title')->get(0)->getValue()['value'];
$entity->get('title')->first()->get('value')->getValue();
$entity->get('title')->first()->getValue()['value'];

```

And so on.

And then, depending on the entity, for the entity title (or name or label), you can sometimes do...

```
$entity->getName();

```

What is the best practice? The first expression above (using the "value" property) is most concise -- are there any drawbacks to using it?

Is there any reason to prefer the first() helper instead of get(0)? Why does the first() helper exist? Are there cases when the first value in a field list doesn't have a 0 index?

# Answer
There's likely no fast rule-of-thumb here because you're describing a few different things that will greatly depend on context (what entity, what fields?).

First, let's disregard `::getName()`; it's a Taxonomy specific-wrapper around `::label()` (whatever the entity has defined as its readable label in the entity definition it will return it). There's no such things a multiple labels so let's get to the main question being asked here: best practice on pulling field values.

It really depends on what you're trying to do.

If you look into `::get()` method on the entity, you'll see its basically pulling data out of the internal `$fields` property which is is an array of objects that implement `FieldItemListInterface`. Classes that implement the interface supply the various `get()`, `first()`, `getValue()` methods that you can use when you're dealing with fields that may have one, none, or many values. They do extra overhead of checking for the existence of data or making sure it exists.

If you know for sure the data exists in your class you can skip the chaining of methods/properties and just get to the directly (e.g. `$node->title->value`), if not, these methods exists when you're dealing with uncertain structure in your entity/fields. Whatever the case, it depends on the *specific entity class* and the *specific implementation of* `FieldItemListInterface` to really decide what is best.
> 2 votes

---
Tags: entities
---


# Question
Title: PDOException: SQLSTATE[HY000] [2002] No such file or directory in lock_may_be_available() (line 167
I have Drupal 7.2 application site and have upgraded from php 5.6 to php 7.2 since it reached its eol, also upgraded to rhel 8 from rhel 6.5, but while connecting to rds I am getting following error, however while trying to connect to db locally, was able to connect successfully. What does this mean and How can I solve this

# Answer
*lock\_may\_be\_available()* is the first call that Drupal makes, in order to try a connection with the database. Then, the first idea seems that is not unable connect to your database so your mysql isn't running well or better, your Drupal can't connect to your MySQL server. Ok.

**The next step is think'bout the reasons:**

1- In some cases, it's just due to use "localhost" instead of 127.0.0.1 in your settings.php or local.settings.php file. Review this in the database config block:

```
$databases['default']['default'] = [
  'database' => 'db_name',
  'username' => 'db_user',
  'password' => 'db_password',
  'host' => '127.0.0.1',
  'driver' => 'mysql',
  'port' => 3306,
  'prefix' => "",
 ];

```

In some cases using Containers (Docker, DDEV, etc) may be necessary change as: `'host' => 'localhost',` to `'host' => 'mariadb',`.

2- Maybe the MySQL daemon is crashing. Stop the daemon and restart it. You can use: `/etc/init.d/mysqld restart` or: `service mysqld restart` // `service mysql restart`.

3- If you're using some special kind of configuration as multisite, multidomain, etc, then get some of log info from your system, using: `tail -f /var/log/mysql/error.log`
> 3 votes

---
Tags: database
---


# Question
Title: I want to call ajax callback function from another module's controller in Drupal 8. is it possible?
I am giving the code of my main form and the controller of another module where the ajax callback is written. Actually this ajax callback function will be used in different forms. So I want to create a common module to call this function. Kindly help. This is the form field of main form:

```
    $form['construction_type'] = array(
  '#type' => 'radios',
  '#title' => $this->t('1. Whether Boiler or Economiser:'),
  '#default_value' => 'b',
  '#options' => array('b' => $this->t('Boiler'), 'e' => $this->t('Economizer')),
  '#ajax' => [
                  'callback' => '\Drupal\common_functions\Controller\CommonFunctionsController :: ajax_load_boiler_size',

               ],
);

```

And this is the ajax callback function in controller of another module

```
    class CommonFunctionsController extends ControllerBase{  public function ajax_load_boiler_size(array &$form, FormStateInterface $form_state){
$response = new AjaxResponse();
$response->addCommand(new ReplaceCommand('#boiler_size_div', $form['boiler_size']));
return $response;

```

}

The error message is Symfony\Component\HttpKernel\Exception\HttpException: The specified #ajax callback is empty or not callable.

# Answer
If anything, this should be:

```
'callback' => ['\Drupal\common_functions\Controller\CommonFunctionsController', 'ajax_load_boiler_size'],

```

That's only if it's going to work though. No guarantees.
> 3 votes

---
Tags: ajax
---


title: How add Ckeditor on EntityType field

text:
# Question
Title: How add Ckeditor on EntityType field
I make a custom Entity content module, and for a field i want ckEditor.

Here my code for this field :

```
$fields['description'] = BaseFieldDefinition::create('string_long')
    ->setLabel(t('Description'))
    ->setRequired(true)
    ->setDisplayOptions('view', array(
        'label' => 'above',
        'type' => 'string_textarea',
        'weight' => -4,

    ))
    ->setDisplayOptions('form', array(
        'type' => 'string_textarea',
        'weight' => -4,
        'settings' => array(
            'placeholder' => 'Description de votre annonce',
        ),
    ))
    ->setDisplayConfigurable('view', TRUE)
    ->setDisplayConfigurable('form', TRUE);

```

> Type : string\_textarea

How can i tell Drupal 8 to get the widget for textarea basic html mode ?

# Answer
> 5 votes
There are 4 different field types that contain a text

* string
* string\_long
* text
* text\_long

What long means should be obvious, the difference between string and text is that text has a format (like full\_html or basic\_html) while string is always plain text.

You are currently using string\_long, it doesn't make sense to show a wyswiwyg editor for that, since it would always escape all HTML. Use text\_long instead.

# Answer
> 0 votes
Piggybacking on @Berdir's answer, add this to your settings array:

`'allowed_formats' => ['ckeditor' => TRUE]`

---
Tags: 8, entities
---


# Question
Title: how to create custom template for a product type in drupal 8 commerce
I want to have separate twig templates for each product type in drupal 8 commerce to customize them.

is there any way to do that?

# Answer
> 1 votes
by default drupal 8 uses `commerce-product.html.twig`. you can find it from `modules/contrib/commerce/modules/product/templates/` and copy and paste it to your `template` folder in your theme.

and to create separate template file for each product type you can add `--product type` at the end of file name. for example if you have `shoes` product type then you should create a file `commerce-product--shoes.html.twig`.

then drupal uses the new template for shoes product type only.

---
Tags: 8, theming, commerce
---


# Question
Title: How to get bundle machine name when viewing the creation form?
From the creation form (any bundle), I need to get the content type label/name to use it in a link (in a block, for example) and pre-filter the View opened by this link.

Example :

The user wants to add a new Article and opens the Article creation form.

In a special block (let's say in Highlighted region), a link is displayed : "Clone existing".

This link leads to a View listing the existing Articles with a clone button for each.

This View can be filtered by content type.

So the link would be something like :

```
<a href"/existing-nodes?type=[node:content-type]">Clone existing</a>

```

How to get the \[node:content-type\] (just here to clarify, good old token) from the form?

Any tip would be nice.

# Answer
Writing your own question is always useful.

I may have a solution, even if you may have something different, so please share.

I have built a Views block with a custom text (my link) as unique field (only show one result, you do not need to repeat the link, of course).

I have added a contextual filter based on content type with a default value provided by url, raw value, third component (node/add/mybundle\_name), with case transform in lower case.

My link looks like :

```
<a href="/existing-nodes?type={{ raw_arguments.type }}">Clone existing</a>

```

And it works.
> 0 votes

---
Tags: views, 8, forms
---


title: Local Drupal issue

text:
# Question
Title: Local Drupal issue
Receiving the following on my local machine. Any suggestions?

Warning: include(/app/drupal/vendor/composer/../../docroot/core/lib/Drupal/Core/DrupalKernel.php): failed to open stream: No such file or directory in /app/drupal/vendor/composer/ClassLoader.php on line 444 Call Stack: 0.2061 409760 1. {main}() /app/drupal/docroot/index.php:0 0.2147 533832 2. spl\_autoload\_call() /app/drupal/docroot/index.php:16 0.2147 533888 3. Composer\Autoload\ClassLoader-\>loadClass() /app/drupal/docroot/index.php:16 0.2147 533888 4. Composer\Autoload\includeFile() /app/drupal/vendor/composer/ClassLoader.php:322 Warning: include(): Failed opening '/app/drupal/vendor/composer/../../docroot/core/lib/Drupal/Core/DrupalKernel.php' for inclusion (include\_path='/app/drupal/vendor/pear/archive\_tar:/app/drupal/vendor/pear/console\_getopt:/app/drupal/vendor/pear/pear-core-minimal/src:/app/drupal/vendor/pear/pear\_exception:.:/usr/local/lib/php') in /app/drupal/vendor/composer/ClassLoader.php on line 444 Call Stack: 0.2061 409760 1. {main}() /app/drupal/docroot/index.php:0 0.2147 533832 2. spl\_autoload\_call() /app/drupal/docroot/index.php:16 0.2147 533888 3. Composer\Autoload\ClassLoader-\>loadClass() /app/drupal/docroot/index.php:16 0.2147 533888 4. Composer\Autoload\includeFile() /app/drupal/vendor/composer/ClassLoader.php:322 Fatal error: Uncaught Error: Class 'Drupal\Core\DrupalKernel' not found in /app/drupal/docroot/index.php on line 16 Error: Class 'Drupal\Core\DrupalKernel' not found in /app/drupal/docroot/index.php on line 16 Call Stack: 0.2061 409760 1. {main}() /app/drupal/docroot/index.php:0

# Answer
> 1 votes
**UPDATE**

The site settings are located on `web/sites/default/settings.php` or `docroot/sites/default/settings.php` depending on your project scaffolding. The most important setting on this file is the database connection.

```
/**
 * Database configuration.
 */
$databases = [
  'default' =>
    [
      'default' =>
        [
          'database' => 'DB_NAME',
          'username' => 'DB_USERNAME',
          'password' => 'DB_APSSWORD',
          'host' => 'DB_HOST',
          'port' => 'DB_PORT',
          'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql',
          'driver' => 'mysql',
          'prefix' => '',
        ],
    ],
];

```

To check all the settings see the repo.

For more information on how to setup your environment and install Drupal check the official documentation.

**ORIGINAL ANSWER**

I'm going to venture myself and state that you have one of the following problems:

1. You haven't run `composer install` at your codebase root (from your post should be */app/drupal/*) or has exited with an error code.
2. You (I mean your webserver process) don't have permission to access either `core` directory or any of it's content. This can happen if yo run `composer install` as root (don't do sudo!).
3. And last but not least check your VM (you just tagged this as VM related) shared folders, meaning that if you don't map your entire codebase from your local to the VM you can found yourself with missing files errors like the one posted.

For future questions you need to come up with more than just pasting your terminal output. Happy coding!

---
Tags: virtual-machine
---


title: title in page.html.twig

text:
# Question
Title: title in page.html.twig
I find out `title` is not exists in `page.html.twig`, and there is a file `page-title.html.twig` to theme title, then in block section we should use block title. ( \** I don't want use this strange structure for title,really I didn't find out why Drupal do this !!!).

any way I want to provide it by myself, maybe `hook_preprocess_page` is solution,any body know how can I add `title` variable to page variables ?

# Answer
This is my solution for adding `title` to page templates. in `yourthemename.theme` add

```
function hook_preprocess_page(&$vars) {
    if(isset($vars['node'])) {
        $vars['title'] = $vars['node']->title->value;
    }
    else{
       $vars['title'] =$vars['page']['#title'];
    }
 }

```

then `title` exist in page templates.
> 0 votes

# Answer
You can get it with:

```
{% if node.label %}
  {{ node.label }}
{% else %}
  {{ page['#title'] }}
{% endif %}

```
> 0 votes

---
Tags: 8, theming
---


# Question
Title: How can I change the 'type' of Drupal script tags from text/javascript to text/plain?
I could use some assistance in figuring out a way to change script tags for a Drupal 7 site to be `"text/plain"` instead of `"text/javascript"` And additional attribute(s). I can tell already that script injection is something that is all over the board when it comes to implementation. Facebook 'pixel' for example is injecting script code outside of the `drupal_add_js` mechanism. And there are plenty of script tags added in templates (which are simple to change as necessary) but I would like to come up with a solution for the actual Drupal injected scripts, and nothing I’ve tried has worked yet.

## Reasoning

This change is required by the service provided via https://eucookie.eu/ for handling GDPR related functionality. I technically only need to change scripts that are adding cookies.

## What I've tried without success

**Implementing hook\_js\_alter() in a module**

This provides access to some script information, but I don't see a way to change the script `type` attribute at all. I can however add attributes, just not alter the `type` as necessary.

**Implementing phptemplate\_preprocess\_html\_tag**

```
mytheme_preprocess_html_tag(&$variables) {
  if ($variables['element']['#tag'] == 'script') {
    $variables['element']['#attributes']['type'] = 'text/plain';
    $variables['element']['#attributes']['data-cookie-if'] = 'analytical';
  }
}

```

The additional attribute of `data-cookie-if` IS being added as expected. But the type is not being changed, in fact, there is no type at all in the resulting markup.

## Additional complications

Script Aggregation if enabled will add another twist, and I'm fairly certain I will need to disable this, but still wanted to mention in case there is a preferred workaround.

# Answer
> 1 votes
I solved, mostly by implementing the `theme_process_html_tag` method in the template. Note the **PROCESS** not **PRE**process.

In my case, the theme was extending the 'zen' theme, which was actually removing all 'type' attributes from scripts in the header (I don't know exactly why). But I was able to overwrite this behavior in the theme hook mentioned above.

---
Tags: 7, javascript, cookies
---


# Question
Title: Get the values of a checkbox field in node when evaluating in block condition plugin
I have a field that is a checkbox with multiple values... I'm trying to do an evaluation in a condition plugin where I'm checking against the checked values. For a boolean field, the syntax is:

```
$value = $this->getContextValue('node')->field_news_filter_menus->value;

```

Since field\_news\_filter\_menus is a checkboxes field, I need to get the array of checked values. These all return null:

```
$this->getContextValue('node')->field_news_filter_menus->list->values;
$this->getContextValue('node')->field_news_filter_menus->values;
$this->getContextValue('node')->field_news_filter_menus->list;

```

PHPstorm xdebug says the path is

```
$this->getContextValue('node')->field_news_filter_menus->list[0]->value;

```

But null is returned for that also. I can see the values in xdebug that follow a path as

```
$this->getContextValue('node')->field_news_filter_menus->list[0]->values

```

and see an expected value, but that path also returns null.

What is the syntax for getting that array list?

# Answer
> 1 votes
**Original Answer**

The proper way to access field values is `$entity->get('my_field')->getValue()` in your case this will return an array like:

```
Array(
  0 => Array(
    'value' => 'value1'
  ),
  1 => Array(
    'value' => 'value2'
  ),
  2 => Array(
    'value' => 'value3'
  ),
  // And so on as many values your field has.
)

```

Also you can check if the field is empty by calling `$entity->get('my_field')->isEmpty()`. Calling only your field like `$entity->get('my_field')` will **NOT** return the field value but the field instance. Calling for an unexisting field will throw a runtime exception.

**UPDATE**

To check if the value exists you can either iterate over the array or do something like this

```
$arr = [
  0 => [
    'value' => 'value1',
  ],
  1 => [
    'value' => 'value2',
  ],
  2 => [
    'value' => 'value3',
  ],
];

$arr = array_map(function ($v) {
  return $v['value'];
}, $arr);

assert(in_array('value2', $arr, TRUE), 'Assert that contains value2');
assert(in_array('other', $arr, TRUE), 'Assert that contains other. This will fail');

```

You can try it here.

---
Tags: plugins
---


# Question
Title: Why do D7 form builders have a $form parameter?
In Drupal 7, the first parameter of a form builder function is $form.

When implementing a form builder function, one has the option of either adding stuff to this existing array and returning it, or creating a new $form array, discarding what was passed into the function. In my experience, both of these options usually work fine.

What's the point? Is there ever a case when this parameter is needed?

I think to vaguely remember that this is somehow relevant when a form is rebuilt, so this would be the old form.. but I would not really know what to do with it.

*Note: The same question already exists for Drupal 8, but it seems to be a policy here that we need distinct questions for D7 and D8. I was going to answer it for D7, and I realize it would be off-topic in the D8 question.*

# Answer
> 2 votes
At least for D7 there is one scenario where the passed-in $form parameter does make a difference.

With `hook_forms()`, it is possible to define a `'wrapper_callback'` for a form definition for a specific form id.

From the hook documentation https://git.drupalcode.org/project/drupal/-/blob/7.69/modules/system/system.api.php#L1821:

```
/**
 [..]
 *   - wrapper_callback: (optional) Any callable to invoke before the form
 *     builder defined in 'callback' is invoked. This wrapper callback may
 *     prepopulate the $form array with form elements, which will then be
 *     already contained in the $form that is passed on to the form builder
 *     defined in 'callback'. For example, a wrapper callback could setup
 *     wizard-like form buttons that are the same for a variety of forms that
 *     belong to the wizard, which all share the same wrapper callback.
 */

```

So if a wrapper callback was defined, then the $form array passed as a parameter may already be prepopulated.

I found some contrib modules where this is being used (simply grep for 'wrapper\_callback'):

* ctools does not set the wrapper callback in `hook_forms()`, instead it sets `$form_state['wrapper_callback']` directly.
* entity uses hook\_forms() to set a wrapper callback to a form definition, but also sets it in `$form_state['wrapper_callback']` directly.
* media\_bulk\_upload does use `hook_forms()` to specify a wrapper callback.

---
Tags: 7, forms
---


# Question
Title: Set parent for programmatically created menu links
I'm currently programmatically generating a menu and menu links inside it. This is working but I'm having trouble setting the parents for the links. Here is my current code

```
    $langcode = 'es';
    if (empty($menu_id)) {return ['#markup' => 'menu not specified'];}
    $menu = \Drupal::entityTypeManager()->getStorage('menu')
        ->create([
            'langcode' => $langcode,
            'status' => TRUE,
            'label' => $menu_id,
            'id' => $menu_id,
            'description' => 'this menu was created programmatically',
        ])->save();

    $items = [
        '1' => 'item 1',
        '2' => 'item 2',
        '3' => 'item 3',
    ];
    $parent = NULL;
    foreach ($items as $id => $title) {
        if ($id == '2') {$parent = '1';} else { $parent == NULL;}
        $menu_link = MenuLinkContent::create([
            'title' => $title,
            'link' => ['uri' => 'internal:/node/' . $nid],
            'menu_name' => $menu_id,
            'expanded' => true,
            'langcode' => $langcode,
            'status' => TRUE,
            'parent' => $parent,
        ]);

        $menu_link->save();
        var_dump($menu_link->getParentId());
    }

```

This line

```
var_dump($menu_link->getParentId());

```

is actually displaying the correct parent. But when visiting the menu in the admin page the parent is not set and is at the top level of the menu. Is there a specific issue with my code? How would you set the parent of a programmatically generated menu link?

# Answer
Turns out when setting the parent you don't use the id of the menu\_link\_content you use the 'menu\_link\_content:' followed by the uuid of the link. Here's an example of a properly formatted parent field.

> 'menu\_link\_content:0025c274-4db3-4745-b51e-714c998b5a50'

I fixed the code like so

```
    foreach ($items as $id => $title) {
        if ($id == '2') {
            $parent = 'menu_link_content:' . $previous_menu_link->uuid();
        } else {
            $parent == NULL;
        }

        $menu_link = MenuLinkContent::create([
            'title' => $title,
            'link' => ['uri' => 'internal:/node/' . $nid],
            'menu_name' => $menu_id,
            'expanded' => true,
            'langcode' => $langcode,
            'status' => TRUE,
            'parent' => $parent,
        ]);

        $menu_link->save();
        $previous_menu_link = $menu_link;
    }

```
> 6 votes

# Answer
This is what I ended up using.

```
use Drupal\menu_link_content\Entity\MenuLinkContent;

\Drupal::entityTypeManager()
  ->getStorage('menu')
  ->create([
    'id' => 'test-menu',
    'label' => 'Test menu',
    'description' => 'Description text.',
  ])
  ->save();

$menu_link_1 = MenuLinkContent::create([
  'title' => 'Link 1',
  'link' => ['uri' => 'internal:/foo'],
  'menu_name' => 'test-menu',
  'expanded' => TRUE,
]);
$menu_link_1->save();

$menu_link_2 = MenuLinkContent::create([
  'title' => 'Link 2',
  'link' => ['uri' => 'internal:/bar'],
  'menu_name' => 'test-menu',
  'expanded' => TRUE,
  'parent' => $menu_link_1->getPluginId(),
]);
$menu_link_2->save();

```
> 2 votes

---
Tags: 8, entities, navigation
---


title: How to alter entity field in drupal 8?

text:
# Question
Title: How to alter entity field in drupal 8?
I add field (select list) in Group form (form\_id = 'group\_drupalcamps\_add\_form') with UI. Now i want to alter/target that field and pass variable to it. Any idea will be appreciated. Thanks in advance.

# Answer
> 0 votes
```
/**
 * Manage alters for group_drupalcamps_add_form
 *
 * @param $form
 * @param $form_state
 * @param $form_id
 */
function mymodule_form_alter(&$form, $form_state, $form_id) {
  if ($form_id === 'group_drupalcamps_add_form') {
    // alter the field within the $form array
  }
}

```

---
Tags: hooks
---


# Question
Title: Modify data structure of a view REST export
I need to build a sitemap.xml with a REST export with a view. By default I get this data structure with a REST export.

```
<?xml version="1.0"?>
  <response>
    <item key="0">
      <field1>...

```

How can I modify the tags of this structure? I need to get this tags.

```
<?xml version="1.0" encoding="UTF-8"?>
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    <url>
      <loc>...

```

These tags must be changed.

```
<response> > <urlset...
<item> > <url>

```

Do I write a custom serializer for the REST export or a specific encoder? How do I proceed?

# Answer
In D8 you need to create custom serializer and encoder to alter the XML tags.

1. Create custom serializer Ref : https://gist.github.com/BERRAMOU/dd21e009eec0e3d98733e78360122892
2. Write custom encoder

-

```
namespace Drupal\custom_serializer;
use Drupal\serialization\Encoder\XmlEncoder as SerializationXMLEncoder;
use SimpleXMLElement;
 /**
 * Encodes xml API data.
 *
 * @internal
 */
class myXMLEncoder extends SerializationXMLEncoder {

  /**
   * The formats that this Encoder supports.
   *
   * @var string
   */
  protected static $format = ['myxml'];

  /**
   * {@inheritdoc}
   */
  public function getBaseEncoder() {
    $base_encoder = parent::getBaseEncoder();
    $base_encoder->setRootNodeName('jobs');
    return $base_encoder;
  }
}

```
3. Then in View under 'FORMAT' you will see both options.
4. After save, you should get XML with required tag.
> -1 votes

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


title: Retrieving the product published field

text:
# Question
Title: Retrieving the product published field
Within a node template I can retrieve the status of the published flag by calling the code: `node.isPublished()`

How do I retrieve the status of the published flag within a commerce product...

I've tried:

```
product.isPublished()
product.status
product.published

```

But I can't seem to get it to print. I've tried to look through the core code where status seems to be the key, but no luck, plus api.drupalcommerce.org is down.

Any advice?

Thanks in advance

# Answer
`{% if product_entity.status.value == 0 %}
 {%
 set classes = [
 'is-unpublished'
 ]
 %}
{% endif %}
<article{{ attributes.addClass(classes) }}>
..
</article>`
> 1 votes

---
Tags: 8, nodes, commerce
---


title: How to disable watchdog table entry?

text:
# Question
Title: How to disable watchdog table entry?
My drupal 8 watchdog entry running very vastly. I want to stop the watchdog entry for few times immediate basis and resolve the issues. Please suggest.

# Answer
The Database Logging (`dblog`) is responsible for logging to the Watchdog table. Uninstall the module and it will remove the table and logging to it.
> 1 votes

---
Tags: 8, log-messages
---


title: ckeditor installation

text:
# Question
Title: ckeditor installation
I went to the ckeditor website made a custom build of an ckeditor with the plugins I want, then downloaded and extracted it to

```
sites/all/libraries/ckeditor

```

(I previously installed the libraries and ckeditor modules)

then I went to the global configurations settings for ckeditor

admin/config/content/ckeditor/editg

For the "\> Path to CKEditor * " value I entered

```
%l/ckeditor

```

which sets the location of ckeditor to

```
/sites/all/libraries/ckeditor

```

and I set the " Path to the CKEditor plugins directory " value to

```
%l/ckeditor/plugins

```

which sets the location of the plugins to

```
/sites/all/libraries/ckeditor/plugins

```

I save the configuration and flush all caches. But whenever I go to edit a piece of content there is NO editor and NO body field at all on the page.

I can re-enter the default url used to install ckeditor and the editor will appear on the page, but of course, without my plugins. I have no error messages. Anyone have any idea why ckeditor can't find itself?

thanks

# Answer
> 1 votes
Just a quick answer to my question for other drupal noobies making the same error as I have. There seems to be a bit debate between installing stand alone ckeditor or installing ckeditor via durpal's wysiwyg module (convenient for installing multiple wysiwyg editors, if need that level of choice). For more info read this.

At the risk of drawing wrath, with drupal, "more modules" = "more chances for errors and conflicts" so I am go with the stand alone ckeditor install.

First install the ckeditor drupal module, from the drupal project page.

Then, download the BASIC ckeditor script library from ckeditor.com .

NOTE: DO NOT ATTEMPT TO ADD PLUGINS AT THIS POINT, JUST DOWNLOAD THE PLAIN, VANILLA, CLASSIC DOWNLOAD. This is what caused the error above, some plugins do NOT "play nicely" with drupal.

After you have the download of the plain ckeditor script library downloaded, place into the /sites/all/libraries/ directory, so that you end up with /sites/all/libraries/ckeditor/ckeditor.js file structure.

Then in your drupal site go to /admin/config/content/ckeditor and edit the "Global Settings" , so that the "Path to ckeditor" is %l/ckeditor (sites/all/libraries/ckeditor) and the "Path to plugins" is %m/plugins (sites/all/modules/plugins)

NOTE: By default ckeditor points to a web address (CDN) for the "path to editor" value, which is mighty inconvenient if you work offline on a local dev server without internet from time to time.

Save Configurations

Assuming your install was successful, meaning that you did not have any red text drupal messages at /admin/config/content/ckeditor page complaining about missing libraries, you can go to /node/add/page and see if your locally installed ckeditor is working properly. If it loads, you are good to go.

Now for the fun part, adding all those plugins that made your mouth water, but you couldn't add before you downloaded the ckeditor script library.

Go to ckeditor.com make a list of all the plugins that you desire for your local, stand alone editor, then download each plugin individually.

Extract each plugin that you downloaded to your sites/all/modules/ckeditor/plugins folder.

NOTE: DO NOT extract the plugins folder located in the drupal sites/all/libraries/ckeditor/plugins folder... all your neat plugins and hours of work will be wiped out when you update ckeditor in the future.

After all your plugins are extracted, edit your "ckeditor.config.js" file located at sites/all/modules/ckeditor.

look for "config.extraPlugins = '';" and enter the name of your plugin. For more than plugin, separate each plugin with a " , " .

For more details on plugin installation READ THE DOCUMENTATION HERE.

I highly recommend, adding plugins one at a time and testing in your browser, so that you can easily remove the mean plugin that won't play nice with others.

At this point go back to /admin/config/content/ckeditor and edit INPUT FORMAT that you want to use with ckeditor. Look under "Editor Appearance" and scroll down, your added plugins should appear as checkbox options. Select the ones you want...then get back to "drupaling" :-)

# Answer
> 0 votes
Great tutorial! My only addition would be: After you enabled the module and set the global settings as described, you have to turn on the CKEditor for the Advanced Text Format configuration here: `/admin/config/content/ckeditor/edit/Advanced` (under Basic Setup)

---
Tags: wysiwyg
---


# Question
Title: How do I conditionally add a class to my page title within page-title.html.twig?
I have a boolean field within my Page content type called *Hide Title*.

I need to conditionally add a class to h1's to visually hide them if this field is checked.

I have the following within *page-title.html.twig*

```
{% set hide_title = content.field_hide_title.0 %}

{% if 'On' in hide_title %}
    <h1 {{ title_attributes.addClass('visually-hidden') }}>{{ title }}</h1>
{% else %}
  <h1{{ title_attributes }}>{{ title }}</h1>
{% endif %}

```

This is is not adding the class while the boolean is set on or off.

# Answer
> 1 votes
In `page-title.html.twig` you don't have access to `content` variable.
according to `page-title.html.twig` comment section:

Available variables are:

* title\_attributes
* title\_prefix
* title
* title\_suffix

So you should implement `hook_preprocess_HOOK` to passe `hide_title` to your twig like the following:

```
/**
 * Implements hook_preprocess_HOOK().
 */
function MYMODULE_preprocess_page_title(&$variables) {
  if ($node = \Drupal::routeMatch()->getParameter('node')) {
    $variables['hide_title'] = $node->field_hide_title->value;
  }
}

```

Now you can do the following in your twig:

```
{% if hide_title == "1" %}
    <h1 {{ title_attributes.addClass('visually-hidden') }}>{{ title }}</h1>
  {% else %}
    <h1{{ title_attributes }}>{{ title }}</h1>
{% endif %}

```

---
Tags: 8, theming, nodes
---


# Question
Title: What's the best way to add an element to a form AFTER submission?
I have a form that I'm trying to build that is semantically identical to billions of others on the net:

1. First GET request returns an empty form with no results displayed.
2. Submitting the form POSTs all data to the same page.
3. Server collects information from the database/other service based upon the submitted data.
4. Final response contains the same form with the fields still filled out, and a results table at the bottom.

But implementing this classic workflow seems to be impossible in Drupal 8.

I have been reading outdated documentation on the D7 Form API for what seems like forever, and have decided that it's probably best for me to implement a form that submits via AJAX and adds the results table to the page dynamically. But I'd like to know whether this would be the best approach, as well as other methods of doing what I want whether they're "better" or not.

I'd love to give you guys an idea of what I've tried, but the appropriate project files, as well as my brain, are so scrambled right now that I can't figure out what to show. So just some general pointers in the right direction would be very much appreciated.

# Answer
Expanding that comment into a minimalist example code answer:

```
public function submitForm(array &$form, FormStateInterface $form_state) {

  // get the submitted data
  $foo = $form_state->getValue('foo');

  // call any other service to process your form submission values
  // (you can replace this with dependency injection of course)
  $bar = \Drupal::service('other.service')->baz($foo);

  // add results element to the $form

  $form['results'] = [
    '#type' => 'table', // or markup, or whatever you want to build your results
     // could also do #theme for a custom theme hook.
     // Use $foo, $bar etc however you want here
  ];

  // rebuild the form with your newly added results element,
  // and prevent drupal from redirecting to a separate GET page,
  // so that the populated form remains visible above the results.
  $form_state->setRebuild(TRUE);
  $form_state->disableRedirect(TRUE);
}

```
> 0 votes

---
Tags: forms
---


# Question
Title: How do I make a custom module trigger a function when content is unpublished?
**In short:**

Utilizing Drupal 8, I'm hoping to to "hook" into an entity update event for a specific content type so that I can then take further action when that event occurs. In this case, delete the node if it's unpublished.

**In long:**

The Scheduler module allows scheduling of nodes to publish and unpublish as scheduled. My goal is to then delete a specific-type node when it gets unpublished.

At first I tried to use the Rules module to trigger removal of the unpublished node. However, Rules is not working properly on my website currently (getting errors when attempting to configure unpublish event node type). It spurs interest to see how I can accomplish the same task with a small custom module. I'm moving forward with a goal to understand Drupal 8 modules and module development better.

As a result, I wish to move handling of unpublish events for my content type into module code. Other examples I'm finding relate to Drupal 7. I'm building a Drupal 8 module.

Here is my starting module code so far (**rough, draft, untested** as I work out logistics).

Module structure:

```
hour_helper
  hour_helper.info.yml
  hour_helper.services.yml
  src
    EventSubscriber
      HourUnpublishEventSubscriber.php

```

hour\_helper.info.yml:

```
name: Hours of Operation Helper
type: module
description: 'Utilities to assist with hours of operation, starting with the removal of hour content once it is unpublished via the scheduler module.'
package: Custom
core: 8.x

```

hour\_helper.services.yml:

```
services:
  hour_helper.node_subscriber:
    class: Drupal\hour_helper\EventSubscriber
    arguments: ['@entity.manager']
    tags:
      - { name: event_subscriber }

```

HourUnpublishEventSubscriber.php:

```
<?php

/**
 * @file
 * Event subscriber
 */

namespace Drupal\hour_helper\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;

//!!! use (... something entity or node related, we want to subscribe to entity updates?)

/**
 * Remove hour of opperation nodes after/when they are scheduled to unsubscribe to prevent garbage accumulation
 */
class HourUnpublishSubscriber implements EventSubscriberInterface  {
  /**
   * {@inheritdoc}
   */
  public function getSubscribedEvents() {

    // Subscribe to node update events with priority 0.
    //!!! which event....
    $events[SomethingEvents::SOMETHING][] = array('checkForRemoveStatus');
    return $events;
  }

  /**
   * This method is called whenever the ????? event is dispatched.
   *
   * @param GetResponseEvent $event
   */
  public function checkForRemoveStatus(GetResponseEvent $event) {

    //print '<pre>';
    //print_r($event);
    //print '</pre>';

    die('Well, at least an event has been triggered to be handled here...');

  }

}

```

I continue to ponder the Drupal events doc page...

# Answer
Based on this: **My goal is to then delete a specific-type node when it gets unpublished.**

You can use hook\_ENTITY\_TYPE\_update.

You will have to just check, if the (not the original) entity is unpublished, and the it's type is the one you are looking for.

+1: From OO perspective you can also take a look on events.
> 1 votes

# Answer
This is quite easy to do with Rules in Drupal 8, which is what you originally asked ... It took me less than a minute to write and test the Rule below.

Rules actually automatically generates events for all Entity CRUD operations (for all entities of any type, even custom ones) and allows you to react on those events in order to conditionally perform actions. If you want to enable Rules just to make these events available so you can react to them with custom code you can do that. But it far more convenient to use Rules for the entire task, because Rules allows you to easily prototype and modify the operation both initially and in the future when you decide you want to change some detail of your task. Just as Views makes it easier to compose and display the results of a complex DB query without having to know a lot about the implementation details, Rules allows you to add and customize workflow.

Here's a complete example that accomplishes what you asked for. I chose to use the content type "Basic page" as that is pre-installed with Drupal so the Rule below will work on any site. Just change that to be the specific type (or types, even) that you're interested in. This example will DELETE any basic page that was updated if (and only if) that update changed the status of the page from published to unpublished. It also displays a message informing the user of what happened.

As I said, you can modify this, add additional conditions, add additional actions, alter the type that triggers the Rule, etc. to suit your needs.

```
langcode: en
status: true
dependencies: {  }
id: delete_content_of_type_basic_page_when_it_it_unpublished
label: 'Delete content of type "Basic page" when it it unpublished'
events:
  -
    event_name: 'rules_entity_update:node--page'
description: 'For https://drupal.stackexchange.com/questions/203680/how-do-i-make-a-custom-module-trigger-a-function-when-content-is-unpublished?rq=1'
tags: {  }
weight: 0
config_version: '3'
expression:
  id: rules_rule
  uuid: 4d5f5940-5d74-4402-9391-b2d0261f0194
  weight: 0
  conditions:
    id: rules_and
    uuid: 5c39832c-eed4-4ec7-b5c7-36f29879e22b
    weight: 0
    conditions:
      -
        id: rules_condition
        uuid: 7152f83f-5481-41bf-8f20-c8f3f0d89c48
        weight: 0
        context_values: {  }
        context_mapping:
          node: node_unchanged
        context_processors: {  }
        provides_mapping: {  }
        condition_id: rules_node_is_published
        negate: false
      -
        id: rules_condition
        uuid: 1694c6b6-96f7-4eb6-9026-3e7140bb2be9
        weight: 0
        context_values: {  }
        context_mapping:
          node: node
        context_processors: {  }
        provides_mapping: {  }
        condition_id: rules_node_is_published
        negate: true
  actions:
    id: rules_action_set
    uuid: d9e46518-c5e6-4839-8de8-3175b184a6fd
    weight: 0
    actions:
      -
        id: rules_action
        uuid: 8afb621c-3518-44bd-8a31-e1c2f7b0bc5b
        weight: 0
        context_values: {  }
        context_mapping:
          entity: node
        context_processors: {  }
        provides_mapping: {  }
        action_id: rules_entity_delete
      -
        id: rules_action
        uuid: 1f554fa6-2a44-4d03-985d-f87412cd0843
        weight: 0
        context_values:
          message: 'The basic page "{{ node.title }}" was deleted'
          type: status
          repeat: true
        context_mapping: {  }
        context_processors:
          message:
            rules_tokens: {  }
          type:
            rules_tokens: {  }
          repeat:
            rules_tokens: {  }
        provides_mapping: {  }
        action_id: rules_system_message

```
> 0 votes

# Answer
If it is your own ContentEntity, hook on the `::save()` method of your ContentEntityForm descendant (you may also `\Drupal::service('event_dispatcher')->dispatch(... something ...)` here. This would be OO.

If you can not do this, go for the hook\_entity\_update, this is not OO. But there is currently no `Event` in core for this.

Regards,

Rainer
> -1 votes

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


# Question
Title: How can you get the route name/path of groups?
I am new to Drupal 8. I am working on groups module and want to assign different themes to different groups.So i need route name of groups. how to find route name of groups. Thanks in advance.

```
/**
   * {@inheritdoc}
   */
  public function applies(RouteMatchInterface $route_match) {
    // Use this theme on a certain route.
    return $route_match->getRouteName() == 'i need route name of groups here';

  }

```

# Answer
There is probably a \*.routing.yml in the group module where you can find the route name, but routes can also be defined dynamically. So better use a tool, for the command line Drupal Console or for UI the devel module:

Install the devel module, visit the page you want to find out the route name for and click on "Current route info". The route name is in the variable `_route`. You find also the route parameters you might need to further narrow down the route.
> 1 votes

---
Tags: 8, routes, group
---


title: How can I fetch the Inviter in Rules

text:
# Question
Title: How can I fetch the Inviter in Rules
How can I fetch the Inviter of a user in Rules? I'm using Drupal 7 and the invite-by-email module (part of the invite module https://www.drupal.org/project/invite), to allow users to invite others. Now i need to create a Rule that grants points to Inviters whenever their Invitees vote on the site.The voting is controlled with the voting api module, which gives a Rules event. The event is User votes on a Node The Invitee is \[vote:user\] For the inviter I tried \[vote:user:invite:inviter\] but it didn't work. Been going around in circles, but just can't find the Inviter.

any help appreciated. d

# Answer
As a workaround, i ended up using the views rules module at drupal.org/project/views\_rules which "Provides Views directly as Rules actions and loops to seamlessly use view result data."
> 1 votes

# Answer
If you're trying to do something, please show your work so we don't have to guess what you've tried. The variables available to Rules depend on what events you're using, so leaving that part out of your description means we can only guess how to help you. A great way to communicate how you've configured your Rule is to post an export of the Rule you're writing and point out where you're having the problem.

That said, here is a very simple Rule which displays the name of the 'inviter' when a new invitation is created. You can import this and modify it for your specific needs. I used the Invite module from https://www.drupal.org/project/invite

```
{ "rules_test_invite" : {
    "LABEL" : "test invite",
    "PLUGIN" : "reaction rule",
    "OWNER" : "rules",
    "REQUIRES" : [ "rules", "invite" ],
    "ON" : { "invite_insert" : [] },
    "DO" : [ { "drupal_message" : { "message" : [ "invite:inviter:name" ] } } ]
  }
}

```
> 0 votes

---
Tags: 7, rules
---


# Question
Title: How do you render the View, Edit, Delete, and Revisions tabs in a specific location on node/page?
What is the Drupal 8 equivalent of the following code?

```
<?php if (!empty($tabs)): ?>
<?php print render($tabs); ?>
<?php endif; ?>

```

I don't want/prefer `{{ content }}` as there are other fields in the page which get rendered at the same position and corrupt my template.

# Answer
> 0 votes
*I found the answer as following:* 1.) As i was doing a custom theme based on bootstrap cdn staterkit, it could not render tabs off the bat. so i created a region tabs in `<theme>.info.yml`.

2.) I created a block using tabs "core" module into the tabs region.

3.)Used `{{ page['tabs'] }}` to render tab region. **Thanks for all the help, you guys are the best.**

# Answer
> 3 votes
The tabs are in a block now. Go to the block layout (/admin/structure/block) and move the block **Tabs** to another position.

If you don't find an existing region for what you want to do, add a new region to your theme and place this region in the page template **page--node.html.twig**, so that you can integrate the tabs inside of your html structure. If you need the tabs in different positions you can add multiple instances of the tabs block and configure the visibility.

Try not to put tabs in nodes, combine those two on the page level in a custom page template for nodes if possible.

But, in case you need them between the content of the node. you can do it on node.html.twig. It is not the best practice but can be done if required, but you need to take care, that the caching is working and that the tabs don't appear when the node is used in other places.

# Answer
> 1 votes
Using Twig Tweak you can render blocks inside node templates. For example to ***render the primary tabs block*** use:

```
{{ drupal_block('local_tasks_block') }}

```

# Answer
> 0 votes
You can place the *bartik\_local\_tasks* block (for the Bartik theme) inside your theme block placement.

* Visit /admin/structure/block.
* Select your theme from the tabs panel; for example select *bartik* from the panel

>
* Move the *tabs* block in the theme block placement up/down to set it for all

>

Note: Moving the *tabs* block up/down will move this block for all the content and all the content types using that theme.

There is a programmatic way to do this.

* Create a custom tag block inside modules/\[module-name\]/src/Plugin/Block directory where you create View, Edit, Delete, and Revisions tabs
* Create the TwigExtension.php file inside modules/\[module-name\]/src
* Use this extension in the theme

```
function hook_preprocess_node($variables) {
  $abc = \Drupal::service('plugin.manager.block')->createInstance('{block_name}',[]);
  $variables['custom_tabs_block'] = $abc->build();
}

```
* Use this variable inside your node.html.twig

Alternatively, you can write your preprocess function, for example `hooks_preprocess_page(){}` for page.html.twig, where you can check the node content type and other fields, and set a variable.

```
function mytheme_preprocess_node(&$variables) {
  $variables['type'] = '';

  if (isset($variables['node']->get('type')->target_id) && $variables['node']->get('type')->target_id == 'articles') {
    $variables['type'] = 'articles';
  }
}

```

This variable will be available for in the node.html.twig file.

```
{%if type == 'articles' %}
  You are watching an {{ type }} content.
{% else %}
  You are visiting a non-article content.
{% endif %}

```

# Answer
> 0 votes
The equivalent twig code is as follows

```
{% if tabs %}
  {{ tabs }}
{% endif %}

```

---
Tags: 8, theming
---


# Question
Title: Why can't I delete certain content types?
I have a Drupal 7 site, and under the Content Types menu, only certain types have a 'delete' link under the 'Operations' column.

All of the content types have the links for 'edit', 'manage fields', and 'manage display', but only some have 'delete', while others don't.

I checked my permissions for my user, and I do have `Administer content types` -- but that appears to be one global permission for all content types, so I still don't understand why some are deletable while others aren't.

Why do only certain content types have a delete operation?

# Answer
You can only delete content types that are created manually (via *`admin/structure/types/add`*).

Content types that don't have a delete link are created programmatically by *installed* modules and will be deleted after you *uninstall* them.
> 15 votes

# Answer
Delete them by going to the url `/admin/structure/types/manage/content-machine-name/delete`

content-machine-name being the machine name of the content type you want to delete. Also, make sure you use dashes in the machine name rather then underscores.
> 31 votes

# Answer
As hpn mentioned You can only delete content types that are created manually.

But sometimes things do not go smoothly. The following steps have helped to delete content types. You need to backup the database before trying this.

\[To manually delete a content type that was created  #16 Nedjo suggests in drupal.org:

> * Disable the feature. I've sometimes run into problems with this--a feature won't disable. In this case, remove the feature module code.
> * Determine the machine name of the content type to be deleted, e.g., 'mytype'.
> * In the database, revert the node\_type table values to the defaults as if the content type were created manually through the UI:
>
> `mysql> UPDATE node_type SET module = 'node', custom = 1, modified =
>  1, locked = 0 WHERE type = 'mytype';`
>
> * Clear caches to ensure the content type change is available.
> * Through the UI at admin/structure/types, delete the content type.

The above steps is what it has helped me before when I have found those problems.
> 2 votes

# Answer
Disabling the module providing the content type should be the first step. If the content type was provided by a feature, you'll still need to delete the content type.

For a well-made Feature, the ability to delete a content type will come after the Feature providing it is disabled. This is preferred over deleting a content type out from under a feature, as current functionality or future updates to that feature may require the content type.

Note that you can disable feature modules (admin/modules) even when the the Features interface is (rather foolishly) claiming it cannot disable a module because of conflicts.

And if you don't want to do anything through the interface, and just want to do code, here's an example of an update hook that will disable the Commons Wiki and Posts Features and then delete the content types they provide.

```
<?php
/**
 * Remove unused features and content types.
 */
function example_update_7001() {
  // Delete one (spam) piece of wiki content.
  node_delete(2731);
  // Disable Feature modules.
  module_disable(array('commons_wikis_pages', 'commons_wikis', 'commons_posts'));
  // Delete content types.
  node_type_delete('wiki');
  node_type_delete('post');
}
?>

```
> 1 votes

# Answer
I did this but I get this error message: Notice: Undefined index \[content-type\] in node\_filter\_form() Any idea how to fix this?
> 0 votes

---
Tags: 7, nodes
---


title: Extend a form from a custom module

text:
# Question
Title: Extend a form from a custom module
I'm newbie with Drupal 8. I was entrusted with hanging a custom module to a Drupal 8 default login form.

>

Together with these fields, I would like to see those of my form, which should be generated in this way.

```
use Drupal\trust_ppg\Consents;

function hooks_enabled()
{
    $value = \Drupal::config('trust_ppg.settings')->get('trust_ppg_module_checkbox_value');
    if ($value == 1) {
        return true;
    }
    return false;
}

// Implements hook_form_FORM_ID_alter(). - REGISTRATION form
function trust_ppg_form_user_register_form_alter(&$form, &$form_state)
{
    if (hooks_enabled() == false) {
        return $form;
    }
    $account = $form['#user'];
    $form = array_merge($form, trust_ppg_policies_form($account));
    $form['#validate'][] = 'registration_extra_fields_validate';
}

// Implements hook_validate(). FIXME -> move to formbuilder
function registration_extra_fields_validate($form, &$form_state)
{
    if (hooks_enabled() == false) {
        return;
    }
    $error_messages = [];
    if ($form_state['input']['tou'] != '1') {
        $error_messages["body"][] = 'In order to proceed with the registration you need to accept the Terms Of Use!';
    }
    if ($form_state['input']['privacy_policy'] != '1') {
        $error_messages["body"][] = 'In order to proceed with the registration you need to accept the Privacy Policy!';
    }
    if ($form_state['input']['cookie_policy'] != '1') {
        $error_messages["body"][] = 'In order to proceed with the registration you need to accept the Cookie Policy!';
    }
    // Add new mandatory policies here
    foreach ($error_messages as $element => $messages) {
        // FIXME - form_set_error($element, theme('item_list', array('items' => $messages)));
    }
}

// Implements hook_form_FORM_ID_alter(). - USER PROFILE form
function trust_ppg_form_user_profile_form_alter(&$form, $form_state)
{
    if (hooks_enabled() == false || $form['#user_category'] != 'account') {
        return $form;
    }
    $account = $form['#user'];
    $form = array_merge($form, trust_ppg_policies_form($account));
}

// Implements hook_help().
function trust_ppg_help($path, $arg)
{
    switch ($path) {
        case "admin/help#trust_ppg":
            return t("Help Hook");
            break;
    }
}

// Implements hook_cron().
function trust_ppg_cron()
{
    Consents::process_consents_batch();
}

/**
 * Implements hook_user_insert().
 * - A user account was created.
 *
 * @FIXME
 * The $edit and $category parameters are gone in Drupal 8. They have been left
 * here in order to prevent 'undefined variable' errors, but they will never
 * actually be passed to this hook. You'll need to modify this function and
 * remove every reference to them.
 */

function trust_ppg_user_insert(\Drupal\user\UserInterface $account, $category, $edit = null)
{
    $tou_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_tou_guid_value');
    if ($tou_guid != 0) {
        $tou_consent = $edit['tou'];
        if ($tou_consent == 1) {
            give_consent_user($tou_guid, $account);
        }
    }

    $privacy_policy_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_privacy_policy_guid_value');
    if ($privacy_policy_guid != 0) {
        $privacy_consent = $edit['privacy_policy'];
        if ($privacy_consent == 1) {
            give_consent_user($privacy_policy_guid, $account);
        }
    }

    $cookie_policy_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_cookie_policy_guid_value');
    if ($cookie_policy_guid != 0) {
        $cookie_consent = $edit['cookie_policy'];
        if ($cookie_consent == 1) {
            give_consent_user($cookie_policy_guid, $account);
        }
    }

    $cookie_policy_3rdparties_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_cookie_3rdparties_guid_value');
    if ($cookie_policy_3rdparties_guid != 0) {
        $cookie_3rdparties_consent = $edit['cookie_3rdparties'];
        if ($cookie_3rdparties_consent == 1) {
            give_consent_user($cookie_policy_3rdparties_guid, $account);
        }
    }

    $cookie_policy_additional_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_cookie_additional_guid_value');
    if ($cookie_policy_additional_guid != 0) {
        $cookie_additional_consent = $edit['cookie_additional'];
        if ($cookie_additional_consent == 1) {
            give_consent_user($cookie_policy_additional_guid, $account);
        }
    }

    $marketing_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_marketing_guid_value');
    if ($marketing_guid != 0) {
        $marketing = $edit['marketing'];
        if ($marketing == 1) {
            give_consent_user($marketing_guid, $account);
        }
    }

    $db_publication_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_db_publication_guid_value');
    if ($db_publication_guid != 0) {
        $db_publication = $edit['db_publication'];
        if ($db_publication == 1) {
            give_consent_user($db_publication_guid, $account);
        }
    }

    $soft_optin_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_soft_optin_guid_value');
    if ($soft_optin_guid != 0) {
        $soft_optin = $edit['soft_optin'];
        if ($soft_optin == 1) {
            give_consent_user($soft_optin_guid, $account);
        }
    }

    $profiling_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_profiling_guid_value');
    if ($profiling_guid != 0) {
        $profiling = $edit['profiling'];
        if ($profiling == 1) {
            give_consent_user($profiling_guid, $account);
        }
    }

    $newsletter_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_newsletter_guid_value');
    if ($newsletter_guid != 0) {
        $newsletter = $edit['newsletter'];
        if ($newsletter == 1) {
            give_consent_user($newsletter_guid, $account);
        }
    }
}

/**
 * Implements hook_user_update().
 *
 * @FIXME
 * The $edit and $category parameters are gone in Drupal 8. They have been left
 * here in order to prevent 'undefined variable' errors, but they will never
 * actually be passed to this hook. You'll need to modify this function and
 * remove every reference to them.
 */
function trust_ppg_user_update(\Drupal\user\UserInterface $account, $category, $edit = null)
{
    $tou_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_tou_guid_value');
    if ($tou_guid != 0) {
        $tou_consent = $edit['tou'];
        manage_consent_wrapper($tou_consent, $tou_guid, $account);
    }

    $privacy_policy_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_privacy_policy_guid_value');
    if ($privacy_policy_guid != 0) {
        $privacy_consent = $edit['privacy_policy'];
        manage_consent_wrapper($privacy_consent, $privacy_policy_guid, $account);
    }

    $cookie_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_cookie_policy_guid_value');
    if ($cookie_guid != 0) {
        $cookie_consent = $edit['cookie_policy'];
        manage_consent_wrapper($cookie_consent, $cookie_guid, $account);
    }

    $cookie_3rdparties_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_cookie_3rdparties_guid_value');
    if ($cookie_3rdparties_guid != 0) {
        $cookie_3rdparties_consent = $edit['cookie_3rdparties'];
        manage_consent_wrapper($cookie_3rdparties_consent, $cookie_3rdparties_guid, $account);
    }

    $cookie_additional_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_cookie_additional_guid_value');
    if ($cookie_additional_guid != 0) {
        $cookie_additional_consent = $edit['cookie_additional'];
        manage_consent_wrapper($cookie_additional_consent, $cookie_additional_guid, $account);
    }

    //
    $marketing_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_marketing_guid_value');
    if ($marketing_guid != 0) {
        $marketing = $edit['marketing'];
        manage_consent_wrapper($marketing, $marketing_guid, $account);
    }

    $db_publication_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_db_publication_guid_value');
    if ($db_publication_guid != 0) {
        $db_publication = $edit['db_publication'];
        manage_consent_wrapper($db_publication, $db_publication_guid, $account);
    }

    $soft_optin_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_soft_optin_guid_value');
    if ($soft_optin_guid != 0) {
        $soft_optin = $edit['soft_optin'];
        manage_consent_wrapper($soft_optin, $soft_optin_guid, $account);
    }

    $profiling_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_profiling_guid_value');
    if ($profiling_guid != 0) {
        $profiling = $edit['profiling'];
        manage_consent_wrapper($profiling, $profiling_guid, $account);
    }

    $newsletter_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_newsletter_guid_value');
    if ($newsletter_guid != 0) {
        $newsletter = $edit['newsletter'];
        manage_consent_wrapper($newsletter, $newsletter_guid, $account);
    }
    // Add new entries here
}

function _expose_var(&$build, $var_name, $var)
{
    $build['#attached']['drupalSettings'][$var_name] = $var;
}

//Implements hook_preprocess_page(). - useful to force redirect, for example
function trust_ppg_page_attachments(array &$page)
{
    $user = \Drupal::currentUser();

    _expose_var($page, 'ga_guid', \Drupal::config('trust_ppg.settings')->get('trust_ppg_ga_guid'));

    /* If you change the default text remember to change all occurences */
    _expose_var($page, 'main_text', \Drupal::config('trust_ppg.settings')->get('trust_ppg_popup_text'));
    _expose_var($page, 'policy_label', \Drupal::config('trust_ppg.settings')->get('trust_ppg_popup_label'));

    if (\Drupal::currentUser()->isAuthenticated()) {
        $cookie_policy_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_cookie_policy_guid_value');
        $consent = Consents::get_consent($cookie_policy_guid, $user);

        $cookie_policy_3rdparties_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_cookie_3rdparties_guid_value');
        $consent_3rdparties = Consents::get_consent($cookie_policy_3rdparties_guid, $user);

        $cookie_policy_additional_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_cookie_additional_guid_value');
        $consent_additional = Consents::get_consent($cookie_policy_additional_guid, $user);
        // Add mandatory policies here

        // Expose variables for JS
        _expose_var($page, 'cookie_3rdparties_consent', is_array($consent_3rdparties));
        _expose_var($page, 'cookie_additional_consent', is_array($consent_additional));
        _expose_var($page, 'username', $user->getAccountName());
    } else {
        // Expose policies GUIDs
        _expose_var($page, 'cookie_policy', \Drupal::config('trust_ppg.settings')->get('trust_ppg_cookie_policy_guid_value'));
        _expose_var($page, 'cookie_3rdparties', \Drupal::config('trust_ppg.settings')->get('trust_ppg_cookie_3rdparties_guid_value'));
        _expose_var($page, 'cookie_additional', \Drupal::config('trust_ppg.settings')->get('trust_ppg_cookie_additional_guid_value'));

        _expose_var($page, 'cookie_policy_label', \Drupal::config('trust_ppg.settings')->get('trust_ppg_popup_cookie_policy_label'));
        _expose_var($page, 'cookie_3rdparties_label', \Drupal::config('trust_ppg.settings')->get('trust_ppg_popup_cookie_3rdparties_label'));
        _expose_var($page, 'cookie_additional_label', \Drupal::config('trust_ppg.settings')->get('trust_ppg_popup_cookie_additional_label'));

        _expose_var($page, 'cookie_policy_body', \Drupal::config('trust_ppg.settings')->get('trust_ppg_popup_cookie_policy_body'));
        _expose_var($page, 'cookie_3rdparties_body', \Drupal::config('trust_ppg.settings')->get('trust_ppg_popup_cookie_3rdparties_body'));
        _expose_var($page, 'cookie_additional_body', \Drupal::config('trust_ppg.settings')->get('trust_ppg_popup_cookie_additional_body'));

        if (isset($_COOKIE['consent_3rdparties_cookies']) && isset($_COOKIE['consent_additional_cookies'])) {
            _expose_var($page, 'cookie_3rdparties_consent', $_COOKIE['consent_3rdparties_cookies']);
            _expose_var($page, 'cookie_additional_consent', $_COOKIE['consent_additional_cookies']);
        }
    }

    if (hooks_enabled()) {
        $page['#attached']['library'][] = 'trust_ppg/main';
        //redirect_to_consent('');

        //POPUP
        if (
            \Drupal::currentUser()->isAnonymous()
            && \Drupal\Core\Url::fromRoute("<current>")->toString() != '/privacy-policy-full'
            && \Drupal\Core\Url::fromRoute("<current>")->toString() != '/user'
            && \Drupal\Core\Url::fromRoute("<current>")->toString() != '/user/register'
            && \Drupal\Core\Url::fromRoute("<current>")->toString() != '/company/register'
            && \Drupal\Core\Url::fromRoute("<current>")->toString() != '/research/register'
        ) {
            dpm('popup');
            $page['#attached']['library'][] = 'trust_ppg/modal';
            $page['#attached']['library'][] = 'trust_ppg/disabler';
            dpm($page, '$page');
        }
    }
}

/**
 * Implements hook_node_access().
 *
 * @param string $op
> git for-each-ref --format %(refname) %(objectname) --sort -committerdate
 *      The operation to be performed. Possible values:
 *          "create"
 *          "delete"
 *          "update"
 *          "view"
 * @param string $node
 *         Either a node object or the machine name of the
 *        content type on which to perform the access check.
 * @param object $user
 *
 * @return constant
 *        NODE_ACCESS_ALLOW, NODE_ACCESS_DENY, NODE_ACCESS_IGNORE
 */
function trust_ppg_access($op, $node, $account)
{
    return ($op == 'view' && ($account->hasPermission('permission ro') || $account->hasPermission('administer trust_ppg')));
}

// Implements hook_theme().
function trust_ppg_theme()
{
    return array();
}

function trust_ppg_policies_form_submit($form, &$form_state)
{
    if (\Drupal\Core\Url::fromRoute("<current>")->toString() == 'privacy-policy-full') {
        $user = \Drupal::currentUser();
        $account = $user;
    } else {
        $account = $form['#user'];
    }

    $tou_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_tou_guid_value');
    if ($tou_guid != 0) {
        $tou_consent = $form_state['input']['tou'];
        manage_consent_wrapper($tou_consent, $tou_guid, $account);
    }

    $privacy_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_privacy_policy_guid_value');
    if ($privacy_guid != 0) {
        $privacy_consent = $form_state['input']['privacy_policy'];
        manage_consent_wrapper($privacy_consent, $privacy_guid, $account);
    }

    $cookie_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_cookie_policy_guid_value');
    if ($cookie_guid != 0) {
        $cookie_consent = $form_state['input']['cookie_policy'];
        manage_consent_wrapper($cookie_consent, $cookie_guid, $account);
    }

    $cookie_3rdparties_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_cookie_3rdparties_guid_value');
    if ($cookie_3rdparties_guid != 0) {
        $cookie_3rdparties_consent = $form_state['input']['cookie_3rdparties'];
        manage_consent_wrapper($cookie_3rdparties_consent, $cookie_3rdparties_guid, $account);
    }

    $cookie_additional_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_cookie_additional_guid_value');
    if ($cookie_additional_guid != 0) {
        $cookie_additional_consent = $form_state['input']['cookie_additional'];
        manage_consent_wrapper($cookie_additional_consent, $cookie_additional_guid, $account);
    }
    //
    $marketing_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_marketing_guid_value');
    if ($marketing_guid != 0) {
        $marketing = $form_state['input']['marketing'];
        manage_consent_wrapper($marketing, $marketing_guid, $account);
    }

    $db_publication_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_db_publication_guid_value');
    if ($db_publication_guid != 0) {
        $db_publication = $form_state['input']['db_publication'];
        manage_consent_wrapper($db_publication, $db_publication_guid, $account);
    }

    $soft_optin_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_soft_optin_guid_value');
    if ($soft_optin_guid != 0) {
        $soft_optin = $form_state['input']['soft_optin'];
        manage_consent_wrapper($soft_optin, $soft_optin_guid, $account);
    }

    $profiling_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_profiling_guid_value');
    if ($profiling_guid != 0) {
        $profiling = $form_state['input']['profiling'];
        manage_consent_wrapper($profiling, $profiling_guid, $account);
    }

    $newsletter_guid = \Drupal::config('trust_ppg.settings')->get('trust_ppg_newsletter_guid_value');
    if ($newsletter_guid != 0) {
        $newsletter = $form_state['input']['newsletter'];
        manage_consent_wrapper($newsletter, $newsletter_guid, $account);
    }
}

```

How do I create an alleged hook (in Drupal 7 hooks were used, I think maybe they should also be used in Drupal 8) that allows me to add my module to the default form of Drupal 8?

# Answer
As start, the code shown in the question is wrong in many points, in particular because it is using Drupal 7 code that has not been converted to Drupal 8. Drupal core has modules that alter the user edit form, which could be taken as guide for your own code.

Since you want to alter the user edit form when users create an account and when an existing account is edited, you should look at `contact_form_user_form_alter()`, which is used for exactly those two forms. From that hook implementation, and the code in `AccountForm::form()`, you will notice that:

* `#user_category` isn't used anymore
* The user account being edit isn't given anymore in `$form['#user']` but it can be retrieved with `$form_state->getFormObject()->getEntity()`
* The user editing that account, useful to determine who is editing the account, or to check if somebody is registering an account, can be retrieved with `\Drupal::currentUser()`
* Any submission handler for the button used to create the account or edit it is added with `$form['actions']['submit']['#submit'][]`, while a validation handler is added with `$form['actions']['submit']['#validate'][]`

As for the form submission and validation handlers:

In some cases, it can be helpful to understand if a hook implementation like `contact_form_user_form_alter()` is invoked when people registers their own account, an administrator is creating an account for somebody else, or an account is edited. In those cases, the following code can be helpful.

```
$account = $form_state->getFormObject()->getEntity();
$user = \Drupal::currentUser();
$register = $account->isAnonymous();
$admin_create = $register && $account->access('create');
$self_register = $register && !$admin_create;

```

Using that code, `$register` is `TRUE` if an account is being registered/created, or `FALSE` if an existing account is edited; `$admin_create` is `TRUE` if the account is being created from an administrator user; `$self_register` is `TRUE` when the account is being registered.

As for the `hook_user_insert(Drupal\Core\Entity\EntityInterface $entity)` and `hook_user_update(Drupal\Core\Entity\EntityInterface $entity)` hooks (which are documented as `hook_ENTITY_TYPE_insert()` and `hook_ENTITY_TYPE_update()`), they only receive an argument: The user object being inserted or updated. Any information about the user needs to be taken from that argument, which is the user object after it has been edited/created. For `hook_user_update(Drupal\Core\Entity\EntityInterface $entity)`, `$entity->original` returns the user object before it was changed.

As side note, any function implemented by a module should be prefixed by its machine name, to avoid conflicts with other modules. (The functions of all the modules are global.)
You have a `hooks_enabled()` function that could create name clashes with the Hooks module, when it adds a `hooks_enabled()` function to its code. You have also a `registration_extra_fields_validate()` function whose name starts with the machine name of the Entity Registration module.
> 2 votes

---
Tags: forms, hooks
---


# Question
Title: Using variation values in a template file
I am building a site that uses Drupal Commerce 2 and I am attempting to build out the product details template. Our product has a variation (field\_accessories) which is an entity reference to another product and I am trying to access the title, the images (first one actually), and the link to the product's detail page.

I got the title showing with

```
{{ product['field_accessories'][0]['#commerce_product_variation'].getTitle() }}

```

and I can get the sku and price with:

```
{{ product['field_accessories'][0]['#commerce_product_variation'].getPrice() }}
{{ product['field_accessories'][0]['#commerce_product_variation'].getSku() }}

```

But I need the images and url link. Based on the available methods from using kint on the product\['field\_accessories'\]\[0\]\['#commerce\_product\_variation'\] I tried:

```
{{ product['field_accessories'][0]['#commerce_product_variation'].get('field_images') }}

```

and

```
{{ product['field_accessories'][0]['#commerce_product_variation'].getField('field_images') }}

```

and

```
{{ product['field_accessories'][0]['#commerce_product_variation'].getField('images') }}

```

but nothing is working and all come back with null.

When I do a `product['field_accessories'][1]['#commerce_product_variation'].getFields()` I see the following:

Any ideas on how to access the field\_images and url to that product's page? Thanks and let me know!

# Answer
> 1 votes
Here is the answer that worked for me:

```
product.field_accessories[0]['#commerce_product_variation'].get('field_images')[0].entity.fileuri)

```

And I looped over the product.field\_accessories to get just the first image for each product

---
Tags: theming, commerce
---


# Question
Title: What is the difference between renderPlain and renderRoot?
I added a login form to a node with following code :

```
function my_theme_preprocess_node(&$variables) {
  $node = $variables['node'];
  $variables['login_form'] = FALSE;
  $variables['username'] = FALSE;
  if(Drupal::currentUser()->isAnonymous()) {
    $form = Drupal::formBuilder()->getForm(\Drupal\user\Form\UserLoginForm::class);
    $variables['login_form'] = Drupal::service('renderer')->renderPlain($form); // renderRoot
  } else {
    $variables['username'] = Drupal::currentUser()->getDisplayName();
  }
}

```

If I change the line

```
$variables['login_form'] = Drupal::service('renderer')->renderPlain($form);

```

to

```
$variables['login_form'] = Drupal::service('renderer')->renderRoot($form);

```

I can't see any difference. Both seem to work either.

I read the docs, the latter mostly checks

> Whether we're currently in a ::renderRoot() call.

but it is not very explicit about why /when this check is useful and so when to use the former or latter function ?

# Answer
`renderRoot()` is for rendering the final result returned by a controller. `renderPlain()` is for rendering HTML which is not part of a page and is used for other purposes like emails. In your case the correct method would be `render()`, because this is the only render method which can bubble up metadata to the page. But you don't need to render at all, because you can put the unrendered form build array in a template variable and it will be rendered automatically when printed in TWIG.
> 9 votes

---
Tags: 8, theming
---


title: Understanding behaviors

text:
# Question
Title: Understanding behaviors
I created a custom theme (a sub-theme of Radix base theme).

I am loading a custom Javascript file (`radix_sub.script.js`) using the module `libraries.yaml` file like below:

```
style:
  version: VERSION
  css:
    theme:
      assets/css/radix_sub.style.css: {}
  js:
    '//cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js': { type: external }
    '//stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js': { type: external }
    assets/js/radix_sub.script.js: {}
  dependencies:
    - core/drupal
    - core/jquery
    - core/jquery.once
    - core/drupalSettings

```

In that file, I want to register some JavaScript events (Bootstrap modal).

```
(function ($, Drupal) {

  Drupal.behaviors.portfolio = {

    attach: function (context) {

      console.log('CONTEXT', context);

      $('#portfolioModal', context).once('bootstrap-modal-shown', console.log('*** REGISTER SHOWN.BS.MODAL')).each(function () {
        $(this).on('shown.bs.modal', function () {
          console.log('SHOWN BS MODAL EVENT TRIGGERED');
        });
      });

      $('#portfolioModal', context).once('bootstrap-modal-hidden', console.log('*** REGISTER HIDDEN.BS.MODAL')).each(function () {
        $(this).on('hidden.bs.modal', function () {
          console.log('HIDDEN BS MODAL EVENT TRIGGERED');
        });
      });
    }
  }
}(jQuery, Drupal));

```

It works. I see both *SHOWN BS MODAL EVENT TRIGGERED* and *HIDDEN BS MODAL EVENT TRIGGERED* logs when I show/hide the modal.

First, am I doing that the correct way?

This is what my developer console looks like when I load my page.

>

This happens only when I am logged in (as admin). When I am anon, I see the *REGISTER* logs only once but here my code seems to be executed 11 times.

Is this normal behavior? And/or why is it doing that?

# Answer
> 5 votes
The answer lies here:

```
$('#portfolioModal', context).once('bootstrap-modal-shown', console.log('*** REGISTER SHOWN.BS.MODAL'))

```

The code that comes after the call to `$.once()` will only be executed once. However, `$.once()` can and will be executed multiple times. That's why you are seeing your calls to the `console` being called multiple times. If you change this:

```
$('#portfolioModal', context).once('bootstrap-modal-shown', console.log('*** REGISTER SHOWN.BS.MODAL')).each(function () {
  // Do some stuff.
});

```

To this:

```
$('#portfolioModal', context).once('bootstrap-modal-shown').each(function () {
  console.log('*** REGISTER SHOWN.BS.MODAL');
  // Do some stuff.
});

```

You will see that your call to the console is only shown a single time. Note that you also will not use `$.on()` here, as the Drupal behaviors API replaces any necessity of it. Each time `Drupal.behaviors` are executed, a `context` argument is passed to it. This will be either the full DOM, or any HTML being newly inserted into the DOM. So where in normal jQuery you might do this:

```
$(".some_selector").on(someCallbackFunction);

```

In Drupal, you would do this:

```
Drupal.behaviors.someArbitraryKey = {
  attach: function(context) {
    $(context).find(".some_selector").once("some-arbitrary-key").each(someCallbackFunction);
  }
};

```

This is the Drupal way of achieving the same goals as using `$.on()` in normal jQuery, while tying in with the Drupal APIs.

---- Edit ----

Original answer below, which turned out to not answer the question. Left for posterity's sake, and because it actually adds on well to the end of the above answer.

Drupal behaviors are called on page load. It is also called upon AJAX load. You need to wrap code in `$.once()` if it should only be executed a single time:

```
function someElementListener(context) {
  // Call to $.once():
  $(context).find(".some_selector").once("arbitrary-key").each(function () {
    // Code here will only be executed a single time on
    // each .some_selector element found
    $(this).click(function() {alert("clicked");});
  });
}

Drupal.behaviors.someModule = {
  attach: function (context) {
    someElementListener(context);
  }
};

```

If you wrap your code in $.once(), your console calls will only be logged a single time.

---
Tags: 8, javascript
---


# Question
Title: Is it possible to run 2 different sites in Acquia Dev Desktop on the same computer?
I'm running Acquia Dev Desktop and I'm wondering if it's possible to clone 2 different sites with different Drupal versions.

I tried cloning both sites, but when I cloned the second one and tried to view it, I got the following message.

>

Is there a way I can use both in the same computer?

# Answer
> 2 votes
Yes, you can install more than one Drupal site on the same computer using Acquia Dev Desktop. Check out Acquia's documentation here:

https://docs.acquia.com/dev-desktop/sites/

It looks to me like maybe you have specified an identical name for the local database. The error screen that you see is one that commonly appears when a new Drupal installation attempts to run `install.php` but the database is already set up.

As an experiment, click on the link that says "View your existing site." If my hunch is correct, you should see the content of your other site.

Instead, try selecting "Create a new database" when setting up the second site, and make certain that you give it a different name than the first site uses.

If the two cloned sites just happen to have identically-named databases, you should still be able to create a new one with a new name, and then import a sqldump of the second site's database into that.

---
Tags: acquia-dev-desktop, acquia-cloud
---


title: Link doesn't appear in configuration

text:
# Question
Title: Link doesn't appear in configuration
I'm a Drupal 8 newbie writing my first module. I have a functioning yet basic module to learn from. What I'm working on now is for it to get a value from a configuration page. If I go directly to the URL for the custom config page it works. What I'm having trouble with is getting a link to my custom config page to display on the main config page. I've been through every tutorial I can find on this topic and I just can't figure out why it's not working. I hope you all can help. Here's my yml files.

### itemrequest.info.yml

```
name: Item Request
type: module
description: Allow the public to request items for the District to purchase.
package: YCFLD
core: 8.x
configure: itemrequest.settings

```

### itemrequest.routing.yml

```
itemrequest.itemrequest:
  path: '/itemrequest'
  defaults:
    _controller: '\Drupal\itemrequest\Controller\ItemrequestController::itemrequestpage'
    _title: 'Request an Item'
  requirements:
    _permission: 'access content'
itemrequest.settings:
  path: '/admin/config/itemrequest/settings'
  defaults:
    _form: '\Drupal\itemrequest\Form\ItemrequestForm'
    _title: 'Item Request'
  requirements:
    _permission: 'access administration pages'
  options:
    _admin_route: TRUE

```

### itemrequest.links.menu.yml

```
itemrequest.settings:
  title: 'Item Request Form'
  description: 'Settings for Item Request form"
  route_name: itemrequest.settings
  parent: system.admin_config

```

Do see anything wrong in the .yml files? Is there anything I can do to troubleshoot this?

# Answer
`system.admin_config` is the top-level page at `/admin/config`. Normally you would not put your configuration link there - you would instead put it into one of the blocks that appear on that page. For example, the "People" block would be `user.admin_link`, so if you want your config under "People" use `user.admin_link` as your parent. Or if you want to put your configuration link under the "Development" block use `system.admin_config_development` as your parent.

If you want to create your own top-level block then you need to do things a little differently - see the core user module and how it defines `user.admin_link` in `user.links.menu.yml` and `user.routing.yml` \- this shows how a new block is created on the config page. You can then put your configuration link in that new block.
> 1 votes

---
Tags: routes
---