title: user login by php code

text:
# Question
Title: user login by php code
i have an app that sends username and password to a php script. This script checks that a user exists on Drupal 8 with username and password entered but does not log the user. I need the user to log in. Can someone help me? thanks Follows script code

```
use Drupal\user\UserAuthInterface;
use Drupal\Core\DrupalKernel;
use Symfony\Component\HttpFoundation\Request;
// Boot Drupal.
$autoloader = require __DIR__ . '/autoload.php';

$request = Request::createFromGlobals();
$kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod', FALSE);
$kernel->boot();
// Recupero Nome utente e password
$nome_ut = $_POST['nome'];
$pass_ut = $_POST['pwd'];
// effettuo il controllo
$user_auth = \Drupal::service('user.auth');
$user_storage = \Drupal::entityTypeManager()->getStorage('user');

$accounts = $user_storage->loadByProperties(['name' => $nome_ut, 'status' => 1]);
$account = reset($accounts);

if ($account) {
    if ($user_auth->authenticate($nome_ut, $pass_ut))
       {
    // The credentials are correct.
       $id = $user_auth->authenticate($nome_ut, $pass_ut);
       print ("Accesso riuscito!");
           // Force session start if we don't already have a session.
               $session = \Drupal::service('session');
               if (!$session->isStarted()) {
               $session->migrate();
           }
           if (\Drupal::currentUser()->isAuthenticated()) {
               // This user is logged in.
               print ("Utente Loggato");
           } else {
               // This user is not logged in.
               print ("Utente non Loggato");
           }

       }
}

```

# Answer
> 1 votes
Could you use a rest endpoint? Because this is already handled by drupal core. See for example this or this

Otherwise I think you're just missing `user_login_finalize` after your check if the user is logged in.

```
user_login_finalize($user);
//Eventually redirect the user somewhere
$user_destination = \Drupal::destination()->get();
$response = new RedirectResponse($user_destination);
$response->send();

```

---
Tags: 8
---


# Question
Title: How to override version of drupal-library (JavaScript library) included with composer merge-plugin?
I'm using Webform with the composer-merge plugin to bring in its libraries:

Relevant parts of my composer.json file:

```
    "extra": {
         "installer-paths": {
             "public_html/core": ["type:drupal-core"],
             "public_html/libraries/{$name}": ["type:drupal-library"],
             "public_html/modules/contrib/{$name}": ["type:drupal-module"]
         },
         "merge-plugin": {
             "include": [
                 "public_html/modules/contrib/webform/composer.libraries.json"
             ]
         }
     }

```

I can define my own version of the same library successfully, by using the same code as Webform's `composer.libraries.json` does but changing the version— successful in that it is recognized:

```
    "repositories": {
        "drupal": {
            "type": "composer",
            "url": "https://packages.drupal.org/8"
        },
        "jquery.inputmask": {
            "type": "package",
            "package": {
                "name": "jquery/inputmask",
                "version": "5.0.6-beta.45",
                "type": "drupal-library",
                "extra": {
                    "installer-name": "jquery.inputmask"
                },
                "dist": {
                    "url": "https://github.com/RobinHerbots/jquery.inputmask/archive/5.0.5.zip",
                    "type": "zip"
                },
                "require": {
                    "composer/installers": "~1.0"
                }
            }
        }
    },

```

But unsuccessful in that it does not have priority over the one defined in webform:

> * Root composer.json requires jquery/inputmask 5.0.6-beta.45, it is satisfiable by jquery/inputmask\[5.0.6-beta.45\] from package repo (defining 1 package) but jquery/inputmask\[5.0.5\] from package repo (defining 1 package) has higher repository priority. The packages with higher priority do not match your constraint and are therefore not installable. See https://getcomposer.org/repoprio for details and assistance.

But i don't know how to take priority.

# Answer
> But unsuccessful in that it does not have priority over the one defined in webform:

There is a way, just define custom repository before Drupal packages repository .

Change your composer.json to

```
   "repositories": {
        "jquery.inputmask": {
            "type": "package",
            "package": {
                "name": "jquery/inputmask",
                "version": "5.0.6-beta.45",
                "type": "drupal-library",
                "extra": {
                    "installer-name": "jquery.inputmask"
                },
                "dist": {
                    "url": "https://github.com/RobinHerbots/jquery.inputmask/archive/5.0.5.zip",
                    "type": "zip"
                },
                "require": {
                    "composer/installers": "~1.0"
                }
            }
        },
       "drupal": {
            "type": "composer",
            "url": "https://packages.drupal.org/8"
        }
    },

```
> 0 votes

---
Tags: webforms, composer, 9
---


# Question
Title: How do I set the default value for results?
I'm on Drupal 9 and I have an AJAX view with a page display and an exposed filter block and a single filter exposed, "Search: Fulltext search".

The *Search API* module is configured to search inside 3 different content types: products, articles and authors.

I've also configured a facet to filter on the content type, using a widget type "links", that let filter one content type at time.

So, initially, the search page shows the results of all the three content types. After users filter a content type, they can switch only to another one and never return to the *no content type chosen* situation.

What I need to do is that the results are listed by default with one specific content type. If that content type yield no results, the following content type should be used as filter. If there are no result, just *no result* should be shown.

In other words, the *no content type chosen* situation should be never available.

I've tried to force the filter on content type, if missing, using `hook_views_pre_view()`, but it doesn't seem to work.

Actually, I've no idea if it is the correct approach or I have to work on different hook\somewhere else.

How do I solve this issue?

This is the hook code.

```
function my_module_views_pre_view(\Drupal\views\ViewExecutable $view, $display_id, array &$args) {
  if ($view->id() === 'search' && $display_id === 'page_1') {
    $active_filters = $view->getExposedInput();
    if (isset($active_filters["search_api_fulltext"]) && !empty($active_filters["search_api_fulltext"])) {
      if (!isset($active_filters["f"])) {
        $active_filters["f"] = ['content_type:product'];
      }
      else {
        $content_type_filter_exists = FALSE;
        foreach ($active_filters["f"] as $facet_filter) {
          $filter_array = explode(':', $facet_filter);
          if ($filter_array[0] === 'content_type') {
            $content_type_filter_exists = TRUE;
            break;
          }
        }
        if (!$content_type_filter_exists) {
          $active_filters["f"] = array_merge($active_filters["f"], ['content_type:product']);
        }
      }
      $view->setExposedInput($active_filters);
    }
  }
  //$view->exposed_raw_input = array_merge($active_filters, (array) $view->exposed_raw_input);
}

```

# Answer
Initially I tried some different approach:

* extending `SearchApiString`
* an event subscriber of `KernelEvents::REQUEST` altering the incoming url

because by default facets\search api rely on the query\_params of the url and the "values" of the relative facets various objects of the modules. Both of those those approaches kinda failed, because the first didn't update the query params of the request, while the second caused some bugs that I couldn't manage to solve.

The working approach was to extending the default url\_processor `QueryString` \- which both initialises the active facets filters and build the url after a search.

Injecting the logic when the class is created allowed to do a minor modification without breaking all the standard functionality.

```
<?php

namespace Drupal\my_module\Plugin\facets\url_processor;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\facets\Plugin\facets\url_processor\QueryString;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;

/**
 * Query string URL processor.
 *
 * @FacetsUrlProcessor(
 *   id = "my_query_string",
 *   label = @Translation("My Query string"),
 *   description = @Translation("Add description")
 * )
 */
class MyQueryString extends QueryString {

  /**
   * A string of how to represent the facet in the url.
   *
   * @var string
   */
  protected $urlAlias;

  /**
   * The event dispatcher.
   *
   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
   */
  protected $eventDispatcher;

  /**
   * {@inheritdoc}
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition, Request $request, EntityTypeManagerInterface $entity_type_manager, EventDispatcherInterface $eventDispatcher) {
    //this is where I'm adding a default value
    $this->alterRequest($request);
    parent::__construct($configuration, $plugin_id, $plugin_definition, $request, $entity_type_manager, $eventDispatcher);
  }

  /**
   * @param \Symfony\Component\HttpFoundation\Request $request
   */
  private function alterRequest(Request $request): void {
    $query = $request->query;
    $facet_new_parameters = [];

    if ($facet_parameters = $query->get('f')) {
      $content_type_filter_exists = FALSE;

      foreach ($facet_parameters as $parameter) {
        $sepator = ':';
        $explosion = explode($sepator, $parameter);
        $facet_type = array_shift($explosion);
        if ($facet_type === 'content_type') {
          $content_type_filter_exists = TRUE;
        }
      }

      if (!$content_type_filter_exists) {
        $facet_new_parameters = array_merge($facet_parameters, ['content_type:prodotto']);
      }
    }
    else {
      $facet_new_parameters = ['content_type:prodotto'];
    }

    if (!empty($facet_new_parameters)) {
      $query->set('f', $facet_new_parameters);
    }
  }
}

```
> 0 votes

---
Tags: views, search, 9
---


# Question
Title: Search api, postprocess processor: how re-run the query?
The documentation of the search api module says, about postprocess processor:

> Postprocess query
>
> This is probably the rarest phase for processors to use, postprocessing the query after it has run to manipulate the search results in some way. A prominent example would be the "Highlight" processor, which highlights the search keywords in the returned results. But this phase could also be used, for example, to re-run the query completely with some changes in case no results were found the first time.

However, it doesn't explain how to re-run the query completely.

I've to implement exactly in the case described: I've a search api view, with a fulltext search exposed filter and some facets.

If the query returns no results, I need to re-run it with some changes on the conditions provided by one of the facet filters (the content type), before actually rendering the result page.

This must be done programmatically and not by user interaction.

I've checked the *ProcessorInterface* but the only relative method seems to be the `function postprocessSearchResults(ResultSetInterface $results);`.

*Highligh*, the only processor implementing a `postprocess_query` stage doesn't do anything similar at what I need to do.

Any idea how I could re-run the query? I'm on a D9 site. Alternative solutions to the problem would be useful too, I'm trying this way as the documentation gives the idea it is a possible solution.

# Answer
> 0 votes
I managed to do something, despite not being optimal: in the `postprocessSearchResults` when necessary I send a `RedirectResponse` with new search params thus "reloading" the page and "re-running the search.

```
  /**
 *
 * @SearchApiProcessor(
 *   id = "my_processor",
 *   label = @Translation("A label"),
 *   description = @Translation("A description"),
 *
 *   stages = {
 *     "postprocess_query" = 0,
 *   }
 * )
 */
class MyProcessor extends ProcessorPluginBase {

  //Skipped constructor and boilerplate code

  /**
   * {@inheritdoc}
   */
  public function postprocessSearchResults(ResultSetInterface $results) {
    if ($results->getResultCount()) {
      return;
    }

    $query = $results->getQuery();

    if ($query->getProcessingLevel() != QueryInterface::PROCESSING_FULL) {
      return;
    }

    $request = $this->requestStack->getMasterRequest();
    $request_query = $request->query;
    $sdm_empty_result_automatic_search = $request_query->has($this->requestQueryTag) ? $request_query->get($this->requestQueryTag) : FALSE;

    /*
     * Necessary to identify between user search and ones run by this class.
     * E.g. if the user is on "article", and does a new search, it must stay
     * on "articles" if there is any result.
     * However if there are no results, the class must redirect to the fist
     * content type and so on until all are searched
     *
     */
    $next_type_value = NULL;
    $condition_type_value = $this->sdmSearchQueryHelper->queryGetConditionValue($query);
    switch ($condition_type_value) {
      case 'prodotto':
        $next_type_value = 'scheda_espositore';
        $sdm_empty_result_automatic_search = TRUE;
        break;
      case 'scheda_espositore':
        $next_type_value = $sdm_empty_result_automatic_search ? 'article' : 'prodotto';
        break;
      case 'article':
        $next_type_value = $sdm_empty_result_automatic_search ? NULL : 'prodotto';
        break;
    }

    if (empty($next_type_value)) {
      return;
    }

    $query_params = [
      'search_api_fulltext' => $query->getOriginalKeys(),
      'sort_by' => 'title',
      'f' => ['content_type:' . $next_type_value],
    ];

    if ($sdm_empty_result_automatic_search) {
      $query_params[$this->requestQueryTag] = $this->requestQueryTag;
    }

    $response = new RedirectResponse(Url::fromRoute('<current>', [], ['query' => $query_params])
      ->toString());
    $response->send();

     /**
     * todo
     * can't use the following logic but had to do all the previous because I have overriden the default url_processor,
     * so there is always a "default" content type if none is selected.
      * So unfortunately I can't run a generic query to search for all the results.
      * For a "standard" search implementation the following one could be a good starting point
     */
    /*
    $original_query = $query->getOriginalQuery();
    $original_query->addTag($this->queryTag);
    $this->sdmSearchQueryHelper->addConditionToQuery($original_query, 'facet:type', ['type' => 'scheda_espositore']);
    $results = $original_query->execute();

    $items = $results->getResultItems();
    $iterator = $results->getIterator();
    */

  }

```

---
Tags: search, 9
---


# Question
Title: How can I alter the form to add a new validation handler?
I'm on Drupal 8. I have created a new media bundle (*permissions\_json*) which can be created from /media/add/permissions\_json, where administrator user can upload specifically formatted JSON files, and I can upload to it all fine and dandy.

I'd like to validate the file uploaded and rename it. The problem is that I can't find a way to hook into the form. Implementing `hook_form_FORM_ID_alter()` with `gp_mcs_media_permissions_json_add_form_alter()` or `gp_mcs_form_media_permissions_json_add_form_alter()` doesn't have any effect. Even the following implementation didn't have any effect.

```
function gp_mcs_form_alter(&$form, &$form_state, $form_id) {
  dpm($form);
}

```

If I put a simple `exit()` on the module's page stops the website, so the file is accessible. I just can't figure out how to add validation to this add media form.

How can I alter the form to add a new validation handler?

# Answer
The form ID for an entity is a string that is usually returned from `EntityForm::getFormId()`, if the class used for the form doesn't override that method.

This means that, by default, the form ID follows the `<entity>_<bundle>_<operation>_form` schema, where the `<operation>` part is omitted, when *operation* is *default*.
Since the Media module doesn't have a class that overrides `EntityForm::getFormId()`, the correct ID for the form that creates a new Media entity whose bundle is *permissions\_json* is *media\_permissions\_json\_add*. This means that a module whose machine name is *gp\_mcs* can alter that form by implementing `hook_form_FORM_ID_alter()` with a function named `gp_mcs_form_media_permissions_json_add_alter`. (Note that the form ID is between *form* and *alter*.)
> 0 votes

---
Tags: forms, media
---


# Question
Title: How to use "content entity" type in a View?
I have used the "content entity example" coming from the example project.

This example module/:

* defines a "Contact" entity; it can have extra fields, displays can be configured (web/admin/structure/content\_entity\_example\_contact\_settings),
* gives the user the form to add content (contact info) to it. (/web/content\_entity\_example\_contact/add)
* shows a list of the contacts (/web/content\_entity\_example\_contact/list)

I want to make my list as a View, but this content entity type "Contact" is not there to select it. (I see only the node content types, users, taxonomy etcI

How can I use this content in a View ? What do I have to do to make this content available in the Views UI ? Where can I find the content entity types to create a view ? Or how can I add those ?

# Answer
Add a views\_data handler to the entity type. Either the core standard `EntityViewsData`

*content\_entity\_example/src/Entity/Contact.php*

```
  *   handlers = {
  *     "views_data" = "Drupal\views\EntityViewsData",

```

or a custom handler:

*content\_entity\_example/src/Entity/Contact.php*

```
  *   handlers = {
  *     "views_data" = "Drupal\content_entity_example\Entity\ContactViewsData",

```

*content\_entity\_example/src/Entity/ContactViewsData.php*

```
<?php

namespace Drupal\content_entity_example\Entity;

use Drupal\views\EntityViewsData;

/**
 * Makes the content_entity_example entity available to Views
 *
 * @ingroup content_entity_example
 */
class ContactViewsData extends EntityViewsData {

  /**
   * {@inheritdoc}
   */
  public function getViewsData() {
    $data = parent::getViewsData();

    // $data from the parent is enough to make it work but modify as required.

    // Give it a more description name in the dropdown when creating a new view.
    $data['contact']['table']['base']['title'] = $this->t('Content Entity Example Contact');

    // Make the Name field appear to Views as Last Name
    $data['contact']['name']['title'] = $this->t('Last Name');

    return $data;
  }

}

```

To apply this code to the example module use the patch from this issue https://www.drupal.org/project/examples/issues/2725833
> 1 votes

---
Tags: views, entities
---


# Question
Title: "drush config-split:export" returns "Not enough arguments (missing: 'split')."
I recently upgraded from Drupal 8 to Drupal 9. I've used `drush config-split:export` (`drush csex`) quite often. After the upgrade, it keeps throwing an error which is very vague.

> Not enough arguments (missing: "split")

No documentation says what the problem is, nor what that argument should be.

Any help would be much appreciated.

# Answer
> 0 votes
In order to use config-split:export with Drupal 9, make sure you go to admin/config/development/configuration/config-split. There you should see if you already have a split configuration and use machine name: drush config-split:export \[machine\_name\]

---
Tags: drush
---


title: Disable menu link for certain languages

text:
# Question
Title: Disable menu link for certain languages
I'm looking for a way to disable a single link in a menu for a certain language. From my understanding with similar issues, I'd like to activate translation for the menu link field *Enabled* since this is set for all languages by default.

Is there a way to activate the translation of this field for menu links?

# Answer
This wasn't quite exactly what I was looking for, but it suits my case and is simple to use, so just in case anyone stumbles upon this looking for a solution:

The Menu Manipulator module hides menu links that are pointing to contents without the current translation. You can specify which menus will be managed by this, so this is at least a little bit customizable.
> 0 votes

---
Tags: i18n-l10n, navigation, 9
---


# Question
Title: Use read-only database user to minimize security risks
I want to phase out an old, unmaintained Drupal 6 page. The website should still stay online, but should be read-only.

I want to change the database user so that it only has `read` privileges. I don't want the website to be exploitable due to any security issue in Drupal 6 or any of the modules.

Is it possible to give Drupal 6 a read-only database user? On which table is write access necessary?

# Answer
You can make make read only tables, except: `semaphor, watchdog, cache, cache_[ANOTHERS], sessions`, and another tables, depend on installed modules, that can modify tables on guests visiting, login or form submissions, like statistics, flag and so on.

But I strongly **DO NOT** recommend this, because instead of showing read only site you may show white or errors screen.

Better is use Read only mode module, that make site read only (prevent form submissions and so on).

Another solution - make backup, then restore 1 per day via bash script. Samples.
> 0 votes

# Answer
For a site where users cannot login, read-only access to the database is possible if none of the modules:

* Saves data in the database
* Stores data in the user session
* Caches data
* Uses the locking system implemented by Drupal core
* Saves or deletes persistent variables
* Logs errors

If the purpose is just avoiding the Drupal site writes on the database, it's possible to provide a different implementation for session handlers, cache, and lock system that stores data in Memcached, but the data would not persist as in a database. (This could be the desired effect, if the site isn't supposed to store the data for anonymous users for much long.)
The data stored from `watchdog()` can be stored in the system Syslog, if instead of the *Database logging* module the site uses the *Syslog* module. As for storing data in the session, keep in mind that even a call to `drupal_set_message()` causes Drupal to add data in the session.

Otherwise, for a partial read-only access to the database, the following database tables should be writable.

* cache
* cache\_\*
* semaphore
* sessions
* variable (This is the database table where the values submitted in a form could be saved, especially setting values.)
* watchdog, if the *Database logging* module is enabled
* Any database table the installed modules use to write data
> 0 votes

# Answer
You could have a module like Boost or similar generate a static version of the site and then just serve that and decommission the database - unless you have some dynamic/ajax action happening. You could also dump the site with a tool like HTTrack.
> 0 votes

---
Tags: database, 6, security
---


# Question
Title: How to deal with block cached for anonymous user?
I have placed in the site's home, via twig, a views block of nodes with a random sort and obliviously no cache active. Therefore, at each page reload different nodes appear.

The problem is that block get still cached for anonymous users.

For now, we can't yet do performance test of the site, so we'd rather not disable the Internal Page Cache module.

The Internal page documentation says:

> Websites that serve personalized content to anonymous users (dynamic, per-session, e.g. a shopping cart) will want to disable the Internal Page Cache module. This module assumes pages are identical for all anonymous users. Those websites can still take advantage of the Dynamic Page Cache module though, or can alternatively do their personalization using JavaScript + AJAX.

So after failing to find anything else useful, I've tried this solution to solve the issue.

Unfortunately I found no module that let "load" the block via ajax, or something similar. For now I've implemented a custom route that render the block view:

```
<?php

namespace Drupal\my_module\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Render\Renderer;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\sdm_user\SdmUserSalesforceClient;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Response;

/**
 * Provides route responses for the Example module.
 */
class myController extends ControllerBase {

  /**
   * @var \Drupal\Core\Render\Renderer
   */
  private  $renderer;

  /**
   * @param \Drupal\Core\Render\Renderer $renderer
   */
  public function __construct(Renderer $renderer){
    $this->renderer = $renderer;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('renderer')
    );
  }

  /**
   * @return array
   *   A simple renderable array.
   */
  public function render($id, $display) {
    $view = \Drupal\views\Views::getView($id);
    $render_array = $view->buildRenderable($display);
    $rendered = $this->renderer->renderRoot($render_array);
    $response = new Response();
    $response->setContent($rendered);

    return $response;

  }
}

```

And then in a custom js, load the necessary block via ajax:

```
(function ($, Drupal, drupalSettings) {
    Drupal.behaviors.products_in_home = {
        attach: function (context, settings) {
            $(document, context).once('products_in_home').each(function () {
                jQuery.ajax({
                    url: drupalSettings.path.currentLanguage + '/ajax/views/prodotti/products',
                    method: 'GET',
                    success: function (data) {
                        $(".products-container-home").html(data);
                    }
                });
            });
        }
    }
}(jQuery, Drupal, drupalSettings));

```

This works, but I'm kinda baffled: is this the only approach possible?

I mean, do I need to manually load via ajax every block\thing in the site that will suffer this problem?

Isn't there a module\API\some approach that can generally solve this problem, without custom specific code for every element?

# Answer
In general the Internal Page Cache is a rather simple solution. If you need more features replace it with Varnish or a CDN. Most of them provide similar features, like for example ESI to serve dynamic blocks if you can't use Javascript. I think most people would use Javascript in this case, though, and there is a fully cacheable solution. You can deliver all eligible node IDs in drupalSettings and then select and fetch a random node in Javascript. Both, the page containing all eligible nodes and the node randomly fetched, are cacheable, so you can provide the dynamic behavior without any performance hit.
> 1 votes

---
Tags: 8, views, blocks, ajax, caching
---


# Question
Title: OAuth2 Server sending 'null' as the 'sub' parameter instead of User Id
When using `OAuth2 Server` module to authorize with users, I am not receiving the correct "sub" parameter in the response from the server. I get all the other fields such as email, email\_verified, name, preferred\_username, and zoneinfo; just not the sub parameter.

```
{
  "sub": null,
  "email": "someuser@email.com",
  "email_verified": true,
  "name": "SomeUser",
  "preferred_username": "SomeUser",
  "zoneinfo": "Asia/Dubai"
}

```

I've tried using `hook_oauth2_server_user_claims` to alter this parameter like so:

```
function my_module_oauth_server_user_claims($account, $requested_scopes) {
  $claims = [];

  if (in_array('profile', $requested_scopes)) {
    $claims['sub'] = $account->id();
    $claims['user_id'] = $account->id(); // for debugging purposes
  }

  return $claims;
}

```

Doing this adds the `user_id` parameter to the response body, but the 'sub' is still set as 'null', therefore it must get override afterwards.

I double checked the documentation and have all the permissions set, but can't find anything about this particular issue.. Why is \`sub' not being populated correctly during this process?

# Answer
The code invoking that hook is the following one, in the OAuth2Storage.php file, line 750 and forward.

```
    // Prepare the default claims.
    $claims = [
      'sub' => $account->{$sub_property}->value,
    ];

    if (in_array('email', $requested_scopes)) {
      $claims['email'] = $account->getEmail();
      $claims['email_verified'] = $this->configFactory->get('user.settings')
        ->get('verify_mail');
    }

    if (in_array('profile', $requested_scopes)) {
      if (!empty($account->label())) {
        $claims['name'] = $account->getDisplayName();
        $claims['preferred_username'] = $account->getAccountName();
      }
      if (!empty($account->timezone)) {
        $claims['zoneinfo'] = $account->getTimeZone();
      }
      $anonymous_user = new AnonymousUserSession();
      if ($anonymous_user->hasPermission('access user profiles')) {
        $claims['profile'] = $account->toUrl('canonical', ['absolute' => TRUE]);
      }
      if ($picture = $this->getUserPicture($account)) {
        $claims['picture'] = $picture;
      }
    }

    // Allow modules to supply additional claims.
    $claims += $this->moduleHandler->invokeAll('oauth2_server_user_claims', [
      'account' => $account,
      'requested_scopes' => $requested_scopes,
    ]);

    // Finally, allow modules to alter claims.
    $context = [
      'claims' => &$claims,
      'account' => $account,
      'requested_scopes' => $requested_scopes,
    ];
    $this->moduleHandler->alter('oauth2_server_user_claims', $context);
    return $claims;

```

Since the code first sets `$claims` to `['sub' => $account->{$sub_property}->value]` and then adds any array key returned from that hook with `$claims += $this->moduleHandler->invokeAll('oauth2_server_user_claims', ['account' => $account, 'requested_scopes' => $requested_scopes]);` the hook cannot change the value for the *sub* key. You can verify how PHP behaves with the following simpler code.

```
$array = ['sub' => NULL];
$array += ['sub' => 10, 'id' => 10];
print_r($array);

```

To alter the *sub* key, the module should implement `hook_oauth2_server_user_claims_alter()`.

```
function my_module_oauth_server_user_claims_alter(&$context) {
  if (in_array('profile', $context['requested_scopes'])) {
    $context['claims']['sub'] = $account->id();
    $context['claims']['user_id'] = $account->id();
  }
}

```
> 0 votes

---
Tags: 8, authentication-authorization
---


title: OO way of adding base field definitions

text:
# Question
Title: OO way of adding base field definitions
Is there any object oriented Drupal way to add a base field definition to an existing entity?

Of course I am familiar with `hook_entity_base_field_info()`, but I am trying to keep my .module file size to a minimum and I was wondering if there is a way to achieve that with a different approach.

I thought of decorating or extending the entity, but after some research, I stumbled upon these issues that suggest those two options aren't yet possible or elegant, as far as I understand.

Am I wrong in that?

Is there anyone that has had the same case and found a different approach?

Edit: TL;DR answer is that trying to replace this specific hook, will cause more confusion, rather than improve code clarity.

# Answer
> 2 votes
Without using any hook, it's not possible, since to change the class used for an entity, it's necessary to implement `hook_entity_type_alter()`, which is problematic to implement when more modules need to alter the entity definition, for example changing the same entity handler, or the entity class, for the same entity type.

Imagine two different modules that try to add two different fields to the User entity by replacing the entity class used for it. For example, the first module could use the following code.

```
/**
 * Implements hook_entity_type_alter().
 */
function login_id_entity_type_alter(array &$entity_types) {
  $entity_types['user']->setClass(LoginIdUser::class);
}

```

```
namespace Drupal\login_id\Entity;

use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\user\Entity\User;

class LoginIdUser extends User {

  /**
   * {@inheritdoc}
   */
  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
    /** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */
    $fields = parent::baseFieldDefinitions();

    $fields['login_id'] = BaseFieldDefinition::create('string')
      ->setLabel(t('Login ID'))
      ->setDescription(t('The ID used for the login credentials.'))
      ->setRequired(TRUE)
      ->setConstraints([
        'LoginId' => [],
        'LoginIdUnique' => [],
    ]);

    $fields['name']->getItemDefinition()
      ->setClass('\\Drupal\\login_id\\LoginIdItem');

    return $fields;
  }

}

```

The second module could use the following code.

```
/**
 * Implements hook_entity_type_alter().
 */
function admin_email_entity_type_alter(array &$entity_types) {
  $entity_types['user']->setClass(AdminEmailUser::class);
}

```

```
namespace Drupal\admin_email\Entity;

use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\user\Entity\User;

class AdminEmailUser extends User {

  /**
   * {@inheritdoc}
   */
  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
    /** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */
    $fields = parent::baseFieldDefinitions();

    $fields['admin_mail'] = BaseFieldDefinition::create('email')
      ->setLabel(t('Admin email'))
      ->setDescription(t('The email used from administrator users to contact the user.'))
      ->setDefaultValue('')
      ->addConstraint('UserMailUnique')
      ->addConstraint('UserMailRequired')
      ->addConstraint('ProtectedUserField');

    return $fields;
  }

}

```

With two modules implementing that code, only a field would be added to the entity. To get both the entity fields, `LoginIdUser` should extend `AdminEmailUser`, which would mean that a module should have the other one as dependency.
This assumes that `admin_email_entity_type_alter()` is executed before `login_id_entity_type_alter()`. This is what normally happens when the hook execution order isn't altered by a `hook_module_implements_alter()` implementation, or when the weight associated to module isn't altered by a call to `module_set_weight()`. In the case `login_id_entity_type_alter()` is invoked before `admin_email_entity_type_alter()`, for example because a module implements `hook_module_implements_alter()` and changes the order of those hook implementations, `AdminEmailUser` should extend `LoginIdUser`.

Implementing `hook_entity_base_field_info()` allows to modules to add different fields without interfering with each other.

As side note, hooks can be placed in a file different from a .module file, which is automatically loaded from Drupal. Which files are loaded from Drupal when looking for hooks is influenced by `hook_hook_info()`. With the implementations of that hook done from Drupal core modules, the files Drupal core looks for are the following.

* `<module_name>.tokens.inc`
* `<module_name>.views.inc`

If you are interested in reducing the size of the .module file, implementing `hook_hook_info()` to tell Drupal where your module hooks are could be a way to achieve it.

# Answer
> 1 votes
You could override the class being used for the entity type, and add your base field definitions there.

For example, if you wanted to add a base field definition to the Node entity, you could do this:

```
namespace Drupal\[EXAMPLE]\entity;

use Drupal\node\Entity\Node as NodeBase;

class Node extends NodeBase {

  /**
   * {@inheritdoc}
   */
  public function baseFieldDefinitions() {
    $definitions = parent::baseFieldDefinitions();
    // Add your field definition here

    return $definitions;
  }

}

```

Then you would override the class used as the backend for nodes, overriding the default node class with your class that extends it:

```
/**
 * Implements hook_entity_type_alter().
 */
function HOOK_entity_type_alter(array &$entity_types) {
  $node = &$entity_types['node'];
  $node->setClass('Drupal\[EXAMPLE]\Entity\Node');
}

```

I would suggest that if you are creating a contributed module to be used across projects, you're better off using a hook to add your field definition. This method should only be used for projects in which you're able to control the inheritance/hierarchy of the objects backing an entity type.

---
Tags: entities, 9
---


# Question
Title: Fresh Drupal Install, UnmetDependenciesException
Has anyone ever ran into this, `UnmetDependenciesException`? From googling there is quite a bit of talk about this, but so far no concrete solution.

and at the bottom (you can't see it in the screenshot) it says

```
dependencies in Drupal\Core\Config\UnmetDependenciesException::create() (line 84 of core/lib/Drupal/Core/Config/UnmetDependenciesException.php).

Drupal\Core\Config\UnmetDependenciesException::create('standard', Array) (Line: 460)
Drupal\Core\Config\ConfigInstaller->checkConfigurationToInstall('module', 'standard') (Line: 132)
Drupal\Core\ProxyClass\Config\ConfigInstaller->checkConfigurationToInstall('module', 'standard') (Line: 141)
Drupal\Core\Extension\ModuleInstaller->install(Array, ) (Line: 83)
Drupal\Core\ProxyClass\Extension\ModuleInstaller->install(Array, ) (Line: 1571)
install_install_profile(Array) (Line: 661)
install_run_task(Array, Array) (Line: 539)
install_run_tasks(Array) (Line: 116)
install_drupal(Object) (Line: 44)

```

I got this when I did a fresh install of LAMP.

I do not get this error when installing Drupal 8 in another LAMP server that I setup a long time ago.

This leads me to believe that it could be some php extension missing or some other package, but according to the installer, it doesn't say that I am missing anything.

Suggested solutions that have not worked:

* Using a different browser with all history & cookies cleared.
* Made sure no Core files are missing, since I did a `wget` and then unzip. Even tried diff core versions.
* Selecting Minimal install.

# Answer
> 1 votes
I did another LAMP install and it worked. So IDK why it didn't before.

Although accidentally I managed to break it when using a bad php.ini file, so I guess that's one possibility.

# Answer
> 1 votes
So, I got this error trying to install Drupal 8.9.13 on MAMP Pro 6.3 using FireFox 87.0 on macOS Big Sur 11.2.3.

Switched over to Safari, and the installation completed.

Big lesson here, I suppose: Try a different browser!

---
Tags: 8, installation
---


# Question
Title: Create and use a custom Account Email Template
I am trying to create a new Email Template for the Account object using hook\_form\_alter. Here is how I create the template:

```
function my_module_form_user_admin_settings_alter(&$form, &$form_state, $form_id) {
    // Adds the Course Link email template to the Account Settings form
    $form['email_course_link'] = [
        '#type' => 'details',
        '#title' => t('Course link'),
        '#description' => t('Edit the course link e-mail messages sent to mobile app users.'),
        '#group' => 'email',
    ];
    $form['email_course_link']['user_mail_course_link_subject'] = [
        '#type' => 'textfield',
        '#title' => t('Subject'),
        '#maxlength' => 180,
    ];
    $form['email_course_link']['user_mail_course_link_body'] = [
        '#type' => 'textarea',
        '#title' => t('Body'),
        '#rows' => 15,
    ];
}

```

This adds the email template to my Account Settings form, but I am having two issues:

1. When I go to edit the template and hit save configurations, nothing gets saved. The subject and body edits I made are wiped out and they no longer exist.
2. When I go to use the template via `_user_mail_notify('email_course_link', $user)` I get NULL as the response and there are zero errors to be found and I never get the email...

What else am I missing here?

# Answer
Specifically, your code is modifying the `AccountSettingsForm`, which is part of the core Drupal "user" module.

If you look at the source code for that form, you will see that like all forms in Drupal, it has both a `buildForm()` method and a `submitForm()` method. The problem with your code is that you've added form elements to the form array created by default in `buildForm()`, but you HAVEN'T added any code to process those new form elements and save the entered data, which is normally done in `submitForm()`. If you create new form elements in an existing form, you are responsible for processing the input and saving the entered values. Your custom module is the only thing that knows about those new elements, and your custom module is responsible for storing that data.

Let's look at one of the other elements defined in `AccountSettingsForm`, for example the `email_admin_created` details element which contains a `user_mail_register_admin_created_subject` and a `user_mail_register_admin_created_body`. It looks like you copied this code and changed the names for your own purpose, which is a good way to approach this. But if you search AccountSettingsForm, you will see that those form elements are also processed and saved in `submitForm()`, and you haven't added any new equivalent code for your new form elements.

The key then is to write your own function to execute on form submit, and in that function you need to save the values entered into your custom form elements. The way to tell Drupal about your custom submit function is to add the following at the end of your `hook_form_alter()`:

```
$form['actions']['submit']['#submit'][] = 'my_module_email_course_link_submit';

```

Then add your function to my\_module.module:

```
/**
 * Submit callback to  process and save email_course_link form elements.
 */
function my_module_email_course_link_submit(array $form, FormStateInterface $form_state) {
  $entered_body_value = $form_state->getValue('user_mail_course_link_body');
  $entered_subject_value = $form_state->getValue('user_mail_course_link_subject');

  // Save $entered_body_value and $entered_subject_value somewhere.
  // For example, in a database table created by your module, or
  // in a config object created by your module.
  // Where and how you save it is up to you.
}

```

You will also have to add `use Drupal\Core\Form\FormStateInterface;` at the top of my\_module.module.

When you look at `AccountSettingsForm` you will see the user module saves form elements in the `user.settings` and `user.mail` configs. DON'T use those configs to store your custom data - define your own custom config if you want to store your own custom data in a configuration object.
> 0 votes

---
Tags: 8, emails
---


# Question
Title: How to block Webform spam based on keyword strings?
Short Version: I would like to block Webform submissions that contain strings e.g. `mail.ru` in the webform textarea field.

Recently I am seeing a lot of fake webform submissions. I am already using honeypot contrib module and I have added quite a number of IP addresses to the server firewall. However there is still some spam coming through. For now I don't want use Captcha. Luckily those spammers always using recurring pattern e.g. same domain name.

How I can automatically block submission that contain certain strings? Using the rules + webform\_rules module and Regex doesn't allow me to choose the Webform email field as data selector. `selected-webform:0` doesn't look valid. What's the right way to block webform spam?

```
{ "rules_blocking_fake_registration" : {
"LABEL" : "Blocking fake registration",
"PLUGIN" : "reaction rule",
"OWNER" : "rules",
"REQUIRES" : [ "rules", "webform_rules" ],
"ON" : { "webform_rules_submit" : [] },
"IF" : [
  { "node_is_of_type" : {
      "node" : [ "node" ],
      "type" : { "value" : { "contactform" : "contactform" } }
    }
  },
  { "text_matches" : {
      "text" : [ "selected-webform:0" ],
      "match" : "plusgmail.ru",
      "operation" : "regex"
    }
  }
],
"DO" : [
  { "block_ip" : [] },
  { "mail" : {
      "to" : "admin@address.com",
      "subject" : "[site:name]: Blocking user [data:e_mail-value]",
      "message" : "E-Mail Adresse: [data:e_mail-value]",
      "language" : [ "site:current-user:language" ]
    }
   }
  ]
 }
}

```

I really appreciate any comments and help.

# Answer
### Part 1 - Getting access to Webform data

Below is something about using Webform data in Rules, which may help you to get this to work (assuming your question is about D7) ...

You have access to all ***labels*** and all ***values*** of any field of a webform, ie:

* `[data:MachineFieldName-title]` contains the **Label** of a field with machine name MachineFieldName.
* `[data:MachineFieldName-value]` contains the **Value** of a field with machine name MachineFieldName

So try to use similar tokens. For way more details on this, and a prototype rule about this, refer to the answer to "How to create a node from a Webform using rules?":

* the *1st attempt* relates to using "Entity has field" (which doesn't work for Webform)
* the *2nd attempt* explains what to do in case of Webform.

With the above in mind, and looking at something I see in your Rules Actions (not Rules Conditions ...), it seems that `[data:e_mail-value]` might be pretty close to what you're looking for (assuming it's the field with machine name `e_mail` that you want to validate via a Rules Condition, since you didn't mention the machine name of the Webform field to be validated).

### Part 2 - Validating Webform data

Everything described in Part 1 is a good start, but not sufficient to achieve your goal (= validate what is contained "*in the Webform textarea field*", as in your question).

However, you can use a technique similar to what is described in my answer to "How to do a data comparison to submitted webform data when using the Rules module?". For short:

* Add an extra Rules Action to `Add a variable`, with a value assigned to it equal to your `[data:e_mail-value-raw]`.
* Add whatever validation you want, probably also something like a text comparison to check if the newly added variable value *contains* `mail.ru`.

Be aware: as per the Rules Event you are using in the rule contained in your question, the Webform has been submitted (and I'm not aware of what seems like a more appropriate event like "Before submitting a Webform"). So therefor your existing Rules Action to block the IP address is a good start (together with the eMail to the admin). But know that the form is submitted already.
> 1 votes

# Answer
Try Webform Validation module. It adds validation options for webforms. One of these extra options is "words blacklist", where you can specify the list of strings that should not appear in your field. The spammers will get validation error and will not be able to submit the form.
> 2 votes

# Answer
To Avoid spam words on Webform. Try this module (Drupal 8 & 9) https://www.drupal.org/project/webform\_spam\_words We can specify the spam keywords, field and error message. It's a simple and lightweight module
> 0 votes

---
Tags: 7, webforms
---


# Question
Title: How do I download a form table in CSV or Excel format?
I have a form whose class extends `FormBase`. The form has a table that gets populated with information.

Basically I'm trying to download the table into a CSV or Excel file. I've seen some old posts for Drupal 6 but I haven't found anything for Drupal 8.

How can I achieve this?

# Answer
You can use the Forena module. Here is a quote from that page:

> ... built of the idea of using SQL to get data out of a database and use XHTML and CSS to format it into web reports.

Here is another quote from Reasons for considering Forena, related to the "CSV" in the question here:

> Reports created by Forena ... can be saved (exported) as PDF documents with embedded graphs. Other supported formats for saving a report are **CSV**, XLS, DOC, HTML, etc.

So if you'd install Forena in your site, then these are the steps for creating such CSV files are:

* a custom **SQL query** (where you have the full power of SQL at your fingertips, and various extra features related to it provided by Forena, such as using report parameters, conditional SQL, etc).
* a custom **report** (either by an admin, or by just any regular user who is either familiar with creating what is called 'FRX files', or otherwise using the WYSIWYG report editor).
* saving the report in (eg) CSV format, using one of the typical hyperlinks to do so in a report.

For an **online demo** (prior to installing/testing Forena), checkout these links:

Disclosure: I'm a **co**-maintainer of this module,
I hope this does not violate the site's policy on self-promotion.
> 0 votes

---
Tags: 8, forms
---


# Question
Title: How do I allow only username for password reset form?
By default the password reset module allows 'Username OR E-mail'. I am using a module which allows multiple user accounts with a same email address (but usernames are unique and auto-generated), so the unique user account identifier for logging in is username, I need this to be the same in case of password reset form.

# Answer
Using the following module solves the issue as it has an option to make the reset form 'username only'. https://www.drupal.org/project/sharedemail\_pass\_reset/
> 0 votes

---
Tags: 8, authentication-authorization
---


# Question
Title: Is there a way to generate dummy content for custom entities?
Since the Devel module allows to generate content only for core entities:

> Accelerate development of your site or module by quickly generating nodes, comments, terms, users, and more.

Is there a module or a proper way to generate fake content for custom entities?

*I mention that I found Devel contrib module that includes the Devel Contrib Generate module, which allows generating of dummy entities of any entity type, but I've not been able to figure out how to use it with D8.*

# Answer
> 1 votes
I've just wrote a blog post on this topic, it's in french but the code should be easily understandable : https://kgaut.net/snippets/2021/drupal-utiliser-devel-generate-pour-generer-des-entites-personalisees.html

You can write a plugin that extend DevelGenerateBase, I used ExampleDevelGenerate as an inspiration : https://api.drupal.org/api/devel/devel\_generate%21tests%21modules%21devel\_generate\_example%21src%21Plugin%21DevelGenerate%21ExampleDevelGenerate.php/class/ExampleDevelGenerate/8.x-1.x

# Answer
> 0 votes
To create some demo entities programmatically, you can use a while loop in your function. To create 100 entities:

```
$i = 1;
while ($i <= 100) {
  $i++;

  // Create an entity here.  See https://www.drupal.org/docs/8/api/entity-api/working-with-the-entity-api#s-create
  // To create random strings (e.g. for fake titles), refer to the comments at https://www.php.net/manual/en/function.rand.php -- In here you will find different ways to create random strings.
  // Generate some Lorem Ipsum text if you like: https://stackoverflow.com/a/20633400/861826

}

```

---
Tags: 8
---


# Question
Title: Error trying to submit webform programatically multiple times: Duplicate entry for key 'PRIMARY'
I'm trying to programatically send submissions to a webform n times, but when I submit the submission info to the webform for the second time, I get an Integrity constraint violation: 1062 Duplicate entry 'nnnnn' for key 'PRIMARY' error. It appears the second submission has the same sid as the first submission, but I'm not sure why as I'm submitting NULL for the sid. How can I submit the webform again with the same information, but get a new sid? Drupal 7.

Here is the error in full:

PDOException: SQLSTATE\[23000\]: Integrity constraint violation: 1062 Duplicate entry '211-82916-1-0' for key 'PRIMARY': INSERT INTO {webform\_submitted\_data} (nid, sid, cid, no, data) VALUES (:db\_insert\_placeholder\_0, :db\_insert\_placeholder\_1, :db\_insert\_placeholder\_2, :db\_insert\_placeholder\_3, :db\_insert\_placeholder\_4); Array ( \[:db\_insert\_placeholder\_0\] =\> 211 \[:db\_insert\_placeholder\_1\] =\> 82916 \[:db\_insert\_placeholder\_2\] =\> 1 \[:db\_insert\_placeholder\_3\] =\> 0 \[:db\_insert\_placeholder\_4\] =\> Bob ) in drupal\_write\_record() (line 7471 of /var/www/html/includes/common.inc).

And the code:

```
function wildsight_raffle_tickets_webform_submission_insert($node, $submission) {
  if ($submission->nid == 210) {

  $node_out = node_load('211');
  global $user;

  $data = array(
    'first_name' => $submission->data[3][0],
    'last_name' => $submission->data[4][0],
    'email' => $submission->data[14][0],
  );

  module_load_include('inc', 'webform', 'webform.module');
  module_load_include('inc', 'webform', 'includes/webform.submissions');

  // This methods will arrange $data in the right way
  $data = _webform_client_form_submit_flatten($node_out, $data);
  $data = webform_submission_data($node_out, $data);

  $submission_out = (object) array(
    'nid' => '211',
    'uid' => $user->uid,
    'sid' => NULL,
    'submitted' => REQUEST_TIME,
    'completed' => REQUEST_TIME,
    'remote_addr' => ip_address(),
    'is_draft' => FALSE,
    'data' => $data,
  );

  $number_of_tickets = $submission->data[24][0];
  while ($number_of_tickets > 0) {
    webform_submission_insert($node_out, $submission_out);
    $number_of_tickets -= 1;
  }
}
}

```

# Answer
> 0 votes
Not entirely sure why, but moving the line that sets $submission\_out inside the loop fixes this problem.

---
Tags: webforms
---


title: Migrate taxonomy terms with CSV

text:
# Question
Title: Migrate taxonomy terms with CSV
I would like to import some taxonomy terms with a taxonomy term in it. `field_regio2` is a `entityreference` field to an existing taxonomy term `Regio`. But for some reason it is not working. Everything is working except the `field_regio2`.

```
$ drush mmsg import_gemeentes gives:

```

> the entity\_lookup plugin requires a value\_key, none located

?

```
uuid: 7eef6b86-80db-40ab-8359-91d20675e0f2
langcode: en
status: true
dependencies: {  }
id: import_gemeentes
class: null
field_plugin_method: null
cck_plugin_method: null
migration_tags: null
migration_group: migrated_terms
label: 'Migrate gemeentes from the source CSV to taxonomy terms'
source:
  plugin: csv
  path: 'public://gemeentes.csv'
  header_row_count: 1
  keys:
    - id
  column_names:
    -
      id: id
    -
      vid: vid
    -
      name: name
    -
      field_stratenplan_luchtfoto_refe: field_stratenplan_luchtfoto_refe
    -
      rid: rid
    -
      rid22: rid2
process:
  vid:
    plugin: default_value
    default_value: gemeente
  name: name
  field_stratenplan_luchtfoto_refe: field_stratenplan_luchtfoto_refe
  field_regio: rid
  field_regio2:
    plugin: entity_generate
    source: rid22
destination:
  plugin: 'entity:taxonomy_term'
migration_dependencies: null

```

# Answer
The syntax for entity\_generate is:

```
field_regio2:
    plugin: entity_generate
    source: rid22
    entity_type: taxonomy_term
    # This is where migration breaks. It need to know which key to look for.
    value_key: name
    bundle_key: vid
    bundle: YOUR_VOCABULARY_MACHINE_NAME

```
> 1 votes

# Answer
It's important to realize that it's the `value_key` that will be matched to whatever is coming from the source. So if we say `value_key: name` then the source should contain strings (not IDs!) that will be matched to or create taxonomy term names.

Here's a working section of code from the `process` section of a node migration YAML-file. It gets *names* of category terms via `source`, matching them to names in vocabulary called "categories". If a a term with the given name does not exist in the vocabulary, it will be created.

```
# Name of the entityreference field, and we specify its property target_id:
field_category/target_id:
    plugin: entity_generate
    # "category" contains the textual name of the category, coming e.g. from CSV:
    source: category
    # Do not change this:
    entity_type: taxonomy_term
    # Do not change this:
    value_key: name
    # Do not change this:
    bundle_key: vid
    # Machine name of your vocabulary:
    bundle: categories

```

Here's the best page ever I've found on the subject: https://understanddrupal.com/articles/understanding-entitylookup-and-entitygenerate-process-plugins-migrate-tools
> 0 votes

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


title: hook_views_query_alter() not working?

text:
# Question
Title: hook_views_query_alter() not working?
Very new to web development and even newer to Drupal, and I have some questions that I would really appreciate if anyone could answer!

Here's my yml file, \[Site name\] is actually just the name of our site:

```
name: '[Site name] Query'
description: '[Site name] custom queries'
package: [Site name]
core: '8.x'

```

Here's my .module file:

```
<?php

use Drupal\views\ViewExecutable;
use Drupal\views\Plugin\views\query\QueryPluginBase;

function ppc_query_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {
  $database = \Drupal::database();
  if ($view->id() == 'test_events') {
    $query = $database->query("SELECT node__field_date.field_date_value AS node__field_date_field_date_value,
      node_field_data.nid AS nid, DATE_FORMAT((node__field_date.field_date_value + INTERVAL -18000 SECOND), '%Y%m%d%H') AS node__field_date_field_date_value_hour
      FROM {node_field_data} node_field_data
      LEFT JOIN {node__field_date} node__field_date ON node_field_data.nid = node__field_date.entity_id AND node__field_date.deleted = '0'
      WHERE (node_field_data.status = '1') AND (node_field_data.type IN ('event')) AND (node__field_date.field_date_value >= DATE(NOW()))
      ORDER BY node__field_date_field_date_value_hour DESC, node__field_date_field_date_value ASC
      LIMIT 4 OFFSET 0
");
  }
}

```

I copied and pasted the query that was already being run on my view and added some changes that the views module couldn't support.

However when I run this module on my site, nothing changes. How do I get my query alter function to work?

Thank you!

# Answer
Nothing changes because you are setting a hook argument, but the value to which you set that argument isn't kept.

`hook_views_query_alter()` is invoked from `Sql::alter()`, which is called from `ViewsExecutable::build()`.

```
// ViewsExecutable::build()
// Let modules modify the query just prior to finalizing it.
$this->query->alter($this);

```

```
// Sql::alter()
\Drupal::moduleHandler()->invokeAll('views_query_alter', [$view, $this]);

```

The only way to alter the query is using the methods made available from `QueryPluginBase` (including its parent classes), not replacing an object passed to the hook with another object. See for example `workspaces_views_query_alter()`, an implementation of that hook done from a Drupal core module. The relevant code altering the query is the following one (`ViewsQueryAlter::alterQuery()` and `ViewsQueryAlter::alterQueryForEntityType()`). (I copied only the comments that could help in understanding the changes done to the query.)

```
public function alterQuery(ViewExecutable $view, QueryPluginBase $query) {
  if (!$this->workspaceManager->hasActiveWorkspace()) {
    return;
  }

  if (!$query instanceof Sql) {
    return;
  }

  $entity_type_ids = [];
  foreach ($query->relationships as $info) {
    $table_data = $this->viewsData->get($info['base']);
    if (empty($table_data['table']['entity type'])) {
      continue;
    }
    $entity_type_id = $table_data['table']['entity type'];

    $entity_type_ids[$entity_type_id] = $entity_type_id;
  }
  $entity_type_definitions = $this->entityTypeManager->getDefinitions();
  foreach ($entity_type_ids as $entity_type_id) {
    if ($this->workspaceManager->isEntityTypeSupported($entity_type_definitions[$entity_type_id])) {
      $this->alterQueryForEntityType($query, $entity_type_definitions[$entity_type_id]);
    }
  }
}

protected function alterQueryForEntityType(Sql $query, EntityTypeInterface $entity_type) {
  $table_mapping = $this->entityTypeManager->getStorage($entity_type->id())->getTableMapping();
  $field_storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($entity_type->id());
  $dedicated_field_storage_definitions = array_filter($field_storage_definitions, function ($definition) use ($table_mapping) {
    return $table_mapping->requiresDedicatedTableStorage($definition);
  });
  $dedicated_field_data_tables = array_map(function ($definition) use ($table_mapping) {
    return $table_mapping->getDedicatedDataTableName($definition);
  }, $dedicated_field_storage_definitions);
  $move_workspace_tables = [];
  $table_queue =& $query->getTableQueue();
  foreach ($table_queue as $alias => &$table_info) {
    if ($table_info['table'] == 'workspace_association') {
      break;
    }

    if ($field_name = array_search($table_info['table'], $dedicated_field_data_tables, TRUE)) {
      $relationship = $table_info['relationship'];

      if ($table_info['join']->field != 'entity_id') {
        continue;
      }

      $new_table_name = $table_mapping->getDedicatedRevisionTableName($field_storage_definitions[$field_name]);

      $workspace_association_table = $this->ensureWorkspaceAssociationTable($entity_type->id(), $query, $relationship);

      // Update the join to use our COALESCE.
      $revision_field = $entity_type->getKey('revision');
      $table_info['join']->leftFormula = "COALESCE({$workspace_association_table}.target_entity_revision_id, {$relationship}.{$revision_field})";

      // Update the join and the table info to our new table name, and to join
      // on the revision key.
      $table_info['table'] = $new_table_name;
      $table_info['join']->table = $new_table_name;
      $table_info['join']->field = 'revision_id';

      // Finally, if we added the workspace_association table we have to move
      // it in the table queue so that it comes before this field.
      if (empty($move_workspace_tables[$workspace_association_table])) {
        $move_workspace_tables[$workspace_association_table] = $alias;
      }
    }
  }

  // JOINs must be in order. i.e, any tables you mention in the ON clause of a
  // JOIN must appear prior to that JOIN. Since we're modifying a JOIN in
  // place, and adding a new table, we must ensure that the new table appears
  // prior to this one. So we recorded at what index we saw that table, and
  // then use array_splice() to move the workspace_association table join to
  // the correct position.
  foreach ($move_workspace_tables as $workspace_association_table => $alias) {
    $this->moveEntityTable($query, $workspace_association_table, $alias);
  }
  $base_entity_table = $entity_type->isTranslatable() ? $entity_type->getDataTable() : $entity_type->getBaseTable();
  $base_fields = array_diff($table_mapping->getFieldNames($entity_type->getBaseTable()), [
    $entity_type->getKey('langcode'),
  ]);
  $revisionable_fields = array_diff($table_mapping->getFieldNames($entity_type->getRevisionDataTable()), $base_fields);

  // Go through and look to see if we have to modify fields and filters.
  foreach ($query->fields as &$field_info) {
    // Some fields don't actually have tables, meaning they're formulae and
    // whatnot. At this time we are going to ignore those.
    if (empty($field_info['table'])) {
      continue;
    }

    // Dereference the alias into the actual table.
    $table = $table_queue[$field_info['table']]['table'];
    if ($table == $base_entity_table && in_array($field_info['field'], $revisionable_fields)) {
      $relationship = $table_queue[$field_info['table']]['alias'];
      $alias = $this->ensureRevisionTable($entity_type, $query, $relationship);
      if ($alias) {
        // Change the base table to use the revision table instead.
        $field_info['table'] = $alias;
      }
    }
  }
  $relationships = [];

  // Build a list of all relationships that might be for our table.
  foreach ($query->relationships as $relationship => $info) {
    if ($info['base'] == $base_entity_table) {
      $relationships[] = $relationship;
    }
  }

  // Now we have to go through our where clauses and modify any of our fields.
  foreach ($query->where as &$clauses) {
    foreach ($clauses['conditions'] as &$where_info) {
      // Build a matrix of our possible relationships against fields we need
      // to switch.
      foreach ($relationships as $relationship) {
        foreach ($revisionable_fields as $field) {
          if (is_string($where_info['field']) && $where_info['field'] == "{$relationship}.{$field}") {
            $alias = $this->ensureRevisionTable($entity_type, $query, $relationship);
            if ($alias) {
              // Change the base table to use the revision table instead.
              $where_info['field'] = "{$alias}.{$field}";
            }
          }
        }
      }
    }
  }
}

```

The code is also wrong because it replaces an instance of a class that extends `QueryPluginBase` with an instance of a class that implements `StatementInterface`. Those objects aren't interchangeable, as their methods and properties are different.
> 0 votes

---
Tags: 8, views
---


# Question
Title: Is there any way to show a "computed field" value in edit mode?
I have a content type (*Structure*) to which I added a text computed field.

In a module I added the following code.

```
function computed_field_field_reference_compute($entity_type_manager, $node, $fields, $delta) {
  return GedProjectUtils::computeReference($node);
}

```

It works: In view mode, the field is well calculated.

Now I would like to show this field on edit mode. I added this code to the module.

```
function gedproject_form_alter(&$form, &$form_state, $form_id) {
  if ($form_id == 'node_structure_edit_form') {
    $form['field_reference']['widget'][0]['value']['#type']='textfield';
    $form['field_reference']['widget'][0]['value']['#title']='Référence';
    $form['field_reference']['widget'][0]['value']['#attributes']['readonly'] = 'readonly';
  }
}

```

The field is shown empty.

>

What do I need to change in the code I added?

# Answer
So the solution found is : I catched the node id from *$form\_state* and then catched the field value from the node like this :

```
 /**
 * Implements hook_form_alter
 *
 */
function gedproject_form_alter(&$form, &$form_state, $form_id) {
  if ($form_id == 'node_structure_edit_form') {
    $form_object = $form_state->getFormObject();
    if ($form_object instanceof EntityForm) {
      $entity = $form_object->getEntity();
      $value = $entity->get('field_reference')->value;
      $form['field_reference']['widget'][0]['value']['#value'] = $value;
      $form['field_reference']['widget'][0]['value']['#type']='textfield';
      $form['field_reference']['widget'][0]['value']['#title']='Référence';
      $form['field_reference']['widget'][0]['value']['#attributes']['readonly'] = 'readonly';
    }
  }
}

```
> 2 votes

---
Tags: forms, 9
---


# Question
Title: JSON:API error unprocessable entity status 422 entity_id this value should not be null
I'm trying to post a flag with JSON:API but I'm getting the following error:

```
"errors": [
    {
        "title": "Unprocessable Entity",
        "status": "422",
        "detail": "entity_id: This value should not be null.",
        "source": {
            "pointer": "/data/attributes/entity_id"
        }
    }

```

I'm very confused because in the data I'm posting, the `entity_id` is definitely included...

# Answer
Check if you have the JSON:API Extras module enabled.

If you do, you or someone else may have disabled the field for the resource you are trying to POST to at `/admin/config/services/jsonapi/resource_types`.

In this case, I had disabled the `entity_id` field for my flag resource, causing the "null" error even when I posted a value with axios.
> 0 votes

---
Tags: 8
---


# Question
Title: How to add template suggestions for form elements of a certain form ID?
I'm looking to edit some Twig templates for certain form elements, but I need template suggestions only for certain form IDs. How do I go about that?

I currently have the following functions in my .theme file:

```
function exmaple_form_alter(&$form, $form_state, $form_id) {
  if ($form_id == 'user_register_form') {
    // Something here to alter suggestions of form elements only for the form with the ID 'user_register_form'
  }
}

function example_theme_suggestions_form_element_alter(array &$suggestions, array $variables, $hook) {
  if (isset($variables['element']['#id'])) {
    $suggestions[] = $hook . '__' . str_replace('-','_', $variables['element']['#id']);
    // This works but returns global form element suggestions
    // For example 'form-element--edit-mail.html.twig' which affects all email edit form elements
    // I need suggestions like 'form-element--user-register-form--edit-mail.html.twig'
  }
}

```

# Answer
This is kind of tricky as you'd have to write a lot of custom code to pass the form id to a form element as you stated above. Fortunately, there is a contrib module called "Themable forms" which does exactly that, both for form elements and form element labels.

So for example, if I am on the user login page, for the form elements, I see these default theme hooks for all individual form fields.

```
<!-- THEME HOOK: 'form_element' -->
<!-- BEGIN OUTPUT from 'themes/custom/mytheme/templates/form/form-element.html.twig' -->

<!-- THEME HOOK: 'form_element_label' -->
<!-- BEGIN OUTPUT from 'themes/custom/mytheme/templates/form/form-element-label.html.twig' -->

```

Note, there is no granularity at all for the above.

Now, after I enable *Themable forms*, I start to see very granular theme hook suggestions being output in the form of

```
  form-element--[form-id]--[element-type].html.twig
  form-element--form-id--[form-id].html.twig
  form-element--type--[element-type].html.twig
  form-element.html.twig

```

So for example, with the username field, I now see

```
<!-- THEME HOOK: 'form_element' -->
<!-- FILE NAME SUGGESTIONS:
   * form-element--user-login-form--textfield.html.twig
   * form-element--form-id--user-login-form.html.twig
   * form-element--type--textfield.html.twig
   x form-element.html.twig
-->

<!-- THEME HOOK: 'form_element_label' -->
<!-- FILE NAME SUGGESTIONS:
   * form-element-label--user-login-form--textfield.html.twig
   * form-element-label--form-id--user-login-form.html.twig
   * form-element-label--type--textfield.html.twig
   x form-element-label.html.twig
-->

```

You'll now note a few things, the form id in the form element suggestion as well as the name of the form element itself.

This should get you where you want to be according to your question above.

You can install the module with:

```
composer require drupal/themable_forms

```
> 5 votes

---
Tags: 8, theming, forms, hooks
---


title: Views rest export does not update

text:
# Question
Title: Views rest export does not update
I have two websites.

On *website 1* I have a calendar showing upcoming events. Next to a regular views page I also created a views rest export page with 6, of the most near in the future, upcoming events.

On *website 2* I created a rest import function with JavaScript. Getting the 6 events and showing them in a block.

Now my problem is that, when a day is passed and an event has taken place, the event is not removed from the block in *website 2*. I'm not caching anything on *website 2* and it is just importing the rest export every time the page loads. And I turned the cache of the view on *website 1* off.

It looks like the problem is with *website 1* (because emptying the cache on *website 1* fixes the problem on *website 2* for some reason it is not updating the rest export when an event has passed.

The updating of the rest export on *website 1* should take place because the filter of the view is set to only show the upcoming events.

I find it hard to explain what the problem really is hopefully my explanation makes sense (at least a little bit).

So what I'm wondering if this is a normal behavior? Did I miss a setting?

# Answer
> 1 votes
These things would be easy to solve if you could set a cache expiration time for anonymous requests in UI, but you can't unfortunately. See Page cache not rebuilding after page cache maximum age is reached

In this case you could add an additional query parameter containing the current date when website 2 is requesting content from website 1 with javascript. This would create a new cache entry on every date change in the page cache of website 1, even if the query parameter is never used. You need to disable caching of the View itself so that it doesn't get cached in other places.

---
Tags: 8, views
---


# Question
Title: Success with cookie, fails with JWT: RuntimeException: Failed to start the session because headers have already been sent
My Controller is working with cookie auth but failing with JWT. This Controller is supposed to flag an entity for the logged-in user.

If I am using cookie auth, there are no errors and everything works as expected.

But when I try to use JWT, **although the entity does get flagged correctly, I get the following error in the Drupal logs:**

> RuntimeException: Failed to start the session because headers have already been sent by "/app/vendor/symfony/http-foundation/Response.php" at line 377. in Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage-\>start() (line 150 of /app/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php)

How do I fix this error?

Here's how I'm using JWT auth in Postman:

```
POST http://example.com/api/group_add?_format=json

```

Headers:

* Accept: application/vnd.api+json
* Content-Type: application/vnd.api+json
* Cache: no-cache
* Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MTgyMDk4MjAsImV4cCI6MTYyMzM5MzgyMCwiZHJ1cGFsIjp7InVpZCI6IjI3In19.5uDJMtokLXD6K63H5Ikb-F870EYFMrgE4mItTuTT3bI

Request body:

```
{
    "entity_id": "14"
}

```

As for the Controller, here's **MYMODULE.routing.yml**:

```
MYMOUDLE.api_flagging.http:
  path: '/api/group_add'
  defaults:
    _controller: '\Drupal\MYMODULE\Controller\ApiFlagging::flag'
  methods: [POST]
  requirements:
    _permission: 'view own commerce_order'
    _format: 'json'
  options:
    no_cache: 'TRUE'

```

Here's **ApiFlagging.php**:

```
<?php

namespace Drupal\MYMODULE\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\flag\FlagServiceInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Serializer;

/**
 * Class ApiFlagging.
 *
 * Https://www.drupal.org/project/flag/issues/3091824#comment-13336379
 */
class ApiFlagging extends ControllerBase {

  const FLAG_ID = 'ABC';

  /**
   * The flag service.
   *
   * @var \Drupal\flag\FlagServiceInterface
   */
  protected $flagService;

  /**
   * The serializer.
   *
   * @var \Symfony\Component\Serializer\Serializer
   */
  protected $serializer;

  /**
   * The available serialization formats.
   *
   * @var array
   */
  protected $serializerFormats = [];

  /**
   * Constructs a new ApiFlagging object.
   */
  public function __construct(Serializer $serializer, array $serializer_formats, FlagServiceInterface $flag) {
    $this->serializer = $serializer;
    $this->serializerFormats = $serializer_formats;
    $this->flagService = $flag;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    if ($container->hasParameter('serializer.formats') && $container->has('serializer')) {
      $serializer = $container->get('serializer');
      $formats = $container->getParameter('serializer.formats');
    }
    else {
      $formats = ['json'];
      $encoders = [new JsonEncoder()];
      $serializer = new Serializer([], $encoders);
    }

    return new static(
      $serializer,
      $formats,
      $container->get('flag')
    );
  }

  /**
   * Flagging.
   */
  public function flag(Request $request) {
    $format = $this->getRequestFormat($request);

    $content = $request->getContent();
    $flagData = $this->serializer->decode($content, $format);
    $flag = $this->flagService->getFlagById(self::FLAG_ID);
    $flaggableEntityTypeId = $flag->getFlaggableEntityTypeId();

    $my_goals = NULL;
    if (array_key_exists('goals', $flagData)) {
      $my_goals = $flagData['goals'];
    }

    $entity = \Drupal::entityTypeManager()
      ->getStorage($flaggableEntityTypeId)
      ->load($flagData['entity_id']);

    if ($my_goals === NULL) {
      return new JsonResponse([
        'error_message' => 'Goals not set.',
      ], 400);
    }

    try {
      /** @var \Drupal\flag\Entity\Flagging $flagging */
      $flag->set('field_goals', $my_goals);
      $flagging = $this->flagService->flag($flag, $entity);
    }
    catch (\LogicException $e) {
      $message = $e->getMessage();
      return new JsonResponse([
        'error_message' => $message,
      ], 400);
    }

    return new JsonResponse([
      'message' => 'flag success',
      'flagging_uuid' => $flagging->uuid(),
      'flagging_id' => $flagging->id(),
      'flag_id' => $flagging->getFlagId(),
    ]);
  }

  /**
   * Gets the format of the current request.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request.
   *
   * @return string
   *   The format of the request.
   */
  protected function getRequestFormat(Request $request) {
    $format = $request->getRequestFormat();
    if (!in_array($format, $this->serializerFormats)) {
      throw new BadRequestHttpException("Unrecognized format: $format.");
    }
    return $format;
  }

}

```

More info:

Here's the rest of the error:

```
#0 /app/web/core/lib/Drupal/Core/Session/SessionManager.php(164): Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage->start()
#1 /app/web/core/lib/Drupal/Core/Session/SessionManager.php(195): Drupal\Core\Session\SessionManager->startNow()
#2 /app/vendor/symfony/http-foundation/Session/Session.php(196): Drupal\Core\Session\SessionManager->save()
#3 /app/web/core/lib/Drupal/Core/StackMiddleware/Session.php(60): Symfony\Component\HttpFoundation\Session\Session->save()
#4 /app/web/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(47): Drupal\Core\StackMiddleware\Session->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#5 /app/web/core/modules/page_cache/src/StackMiddleware/PageCache.php(106): Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#6 /app/web/core/modules/page_cache/src/StackMiddleware/PageCache.php(85): Drupal\page_cache\StackMiddleware\PageCache->pass(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#7 /app/vendor/asm89/stack-cors/src/Asm89/Stack/Cors.php(49): Drupal\page_cache\StackMiddleware\PageCache->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#8 /app/web/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(47): Asm89\Stack\Cors->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#9 /app/web/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(52): Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#10 /app/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#11 /app/web/core/lib/Drupal/Core/DrupalKernel.php(708): Stack\StackedHttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#12 /app/web/index.php(19): Drupal\Core\DrupalKernel->handle(Object(Symfony\Component\HttpFoundation\Request))
#13 {main}
.

```

When I test using my frontend app, I'm also getting the following error in the browser console:

```
Access to XMLHttpRequest at 'http://example.com/api/group_add' from origin 'http://localhost:8100' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

```

When I check the browser dev tools Network tab, I see that the preflight request to `group_add` has an `Access-Control-Allow-Origin: http://localhost:8100` header, but the xhr request for `group_add` does not.

# Answer
> 0 votes
Ugh, I found the problem.

In a `hook_flagging_insert()`, I was redirecting users upon node creation:

```
$response = new RedirectResponse($redirect_url);
$response->send();

```

Removing the redirect eliminates the problem with jwt.

---
Tags: flags, authentication-authorization, 9
---


# Question
Title: I'm attempting to render responsive images and control art direction. Could somebody see whyI can't get breakpoints.yml to be respected?
I'm creating a title/ hero-image section that consists of two 50/50 columns at desktop but then stacks at about tablet (e.g. 1024px)

I’m using a custom twig template for this content type.

I want serve WebP images if the users browser will tolerate that, I also want to serve responsive derivatives and control art direction (i.e. different dimensions and cropping depending on screen width).

To that end I’m trying to use the WebP module together with the Responsive Image module and image styles.

I’ve created two responsive image styles *(PMP Hero Col One & PMP Hero Col Two)* and six image styles *(PMP Hero C1 Desktop, PMP Hero C1 Tablet, PMP Hero C1 Phone, PMP Hero C2 Desktop, PMP Hero C2 Tablet, PMP Hero C2 Phone)* each with a scale and crop effect.

I’ve created a breakpoints.yml In my theme’s root directory and I can see its breakpoints within the Responsive Image configuration interface.

```
my_theme.pmp_hero_col_1.phone:
  label: pmp_hero_col_1_phone
  mediaquery: '(max-width: 900px)'
  weight: 0
  multipliers:
    - 1x
  group: neiu_main.pmp_hero_col_1
my_theme.pmp_hero_col_1.tablet:
  label: pmp_hero_col_1_tablet
  mediaquery: '(max-width: 1024px)'
  weight: 1
  multipliers:
    - 1x
  group: neiu_main.pmp_hero_col_1
my_theme.pmp_hero_col_1.desktop:
  label: pmp_hero_col_1_desktop
  mediaquery: '(min-width: 1025px)'
  weight: 2
  multipliers:
    - 1x
  group: neiu_main.pmp_hero_col_1
my_theme.pmp_hero_col_2.phone:
  label: pmp_hero_col_2_phone
  mediaquery: '(max-width: 900px)'
  weight: 0
  multipliers:
    - 1x
  group: neiu_main.pmp_hero_col_2
my_theme.pmp_hero_col_2.tablet:
  label: pmp_hero_col_2_tablet
  mediaquery: '(max-width: 1024px)'
  weight: 1
  multipliers:
    - 1x
  group: neiu_main.pmp_hero_col_2
my_theme.pmp_hero_col_2.desktop:
  label: pmp_hero_col_2_desktop
  mediaquery: '(min-width: 1025px)'
  weight: 2
  multipliers:
    - 1x
  group: neiu_main.pmp_hero_col_2

```

I’ve selected the appropriate breakpoint group in each of my responsive image styles. Within each responsive image style I’ve selected “Select a single image style.” for each breakpoint and associated the image style I want with it.

I’ve tried setting the fallback image style to “None (original image)” & “empty image”.

I’ve tried rendering the responsive image style in my template like:

```
{{ drupal_image(node.field_pmp_hero_image.entity.uri.value, 'pmp_hero_col_two', responsive=true) }}

```

and like:

```
{% set heroCol2ImagePath = node.field_pmp_hero_image.entity.uri.value %}

{%
    set heroCol2ResponsiveImageStyle = {
        '#theme': 'responsive_image',
        '#responsive_image_style_id': 'pmp_hero_col_two',
        '#uri': heroCol2ImagePath,
        '#attributes': { class: 'img-responsive', alt: 'MBA Students' },
    }
%}

{{ heroCol2ResponsiveImageStyle }}

```

In both cases I do get the picture tag and the appropriate srcsets:

Also the derivatives are written to the file system in the appropriate directories and are available.

The problem I'm experiencing is that the breakpoints.yml is never respected i.e. the image never changes to the appropriate derivative. It's always either the default or empty if the fallback is set to empty.

This makes me think there's something wrong with my breakpoints.yml e.g. syntax, media queries, etc. So, I've tried about a zillion permutations without success.

I am able to get this working if instead of using the Responsive Image module at all, I manually do:

```
<picture>
    <source media="(max-width:900px)" srcset="{{ heroCol2ImagePath|image_style('pmp_hero_c2_phone') }}">
    <source media="(max-width:1024px)" srcset="{{ heroCol2ImagePath|image_style('pmp_hero_c2_tablet') }}">
    <source media="(min-width:1025px)" srcset="{{ heroCol2ImagePath|image_style('pmp_hero_c2_desktop') }}">
    <img class="pmp-vtwo-hero-image" src="{{ file_url(heroCol2ImagePath) }}" alt="Flowers" style="width:auto;">
</picture>

```

But, I don't know how to then use the WebP module with this approach.

If somebody out there is able to point out what bone-head mistake I'm making, you'd forever have my gratitude.

# Answer
It looks like you have a typo in your breakpoints.yml. See this the breakpoints.yml documentation.

An example as defined in bartik theme:

```
bartik.narrow:
label: narrow
mediaQuery: 'all and (min-width: 560px) and (max-width: 850px)'
weight: 1
multipliers:
  - 1x

```

The correct key is `mediaQuery`, not `mediaquery`. The result is your media queries aren't recognized so you only get default images on all devices. Update that and you'll be all set.
> 1 votes

---
Tags: media
---


# Question
Title: Pass a webform value to a node via Rules?
I have a rule that is successfully creating a node of a content type when a webform is submitted. How do I pass a value from the webform into the created node via rules? (This is a Drupal 7 site.)

# Answer
1. Create Hidden inputs in webform.
2. Assign to this hidden inputs values (prepopulated in url, tokens, webform rules or programmatically).
3. Assign these inputs to content fields using Rules.
> 0 votes

---
Tags: webforms, rules
---


# Question
Title: How do I get the image path inside a jsonapi response?
I a content type for movies that includes plain text fields, and an image. The field name for the image is field\_movie\_poster.

Using the JSON API, How do I include the image path in the request?

If I put this Postman I get the correct response:

```
https://drupal-api.ddev.site/jsonapi/node/movies?include=image_field

```

But the same does not work with JavaScript

```
getMovies() {
    fetch('https://drupal-api.ddev.site/jsonapi/node/movies',{
        include: 'image_field'
    })
    .then(res => {
        return res.json()
    })
    .then(res => {

        this.movies.push(...res.data)
        console.log(res.data)

    })
    .catch(err => console.log(err))
}

```

How do I include the image path in the request?

If you look at the image attached, you will see that field\_movie\_poster is under the relationships attribute, and it does not include the image path.

# Answer
For entity reference/images fields you have to include them to have their "full" data:

```
https://drupal-api.ddev.site/jsonapi/node/movies?include=image_field

```

The response will contain an object called `included` where you'll find that data,

Details for a image field:

```
{
  "type": "file--file",
  "id": "cc6ef625-8549-49c3-bc1c-0ef41551d8f7",
  "links": {
    "describedby": {
      "href": "drupal-api.ddev.site/jsonapi/file/file/resource/schema"
    },
    "self": {
      "href": "drupal-api.ddev.site/jsonapi/file/file/cc6ef625-8549-49c3-bc1c-0ef41551d8f7"
    }
  },
  "attributes": {
    "drupal_internal__fid": 29282,
    "langcode": "it",
    "filename": "1920x1080.jpg",
    "uri": {
      "value": "public://images/articles/2021-04/1920x1080.jpg",
      "url": "/sites/default/files/images/articles/2021-04/1920x1080.jpg"
    },
    "filemime": "image/jpeg",
    "filesize": 40767,
    "status": true,
    "created": "2021-04-13T10:22:44+00:00",
    "changed": "2021-04-13T10:22:52+00:00"
  },
  "relationships": {
    "uid": {
      "data": {
        "type": "user--user",
        "id": "c178495f-8a4c-410c-8e83-ebac79bc2162"
      },
      "links": {
        "related": {
          "href": "drupal-api.ddev.site/jsonapi/file/file/cc6ef625-8549-49c3-bc1c-0ef41551d8f7/uid"
        },
        "self": {
          "href": "drupal-api.ddev.site/jsonapi/file/file/cc6ef625-8549-49c3-bc1c-0ef41551d8f7/relationships/uid"
        }
      }
    }
  }
}

```

Please note you'll have to cross-check the id of that file with the id value of the `image_field` to get the correct file\url for each different image.
> 2 votes

---
Tags: javascript
---


title: Remove separator from breadcrumb

text:
# Question
Title: Remove separator from breadcrumb
How can I remove the `<` from the breadcrumb I tried `implode('<', $value['data'])` but no luck

```
   function bootstrap_fit_breadcrumb($variables) {
            $breadcrumb = $variables['breadcrumb'];

            if (!empty($breadcrumb)) {
              //$breadcrumb[] = drupal_get_title();
              $breadcrumbs = '<ul class="breadcrumbs_list">';

              $count = count($breadcrumb) - 1;

              foreach ($breadcrumb as $key => $value) {
                  if ($key==0) {
                    $breadcrumbs .= '<li>' . $value . '</li>';
                  } else {
                   $breadcrumbs .= '<li><span></span>' . $value['data'] . '</li>';

                  }
              }
              $breadcrumbs .= '</ul>';
              return $breadcrumbs;
      }
   }

```

**displaying**

```
Home < Financial Services

```

# Answer
Have you reviewed this page?

https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme\_breadcrumb/7

Also, what theme are you using? I would check the base theme to make sure that it isn't preprocessing the breadcrumbs itself. If so, you'll need to make sure you are calling the appropriate function in order to override the breadcrumb settings.
> 0 votes

# Answer
Super simple solution: use `str_replace`. In page.tpl.php replace

```
print $breadcrumb;

```

with

```
print str_replace(" < ", "  ", $breadcrumb);

```

or

```
print str_replace(" < ", " > ", $breadcrumb);

```

**NOTE**: I assume that '`<`' is surrounded by specific pattern otherwise `str_replace` will remove all opening parenthesis of html tags in $breadcrumb variable.
> 0 votes

# Answer
**Drupal 8:** I found a *breadcrumb.html.twig* file in my theme. The separator was coded into the theme (Businessgroup Zymphonies Theme). After removing it from the template, you can add a custom separator using CSS. Something like this:

```
.breadcrumb li:not(:last-child):after {
   content: ">"
}

```
> 0 votes

---
Tags: theming
---


# Question
Title: How to alter file upload description text
I'm trying to customize text for a file upload widget in Drupal 8.

Here is a screenshot of the widget:

I just want to display the first `15 MB` which I added as file field description text in the content type.

The trouble is the last 3 lines of text have no HTML tags so I can't selectively hide it with `display:none;`. Here is what I mean:

I tried this CSS but it doesn't work:

```
#edit-field-dataset-file-0--description * {
    display:none;
}

#edit-field-dataset-file-0--description p {
    display:inline !important;
}

```

Does anyone have any suggestions?

# Answer
> 1 votes
I used `hook_field_widget_form_alter` as suggested by DannyEnglander and leymannx.

8.8.x hook\_field\_widget\_form\_alter

Replace `MYMODULE` with your custom module's name and alter the element array to suit your needs.

```
/**
 * Implements hook_field_widget_form_alter().
 */
function MYMODULE_field_widget_form_alter(&$element, \Drupal\Core\Form\FormStateInterface $form_state, $context) {
  if (get_class($context['widget']) === 'Drupal\file\Plugin\Field\FieldWidget\FileWidget' && !empty($context['form']['#attributes'])) {
    $element['#description'] = '<p>File must be less than <strong>15 MB</strong>.<br/>Allowed types: <code>txt csv tsv xls xlsx sav sas7bdat</code></p>';
  }
}

```

# Answer
> 1 votes
You can add a preprocess function like this:

```
function theme_preprocess_file_upload_help(array &$variables) {
  $description = $variables['descriptions']; // It's where the three objects are, you can manipulate the description.
}

```

Or if you want just to add some wrappers around each element, modify the template `file-upload-help.html.twig`. Currently it looks like this:

```
{#
/**
 * @file
 * Theme override to display help text for file fields.
 *
 * Available variables:
 * - descriptions: Lines of help text for uploading a file.
 *
 * @see template_preprocess_file_upload_help()
 */
#}
{{ descriptions|safe_join('<br />') }}

```

Instead you can do:

```
{% for item in descriptions %}
  <div class="help-text-item">
    {{ item }}
  </div>
{% endfor %}

```

# Answer
> 1 votes
If altering the file field widget description in hook\_field\_widget\_form\_alter() doesn't work.

You can try to add a process function to change the description like it is mentioned by gnuget in this Drupal issue :

```
/**
 * Implements hook_field_widget_form_alter().
 */
function HOOK_field_widget_form_alter(&$element, FormStateInterface &$form_state, $context) {
  if (isset($element['#field_name']) && $element['#field_name'] == 'field_media_image') {
      // Add process callback to change field description.
      $element['#process'][] = '_change_description';
    }
  }
}

/**
 *  The description field is build in a #process callback so we need to add another callback.
 */
function _change_description($element, FormStateInterface $form_state, $form) {
  $element['#description'] = t('Your new description');
  return $element;
}

```

---
Tags: 8, theming, files
---


# Question
Title: Programmatically add a translation with proper content moderation state
I am trying to add a node translation programmatically when a node is created (using hook\_ENTITY\_TYPE\_insert). My code works fine except that the translation doesn't really have a content moderation state. My code is the following.

```
function mymodule_add_translation($node) {
  // get the current language, and determine the translation language
  $lang = \Drupal::languageManager()->getCurrentLanguage()->getId();
  $langcode = ($lang == 'en') ? 'fr' : $lang;
  $node->setRevisionTranslationAffected(FALSE);
  $node->addTranslation($langcode, ['title' => $node->getTitle()])->save();
}

```

This is what the content\_moderation\_state\_field\_revision table looks like for two nodes; the first one was manually translated and the second one was translated using the above code.

>

I am clearly doing something incorrect. Any suggestion would be really appreciated.

# Answer
What is the problem you're seeing with the moderation state table? What I think I'm seeing is:

1. Revision 15 is the initial save of the node.
2. Revision 16 is the second save, where fr has changes and en does not.

I can't see anything too unusual here, but if you are running into errors later in the lifecycle of the node, that would be helpful information.

One red flag I can see in the code is manually calling `setRevisionTranslationAffected` and `addTranslation`. Many of the Content Moderation tests use the following methods to add translations and to create revisions, because that's how additional bits of metadata like the `revision_translation_affected` column are set:

```
$storage = \Drupal::entityTypeManager()->getStorage('node');
// You may try either of these two methods for creating a revision or a translation.
$storage->createRevision($node);
$storage->createTranslation($node, 'fr', $node->toArray());

```
> 0 votes

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


title: Delete Query Syntax

text:
# Question
Title: Delete Query Syntax
I am trying to write queries in drupal 8 , I know that I must use the delete() function , however just for testing on my draft I tried the following query and it worked :

```
$connection = \Drupal::database();
$sql = "DELETE FROM role_expire WHERE uid = :uid AND rid = :rid ";
$query_ = $connection->query($sql, array(":uid" => $drupal_user_id ,":rid" => $role_id));

```

My question is should I ADD a execute() at the end like then following example that I found while searching and if yes, then how did my query manage to work without it ? :

```
 $database = \Drupal::database();
 $query = $database->query("DELETE FROM table WHERE name='jane' AND nickmane='" . $nickname . "'");
 $query->execute();

```

same question for the INSERT query.

# Answer
You do not need $query-\>execute.

Drupal gives this example. As you have already mentioned there is recommendation to not use query for simple INSERT, UPDATE or DELETE queries.

And you can see in Connection::query that the query is executed.
> 0 votes

---
Tags: 8
---