title: User Stats Total Nodes, Comments, Flags

text:
# Question
Title: User Stats Total Nodes, Comments, Flags
Is it possible to create a view be that a page or block that shows the following.

-Total number of nodes user has created. -Total number of comments (all comment types) user has posted. -Total of how many times this user nodes or comments have beed Flaged as HELPFUL. (flag module is used for this)

All this using the path /user/%user/stats

Which means contextual filter user ID value will be provided from url path 2.

I am stuck with the three bullet points above. Or how to calculate the totals for each.

# Answer
You can use Views Aggregation to count the number of results for your specific cases.

## Count number of nodes authored by a user

Since your results (to be counted) are nodes it makes sense to create Content type Views:

1. Create Content Views with Page Display and Filter out the node types you want to count later.
2. Add node Title field or Node ID under Fields to test you have the correct list of content. Add Author ID or Name to test the Contextual filter later.
3. Set Path to `user/%user/stats`, set Menu Tab so it displays under Drupal tabs on user pages
4. Add a Contextual filter Author UID (provide default value; User ID from URL) and test by entering user ID number into the Preview, make sure you see all the nodes created by the author with that UID
5. Under Fields make sure you only have **ONE node field** (if you have both NID and Title, remove Title). You can keep the Author fields, for testing if nothing else. We'll use one unique node field to count them aggregated. If we add another node field it will force Views results to show one node per Views row, which will make counting impossible
6. **Remove Sorting criteria** \- they mess up Aggregation!
7. Turn on Aggregation
8. Edit the NID Field Aggregation setting to `Count` or `Count DISTINCT` (if you don't have duplicates in your Views results Count will do)

You now have Views counting the results! If you enter user UID into Preview you will see the number of their nodes in the NID field (adjust the NID field label accordingly).

If you enter `all` into Preview you will see a list of all Authors (from that nodes list, if you kept that Field) and the number of the nodes they created.

If you visit a user page you should see the stats tab added to Drupal tabs. Adjust the Permissions for the Views Page to limit who can see it.

## Count number of comments authored by a user

Repeat the above procedure to count comments, only start with Commment Views Type, set a different Path `user/%user/stats-comment` and Menu tab

## Count flags

To count how many of the listed nodes are flagged you'll need a Relationship to that information, then you'll be able to list a Field with the Flag status and use Aggregation to count them, similar to the above.

There are answers/articles on how to set up Flag Relationship in Views to display various information.
> 1 votes

---
Tags: views, 8
---


# Question
Title: Can I programmatically inject a route parameter?
In my projects I often have some special node (types), for which I'm creating custom admin routes and custom edit forms. E.g. I've got a restaurant homepage offering daily lunch specials. For ease of use I've created a custom route with a custom edit mask for the lunch specials of the upcoming week

`wt_lunch.routing.yml`

```
wt_lunch.week:
  path: '/admin/lunch/week'
  defaults:
    _title: 'Edit daily specials of the week'
    _controller: '\Drupal\wt_lunch\Controller\WtLunchController::week'
  requirements:
    _node_add_access: 'node:wt_lunch'
  options:
    _node_operation_route: TRUE
    _admin_route: true

```

`WtLunchController.php`

```
public function week() {
  //only one node of type wt_lunch exists
  $menuNodes = \Drupal::entityTypeManager()
    ->getStorage('node')
    ->loadByProperties(['type' => 'wt_lunch']);
  $menuNode = reset($menuNodes);

  // edit the lunch node in customized "week" form display mode
  $form = \Drupal::entityTypeManager()
    ->getFormObject('node', 'week')
    ->setEntity($menuNode);

  return \Drupal::formBuilder()->getForm($form);
}

```

Our customers like those reduced edit forms for frequently changing content, I'm using similar variations in multiple projects. However, those routes have the disadvantage of missing a proper `node` route parameter. E.g. a `.module` function like

`othermodule.module`

```
function othermodule_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
  if ($hook == 'node_edit_form') {
    if ($node = \Drupal::routeMatch()->getParameter('node')) {
      $suggestions[] = 'node_edit_form__' . $node->bundle();
    }
  }
}

```

does not work.

Question:

Can my module wt\_lunch somehow inject the node parameter into my custom route, so that othermodule can query the `node` route parameter without changing code in othermodule?

Update:

Those nodes are usually one-off nodes, so I have a constant path without a numeric ID in the path. I need a solution that works without upcasting a variable from the path and without upcasting a hard coded `defaults:` value from routing.yml (I already have a solution with upcasting a "magic" default value, but I want to get rid off hard coded IDs)

# Answer
> 1 votes
You can by altering the existing route.

**wt\_lunch/src/Routing/RouteSubscriber.php**

```
namespace Drupal\wt_lunch\Routing;

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

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

  /**
   * {@inheritdoc}
   */
  protected function alterRoutes(RouteCollection $collection) {
    if ($route = $collection->get('wt_lunch.week')) {
      $route->setPath('/admin/lunch/week/{node}');
      $nid = // some code to get nid
      $route->setDefault('node', $nid);
    }
  }

}

```

**wt\_lunch/wt\_lunch.services.yml**

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

```

# Answer
> 1 votes
In the routing path you want to include the node parameter, so it would be something like this.

```
/admin/lunch/week/{node}

```

For more info look here

---
Tags: 8, routes
---


# Question
Title: Get all the machine names of the date formats
I have searched the Drupal documentation but until now I have not found the way to obtain the machine names of the date formats that the user creates from the Drupal administration.

They would be the formats that appear in the following route:

```
admin/config/regional/date-time

```

What would be the way to obtain them? Is there any service for this?

# Answer
> 3 votes
Date formats are entities so you can use the Entity Type Manager service:

```
$formats = \Drupal::entityTypeManager()
  ->getStorage('date_format')
  ->loadMultiple();

$ids = array_keys($formats);

```

---
Tags: 8, datetime
---


# Question
Title: how to know block id in class which extend from BlockBase
I am trying to get block id in my custom module in build function of class which extends `BlockBase`.

How can i do this?

i want to use this block\_id in another class controller to have configuration of that block so i think best place to get block id and send it to controller class is build function like below code

```
        class XController extends ControllerBase{
        ...
        $block = \Drupal\block\Entity\Block::load($block_id);
        if ($block) {
        ...

```

any other suggestion to do this is appreciated.

# Answer
`BlockBase` is a plugin, and as such has a `getPluginId` method. As you need this inside the class itself, it's as easy as:

```
$id = $this->getPluginId();

```
> 1 votes

---
Tags: 8, blocks
---


# Question
Title: How to create a view block of one content type filtered by author?
I have a Book Club website which has a page created from a view where different members can add their recommendations. I want to create a block view which can be inserted into a Panel Page for each author, but I am struggling to get the contextual filters and relationships to work.

Below is the basic setup of the view page.

This is the contextal filter setting.

This now works if I put a user name into the context preview section. I thought if I added a user context to the panel page this would then allow me to add the user name into the block, but I don't get this option.

# Answer
I eventually got this to work by using a page view with a path /reading-list/% rather than trying to make it work through panels.
> 0 votes

---
Tags: views, panels
---


title: REST export for nested lists of content

text:
# Question
Title: REST export for nested lists of content
Ok here is my problem;

I have a list of boats. Each boat have a list of engines that can be fitted to it. Engine is not in anyway coupled with boat as a particular engine can be mounted to any kind of boat.

Boat is a content type, Engine is a content type. Boat has a field of multiple engines in it.

I want this to output in json like this:

```
[{
    "title":"boat_name",
    "engines":[{
                 "title":"engine_name",
                 "model":"model_nr"
               },{
                 "title":"engine_name2",
                 "model":"model_nr2"
               }]
}]

```

I've tried to accomplish this through the RESTful web service module by creating a REST export view, but there is no formatter that seem to give me json output. I've understood that you could accomplish this by using View Field View but in my case it would require the engine to have a reference to a specific boat, am I right? That is not really what I want.

Is there any other way I can accomplish this?

# Answer
> 3 votes
You can create a custom Normalizer to use and Views will respect that. Within that, you can create the output structure however you like.

Here is an example class, where "Collection" is the content type:

```
<?php

declare(strict_types = 1);

namespace Drupal\mymodule\Normalizer;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Url;
use Drupal\image\Entity\ImageStyle;
use Drupal\serialization\Normalizer\ContentEntityNormalizer;
use Drupal\node\NodeInterface;

/**
 * Class CollectionNormalizer.
 *
 * @package Drupal\mymodule\Normalizer
 */
class CollectionNormalizer extends ContentEntityNormalizer {

  /**
   * The interface or class that this Normalizer supports.
   *
   * @var string
   */
  protected $supportedInterfaceOrClass = [
    'Drupal\node\NodeInterface',
  ];

  /**
   * The entity manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * {@inheritdoc}
   */
  public function __construct(EntityTypeManagerInterface $entity_type_manager) {
    $this->entityTypeManager = $entity_type_manager;
  }

  /**
   * {@inheritdoc}
   */
  public function supportsNormalization($data, $format = NULL) {
    if (!is_object($data) || !$this->checkFormat($format)) {
      return FALSE;
    }

    if ($data instanceof NodeInterface && $data->getType() == 'collection') {
      return TRUE;
    }

    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function normalize($entity, $format = NULL, array $context = []) {
    $attributes = [];
    $attributes['id'] = $entity->id();
    $attributes['title'] = $entity->label();
    $attributes['summary'] = $entity->get('field_summary')->value;
    $attributes['card_type'] = $entity->get('field_directory_view')->value;
    $attributes['list_image'] = NULL;
    $attributes['url'] = Url::fromRoute('entity.node.canonical', ['node' => $attributes['id']])->toString();

    $media = $entity->get('field_featured_image')->getValue();

    if (!empty($media)) {
      $item = $this->entityTypeManager->getStorage('media')->load($media[0]['target_id']);
      $image = $item->get('image')->getValue();

      $attributes['list_image'] = [
        'src' => ImageStyle::load('collection_listing')
          ->buildUrl($item->image->entity->getFileUri()),
        'alt' => $image[0]['alt'],
      ];
    }

    return $attributes;
  }

}

```

You also have to declare it in your modules services.yml file:

```
  mymodule.collection_entity:
    class: Drupal\mymodule\Normalizer\CollectionNormalizer
    arguments: ['@entity_type.manager']
    tags:
      - { name: normalizer, priority: 10 }

```

You can inject whatever other services you need, used entityTypeManager as an example.

Views will use this instead of the default, so long as the priority is higher. IMO this is far easier than trying to use the Views UI to get the output you want from a REST Export display. You are free to construct the output in whatever way you wish.

So in your case, you would replace "Collection" with "Boat" and check that the content type is 'boat', then build the response as you need it.

---
Tags: views, 8
---


# Question
Title: Show only nodes in a view which have the same value of a text field (field_phone_number)
I have a content type "ABC" with the "telephone" field (field\_phone\_number) added to it, and I already have on my website around 1K nodes of this content type "ABC".

I want to display a table view on every node of type "ABC", displaying the title of all other nodes that have the same "telephone value" as the content type being viewed.

I have tried to use the EVA module but with no success.

This is what I have done so far:

* Create an EVA View that show: "content" of type: "ABC".
* Add all the field that I want to display.
* EVA Settings:

+ Entity Type: Content
    + Bundles: ABC
    + Arguments: Check the "Use tokens from the entity the view is attached to" and add: "\[node:field\_phone\_number\]"
* Add contextual filter: "Content ID" with provide default value: "Content ID from URL"

**Results:**

A table view displayed on every node with only the node itself is displayed inside the table !!!! (Not what I need)

**For example:**

If lets say i have 12 nodes that have the same phone number value: "123456789". What I am expecting to see is a view table displaying all the title of those 12 nodes every time I visit the page of each and every node of those 12 nodes.

Usually doing the above by using an entity reference field as the comon value between nodes is very easy, but the problem is that I do not have an entity reference field in commun between the nodes, rather I have the "Phone Number" field which is obviously it is not of type "entity reference".

Any help ?

Thank you,

# Answer
> 1 votes
You're on the right track with EVA. To list sibling nodes based on common taxonomy terms or entity reference fields you can just use Views configuration, but if you need to filter based on a different common field EVA is the way to go.

You did everything right except for the last step - you don't need Content ID Contextual filter, you're not comparing nodes, you're comparing Phone number fields! And the argument you set for EVA is the phone number, so that is the information you need to include there.

Add a Contextual filter for the phone field, you can configure it:
`When the filter value is NOT available`
**Display contents of "No results found"**
`When the filter value IS available or a default is provided`
**Specify validation criteria**
**Numeric** (or whatever is applicable in your case)
**Hide view** (if it doesn't validate)

That should list all the nodes that share that field, including the node you're currently viewing.

If you want to exclude the current node from that list you need to add another Contextual filter, this time it will be the Content ID one:
`When the filter value is NOT available`
**Provide default value**
**Content ID from URL**
`When the filter value IS available or a default is provided`
**Specify validation criteria**
**Content**
\[ABC\]
`More`
**Exclude**

That one will exlude the current node from that list.

---
Tags: views, 8
---


# Question
Title: Creating iframe to be used in other websites
I've created a controller with the route "/get-info", the controller return a template `custom-info.html.twig`

`my_module/src/Controller/MyController.php` file:

```
<?php

namespace Drupal\my_module\Controller;

use Drupal\Core\Controller\ControllerBase;

class MyController extends ControllerBase {

  public function content() {

    return [
      '#theme' => "custom_info",
    ];
  }
}

```

`my_module/templates/custom-info.html.twig` file:

```
<h1> Hello World </h1>

```

When visiting the path: www.mySite.com/get-info I get response with the header an footer of my site and "Hello World" as content. but when using `<iframe src="www.mySite.com/get-info"></iframe>` in an other Drupal project I get an empty iframe.

Is this the right way to create iFrame-ready Widgets in Drupal 8? if so what should I add/remove to make it work?

# Answer
The problem was solved by installing and enabling Allow site iframing contrib module.

> Drupal 8 has a feature built into core that stops the site from being rendered within an iframe, there are good reasons for this - https://www.drupal.org/node/2514136 But sometimes you want your site in an iframe, so that's what this module does.
> 1 votes

---
Tags: 8, page-controller
---


# Question
Title: How to properly alter a field description in hook form alter
I am altering a field description (help text) based on certain conditions. However, sometimes the description element to alter is set at `$element['value']['#description'] = $new_description;` And other times it's set at `$element['#description'] = $new_description;` I am doing this in a field widget alter hook:

```
/**
 * Implements hook_field_widget_form_alter().
 */
function my_module_field_widget_form_alter(&$element, \Drupal\Core\Form\FormStateInterface $form_state, $context) {
    // do the following based on some conditions:
    $html = \Drupal::service('renderer')->render($some_render_array]);
    /* @var Drupal\Core\Field\FieldFilteredMarkup $new_description */
    $new_description = \Drupal\Core\Field\FieldFilteredMarkup::create(t($element['#description'] . $html));
    // For some elements this works:
    $element['#description'] = $new_description;
    // For other I need to do this:
    $element['value']['#description'] = $new_description;
  }

}

```

Whats the difference? and do I have to use some logic to target the appropriate element?

# Answer
The difference is that `$element['value']['#description']` is the description for the property `value`. If you have a field with multiple properties you can set a description for each property. This is the common practice, but a field widget doesn't have to do it this way. Check formElement() of the widget to see how the element keys are used. Most widgets copy the content of `$element` to the subkey `value`, but not all.
> 0 votes

---
Tags: 8, nodes, forms
---


# Question
Title: Can't acces my site and without login and drush clear cache
I moved my local site to shared host.

* all code has been deployed to the staging server
* I changed settings.php
* import the database from local to the staging server database

When I try to reach the site I get an `HTTP ERROR 500`. In phpMyAdmin I TRUNCATE the cache tables:

```
TRUNCATE cache_config;
TRUNCATE cache_container;
TRUNCATE cache_data;
TRUNCATE cache_default;
TRUNCATE cache_discovery;
TRUNCATE cache_dynamic_page_cache;
TRUNCATE cache_entity;
TRUNCATE cache_menu;
TRUNCATE cache_render;
TRUNCATE cache_toolbar;

```

If I look in the error logs I see:

```
AH01071: Got error 'PHP message: PHP Fatal error:  Class 'Drupal\\Core\\Cache\\DatabaseBackend' not found in /home/mysite.nl/public_html/staging/index.php on line 16\n'

```

What do I have to do?

# Answer
I was missing `$settings['hash_salt'] = '';`. I changed this to `$settings['hash_salt'] = 'CHANGE THIS';` after this I could reach the site!
> 0 votes

# Answer
Make sure your database connection to the DB server is correct. I was using an older local password for DB which caused a similar issue when moving to live hosting.
> 0 votes

---
Tags: drush, caching, staging
---


# Question
Title: How to update the database schema without losing existing data
I have a custom module with my\_module.install file defining my two data tables in mysql. Module is already installed and I am able to save data from forms into them. Now my requirements changed and I need to create a third table in the same module. How do I approach the reinstall of the module.

1) Prepare MYSQl scripts to backup the existing data into temp tables and after uninstall and reinstalling the module, copy back the data into tables from temp tables.

2) Looked into https://www.drupal.org/project/backup\_migrate not compatible to drupal core version I am using 8.6.0

3) Any other modules or ideas?

edited after the suggestion: I have created the below function in mymodule.install file and also edited mymodule\_schema() method and run drush updatedb. Still no db changes as expected table is not getting created. Not able to look into system table to get the recent version number as mentioned elsewhere because that table is not available in my current local DB.

```
   function mymodule_update_8601(){
    $spec = array(
    'fields' => array(
     'application_setup_id' => array(
     'type' => 'serial',
     'not null' => TRUE,
     'description' => 'Primary Key: ID',
     ),
    ),
   );
   $schema = Database::getConnection()->schema();
   $schema->createTable('application_setup', $spec);
  }

```

# Answer
> 5 votes
When you want to apply database schema changes you don't reinstall the module. You apply these changes via `hook_update_N()` from your `MYMODULE.install` file.

See Updating Database Schema and/or Data in Drupal 8 on drupal.org.

> If your module is making a data model change related to database schema that your module defines with `hook_schema()`, then you need to properly update your data model. The two steps are:
>
> 1. Update your `hook_schema()` code so that it reflects your new data model if the database table and field definitions have changed. This will make sure that people that install your module after you made the change will install the correct database tables. See the Schema API documentation for details on that.
> 2. Write a `hook_update_N()` function. This will update the database for existing users of your module who already had it installed before you made the change so that they can continue to function. This is described below.

There also is a sample on how to add a new table from `hook_update_N()`:

> ```
> $spec = [
>   'description' => 'My description',
>   'fields'      => [
>     'myfield1' => [
>       'description' => 'Myfield1 description.',
>       'type'        => 'varchar',
>       'length'      => 255,
>       'not null'    => TRUE,
>       'default'     => '',
>     ],
>     'myfield2' => [
>       'description' => 'Myfield2 description',
>       'type'        => 'text',
>       'not null'    => TRUE,
>     ],
>   ],
>   'primary key' => ['myfield1'],
> ];
> $schema = Database::getConnection()->schema();
> $schema->createTable('mytable2', $spec);
>
> ```

---
Tags: 8, database, updating
---


# Question
Title: Render View with title in twig template?
Is there a version of drupal\_view (or alternative) that will render a view with the title? It's a pain to hardcode titles or preprocess individual views just to get a title to render that is already configured per display from the views interface.

# Answer
> 1 votes
You should develop a custom twig extension for that.
I developed a simple module twig\_views for that purpose check it out.

Example of use:

```
 {{ render_view('view_machine_name', 'display_name') }}

```

if you use `8.2.6` drupal version, and twig\_xdebug Module you may have some error like:

> The website encountered an unexpected error. Please try again later. Symfony\Component\DependencyInjection\Exception\LogicException: Service 'twig\_xdebug' for consumer 'twig' does not implement Twig\_ExtensionInterface.

Those steps in this comment will solve your problem.

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


# Question
Title: Alter The User Login Form Into Multi Steps
I want to alter my default user login form into multi step. therefore I have wrote a code which I'm pasting below. but this code is not working, whenever I click on "Next" button it will return on the first step. please help me to get rid out of this problem. Here is my code:

```
function signin_form_alter(&$form, &$form_state, $form_id) {
  if ($form_id == 'user_login' || $form_id == 'user_login_block') {
    unset($form['name']);
    unset($form['pass']);
    unset($form['actions']);
    $form['mail'] = [
      '#title'       => t('Email'),
      '#description' => t('Enter your Email'),
      '#type'        => 'textfield',
      '#required'    => 'true',
      '#maxlength'   => 40,
    ];
    $form['next'] = [
      '#name'   => 'next',
      '#value'  => t('Next'),
      '#type'   => 'submit',
      '#submit' => ['signin_step1_form_submit'],
    ];
    $form['#validate'] = ['signin_step1_form_validate'];
  }
}

function signin_step1_form_validate($form, &$form_state) {
  $mail = $form_state['values']['mail'];
  if (empty(user_load_by_mail($mail))) {
    drupal_set_message(t('The entered email is not associated with any account.'));
  }
}

function signin_step1_form_submit($form, $form_state) {
  $form_state['stored_values']['mail'] = $form_state['values']['mail'];
  return signin_step2_form($form, $form_state);
}

function signin_step2_form($form, &$form_state) {
  $form['pass'] = [
    '#title'       => t('Password'),
    '#description' => t('Enter the password'),
    '#type'        => 'password',
    #required' => 'true',
  ];
  $form['signin'] = [
    '#name'   => 'signin',
    '#value'  => t('SignIn'),
    '#type'   => 'submit',
    '#submit' => ['signin_step2_form_submit'],
  ];
  $form['#validate'] = ['signin_step2_form_validate'];
}

function signin_step2_form_validate($form, &$form_state) {
  $mail = $form_state['stored_values']['mail'];
  $user_obj = user_load_by_mail($mail);
  $name = $user_obj->name;
  $pass = $form_state['values']['pass'];
  if (!user_authenticate($name, $pass)) {
    drupal_set_message('error', t('The Password you entered is incorrect'));
  }
}

function signin_step2_form_submit($form, &$form_state) {
  $mail = $form_state['stored_values']['mail'];
  $user_obj = user_load_by_mail($mail);
  $form_state['uid'] = $user_obj->uid;
  user_login_submit($form, $form_state);
}

```

# Answer
Finally I got it to worked. here is my code, I'm pasting this here to saves someone time.

```
function signin_form_alter(&$form, &$form_state, $form_id) {
  if ($form_id == 'user_login' || $form_id == 'user_login_block') {
    unset($form['name']);
    unset($form['pass']);
    unset($form['actions']);
    if (isset($form_state['step2'])) {
      $form = signin_step2_form($form, $form_state);
      return $form;
    }
    $form['mail'] = [
      '#title'       => t('Email'),
      '#description' => t('Enter your Email'),
      '#type'        => 'textfield',
      '#required'    => 'true',
      '#maxlength'   => 40,
    ];
    $form['next'] = [
      '#name'   => 'next',
      '#value'  => t('Next'),
      '#type'   => 'submit',
      '#submit' => ['signin_form_submit'],
    ];
    $form['#validate'] = ['signin_step1_form_validate'];
    return $form;
  }
}

function signin_step1_form_validate($form, &$form_state) {
  $mail = $form_state['values']['mail'];
  if (empty(user_load_by_mail($mail))) {
    form_set_error('mail', t('The entered email is not associated with any account.'));
  }
}

function signin_step2_form($form, $form_state) {
  $form['pass'] = [
    '#title'       => t('Password'),
    '#description' => t('Enter the password'),
    '#type'        => 'password',
    #required' => 'true',
  ];
  $form['signin'] = [
    '#name'   => 'signin',
    '#value'  => t('SignIn'),
    '#type'   => 'submit',
    '#submit' => ['signin_form_submit'],
  ];
  $form['#validate'] = ['signin_step2_form_validate'];
  return $form;
}

function signin_step2_form_validate($form, &$form_state) {
  $mail = $form_state['stored_values']['mail'];
  $user_obj = user_load_by_mail($mail);
  $name = $user_obj->name;
  $pass = $form_state['values']['pass'];
  if (!user_authenticate($name, $pass)) {
    form_set_error('pass', t('The Password you entered is incorrect'));
  }
}

function signin_form_submit($form, &$form_state) {
  if ($form_state['clicked_button']['#name'] == 'next') {
    $form_state['step2'] = TRUE;
    $form_state['stored_values']['mail'] = $form_state['values']['mail'];
    $form_state["rebuild"] = TRUE;
  }
  else {
    $mail = $form_state['stored_values']['mail'];
    $user_obj = user_load_by_mail($mail);
    $form_state['uid'] = $user_obj->uid;
    user_login_submit($form, $form_state);
  }
}

```
> 0 votes

---
Tags: 7, users, forms
---


title: Missing argument 1 for hook_menu()

text:
# Question
Title: Missing argument 1 for hook_menu()
I'm getting this error in my custom module.

> \[warning\] Missing argument 1 for signin\_menu(), called in /public\_html/includes/menu.inc on line 2813 and defined signin.module:2

here is my code to create hook menu call back.

```
function signin_menu(&$items) {
  $items = array();

  $items['signin'] = array(
    'title' => 'Sign In',
    'description' => 'Sign In Form',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('user_login'),
    'access callback' => TRUE
  );

  return $items;
}

```

# Answer
No need to pass arguments to `hook_menu` Just remove the param `&$items`:

```
/**
 * Implements hook_menu().
 */
function wa_simulator_menu() {
  $items = array();
  $items['signin'] = array(
    'title'           => 'Sign In',
    'description'     => 'Sign In Form',
    'page callback'   => 'drupal_get_form',
    'page arguments'  => array('user_login'),
    'access callback' => TRUE,
  );

  return $items;
}

```
> 1 votes

# Answer
In Drupal 6 and Drupal 7, `hook_menu()` implementations don't get any argument. That is the reason of the error message.
They got an argument in previous Drupal versions, but it was not passed by reference. It's probable you are getting confused between `hook_menu()` and `hook_menu_alter()`. The latter gets a `$&items` parameter, but the purpose of the hook is slightly different. (It's altering routes defined from other modules, rather than defining new routes.)

As side note, I would rather use the following code.

```
function signin_menu() {
  $items = array();

  $items['signin'] = array(
    'title' => 'Sign in',
    'description' => 'Sign in form',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('user_login'),
    'access callback' => 'user_is_anonymous',
  );

  return $items;
}

```

The login form should be visible only to anonymous users. Logged in users should not see it, since they already logged in, but the `'access callback' => TRUE` line is making it accessible to every user, contrary to what Drupal (correctly) does.
> 2 votes

---
Tags: 7, routes
---


# Question
Title: Workbench Moderation: Published data overriding draft data on draft view
Moderation's draft content not showing on the front end.

When a node has both a published and a draft, the data from the published overrides the draft data on the front end when viewing the draft.

For example www.example.comnode/6 and www.example.com/node/6/draft will display identically.

Does this require a special hook in the theme?

# Answer
It was a two-part solution. There was a bug that needed patch #16 from here: https://www.drupal.org/project/workbench\_moderation/issues/2021903

I also needed to add vid to node\_load: When I combined the `$vids = $node->vid;
 $node = node_load( $nid, $vids );`  with patch #16 from: https://www.drupal.org/project/workbench\_moderation/issues/2021903 it solves the issue.
> 1 votes

---
Tags: 7, theming, workbench
---


# Question
Title: Can I have more than one child content type for a book content type?
I have two content type I selected as book content type: Text\_book\_type and audio\_book\_type.

Now, I need that these content have different child content type:

chapter\_type for text\_book\_type and audio\_chapter\_type for audio\_book\_type.

Is there a way in Drupal 8 to achieve this?

# Answer
Yes you can create two different Content types for your chapter nodes, just like you have different Content types for your book nodes.

The "Parent-Child" relationship you describe is usually accomplished with Entity references.

Add Entity reference fields to your chapter\_type and audio\_chapter-type and configure them to reference text\_book\_type and audio\_book\_type nodes respectively.

Then when creating a chapter node you'll be able to choose which book node it belongs to.

You can use Views to list all child nodes on a book page through that reference field, if you need such a list.
> 1 votes

---
Tags: 8, nodes
---


# Question
Title: "Experimental modules found: Place blocks" but I am using core 8.6
I am using Drupal 8.6.2. When I open the status report I see the following warning:

> Experimental modules found: Place Blocks. Experimental modules are provided for testing purposes only. Use at your own risk.

I researched this issue and found that place blocks has been part of Drupal Core since 8.2.

When I check the list of installed modules then I see no reference to a module named place blocks or `block-place`. I also ran `drush pm-list` and grepped for `place` and `block`. Is it possible that my database contains some outdated dangling reference to this module? How can I clean up my Drupal installation and get rid of this warning?

# Answer
The module is present since Drupal 8.2, but since Drupal 8.4 it has been marked hidden. That explains why you get the error, but you don't find the module: The module was enabled before updating to Drupal 8.4 or later, and it is still installed; once Drupal was updated to any version after 8.4, the module has been just marked as hidden (without changing its status of installed module).

If you look at the content of the block\_place.info.yml file (core/modules/block\_place/block\_place.info.yaml), you will notice the following lines.

```
package: Core (Experimental)
version: VERSION
core: 8.x
hidden: true

```

If you remove the `hidden: true` line, and visit admin/modules to install a module that you then uninstall, you will see that module listed in /admin/modules/uninstall, and you will be able to uninstall it.
Installing and uninstalling a module is done just to force Drupal to clear the cache it uses to keep the list of found modules. In this case, Drupal will notice the module is not marked anymore as hidden, and it will show it again.
> 2 votes

---
Tags: database, updating
---


title: Multiple Field , same ajax callback

text:
# Question
Title: Multiple Field , same ajax callback
I created a custom field. The hook\_field\_widget\_form has an submit element with an ajax callback:

```
  //Add more
  $element['add_zone'] = array(
    '#type' => 'submit',
    '#name' => $field_name . '-add_more-' .'name',
    '#value' => t('+'),
    '#limit_validation_errors' => array(),
    '#submit' => array('dms_polygone_add_more_add_one'),
    '#ajax' => array(
      'callback' => 'dms_polygone_add_more_callback',
      'wrapper' => 'zones-fieldset-wrapper-'. $field_name,
    ),
  );

```

I add 2 fields of my custom field type to a node. Ex: field\_polygone (field type: polygone) and field\_polygone\_2 (field type: polygone). When i press the 'add\_more' button of my first field\_polygone, my callback function 'dms\_polygone\_add\_more\_add\_one' thinks the trigger element is field\_polygone\_2.

Callback function:

```
function dms_polygone_add_more_callback($form, $form_state) {
  //@todo: $i gebruiken voor veld als er meerdere waardes zijn toegestaan.
  $parents = $form_state['triggering_element']['#parents'];
  return $form[$parents[0]][$parents[1]][$parents[2]]['zones'];
}

```

$parents\[0\] is the field\_name =\> When i press 'add\_more' , the $parents\[0\] variable in the callback function should be 'field\_polygone', but its field 'field\_polygone\_2'.

How can is solve this problem? Why does the callback function think the trigger element is field\_polygone\_2 instead of field\_polygone.

# Answer
I had a similar issue in Drupal 8. I had one form with multiple entities (not drupal entities) that had an identical subset of fields and each had their own button. My `$form_state->getTriggeringElement()` would always show the last of these buttons, no matter which I clicked. My issue was that I was not setting `#name` on the buttons and they were both defaulting to `op` (they had the same `#name` value). Explicitly setting a unique `#name` for each button resolved my issue.
> 2 votes

# Answer
The following link helped me to change 2 elements from a form through AJAX callback, whitout having to return the whole `$form` :

Is it possible to replace more than one form element (wrappers) triggered by only one #ajax trigger element?
> -1 votes

---
Tags: 7, forms, ajax
---


# Question
Title: Drush 9 pulling incorrect site information on Multi-site
We have a multisite set up with Drupal 8.6.1 and Drush 9.4.0. This is set up on two servers that were set up identically.

The issue we are running into is that Drush is only detecting defaultsite, no matter what directory we are in.

This is creating two issues:

On server #1 - defaultsite no longer exists. Drush is still trying to use it's URI and DB variables, so is throwing an "unable to bootstrap the Drupal database" error, and we cannot run any database-related commands.

On server #2 - defaultsite exists, so it is able to run commands. But, when we try to enable a module, it enables the module on defaultsite no matter what site's folder we are in.

Here is what drush status outputs (whe inside of the directory: gtest.ng.evenvision.com):

```
 Drupal version   : 8.6.1
 Site URI         : default
 DB driver        : mysql
 DB hostname      : localhost
 DB port          : 3306
 DB username      : defaultuser
 DB name          : defaultsite
 Database         : Connected
 Drupal bootstrap : Successful
 Default theme    : ev_base_child
 Admin theme      : ev_seven
 PHP binary       : /usr/bin/php
 PHP config       : /etc/php.ini
 PHP OS           : Linux
 Drush script     : /usr/bin/drush
 Drush version    : 9.4.0
 Drush temp       : /tmp
 Drush configs    : /var/www/nginx/vendor/drush/drush/drush.yml
                    /var/www/nginx/drush/drush.yml
 Install profile  : minimal
 Drupal root      : (removed)
 Site path        : sites/default
 Files, Public    : public
 Files, Private   : private
 Files, Temp      : /tmp

```

And, this is what settings.php for gtest.ng.evenvision.com contains:

```
$databases['default']['default'] = array (
  'database' => 'gtest2018',
  'username' => 'gtest2018',
  'password' => '54352432346greg433',
  'prefix' => '',
  'host' => 'localhost',
  'port' => '3306',
  'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql',
  'driver' => 'mysql',
);

```

# Answer
> 1 votes
I don’t know how your setup is now but you need to have something like this.

```
 sites/site-name-1/settings.php

```

Containing

```
$databases['default']['default'] = array (
  'database' => 'gtest2018',
  'username' => 'gtest2018',
  'password' => '54352432346greg433',
  'prefix' => '',
  'host' => 'localhost',
  'port' => '3306',
  'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql',
  'driver' => 'mysql',
);

```

And then the same for the other instances.

Then when you want to install a module in one instance run `drush en MODULE_NAME -l site-name-1`.

For more info look here: Multisite folder structure in Drupal 8.

---
Tags: 8, drush
---


# Question
Title: Programatically change autocomplete views exposed filter by a select list
I have a view which has a filter by users that created de nodes. This filter is exposed, in order to select an user and filter the results by this user. I would like to change the default autocomplete filter by a select list of user, and in this list I want to show only some users.
I followed this tutorial but without success. Although I change "uid" form field, It does anything. How could I do it? Thanks

UPDATE: my code.

```
function my_module_form_views_exposed_form_alter(&$form, FormStateInterface $form_state, $form_id) {

$pos = stripos($form['#id'], 'views-exposed-form-my-form');
if ($pos !== false) {

    $opcusuarios = array();
    $query = \Drupal::entityQuery('user');
    ... some conditions ...
    $entity_ids = $query->execute();
    $usuarios = \Drupal::entityTypeManager()->getStorage('user')->loadMultiple($entity_ids);
    foreach($usuarios as $user) {
        $opcusuarios[$user->id()] = $user->getAccountName();
    }

    // Change the input
    $form['uid']['#type'] = 'select';
    $form['uid']['#multiple'] = FALSE;

    // Add the $options from above to our select list
    $form['uid']['#options'] = $opcusuarios;
    unset($form['uid']['#size']);
}

```

}

# Answer
It won't work because `validateExposed` function from `core/modules/user/src/Plugin/views/filter/Name.php` expect the values to be something like :

```
array:1 [▼
   0 => array:1 [▼
     "target_id" => "1"
   ]
 ]

```

So if you just modified the autocomplete to select you will send the value of `uid` like `1` for example:

**Suggested solution**

* Add Validation function before `validateExposed` and refactor the value of input uid.
Here is code snippet:

```

/**
 * Implements hook_form_FORM_ID_alter().
 */
function my_module_form_views_exposed_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  // If not the view we are looking, move on
  if (stripos($form['#id'], 'views-exposed-form-my-form')) {
    return FALSE;
  }
  //- query
  $query = \Drupal::entityQuery('user');
  /**
   * @TODO: Add your conditions.
   */
  $options = [];
  //- get user ids
  $entity_ids = $query->execute();
  //- Get Users List
  $users_list = \Drupal::entityTypeManager()
    ->getStorage('user')
    ->loadMultiple($entity_ids);
 //- Prepare the list options.
  foreach ($users_list as $user) {
    $options[$user->id()] = $user->getAccountName();
  }
  if (isset($form['uid'])) {
    //- alter the Uid field.
    $form['uid'] = [
      '#type'         => 'select',
      '#options'      => $options,
      '#empty_option' => t('-- Select User --'),
      '#multiple'     => FALSE,
    ];
    unset($form['uid']['#size']);
    //- Add Validate in the beginning To be before validateExposed
    array_unshift($form['#validate'], 'validateUserExposedFilter');
  }
}

/**
 * @param $form
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 */
function validateUserExposedFilter(&$form, FormStateInterface $form_state) {
//- Get the input value
  $input = $form_state->getValue('uid');
  //- Prepare value to be like the expected
  if ($input) {
    $value = [
      0 => [
        "target_id" => $input,
      ],
    ];
    //- Set the new value
    $form['uid']['#value'] = $value;
    //- Update the form state
    $form_state->setValue('uid', $value);
  }
}

```
> 2 votes

---
Tags: views, 8
---


# Question
Title: Can You Render Data from Forced Fields in Views While Using format "Show: Content"
Drupal 8.x

Is it possible to render fields in a view when the display format setting is set to format "Show: Content" ?

For my case, the contrib module I am using has some views integration that provides field data I would like to show, but have found to be difficult to access via preprocess. I also have some programmatically created data I need to show via my template provided by a `HOOK_preprocess_HOOK()`.

I am kind of stuck in the middle. So I am curious if there is a way to render, or maybe get the data of, the forced fields while setting my view to "format: content"?

# Answer
> 0 votes
Yes, you have to decide whether you want to render Content or Fields. Those are two different concepts in Views. The first renders the entity template in the selected view mode and you configure the fields in the content type. The second renders Views field plugins which you configure in Views UI.

You can approach the issue from both sides:

# Show: Content | View Mode

If you start with a View showing content, in the view mode you've selected, display a field for a second View dispay of the same entity. In the second View display select **Show: Fields**.

You need a module to add the View field to the content type, for example **EVA: Entity Views Attachment** or **Views Reference Field**.

# Show: Fields | Settings

Or start with showing fields, add a field for the rendered entity

and select a view mode, which you then can configure, preprocess and theme like a normal node template.

---
Tags: views, 8
---


title: Can not use a comment display mode

text:
# Question
Title: Can not use a comment display mode
In my comment type, I created a "teaser" view.

When I enable the debug mode in the "service.yml" file, the display modes of the comments do not appear in the suggestions.

Why ?

How to use a different display mode than the default one ?

How to customize the display mode "teaser" with a twig file ?

I created a file named `comment--field-article-commentaire--teaser.html.twig`

It contains the following code, but it is not rendered

```
<article{{ attributes.addClass('js-comment comment--field-article-commentaire--teaser') }}>

  test comment

</article>

```

I also created the following file `comment--field-article-commentaire.html.twig`

It contains the following code. It works, but impossible to use "teaser"

```
<article{{ attributes.addClass('js-comment comment--field-article-commentaire') }}>

  <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 bs-region-comment">

    <div class="col-xs-3 col-sm-3 col-md-2 col-lg-2 bs-left">
      {{ drupal_entity('user', comment.uid.target_id, 'comment') }}

      <div class="bs-field-status">
       {{ drupal_entity('user', comment.uid.target_id, 'status') }}
      </div>

    </div>

    <div class="col-xs-9 col-sm-9 col-md-10 col-lg-10 bs-right">
     <b><i>{{ created }}</i></b>
     {{ content.field_article_commentaire }}
     {{ content.links }}
     {{ content.flag_report_comment }}
    </div>

  </div>

</article>

```

**UPDATE**

I found this :

https://www.drupal.org/docs/8/theming/twig/twig-template-naming-conventions

```
Pattern: comment--[comment-field-name]--[node-type].html.twig
Base template: comment.html.twig (base location: core/modules/comment/template/comment.html.twig)

```

There is no explanation for displaying a display mode. However, it is possible to add display modes to the type of comment.

How to use them ?

# Answer
> 0 votes
`{{ drupal_entity('comment', content.comment_body['#object'].id, 'teaser') }}`

should do the trick.

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


# Question
Title: Get the thumbnail of the uploaded video programmatically
I have a content type include a file field for uploading videos, what I want to do is to get the thumbnail of the uploaded video automatically from my custom module.

# Answer
> 1 votes
I've solved it by using PHP FFmpeg

`$ffmpeg = Drupal::service('php_ffmpeg');
$video = $ffmpeg->open('sample.mp4');
$video
->frame(FFMpeg\Coordinate\TimeCode::fromSeconds(1))
->save('frame.jpg');`

---
Tags: 8, entities
---


# Question
Title: How to render a variable which is having a wysiwyg type format?
I am creating a variable in admin section that is of form type **#type = text\_format** to get wysiwyg kind of editor. But having difficulty while displaying the field value in template file. I am trying to print like below in template file:

```
print_r(variable_get('web_pagetext', ''));

```

But in frontend its showing like below:

```
Array ( [value] => text, text..
[format] => full_html )

```

How I can remove Array(\[value\], \[format\]) and only text to get displayed.

# Answer
> 0 votes
You should use `check_markup` for that:

```
$item = variable_get('web_pagetext', ['value' => '', 'format' => '']);
$formatted_text = check_markup($item['value'], $item['format');

```

---
Tags: 7, forms
---


# Question
Title: How can I have separate databases for each domain (countries) of my Drupal website?
I want to have one Drupal website codebase and one apache server instance, but have each country (domain) have it's own database.

Practical example:

I want all content to be completely separated from each other. One country doesn't have access to another countries database. I want to avoid having to add a new content type 9 times because I have 9 different databases. How can I fix this problem?

# Answer
> 2 votes
It is pretty simple to set up a multi-site with a single codebase. You could have the following directory structure (source):

```
-- core

-- modules
   -- contrib
   -- custom

-- themes
   -- contrib
   -- custom

-- sites
   -- site1.com
         -- modules
            -- custom
            -- contrib
         -- themes
            -- custom
            -- contrib
         -- files
         -- settings.php
   -- site2.com
         -- modules
            -- custom
            -- contrib
         -- themes
            -- custom
            -- contrib
         -- files
         -- settings.php
   -- site3.com
         -- modules
            -- custom
            -- contrib
         -- themes
            -- custom
            -- contrib
         -- files
         -- settings.php

```

The directories

```
--core

-- modules
   -- contrib
   -- custom

-- themes
   -- contrib
   -- custom

```

will be available to all your sites.

It is the

```
-- sites

```

directory that is responsible for the multisite. Take `-- site1` as an example:

The modules, themes, and files for the site1 could stay in this directory. What is not mentioned in the documentation is the use the `settings.php` file. Each site could have its own settings.php file. This is typically a copy of `sites/default/default.settings.php`. This also means that each site could have its own database. And the entry for the database is made in the `$databases` array inside their respective settings.php files.

## If you want to share config:

The steps that could be followed:

1. Point the virtual host (domain) for `site1.com` to the Drupal root, i.e. the directory which has `core`, `modules`, `themes`, etc.
2. Visit `site1.com` and install Drupal normally.
3. Edit the `sites/site1.com/settings.php` to add the location of the directory where you wish to save the configuration yml files using the variable `$config_directories[CONFIG_SYNC_DIRECTORY] = '<RELATIVE_PATH_TO_DIRECTORY>';`
4. Export configuration using Drush - `drush cex`. This will export complete configuration in the configured sync directory.

For site2.com and more subsites:

1. Dump the database of site1.com and clone it to a new database to use as a database for site2.com. Cloning the exact same database is important as the configuration management works on cloned sites and not freshly installed ones. This is because the site's UUID needs to be the same.
2. Have the value of the cloned database in `sites/site2.com/settings.php`. This means you do not have to install Drupal again. Just using the database of an already installed Drupal site.
3. Copy the variable `$config_directories[CONFIG_SYNC_DIRECTORY]` along with its value from `sites/site1.com/settings.php` to `sites/site2.com/settings.php`. This will let you use the same configuration for `site2.com` as well.
4. Follow steps 1-3 above to create more multi-sites.

---
Tags: 8, database, multi-sites
---


title: How to disable H1 heading in CKeditor?

text:
# Question
Title: How to disable H1 heading in CKeditor?
I'd like to give some authors a better SEO experience. How can I disable Heading 1 in builtin CKeditor in Drupal 8?

# Answer
> 12 votes
1) Take a look at the editors you're using on your site at /admin/config/content/formats

2) For each editor, you'll need to update the allowed tags. Let's edit the FULL HTML format for this example, so click on the "Configure" button for "Full HTML" (/admin/config/content/formats/manage/full\_html).

3) Under "Enabled Filters", click the checkbox labelled "Limit allowed HTML tags and correct faulty HTML". A filter settings form will appear below. Remove the h1 tag from the field labelled "Allowed HTML Tags".

4) Save

Test that it's removed from the drop down:

# Answer
> 3 votes
```
/**
 * Implements hook_editor_js_settings_alter().
 */
function YOURMODULE_editor_js_settings_alter(array &$settings) {

    // Set default CKEditor format tags
    $settings['editor']['formats']['html']['editorSettings']['format_tags'] = "p;h2;h3;h4;h5;h6";
}

```

`['html']` may need to be replaced by the machine name of the text format you would like to change.

# Answer
> 2 votes
You can create a custom Plugin for that:

```
<?php

namespace Drupal\custom_module\Plugin\CKEditorPlugin;

use Drupal\editor\Entity\Editor;
use Drupal\ckeditor\Plugin\CKEditorPlugin\Internal as InternalCore;

/**
 * Defines the "internal" plugin (i.e. core plugins part of our CKEditor build).
 *
 * @CKEditorPlugin(
 *   id = "internal",
 *   label = @Translation("CKEditor core")
 * )
 */
class Internal extends InternalCore {

  /**
   * {@inheritdoc}
   */
  public function getConfig(Editor $editor) {
    $config = parent::getConfig($editor);
    $config['format_tags'] = 'p;h2;h3;h4;h5;h6;pre';
    return $config;
  }

}

```

And

```
/**
 * Modify the list of available CKEditor plugins.
 *
 * This hook may be used to modify plugin properties after they have been
 * specified by other modules.
 *
 * @param $plugins
 *   An array of all the existing plugin definitions, passed by reference.
 *
 * @see CKEditorPluginManager
 */
function custom_module_ckeditor_plugin_info_alter(array &$plugins) {
  $plugins['internal']['class'] = 'Drupal\custom_module\Plugin\CKEditorPlugin\Internal';
}

```

# Answer
> 1 votes
From http://www.drupalthemer.org/blog/drupal-8-how-to-ckeditor-custom-styles:

Disable Unwanted Buttons

Visit /admin/config/content/formats/manage/text format name.

Under 'Toolbar Configuration', to disable a toolbar button, drag it from its place on the 'Active toolbar' up to 'Available buttons'. Repeat this process for each button you want to remove. To re-enable abutton just do the reverse and drag it back to the 'Active toolbar'. \*Buttons must be assigned to a 'group' (the areas denoted by the faint, dotted borders).

---
Tags: 8, wysiwyg
---


# Question
Title: Is there a way to export a list of all pages and files?
I received an email from a customer today requesting a list of all pages and files on their website. Since they are content administrators, I am about to reply with simple instructions for navigating the "Content" area since all the information they are looking for is there.

Is there a module or a tool of sorts that could help generate a list automatically of all the nodes within a site?

# Answer
> 0 votes
You can use Views Data Export

> This module is designed to provide a way to export large amounts of data from views. It provides a display plugin that can rendered progressively in a batch. Style plugins are included that support exporting in the following types:
>
> * CSV
> * Microsoft XLS (or really simple HTML understood by Excel)
> * Microsoft DOC
> * Basic TXT
> * XML

and export as CSV. I recommend you watch this video tutorial.

Your client can then view the CSV file in Excel.

---
Tags: nodes
---


# Question
Title: How to print product ID in commerce-product.html.twig template
I am not able to figure out how to print the product id in my commerce-product.html.twig for display on the product detail page. I tried different combinations of { product.field\_id }} { product.id }} and can't find it.

# Answer
> 4 votes
You need to retrieve it from **`product_entity`**

```
{{ kint(product_entity.product_id.value) }}

```

> Search "Available Variables" for more information here *https://docs.drupalcommerce.org/commerce2/developer-guide/products/displaying-products/theme-product*

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


title: Get the published date of a node

text:
# Question
Title: Get the published date of a node
I would like to get the published date of my node. On the site I created a news block with a view. In the view I added a field and customised it by the rewrite results with "published on `{{ created }}`". But sometimes I create the news the day before, then it shows the created date, but I will like to have the published date.

What's the name for the published date? `{{ publish_on }}` didn't works.

# Answer
> 6 votes
There isn't a **published date** out of the box.

You can try using Publication Date module.

> The Publication Date module adds a "Published on" date for each node, containing the date when it was first published.

---
Tags: 8
---


# Question
Title: How to iterate over AJAX-added form elements
In this snippet, located under `src/Form/DocumentCreationForm.php`, I create an **AJAX callback** that adds more `section` elements to the form when the `add section` button is clicked.
Now I'm trying to read those values in `submitForm` or `validateForm`.

I read somewhere (sorry, I can't remember where) that it was impossible to submit AJAX-added form elements via submit Form, so I'm thinking I might have to intercept the submit button, add a Controller and submit the form to the controller via javascript when the submit button is pushed.

Does anyone know of a better way?

```
<?php

namespace Drupal\mc_documents\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\cloud\CloudAdapter;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\AppendCommand;
use Drupal\Core\Url;

class DocumentCreationForm extends FormBase {

  /**
   * {@inheritdoc}
   */
  public function getFormId(){
    return 'mc_documents_DocumentCreationForm';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state, $param1=NULL){
    // $form['attached']['library'][] = 'mc_documents/mc_documents';

    // ... populate the form here. see https://api.drupal.org/api/drupal/elements for options
    $form_state->set('num_sections', 0);

    $form['document_name'] = [
      '#weight' => 0,
      '#title' => t('Document Name'),
      '#type' => 'textfield',
      '#attributes' => [
        'id' => 'document_name'
      ]
    ];

    $cloud = new CloudAdapter();

    $document_types = $cloud->query(
      "select * from MC_Selection_V where Category = 'MC_Documents_Document_Type';",
      []
    );

    $formatted_document_types = [];
    $len = count($document_types);
    for($i = 0; $i < $len; $i++){
      $formatted_document_types[$document_types[$i]['Value']] = $document_types[$i]['Caption'];
    }

    $form['document_type'] = [
      '#weight' => 1,
      '#title' => t('Document Type'),
      '#type' => 'select',
      '#options' => $formatted_document_types
    ];

    $form['sections'] = [
      '#weight' => 2,
      '#type' => 'container',
      '#title' => t('Sections'),
      '#attributes' => [
        'id' => ['sections']
      ]
    ];

    $form['add_section'] = [
      '#weight' => 3,
      '#value' => t('Add Section'),
      '#type' => 'button',
      '#ajax' => [
        'callback' => '::addSection',
        'event' => 'click',
        'wrapper' => 'sections'
      ],
    ];

    $form['submit'] = [
      '#weight' => 4,
      '#type' => 'submit',
      '#value' => t('Submit'),
      '#attributes' => [
        // 'onclick' => 'function(){ submit_document(); return false; }'
      ],
    ];

    return $form;
  }

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

    $form_state->setValue('num_sections', $form_state->getValue('num_sections') + 1);

    $cloud = new CloudAdapter();
    $field_types = $cloud->query(
      "select * from MC_Selection_V where Category = 'MC_Documents_Field_Type';",
      []
    );
    $formatted_field_types = [];
    $len = count($field_types);
    for($i = 0; $i < $len; $i++){
      $field_type = $field_types[$i];
      $formatted_field_types[$field_type['Value']] = $field_type['Caption'];
    }

    $elems = [
      [
        '#type' => 'html_tag',
        '#tag' => 'hr'
      ],
      [
        '#type' => 'select',
        '#title' => t('Field Type'),
        '#options' => $formatted_field_types,
        '#attributes' => [
          'class' => ['field-type']
        ]
      ],
      [
        '#type' => 'textarea',
        '#title' => t('Section'),
        '#placeholder' => 'Enter a placeholder here...',
        '#attributes' => [
          'class' => 'section-markup'
        ],
        '#value' => ''
      ]
    ];

    $renderer = \Drupal::service('renderer');
    $response = new AjaxResponse();
    $response->addCommand(new AppendCommand('#sections', $renderer->render($elems)));
    return $response;
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state){

  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state){
    drupal_set_message($form_state->getValues('sections'));
    // insert document
    // insert document field
    // insert document field data
  }
}

?>

```

# Answer
> 1 votes
You can't create form elements in an ajax callback and can't modify $form\_state either.

This is a working example for the same task, which creates the form elements in `buildForm()` and modifies `$form_state` in submit handlers:

```
<?php

namespace Drupal\form_api_example\Form;

use Drupal\Core\Form\FormStateInterface;

/**
 * Implements the ajax demo form controller.
 *
 * This example demonstrates using ajax callbacks to add people's names to a
 * list of picnic attendees.
 *
 * @see \Drupal\Core\Form\FormBase
 * @see \Drupal\Core\Form\ConfigFormBase
 */
class AjaxAddMore extends DemoBase {

  /**
   * Form with 'add more' and 'remove' buttons.
   *
   * This example shows a button to "add more" - add another textfield, and
   * the corresponding "remove" button.
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $form['description'] = [
      '#type' => 'item',
      '#markup' => $this->t('This example shows an add-more and a remove-last button.'),
    ];

    // Gather the number of names in the form already.
    $num_names = $form_state->get('num_names');
    // We have to ensure that there is at least one name field.
    if ($num_names === NULL) {
      $name_field = $form_state->set('num_names', 1);
      $num_names = 1;
    }

    $form['#tree'] = TRUE;
    $form['names_fieldset'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('People coming to picnic'),
      '#prefix' => '<div id="names-fieldset-wrapper">',
      '#suffix' => '</div>',
    ];

    for ($i = 0; $i < $num_names; $i++) {
      $form['names_fieldset']['name'][$i] = [
        '#type' => 'textfield',
        '#title' => $this->t('Name'),
      ];
    }

    $form['names_fieldset']['actions'] = [
      '#type' => 'actions',
    ];
    $form['names_fieldset']['actions']['add_name'] = [
      '#type' => 'submit',
      '#value' => $this->t('Add one more'),
      '#submit' => ['::addOne'],
      '#ajax' => [
        'callback' => '::addmoreCallback',
        'wrapper' => 'names-fieldset-wrapper',
      ],
    ];
    // If there is more than one name, add the remove button.
    if ($num_names > 1) {
      $form['names_fieldset']['actions']['remove_name'] = [
        '#type' => 'submit',
        '#value' => $this->t('Remove one'),
        '#submit' => ['::removeCallback'],
        '#ajax' => [
          'callback' => '::addmoreCallback',
          'wrapper' => 'names-fieldset-wrapper',
        ],
      ];
    }
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Submit'),
    ];

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'form_api_example_ajax_addmore';
  }

  /**
   * Callback for both ajax-enabled buttons.
   *
   * Selects and returns the fieldset with the names in it.
   */
  public function addmoreCallback(array &$form, FormStateInterface $form_state) {
    return $form['names_fieldset'];
  }

  /**
   * Submit handler for the "add-one-more" button.
   *
   * Increments the max counter and causes a rebuild.
   */
  public function addOne(array &$form, FormStateInterface $form_state) {
    $name_field = $form_state->get('num_names');
    $add_button = $name_field + 1;
    $form_state->set('num_names', $add_button);
    // Since our buildForm() method relies on the value of 'num_names' to
    // generate 'name' form elements, we have to tell the form to rebuild. If we
    // don't do this, the form builder will not call buildForm().
    $form_state->setRebuild();
  }

  /**
   * Submit handler for the "remove one" button.
   *
   * Decrements the max counter and causes a form rebuild.
   */
  public function removeCallback(array &$form, FormStateInterface $form_state) {
    $name_field = $form_state->get('num_names');
    if ($name_field > 1) {
      $remove_button = $name_field - 1;
      $form_state->set('num_names', $remove_button);
    }
    // Since our buildForm() method relies on the value of 'num_names' to
    // generate 'name' form elements, we have to tell the form to rebuild. If we
    // don't do this, the form builder will not call buildForm().
    $form_state->setRebuild();
  }

  /**
   * Final submit handler.
   *
   * Reports what values were finally set.
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $values = $form_state->getValue(['names_fieldset', 'name']);

    $output = $this->t('These people are coming to the picnic: @names', [
      '@names' => implode(', ', $values),
    ]
    );
    $this->messenger()->addMessage($output);
  }

}

```

Source: https://www.drupal.org/project/examples

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


# Question
Title: Views filter criteria groups OR not working?
I have a view with `FILTER CRITERIA` set. I have three groups, each joined by `AND`s, with the criteria within each group `OR`ed. The final group are two taxonomy terms, meaning I want my query to return nodes that have one term or the other. No node will have both of the tags set.

However, when I see the SQL in the preview, the terms seem are connected by a `AND`.

```
WHERE (((node__field_event_categories.field_event_categories_target_id = '434')) AND ((node__field_event_categories2.field_event_categories_target_id = '212'))) AND ((taxonomy_index.status = '1') AND (((DATE_FORMAT((node__field_date_range.field_date_range_value + INTERVAL -14400 SECOND), '%Y-%m-%d\T%H:%i:%s') >= DATE_FORMAT(('2018-10-22T04:00:00' + INTERVAL -14400 SECOND), '%Y-%m-%d\T%H:%i:%s'))) OR ((DATE_FORMAT((node__field_date_range.field_date_range_end_value + INTERVAL -14400 SECOND), '%Y-%m-%d\T%H:%i:%s') >= DATE_FORMAT(('2018-10-22T04:00:00' + INTERVAL -14400 SECOND), '%Y-%m-%d\T%H:%i:%s')))))

```

This causes my query not to return any results.

I've tried removing each tid, one by one, and when one is missing I get results. So I'm thinking I should get both sets of results when they are set to `OR`.

To make sure I was reading the SQL correctly, I ran it through a pretty-printer:

To double check, I exported the views config, to see what the yaml said about the WHERE clause, and it all seems to be `OR` within the groups:

I even re-imported the configuration from this yaml, but I still get the same results.

How can I get my taxonomy criteria to function using `OR`?

**Edit** In the course of troubleshooting, I tried to simplify the filter criteria. I removed everything, all groups and criteria. This is what I have now:

This is Drupal 8.6.2. We just updated, so I'm wondering if this is a new bug.

# Answer
> 0 votes
This answer on a related (perhaps duplicate?) issue provided settings that got the display working for me.

First, on each filter criteria, check "Reduce Duplicates".

**EDIT** Actually, the answer I linked says to uncheck the "DISTINCT" in the Advanced settings for the query. However, when I did that, I got duplicates. I unchecked it, and I got the right number of results. So that second step may not be necessary for you.

---
Tags: views
---


# Question
Title: Handling external authentication with routing.yml
I am building a new page (general use of "page", not literally referring to the node type) in my Drupal site that should only be accessible to members that have a certain permission. I have defined the routing for this in `mymodule.routing.yml` as follows:

```
mymodule.connector:
  path: '/mymodule/connector'
  defaults:
    _controller: \Drupal\mymodule\Controller\MymoduleConnectorController::content
  requirements:
    _permission: 'view mymodule entity'

```

This so far is working nicely, my accounts with this permission are able to view the content.

I also need `/mymodule/connector` to be accessible via an ajax request from a web view app that I made, which uses the same login credentials as my Drupal site, so essentially I need external authentication to work. I have tried to accomplish it by adding basic auth to my routing as explained here:

```
mymodule.connector:
  path: '/mymodule/connector'
  defaults:
    _controller: \Drupal\mymodule\Controller\MymoduleConnectorController::content
  requirements:
    _permission: 'view mymodule entity'
  options:
    _auth: ['basic_auth']

```

However, when testing with Postman, using Basic Auth with my user credentials, this is a 403 error.

Is something wrong with my routing? I am not sure what else it could be as I am not very experienced with authentication.

# Answer
I can't believe I missed this. I simply had to go to /admin/modules and enable these:
> 2 votes

# Answer
Thank for putting this up. I have been spending hours trying to get key\_auth to work with a custom endpoint in one of my contrib modules, and what I was missing was the options key:

```
spectra.post_api:
  path: '/spectra/post'
  defaults:
    _title: 'Spectra POST API'
    _controller: '\Drupal\spectra\Controller\SpectraController::post_api'
  methods: [POST]
  options:
      _auth: ['key_auth', 'cookie']
  requirements:
    _permission: 'access spectra post api'

```

Just to expand on your solution, the auth array can include multiple parameters, and in my case, I did not need RESTful Web Services or any of the listed modules. However, HTTP Basic Auth might need them.
> 2 votes

---
Tags: 8, routes
---


# Question
Title: For views, how do I display only one of a series with the same title?
We have a view that displays upcoming events. We use civiCRM for events. Some of our events are ongoing: they have the same title, but different dates. What logic could I use to display the next upcoming event, without displaying all upcoming events?

For instance, at the moment the events displayed are based on date. Any event that is not yet passed will display in the view. But for a class which meets 10 times, I do not want 10 of the same title, just the next class and its date/time.

# Answer
> 0 votes
To only show one immediate next upcoming event:

1. add **Sort criteria** by date (ascending so you get the events that are soonest to happen as first on the list), make sure the correct one come up top
2. and then limit your results to only show 1 row (**Display a specified number of items** to 1 under Pager options, but don't output the Pager).

---
Tags: views
---


title: The pdf_thumbnail plugin does not exist

text:
# Question
Title: The pdf_thumbnail plugin does not exist
After I tried to update the database I got the following error:

> Failed: Drupal\Component\Plugin\Exception\PluginNotFoundException:
> \[error\] The "pdf\_thumbnail" plugin does not exist. in Drupal\Core\Plugin\DefaultPluginManager-\>doGetDefinition() (line 52 of /home/www/sites/stdev/web/core/lib/Drupal/Component/Plugin/Discovery/DiscoveryTrait.php).

I tried to find the solution and found one of a similar issue:

drush updb fails with "the 'media\_delete\_action' plugin does not exist" after 8.4.6 \> 8.5.1 update

Alas, I can't apply that solution because I don't know which module uses the pdf\_thumbnail plugin. How can I find it out which module uses the plugin and solve the problem with the update?

# Answer
I resolved this by first re-enabling the PDF module, and then by updating core.

In my composer setup, the PDF module was a git submodule. However, my shared host would not work with that. So, I removed the cached information

```
git rm --cached web/modules/contrib/pdf

```

And then I added the code in that directory to my main repo.

Next, enable the PDF module.

Finally, proceed with the update process, and run `update.php` or `drush updb`
> 0 votes

---
Tags: 8, updating, plugins
---


# Question
Title: Is the AJAX from image fields and multi-line fields breaking my JavaScript?
We have added tooltips to our node form using this method: https://inclusive-components.design/tooltips-toggletips/

It includes a JavaScript/jQuery snippet that works perfectly *except*...

* If the tooltip is inside an image file upload, it works, but if you upload an image and that field updates, then the tooltip no longer works.
* If the tooltip is inside a multi-line field, it works, but if you add a new item, the tooltip no longer works.

The other tooltips keep working. It's as if those tooltips were removed from the `document.querySelectorAll`. Would this have something to do with Drupal 8's AJAX updating those fields?

Here is the JS:

```
(function ($) {

  $(document).ready(function() {
    console.log("dom is ready");
    // Get all the toggletip buttons
    var toggletips = document.querySelectorAll('[data-toggletip-content]');

    // Iterate over them
    Array.prototype.forEach.call(toggletips, function (toggletip) {
      // Get the message from the data-content element
      var message = toggletip.getAttribute('data-toggletip-content');

      // Get the live region element
      var liveRegion = toggletip.nextElementSibling;

      // Toggle the message
      toggletip.addEventListener('click', function () {
        console.log('clicky');
        liveRegion.innerHTML = '';
        window.setTimeout(function() {
          liveRegion.innerHTML = '<span class="alert alert-info isu-alert_sm isu-tooltip-bubble">'+ message +'</span>';
        }, 100);
      });

    // Close on outside click
    document.addEventListener('click', function (e) {
      if (toggletip !== e.target) {
        liveRegion.innerHTML = '';
      }
    });

    // Remove toggletip on ESC
    toggletip.addEventListener('keydown', function (e) {
      if ((e.keyCode || e.which) === 27)
        liveRegion.innerHTML = '';
      });
      // Remove on blur
      toggletip.addEventListener('blur', function (e) {
        liveRegion.innerHTML = '';
      });
    });
  });
})(jQuery);

```

Note: I know absolutely nothing about AJAX other than it seems to be what's updating the fields's HTML.

# Answer
> 0 votes
> Would this have something to do with Drupal 8's AJAX updating those fields?

Yes, you need to add

### Drupal.behaviors

In order for your Drupal JS code to be able to run after AJAX.

> using the functionality of `Drupal.behaviors` and `once()`. If used properly this will ensure that your code runs both on normal page loads **and when data is loaded by AJAX** (or BigPipe!)
>
> Any object defined as a property of `Drupal.behaviors` will get its `attach()` method called when the DOM has loaded both initially **and after any AJAX calls**.

I suggest you read the Javascript API Overview.

---

Alternatively, If you don't want to use `Drupal.behaviors`, I believe if you change all the `.addEventListener` to `.on` that will also do.

---
Tags: 8, javascript, ajax, tooltips
---


# Question
Title: Possible to call a function after user sets their password for the first time after using their one-time login link?
I have a Drupal 8 site and I need to dynamically redirect the user after they set their password for the first time after using their one-time login link.

I cannot figure out any way to hook into the workflow for this condition. Is it possible?

# Answer
If you wanna redirect users, the below solution won't work, because `'/user/reset/{uid}/{timestamp}/{hash}/login'` controlled by `UserController::resetPassLogin` action if you see the return of `resetPassLogin` :

> * @return \Symfony\Component\HttpFoundation\RedirectResponse
>     + Returns a redirect to the user edit form if the information is correct.
>     + If the information is incorrect redirects to 'user.pass' route with a
>     + message for the user.

I figure out a solution but i'm not sure if its the best way to do it:
for example if i want to redirect the user after first login to `admin/content`, i will override the `resetPassLogin` action like the following:

**myModule.routing.yml**

```
myModule.reset.login:
  path: '/user/reset/{uid}/{timestamp}/{hash}/login'
  defaults:
    _controller: '\Drupal\myModule\Controller\CustomUserController::customResetPassLogin'
    _title: 'Reset password'
  requirements:
    _user_is_logged_in: 'FALSE'
  options:
    _maintenance_access: TRUE
    no_cache: TRUE

```

**myModule/src/Controller/CustomUserController.php**

```
namespace Drupal\myModule\Controller;

use Drupal\user\Controller\UserController;
use Drupal\Component\Utility\Crypt;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

class CustomUserController extends UserController {

  public function customResetPassLogin($uid, $timestamp, $hash) {
    // The current user is not logged in, so check the parameters.
    $current = REQUEST_TIME;
    /** @var \Drupal\user\UserInterface $user */
    $user = $this->userStorage->load($uid);

    // Verify that the user exists and is active.
    if ($user === NULL || !$user->isActive()) {
      // Blocked or invalid user ID, so deny access. The parameters will be in
      // the watchdog's URL for the administrator to check.
      throw new AccessDeniedHttpException();
    }

    // Time out, in seconds, until login URL expires.
    $timeout = $this->config('user.settings')->get('password_reset_timeout');
    // No time out for first time login.
    if ($user->getLastLoginTime() && $current - $timestamp > $timeout) {
      $this->messenger()
        ->addError($this->t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.'));
      return $this->redirect('user.pass');
    }
    elseif ($user->isAuthenticated() && ($timestamp >= $user->getLastLoginTime()) && ($timestamp <= $current) && Crypt::hashEquals($hash, user_pass_rehash($user, $timestamp))) {
      user_login_finalize($user);
      $this->logger->notice('User %name used one-time login link at time %timestamp.', [
        '%name'      => $user->getDisplayName(),
        '%timestamp' => $timestamp,
      ]);
      $this->messenger()
        ->addStatus($this->t('You have just used your one-time login link. It is no longer necessary to use this link to log in. Please change your password.'));
      // Let the user's password be changed without the current password
      // check.
      $token = Crypt::randomBytesBase64(55);
      $_SESSION['pass_reset_' . $user->id()] = $token;
      /**
       * @TODO: change the route name to yours
       */
      return $this->redirect(
        'system.admin_content',
        [],
        [
          'absolute' => TRUE,
        ]
      );
    }

    $this->messenger()
      ->addError($this->t('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.'));
    return $this->redirect('user.pass');
  }

}

```

**NOTE** : change `system.admin_content` by your route name.
> 1 votes

# Answer
You could try using hook\_user\_login() in a custom module or theme.

```
function my_module_user_login($account) {

  // Get the end part of the URL.
  $request_uri = \Drupal::request()->getRequestUri();

  // Check if the URL is a reset / one time login link.
  if (strpos($request_uri, 'user/reset') !== false) {
    _my_module_custom_function();
  }
}

function _my_module_custom_function() {
  // Do stuff.
}

```
> 1 votes

---
Tags: 8, users
---


title: How to inject User class as dependency?

text:
# Question
Title: How to inject User class as dependency?
How to do I inject Drupal\user\Entity\User class as dependency? I could not find service name in service list. for example:

```
$user = User::load($this->currentUser->id())

```

for this code I want to call like this

```
$user = $this->user->load(($this->currentUser->id())

```

when injecting in Controller. How can I achieve this?

# Answer
> 8 votes
You don't have to inject services in the controller, because ControllerBase already provides the services needed. So this should work:

```
$user = $this->entityTypeManager()->getStorage('user')->load($this->currentUser()->id());

```

---

For other services not included in ControllerBase, see How do I use dependency injection on \Drupal::moduleHandler() to a Controller?

---
Tags: 8
---


# Question
Title: Can't clean URL from custom query parameters
I have a client whose company has different branches. The data are retrieved from a DB (not the Drupal one). For example, the url `mydrupalsite.com/branch?branchID=1` should lead to `mydrupalsite.com/branch/1`, `mydrupalsite.com/branch?branchID=23` to `mydrupalsite.com/branch/23`. I have created a page for the branches, at which the content will change slightly and dynamically, according to the branch we choose. I've tried using `InboundPathProcessorInterface()`, `OutboundPathProcessorInterface()` and `PathProcessorAlias()`, but I can't get rid of the query `?branchID=23`, so I only get `mydrupalsite.com/branch/23?branchID=23`.

Is there any way to achieve that?

# Answer
> 1 votes
I think you can use Redirection Rules in your .htaccess file to achieve this task.

```
<IfModule mod_rewrite.c>
  RewriteEngine on
  RewriteCond %{REQUEST_URI} ^(/branch)$
  RewriteCond %{QUERY_STRING} branchID=(.*)
  RewriteRule (.*) /branch/%1?  [R=301,L]
</IfModule>

```

Hope, this helps you.

---
Tags: database, uri
---


# Question
Title: Exclude certain webform field to be saved in Database
Is there a way we can exclude certain webform field to be saved in Database. Don't want to exclude from email data.

# Answer
This feature is currently not supported by the Webform module for Drupal 8. You would have to write custom code using hook\_webform\_submission\_presave() to remove unwanted element data.
> 2 votes

---
Tags: 8, webforms
---


# Question
Title: Translatable strings not appearing in the translation interface
I've recently started working on a custom D8 module. I've used `$this->t()` (or `t()`) wherever I had to output a text so that this text would then be translatable.

My module works correctly, but when I visit Drupal's "User interface translation" (`admin/config/regional/translate`) page, those strings are nowhere to be found.

**Two examples:**

\_1. in the `mymodule.module` file, I have implemented the `hook_help()` function to display a help page in the admin:

```
<?php

/**
 * Implements hook_help().
 */
function mymodule_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    case 'help.page.mymodule':
      return t('<p>Here is the (much longer) help text.</p>');
  }
}

?>

```

The help page is visible in the admin and shows the text; however this text cannot be found in the "User interface translation" page.

\_2. the module has a configuration page in the admin, with several fields and a description for each field. This configuration form is defined in `src/Form/mymoduleForm.php`:

```
<?php

namespace Drupal\mymodule\Form;

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

class mymoduleForm extends ConfigFormBase {

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    // Form constructor.
    $form = parent::buildForm($form, $form_state);
    $config = $this->config('mymodule.settings');
    $form['field1'] = array(
      '#type' => 'textfield',
      '#title' => $this->t('The first field:'),
      '#default_value' => $config->get('mymodule.field1'),
      '#description' => $this->t('<p>Description for this field.</p>'),
      '#required' => TRUE,
    );
    // and so on
  }
  // other code
}
?>

```

Here again, the configuration page works, all the fields are visible, but none of the strings in the code above appear on the "User interface translation" page (neither the title of the string, nor the description).

**The same goes for strings declared in the `*.yml` files:** they're supposed to be picked up automatically by Drupal, but I don't seen any of them in the interface... (e.g. the module description in the `*.info.yml` file.)

What am I missing?

# Answer
> 5 votes
As suggested by @prkos in the comments, visiting the pages (both public and admin) *in the target language* caused the corresponding strings to be discovered by Drupal and therefore become available in the interface translation. For each string, you have to visit the corresponding page (or block).

If you have custom error or confirmation messages, you apparently have to trigger them before they appear too. (? not entirely sure)

(I thought the "updating translations" progress bar that you see during the module installation was precisely Drupal listing all translations available in the module, but apparently that's not the case.)

---
Tags: i18n-l10n
---


# Question
Title: Why is it possible to write any string in the hash salt
I have researched a bit, but I couldn't find a precise answer to why it's possible to write any string in `$settings['hash_salt']`.

For me it doesn't make sense that the site chashes if no hash salt has been set and if I then go to settings.php and write,

```
$settings['hash_salt'] = 'x';

```

the site works just fine.

Why does the site work just because hash salt have any value?

# Answer
You can read about it in the default.settings.php file:

> Salt for one-time login links, cancel links, form tokens, etc.
>
> This variable will be set to a random value by the installer. All one-time login links will be invalidated if the value is changed. Note that if your site is deployed on a cluster of web servers, you must ensure that this variable has the same value on each server.
>
> For enhanced security, you may set this variable to the contents of a file outside your document root; you should also ensure that this file is not stored with backups of your database.

This value is used by Drupal as salt for some cryptographic operations. It should be a random value. It adds more security to those cryptographic operations (as long as the value is not known by an attacker).

As far as I know Drupal uses a auto-generated salt value based on Database credentials when no salt is set, so Drupal should work without a salt value.
> 6 votes

# Answer
Drupal requires a settings key called hash\_salt to secure transient hashes.

It will be generated during Drupal install and added to settings.php. If you use a different settings.php in different environments (for example, a settings.local.php), you may have forgotten to set this variable.

Add a line to your local settings.php like:

$settings\['hash\_salt'\] = 'whatever you like';
> 1 votes

---
Tags: configuration
---


title: Create a custom date format

text:
# Question
Title: Create a custom date format
Our organization's style guide expects dates in this format:

> Oct. 31 2018 01:30 p.m.

I looked the date format drop-down in our views, and there were several options, including a custom one. However, I noticed that none of these are able render the post meridien with abbreviating periods.

There is option for a custom date format, but it seems to use the encoding for PHP `date()` function. Looking at the manual, there is `a`, which gives me `pm`, and `A`, which gives me `PM`, but I don't see anything specifying one with periods.

I looked in the code base for where date formatters are stored, and they all seem to be yaml files specifying `date()` formats:

```
$ cat core/modules/system/config/install/core.date_format.html_yearless_date.yml
...
pattern: m-d
...

```

So it seems that the built-in custom date formats are limited to what can be specified with PHP's `date()` function.

I googled for ways to create a custom date format, but only found explanations of where to specify a custom date format: https://www.ostraining.com/blog/drupal/d8-dates/

How can I create a date formatter where I can use PHP code to give myself a date format with properly abbreviated meridiens?

# Answer
> 2 votes
You are correct, the core `date.formatter` service uses the native PHP date formatting strings, none of which have 'a.m.' or 'p.m.' in them.

If you really need this, I suspect this would work.

* Create a new module, with a class that extends `DateFormatter`.
* In the `format` method, call the parent method, and then do string replacment to convert 'am' to 'a.m.' and 'pm' to 'p.m.'
* Replace the `date.formatter` service with the one you created.

This may have side effects that I'm not thinking of right now.

# Answer
> 0 votes
You can create a new dateFormat like `Oct. 31 2018 01:30 p.m` here is the steps :

1. From `admin/config/regional/date-time/formats/add` Create new Format with `M d Y h:i A` as Format string.
2. Create field of type date:
3. `Manage Display` and `Manage Form display` for the field like the following:
4. In the front you will get:

**Note**:
If you want to add period after month and `pm` or `am` Change `M d Y h:i A` by `M. d Y h:i a.`

---
Tags: 8, datetime
---


# Question
Title: How do I remove the language_content_entity query from the canonical URL?
Following the Translating content documentation we have setup our site to have `Spanish`, `English`, and `Japanese`. When we are adding, editing, deleting, and publishing pages in different languages we have no issues. However, when we view the canonical url, we find that the `language_content_entity=en` is added for `English` pages, for example. We are already adding the `hreflang` to each page so don't need this extra parameter in the `canonical`.

For all English pages:

```
<link rel="canonical" href="https://www.somedomain.net/some/path?language_content_entity=en">

```

For all Spanish pages:

```
<link rel="canonical" href="https://www.somedomain.net/some/path?language_content_entity=es">

```

How can we remove the `language_content_entity=en` from the `canonical` url?

# Answer
Just to illustrate once again what you are supposed to do with the first duplicate question's answer:

```
/**
 * Implements hook_page_attachments_alter().
 */
function MYTHEME/MYMODULE_page_attachments_alter(array &$attachments) {

  foreach ($attachments['#attached']['html_head'] as $key => $head) {

    if ((isset($head[1]) ? $head[1] : FALSE) == 'canonical_url') {

      $href = isset($head[0]['#attributes']['href']) ? $head[0]['#attributes']['href'] : FALSE;

      if ($href) {
        // Strip ?language_content_entity=en from the end of the URL if exists.
        $href = rtrim($href, '?language_content_entity=en');
        // Strip ?language_content_entity=es from the end of the URL if exists.
        $href = rtrim($href, '?language_content_entity=es');
        // Then set the new value.
        $attachments['#attached']['html_head'][$key][0]['#attributes']['href'] = $href;
      }
    }
  }
}

```
> 2 votes

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


title: How do I overwrite meta tags?

text:
# Question
Title: How do I overwrite meta tags?
In Drupal 7 I used this to unset unneeded elements and total overwrite meta tags:

```
function MYTHEME_html_head_alter(&$head_elements) {
  // Unset all.
  $remove_array = array(
    'system_meta_content_type',
    'system_meta_generator',
    'system_shortcut_icon',
    'metatag_canonical',
  );
  foreach ($remove_array as $key) {
    if ($head_elements[$key]) {
      unset($head_elements[$key]);
    }
  }
  // Optimize the mobile viewport.
  $head_elements['mobile_viewport'] = array(
    '#type' => 'html_tag',
    '#tag' => 'meta',
    '#weight' => -10,
    '#attributes' => array(
        'name' => 'viewport',
        'content' => 'initial-scale=1.0, maximum-scale=1.0, user-scalable=no, width=device-width',
    ),
  );
  // ...
  // Many other my META tags
  // ...
}

```

But in Drupal 8 this doesn't work anymore. New elements such as `$head_elements['mobile_viewport']` don't get added.

And old meta tags, such as

* `<meta name="Generator" content="Drupal 8 (https://www.drupal.org)">`
* `<meta name="MobileOptimized" content="width">`
* `<meta name="HandheldFriendly" content="true">`
* `<link rel="shortcut icon" href="http://example.com/core/misc/favicon.ico" type="image/vnd.microsoft.icon">`

are still in `<head>`. How can I remove them and add others instead in Drupal 8?

# Answer
> 14 votes
Drupal 8 doesn't use `hook_html_head_alter()`. Its usage has been removed in Remove invocation of hook\_html\_head\_alter(). As described in drupal\_add\_\*() deprecated in favour of #attached, in Drupal 8 is possible to use `hook_page_attachments()` and `hook_page_attachments_alter()`, for example using code similar to the following one, suggested in one of the comments for the change record.

```
function MYTHEME_page_attachments_alter(array &$page) {
  $viewport = [
    '#type' => 'html_tag',
    '#tag' => 'meta',
    '#attributes' => [
      'name' => 'viewport',
      'content' => 'width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no',
    ],
  ];

  $page['#attached']['html_head'][] = [$viewport, 'viewport'];
}

```

# Answer
> 2 votes
override meta from page generate in routing/controller

```
/**
 * Implements hook_metatags_alter().
 */
function mymod_metatags_alter(array &$metatags, array $context) {
  $route_name = \Drupal::service('current_route_match')->getRouteName();

  if ($route_name == "mymod.default_controller_content") {
    ...
  }
}

```

# Answer
> 2 votes
Just to alter this code is useful:

```
/**
 * Implements hook_page_attachments_alter().
 */
function MY_THEME_page_attachments_alter(array &$variables) {
  if ($variables['#attached']['html_head']) {
    foreach ($variables['#attached']['html_head'] as $delta => $tag) {
      if (isset($tag[0]['#tag']) && $tag[0]['#tag'] == '__THE_DESIRED_TAG__' && $tag[0]['#attributes']['rel'] == '__THE_REL_ATTR__') {
        $variables['#attached']['html_head'][$delta][0]['#attributes']['__THE_DESIRED_ATTR__'] = '__THE_DESIRED_VALUE__';
      }
    }
  }
}

```

---
Tags: 8, meta-tags
---


# Question
Title: what does "vcs" mean when i export a configuration
I would like to know what vcs stands for .

when i do a drush config:export, i have to choose "vcs" but I'm not sure what that means? I am on an acquia server , does that have something to do with it?

# Answer
By default, Drupal 8 uses the sync configuration directory type for configuration, Acquia Cloud uses the vcs configuration directory type for that purpose.
> 1 votes

---
Tags: configuration
---


title: Installing live drupal website in local

text:
# Question
Title: Installing live drupal website in local
I am trying to install my live drupal (version 8) website in localhost. I have downloaded the files and folders. I have imported the database. And updated settings.php file.

But when I try to log in, after entering username and password, the page redirects to core/install.php and I get this message - "Drupal already installed".

what does this mean? How can I resolve this?

# Answer
This error message is shown when you try to install Drupal, but the Drupal install script detects that there is already a *populated* (i.e. not empty) database present in the database pointed to by `settings.php`.

Here is how you set up an empty database to hold Drupal (provided the database is named "drupal").

```
$ mysql -u root -p
Password:
mysql> DROP DATEBASE drupal;
mysql> CREATE DATABASE drupal;
mysql> GRANT USAGE ON *.* TO drupal@localhost IDENTIFIED BY 'password';
mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER,
    -> LOCK TABLES, CREATE TEMPORARY TABLES ON drupal.* TO drupal@localhost
    -> IDENTIFIED BY 'password';
mysql> quit
Bye
$

```

This will create an empty database named "drupal", belonging to a database user named "drupal", with the passord "password". (Choose a more obscure password than "password".)

Then delete `settings.php` and copy `default.settings.php` to `settings.php`. Do *not* edit it. Make sure the directory where `settings.php` lives and `settings.php` itself is writable by the web server. That will make the install script create a new `settings.php` with the correct values during the install.

After doing all this: Start again installing Drupal.

After installation is complete, change the permissions for `settings.php` and its directory back to safe values. Check that there are no warnings in the Drupal status report.
> 1 votes

# Answer
The problem is Drupal installation has detected an existing installation using active configuration on settings.php.

The easiest way to get rid of this error is to replace settings.php under sites/default/ directory with content from default.settings.php file.
> 0 votes

---
Tags: theming
---