# Question
Title: How do I get a textarea form field to use a full HTML WYSIWYG editor?
I have configuration form for a custom module.

```
namespace Drupal\amu_http_status_code_display\Form;

use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;

class AmuHttpStatusCodeDisplayConfigForm extends ConfigFormBase {

  public function getFormId() {
    return 'AmuHttpStatusCodeDisplayConfigForm';
  }

  public function buildForm(array $form, FormStateInterface $form_state) {

    $config = $this->config('amustatuscode.settings');
    $form = parent::buildForm($form, $form_state);

    $form['404_message'] = array(
      '#type' => 'textarea',
      '#title' => t('404 error message'),
      '#description' => t('error 404 message.'),
      '#default_value' => '404 error',
    );

    $form['403_message'] = array(
      '#type' => 'textarea',
      '#title' => t('403 error message'),
      '#description' => t('error 403 message.'),
      '#default_value' => '403 error',
    );

    return $form;
  }

  public function submitForm(array &$form, FormStateInterface $form_state) {
    $config = $this->config('amustatuscode.settings');
    $config->set('404_message', $form_state->getValue('404_message'));
    $config->set('403_message', $form_state->getValue('403_message'));
    $config->save();

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

  public function getEditableConfigNames() {
    return ['amustatuscode.settings'];
  }

}

```

How can I make the form field I add use a WYSIWYG editor?

# Answer
You want to use the TextFormat form element: https://api.drupal.org/api/drupal/core%21modules%21filter%21src%21Element%21TextFormat.php/class/TextFormat/8.4.x

Note that this will give you a value and a format in a nested form structure, you need to store both (or lock to a specific format) and then render the content with that format to avoid security issues.
> 4 votes

# Answer
Like so:

```
    $config = $this->config('amustatuscode.settings');
    $form = parent::buildForm($form, $form_state);

    $form['404_message'] = array(
      '#type' => 'text_format',
      '#title' => t('404 error message'),
      '#description' => t('error 404 message.'),
      '#default_value' => $config->get('404_message'),
      '#format' => $config->get('404_message_format'),
    );

    $form['403_message'] = array(
      '#type' => 'text_format',
      '#title' => t('403 error message'),
      '#description' => t('error 403 message.'),
      '#default_value' => $config->get('403_message'),
      '#format' => $config->get('403_message_format'),
    );

    return $form;

  }

  public function submitForm(array &$form, FormStateInterface $form_state) {
    $config = $this->config('amustatuscode.settings');
    $values = $form_state->getValues();
    $config->set('404_message', $values['404_message']['value']);
    $config->set('404_message_format', $values['404_message']['format']);
    $config->set('403_message', $values['403_message']['value']);
    $config->set('403_message_format', $values['403_message']['format']);
    $config->save();

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

  public function getEditableConfigNames() {
    return ['amustatuscode.settings'];
  }

```

Please note this requires a change to your `amustatuscode.settings` schema definition to include the additional `format` string
> 1 votes

---
Tags: 8, forms, wysiwyg
---


# Question
Title: Upgrading to core 8.8 results in a path alias error
I have been trying to upgrade a site from Drupal 8.7.7 to 8.8, and following the release documentation, I upgraded the core and pathauto module at the same time. So my composer.json has the core set to ^8.8, and pathauto to 1.8.

After running drush updb I get the following error in the console:

```
 [error]  ParseError: syntax error, unexpected '::conf' in module_load_include() (line 73 of /var/www/html/docroot/core/modules/shortcut/shortcut.install) #0 /var/www/html/docroot/core/includes/module.inc(99): module_load_include('install', 'shortcut')
#1 /var/www/html/docroot/core/includes/install.inc(88): module_load_install('shortcut')
#2 /var/www/html/docroot/vendor/drush/drush/src/Commands/core/UpdateDBCommands.php(41): drupal_load_updates()
#3 [internal function]: Drush\Commands\core\UpdateDBCommands->updatedb(Array)
#4 /var/www/html/docroot/vendor/consolidation/annotated-command/src/CommandProcessor.php(257): call_user_func_array(Array, Array)

```

When I visit the front-end of the site I see the following:

```
Symfony\Component\DependencyInjection\Exception\InvalidArgumentException: Unable to replace alias "path.alias_whitelist" with actual definition "path_alias.whitelist". in Symfony\Component\DependencyInjection\Compiler\ReplaceAliasByActualDefinitionPass->process() (line 57 of /var/www/html/docroot/vendor/symfony/dependency-injection/Compiler/ReplaceAliasByActualDefinitionPass.php).

```

Any idea how can successfully upgrade from Drupal 8.7.7 to 8.8?

Note: I still have a backup of the site and db, so an explanation on the upgrade would also work.

# Answer
Pathauto 8.x-1.6 is the only version working with both Drupal 8.7 and 8.8. So first update Pathauto to this version, then update core and then update Pathauto to the current version. Run `drush updb` in each step. See the change record http://drupal.org/node/3089457.
> 3 votes

---
Tags: 8, path-aliases
---


# Question
Title: Alphabetical case insensitive grouping of results
My question is about grouping results alphabetically in a case insensitive way in Drupal 8. I have a view displaying people's names alphabetically grouped by last name. My problem is the following: I have some people with last name that starts with upper case D and other whose last name starts with lower case d (Example: de XXX). Right now the upper case D and the lower case d are grouped separately. I need to be able to group them together.

# Answer
To group all of your results by Last initial, capitalized:

First, under *Format \> Settings \> Grouping field X*, make sure you check the box to "Use rendered output to group rows".

Second, update the field settings for the field you're using to group the results - under *Rewrite Results*, check "Trim this field to a maximum number of characters," and enter 1 for the length.

Finally, again under *Rewrite Results*, check the box to "Override the output of this field with custom text". You can use twig in the custom text field and leverage twig's upper filter to convert the entire string to uppercase. You'll have to check the *Replacement Patterns* for the correct field name to use in your case, but the end result should look something like:

```
{{ field_last_name|upper }}

```

The result is your view results grouped by last initial, capitalized (whether they actually start with a capital letter or not).
> 0 votes

---
Tags: 8, views
---


# Question
Title: Is there any compatibility issue with Amazon Aurora?
I'm exploring the possibility of using Aurora as a database backend for a large-ish Drupal 7 site because of the potential performance gains. Before I do that, though, I'd like to know if there are any outstanding issues with this kind of setup.

So far, the only resource I've found are this fixed Drupal.org issue and a number of rather old questions on this site. I'm still a bit concerned because the Aurora FAQ states:

> **Q: What does "MySQL-compatible" mean?**
>
> It means that ***most*** of the code, applications, drivers and tools you already use today with your MySQL databases can be used with Aurora with little or no change. The Amazon Aurora database engine is designed to be wire-compatible with MySQL 5.6 using the InnoDB storage engine. Certain MySQL features like the MyISAM storage engine are not available with Amazon Aurora.

(Emphasis mine)

It's the "most" that has me worried, specifically. Are there any pitfalls when using Drupal 7 with Aurora?

# Answer
> 3 votes
Aurora isn't widely used in the community, but all the materials I've seen say it works (like this post).

To test a setup using AWS you could use the test drives setup by Acquia. I'm not sure if they are running Aurora on those or not, but it is probably worth a look.

As for testing custom modules there is a degree to which you cannot know until you either check a module and test it out, but *most* Drupal 7 contrib modules make use of Drupal's internal query APIs and therefore *should* not present issues on Aurora.

# Answer
> 9 votes
We are running a couple of projects on Amazon Aurora with no issues so far. Our largest project is a D7 project with many contributed modules, and a large amount of data being moved.

We've never noticed any DB related issues aside from normal MySQLisms. It's really transparent to Drupal. We use normal Mysql connection strings and zero core changes.

Aurora is more expensive. A lot more expensive. It only starts at the db.r3.large tier, and works out to a monthly (non upfront rate of 0.190 per hour). So the price difference must be considered.

# Answer
> 0 votes
I have not experienced any compatibility issues on 2 mid-size projects with over 100k nodes and over 20k users on D7.

One of the sites I'm looking at has about 20 custom modules and 80 contrib ones.

---
Tags: 7, database
---


# Question
Title: How can we manually enter a payment using their credit card information for a customer in Commerce?
We have customers that are not web savvy and would like us to enter their order for them on the website.

Currently, the admin masquerades as the user, creates the order as them, checks out as them, and then enters their credit card info. Then un-masquerades.

Is there an easier way to do this? We've tried Creating an Order in the backend but the Payments tab says the following for all Credit Card payment methods.

```
"There are no reusable payment methods available for the selected gateway."

```

We were hoping it would be as easy as manually creating the order and then enter the payment for Commerce does not seem to allow enter CC info on the backend.

***We're using Authorize.net***

# Answer
* Create the order in the admin UI and add the user as the first step of creating the order.
* Save the order.
* Go to People and then edit the user you just created. Add payment info.
* Then add shipping info in the orders \> shipments tab (if you have a shippable product).
* Then go to Orders \> Payments and you should be able to add the payment that you just added and make the charge (if cc).
* Then edit the order and place the order.

Maybe there is an easier way to do this but so far these are the bizarre steps I am going through.
> 1 votes

---
Tags: commerce
---


# Question
Title: How can I have links to anchors in menus?
How can I add a menu link as /test#anchor and link to #anchor section on the test page?

I need to add a menu item that links to specific section on the target page. Drupal 7 had menu tokens to achieve it. How can I do this in Drupal 8?

# Answer
> 5 votes
1. Add/edit a menu link
2. Enter `/test#anchor` in the "Link" field
3. Save the link
4. You're done, make tea :)

Point being - fragments are accepted as part of the link in Drupal 8.

One exception seems to be the home page; it doesn't work with the `<front>` token, nor `/#anchor`. The fragment isn't rendered in either case.

# Answer
> 0 votes
For the homepage enter: `<front>#your-anchor` in the **link field**

---
Tags: 8, navigation
---


title: Find which views are using Views PHP

text:
# Question
Title: Find which views are using Views PHP
I have a project that is using Views PHP, and I'd like to figure out which views are using PHP from this module. This will help me determine if I can easily remove the dependency on the module.

Is there a query or something else I can use to figure out what views are using this module?

# Answer
Searching cache\_views as this answer suggested is a partial solution that helps, but a full solution will include views that are not cached.

In order to do this, you can export views to code (e.g. using the Features module), and search the views for `'php'` or `php` to find Global: PHP fields and sort criteria.
> 0 votes

# Answer
Using the PHP filter in the fields should require you to use `<?php ?>` tags as part of the content.

The views data (unless its been exported with features) will sit inside the cache\_views table in your DB, so you should be able to reliably find views using PHP filter by searching like so:

`SELECT * FROM cache_views WHERE data LIKE '%<?php%';`

There is probably a more sophisticated way of finding this.. but hopefully its a good start!
> 1 votes

# Answer
Searching the views table directly seems to be best approach:

```
SELECT
  v.vid,
  v.name,
  d.display_title
FROM
  views_display d
JOIN
  views_view v
USING
  (vid)
WHERE
  d.display_options LIKE '%<?php%'
ORDER BY v.name;

```

```
+-----+----------------------------------------------+-------------------------------+
| vid | name                                         | display_title                 |
+-----+----------------------------------------------+-------------------------------+
| ID  | VIEW MACHINE NAME                            | VIEW DISPLAY NAME             |
+-----+----------------------------------------------+-------------------------------+

```
> 0 votes

---
Tags: 7, views
---


# Question
Title: How do I show a confirm dialog in hook_node_validate()?
I want to check a integer field value in a node add/edit form, if the value is greater than 10, I need display a JS confirm dialog "Are you sure your \*** is greater than 10?", if user click on "Yes", the form validation passes, if "No", the node add form displays as it is and wait the use to edit.

How can I achieve this?

# Answer
This solution is pure javascript, but is NOT based on hook\_node\_validate, is based on event KEY\_UP, its different and may be a better one, since it diplays or hides span html error on edit form and not on form validate, mutch more user friendly.

This JS validates one content type, you have to change machine name in `querySelector`, and it validates all numeric fields (integer or decimal, with different RegExp) and one field to not contain any commas, ofcourse you have to adapt your code.

It builds a `<span>` below each field that finds in `querySelectorAll(your-type-field)` and hides or display span text adding or removing a class `display:none`. take a close look on RegExp settings, this does the magic.

`<span>` added is `<HTML>`, so you can add your select box in `$span.textContent`

my site is D7, not shure if works on D8, hope it helps.

RegExp

css

```
.none {display: none;}
.form-error {
  color: red;
  transition: all 1000ms ease;
}

```

javascript

```
  /*strict */
const d = document;

function validar_campos(){
  const $form = d.querySelector("#poliza-node-form");

  if($form) {

    const $inputsInteger = d.querySelectorAll(".field-type-number-integer");
    const $inputsDecimal = d.querySelectorAll(".field-type-number-decimal");
    // const $inputsTitle = d.querySelectorAll(".form-item-title");
    // const $inputsText = d.querySelectorAll(".field-type-text");
    const $formMarcaComercial = d.querySelector("#edit-field-poliza-marca-comercial");

      // create span integer fields
      $inputsInteger.forEach((input) => {
        const $span = d.createElement("span");
        $span.id = 'span-' + input.id + '-und-0-value';
        $span.textContent = "Solo acepta numeros enteros, sin signos ni comas";
        $span.classList.add("form-error", "none");
        input.insertAdjacentElement("afterend", $span);
        $span.setAttribute('tipo', 'integer');
      });

      // create span decimal fields
      $inputsDecimal.forEach((input) => {
        const $span = d.createElement("span");
        $span.id = 'span-' + input.id + '-und-0-value';
        $span.textContent = "Solo acepta numeros enteros o decimales, sin signos ni comas";
        $span.classList.add("form-error", "none");
        input.insertAdjacentElement("afterend", $span);
        $span.setAttribute('tipo', 'decimal');
      });

      // create span marca comercial field
      const $spanMarca = d.createElement("span");
      $spanMarca.id = 'span-marca';
      $spanMarca.textContent = "NO acepta comas, eliminalas";
      $spanMarca.classList.add("form-error", "none");
      $formMarcaComercial.insertAdjacentElement("afterend", $spanMarca);
      $spanMarca.setAttribute('tipo', 'marcaComercial');

      //event listener
      d.addEventListener("keyup", e=> {
        let $currentSpan;
        let $input = e.target;
        let $key = $input.value;
        let spanId = 'span-' + e.target.id;

          // *****************  MARCA COMERCIAL **************

          // verificar campo poliza marca comercial
          if(spanId == 'span-edit-field-poliza-marca-comercial-und'){
            let reText = new RegExp("[,]+");
              $currentSpan = d.getElementById('span-marca');
              // display error, remove class none
              if($currentSpan && $key !== "") {
                return reText.exec($key)
                ? $currentSpan.classList.remove("none")
                : $currentSpan.classList.add("none");
              }
          } // fin es campo marca marcaComercial

        // *****************  CAMPO NUMERICO **************

        let reInt = new RegExp("^[+-]?\\d+$");
        let reDec = new RegExp("^[+-]?[0-9]+(?:\\.[0-9]+)?$");

        $currentSpan = d.getElementById(spanId);

        // verificar que el span lo encuentre, no funciona en field collection
        if($currentSpan){
          tipo = $currentSpan.getAttribute('tipo');

          if(tipo == 'integer' || tipo == 'decimal') {

              // verificar campo INTEGER
              if(tipo == 'integer' && $key !== "") {
                return !reInt.exec($key)
                ? $currentSpan.classList.remove("none")
                : $currentSpan.classList.add("none");
              }

              // verificar campo DECIMAL
              if(tipo == 'decimal') {

                // validate run validate, starts to validate all
                let validate = 1;
                keyPressed = e.which; // 110 y 190 : decimal point
                if(keyPressed == 110 || keyPressed == 190) {
                  validate = 0; //a point, avoid run validate
                }
                  //run validate decimal
                  if(validate === 1 && $key !== "") {
                    return !reDec.exec($key)
                    ? $currentSpan.classList.remove("none")
                    : $currentSpan.classList.add("none");
                  } // fin run validacion

              } // fin campo decimal

              } // fin campo es campoEsNumerico
        } // fin if cuurent span, field colection not working

    }); // fin addEventListener

  } //fin if form

} // fin validar campos

// run functions on dom load
d.addEventListener("DOMContentLoaded", (e) => {
   validar_campos();
});

```

RegExp javascript
> 1 votes

---
Tags: nodes, forms
---


# Question
Title: Ajax table form with tabledrag only works properly AFTER first submit
So this one has me kind of stumped.

With a simple table with 4 rows, I drag one row to another position, then hit save (submit). When the ajax returns and replaces the form, the rows snap back to the original position. If I reload the page, I can see the order in which I dragged, did indeed get saved.

I reload the page, starting over, but this time start by pressing save, before dragging anything - and nothing appears to happen. But all form inputs id's and names now have a random-string, e.g. --XjidQzhH, appended to them. I can now drag a row and hit save again - and it actually works! It stays where I dragged it, also when reloading the page.

Once the form has been replaced the first time, everything seems to work as it should.

Hopefully someone knows what I'm doing wrong here - I've been rabbit-holing this for two days now and can't figure out what I'm missing.

Happens for me on Drupal 8.9.13, both with Bartik and Bootstrap themes.

Here's some reproducable code:

```
namespace Drupal\some_module_name\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;

class TestForm extends FormBase {

    public function getFormId() {
        return 'testform';
    }

    public function buildForm(array $form, FormStateInterface $form_state, $args = NULL) {

        #If form is shown for the first time, get some default values or previous values from session
        $values = $form_state->getValues();
        if (!$values){

            #Load data from session if exists, otherwise we use some temp test data.
            #Using persistant data to allow for manual reload of page, in order to demonstrate that data and sorting of rows is saved correctly.
            $names = @$_SESSION['test_data_names'];
            if (!$names){
                $values =   [
                    "names"=>[
                        ["first_name"=>"ONE",   "last_name"=>"ONE",],
                        ["first_name"=>"TWO",   "last_name"=>"TWO",],
                        ["first_name"=>"THREE", "last_name"=>"THREE",],
                        ["first_name"=>"FOUR",  "last_name"=>"FOUR",],
                    ],
                ];
            }else{
                $values = ["names"=>$names,];
            }

            $form_state->setValues($values);
        }

        #Build the simple table for the form
        $form['names'] = [
            "#type"=>"table",
            "#tree"=>true,
            '#empty' => t('No names yet.'),
            '#tabledrag' => [[
                'action' => 'order',
                'relationship' => 'sibling',
                'group' => "group-order-weight",
            ]],
            '#header' =>[
                ["data"=>t("First name"),],
                ["data"=>t("Last name(s)"),],
                ["data"=>t("Weight"),],
            ],
        ];

        #Build table rows for the name inputs
        foreach ($values['names'] as $key=>$name_data){

            $row = [
                '#attributes'=>[
                    'class' => ['draggable'],
                ],

                "first_name"=>[
                    "#type"             => "textfield",
                    "#default_value"    => $name_data['first_name'],
                ],

                "last_name"=>[
                    "#type"             => "textfield",
                    "#default_value"    => $name_data['last_name'],
                ],

                "weight"=>[
                    "#type"             => "weight",
                    "#attributes"=>[
                        'class' => ["group-order-weight"],
                    ],
                ],
            ];
            $form['names'][] = $row;

        }

        $form['save'] = [
            "#type"             => "submit",
            "#value"            => t("Save"),
            '#submit'           => ['::ajax_save_submit'],
            '#ajax'             => [
                'callback'  => '::ajax_save_submit_callback',
                'wrapper'   => 'table-wrapper',
            ],
        ];

        return $form;

    }

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

        parent::validateForm($form, $form_state);
        // return $form;

    }

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

    }

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

        $response = new AjaxResponse();
        $response->addCommand(new ReplaceCommand('#testform', $form));
        return $response;

    }

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

        #Get the names and save 'em in session so they persist if user reloads page
        $names = $form_state->getValue("names");

        $_SESSION['test_data_names'] = $names;

        $form_state->setValue("names",$names);
        $form_state->setRebuild(TRUE);

    }

}

```

# Answer
Looks like the culprit was how I was adding the name rows to the form.

```
Wrong:   $form['names'][] = $row;

```

```
Correct: $form['names'][$key] = $row;

```

The reason it was "working" after the first submit, was that the replaced form had appended token id's to the form, so the replacement could not occur on the #testform id, as it no longer existed.

Here's the updated code that works, which also includes functionality to insert new rows:

```
namespace Drupal\some_module_name\Form;

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

class TestForm extends FormBase {

    public function getFormId() {
        return 'testform';
    }

    public function buildForm(array $form, FormStateInterface $form_state, $args = NULL) {

        $values = $form_state->getValues();
        if (!$values){
            $values =   [
                "names"=>[
                    ["first_name"=>"ONE",   "last_name"=>"ONE",   ],
                    ["first_name"=>"TWO",   "last_name"=>"TWO",   ],
                    ["first_name"=>"THREE", "last_name"=>"THREE", ],
                    ["first_name"=>"FOUR",  "last_name"=>"FOUR",  ],
                ],
            ];

            $form_state->setValues($values);
        }

        #Build the simple table for the form
        $form['names'] = [
            "#type"=>"table",
            "#tree"=>true,
            '#empty' => t('No names yet.'),
            '#tabledrag' => [[
                'action' => 'order',
                'relationship' => 'sibling',
                'group' => "group-order-weight",
            ]],
            '#header' =>[
                ["data"=>t("First name"),],
                ["data"=>t("Last name(s)"),],
                ["data"=>t("Weight"),],
            ],
            '#prefix'      => "<div id='table-wrapper' class='col-sm-12'>",
            '#suffix'      => '</div>',

        ];

        #sort by weights - not necessary since we receive them in correct order in the array
        // uasort($values['names'], 'Drupal\Component\Utility\SortArray::sortByWeightElement');

        #Build table rows for the name inputs
        foreach ($values['names'] as $key=>$name_data){

            $row = [
                '#attributes'=>[
                    'class' => ['draggable'],
                ],

                "first_name"=>[
                    "#type"             => "textfield",
                    "#default_value"    => $name_data['first_name'],
                ],

                "last_name"=>[
                    "#type"             => "textfield",
                    "#default_value"    => $name_data['last_name'],
                ],

                "weight"=>[
                    "#type"             => "weight",
                    "#attributes"=>[
                        'class' => ["group-order-weight"],
                    ],
                ],
            ];
            $form['names'][$key] = $row;

        }

        $form['save'] = [
            "#type"             => "submit",
            "#value"            => t("Save"),
            '#submit'           => ['::ajax_save_submit'],
            '#ajax'             => [
                'callback'  => '::ajax_save_submit_callback',
                'wrapper'   => 'table-wrapper',
            ],
        ];

        $form['add'] = [
            "#type"             => "submit",
            "#value"            => "Insert row",
            '#submit'           => ['::ajax_insert_row_submit'],
            '#ajax'             => [
                'callback'  => '::ajax_insert_row_submit_callback',
                'wrapper'   => 'table-wrapper',
            ],
        ];

        return $form;

    }

    // public function validateForm(array &$form, FormStateInterface $form_state) {
    //     $values = $form_state->getValues();
    //  parent::validateForm($form, $form_state);
    // }

    public function submitForm(array &$form, FormStateInterface $form_state) {
        #has to be defined, otherwise we get errors like:
        #TestForm contains 1 abstract method and must therefore be declared abstract or implement the remaining methods
    }

    public function ajax_save_submit_callback(array &$form, FormStateInterface $form_state){
        return $form['names'];
    }

    public function ajax_save_submit(array &$form, FormStateInterface $form_state){
        $names = $form_state->getValue("names");

        #save logic goes here..

        $form_state->setValue("names",$names);
        $form_state->setRebuild(true);
    }

    public function ajax_insert_row_submit(array &$form, FormStateInterface $form_state){
        $names = $form_state->getValue("names");
        $names[] = ["first_name"=>"NEW",   "last_name"=>"NEW",];

        $form_state->setValue("names",$names);
        $form_state->setRebuild(true);
    }

    public function ajax_insert_row_submit_callback(array &$form, FormStateInterface $form_state){
        return $form['names'];
    }

}

```
> 3 votes

---
Tags: 8, forms, ajax
---


# Question
Title: Limit the number of nodes created from anonymous users
I am in the process of finishing the development of a small appointment scheduling system, just using node creation as an appointment. Anonymous users can thus make appointments via a node creation form.

In order to slightly limit the ease of spamming from a malicious person, I would have liked to know if it was possible to limit the number of nodes created by an anonymous user. I am well aware that it is not very effective security, but the objective is simply to limit the damage.

I found an extremely close answer to my question on Limit amount of content creation per anonymous user.

```
/**
 * Implements hook_node_access
 */
function MY_MODULE_node_access($node, $op, $account) {

    // Get node type, can be string or node object
    $type = is_string($node) ? $node : $node->type;
    if ($type == 'type_of_interest') {
      // Check flood limit for anon users node creation
      if ($op == 'create' && user_is_anonymous()) {
        // Ensure checked only once per page request, node/add path only
        $access_checked = &drupal_static(__FUNCTION__);
        if(empty($access_checked) && arg(0) == 'node' && arg(1) == 'add') {
          // Record the access check for drupal_static
          $access_checked = TRUE;
          // Check create_node limit of 2 per 12 hours
          // Note, you could make the threshold and window config variables.
          if(!flood_is_allowed('create_node', 2, 43200)){
            drupal_set_message(t('There is a node creation limit of 2 nodes per 12 hours. Please try again later.'), 'error');
            return NODE_ACCESS_DENY;
          }else{
            // Register the node create event to test against for 48 hours
            // Note, you could move this somewhere else to ensure the node is actually saved, e.g hook_node_insert
            flood_register_event('create_node', 172800);
          }
        }
      }
    }

  return NODE_ACCESS_IGNORE;

}

```

Unfortunately, this is a code block for Drupal 7.

Would the code be similar for Drupal 8?
I am not very used to programming with Drupal 8.

# Answer
Drupal 8 invokes a specific hook when an entity is created: `hook_entity_create_access()`. Apart that, Drupal 8 code has been converted to a more OOP code, and it uses methods where before it used functions.

The following code is the one I would use for Drupal 8.

```
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Session\AccountInterface;

/**
 * Implements hook_ENTITY_TYPE_create_access().
 */
function mymodule_node_create_access(AccountInterface $account, array $context, $entity_bundle) {
  $flood = \Drupal::flood();
  if ($entity_bundle == 'type_of_interest' && $account->isAnonymous()) {
    if (!$flood->isAllowed('create_node', 2, 43200)) {
      return AccessResult::forbidden('There is a node creation limit of 2 nodes per 12 hours.');
    }
    $flood->register('create_node', 43200);
  }
  return AccessResult::neutral();
}

```
> 4 votes

# Answer
You can try the Entity Limit module. I've used it in the past. I believe you can set the number of nodes created with the anonymous user role. You can install it with

```
composer require drupal/entity_limit

```

And then enable the module.

As of this writing, its not available for Drupal 9 though.

https://www.drupal.org/project/entity\_limit
> 0 votes

---
Tags: 8, nodes
---


# Question
Title: How to set a field value from parent template in Twig?
I have `node--article--teaser.html.twig` which calls `image-formatter.html.twig` to printout the `field_image`. Everything works fine except that I don't want the `field_image` itself to be a linked. i.e `image-formatter.html.twig` should not find a `url` to only print an `img` tag.

I have tried the following in `node--article--teaser.html.twig`:

```
{#{ content }#}
  {% set content.field_image[0]['#url'] = null %}{% endset %}
  {{ content.field_image }}
    {{ label }}

```

The second line causing the following error:

> Twig\_Error\_Syntax: Unexpected token "punctuation" of value "." ("end of statement block" expected) in "themes/saidbakr/templates/node--article--teaser.html.twig" at line 78. in Twig\_TokenStream-\>expect() (line 87 of vendor\twig\twig\lib\Twig\TokenStream.php).

While `{{ content.field_image[0]['#url'] }}` prints the url without any problems!

The `image-formatter.html.twig` code is as the following:

```
{% if url %}
  <a href="{{ url }}">{{ image }}</a>
{% else %}
  {{ image }}
{% endif %}

```

There is no any suggestion from twig debug about any alternative template for `image-formatter.html.twig`

Also using Theme Hook `image_formatter` as `saidbakr_preprocess_image_formatter` will not be an option because it is not possible to know the view context, i.e teaser or full, or content types to restrict this interaction for articles type. I think that the most simple way is from Twig, but how?!

# Answer
> 0 votes
There should be no reason to change any code for this, as you are using the default image formatter. You should be able to remove the link by going to `/admin/structure/types/manage/article/display/teaser` and change your field's formatter settings using the cogwheel-button and then select "None" for "Link image to".

Should the `teaser` view mode configuration page not be available, got to `/admin/structure/types/manage/article/display` and activate its checkbox on the far bottom of the page, then save and try again.

# Answer
> -2 votes
I think that you have to think otherwise but this code will help you :

node--article--teaser.html.twig {{ content.field\_image | merge ({ 'url' : value }) }}

---
Tags: theming, 8
---


# Question
Title: How do I programmatically translate a string?
I am working on one custom module to alter the fields labels of user login form for visitors. I created a module to alter the labels in the user login form. The French language is enabled on the setup.

I am trying to translate the field labels on the user login form to French. I am using the following approach in a module.

I have included the namespace with `use Drupal\Core\StringTranslation`. After that, I have debugged the `$form` variable inside `hook_form_alter()`.

From this debugged variable, I am trying to translate the filed label 'E-mail' to French.

```
function hook_form_alter ({here goes all the parameters of alter hooks}){
  $form['name']['#title'] = t('E-mail');
}

```

I am not able to see the translated Email field label on the user login form.

Am I missing anything? What should I do to translate the strings using `hook_form_alter()`?

# Answer
you have find and add your translation string here : admin/config/regional/translate
> 0 votes

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


# Question
Title: How do I create a filter based on the current user's role in a search_api based view?
I would like to filter a search\_api based view using the current user's roles as input. I don't see a contextual filter for the current user in my search\_api based content view at all. How can I accomplish something like the "Published or has role" feature of this contrib module? This one seems to only work on non-search\_api views.

# Answer
> 0 votes
Thanks to @prkos reminding me that this is a regular node access setup issue. This was solved when I found this issue in the search\_api queue about incompatibility with "View All Unpublshed content"

---
Tags: 8, views, search
---


# Question
Title: How can I extend the webform #states conditions to save a custom set of conditions for webform fields?
I need to store sets of conditions on the webform field level (these would then be evaluated according to the field's submitted values within a custom submit callback).

I would like these conditions to be editable on the webform field level just like #states conditions that honour the field type for value ranges, set values, boolean etc:

No new custom state is necessarily needed here as this new type of condition would not be related to interacting fields. I'm merely looking for a way to expand webforms and add a custom field to each element that will allow adding (on the UI) sets of conditions based on its value(s). These sets would be part of the field's properties and accessible in a validation / submit callback.

# Answer
> 0 votes
There's a hook for that: hook\_webform\_element\_configuration\_form\_alter(), webform\_example\_element\_properties.module provides a simple example too.

---
Tags: 8, webforms
---


title: ctools module keeps getting unloaded?

text:
# Question
Title: ctools module keeps getting unloaded?
I keep getting this error while trying to access content using The Course module.

> Call to undefined function `ctools_get_plugins()`

To my understanding this means the ctools module is not loaded, which is why the Course module cannot access this function. When I run `drush cc all` on the background and refresh, it works, but only once.

I've no idea what to do about it.

# Answer
> 2 votes
`ctools_get_plugins()` isn't a function defined in a module file, but in the includes/plugin.inc file in the directory containing the *Chaos tools* module.

It's not the module that isn't loaded. It's the module that is calling the function that should first load the file containing that function, for example adding the following line before the function call.

```
ctools_include('plugins');

```

This is the solution used, for example, in ctools\_get\_plugins() is called without including plugins.inc.

What you can do is checking the issue queue for the module that is causing that error, and see if the issue has been already reported and fixed. If it's so, you should update the module and use that release.

For what I can see, the `CourseObject::access()` method of the Course module does include the file before the function, and so do other methods in that file, which is the only file using `ctools_get_plugins()` in the Course module. (I did check with `git grep --fixed-strings 'ctools_get_plugins'` in the directory containing a copy of the 7.x-2.x branch.)
Probably the module causing the issue is another one.

---
Tags: 7, chaos-tool-suite
---


# Question
Title: Circular dependency on base fields' module
I have 3 custom modules that provide custom entities with all fields defined as base fields.

**Contact module** \- provides a contact entity(it could be one of the type: a person or email or phone etc) that has a store entity reference field.

**Store module** \- provides a store entity that has a region entity reference field.

**Region module** \- provides geographical region entity that has a contact entity reference field.

So

1. Contact depends on Store
2. Store depends on Region
3. Region depends on Contact

Each definition would look like this:

```
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
  ...
   $fields['store'] = BaseFieldDefinition::create('entity_reference')
    ->setLabel(t('Store'))
    ->setDescription(t('Select the store the contact is part of.'))
    ...
    ->setSetting('target_type', 'custom_store')
    ...
}

```

As these modules provide more than just entity (and custom field API plugins), We can't combine them together.

when trying to install the modules, getting an unknown entity target\_type error.

# Answer
Since the dependency is purely on the reference field, we could add the `target_type` later. For example, setting the `target_type` at the bundleFieldDefinition wouldn't throw the install's error and set the right target\_type.

So the `baseFieldDefinitions` would be

```
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
  ...
  $fields['store'] = BaseFieldDefinition::create('entity_reference')
    ->setLabel(t('Store'))
    ->setDescription(t('Select the store the contact is part of.'))
    ...
}

```

And the `bundleFieldDefinitions` would be

```
public static function bundleFieldDefinitions(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) {
  $fields = parent::bundleFieldDefinitions($entity_type, $bundle, $base_field_definitions);
  $fields['store'] = clone $base_field_definitions['store'];
  $fields['store']->setSetting('target_type', 'custom_store')
  ...
  return $fields;
}

```
> 1 votes

---
Tags: 8, entities
---


# Question
Title: Trigger Emails/Handlers on Webform submissions update
Is there a way to trigger the `Emails/Handlers`, When webform submission has been updated. I'm using `webform_mailchimp` as a handler. When I'm submitting a new record, handler is working fine and I can receive the data into my Mailchimp account. I have a function to update the submission via code. But it doesn't trigger, when I update the same record. Following is my code to update the webform submission:

```
$webform = Webform::load('my_form');
$is_open = WebformSubmissionForm::isOpen($webform);
$sid = 2;

if ($is_open === TRUE) {
    // Load submission
    $webform_submission = WebformSubmission::load($sid);

    // Modify submission values
    $webform_submission->setElementData('preferred_date', $form_state->getValue('preferredDate'));
    $webform_submission->setElementData('preferred_time', $form_state->getValue('preferredTime'));

    // Validate submission.
    $errors = WebformSubmissionForm::validateWebformSubmission($webform_submission);

    // Check there are no validation errors.
    if (!empty($errors)) {
        drupal_set_message(t('Failed! Please contact the administrator.'), 'error');
    }
    else {
        // Submit values and get submission ID.
        $webform_submission = WebformSubmissionForm::submitWebformSubmission($webform_submission);
        drupal_set_message(t("Success! ".$webform_submission->id()), 'status');
    }
}

```
1. Is there a way to trigger the handler through code(programmatically)?
2. Is there a default configuration in the CMS?

Thanks guys,

# Answer
The below code is untested but should point you in the right direction.

```
// Get a handler.
$webform_submission = WebformSubmission::load(WEBFORM_ID)
$handlers = $webform_submission->getWebform()->getHandlers();
$handler = $handlers[HANDLER_ID];

// Invoke submit form.
@see \Drupal\webform\Plugin\WebformHandlerInterface::submitForm
$form = []; // Might need to get the $form.
$form_state = new FormState(); // Might need to be build the form state.
$handler->submitForm($form, $form_state, $webform_submission);

```

@see \Drupal\webform\Entity\Webform::invokeHandlers

@see \Drupal\webform\Entity\WebformSubmission::invokeWebformHandlers
> 1 votes

# Answer
```
  $webform_submission = \Drupal\webform\Entity\WebformSubmission::load(submission_id);
  /** @var \Drupal\webform\Entity\Webform $webform */
  $webform = $webform_submission->getWebform();
  /** @var \Drupal\webform\Plugin\WebformHandler\EmailWebformHandler $handler */
  $handler = $webform->getHandler(handler_id);
  $message = $handler->getMessage($webform_submission);
  $handler->sendMessage($webform_submission, $message);

```

In case the above solution doesn't alternative can be this as well.
> 2 votes

# Answer
If the Mailchimp handler supports it, you can select the option to fire the handler "...when submission is updated" within the handler settings.
> 0 votes

---
Tags: 8, hooks, webforms, emails
---


# Question
Title: Can't get annotated plugin discovery to fire
I've checked Debug plugin discovery and tried restarting Apache and setting $settings\['class\_loader\_auto\_detect'\] = FALSE; in settings.php, with no change.

I have a custom module. Setting a breakpoint anywhere that I know **should** fire works, including the config form for my module.

The module defines a plugin of the annotated type, and I cannot get any code in the plugin manager to fire. I also note that I've used a contrib module as a model, and cannot get code in its plugin manager to break either.

If I refer to the module as 'x', in x/src/XManager.php I have:

```
namespace Drupal\x;

use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\x\Annotation\X;

/**
 * Class XManager
 *
 * @package \Drupal\x
 */
class XManager extends DefaultPluginManager {

  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
    $subdir = 'Plugin/X';
    $plugin_interface = XInterface::class;
    $plugin_definition_annotation_name = X::class;
    parent::__construct($subdir, $namespaces, $module_handler, $plugin_interface, $plugin_definition_annotation_name);
    $this->alterInfo('x_info');
    $this->setCacheBackend($cache_backend, 'x_plugins');
  }

}

```

and in x/src/Annotation/X.php

```
namespace Drupal\x\Annotation;

use Drupal\Component\Annotation\Plugin;

/**
 * @see plugin_api
 *
 * @Annotation
 */
class X extends Plugin {

  /**
   * The plugin ID.
   *
   * @var string
   */
  public $id;

  /**
   * The plugin label.
   *
   * @var \Drupal\Core\Annotation\Translation
   *
   * @ingroup plugin_translatable
   */
  public $label;

  /**
   * The plugin field type IDs.
   *
   * @var array
   */
  public $field_type_ids = [];

}

```

and in x/src/Plugin/X/XTest.php

```
namespace Drupal\x\Plugin\XTest;

/**
 * Class XTest.
 *
 * @X(
 *   id = "x_test",
 *   label = @Translation("X Test"),
 *   field_types = {
 *     "text",
 *     "text_long",
 *     "text_with_summary",
 *   },
 *
 * )
 */
class XTest {

}

```

# Answer
To debug a plugin manager use it as a service and get the discovered plugins:

```
$type = \Drupal::service('plugin.manager.archiver');
$plugin_definitions = $type->getDefinitions();

```

This gets the plugin definitions from cache. If you want to rerun the discovery process because you have added or modified a plugin annotation clear the entire Drupal cache or only the plugin manager specific one:

```
$type->clearCachedDefinitions();

```

Reference: https://www.drupal.org/docs/drupal-apis/plugin-api/creating-your-own-plugin-manager
> 1 votes

---
Tags: plugins, 9
---


# Question
Title: How can I solve form "errors has been found" with custom Ajax dependant field and other Ajax fields?
I've altered a node\edit form where there are two taxonomy refence fields with a select widget.

I've added a custom ajax callback on the first field, in order to populate the options of the second one only with a subset of values.

This works fine, the problem is the presence of another ajax field - e.g. a image field.

If the user set the "select fields" first and then try to add an image, the ajax of the image field always returns "1 error has been found" on the second select field. Debugging that part kinda makes sense, as the form\_state values only contains at that point the values of "image field" and thus the 2nd select field is left only with "\_none" value.

With this patch the problem is somewhat better, as the ajax on the image fields works. However if the user populate the select first and the image field after, the form submit validation returns again the "1 error has been found" on the second select field, with that field populated only by "\_none" value, while the first select field still contains the correct selected value.

I don't know if this is a known bug of D9, if there are other relative patches I couldn't find, or if I'm doing something wrong in my custom code - attached below.

Thanks for any help.

```
<?php

/**
 * Implements hook_form_FORM_ID_alter().
 */
function my_module_form_node_my_node_form_alter(&$form, $form_state, $form_id)
{
    my_module_node_custom_form_alter($form, $form_state);
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function my_module_form_node_my_node_edit_form_alter(&$form, $form_state, $form_id)
{
    my_module_node_custom_form_alter($form, $form_state);
}

function my_module_node_custom_form_alter(&$form, $form_state)
{
     $form['my_first_field']['widget']['#ajax'] = [
        'callback' => 'my_module_my_first_field_callback',
        'wrapper' => 'my-second-field-wrapper',
        'event' => 'change',
        'progress' => [
            'type' => 'throbber',
        ],
     ];
     $form['my_first_field']['#limit_validation_errors'] = [];

     $selected_value = !empty($form_state->getValue('my_first_field')) ? $form_state->getValue('my_first_field')[0]['target_id'] : null;
     if(empty($selected_value)) {
         if(!empty($form["my_first_field"]["widget"]["#default_value"])) {
             $selected_value = $form["my_first_field"]["widget"]["#default_value"][0];
         }
     }

    my_module_set_my_second_field($form, $selected_value);
}

function my_module_set_my_second_field(&$form, $selected_value)
{
     $form['my_second_field']['#limit_validation_errors'] = [];
     $options = my_module_get_my_second_field_options($selected_value);

     $form['my_second_field']['#prefix']='<div id="my-second-field-wrapper">';
     $form['my_second_field']['#suffix']='</div>';
     $form['my_second_field']['widget']['#options'] = $options;
     $form['my_second_field']['#limit_validation_errors'] = [];
}

function my_module_get_my_second_field_options($selected_value, $vid = 'my_vid')
{
    if(empty($selected_value)) {
        return ['_none' => t("- Select 'my first field' first -")];
    }

    $options = ['_none' => t("- Select a value -")];

    $terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree($vid, 0, null, true);
    foreach($terms as $term) {
        if($term->field_to_check->target_id == $selected_value) {
            $options[$term->id()] = $term->getName();
        }
    }

    return $options;
}

function my_module_my_first_field_callback(array &$form, \Drupal\Core\Form\FormStateInterface $form_state)
{
    return $form['my_second_field'];
}

```

edit:

as I had to deliver something usable, as workaround I've set up a check on the triggering\_element: it does the logic if that is empty (case of page load\submit) or has the name of "my\_field", otherwise skip it. Not probably the correct or most elegant way to do it, but it seems to work so for now I'm doing this way. Kudos to anyone who know and explains how to do it better.

Updated relative code:

```
function my_module_node_custom_form_alter(&$form, $form_state)
{
     $form['my_first_field']['widget']['#ajax'] = [
        'callback' => 'my_module_my_first_field_callback',
        'wrapper' => 'my-second-field-wrapper',
        'event' => 'change',
        'progress' => [
            'type' => 'throbber',
        ],
     ];

     if( !isset($triggering_element) || (isset($triggering_element['#field_name']) && $triggering_element['#field_name'] === 'my_first_field')) {
      $selected_value = !empty($form_state->getValue('my_first_field')) ? $form_state->getValue('my_first_field')[0]['target_id'] : null;
      if(empty($selected_value)) {
          if(!empty($form["my_first_field"]["widget"]["#default_value"])) {
              $selected_value = $form["my_first_field"]["widget"]["#default_value"][0];
          }
      }

      my_module_set_my_second_field($form, $selected_value);
    }
}

```

# Answer
> 0 votes
I had to work again on this part of my site. This time to check a dependant field inside a paragraph, so I had the same problem - empty form\_state values - in a more complicated situation.

In the end what worked was to switch to form\_state-\>getUserInput, which seems to always contain the updated data. Please note, as stated by the docs, this array insn't sanitized nor validated so it could be vulnerable to user injection - however my case was safe enough, as only integer are allowed values.

Here the code I'm using to get the 1st select value, from node and paragraph alter hook:

```
  $user_input = $form_state->getUserInput();

  //check to avoid user injection, my case was a taxonomy term select, so allowed values are integer
  $my_field_value = isset($user_input["my_field"]) ? intval($user_input["my_field"]) : NULL;

  // in case of edit node, when loading the form user input is empty -> get the initial value
  if (empty($my_field_value) && !empty($form["my_field"]["widget"]["#default_value"])) {
    $my_field_value = $form["my_field"]["widget"]["#default_value"][0];
  }

  if(empty($my_field_value)) {
    $my_field_value = '_none';
  }

  return $my_field_value;

```

---
Tags: forms, ajax, 9
---


# Question
Title: How do I validate an entity before programmatically saving it?
I have various functions/methods that programmatically create entities (nodes). I just noticed that invalid data has been getting stored in the db though these functions (invalid in that it doesn't meet the rules defined for that field).

How do I validate the data before $entity-\>save()?

I have seen documentation saying that I simply need to do $entity-\>validate() and then count if there are any errors; but I have marked a few fields as required and then left them empty in my API function and $entity-\>validate()-\>count() is always 0.

What am I missing?

# Answer
> 4 votes
Looking the api it should be simple:

```
$violations = $entity->validate();
  if ($violations->count() > 0) {
    // Validation failed.
  }

```

---
Tags: 8, entities
---


# Question
Title: Change the path used by a route dynamically
Taxonomy terms have a canonical path of `/taxonomy/term/{taxonomy_term}` by default, which displays a View with content tagged with that term.

For certain taxonomies I would like to use a different view, dependent on the taxonomy bundle, so I can't just override the path entirely.

For example, **"article"** tags should link to `/articles/{taxonomy_term}`, **"product"** tags to `/products/{taxonomy_term}`.

My idea is to override the `entity.taxonomy_term.canonical` route adding some kind of callback that generates the path dynamically based on the taxonomy\_term. Is this possible?

# Answer
I don't believe that there is any need for custom route manipulation in your case, and besides, while I am not entirely sure, I don't think that something like what you describe, would actually work, since then you might have issues with the view's contextual filtering.

I believe the TVI Module is the answer to your problem. It can be used to specify the view/views that will be used in the vocabularies of your choice.
> 2 votes

# Answer
I used a path processor service to rewrite the outgoing paths for taxonomy terms that belong to a specific bundle.

Service definition:

```
services:
  my_module.path_processor:
    class: Drupal\my_module\Path\PathProcessor
    tags:
      - { name: path_processor_outbound, priority: 50 }

```

The processor class:

```
<?php

namespace Drupal\my_module\PathProcessor;

use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Url;
use Symfony\Component\HttpFoundation\Request;

/**
 * Path processor to redirect taxonomy paths to a view with contextual arg.
 */
class TaxonomyPathProcessor implements OutboundPathProcessorInterface {

  /**
   * {@inheritdoc}
   */
  public function processOutbound($path, &$options = [], Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) {
    if (isset($options['entity_type'], $options['entity']) && $options['entity_type'] === 'taxonomy_term') {
      /** @var Drupal\taxonomy\Entity\Term $entity */
      $entity = $options['entity'];
      if ($entity->bundle() === 'article_tags') {
        $url = Url::fromRoute('view.articles.page_1', ['arg_0' => $entity->id()]);
        return $url->toString();
      }
    }

    return $path;
  }

}

```
> 2 votes

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


title: Page titles are wrapped in a span tag

text:
# Question
Title: Page titles are wrapped in a span tag
On a site we are working on I was asked why the page titles are wrapped with a span tag. We are not using a base theme.

I checked everywhere I could think of, but came up short.

In page-title.html.twig:

```
{#
/**
 * @file
 * Theme override for page titles.
 *
 * Available variables:
 * - title_attributes: HTML attributes for the page title element.
 * - title_prefix: Additional output populated by modules, intended to be
 *   displayed in front of the main title tag that appears in the template.
 * - title: The page title, for use in the actual content.
 * - title_suffix: Additional output populated by modules, intended to be
 *   displayed after the main title tag that appears in the template.
 */
#}
{{ title_prefix }}
{% if title %}
  <h1{{ title_attributes }}>{{ title }}</h1>
{% endif %}
{{ title_suffix }}

```

At this point, `title` is a render array. It is passed in as a Markup object. Where is this happening?

The span doesn't hurt anything, but at this point I just want to find where that is occurring. Is there an option to switch that off? I can preprocess\_page\_title and strip tag it, but unless there is a good reason, I would rather not have it at all.

# Answer
> 3 votes
This is probably not the case for all pages? I think what you see is the rendered result of an entity field like for example this node field template:

*field--node--title.html.twig:*

```
<span{{ attributes }}>
  {%- for item in items -%}
    {{ item.content }}
  {%- endfor -%}
</span>

```

In other cases when the title is not bubbled up from the rendered main content and it is taken from the route definition there shouldn't be a span tag unless there is a title callback placing such a tag.

---
Tags: theming, 9
---


# Question
Title: Is it possible to change the Taxonomy Parent Term's select list to be a Hierarchical select list?
The *Taxonomy relations \> Parent terms* select list is lengthy to look through so ideally we'd like a Hierarchical select list. We already use the *Client-side hierarchical select* module for other fields, but it doesn't appear to be designed for this particular field. Is there a way for me to programmatically apply the module to this field by altering the form in `hook_form_alter()`?

>

We've considered using the *Chosen* module but an autocomplete isn't exactly the functionality we're hoping for. I also understand there's the *Simple hierarchical select* module, but since we already use CSHS, I wasn't sure if it was advisable to have both CSHS and SHS. For the time being we've changed `['#multiple'] = false;` to turn it into a basic drop-down.

# Answer
Chosen's autocomplete is just a bonus. You can have your normal hierarchy using chosen and it is much more accessible than other options you have. I would at least give it a go.

Now considering SHS and CSHS.. they are quite similar and I would not recommend using them side by side.

The main reason for that is that they tackle the same issue with a different approach and -best case scenario- one's functionallity would overwrite the other's.

Now as far as I know they do indeed have compatibility issues as stated in CSHS's module page (at the time this was posted)

> It is possible to use the CSHS widget as an exposed filter for taxonomy term reference fields. Please note that this feature might break if SHS module is enabled on your site.

While I think I find CSHS's approach more usable, I would stick with SHS and chosen in your case since you mentioned that your vocabularies are large and CSHS, being client side and all, is more susceptible to performance issues, since it

> \[...\], but in contrast to those modules the whole taxonomy tree is already present at page-load and the whole processing is done "client-side".

Now, to give the devil it's due, both modules have performance issues with very large trees and it is only natural and understandable, but SHS seems like a more stable choice.

Lastly, to achieve what you described, I would prefer a more "Drupal 8" way and would create a new Field Formatter plugin for the entity reference field you are using and attach this to the form in the form display tab of you entity.

To summarize, I would but not use SHS in conjunction with CSHS, and although you have to try it our yourself, the combination of SHS and Chosen seems to me like the obvious choise for your case. Try overriding the entity reference field's field formatter to add the taxonomy parent name to your options if you prefer a more custom approach.
> 0 votes

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


# Question
Title: How to fix this error about the Node class not found?
I've installed Drupal 8, I've created a PHP file to read a XML file and insert some items, but I'm getting this error.

> Fatal error: Uncaught Error: Class 'Drupal\node\Entity\Node' not found in C:\xampp\htdocs\drupal8\migracion.php:12 Stack trace: #0 {main} thrown in C:\xampp\htdocs\drupal8\migracion.php on line 12

This is the code I am using.

```
define('DRUPAL_DIR', __DIR__);
use \Drupal\Core\DrupalKernel;
use \Symfony\Component\HttpFoundation\Request;
use \Drupal\node\Entity\Node;
use \Drupal\file\Entity\File;
use \Drupal\pathauto\PathautoState;

$fileContentXML = file_get_contents(DRUPAL_DIR . "/sites/default/files/migration/data.xml");
$xml_file = simplexml_load_string($fileContentXML);
foreach ($xml_file->channel->item as $item) {
  $node = Node::create(['type' => 'article']);
  $nid = trim($item->nid);
  $node->set('nid', $nid);
  $node->set('uid', 1);
  $node->enforceIsNew();
  $node->save();
}

```

How can I fix this error?

# Answer
> 2 votes
The code shown in the question doesn't bootstrap Drupal, nor does it load the Node module. An external script needs to do that, differently from a module.

I usually use the authenticate.php file as guideline for writing code for an external script file that needs to bootstrap Drupal.

```
use Drupal\Core\DrupalKernel;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\node\NodeInterface;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Route;

// Change the directory to the Drupal root.
// Edit the following line to make it point to the directory containing the autoload.php file.
chdir('.');
$autoloader = (require_once 'autoload.php');

try {
  $request = Request::createFromGlobals();
  $kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod');
  $kernel->boot();

  // A route is required for route matching.
  $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('<none>'));
  $request->attributes->set(RouteObjectInterface::ROUTE_NAME, '<none>');
  $kernel->preHandle($request);

  // Ensure our request includes the session if appropriate.
  if (PHP_SAPI !== 'cli') {
    $request->setSession($kernel->getContainer()->get('session'));
  }
} catch (HttpExceptionInterface $e) {
  $response = new Response('', $e->getStatusCode());
  $response->prepare($request)->send();
  exit;
}

\Drupal::moduleHandler()->addModule('system', 'core/modules/system');
\Drupal::moduleHandler()->addModule('node', 'core/modules/node');
\Drupal::moduleHandler()->load('system');
\Drupal::moduleHandler()->load('node');

$xml = simplexml_load_string(file_get_contents('./sites/default/files/migration/data.xml'));
foreach ($xml->channel->item as $item) {
  $node = \Drupal::entityManager()->getStorage('node')->create(
    'type' => 'article',
    'nid' => (int)$item->nid,
    'uid' => 1,
    'status' => NodeInterface::PUBLISHED
  ]);
  if ($node) {
    // The following line isn't necessary, as create() already calls enforceIsNew().
    // $node->enforceIsNew();
    $node->save();
  }
}

```

As side note, Drupal 8 has a migration system. Instead of using an external script that bootstraps Drupal, it would be better to use that. Depending on the migration, that may require to write a custom module. Even in that case, this route is preferable, as most of the migration code has been already written.

---
Tags: 8, nodes
---


# Question
Title: Entity revision metadata key missing after upgrading
After upgrading a site to Drupal 9, I started seeing the following error when visiting the site:

> Drupal\Core\Entity\Exception\UnsupportedEntityTypeDefinitionException: The entity type banner does not have an "revision\_created" entity revision metadata key. in Drupal\Core\Entity\RevisionableContentEntityBase::revisionLogBaseFieldDefinitions() (line 35 of core/lib/Drupal/Core/Entity/RevisionLogEntityTrait.php).

The site has a custom entity, banner.

I rolled back to Drupal 8.9, created a module to make the same entity revisionable, and added the following code in module.install.

```
/**
 * Implements hook_update_dependencies().
 */
function mirum_entity_revision_update_dependencies() {
  // The update function that adds the status field must run after
  // content_translation_update_8400() which fixes NULL values for the
  // 'content_translation_status' field.
  if (\Drupal::moduleHandler()->moduleExists('content_translation')) {
    $dependencies['mirum_entity_revision'][8400] = [
      'content_translation' => 8400,
    ];

    return $dependencies;
  }
}

/**
 * Add the 'published' and revisionable metadata fields to banner.
 */
function mirum_entity_revision_update_8400() {
  $definition_update_manager = \Drupal::entityDefinitionUpdateManager();

  // Add the published entity key and revisionable metadata fields to the
  // banner entity type.
  $entity_type = $definition_update_manager->getEntityType('banner');
  $entity_type_id = $entity_type->id();

  $entity_keys = $entity_type->getKeys();
  $entity_keys['published'] = 'status';
  $entity_type->set('entity_keys', $entity_keys);

  $revision_metadata_keys = [
    'revision_user' => 'revision_user',
    'revision_created' => 'revision_created',
    'revision_log_message' => 'revision_log_message'
  ];
  $entity_type->set('revision_metadata_keys', $revision_metadata_keys);

  $definition_update_manager->updateEntityType($entity_type);

  // Add the status field.
  $status = BaseFieldDefinition::create('boolean')
    ->setLabel(t('Publishing status'))
    ->setDescription(t('A boolean indicating the published state.'))
    ->setRevisionable(TRUE)
    ->setTranslatable(TRUE)
    ->setDefaultValue(TRUE);

  $has_content_translation_status_field = \Drupal::moduleHandler()->moduleExists('content_translation') && $definition_update_manager->getFieldStorageDefinition('content_translation_status', $entity_type_id);
  if ($has_content_translation_status_field) {
    $status->setInitialValueFromField('content_translation_status');
  }
  else {
    $status->setInitialValue(TRUE);
  }
  $definition_update_manager->installFieldStorageDefinition('status', $entity_type_id, $entity_type_id, $status);

  // Add the revision metadata fields.
  $revision_created = BaseFieldDefinition::create('created')
    ->setLabel(t('Revision create time'))
    ->setDescription(t('The time that the current revision was created.'))
    ->setRevisionable(TRUE);
  $definition_update_manager->installFieldStorageDefinition('revision_created', $entity_type_id, $entity_type_id, $revision_created);

  $revision_user = BaseFieldDefinition::create('entity_reference')
    ->setLabel(t('Revision user'))
    ->setDescription(t('The user ID of the author of the current revision.'))
    ->setSetting('target_type', 'user')
    ->setRevisionable(TRUE);
  $definition_update_manager->installFieldStorageDefinition('revision_user', $entity_type_id, $entity_type_id, $revision_user);

  $revision_log_message = BaseFieldDefinition::create('string_long')
    ->setLabel(t('Revision log message'))
    ->setDescription(t('Briefly describe the changes you have made.'))
    ->setRevisionable(TRUE)
    ->setDefaultValue('')
    ->setDisplayOptions('form', [
      'type' => 'string_textarea',
      'weight' => 25,
      'settings' => [
        'rows' => 4,
      ],
    ]);
  $definition_update_manager->installFieldStorageDefinition('revision_log_message', $entity_type_id, $entity_type_id, $revision_log_message);

  // Uninstall the 'content_translation_status' field if needed.
  $database = \Drupal::database();
  if ($has_content_translation_status_field) {
    // First we have to remove the field data.
    $database->update($entity_type->getDataTable())
      ->fields(['content_translation_status' => NULL])
      ->execute();

    // A site may have disabled revisionability for this entity type.
    if ($entity_type->isRevisionable()) {
      $database->update($entity_type->getRevisionDataTable())
        ->fields(['content_translation_status' => NULL])
        ->execute();
    }

    $content_translation_status = $definition_update_manager->getFieldStorageDefinition('content_translation_status', $entity_type_id);
    $definition_update_manager->uninstallFieldStorageDefinition($content_translation_status);
  }

  return t('@entity_type_label has been converted to revisionable and publishable.', ['@entity_type_label' => $entity_type->getLabel()]);
}

```

I activated the module and upgraded again to Drupal 9, but the error persists.

I am using Drush and Composer but because of this error with the entity, none of the Drush commands work.

For example, running `drush cr` returns the following error.

> The entity type banner does not have an "revision\_created" entity revision metadata key.

# Answer
You have set the missing metakey already in the update hook, but this is not stored in the entity definitions permanently, this is only temporary for the update process:

```
  $revision_metadata_keys = [
    'revision_user' => 'revision_user',
    'revision_created' => 'revision_created',
    'revision_log_message' => 'revision_log_message'
  ];
  $entity_type->set('revision_metadata_keys', $revision_metadata_keys);

```

For this you need to include them in the entity annotations:

*/mymodule/src/Entity/Banner.php*

```
 * @ContentEntityType(
 *   id = "banner",
 *   ...
 *   revision_metadata_keys = {
 *     "revision_user" = "revision_user",
 *     "revision_created" = "revision_created",
 *     "revision_log_message" = "revision_log_message",
 *   },

```

Then the command `drush cr` should update the entity definition without error.
> 4 votes

---
Tags: entities, upgrading, 9
---


title: How do I place a block with Twig Tweak?

text:
# Question
Title: How do I place a block with Twig Tweak?
I've placed a view block via Twig Tweak in page-front.html.twig before and it worked fine. That was on Drupal 8, but now I'm on Drupal 9. This's what I would have done in Drupal 8.

```
<div id="library_carousel">{{ drupal_view('library_carousel ', 'block_carousel') }} </div>

```

This is what I gather I'm supposed to do in Drupal 9.

```
<div id="library_carousel">
    <div>{{ drupal_view('library_carousel ', 'block_carousel') }} </div>
</div>

```

It's not rendering anything. In Views, it shows the view machine name as 'library\_carousel'. Inside the view, it shows the block machine name as 'block\_carousel'.

I've confirmed that if I place the block via the Drupal interface, it does render correctly. I just can't get it to render via Twig Tweak. And yes, I've flush all the caches multiple times. Also, I have other things in the page-front twig that render correctly so I know the template is rendering in general.

Why isn't working?

# Answer
> 2 votes
There's a section in the docs explaining how to print blocks even if they are disabled. Maybe that's your problem.

> Disabled blocks won't be printed unless you suppress access control as follows.
>
> ```
> {{ drupal_entity('block', 'block_id', check_access=false) }}
>
> ```

Is working on D9 for me.

---
Tags: views, 9
---


# Question
Title: Upgrade Status module reports PHPUnit\Framework\TestCase is not an interface
Our site is running Drupal 8.8.13 and we want to move to Drupal 9. I've installed the Upgrade Status module v3.3. When I scan a module, like Admin Toolbar, it reports the problem:

```
web/modules/contrib/admin_toolbar/admin_toolbar_search/admin_toolbar_search.module

Internal error: PHPUnit\Framework\TestCase is not an interface
Run PHPStan with --debug option and post the stack trace to:
https://github.com/phpstan/phpstan/issues/new?template=Bug_report.md

```

Lots of other modules have the same problem.

Is this warning legit? or is there a problem with the configuration of Upgrade Status module?

# Answer
Are you using the latest versions?

Upgrade Status recently released a new version (3.3) a couple days ago, and it seems something is wrong with PHPStan.

Testing on Drupal 8.9.13 with Upgrade Status 3.3 and Admin Toolbar 3.0, after scanning Admin Toolbar, I get only this issue:

```
web/PHPStan failed  0   PHP API deprecations cannot be checked. Reason: Array ( )

```

Which is linked to the bug report listed above.

In any case, regarding your error, if you go to the Admin Toolbar module page on drupal.org, you'll see that both the 3.x and 2.x versions are marked as compatible with Drupal 9.

In general, it's safe to trust modules that say they are compatible with Drupal 9 on their module pages **whose most recent release is after the release of Drupal 9.0 (June 3, 2020).** Maintainers of some modules that were last updated earlier in 2020 marked their modules as compatible with Drupal 9 while it was still in alpha or beta, and then Drupal 9 changed before the release, breaking compatibility with those modules.
> 0 votes

# Answer
This issue has been reported at https://github.com/mglaman/drupal-check/issues/202
> 0 votes

---
Tags: 8, migrations
---


# Question
Title: View listing element by date, exception for the first one
I have a view which list a content type order by date. I just want to add a simple condition : the first element of the list must the one with `ID = X`, even if it was created long time ago. How can i do that ?

# Answer
> 1 votes
Add two sort criteria, the first one on that ID and the second on the date.

Please note that the "ID" must have the same value for all other nodes except the first to make this works.

So probably it will be necessary to use an extra field - boolean, or integer, depending how many nodes you want to fine control.

If you don't have a lot of content to order in that view you otherwise may consider Entityqueue or DraggableViews.

---
Tags: 8, views
---


# Question
Title: How to create Media entity and attach to user field
I'm trying to import user profile pictures from Drupal 7 to Drupal 8. So far I've managed to upload the picture from the url and save it to the file system, create Media entity and use the user id as author. However, the picture doesn't show to the user profile (Manage Form Display), neither to the Edit Media Content Type page.
Here is the code I have so far:

```
$values = array(
      'name' => $user["name"],
      'pass' => [
         'value' => $user["pass"],
         'pre_hashed' => TRUE,
      ],
      'status' => $user["status"],
      'mail' => $user["mail"],
      'field_first_name' => $user["field_first_name"],
      'field_last_name' => $user["field_last_name"],
);

$userStorage = \Drupal::entityTypeManager()->getStorage('user');
$userEntity = $userStorage->create($values);

    $replaced = str_replace("\\", "", $user["picture"]);
    $imageData = file_get_contents($replaced);
    $fileName = basename($replaced);
    $file = file_save_data(
        $imageData,
        "public://". $fileName,
        FileSystemInterface::EXISTS_RENAME
    );

    $media = Media::create([
        'bundle' => 'image',
        'uid' => $userEntity->id(),
        'langcode' => \Drupal::languageManager()->getDefaultLanguage()->getId(),
        'field_media_file' => [
             'target_id' => $file->id(),
             'alt' => $file->getFilename(),
        ],
        'thumbnail' => [
             'target_id' => $file->id(),
             'alt' => $file->getFilename(),
        ],
    ]);

    $media->setName($fileName)->setPublished(TRUE)->save();

    $userEntity->field_user_picture->appendItem($media);

```

Is the something missing in the code?

**UPDATE**

I noticed (as also pointed out in the comments) `$userEntity->save();` is missing after `$userEntity->field_user_picture->appendItem($media);`.

However, after I added that line, I get the following error:

```
Error: Call to undefined method Drupal\media\Entity\Media::getFileUri() in Drupal\image\Plugin\Field\FieldType\ImageItem->preSave() (line 321 of /var/www/html/web/core/modules/image/src/Plugin/Field/FieldType/ImageItem.php) #0 /var/www/html/web/core/lib/Drupal/Core/Field/FieldItemList.php(233): Drupal\image\Plugin\Field\FieldType\ImageItem->preSave()

```

Is it related to `file_save_data()` function?

# Answer
I've found the solution.
I don't need to create a Media entity and append it to the User entity, since the profile picture field type is Image, so I just need to add the `file->id()` to the specific field.
Here is the code:

```
$replaced = str_replace("\\", "", $user["picture"]);
$imageData = file_get_contents($replaced);
$fileName = basename($replaced);
$file = file_save_data(
      $imageData,
      "public://". $fileName,
      FileSystemInterface::EXISTS_RENAME
);

$values = array(
      'name' => $user["name"],
      'pass' => [
         'value' => $user["pass"],
         'pre_hashed' => TRUE,
      ],
      'field_user_picture' => array('target_id' =>$file->id()),
      'status' => $user["status"],
      'mail' => $user["mail"],
      'field_first_name' => $user["field_first_name"],
      'field_last_name' => $user["field_last_name"],
);

$userStorage = \Drupal::entityTypeManager()->getStorage('user');
$userEntity = $userStorage->create($values);

```

Then, if required, I can create the Media entity as following:

```
$media = Media::create([
          'bundle' => 'image',
          'uid' => $userEntity->id(),
          'langcode' => \Drupal::languageManager()->getDefaultLanguage()->getId(),
          'field_media_image' => [
             'target_id' => $file->id(),
             'alt' => $file->getFilename(),
          ],
          'thumbnail' => [
             'target_id' => $file->id(),
             'alt' => $file->getFilename(),
          ],
]);

$media->setName($fileName)->setPublished(TRUE)->save();

```
> 1 votes

---
Tags: 8, users, media, migrations
---


# Question
Title: Where do I find the block id for Twig Tweak module
In order to print a block in a twig file when using the Twig Tweak module you require the block id, but nothing I am trying will work and I get the message that -

This block is broken or missing. You may be missing content or you might need to enable the original module.

# Answer
> 2 votes
Assuming you are using Twig tweak 2.x the following drush command is suggested by the cheat sheet page.

```
drush ev "print_r(array_keys(\Drupal::service('plugin.manager.block')->getDefinitions()));"

```

# Answer
> 2 votes
I think your block id is a machine name of a block that you added here: Admin \> Structure \> Block layout (/admin/structure/block).

When you edit some block you will see the machine name. Besides, a disabled block is also allowed.

# Answer
> 0 votes
You can enable the debug mode for twig template, and then inspect the element. There are in comments with all the id of all the HTML elements. Check this

You enable Twig Debugging in `sites/default/services.yml`. (If `services.yml` does not yet exist; copy `default.services.yml` and copy it to `services.yml.`)

Set the debug variable to true. And clear cache.

```
parameters:
  twig.config:
    debug: true

```

# Answer
> 0 votes
Unfortunately, block can mean a few things in the context of Drupal.

For the Twig Tweak module to render a block you need the numeric id of the block (most of the time - see above link). Navigate to `/admin/structure/block/block-content`. Edit the block you want. You can see the numeric block id in a couple places on the page but I like to look in the address bar (e.g. site.com/block/5). In this example the block id is `5`.

So to render the block put the following in your twig template:

```
{{ drupal_entity('block_content', 5) }}

```

Alex Kuzava is kind of correct. That is the machine name of the block once it is placed in the block layout.

As a note there are a couple other ways to reference the block via twig tweak from the above reference link (e.g. by UUID).

---
Tags: theming
---


# Question
Title: How to display batch progression with default theme of my site?
I just created my first batch using batch API and it seems to work!

The progression bar is properly displayed during the execution and it uses administration theme, Seven for my site, which I guess is the standard behavior.

Is there a way to use the default theme of my site instead?
How do I do that?

# Answer
> 0 votes
In fact, my question was irrelevant.
It appears that I ran my tests of my batch using a user with administrator role. Hence the theme (Seven) used during the execution.
I checked that using another user (not being an administrator) and it was the default theme of my site that was used.

By the way, I got the opportunity of some interesting readings. Thanks!

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


# Question
Title: Installing/Enabling Custom Webform Hander
I am very new to Drupal development though I am an experienced developer. I created a custom Webform Handler following these tutorials and postings:

These for what files to create and where:

Create a custom webform handler in Drupal 8

Create a Webform Handler that sends a notification to Slack

Add a custom submission handler to a form

This post for the code to redirect a user to another webform upon form submission:

Page redirect in custom WebformHandlerBase

I have looked at everything and everything looks correct. Here's what's in the YAML file modules\custom\redirect\_to\_ccbilling\redirect\_to\_ccbilling.info.yml:

```
name: 'Redirect to CC Billing'
description: 'Provides a custom webform handler for the "submit an event" webform so it will redirect to another webform node'
core_version_requirement: ^8.8
Core: 8.x
package: Custom
type: module

```

Here's what's in the modules\custom\redirect\_to\_ccbilling\src\Plugin\WebformHandler\RedirectToCCBillingWebformHandler.php:

```
<?php
namespace Drupal\redirect_to_ccbilling\Plugin\WebformHandler;

use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Serialization\Yaml;
use Drupal\Core\Form\FormStateInterface;
use Drupal\webform\Plugin\WebformHandlerBase;
use Drupal\webform\src\WebformSubmissionInterface;

/**
 * Form submission handler.
 *
 * @RedirectToCCBilling(
 *   id = "Redirect to CC Billing",
 *   label = @Translation("Redirect to CC Billing"),
 *   category = @Translation("Webform Handler"),
 *   description = @Translation("Redirect user to the credit card billing page."),
 *   cardinality =
 *       \Drupal\webform\Plugin\WebformHandlerInterface::CARDINALITY_SINGLE,
 *   results =
 *    \Drupal\webform\Plugin\WebformHandlerInterface::RESULTS_PROCESSED,
 * )
 */

 class RedirectToCCBillingWebformHandler extends WebformHandlerBase {
 /**
  * {@inheritdoc}
  */
    public function submitForm(array &$form, FormStateInterface $form_state, WebformSubmissionInterface $webform_submission) {

       $form_id = 'webform_submission_' . $webform_submission->getWebform()->id() . '_form';
       if($form_id == 'webform_submission_xxx_form') {
         $values = $webform_submission->getData();
         /* Take the action based on the Webform submission values */
       }
    }

    public function confirmForm(array &$form, FormStateInterface $form_state, WebformSubmissionInterface $webform_submission) {
       $form_id = 'webform_submission_' . $webform_submission->getWebform()->id() . '_form';
       if($form_id == 'webform_submission_xxx_form')
       {
          if(based_on_some_condition) {
          /* Here 12 is the node id where i wanted to redirect to */
          $form_state->setRedirect('entity.node.canonical',['node' => 1764]);
       }
    }
}
?>

```

I went into the Extend menu and under Custom section I selected the Redirect to CC Billing module/handler and clicked Install at the bottom of the page. However, when I go back to my webform \>\> setting \>\> emails / handlers \>\> +add handler all I see is this:

I don't know what I am missing for my custom handler to not be appearing in the list of handlers.

# Answer
> 3 votes
A few things in addition to what @4k4 commented:

1. Please replace your annotation plugin reference with `@WebformHandler`
2. Use lower case / snake case names for your Webform Handler ID eg. `redirect_to_ccbilling`
3. Ensure correct directory structure where the plugin discovery will look for Webform Handler classes. This should be `/modules/custom/redirect_to_ccbilling/src/Plugin/WebformHandler/RedirectToCCBillingWebformHandler.php`
4. Ensure that your module is enabled. Either via `drush en redirect_to_ccbilling -y` or by enabling it on the Extend module page.

```
/**
 * Form submission handler.
 *
 * @WebformHandler(
 *   id = "redirect_to_ccbilling",
 *   label = @Translation("Redirect to CC Billing"),
 *   category = @Translation("Webform Handler"),
 *   description = @Translation("Redirect user to the credit card billing page."),
 *   cardinality = \Drupal\webform\Plugin\WebformHandlerInterface::CARDINALITY_SINGLE,
 *   results = \Drupal\webform\Plugin\WebformHandlerInterface::RESULTS_PROCESSED,
 * )
 */
class RedirectToCCBillingWebformHandler extends WebformHandlerBase {

```

---
Tags: 8, webforms
---


# Question
Title: Custom exposed filter for year in date field with ajax
I am trying to create a custom exposed filter which would be a select field with years (2015-2025). This would filter the result with the year of the date they are created. I followed this tutorial https://www.flocondetoile.fr/blog/filter-content-year-views-drupal-8 But here there is no solution for ajax submission, so the page does not refresh and the filter does not apply.

Currently I have this year as a contextual filter and the form field in the hook\_form\_views\_exposed\_form\_alter():

```
$options = [
    'all' => t('- All -'),
    '2015' => '2015',
    '2016' => '2016',
    '2017' => '2017',
    '2018' => '2018',
    '2019' => '2019',
    '2020' => '2020',
    '2021' => '2021',
    '2022' => '2022',
    '2023' => '2023',
    '2024' => '2024',
    '2025' => '2025',
  ];

  $form['year'] = [
    '#title' => t('By year'),
    '#type' => 'select',
    '#options' => $options,
    '#size' => NULL,
    '#default_value' => 'all',
  ];

```

Is there a possibility for the filter to work with ajax submission?

# Answer
You can use this custom filter to filter by year: https://github.com/DarkteK/d8-views-custom-filter/tree/master/d8\_filters

Just change the years you want to use in the file 'ViewsYearsFilter.php'

This will work with Ajax
> 0 votes

# Answer
I believe Views Better Exposed Filters can achieve this: `What else can I do with BEF?: Provide instant feedback by updating the View using AJAX whenever an exposed filter is changed`
> -1 votes

---
Tags: views
---


# Question
Title: Token does not return full dynamic path to Media Image URL
I'm having trouble configuring Metatag for Open Graph and Twitter Card images.

I'm using Drupal 8.9 core Media Image reference field as the source.

`[node:field_hero_image]` (Entity reference field) just returns the bare filename.

`[node:field_hero_image:entity]` (The referenced entity) also returns the bare filename, not the expected path to the image.

`[node:field_hero_image:target_id]` Just returns an integer. (In this case, it is 1.)

I'm reduced to trying to build the path by combining Tokens and plain text, like this:

```
[site:url]sites/default/files/2021-01/[node:field_hero_image]

```

This works, but the problem is that I used the default File Directory path:

```
[date:custom:Y]-[date:custom:m]

```

That means media assets that I upload in February will be at a different path than the ones I uploaded last month, so hard-coding the path to the directory is pointless.

Now, I could change the File Directory path to a string literal like `images`, delete all my Media Images and recreate them.

I'm guessing there is a better approach: either a Token that returns the correct image URL, or a way to bulk reconfigure Media Image assets after moving their File Directory.

Please help!

# Answer
Would you believe:

`node -> media field -> media entity -> image field -> image style -> url`

Makes sense, right? (I mean, it makes sense if you understand core Media and Entity References.)

As a Token, it looks like this:

`[node:field_hero_image:entity:field_media_image:large:url]`

Not exactly intuitive, but it makes sense in hindsight. The Media field is an entity reference to a Media entity containing an Image field that can be rendered in a variety of Image styles, so the token chain goes all the way down that rabbit hole.

This approach generates a valid image URL in the header meta tags.

Of course, AddToAny is still not picking up the metadata when I share a Twitter card ... but that's a different issue!

Just pointing out to nobody in particular that this Token does not show up in the Node section of the "View available tokens" box. ¯\\\_(ツ)\_/¯
> 4 votes

---
Tags: entities, media, tokens, meta-tags
---


# Question
Title: How to sort a View according to contextual filters?
I have a view that filters nodes by a contextual filter that allows multiple values. So it gets a list of node IDs like `234, 235, 210, 56, 78`. Is it possible to sort the result of this view according to the order of the given IDs? I tried to achieve that by deleting all sort criteria, but this leads to an arbitrary order of the results.

# Answer
> 2 votes
The Views Arguments Extras module (for Drupal 7) seems to do what you are looking for. According to the module page, it provides the following:

> **Argument Order Sort**
>
> a sort handler, that allows for the order of items to be based on their order in a multi-value argument

This particular component is ported to Drupal 8: Views Argument Order Sort:

> Using this module will give you a new sort type with a variety of options where you can choose what field to apply the argument values to for sorting.

# Answer
> 0 votes
When you provide a contextual filter with multiple values, views generates a query that has something like this `WHERE (node_field_data.nid IN('72586', '60952', '72516', '50611', '60328', '49852', '49819', '49816', '60609', '49834', '60220', '50841'))`

Views is doing everything properly, however SQL will effectively sort it from lowest to highest integer.

You need to alter the query to something like this here: https://stackoverflow.com/questions/4858828/sql-where-in-sort-by-order-of-the-list

to achieve the sort you want.

---
Tags: views
---


title: Theming a views field with template

text:
# Question
Title: Theming a views field with template
Apologies if there is a good documentation out there, I searched and read other topics for over 30 minutes now, could not figure it multiple things with theming a field.

My objective is to customize a field inside a views called `field_calendly_url`. I found the following field template. I have several questions;

1. Would I need to make changes with set classes, for example change `field--name-` to `field--calendly--url`?
2. How can print my field value, I tried `{{ field.calendly.url }}` and `{{ fields.calendly.url }}` but neither worked.
3. Do I have to keep the i*f statement for label* even if I am not using a label?

\` {% set classes = \[ 'field', 'field--name-' ~ field\_name|clean\_class, 'field--type-' ~ field\_type|clean\_class, 'field--label-' ~ label\_display, \] %} {% set title\_classes = \[ 'field--label', label\_display == 'visually\_hidden' ? 'sr-only', \] %}

```
{% if label_hidden %}
  {% if multiple %}
    <div{{ attributes.addClass(classes, 'field--items') }}>
      {% for item in items %}
        <div{{ item.attributes.addClass('field--item') }}>{{ item.content }}</div>
      {% endfor %}
    </div>
  {% else %}
    {% for item in items %}
      <div{{ attributes.addClass(classes, 'field--item') }}>{{ item.content }}</div>
    {% endfor %}
  {% endif %}
{% else %}
  <div{{ attributes.addClass(classes) }}>
    <div{{ title_attributes.addClass(title_classes) }}>{{ label }}</div>
    {% if multiple %}
      <div class="field--items">
    {% endif %}
    {% for item in items %}
      <div{{ item.attributes.addClass('field--item') }}>{{ item.content }}</div>
      hello...
    {% endfor %}
    {% if multiple %}
      </div>
    {% endif %}
  </div>
{% endif %}

<a href="javascript:void jQuery.colorbox({html:'<iframe width=400 height=400 src={{ field.calendly.url }} {{ fields.calendly.url }} frameborder=0 allowfullscreen></iframe>'})" class="btn-box">Request Meeting</a>

```

\`

# Answer
In case anyone else will look for this later, I was able to display the views field value with the following.

```
{{ view.field.field_calendly_url.original_value }}

```
> 1 votes

# Answer
This answer assumes that the template in your question is the correct field template for your view.

1. You can use whatever classes you want. Views provides a bunch of classes by default, but these classes are just that-- default classes to make it easier for people to theme without having to customize the templates. You can ignore them, remove them, replace them with whatever you want.
2. Usually the field value is printed with `item.content`. If this is the correct template, you should already see it as output in your view.
3. No, you don't need to keep the code for the label. However, if you delete it, you may later forget you deleted it and then set a label in the Views UI, but it will never appear, and this could be confusing because it is disabled in the template.

PS: In the future, try to keep to one question per question on this site. Asking multiple questions is likely to get your question closed as unclear.
> 0 votes

---
Tags: 8
---


# Question
Title: How do I remove the underscore a view adds to a rewritten link?
I am using fields, one has a link, rewriting the link as shown below sending the datetime to webform in the URL. All fields display as they should. When hovering over the link, the URL shows extra space at the end of each field as an underscore as follows. The underscore is not in the datetime field.

> 2018-05-14T15:39:15-0400\_

As seen above the underscore is added. Webform wont prepopulate the datetime field element with the underscore. When manually writing the value, webform correctly prepopulates my webform. The link is `/appt_testform?apptdatet={{ field_apptdatet }}&body={{ body }}`, which becomes `/appt_testform?apptdatet=2018-07-15T18%3A00%3A00-0400_&body=Testing%20the%20appt%2C%20test%20and%20recommend`. The underscore after 0400 should not be there.

I see an underscore is added to other fields, which accept values where the datetime will not. I am trying to do this without using `hook_form_alter()`.

---

After testing on 5 servers using different 8.x versions When importing using feeds module CSV file, If the content field is date and time..imported using feeds then View linked to webform to prepopulate it will add the underscore as shown in the original question. If a field is timestamp (unix number in CSV) after importing, the view rewrite results link does not have the underscore added. I don't know why, however, my site works using the timestamp field type. On all the servers.

# Answer
> -1 votes
This issue happens when the field value is rendered with HTML (which is appended by Drupal date formats).

In your View, edit your time field and make sure to use Custom formatter rather than any of the predefined ones, and provide the custom pattern in 'Date/time format' field. And then underscore should go. Check this useful answer.

In my case I was struggling with underscore appended after Year value (mysirte.com/tag/1994\_), and when I created custom format and used 'Y' the underscore disappeared, see picture:

---
Tags: views, 8
---


# Question
Title: How do I specify a mask for a textfield form element?
My use case is actually to validate a first name field to allow only alphabets and space. Is this actually the best module to use for this? I've looked at `Client-side Validation` and it looks too complex to do something as simple as this. Is there any other module to do exactly this?

I installed the \[Masked Input\]\[1\] module and using the following form element.

```
$form['codeid'] = [
  '#type'=> 'textfield',
  '#mask' => '9999999999'
  '#attributes' => [
    'mask' => '9999999999'
  ],
].

```

Now I know how to apply a mask by adding a field to my custom entity. (1) But how do I specify how many alphabet characters to allow, masked\_input module only allows for specific number of characters to be entered in mask. Doesn't allow for regex style {2, 50} which would mean allow 50 alpa characters (2) I can't get it to allow space character.

# Answer
The module implements a field widget, which is a plugin used to render an entity field when the entity is edited or created.
You cannot use it for a generic form element as the one implemented in the code shown in the question. You need to add a field to an entity (for example, user or node), choose the field type between *Text (Plain)*, *Text (Formatted)*, or *Text (Formatted, long)* and set its widget type to *Masked input*.

You could also programmatically add the entity field to an entity.
> 0 votes

---
Tags: 8, forms
---