# Question
Title: How to redirect a node to an external URL?
Is there a way to "alias" a Drupal site node to an external URL?

I'd like that a click on a link in my Drupal site like 'http://mysite/node/64' redirects to an address like 'http://anothersite/whateverurl'.

I use Fullcalendar View module.
I added a Date range field to a custom content of mine (my modified Basic page).
If I set a date in a node of that type, I can see an item linked to it in the Calendar:  If I click on the item, I am redirected to the corresponding node and I see its content.
For some nodes, I'd like the link to point to an external url instead...

\[Edit\]
I tried something else: I created a new View with no fields at all except the Daterange.
When I use Format: `Unformatted list`, I get this result:  which is OK as I have only one node of the type filtered.
When I use Format: `Full Calendar Display`, I get this result:
which is strange as I have no fields in the View description pointing to the node itself...
Does it mean that Fullcalendar View\] module generates its magic elsewhere?
I'm a bit confused :-(
Any help?

# Answer
Since you want this done in a view here is a possible solution

1. Add a `link field` to your content type `external url` which would need to be followed when available otherwise (if empty) it would take to node view (normal behaviour).

Modify the Fullcalendar View and add follow 2 fields

2. `Link to content` hidden
3. `external url` and adjust the `No results behaviour` to include `{{ view_node }}`
4. Use `external url` as the link

**NOTE:** it appears that `Fullcallendar view` module takes the link to node in a way that it cannot be modified within the view as described above. There is a feature request that asks for such an option. In the meantime perhaps the above can be achieved with another **calendar** module () or modify the URL from within a custom module.
> 1 votes

# Answer
Lets say you have a node on your website `example.com/node/123` with an alias to it `example.com/my-node`

If what you need is:

* when a user try to access the node by going to `example.com/node/123`, so he will be redirected to an **external url.**
* but when the user access the node by going to `example.com/my-node`, no redirection and he will normally have access to the **node page**.

If this what you are asking about, reply to my answer here and i will gibe you a full tutorial on how to do it !

Otherwise, as @clive mentioned, your question does not make sense ! And for what you need the node if you are not going to use it.
> 0 votes

# Answer
It's not easy to do this, as the other answers show.

I wrote a module called "Alien Alias" which does this, it can link to internal or external locations. It also lets you keep stats of the clicks (if you want it to).

(Optionally) it intercepts very early in the page build process to redirect away, although you can let it build a whole page and then redirect.

Here: https://drupal.org/project/alien\_alias
> 0 votes

---
Tags: 8
---


title: How can I replace a string in a text?

text:
# Question
Title: How can I replace a string in a text?
With `{{ content.field_myfield.0 }}`, I get a text. When I use `{{ content.field_myfield.0|replace({"foo" : "bar"}) }}`, nothing is rendered and no errors are reported.

I also tried replacing `content.field_myfield.0` with `content.field_myfield.value` or \`content.field\_myfield.0.value, but I still get an empty string.

How can I replace a string?

Thanks

# Answer
> 7 votes
`content.field_some_field` is a render array, but the `replace` filter operates on strings. You need to render your field before calling replace.

For example:

```
{{ content.field_some_field|render|replace({"hello": "goodbye"}) }}

```

# Answer
> 6 votes
If this is a formatted text field you can replace the source text before it is rendered. This has the advantage that HTML tags are preserved:

```
{% set field_item = content.field_myfield.0  %}
{% set text_replaced = field_item['#text']|replace({'foo': 'bar'}) %}
{{ field_item|merge({'#text': text_replaced}) }}

```

If you are not sure about the field type check the render array `{{ dump(field_item) }}`.

You can flatten the render array before replacing, as @sonfd suggested, if the replaced text doesn't contain HTML tags.

In general there are better ways to replace texts. Implement a text filter or a pre\_render/post\_render callback. See for example https://www.drupal.org/forum/support/module-development-and-code-questions/2020-01-28/creating-a-custom-text-filter or How to alter page content?

---
Tags: 9
---


# Question
Title: Error when installing Rules with composer
I've tried to install the latest Rules via composer on drupal 9.3.9. I am new to composer.

I uses "composer require 'drupal/rules:^3.0@alpha'" and got a similar error that on issue https://www.drupal.org/project/rules/issues/3240025

So, I have tried the dev version ("composer require 'drupal/rules:3.x-dev'") and received another error:

Problem 1 \- drupal/rules dev-3.x requires drupal/typed\_data ^1.0 -\> found drupal/typed\_data\[dev-1.x, 1.0.0-alpha1, ..., 1.x-dev (alias of dev-1.x)\] but it does not match your minimum-stability. \- drupal/rules 3.x-dev is an alias of drupal/rules dev-3.x and thus requires it to be installed too. \- Root composer.json requires drupal/rules 3.x-dev -\> satisfiable by drupal/rules\[3.x-dev (alias of dev-3.x)\].

Is the problem with my syntax or settings?

Thx!

# Answer
Add or change these in composer.json:

```
minimum-stability: dev,
prefer-stable: true

```
> 1 votes

---
Tags: rules, composer
---


title: Cannot set path alias to "/"

text:
# Question
Title: Cannot set path alias to "/"
I have a weird use case where I need to specifically set the path alias of my home page to "/".

I am creating a Gatsby site and the pages are auto generated based completely off of the Drupal path alias. So If I have a path alias of "/about", the /about page will get generated in Gatsby.

The problem is that for my home page, I cannot set the path alias to just "/" in Drupal. If I try doing that on the actual node edit page, it gets stripped out on save and becomes null. Same thing if I try to go the URL aliases admin page and try to manually set it there for my node page. If I try to just put "/", it gets stripped out there also.

I am successfully able to add that path alias as my "Default front page" in "System \> Basic site settings". If I add it there it does not get wiped out on save, however now my home page just becomes a 404 page because it cannot find a page with a path alias of "/". I know this is possible to do somehow because the Drupal/Gatsby youTube tutorial I was following is doing this and it shows the Drupal admin with a path alias of just / for his homepage. I contacted that developer and he said he doesn't remember how he was able to get that to happen and doesn't remember needing to do anything specific to allow for that.

I am using Docksal for my local site and I also have a Pantheon site running the same codebase and database and I can't set it in either of those sites. I am running the latest version of D9 (9.3.9). Can anyone think of a reason I cannot do this? I'm wondering if I need to create a custom module to make this happen or something? Or if I just need to apply some kind of Drupal core patch?

# Answer
> 1 votes
I found a patch for Drupal core that seems to have worked. It was #14 here:

https://www.drupal.org/project/drupal/issues/3100350

---
Tags: path-aliases
---


# Question
Title: Drupal 8/9 Layout Builder and default custom block
We have a number of custom blocks that we've created, and use Layout Builder so that our authors can just drag and drop as needed. We have one Hero block in particular that we would like to see on every page (customized per page), and would love to automatically insert one on node creation for ease of authoring. You can almost do this via default layouts for the content type, but the default block acts more like a reusable block. The first page gets the empty block, user edits it and saves. Any subsequent page then get that edited version.

I'm running the following in a `hook_preprocess_HOOK` (`hook_preprocess_page` specifically):

```
$layoutBuilder = $node->get('layout_builder__layout');
$sections = $layoutBuilder->getSections();
if (isset($sections) && !empty($sections)) {
  $hasHero = FALSE;
  $heroSection = $sections[0];
  $components = $heroSection->getComponents();
  foreach ($components as $component) {
    $blockPlugin = $component->getPlugin();
    if ($blockPlugin instanceof BlockBase) {
      $blockConfig = $blockPlugin->getConfiguration();
      if ($blockConfig['id'] === 'hero_cta') {
        $hasHero = TRUE;
        break;
      }
    }
  }
  if (!$hasHero) {
    $blockEntityManager = \Drupal::entityTypeManager()
      ->getStorage('block_content');

    $block = $blockEntityManager->create(
      [
        'info' => 'Hero CTA for node/' . $nid,
        'type' => 'hero_cta',
        'langcode' => 'en',
      ]
    );
    $block->save();

    $newBlockConfig = [
      'id' => 'hero_cta',
      'provider' => 'hero_cta',
      'label_display' => TRUE,
      'block_id' => $block->id(),
      'context_mapping' => [],
    ];
    $newComponent = new SectionComponent($node->uuid(), 'content', $newBlockConfig);
    $heroSection->appendComponent($newComponent);

    $node->save();
  }
}

```

The above almost works... When I go to the View tab, I can see it. But, when I go to the layout tab, it is not there. If you save the layout override, and then *refresh* again, it suddenly appears.

So, what am I doing wrong?

# Answer
> 1 votes
Alright, our team eventually found the solution we were after, so posting here in case someone else is trying to do this! Ultimately, as pointed out, I was initially doing this in a preprocess hook - *which was then being overwritten by the layout defaults*. Here is what worked:

```
protected function prepareLayout(SectionStorageInterface $section_storage) {
  parent::prepareLayout($section_storage);
  $storageContext = $section_storage->getContexts();
  $node = $storageContext['entity']->getContextData()->getValue();
  if (isset($node)) {
    if ($node->hasField(OverridesSectionStorage::FIELD_NAME)) {
      $nid = $node->id();
      $layoutBuilder = $node->get(OverridesSectionStorage::FIELD_NAME);
      $sections = $layoutBuilder->getSections();
      if (isset($sections) && !empty($sections)) {
        // Set default configuration to compare to make sure page has been generated.
        $defaultHeroSection = $section_storage->getDefaultSectionStorage()->getSection(0);
        $defaultHeroCount = count($defaultHeroSection->getComponents());

        // First section is our 'Hero CTA' region. Check for existence.
        $hasHero = FALSE;
        $heroSection = $layoutBuilder->getSection(0);
        $heroComponents = $heroSection->getComponents();
        $currentHeroCount = count($heroComponents);
        if ($defaultHeroCount === $currentHeroCount) {
          foreach ($heroComponents as $component) {
            $blockPlugin = $component->getPlugin();
            if ($blockPlugin instanceof BlockBase) {
              $blockConfig = $blockPlugin->getConfiguration();
              if ($blockConfig['id'] === 'hero_cta') {
                $hasHero = TRUE;
                break;
              }
            }
          }
          if (!$hasHero) {
            $blockEntityManager = \Drupal::entityTypeManager()->getStorage('block_content');

            // Create a new, empty Hero CTA block.
            $block = $blockEntityManager->create(
              [
                'info' => 'Hero CTA for node/' . $nid,
                'type' => 'hero_cta',
                'langcode' => 'en',
              ]
            );
            $block->save();

            // Insert new Hero CTA block into proper section.
            $newBlockConfig = [
              'id' => 'hero_cta',
              'provider' => 'hero_cta',
              'label_display' => TRUE,
              'block_id' => $block->id(),
              'context_mapping' => [],
            ];
            $newComponent = new SectionComponent($node->uuid(), 'content', $newBlockConfig);
            $heroSection->appendComponent($newComponent);
          }
          // Remove original first section and unused blocks.
          $section_storage->removeSection(0);
          // Reinsert first section.
          $section_storage->insertSection(0, $heroSection);
          $this->layoutTempstoreRepository->set($section_storage);
          // Save the changes.
          $node->save();
        }
      }
    }
  }
}

```

---
Tags: layout-builder
---


title: Post data to term field through JSONAPI

text:
# Question
Title: Post data to term field through JSONAPI
Trying to create node via JsonAPi. It contains a term field and text field. Managed to post text field without any issues but struggle to post values to term field. Anyone guide me to post values to term fields. format shared below

```
"data": {
    "type": "node--verification",
    "attributes": {
        "title": "My custom title"

       }
    "relationships": {
        "field_executing_agency": {
            "data":"BRO",
            "target_id": "1324"
        }

    }
}

```

# Answer
In JSON:API, taxonomy term fields are handled like other entity reference fields.

Posting the `data` below will set the entity reference field `field_entity_reference_taxonomy_term` to an existing taxonomy term.

```
"data": {
    "type": "node--node-type",
    "attributes": {
        "title": "My custom title"
       }
    "relationships": {
        "field_entity_reference_taxonomy_term": {
            "type":"taxonomy-term-type-machine-name",
            "id": "taxonomy-term-uuid"
        }

    }
}

```

If you want to create a new taxonomy term and set the value of `field_entity_reference_taxonomy_term` to that, then you have two steps:

1. POST the new taxonomy term.
2. Update the node to set the value returned from the server when you POSTed the taxonomy term (the server returns the UUID in the response when it creates a new entity/term).

On a standard installation of Drupal, there's a nice UI for automatically creating taxonomy terms on the node edit page, but with decoupled Drupal, you have to build this yourself.
> 0 votes

---
Tags: nodes
---


# Question
Title: How can I add a CSS class to the body tag?
I'm trying to add a custom CSS class to the `<body>` tag, How can I modify or pre-process the `<body>` tag to add the class body, Is it right to style the `<body>` tag with css file?

# Answer
> 9 votes
**If html.html.twig does not exist in your current theme's template folder**, go to `/core/modules/system/templates` folder.

Open **html.html.twig** file. Highlight and copy all code (CTRL + A then CTRL + C)

Go to your theme's template folder `/themes/[yourtheme]/templates` and create a new file called **html.html.twig**, open and paste (CTRL + V) the code you copied.

Change `<body{{ attributes }}>` to `<body{{ attributes.addClass('my-class') }}>`

Clear/Flush the cache to see changes.

# Answer
> 32 votes
Alternatively, you could implement `template_preprocess_html` in your \[themename\].theme file.

```
function themename_preprocess_html(&$variables) {
  $variables['attributes']['class'][] = 'class-name';
}

```

See the documentation: https://api.drupal.org/api/drupal/core%21includes%21theme.inc/function/template\_preprocess\_html/8.2.x

# Answer
> 4 votes
```
/**
 * Add a "nodeType-detail-page" class to the body on a offer detail page
 */
function mymodule_preprocess_html(&$variables) {
  $node = \Drupal::routeMatch()->getParameter('node');
  if($node) {
    $variables['attributes']['class'][] = 'node-'. $node->bundle();
  }
}

```

# Answer
> 3 votes
You can check the Page Specific Class module to add class to body tag of any page. From the module page:

> It is a simple module for Drupal 8 users to add custom CSS class to the tag of the any specific page
>
> Features
>
> * We can add class to body tag of any page
> * Support all Pages which can be created via Node, Views or Custom Route
> * We can add class for Home page also
> * We can add class for All pages also

*Disclaimer, I am the author of the module.*

# Answer
> 3 votes
This is a simple way to add a class based on the URL, in Drupal 9. It doesn't require any module.

```
/**
 * Implements hook_preprocess_html().
 */
function THEMENAME_preprocess_html(&$vars) {
  // Get the current path
  $current_path = \Drupal::service('path.current')->getPath();
  $internal = \Drupal::service('path_alias.manager')->getAliasByPath($current_path);

  // Assign it to body class
  $vars['attributes']['class'][] = str_replace("/", "", $internal);
}

```

---
Tags: theming, 8
---


# Question
Title: View plain text field causing rendering issues for translations
Our news section has a plain text field for article header. At the time of development this seemed like a good idea, however, having gone live, some languages like French has special characters which is now causing rendering issues as example:

Is rendered as:

I understand why this is happening as text fields are "escaped" for security reasons. The problem here is one of hindsight:

1. We cannot delete the field or "convert" the field to a formatted text as that would require all languages to redo all of the articles
2. We cannot add any additional filters in the "view" as there isn't a filter allowing me to say "please allow certain characters" as in this case l' is considered an escaped character in the database.

I assume my only option is to try and override the value in my twig file: (views-view-fields--news.html.twig):

```
<div class="col-md-4">
    <div class="card newscard"> {{ fields.field_summary_image.content }}
        <div class="card-body">
            <h5 class="card-title">
                <a href="{{ fields.view_node.content|render|striptags|trim }}">{{ fields.field_article_title.content|render|striptags }}</a>
            </h5>
            <p>{{ fields.created.content|render|striptags|trim }}</p>
        </div>
    </div>
</div>

```

I have however tried to obtain the raw value but cannot get the raw value as this is a rendered "field" (which renders the wrapping HTML along with content).

I have also tried to move this to the unformatted view (views-view-unformatted--news.html.twig):

```
<div class="card-deck" id="ajaxnewscontainer">
    {% for row in rows %}
        <div class="col-md-4">
            <div class="card newscard"> {{ fields.field_summary_image.content }}
                <div class="card-body">
                    <h5 class="card-title">
                        <a href="{{ fields.view_node.content|render|striptags|trim }}">{{ fields.field_article_title.content|render|striptags }}</a>
                    </h5>
                    <p>{{ fields.created.content|render|striptags|trim }}</p>
                </div>
            </div>
        </div>
    {% endfor %}
</div>

```

However, in this scenario I am not getting any values as doing a {{ dump(row) }} renders my 32GB of RAM useless so cannot determine how to obtain the "row" array elements so that I can hopefully end up getting the "raw" value of fields.field\_article\_title.content|render|striptags

Any ideas?

# Answer
I don't think this has anything to do with translations other than your translation is the version of the node that happens to have an apostrophe in this field.

The issue is because you are rendering your field with `|render|striptags|trim` like:

```
{{ fields.created.content|render|striptags|trim }}

```

This will always cause issues where encoded html entities are displayed.

From @4uk4 in a comment above:

> The translation contains the apostrophe as ASCII character, this is converted to an HTML entity by the default XSS filter applied to all plain text fields and then it is escaped a second time by this Twig filter.

To resolve, render your field without using `|render|striptags|trim`, i.e. just use:

```
{{ fields.created.content }}

```
> 2 votes

---
Tags: views, theming, 9
---


# Question
Title: Table view combined fields, separate sort
I'm looking for some assistance in setting up a table view to be able to sort merged columns separately. At a high level I have the following view table:

| First Name | Last Name | Age/Weight^ |
| --- | --- | --- |
| Ken | Davidson | Age: 42<br>Weight: 200 |

I have all three columns sortable, except that the third column only sorts by `Age`, even though it has two possible sort options. Clicking the `Age/Weight` will only sort by Age, there is no way that I can see to make it sortable by either Age or Weight.

I found a couple links here that don't seem overly optimistic, but I'm hoping that things have changed:

**Edit**

The more I look into this, the more I'm starting to think that the only way to do this is by providing a custom template `views-view-table--custom-view-name.html.twig` and would need to overwrite the one section:

```
      <tr>
        {% for key, column in header %}
          {% if column.default_classes %}
            {%
              set column_classes = [
                'views-field',
                'views-field-' ~ fields[key],
              ]
            %}
          {% endif %}
          <th{{ column.attributes.addClass(column_classes).setAttribute('scope', 'col') }}>
            <div class="th-wrap">
              {%- if column.wrapper_element -%}
                <{{ column.wrapper_element }}>
                  {% if column.title == 'sort by Age/Weight' %}
                    <!-- Add two links hardcoding the URL appropriately -->
                  {% else %}
                    {%- if column.url -%}
                    <a href="{{ column.url }}" title="{{ column.title }}">{{ column.content }}{{ column.sort_indicator }}</a>
                  {%- else -%}
                    {{ column.content }}{{ column.sort_indicator }}
                  {%- endif -%}
                </{{ column.wrapper_element }}>
                  {% endif %}
              {%- else -%}
                {%- if column.url -%}
                  <a href="{{ column.url }}" title="{{ column.title }}">{{ column.content }}{{ column.sort_indicator }}</a>
                {%- else -%}
                  {{- column.content }}{{ column.sort_indicator }}
                {%- endif -%}
              {%- endif -%}
            </div>
          </th>
        {% endfor %}
      </tr>

```

Before I follow this path, does it seem like an optimal solution? The issue here is that I would lose the ability to display the sort indicator, since the column is still directly related to `Age/Weight` (which is just Age) instead of the individual columns.

Is there a global variable available in the `views-view-table` renderer that I would be able to check for the actual sort column? Is the URL available for parsing?

# Answer
I was able to get this working - in the most Drupal best practice way I could think of. If this is way off, I'd love some guidance on how to do this properly, but for those looking for the same:

I added a theme preprocessor to update the table `$variables` with the sort/order at the highest level:

```
function sc_preprocess_views_view_table(&$variables) {
  $view = $variables['view'];
  $handler = $view->style_plugin;
  $variables['sorting'] = array(
    'active' => $handler->active,
    'order' => $handler->order
  );
}

```

The twig file was updated with:

```
// views-view-table--custom-view--page.html.twig
{% if column.title == 'sort by Age/Weight' %}
  <a href="{{ sc_column_sort_url('field_hfs_age', sorting) }}" title="Sort by age">age</a>
  {% if sorting['active'] == 'field_hfs_age' %}
    {{ sc_column_sort_indicator(sorting['order']) }}
  {% endif %} /

  <a href="{{ sc_column_sort_url('field_hfs_weight', sorting) }}" title="Sort by weight">weight</a>
  {% if sorting['active'] == 'field_hfs_weight' %}
    {{ sc_column_sort_indicator(sorting['order']) }}
  {% endif %}
{% else %}
  // Original code
{% endif %}

```

And two custom Twig Functions were created

```
  /**
   * Provides common functionality for building the custom column sort URL required.
   * @param $name the name of the current field
   * @param $sorting the associated array of the current sort
   */
  public function scTableSortUrl($name, $sorting) {
    $nextSort = ($sorting['active'] == $name && $sorting['order'] == 'asc' ? 'desc' : 'asc');
    return "?order={$name}&sort={$nextSort}";
  }

  /**
   * Display the table sort indicator of the current custom table.   I'm sure this exists somehwere
   * else, but I can't find it.
   * @param $initial the initial sort order [asc | desc]
   */
  public function scTableSortIndicator($initial) {
    return [
      '#theme' => 'tablesort_indicator',
      '#style' => $initial,
    ];
  }

```

https://www.hashbangcode.com/article/drupal-9-creating-custom-twig-functions-and-filters for reference.
> 0 votes

---
Tags: views
---


# Question
Title: How to create a configuration export such that it installs two modules, one of which has a dependency on the other?
I've written a custom module that uses `jwt.transcoder` service from the jwt module. In turn, it provides another service:

```
$ cat mymodule.services.yml
services:
  mymodule.jwt:
    class: Drupal\mymodule\Services\Jwt
    arguments: [ '@jwt.transcoder', '@event_dispatcher' ]

```

Everything went smoothly during development. However, neither the jwt module, nor my new custom module, were installed on our site before I started developing this feature.

I added both modules with composer, and did a config export to enable them (and set up the cryptographic keys, etc). However, when I go to do a deployment, I get an error on the config import, which apparently is coming from a dependency on the `jwt.transcoder` service, which is not yet installed at this point.

```
In CheckExceptionOnInvalidReferenceBehaviorPass.php line 86:

  The service "mymodule.jwt" has a dependency on a non-existent service "jwt.transcoder".

```

With our deploy process, we can only deploy one codebase at a time-- meaning that, if I wanted to do a config import to get `jwt` installed first, and another config import with the enabling of my custom module, I would have to do a second deploy to do get that second config codebase up on the server.

Is there any way to get the config import to install jwt first, and then my custom module, in a single codebase, so this dependency is resolved?

# Answer
> 3 votes
You need to mark the `jwt` module as a dependency of mymodule in mymodule.info.yml, via the `dependencies` key.

> dependencies: a list of other modules your module depends on. Dependencies on Drupal core or contrib modules should be namespaced in the format {project}:{module}, where {project} is the project name as it appears in the Drupal.org URL (e.g. drupal.org/project/views) and {module} is the module's machine name. Dependencies can also include version restrictions, for examplewebform:webform (\>=8.x-5.x). Note that if your module has dependencies on other contributed modules or libraries, these should be declared in the module's composer.json file. If you have local custom modules that are dependent on each other you can use {module}:{module} (or {module}:{submodule} for sub-modules.)
>
> Source: Let Drupal know about your module with an .info.yml file

For example, inside your mymodule.info.yml:

```
name: My Module
type: module
...
dependencies:
  - jwt:jwt

```

---
Tags: configuration
---


# Question
Title: Must new tables be created in hook_update_N()?
When you create a new table in `hook_schema()`, should that table be added in a `hook_update_N()` as well? Or is there some trick, or something I missed, to have databae-updates automatically add tables?

The documentation of hook\_update\_N() does not explain anything about introducing new tables, whereas the documentation of `hook_schema()` says:

> The tables declared by this hook will be automatically created **when the module is first enabled**, and removed when the module is uninstalled.

(Highlight is mine)

And if so, how to best avoid duplicating the schema definitions for the new table in both hook\_update\_N() and hook\_schema(). Simply referring to the schema as follows:

```
 function hook_update_N(&$sandbox) {
   $schema = hook_schema();
   $name = "foo";
   $table = $schema["foo"];
   db_create_table($name, $table);
 }

```

Seems to work, but on changing the table again, will fail if a user runs the updates and gets to run two or more hook\_update\_N()s. After all: the first hook\_update\_N will then already install the correct database and the second hook\_update\_M() will try to add/change/alter columns that were already up-to-date.

How do you deal with this?

# Answer
So just a copy paste from drupal.org. You also need to add the schema definition to hook\_schema.

```
/**
 * Create new database table {mytable2}.
 */
function mymodule_update_7101() {
  $schema['mytable2'] = array(
     // table definition array goes here
  );
  db_create_table('mytable2', $schema['mytable2']);
}

```
> 16 votes

# Answer
It's worth to mention that `db_create_table` function is deprecated and has been removed from Drupal 9. Instead you should use:

```
/**
* Create new database table {mytable2}.
*/
function mymodule_update_9000() {
  $schema['mytable2'] = [
     // table definition array goes here
  ];
  \Drupal::database()->schema()->createTable('mytable2', $schema['mytable2']);
}

```
> 3 votes

---
Tags: database, updating
---


# Question
Title: How should I use hook_cache_flush() or hook_rebuild() in a custom module to execute JavaScript code when the cache is cleared?
I'm trying to create a module that executes some JavaScript code when the cache is flushed (via administrator menu or otherwise). I believe I can implement either `hook_cache_flush()` or `hook_rebuild()`, but I'm not quite sure how. I'm not necessarily looking for this to be done for me, but if I could be given a starting point and direction to head in, that'd be great.

# Answer
Neither `hook_cache_flush()` nor `hook_rebuild()` implementation can directly add JavaScript code to a page, simply for the fact they don't get any render array as argument, nor they return a render array to the function/method invoking them. You could add a form submission handler to the *system\_performance\_settings* form for when the *Clear all caches* button on that form is clicked. This won't work when the cache is cleared using other methods, for example by a module that calls `drupal_flush_all_caches()` in its code.

```
// Put the following line on the top of the file containing this code.
use Drupal\Core\Form\FormStateInterface;

function mymodule_form_system_performance_settings_alter(&$form, FormStateInterface $form_state) {
  if (isset($form['clear_cache']['clear'])) {
    if ($form_state->getTemporaryValue('mymodule_cache_cleared')) {
      $form['#attached']['library'][] = 'mymodule/cache.rebuild';
    }
    $form['clear_cache']['clear']['#submit'][] = 'mymodule_system_performance_settings_submit';
  }
}

function mymodule_system_performance_settings_submit(array &$form, FormStateInterface $form_state) {
  $form_state->setRebuild();
  $form_state->setTemporaryValue('mymodule_cache_cleared', TRUE);
}

```

For a solution that works in all the cases `drupal_flush_all_caches()` is called, and adds the JavaScript code to the first page requested after the cache is cleared, I would implement `hook_cache_flush()` to set a value that is then checked in `hook_page_attachments()`.

```
function mymodule_cache_flush() {
  \Drupal::state()->set('mymodule_cache_cleared', TRUE);
}

function mymodule_page_attachments(array &$attachments) {
  $state = \Drupal::state();
  if ($state->get('mymodule_cache_cleared')) {
    // Delete the state value to avoid the library is added to
    // every page after the cache is cleared.
    $state->delete('mymodule_cache_cleared');

    $attachments['#attached']['library'][] = 'mymodule/cache.rebuild';
  }
}

```

To add the JavaScript code to the first *X* pages requested after the cache clear, I would use the following hook implementations. (The following code attaches the library to the first three page requests.)

```
function mymodule_cache_flush() {
  \Drupal::state()->set('mymodule_cache_cleared_count', 3);
}

function mymodule_page_attachments(array &$attachments) {
  $state = \Drupal::state();
  if ($count = $state->get('mymodule_cache_cleared_count')) {
    $attachments['#attached']['library'][] = 'mymodule/cache.rebuild';
    $state->set('mymodule_cache_cleared_count', $count - 1);
  }
}

```

I used `\Drupal::state()->get()` and `\Drupal::state()->set()` because:

* The value set with `drupal_static()` won't be preserved between page requests. The documentation page clearly says:

> All functions requiring a static variable to persist or cache data **within a single page request** are encouraged to use this function unless it is absolutely certain that the static variable will not need to be reset during the page request.
* Setting a session value, each user would get a different value. This means the JavaScript code would be added to the next page requested after the cache is cleared only when that page is requested from the same user who was logged in when the cache has been cleared.

Add JavaScript to Your Theme or Module shows how a JavaScript library is defined from a module.
> 1 votes

---
Tags: hooks, 9
---


# Question
Title: How can I display user information along with its choices?
Usually, polls are anonymized. We need to process total votes, percentages... Now, in the context of Ukraine invasion, an institutional client wants to enhance an Advanced poll existing implementation, in order to **collect citizens' data** about their possibilities to host families, help with donations, and so on. And of course to **reach out to the people who answered the questions**...

While this could certainly be done with Webform, the team is used to poll UI, everything's already in place... and we need to go fast.

I couldn't build a poll Views with relationships and retrieve the voters' ID, emails... or the other way down from users to its poll votes... It seems that Views cannot map poll choices / results with the users who voted.

The `poll_vote` table contains the core info for that feature, with a `chid` field (the choice ID), a poll ID `pid`, and `uid`. The choice label can be then retrieved from `poll_choice_field_data` table...

Should I extend : \`Drupal\poll\PollViewData\ with a couple of **Views field handler plugins**, and how ?

Not sure what would be a straightforward way to solve this. Maybe I'm missing the point ? Any help, *code starter*, is warmly welcome to solve this quickly. Thanks

# Answer
> 0 votes
To solve it quickly, I would use Webform. Webform already does everything you need and is widely use (one of the most popular contrib modules).

The problem is that Poll is designed to be a one-question poll, not a full survey.

Even if your team is less familiar with Webform, the module is designed so that even non-programmers can easily make forms. I imagine that a form to collect basic personal info with some custom fields would only take an hour or two to implement assuming you already had the form design.

Alternately, if there's some problem installing a new module in the environment, I would go for Google Forms, which won't match your site design at all but is also extremely fast to implement and take down.

---
Tags: views, plugins, polls
---


title: Not all tokens are replaced

text:
# Question
Title: Not all tokens are replaced
I like to work with tokens a lot. In the node view it works well with the tokens. All tokens are displayed correctly Now I have built a view and in the header of this view I would like to work with tokens. Therefore I built a "contextual filter → Provide default value → Content ID from URL" in my view. Unfortunately it doesn't seem to display all tokens correctly in this view. Tokens like \[current:date\] are displayed, but \[node:title\] is not displayed. The contextual filter seems to work correctly, because if I display the title of the node via the normal "Page → Fields → Content: Title", everything works fine and the title of the respective node is displayed correctly. But why are the other tokens not displayed? Does anyone have an idea for me? How do I get this implemented?

Many thanks Bavra

# Answer
You only get access to global tokens in Views, anything else takes the form of a "Replacement pattern", and is contextual to the View.

In your case, you need to add the node title to Fields (you can exclude from display if desired), and make sure the *"Use replacement tokens from the first row"* option is selected for your header:

Then you can simply use

```
{{ title }}

```

in the text area.

(You might need to replace `title` with whatever alias the field is given if different, but the hints should make it easy to choose).
> 3 votes

---
Tags: views, tokens
---


# Question
Title: How can I retrieve information about the current request's URL?
I need help finding a way to get the current request URL in a controller and, based on that, change the webform name in the controller.

My code:

```
'#url' => Url::fromRoute('entity.webform.canonical',['webform' => 'form1'])

```

In this line of code, how can I switch between `form1` or `form2` based on whether the request URL contains a particular path?

# Answer
> 5 votes
URL info is available from the `request_stack` service. In procedural code getting the URL looks like this:

```
$url = \Drupal::request()->getRequestUri();

```

There's also a helper to get query parameters if you need them, e.g.

```
if (!empty(\Drupal::request()->query->get('foo'))) {

}

```

In your case you should inject the `request_stack` service into your controller, and get the request object from its `getCurrentRequest()` method (rather than getting it from `\Drupal::request()` directly).

# Answer
> 4 votes
> I need help finding a way to get the current request URL in a controller

In a controller get the request with a type-hinted argument `Request $request`:

```
<?php

namespace Drupal\mymodule\Controller;

use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Core\Url;

class ExampleController extends ControllerBase {

  /**
   * Controller to return a link to a webform.
   */
  public function action(Request $request) {

    // calculate $webform with $request->getPathInfo()

    return [
      '#type' => 'link',
      '#title' => 'Foo',
      '#url' => Url::fromRoute('entity.webform.canonical', ['webform' => $webform]),
      '#cache' => ['contexts' => ['url.path']],
    ];
  }

}

```

You can get other information from the request, but then you need to adjust the cache context(s). See for example https://drupal.stackexchange.com/a/245597/47547

---
Tags: hooks
---


# Question
Title: Files with 644 permissions and owner apache:apache are still failing Security Review module
I am using Drupal 7 and just installed Security Review. According to this page, the files should be 644. I tried that but I am still failing Security Review (I'm getting hundreds of files under the section "The following files and directories appear to be writeable by your web server." I assume this is because the files are owned by apache:apache. What are the correct file ownership and file group membership settings for these files so I can pass the Security Review?

# Answer
> 0 votes
The important thing is to make sure your web server user cannot write to the document root, except for public/private file folders.

There are many, many different configurations that can allow for that (e.g. you might leave the perms at 644 and change user ownership; or you might not change ownership but change perms to 444, or any number of other things).

Ultimately, you should ask your server admin to recommend an appropriate configuration for your OS and the software/accounts that are installed and enabled on it. Crowdsourcing an answer to a question which affects the fundamental security of your website, and potentially server, isn't guaranteed to end well.

# Answer
> 0 votes
As long as the user and the group ownership aren't the ones used to run the web server, any value is fine, for the *Security Review* module. Even the values set when the files have been created would be fine, as long as they aren't the same used to run the web server.

Those files are already owned by apache:apache, which means that either the web server (or PHP) created them, or the user and the group ownership have been changed after the files were created. In both the cases, changing user and the group ownership won't probably resolve the real issue.

---
Tags: 7, files
---


# Question
Title: How do I add an exposed form to search products?
I installed the Search API module and followed the documentation to set up an index and a search box using views and facets. It doesn't show an exposed form to search, even though there is a view and I can get a static grid of products. I set through the blocks where the exposed form should be, but it doesn't appear.

According to How to index Drupal Commerce 2 products for the search? the standard search doesn't show products, but the search block configuration seems to indicate that products can be shown.

>

# Answer
Some themes may not display or properly display a block view in certain block regions. For instance AdaptiveTheme using the AT Tool theme generator would not display the Products search block in main content area of the theme and nor on the recommended /products page. It also does not display the add products or most any other "add" button in Drupal Commerce. Other themes do however. Drupal 7 had a module called "Domain Blocks" which associated theme block settings positively to a Domain when Domain Access was used but no such thing exists in Drupal 9 yet as it is only able to use the AT Tool to generate a theme by a different name and use those settings per domain. Placing the block view does display the Products Search block view if placed in the header or features region wich then opens the results in the main content region. It is hit and miss for varios themes - for instance "Danland" lists the second sidebar but does not display it. It probably has something to do with the new twig system of Drupal
> -1 votes

---
Tags: views, commerce, search
---


# Question
Title: How to reset webform field validation state?
Not sure if I'm asking the question correctly. Here is the configuration:

* I have a form that contains conditionally hidden fields.
* The conditionally hidden webform fields are marked required.
* On the "condition", I've selected "clear values when hidden".

### Repro steps

#### Step 1

Navigate to the page with the form. The form is initially displayed, the field (a checkbox) upon which the condition is contingent is defaulted to `unchecked` and the hidden fields are hidden - as expected.

##### Step 2 (first open)

I check the "*I need student license"* (the contingency field)... The contingent fields now are displayed correctly.

##### Step 3

Uncheck the "*I need student license*" (the contingency field)... The contingent fields are hidden - again, as expected.

##### Step 4 (2nd open)

I check the "*I need student license"* field the **second time** and... now the contingent fields are highlighted in red indicating a validation error.

### Illustrated sequence

The sequence is illustrated in the graphic below.

Question is, how can I restore the fields under "Course Level" to the initial set every time the "I need student license" check box is checked (e.g., changes state from unset to set). Unless I'm missing something, I don't see any options that might "fix" the behavior on the backend/configuration side. I'm guessing this will have to be a frontend javascript solution.

# Answer
You should add/remove the required attribute together with the hidden/show option on the conditional field, instead of set required to the field.

e.g. if you check the postgraduate and the Faculty/Department is required, you set the conditional to display the field AND make it required. On this way, when you "reset" the form, the required attribute isn't there.

There's another way but you need to put the hands in your code.
> -1 votes

---
Tags: webforms
---


title: How do I verify a route exists?

text:
# Question
Title: How do I verify a route exists?
In Drupal 8, I need to verify a route exists or not in a custom module. How can I achieve this?

# Answer
> 10 votes
```
/* @var \Drupal\Core\Routing\RouteProviderInterface $route_provider */
$route_provider = \Drupal::service('router.route_provider');
$route_provider->getRouteByName('abc.xyz');

```

`RouteProviderInterface::getRouteByName()` returns a `\Symfony\Component\Routing\Route` object if it exists, or throws an exception if it does not.

You can use `RouteProviderInterface::getRoutesByNames()` if you don't want an exception.

```
$exists = count($route_provider->getRoutesByNames(['abc.xyz'])) === 1;

```

# Answer
> 6 votes
To check route exist or not use below function:

```
function routeExists($name)
{
    // I assume that you have a link to the container in your twig extension class
    $router = $this->container->get('router');
    return (null === $router->getRouteCollection()->get($name)) ? false : true;
}

```

# Answer
> 0 votes
I would recommend using a try catch block to catch the `RouteNotFoundException` when the route doesn't exist, or is not enabled.

```
use Symfony\Component\Routing\Exception\RouteNotFoundException;

public function routeExists($route_to_check) {
  try {
    \Drupal::service('router.route_provider')
      ->getRouteByName($route_to_check);
  }
  catch (RouteNotFoundException $exception) {
    return NULL;
  }
}

```

This way you can call this function on a non-existent route, catch the exception, return null, or whatever, and continue the execution of the code.

---
Tags: 8, routes
---


# Question
Title: How can I execute a batch process in hook_install()?
When my module is installed, I need to execute a batch process, loading hundreds of entities and doing stuff with them.

This is trivial with hook\_post\_update\_NAME() and its `$sandbox` variable. How can I achieve the same sort of thing via hook\_install()?

# Answer
If it's a custom module you can use `hook_deploy_NAME(&$sandbox)` inside MY\_MODULE.deploy.php. `drush deploy:hook` being executed as part of `drush deploy` will pick up any `hook_deploy_NAME` implementation even of just recently enabled modules.

So make sure you execute `drush deploy` as part of your deployment routine, then your custom module will get enabled first from `drush cim` (and an entry in the core.extension.yml of course) and your custom install logic will get run from `drush deploy:hook` being executed after it.

Found out just recently that `hook_deploy_NAME` implementations won't get registered on module enablement like `hook_update_N` implementations are. Every existing `hook_deploy_NAME` implementation will just be picked up.

Talked about it quickly with Moshe Weitzman (the Drush maintainer) on Drupal Slack asking if this is a bug and he said no it's a feature and that `hook_deploy_NAME` implementations are supposed to only be used in custom modules.

See https://drupal.stackexchange.com/a/254411/15055 for all the commands that are part of `drush deploy`.

---

Have you tried `batch_set` in the install hook? https://drupal.stackexchange.com/a/310282/15055
> 3 votes

---
Tags: 9
---


# Question
Title: How do I properly define a library so that JS will execute after a certain condition is met?
I'm creating a custom module to execute some JS AFTER the cache is cleared. Here is the structure of the module:

Module -\> flush.info.yml

```
name: Flush
type: module
description: A very important module.
package: Custom
version: 1.0
core_version_requirement: ^8 || ^9

```

Module -\> flush.libraries.yml

```
flush:
  version: 1.x
  js:
    js/flush.js: {}
  dependencies:
    - core/jquery
    - core/drupalSettings

```

Module -\> flush.module

```
<?php

use Drupal\Core\Form\FormStateInterface;

/**
 * Implementation of hook_cache_flush()
 */

  function flush_cache_flush() {
    \Drupal::state()->set('flush_cache_cleared', TRUE);
  }

  function flush_page_attachments(array &$attachments) {
    if (\Drupal::state()->get('flush_cache_cleared')) {
      $attachments['#attached']['library'][] = 'flush/flush';
     \Drupal::state()->set('flush_cache_cleared', FALSE);
    }
  }

```

Module -\> JS -\> flush.js

```
(function ($, Drupal, drupalSettings) {
    'use strict';

    Drupal.behaviors.flush = {
      /**
       * Drupal attach behavior.
       */
      attach: function (context, settings) {
        this.settings = this.getSettings(settings);
        alert("flush!");
        console.log ("Hello World");
      },
    };
  })(jQuery, Drupal, drupalSettings);

```

Both functions in flush.module work properly, however the JS that is #attached which is simply supposed to use an alert() never runs. I believe the issue is related to this line specifically : `$attachments['#attached']['library'][] = 'flush/flush';`

Is there something obvious I'm doing wrong? I used page\_attachments as the method to attach the JS because it does not necessarily have to be associated with a specific part of the page, or a render array, etc. Please let me know if you have any potential ideas as to what the issue could be, thanks!

# Answer
I wouldn't use Drupal::state() or hook\_page\_attachments(), those are too static. Better use a session value and a block, see my comment in the previous question.

Set the session value in the hook when the cache is cleared and only if not running from the command line:

```
function flush_cache_flush() {
  if (PHP_SAPI !== 'cli') {
    \Drupal::request()->getSession()->set('run_flush_js', TRUE);
  }
}

```

In the block check the session value and remove it so that it doesn't persist after the redirect.

*src/Plugin/Block/FlushBlock.php*

```
<?php

namespace Drupal\flush\Plugin\Block;

use Drupal\Core\Block\BlockBase;

/**
 * Provides a flush block.
 *
 * @Block(
 *   id = "flush_flush",
 *   admin_label = @Translation("Flush"),
 *   category = @Translation("Custom")
 * )
 */
class FlushBlock extends BlockBase {

  /**
   * {@inheritdoc}
   */
  public function build() {
    $session = \Drupal::request()->getSession();
    $build = [];
    if ($session->get('run_flush_js')) {
      $session->remove('run_flush_js');
      $build['content'] = [
        '#markup' => '<div class="run-flush-js"></div>',
        '#attached' => ['library' => ['flush/flush']],
      ];
    }
    $build['#cache']['max-age'] = 0;
    return $build;
  }
}

```

*js/flush.js*

```
(function ($, Drupal, once) {
  'use strict';

  Drupal.behaviors.flush = {
    /**
     * Drupal attach behavior.
     */
    attach: function (context, settings) {
      if (once('flush', '.run-flush-js', context).length) {
        alert("flush!");
      }
    }
  };
})(jQuery, Drupal, once);

```

*flush.libraries.yml*

```
flush:
  js:
    js/flush.js: {}
  dependencies:
    - core/drupal
    - core/jquery
    - core/once

```

Edit: Update js code, see How can I make this JavaScript code get executed when the BigPipe module is enabled?
> 2 votes

# Answer
I think you should approach this differently. Here's what I would do:

First, implement hook\_cache\_flush() and use it to store the last cache flush time to Drupal state.

```
function flush_cache_flush() {
  \Drupal::state()->set('flush_cache_cleared', time());
}

```

Next, use hook\_page\_attachments() to always attach your javascript on every page, but pass the the cache cleared timestamp to your javascript.

```
function flush_page_attachments(array &$attachments) {
  $attachments['#attached']['library'][] = 'flush/flush';
  $attachments['#attached']['drupalSettings']['flush_cache_cleared'] = \Drupal::state()->get('flush_cache_cleared');
}

```

Last, in your javascript, check if the new cache cleared time is more recent than the previous (you'll need to store this on the client side via local storage or something).

If the times are different, you know the cache was cleared.

```
(function ($, Drupal, drupalSettings) {
  'use strict';

  Drupal.behaviors.flush = {
    attach: function (context, settings) {

      let cacheFlushedTime = drupalSettings['flush_cache_cleared'];
      // Do stuff with the cache_flushed_time here, like
      // compare it to a previously known cache flushed time...

    },
  };
})(jQuery, Drupal, drupalSettings);

```
> 1 votes

---
Tags: hooks, 9
---


title: How to use an IF statement in node.twig

text:
# Question
Title: How to use an IF statement in node.twig
In node.twig (output for articles) I want to use the value of boolean content type field like this, but do not know how to realize this in drupal 8.

```
if boolean content type is true {
  <div class="text">
   {{ content.body }}
  </div>
else {
  <div class="img">
   {{ content.img }}
  </div>
}

```

# Answer
According the Twig If documentation, you would do it like this:

```
{% if content.field_boolean == true %}
  <div class="text">
   {{ content.body }}
  </div>
{% else %}
  <div class="img">
   {{ content.field_image }}
  </div>
{% endif %}

```

You need to use `content.[machine name]` **you can find the machine name of you fields in Manage Display of your content type.**

From my example. change `content.field_boolean` to the machine name of your boolean field. If your boolean field is called "cool" then replace it with `content.field_cool`

Remember to clear/flush the cache to see changes.
> 7 votes

# Answer
You could also use `.value` to check if a boolean field has value in the twig template. What I did was this and it worked:

```
{% if content.field_some_boolean_field.value %}
  <div class="text">
    {{ content.body }}
  </div>
{% else %}
  <div class="img">
    {{ content.img }}
  </div>
{% endif %}

```
> 0 votes

# Answer
I tried solutions given above. But not worked for me. Below code worked for me.

```
{% if node.field_some_boolean_field.0.value == '1' %}
  <div class="text">
    {{ content.body }}
  </div>
{% else %}
  <div class="img">
    {{ content.img }}
  </div>
{% endif %}

```
> 0 votes

---
Tags: theming, 8
---


title: How to create a vertical table

text:
# Question
Title: How to create a vertical table
Can anyone please help me to render theme\_table data in vertical format instead of horizontal format ? Currently horizontal table is overflowing the page and breaking breaking points.

My current code :

```
    $build['table'] = [
      '#type' => 'table',
      '#header' => $header,
      '#rows' => $rows,
      '#empty' => $this->t('No content has been found.'),
    ];

    return $build;

```

It generates following table but I wanted it in vertical table format.Where header is in left and row is in right instead of header in top and rows at bottom.

I am using **Drupal 9.3.9 with Bartik** theme.

I can think of a way where I can process array of header and rows and format it to render in vertical format. Before proceeding to that I am searching if we have any core functions which can provide the same functionality. As It would not be efficient to completely rewrite array data.

# Answer
> 3 votes
Sadly Drupal core does not provide responsive tables.

You'll need to use contrib or custom library solution to solve this issue.

Have a look at these contribs:

**Responsive Tables Filter**

**Simple Responsive Table**

---
Tags: theming, 9
---


# Question
Title: How do I add a revisions tab to a custom content entity to list revisions?
I've just created/generated a custom content entity module using Drush. I've checked I would like to use revisions, but I cannot see its tab.

How can I add the revisions tab to this custom content entity and list revisions for that entity instances?

# Answer
(I show the annotation used from the `\Drupal\node\Entity\Node` class to make clear which keys need to be used and as example of values to assign to those keys.)

Similarly to what done with nodes, the entity class annotation needs to:

1. Declare the revision tables

```
/**
  *   revision_table = "node_revision",
  *   revision_data_table = "node_field_revision",
  */

```
2. Tell Drupal to show the revision UI

```
/**
  *   show_revision_ui = TRUE,
  */

```
3. Tell Drupal which fields in the database match the revision entity fields

```
/**
  *   entity_keys = {
  *     "id" = "nid",
  *     "revision" = "vid",
  *     "bundle" = "type",
  *     "label" = "title",
  *     "langcode" = "langcode",
  *     "uuid" = "uuid",
  *     "status" = "status",
  *     "published" = "status",
  *     "uid" = "uid",
  *     "owner" = "uid",
  *   },
  *   revision_metadata_keys = {
  *     "revision_user" = "revision_uid",
  *     "revision_created" = "revision_timestamp",
  *     "revision_log_message" = "revision_log"
  *   },
  */

```
4. In the *links* section, tell Drupal which links are for the revisions

```
/**
  *   links = {
  *     "canonical" = "/node/{node}",
  *     "delete-form" = "/node/{node}/delete",
  *     "delete-multiple-form" = "/admin/content/node/delete",
  *     "edit-form" = "/node/{node}/edit",
  *     "version-history" = "/node/{node}/revisions",
  *     "revision" = "/node/{node}/revisions/{node_revision}/view",
  *     "create" = "/node",
  *   }
  */

```

Note that in the third step, defining the *revision\_metadata\_keys* values isn't optional, for an entity type that supports revisions. This is made clear in the Revision metadata fields are now defined in the entity annotation change record.

> `ContentEntityTypeInterface::getRevisionMetadataKeys()` has a parameter for backwards compatibility to search for the revision metadata field names through the base field definitions but as of Drupal 9.0.0 it will be removed and the revision metadata keys will have to be explicitly defined in the entity annotation.
> That same change record contains a link to Converting a content entity type to be revisionable and publishable, which is now outdated and it suggests to read Support for automatic entity updates has been removed for the most current information on the topic.

The module implementing the entity must define the routes used for the revision pages. The Node module does it in the node.routing.yml file, but the *route\_provider* class (which implements `EntityRouteProviderInterface`) could define them too.

```
entity.node.version_history:
  path: '/node/{node}/revisions'
  defaults:
    _title: 'Revisions'
    _controller: '\Drupal\node\Controller\NodeController::revisionOverview'
  requirements:
    _access_node_revision: 'view'
    node: \d+
  options:
    _node_operation_route: TRUE

entity.node.revision:
  path: '/node/{node}/revisions/{node_revision}/view'
  defaults:
    _controller: '\Drupal\node\Controller\NodeController::revisionShow'
    _title_callback: '\Drupal\node\Controller\NodeController::revisionPageTitle'
  requirements:
    _access_node_revision: 'view'
    node: \d+

node.revision_revert_confirm:
  path: '/node/{node}/revisions/{node_revision}/revert'
  defaults:
    _form: '\Drupal\node\Form\NodeRevisionRevertForm'
    _title: 'Revert to earlier revision'
  requirements:
    _access_node_revision: 'update'
    node: \d+
  options:
    _node_operation_route: TRUE

node.revision_revert_translation_confirm:
  path: '/node/{node}/revisions/{node_revision}/revert/{langcode}'
  defaults:
    _form: '\Drupal\node\Form\NodeRevisionRevertTranslationForm'
    _title: 'Revert to earlier revision of a translation'
  requirements:
    _access_node_revision: 'update'
    node: \d+
  options:
    _node_operation_route: TRUE

node.revision_delete_confirm:
  path: '/node/{node}/revisions/{node_revision}/delete'
  defaults:
    _form: '\Drupal\node\Form\NodeRevisionDeleteForm'
    _title: 'Delete earlier revision'
  requirements:
    _access_node_revision: 'delete'
    node: \d+
  options:
    _node_operation_route: TRUE

```

The entity class should also extend `EditorialContentEntityBase`, a base entity class with extended revision and publishing support. Using it, adding the revision and publishing fields is simple as adding `$fields = parent::baseFieldDefinitions($entity_type);` to the `baseFieldDefinitions()` method of the entity class.
> 3 votes

# Answer
After reading apaderno's answer and 2 days of research and development I figured out that some important steps are missing, in my opinion.

Therefor I made a more detailed walkthrough. This contains part of apaderno's answer and much parts and copy and past of some code of the node module.

I was using Drupal 9.3 to approach this.

# How to get a revision view of a custom content entity

## Create a Storage-Class for your Entity

Create your Storage-Class like so:

```
<?php

namespace Drupal\my_module;

use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
use Drupal\Core\Session\AccountInterface;

class CustomEntityStorage extends
  SqlContentEntityStorage implements
  CustomEntityStorageInterface
{

  /*
   * {@inheritdoc}
   */
  public function revisionIds(CustomEntityInterface $myEntity)
  {
    return $this->database->query(
      'SELECT [vid] FROM {' . $this->getRevisionTable() . '} WHERE [nid] = :nid ORDER BY [vid]',
      [':nid' => $myEntity->id()]
    )->fetchCol();
  }
  /**
   * {@inheritdoc}
   */
  public function userRevisionIds(AccountInterface $account) {
    return $this->database->query(
      'SELECT [vid] FROM {' . $this->getRevisionDataTable() . '} WHERE [uid] = :uid ORDER BY [vid]',
      [':uid' => $account->id()]
    )->fetchCol();
  }
}

```

The corresponding interface:

```
<?php

namespace Drupal\my_module;

use Drupal\Core\Entity\ContentEntityStorageInterface;

interface CustomEntityStorageInterface extends ContentEntityStorageInterface
{
  /**
   * Gets a list of node revision IDs for a specific node.
   *
   * @param \Drupal\my_module\CustomEntityInterface $myEntity
   *   The node entity.
   *
   * @return int[]
   *   Node revision IDs (in ascending order).
   */
  public function revisionIds(CustomEntityInterface $myEntity);
}

```

Then add this storage class in the handlers section of the custom entity class annotation:

```
...
 handlers = {
 *     "storage" = "Drupal\my_module\CustomEntityStorage",
 *     "list_builder" = "Drupal\my_module\CustomEntityListBuilder",
 ...

```

## Create a Controller for handling the revision view

You can create the controller class by using drush:

```
drush generate controller

```

Give it the name you wish and add these services:

* renderer
* date formatter
* entity repository

Then add the method which should render the revision page like so. A lot of this is copied from the node module

```
/**
   * Generates an overview table of older revisions of a node.
   *
   * @param \Drupal\emp\CustomEntityInterface $myEntity
   *   A node object.
   *
   * @return array
   *   An array as expected by \Drupal\Core\Render\RendererInterface::render().
   */
  public function revisionOverview(CustomEntityInterface $myEntity) {
    $langcode = $myEntity->language()->getId();
    $langname = $myEntity->language()->getName();
    $languages = $myEntity->getTranslationLanguages();
    $has_translations = (count($languages) > 1);
    $myEntityStorage = $this->entityTypeManager()->getStorage('MY_ENTITY_ID'); // see "id" in ContentEntityType annotation of you entity

    $build['#title'] = $has_translations ? $this->t('@langname revisions for %title', ['@langname' => $langname, '%title' => $myEntity->label()]) : $this->t('Revisions for %title', ['%title' => $myEntity->label()]);
    $header = [$this->t('Revision'), $this->t('Operations')];

    $rows = [];
    $default_revision = $myEntity->getRevisionId();
    $current_revision_displayed = FALSE;
    $revisions = $this->getRevisionIds($myEntity, $myEntityStorage);

    foreach ($revisions as $vid) {
      /** @var \Drupal\my_module\CustomEntityInterface $revision */
      $revision = $myEntityStorage->loadRevision($vid);
      // Only show revisions that are affected by the language that is being
      // displayed.
      if (TRUE) {
        $username = [
          '#theme' => 'username',
          '#account' => $revision->getRevisionUser(),
        ];

        // Use revision link to link to revisions that are not active.
        $date = $this->dateFormatter->format($revision->revision_timestamp->value, 'short');

        // We treat also the latest translation-affecting revision as current
        // revision, if it was the default revision, as its values for the
        // current language will be the same of the current default revision in
        // this case.
        $is_current_revision = $vid == $default_revision || (!$current_revision_displayed && $revision->wasDefaultRevision());
        if (!$is_current_revision) {
          /*
          you can add link - example from node module
          $link = Link::fromTextAndUrl($date, new Url('entity.node.revision', ['node' => $node->id(), 'node_revision' => $vid]))->toString();
          */
        }
        else {
          $link = $myEntity->toLink($date)->toString();
          $current_revision_displayed = TRUE;
        }

        $row = [];
        $column = [
          'data' => [
            '#type' => 'inline_template',
            '#template' => '{% trans %}{{ date }} by {{ username }}{% endtrans %}{% if message %}<p class="revision-log">{{ message }}</p>{% endif %}',
            '#context' => [
              'date' => "RevisionId: $vid at $date",
              'username' => $this->renderer->renderPlain($username),
              'message' => ['#markup' => $revision->revision_log->value, '#allowed_tags' => Xss::getHtmlTagList()],
            ],
          ],
        ];
        // @todo Simplify once https://www.drupal.org/node/2334319 lands.
        $this->renderer->addCacheableDependency($column['data'], $username);
        $row[] = $column;

        if ($is_current_revision) {
          $row[] = [
            'data' => [
              '#prefix' => '<em>',
              '#markup' => $this->t('Current revision'),
              '#suffix' => '</em>',
            ],
          ];

          $rows[] = [
            'data' => $row,
            'class' => ['revision-current'],
          ];
        }
        else {
          $links = [];
          $row[] = [
            'data' => [
              '#type' => 'operations',
              '#links' => $links,
            ],
          ];

          $rows[] = $row;
        }
      }
    }

    $build['node_revisions_table'] = [
      '#theme' => 'table',
      '#rows' => $rows,
      '#header' => $header,
      '#attributes' => ['class' => 'node-revision-table'],
    ];

    $build['pager'] = ['#type' => 'pager'];

    return $build;
  }

```

You can customize this that it meet your requirements. This is just an example

## Add routing

extend the my\_module.routing.yml file like so:

```
my_module.my_entity.version_history:
  path: '/admin/my_entity/{myEntity}/revisions'
  defaults:
    _title: 'Revisions'
    _controller: '\Drupal\my_module\Controller\MyEntityController::revisionOverview'
  options:
    parameters:
      myEntity:
        type: entity:MY_ENTITY_ID
  requirements:
    _permission: 'access content'
    myEntity: \d+

```

Important note: the name of the parameter in the curly brackets in path HAS TO MATCH the name of the parameter in the function you are calling to render - in this example:

Parameter name `myEntity` is used as parameter name in function `revisionOverview`

## add task

now you can add a task which should make a tab appear on you entity page. Extend the my\_module.links.task.yml file like so:

```
my_module.my_entity.revisions:
  route_name: my_module.my_entity.version_history
  base_route: my_module.my_entity.canonical
  title: 'Revisions'

```
> 1 votes

---
Tags: 8, entities
---


# Question
Title: How can I set a default value to show for a taxonomy term name in its edit form?
I have a site where I have a taxonomy term. I want to set a default value to the term name and display it when the form loads. In `hook_form_alter()`, I tried to set the default value to *Check term name* in the form. The form loads with this value, but even after changing the value in the text field, the form gets submitted with *Check term name*.

This is the code I am using to set the default value.

```
$form['name']['widget'][0]['value']['#value'] = 'check term name';

```

How do I set the default value?

# Answer
> 0 votes
As @Kevin and @Clive noted, you must use `#default_value` to do this.

```
$form['name']['widget'][0]['value']['#default_value'] = 'check term name';

```

> \[Property\] #default\_value:
>
> **Description:** The value of the form element that will be displayed or selected initially if the form has not been submitted yet. **Should NOT be confused with #value**, which is a hard-coded value the user cannot change!
>
> Source: Form API Reference

(Yes, this is a reference page for D7, but this section still applies.)

---
Tags: forms, 9
---


title: Add html to a node's title

text:
# Question
Title: Add html to a node's title
I have a Drupal 7 site and I'd like to be able to add an HTML link to part of the title of a node. By default Drupal has this as plain text. Is there any way that I can allow HMTL?

Example:

```
This is a <a href="#">title</a>

```

# Answer
> 5 votes
That happens because `template_process_page()` contains the following code.

```
if (!isset($variables['title'])) {
  $variables['title'] = drupal_get_title();
}

```

`drupal_get_title()` calls `check_plain()`.

```
// During a bootstrap, menu.inc is not included and thus we cannot provide a title.
if (!isset($title) && function_exists('menu_get_active_title')) {
  $title = check_plain(menu_get_active_title());
}

```

The only way you have to add HTML to the title is using `hook_preprocess_page()` and set `$variables['title']`.

Keep in mind that `$variables['title']` is also used as value for the `<title>` tag, and browsers should not expect HTML tags inside that tag.

> Titles may contain character entities (for accented characters, special characters, etc.), but may not contain other markup (including comments).

To change just the node title, you should implement `hook_preprocess_node()` and set `$variables['title']`. By default, `template_preprocess_node()` sets it using `$variables['title'] = check_plain($node->title);`.
As side note, for the node title, Drupal 8 uses `$variables['label']`, not `$variables['title']`.

# Answer
> 0 votes
`composer require 'drupal/html_title:^1.4'` did the trick for me.

The `<br>` tag (which was required for my client's request) was even enabled by default. Additionally, the `/admin/config/user-interface/html_title` path allows you to enable arbitrary html tags as serves your needs.

I seem unable for some reason to upvote or comment on Toki's answer, so will try this, instead. Thanks, Toki. Investigating your response resolved this ticket in about ten minutes, call it 30 minutes total to account for investigation to find your note, and to come back and affirm for others that this module works and is fit for purpose as of drupal 9.3.9.

# Answer
> -1 votes
I had the same question and I found a great and simple module HTML Title. You can enter HTML markups inside your node title and it's done. At least, it was OK for me for italic terms.

EDIT : Indeed my answer deserves a -1, the module is now **obsolete** and creates strange things on node titles, forget it!

---
Tags: 7
---


# Question
Title: How to display an image in a Views RSS feed
I am trying to display an image in a Views RSS feed.

1. I have added the following fields to my View: field\_image, field\_title, field\_twitter (These fields come from my node).
2. I then set all fields to "Exclude from display".
3. I then create a new field in Views called "RSS Content".
4. I then place tokens for all the fields (from step 1) into "RSS Content".
5. Finally, I go to "Views \>\> Show \>\> Settings". In the drop down menu for "Description field", I select "RSS Content".

When I look at my RSS feed, the image field is displayed like this:

```
    <description>
<img src="http://example.com/sites/files/nodes/content/images/intro/two-hearts-1312984-1280x960_0.jpg" />
</description>

```

How do I get the image to display properly, instead of the image tags being escaped?

# Answer
> 3 votes
First add the Media RSS namespace to your views-view-rss.tpl.php

```
<rss version="2.0" xml:base="<?php print $link; ?>"<?php print $namespaces; ?> xmlns:media="http://search.yahoo.com/mrss/">

```

Then add the tag to your views-view-row-rss.tpl.php

```
<media:content url="<?php print image_style_url('my_img_style_id', $node->field_image['und'][0]['uri']); ?>" type="image/jpeg" medium="image" height="800" width="600" lang="en" />

```

Lastly make the field data available to your theme's template

```
function mytheme_preprocess_views_view_row_rss(&$vars) {
  $view     = &$vars['view'];
  $options  = &$vars['options'];
  $item     = &$vars['row'];

  // Use the [id] of the returned results to determine the nid in [results]
  $result = &$vars['view']->result;
  $id   = &$vars['id'];
  $node   = node_load( $result[$id-1]->nid );
  $vars['node'] = $node;
}

```

Original article: https://blog.sam-thompson.info/images-in-drupal-rss-feed-without-additional-modules/

# Answer
> 1 votes
I have come across similar problems and somehow CDATA wrapping didn't work for me. I had to override the rss view and create the cdata tag and wrap image inside it programmatically.

I have done this by overriding the RSS view created by views\_rss and views\_data\_export.

Here is how my view setup looks like:

To RSS via custom tpl display I created another display called as Feed which is a data export view of first display.

Here is how the feed display look like which I have overridden.

Next I overrode the Feed display tpl's and created the CDATA image field programmatically.

Here is the view tpl files that I overrode.

Now you would need to preprocess rss view in template to make node's nid available to the views.

```
Here is how I did that.

function bootstrap_preprocess_views_view_row_rss(&$vars) {
$view     = &$vars['view'];
$options  = &$vars['options'];
$item     = &$vars['row'];

// Use the [id] of the returned results to determine the nid in [results]
$result = &$vars['view']->result;
$id   = &$vars['id'];
$node   = node_load( $result[$id-1]->nid );

$vars['title'] = check_plain($item->title);
$vars['link'] = check_url($item->link);
$vars['description'] = check_plain($item->description);
//$vars['description'] = check_plain($node->teaser);
$vars['node'] = $node;
$vars['item_elements'] = empty($item->elements) ? '' :
format_xml_elements($item->elements);
}

```

Lastly here is how I created the CDATA tag programmatically with my custom image style.

Code for two view templates: 1. Display template

```
<?php print "<?xml"; ?> version="1.0" encoding="utf-8" <?php print "?>"; ?>
<rss version="2.0" xml:base="<?php print $link; ?>"<?php print $namespaces;
?>>
<channel>
<title><?php print $title; ?></title>
<link><?php print $link; ?></link>
<description><?php print $description; ?></description>
<language><?php print $langcode; ?></language>
<?php print $channel_elements; ?>
<?php print $items; ?>
</channel>
</rss>

```
2. Row template. Here I am creating CDATA and item tag programmatically.

Hope this helps.

# Answer
> 1 votes
In case someone is looking for a Drupal 9 version of this, here's the code considering your image field is named **field\_image** and you want to use an image style which is called **large** in this case:

In your **mytheme.theme**:

```
use Drupal\node\Entity\Node;

function mytheme_preprocess_views_view_row_rss(&$variables) {
  if($variables['view']->id() == 'your_view_id'){
    $node = Node::load($variables['item_elements'][2]["value"]->__toString());
    $style = \Drupal\image\Entity\ImageStyle::load('large');
    $variables['media'] = $style->buildUrl($node->field_image->entity->getFileUri());
    $variables['media_size'] = $node->field_image->entity->getSize();
  }
}

```

Then in your Twig file views-view-row-rss.html.twig:

```
<item>
  <title>{{ title }}</title>
  <link>{{ link }}</link>
  <description>{{ description }}</description>
  {% for item in item_elements -%}
    <{{ item.key }}{{ item.attributes -}}
    {% if item.value -%}
      >{{ item.value }}</{{ item.key }}>
    {% else -%}
      {{ ' />' }}
    {% endif %}
  {%- endfor %}
  <media:content url="{{ media }}" fileSize="{{ media_size }}" type="image/jpeg" />
</item>

```

# Answer
> -1 votes
wrap it on CDATA tags.

CDATA stands for Character Data and it means that the data in between these strings includes data that could be interpreted as XML markup, but should not be.

---
Tags: 7, views, feeds
---


# Question
Title: how to install drush 10 concurrently with 8?
I'm having some questions about installing the latest drush, and the more I search, the more conflicting advice am I getting. I need both drush 8 and 10, since I am testing both Drupal 7 and 9. I had installed drush 8 a while back as a standalone in /usr/local/bin/drush8. But I can't figure out how to create a similar standalone version of 10 (one that would be moved to /usr/local/bin/drush10). I'm using MACOS 12.3, symfony 4.4.35, PHP 7.4.12, drupal 9.3.9

From Google, answers seem to say to just got to the Drupal 9 installation root and use composer require drush/drush, while drupalize.me says to install it as a dev dependency with composer require --dev drush/drush (is using a dev a good idea?). When I try doing this, it is trying to install drush 11. Should I be using 11 instead of 10? I tried using `composer require "drush/drush: ^10.6.2"` and got a bunch of errors, so using 11 sounds good to me.

https://webchick.net/drush-and-drush has instructions, but I can't get it to work; for one thing, she says to download the release 8.4.1's drush.phar file, with isn't in the repo.

Anyway, I do have 8 working ok, and now would like to be able to use 11 (or 10) for Drupal 9 testing. Can it be installed in a global location like /usr/local/bin, or is tied to each Drupal installation?

# Answer
> 2 votes
Drush for Drupal 9 no longer supports global installation, so you need to install it as a dependency on each Drupal 9 site you want to use it with.

You mentioned being confused by several sites, but the Drush docs are quite clear:

> Drush only supports one install method. It requires that your Drupal site be built with Composer and Drush be listed as a dependency.

So... install drush 11 as a dependency.

Install it as a regular dependency, not a dev dependency because drush should be available in production, and it is a best practice not to install/deploy dev dependencies to production.

You should be using Drush 11 not Drush 10 because, as the documentation linked above states, Drush 10 went end of life in January 2022.

It's good to Google for tutorials, but always check the official documentation first.

---
Tags: drush
---


# Question
Title: How would one make a variable in a module's JS editable via admin menu?
Say I have a library that is attached to a page that brings along with it some JavaScript. For this example, let's say the JavaScript has a variable which is the path to an image that the JS displays. This would look something like:

`variable.setAttribute('src', drupalSettings.path.baseUrl + pathToPicture)`

Now let's say I want to have pathToPicture configurable via Admin Menu so that one may add their own path to their own picture, instead of using the one that is defaulted to.

How might one go about accomplishing this task? I know the basics of setting up a form and adding it to the admin menu, but I don't know enough to complete something like this on my own. If there is existing documentation on this sort of procedure, I'd appreciate being directed towards it. Otherwise any ideas are welcome. Thanks!

# Answer
Imagine a module called Configurable Picture. You want the path to the image to be configurable through the admin UI. A path is a string, so the first thing to do is define the configuration for the path. In this case, the configuration object will be `configurable_picture.settings` `path_to_picture`.

configurable\_picture/config/schema/configurable\_picture.schema.yml:

```
configurable_picture.settings:
  type: config_object
  label: 'Configurable Picture Settings'
  mapping:
    path_to_picture:
      type: string
      label: 'Path to Picture'

```

The next step is to create a configuration form that allows you to edit the configuration item defined in the above schema. Here is the documentation on creating configuration forms: https://www.drupal.org/docs/drupal-apis/configuration-api/working-with-configuration-forms

With the above steps, you will be able save the path to the picture on the configuration page you have created.

There are two steps required to provide that configuration setting to the JavaScript file. The first is to add a dependency to `core/drupalSettings` to your JS library.

configurable\_picture/configurable\_picture.libraries.yml:

```
picture_form:
  js:
    path/to/file.js: {}
  dependencies:
    - core/drupalSettings

```

Then, you attach the library and the settings to the form:

```
$form['#attached']['library'][] = 'configurable_picture/picture_form';
// The configurableSettings key will become the namespace within the
// drupalSettings object in the JS file.
$form['#attached']['drupalSettings']['configurablePicture'] = [
  // Set the path based on the value saved in configuration:
  'pathToPicture' => \Drupal::config('configurable_picture')->get('path_to_picture'),
];

```

Then path/to/file.js can access the settings object like this:

```
(function ($, Drupal, drupalSettings) {

  function getPathToPicture() {
    return drupalSettings.configurablePicture.pathToPicture;
  }

  // Or, from the settings object in Drupal.behaviors:
  Drupal.behaviors.configurablePicture = {
    attach: function (context, settings) {
      console.log(settings.configurablePicture.pathToPicture);
    }
  };
}(jQuery, Drupal, drupalSettings));

```
> 2 votes

---
Tags: 9
---


# Question
Title: Enable layout builder on custom controller page
I created a simple page in a controller class:

```
  public function taxonomyPage($term) {
    return [
      '#type' => 'markup',
      '#markup' => $this->t('<p>Need to render layout builder here.</p>'),
    ];
  }

```

Routing:

```
knmt_taxonomy.detail:
  path: '/tags/{term}'
  defaults:
    _controller: '\Drupal\knmt_taxonomy\Controller\TaxonomyController::taxonomyPage'
  requirements:
    _permission: 'edit own content'

```

For node pages i already use layout builder to render blocks, this is working fine.

I also want to use layout builder within this page. I have zero display options for now, is there a way i can enable layout builder specific for this page so i can render blocks this way?

# Answer
> 3 votes
It's not possible to enable the layout builder without an entity which is supporting view modes.

However, it seems like you have such an entity already. So you could add a view mode at *admin/structure/display-modes/view/add/taxonomy\_term*. Then visit *admin/structure/taxonomy*, select **Manage Display** of a vocabulary and enable the view mode in **Custom display settings** at the bottom of the page. Save and select the tab of the new view mode at the top. Check *Use Layout Builder* in **Layout options**, save again and click the button "Manage Layout".

In the controller render the term with the new view mode:

```
  public function taxonomyPage($term) {
    // load the term or enhance the route to load the term automatically
    // See www.drupal.org/docs/8/api/routing-system/parameters-in-routes/using-parameters-in-routes
    return $this->entityTypeManager()
      ->getViewBuilder('taxonomy_term')
      ->view($term, 'my_view_mode');
  }

```

---
Tags: 9, layout-builder
---


# Question
Title: Is it possible to programmatically add a path alias to a programmatically created node?
I implemented a pre-save hook in a module, which populates nodes by fetching information from various APIs. I can't get my mind around how to add a path alias to newly created nodes.

Is there a function or a class I could use, or should I directly insert data into the database table used for the path alias values?

# Answer
> 23 votes
As of Drupal 8.8, path aliases are now entities, and the method of creating a new alias has changed.

To programmatically save an alias, you can do the following:

```
$path_alias = \Drupal\path_alias\Entity\PathAlias::create([
  'path' => '/unaliased/path',
  'alias' => '/aliased/path',
]);

$path_alias->save();

```

# Answer
> 15 votes
To create the alias, I first check if the alias was set in the user interface, and if it is empty then I set it manually using the label().

```
use Drupal\Core\Entity\EntityInterface;

/**
 * hook_entity_insert
 */
function mymodule_entity_insert(EntityInterface $entity) {
    // Convert to lowercase, trim spaces, replace spaces with underscore and then truncate to 60 chars.
    $pathTitle = substr(str_replace(" ","_",trim(strtolower($entity->label()))),0,60);
    if ($entity->toArray()['path'][0]['alias'] == "") {
        $path = \Drupal::service('path.alias_storage')->save("/node/" . $entity->id(), "/mynewpath/" . $pathTitle, "en");
    }
}

```

https://www.drupal.org/node/1853148 for more information

# Answer
> 6 votes
To get a clean string, you can use this function from the Pathauto module.

```
$clean_string = \Drupal::service('pathauto.alias_cleaner')->cleanString('Your very dirty string, with many URL un-friendly parts!');

```

Source: Drupal: clean a string for use in URLs

# Answer
> 5 votes
Using drupal 8, this is how I did (after a quick search in the **pathauto.module** file) :

**Step 1 : Create the node**

```
$node = Node::create([
    'type' => 'article',
    'title' => 'Test article',
    'langcode' => 'en', // Get it however you want or hardcode it.
    'uid' => [USER_ID] // Get it however you want.
]);

```

**Step 2 : Save the node**

```
$node->save();

```

**Step 3 : Insert the path (make sure the pathauto module is installed and add a pattern for the give node type)**

```
pathauto_entity_insert($node)

```

If you need to update the node programmatically, don't forget to update its path : `pathauto_entity_update($node)` and don't forget to delete it if you delete the node (delete it BEFORE you delete the node of course since the reference will be lost if you don't do that) : `pathauto_entity_delete($node)`

# Answer
> 2 votes
We're maintaining an inofficial port of the pathauto module. You also need token.module, which you can get from here. Use at your own risk, the configuration structure might change, or core updates might break the module temporarily.

Work just started to get those two modules back to drupal.org.

# Answer
> 2 votes
Also if just going a custom content entity and want to add the url path alias to the form ...

just do

```
/**
 * Implements hook_entity_base_field_info().
 */
function YOURMODULE_entity_base_field_info(EntityTypeInterface $entity_type) {
 if (in_array($entity_type->id(), ['ENTITYID'], TRUE)) {
$fields['path'] = BaseFieldDefinition::create('path')
  ->setLabel(t('URL alias'))
  ->setTranslatable(TRUE)
  ->setDisplayOptions('form', [
    'type' => 'path',
    'weight' => 30,
  ])
  ->setDisplayConfigurable('form', TRUE)
  ->setComputed(TRUE);

  return $fields;
}

```

}

# Answer
> 2 votes
You can just set the value of the `path` field:

```
$node->set('path', 'some-alias');
$node->save();

```

# Answer
> 2 votes
For me and Drupal 9.2+ it was:

```
use Drupal\node\Entity\Node;
use Drupal\path_alias\Entity\PathAlias;

$node = Node::create(['type' => 'article']);
$node->set('title', 'article en');
$node->enforceIsNew();
$node->save();
PathAlias::create([
    'path' => "/node/" . $node->id(),
    'alias' => '/article_en_alias',
    'langcode' => 'en',
])->save();
$node_es = $node->addTranslation('es');
$node_es->set('title', 'article es');
$node_es->save();
PathAlias::create([
    'path' => "/node/" . $node_es->id(),
    'alias' => '/article_es_alias',
    'langcode' => 'es',
])->save();

```

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


# Question
Title: How can i redirect to a separate page without filling value in the required field?
I have a site in drupal 9. I have one taxonomy with the name and and another field. I have a Go back button which takes me to a separate destination. The problem is that I cant click go back without giving some values in the name field as it is by default a required field in drupal. I want to click go back without giving anything in that name field. How can i achieve this?

# Answer
> 1 votes
You didn't post any code, but it sounds like your back button is somehow submitting the form (because it is validating the form, and failing required validation).

So the solution is to make sure the back button is not part of the form.

On my own site, to add a back button to some webforms, I add this HTML in a block that is shown on the webform page:

```
<button onClick="window.history.back()" class="button">Back</button>

```

You can easily do something similar with standard HTML + JS.

# Answer
> 1 votes
Another option if you cannot move the back button out of the form (and if it uses the Form API), would be to use the `#limit_validation_errors` property to avoid any form validation.

The example below can be used if you are building the form by yourself. If using a hook\_form\_alter(), just add the `#limit_validation_errors` line.

```
$form['back'] = [
  '#type' => 'submit',
  '#value' => $this->t('Back'),
  '#submit' => ['::yourSubmitCallback'],
  '#limit_validation_errors' => [], // This will ensure any validation is skipped
];

```

---
Tags: 9
---


title: Actual file permisions

text:
# Question
Title: Actual file permisions
With Drupal 8/9 I want to:

1. Have a private file folder
2. Upload files to it (ideally by whatever method but media type will do)
3. Allow only authenticated users (ideally set by role) to view the file (whether by direct link or link in WYSIWYG editor)

Why does this seem to be so hard? Am I missing something?

When I set up private files using the online documentation I can find it either blocks everyone but the Admin from seeing the direct link or allows anonymous users to view the link even if the private files folder is outside the webroot. There seems to be no fine-grained control of permissions.

# Answer
> 1 votes
Drupal terminology can be confusing.

In Drupal core, "public" files mean "open to the public at a publicly accessible URL". With public files, visitors can download the original file and you have no control over that, as the file is made publicly accessible via the webserver.

With "private" files, Drupal itself handles the serving of the files, which means that the files are not available via a webserver URL; instead, Drupal serves the files. **This doesn't have anything to do with permissions or keeping files private from some users; it means keeping files private vs. the web itself.**

## How to add permissions to files

You can use the Private Files Download Permission module. There's a basic tutorial to get you started.

---
Tags: 8, files, .htaccess
---


# Question
Title: CSS, JS, ... not created by drush site:install
Drupal installation is failing after the following steps on a Debian box (database is already created and privileges granted to specific user):

```
(user) $ composer create-project drupal/recommended-project myproject
# ... ask if it is OK to install composer/installers, drupal/core-composer-scaffold and drupal/core-project-message, answer Yes
# ... emits 2 warnings about doctrine/reflection and webmozart/path-util being obsolete
(user) $ cd myproject
(user) $ composer composer require drush/drush
(user) $ drush site:install --locale=fr
# ... asks for MySql credentials
# ... displays admin user credentials.

```

Everything runs smoothly with no error messages, only the 3 questions and 2 warnings mentionned supra.

Yet, after running those commands, according to Drupal installation docs (https://www.drupal.org/docs/develop/using-composer/using-composer-to-install-drupal-and-manage-dependencies#s-install-drupal-using-the-command-line) the site should be accessible through-the-web. Yet, when I browse the configured path (DocumentRoot is set to the "web" directory) I got an unstyled page (same appearance than CSS not applying in drupal 8 site , but stylesheets href are set to "/").

If I take the through-the-web installation path instead of running Drush, then everything runs fine except for minor annoyances with permissions settings under 'web/sites/default'.

Comparing both "web" directories shows that (besides unsignificant differences due to different hashes) no "css", "js", "languages" nor "php" directories are created and populated under the Drush installed "web/sites/default/files" directory.

What am I doing wrong? I could find no help in Drupal documentation nor by googling around.

* Drupal : 9.3.9
* Composer : 2.2.9
* Drush : 11.0.7

Thanks in advance

# Answer
> 1 votes
The folders you mentioned aren't generated by the site installation process, they're generated by a page request to the resulting Drupal site itself.

When you install through the UI, this happens:

* Drupal gets installed
* sites/default/files is created and owned by the web server (typically `www-data`)
* You get redirected to the front end of the site, Drupal generates assets and tries to persist them to sites/default/files
* The request is running as `www-data`, the folder is owned by `www-data`, so all good - files get created.

When you install through the CLI, it's slightly different:

* Drupal gets installed
* sites/default/files is created and owned by the user you ran `drush` as, which wouldn't typically be `www-data` (if it was, you shouldn't be having these problems).
* You visit the site in a browser, Drupal generates assets and tries to persist them to sites/default/files
* The request is running as `www-data`, but the folder is owned by your user, so no dice - files are not created.

High level, the answer is to make sure `www-data` can write to the sites/default/files folder after you install via the CLI. The Securing file permissions and ownerships docs have some suggestions on how you can accomplish this.

---
Tags: drush
---


# Question
Title: If content not published, hide also for admin
I have a entity browser which list my produts. The products are shown only if they are published.

But for some reason, if the product was added before the filtered "publish" was set, the product is still displayed but only for the admin.

How can I prevent the admin to see it ?

EDIT :

For now, I've added a manual check in twig, but i think there's a cleaner way to do it

```
{% for produit in produits %}
   {% if produit['#commerce_product'].published %}
      {{ produit }}
   {% endif %}
{% endfor %}

```

# Answer
> 1 votes
User accounts with permission to view unpublished content can view content.

Usually, the administrator role has the `Administer content` permission, so admins will see unpublished nodes.

To prevent this, go to `/admin/people/permissions`, scroll down to the **Node** category, and remove the `Administer content` permission as well as `Bypass access control` and any other permissions that may allow access to unpublished nodes (contrib modules can add permissions, so I can't list them all here).

If you only have one administrator role, you may want to create a separate role for this purpose.

---
Tags: views, commerce, 9
---


# Question
Title: Is there a way to add a page element to all unpublished pages?
I am trying to add an "unpublished" graphic on all content that is unpublished so editors can clearly see that the page is unpublished, but I have about 15 different content types.

Is there a way I can add this to all content easily with twig templates or otherwise?

# Answer
```
function THEME_preprocess_node(&$variables) {
  $node = $variables['node'];
  if (!$node->isPublished()) {
    $variables['attributes']['class'][] = 'unpublished-class-for-node';
  }
}

```
> 2 votes

---
Tags: theming
---


# Question
Title: Get the node title field value in the form_state
Drupal version 9.3.9.

This is a process of Ajax callback, where the value of the title field is required. In the form node add, the title is displayed. It seems that the form\_state does not apply to the title field. When I inspect the variables, the title field is also not reflected in the values.

```
function my_custom_module_field_widget_process($element, \Drupal\Core\Form\FormStateInterface $form_state,$form) {
  #$element['my_field']['#default_value'] =$form_state->getFormObject()->getEntity()-
  #>label();this work, but useless in the callback

  if(!empty($form_state->getValue('title'))){
    $title = $form_state->getValue('title');
    $element['my_field']['#default_value'] = $title;
  }

  return $element;
}

```

# Answer
> 0 votes
This should work:

```
    $node = \Drupal::routeMatch()->getParameter('node');
    if($node) {
        $title = $node->label();
    }

```

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