# Question
Title: How can I update the block content programmatically?
I have created a block plugin.

`/src/Plugin/Block/Myblock`

```
/**
 * Provides a 'Download component' block.
 *
 * @Block(
 *   id = "my_custom_block",
 *   admin_label = @Translation("My custom block"),
 * )
 */
class Myblock extends BlockBase {
  // Other code
}

```

The above block has a settings form also where I am uploading some files to the block.

I placed this block in two different regions on a page with different settings. (Uploaded different files)

Now I want to update the content of one of them i-e `add a new file` programmatically.

How will I do that? Because I can't load the block and update the field and save the block again because both returns me the same id `my_custom_block`.

```
$this->entityTypeManager->getStorage('block_content')->load('my_custom_block'));

```

And also this returns me null.

Any idea how will I do that? Just like we update the nod entity, I want to update an instance of this block plugin.

**Updated code:**

I get the block like this

```
$node = \Drupal::routeMatch()->getParameter('node');
 if ($node instanceof \Drupal\node\NodeInterface) {
    /** @var \Drupal\layout_builder\Field\LayoutSectionItemList $layout */
    $layout = $node->get('layout_builder__layout');
    $block = $layout->getSection(0)->getComponent('1bb8e7b4-8253-40bc-a1f9-7f02e90edb2f')->getPlugin();
    $block->setConfigurationValue('file', ['6338, 6336']);
 }

```

Now how will I save the node using the block new configuration values?

I tried this.

```
 $node->get('layout_builder__layout')->getSection(0)->getComponent('1bb8e7b4-8253-40bc-a1f9-7f02e90edb2f')->setConfiguration($block->getConfiguration());
 $node->save();

```

# Answer
> 1 votes
Placed blocks are stored in the config entity `block`. The IDs are more or less unpredictable but you can load the blocks by specifying properties:

```
$entity_type_manager = \Drupal::entityTypeManager();
$blocks = $entity_type_manager->getStorage('block')->loadByProperties([
  'plugin' => 'my_custom_block',
  'region' => $region,
  'theme'  => $theme,
]);

```

A code example to load all blocks placed for the custom block plugin, do some checks and modify the settings:

```
$entity_type_manager = \Drupal::entityTypeManager();
$blocks = $entity_type_manager->getStorage('block')->loadByProperties([
  'plugin' => 'my_custom_block',
]);

foreach ($blocks as $block) {
  if ($block->getRegion() == 'my_region') {
    $settings = $block->get('settings');
    // modify settings
    $block->set('settings', $settings);
    $block->save();
  }
}

```

---
Tags: 8, blocks
---


# Question
Title: Get all users with specific roles using EntityFieldQuery
I thought this was an easy task, yet there does not seem to be a Drupal-method for this. I came as far as knowing I have to use `EntityFieldQuery` for this - because the API said conditions for `user_load_multiple()` are deprecated.

So I tried this:

```
  $query = new EntityFieldQuery;
  $query
    ->entityCondition('entity_type', 'user')
    ->propertyCondition('rid',array(1,2,3);

  $result = $query->execute();

```

Yet I got this:

> PDOException: SQLSTATE\[42S22\]: Column not found: 1054 Unknown column 'users.rid' in 'where clause': SELECT users.uid AS entity\_id, :entity\_type AS entity\_type, NULL AS revision\_id, :bundle AS bundle FROM {users} users WHERE (users.rid = :db\_condition\_placeholder\_0) ; Array ( \[:db\_condition\_placeholder\_0\] =\> 3 \[:entity\_type\] =\> user \[:bundle\] =\> user ) in EntityFieldQuery-\>execute()

So my first thought was, that I'd have to join with `users_roles`-Table and so on, but that *will* lead to duplicates.

Does anyone have an idea how to do it?

# Answer
To be honest, I have no idea on how to achieve this. Good examples of how to use the EntityFieldQuery are hard to find. Since noone has answered this question yet and I'm also interested in the solution I'll try to help you out. Described below is my best guess.

A thing to keep in mind: roles are stored in a different table than users. They are added using UserController::attachLoad.

There seem to be three different conditions that you can use with an EntityFieldQuery:

1. entityCondition (Entity specific conditions:'entity\_type', 'bundle', 'revision\_id' or 'entity\_id')
2. fieldCondition (Conditions on fields maintained by the Field API)
3. propertyCondition (Conditions on entity properties)

The most logical option (if there is one) would be to use a propertyCondition, but as roles are added to the user in UserController::attachLoad I don't think the EntityFieldQuery has access to it. I think the EntityFieldQuery just uses the schema defined in hook\_schema().

This leads me to believe that what you're trying to achieve is impossible. A workaround would be to get all the uids with a normal query:

*I don't have access to Drupal right now, so the code below might be off. I'm sure somebody will edit the mistakes.*

```
// Use $query for readability
$query = 'SELECT DISTINCT(ur.uid)
  FROM {users_roles} AS ur
  WHERE ur.rid IN (:rids)';
$result = db_query($query, array(':rids' => array(1,2,3)));

$uids = $result->fetchCol();

$users = user_load_multiple($uids);

```

If it is possible to achieve what you want with EntityFieldQuery, I'll be enlightened.
> 32 votes

# Answer
It should do the trick

```
  $query = new EntityFieldQuery;
  $query
    ->entityCondition('entity_type', 'user')
    ->addTag('role_filter');
  $results = $query->execute();

/**
* Implement hook_query_TAG_alter
*
* @param QueryAlterableInterface $query
*/
function MY_MODULE_query_role_filter_alter(QueryAlterableInterface $query) {
  $query->leftJoin('users_roles', 'r', 'users.uid = r.uid');
  $and = db_and()
            ->condition('r.rid', MY_ROLE_INT, '=');
  $query
    ->condition($and);
}

```
> 19 votes

# Answer
There is in fact a way to do this. In its heart, the EntityFieldQuery (EFQ) is just a database query which can be altered with query alter hooks.

Simplest possible example:

```
function mymodule_get_users_by_rolename($rolename){
  $query = new EntityFieldQuery;
  $query->entityCondition('entity_type', 'user');
  $query->addTag('rolequery');
  $query->addMetaData('rolename', $rolename);

  return $query->execute();
}

function mymodule_query_rolequery_alter(QueryAlterableInterface $query) {
  $rolename = $query->getMetaData('rolename');

  $role_subquery = db_select("role", "role");
  $role_subquery->condition('role.name', $rolename, '=');
  $role_subquery->join('users_roles', "users_to_include", "role.rid = users_to_include.rid");
  $role_subquery->fields('users_to_include', array('uid' => 'uid'));
  $role_subquery->where('users_to_include.uid = users.uid');
  $query->exists($role_subquery);
}

```

There is however a few small caveats with this that would require some more coding. For example in the care of the only condition present in the EFQ being a fieldCondition, the users-basetable won't be present, so when you're fetching users by role and a single fieldCondition, you'd also need to make sure the users table is joined and if not, manually join it, which is a bit of a tedious process.

But for your need, this will do the trick. Realizing you can manually alter EFQ queries opens a huge world of opportunities for creating very powerful queries while still keeping the interface clean and Drupal.

Let me know if you have any questions or issues.

Edit: I actually found a little more performant way of doing this and changed my code accordingly.
> 16 votes

# Answer
```
$user_list = array();
$roles = array('role_1', 'role_2');
$query = db_select('users_roles', 'ur');
$query->join('users', 'u', 'u.uid = ur.uid');
$query->join('role', 'r', 'r.rid = ur.rid');
$query->fields('u',array('uid'));
$query->fields('u',array('mail'));
$query->fields('u',array('name'));

$query->condition('r.name', $roles, 'IN');
$result = $query->execute();

if($result){
    foreach($result as $row ) {
        $uid = $row->uid;
        $user_list[$uid]['mail'] = $row->mail;
        $user_list[$uid]['name'] = $row->name;
        $user_list[$uid]['uid'] = $uid;
    }
}

return $user_list;

```
> 6 votes

# Answer
You can still use EntityFieldQuery() once you get the user ids for your role, if you want to, for example because you want to user fieldCondition() or another method.

Using the $uids array in the above example:

```
$role = user_role_load_by_name('my_role_name');
$result = db_select('users_roles', 'ur')
->fields('ur', array('uid'))
->condition('rid', $role->rid)
->execute();

foreach($result as $record) {
  $uids[] = $record->uid;
}

$query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'user')
->entityCondition('entity_id', $uids, 'IN')
->execute();

```

It would be nicer if EntityFieldQuery had a join method.
> 5 votes

# Answer
```

/**
 * Get users by specific role
 */
function mymodule_get_users_by_role() {
    $role = user_role_load_by_name('rolename');
    $uids = db_select('users_roles', 'ur')
        ->fields('ur', array('uid'))
        ->condition('ur.rid', $role->rid, '=')
        ->execute()
        ->fetchCol();
    $users = user_load_multiple($uids);
}

```
> 5 votes

# Answer
OK This is an old one, but you could do something like this:

```
$my_role_id = [3,4,5];

$query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'user');

$roles_subquery = db_select('users_roles', 'ur');
$roles_subquery->fields('ur', array('uid'));
$roles_subquery->condition('rid', $my_role_id);

$query->propertyCondition('uid', $roles_subquery, 'IN');

$result = $query->execute();

```
> 5 votes

# Answer
This is a little late to the game, but I think it's what the OP was asking for. e.g. no sql, all drupal. Hopefully others will find it useful. The search for this led me here and this is what I came up with.

```
    /**
     * Users with role
     *
     * @param $role mixed The name or rid of the role we're wanting users to have
     * @param $active_user boolean Only return active accounts?
     *
     * @return array An array of user objects with the role
     */
    function users_with_role($role, $active_user = TRUE) {
      $uids = array();
      $users = array();
      if (is_int($role)) {
        $my_rid = $role;
      }
      else {
        $role_obj = user_role_load_by_name($role);
      }
      $result = db_select('users_roles', 'ur')
        ->fields('ur')
        ->condition('ur.rid', $role_obj->rid, '=')
        ->execute();
      foreach ($result as $record) {
        $uids[] = $record->uid;
      };
      $query = new EntityFieldQuery();
      $query->entityCondition('entity_type', 'user')
        ->propertyCondition('uid', $uids, 'IN');
      if ($active_user) {
        $query->propertyCondition('status', 1);
      }
      $entities = $query->execute();
      if (!empty($entities)) {
        $users = entity_load('user', array_keys($entities['user']));
      }
      return $users;
    }

```
> 2 votes

# Answer
In drupal 8 you can't use entityFieldQuery, instead use entityQuery. something like the following will return a list of user with the required role ('editor' in the example).

```
$uids = \Drupal::entityQuery('user')
  ->condition('status', 1)
  ->condition('roles', 'editor')
  ->execute();

$users = User::loadMultiple($uids);
foreach($users as $user){
  $username = $user->get('name')->value;
  $uid = $user->get('uid')->value;
  $userslist[$uid] = $username;
}

```
> 1 votes

# Answer
This is indeed an older topic and I found lots of good approaches.

I would improve a bit on the solution provided by @alf, because I think one query with join is the best approach. However I also like my functions to have parameters and not depend on a constant. So here it goes:

```
function MY_MODULE_load_users_by_role($rid) {
  $query = new EntityFieldQuery;
  $query
    ->entityCondition('entity_type', 'user')
    ->addMetaData('account_role', user_role_load($rid))
    ->addTag('role_filter');
  $results = $query->execute();

  if (isset($results['user'])) {
    $uids = array_keys($results['user']);
    $users = entity_load('user', $uids);
  }
  else {
    $users = array();
  }

  return $users;
}

/**
 * Implement hook_query_TAG_alter
 *
 * @param QueryAlterableInterface $query
 */
function MY_MODULE_query_role_filter_alter(QueryAlterableInterface $query) {
  $rid = $query->alterMetaData['account_role']->rid;
  $query->leftJoin('users_roles', 'r', 'users.uid = r.uid');
  $and = db_and()
    ->condition('r.rid', $rid, '=');
  $query
    ->condition($and);
}

```
> 0 votes

# Answer
You can find a good explanations examples about how to use the EntityFieldQuery here:

but just as Berdir said as for now "EFQ only supports basic properties of an entity, which are part of the entity table. The roles of a user are not exposed in any way to the entity system."
> -1 votes

# Answer
I have no doubt this can be simplified. This can be done though for drupal 8:

```
//retrieve all "VIP" members
$query = \Drupal::entityQuery('user');
$nids = $query->execute();
foreach ($nids as $nid){
    $user = \Drupal\user\Entity\User::load($nid);
    if ($user->hasRole('VIP'))
        $emails_VIP_members[]=$user->getEmail();
}
 $send_to=implode(',',$emails_VIP_members);

```
> -2 votes

---
Tags: 7, entities
---


# Question
Title: Issues trying to understand and debug simplesamlphp error
Simplesamlphp used to work on the site. Something changed and now it does not.

* The IDP remote config has been verified, including the cert
* The SP cert and pem in the simplesamlphp/config folder have been verified
* All the required settings show green on the admin page
* When the ACS URL is entered as a test, a JSON file with the metadata is downloaded

All there seems correct. But when a user attempts to login, the following is thrown into the log file. I note that despite the entry about the candidate having :443, the URL with or without the port results in identical json.

This is Drupal 8.9.6, PHP 7.3.21, simplesamlphp\_auth 8.x-3.2, simplesamlphp 1.18.8

> simplesamlphp ERROR \[ffdf5e0102\] SimpleSAML\Error\Error: UNHANDLEDEXCEPTION
>
> simplesamlphp ERROR \[ffdf5e0102\] Backtrace:
>
> simplesamlphp ERROR \[ffdf5e0102\] 1 /mnt/www/html/mysite/vendor/simplesamlphp/simplesamlphp/www/\_include.php:17 (SimpleSAML\_exception\_handler)
>
> simplesamlphp ERROR \[ffdf5e0102\] 0 \[builtin\] (N/A)
>
> simplesamlphp ERROR \[ffdf5e0102\] Caused by: SimpleSAML\Error\Exception: \> This SP \[https://admin.mysite.gov/simplesaml/module.php/saml/sp/metadata.php/default-sp\] is not a valid audience for the assertion. Candidates were: \[https://admin.mysite.gov:443/simplesaml/module.php/saml/sp/metadata.php/default-sp\]
>
> simplesamlphp ERROR \[ffdf5e0102\] Backtrace:
>
> simplesamlphp ERROR \[ffdf5e0102\] 4 /mnt/www/html/mysite/vendor/simplesamlphp/simplesamlphp/modules/saml/lib/Message.php:718 (SimpleSAML\Module\saml\Message::processAssertion)
>
> simplesamlphp ERROR \[ffdf5e0102\] 3 /mnt/www/html/mysite/vendor/simplesamlphp/simplesamlphp/modules/saml/lib/Message.php:640 (SimpleSAML\Module\saml\Message::processResponse)
>
> simplesamlphp ERROR \[ffdf5e0102\] 2 /mnt/www/html/mysite/vendor/simplesamlphp/simplesamlphp/modules/saml/www/sp/saml2-acs.php:141 (require)
>
> simplesamlphp ERROR \[ffdf5e0102\] 1 /mnt/www/html/mysite/vendor/simplesamlphp/simplesamlphp/lib/SimpleSAML/Module.php:260 (SimpleSAML\Module::process)
>
> simplesamlphp ERROR \[ffdf5e0102\] 0 /mnt/www/html/mysite/vendor/simplesamlphp/simplesamlphp/www/module.php:10 (N/A)
>
> simplesamlphp ERROR \[ffdf5e0102\] Error report with id 86f8550b generated.

# Answer
It turns out that the issue was the entityID on the authenticator end containing the port number, and the default entityID provided by simplesamlphp not so. Having the authenticator change their config entries, dropping the explicit port reference, resolved the issue.
> 0 votes

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


# Question
Title: How can I make views exposed filter case-sensitive?
When I use the exposed filter, if I search for "IMAG" all results showing image show up. Is there a way to have the exposed filter search case sensitive?

**UPDATE** I added a screenshot to explain more. I have tried the operators: is equal to, contains, contains any word, contains all words, and regex. Neither of them are case sensitive.

# Answer
> 1 votes
For the solution I used the hook\_views\_query\_alter and used the LIKE BINARY operator in my condition.

```
condition('node_field_data.title', "%{$word}%", 'LIKE BINARY')

```

---
Tags: 8, views
---


title: Twig debug statements not appearing

text:
# Question
Title: Twig debug statements not appearing
Still new to Drupal, but I've read over the docs countless times and can't figure this out. I'm not seeing Twig's debug comments. I am using a sub-theme of 'Bootstrap 3'. Running on local environment in Windows (XAMPP).

In /sites/default/services.yml

```
debug: true
auto_reload: true
cache: false

```

I've cleared the cache more times than I can count. I've also tried, unsuccessfully, making various changes to settings.php as well, however since these changes aren't in the docs I'm guessing they *shouldn't* be required for enabling debug statements.

# Answer
go to `sites/default/services.yml`

and write in the proper indentation with spaces as below..

```
parameters:
  twig.config:
    debug : true
    auto_reload: true
    cache: false

```

\*remember to follow the same structure as above

If not working clear cache from back-office :
http://your-drupal.com/admin/config/development/performance
> 4 votes

# Answer
I had the same problem. I installed a fresh version of Drupal 8.55 and Bootstrap Theme. Modifying the services.yml didnt work. So I made a search for the word debug and find this file:

core/core.servicies.yml. I Modify this part and it work's

twig.config: debug: true

Somehow that file is taking precedence after services.yml.

Hope this help someone, but it's not really the best answer because we are modifying the core of drupal here.
> 4 votes

# Answer
According to Drupal documentation, the advised method is to copy settings.local.php from sites/ to sites/default, then enable it by uncommenting this in your settings.php:

```
if (file_exists($app_root . '/' . $site_path . '/settings.local.php')) {
  include $app_root . '/' . $site_path . '/settings.local.php';
}

```

Then, add this to your settings.local.php:

```
$settings['container_yamls'][] = DRUPAL_ROOT . '/sites/development.services.yml';

```

(You can also copy development.services.yml, if you want).

Add this to your development.services.yml:

```
parameters:
  twig.config:
    debug: true
    auto_reload: true
    cache: false

```

Then, run `drush cr`. After this, you should see your Twig debugging.
> 4 votes

# Answer
In Drupal 8.8, I needed to do this:

1. copy /sites/example.local.settings.php to /sites/default/local.settings.php to enable local development services (this file enable those services by default).
2. copy /sites/default/default.services.yml to /sites/default/services.yml and change twig debug settings i the twig.config: section (starting at line 39): debug: true auto\_reload: true auto\_reload: true
3. run rebuild.php (this rebuilds caches). drush cr should work too.

I got there after trying many variations of the above answers. Apparently adding the "parameters: twig.config:" (etc) to the development.services.yml file is no longer necessary, as those parameters are now set in /sites/default/services.yml.
> 1 votes

# Answer
Try enabling it manually in your site instance.

In /sites/yoursite/services.yml:

```
twig.config:
  debug: true

```
> 0 votes

# Answer
If you have a multiste installation,

1. Copy **/sites/development.services.yml** to your subsite folder
2. Rename it to be **services.yml**
3. Open your settings.php and find the following line:

> $settings\['container\_yamls'\]\[\] = DRUPAL\_ROOT . '/sites/development.services.yml';

4. Under that line add:

> $settings\['container\_yamls'\]\[\] = DRUPAL\_ROOT . '/sites/YOUR\_SITE\>/services.yml';

5. Save your settings.php
6. Rebuild your caches (drush cr) and now Drupal will be aware of your configuration changes in **services.yml** file
> 0 votes

# Answer
If you copy default.services.yml in sites/default to services.yml, you will see the twig debug parameter is already in there, set to false. I didn't see this, and added the parameters/twig/debug: true at the bottom of the file. That didn't work, and no wonder.

The twig.config section in my services.yml starts at line 35, and the parameters you want are under that. This, in 8.7.
> 0 votes

# Answer
I had the same issue and confusion was with the sites/default/services.yml file. I was editing the default.services.yml file and did not have a services.yml file.
> 0 votes

---
Tags: 8, debugging, theming
---


# Question
Title: why am i getting a Drupal\Component\Plugin\Exception\PluginNotFoundException: when the plugin does exist?
I wrote a custom source plugin and put it in migd8\_node\_blog/src/Plugin/migrate/source/Node.php with

```
<?php

/**
 * @file
 * Contains \Drupal\migd8_node_blog\Plugin\migrate\source\Node.
 */

namespace Drupal\migd8_node_blog\Plugin\migrate\source;

use Drupal\migrate\Row;
use Drupal\node\Plugin\migrate\source\d7\Node as D7Node;

    /**
     * Custom node source including url aliases.
     *
     * @MigrateSource(
     *   id = "migd8_node_blog_node"
     * )
     */

    class Node extends D7Node {

```

but i keep getting this error when i run drush mim migd8\_node\_blog Drupal\Component\Plugin\Exception\PluginNotFoundException: The "migd8\_node\_blog\_node" plugin does not exist. even after flushing the cache and reinstalling the module.

# Answer
> 3 votes
> after flushing the cache and **reinstalling the module**

Check that the module is indeed installed at `/admin/modules`, if it's not, it's due to a migrate tools bug. Perhaps you deleted the part

```
dependencies:
  enforced:
    module:
      - migd8_node_blog

```

that you have in your other question, which fixes the reinstall bug if you have it before you install the module.

But if you installed it without the above code, you'll have to run this code **once/one time only**, and it will allow you to install again as this code removes the stuck configuration.

```
  $module_path = DRUPAL_ROOT . base_path() . drupal_get_path('module', 'migd8_node_blog') . '/config/install';

  $configs = array();
  $files = file_scan_directory($module_path, '/migrate_plus/');
  if ($files) {
    foreach ($files as $file) {
      $configs[] = $file->name;
    }
    // Delete each config using configFactory.
    foreach ($configs as $config_name) {
     \Drupal::configFactory()->getEditable($config_name)->delete();
    }

    return TRUE;
  }

```

---

If the modue is indeed installed, then the issue is due to you trying to assign `migd8_node_blog_node` as a plugin under the wrong key.

Since this is a **source** plugin and should be used only be used under `source:` and not/never under `process:` or any other key. See my answer.

---
Tags: migrations, plugins
---


title: Intercepting when a page is requested

text:
# Question
Title: Intercepting when a page is requested
I am trying to call a function every time a page is loaded. With Drupal 7, I used hook\_init, now I'm using a subscribed event.

```
  public function logRegEntry(GetResponseEvent $event) {
    $request = $event->getRequest();
    $method = $request->getMethod();
    $path = $request->getPathInfo();
    $route_name = \Drupal::routeMatch()->getRouteName();
    if (($method == 'GET') && ($path == '/')) {
      // do it just for this module
      \Drupal::service('wdocsDebug')->logSiteEntry();
    }
  }

```

This works fine for the home page, but I want it for every page a user requests. This is getting called four times; first for the page I'm requestings, and then for: /admin/coffee/get-data /history/5/read /quickedit/attachments

I did notice that on the first request, the $route\_name is set to "entity.node.canonical" -- might this be a better way to check for a page? Probably not, since it would only get triggered by node pages.

I'm looking for a better way to do this, not sure why there are four request events. Thanks.

# Answer
> 2 votes
Your event subscriber is also picking up AJAX requests (e.g. `/admin/coffee/get-data` is an endpoint the Coffee module uses when it's needed in the client browser) which explains why you're seeing multiple events for a page request.

You can filter out those events with the logic best fits your needs (e.g. a `$request->isXmlHttpRequest()` call to check for AJAX requests, or a route whitelist as you mentioned).

---
Tags: event-subscribers
---


title: Multiple Gutenberg blocks in a module

text:
# Question
Title: Multiple Gutenberg blocks in a module
I've created a custom Gutenberg block and would like to know how to add more within the same module. Some suggested to add JS in MY\_CustomModule.libraries.yml, but it does not work.

My custom H2 heading block in index.es6.js. This simple block is working fine.

```
const { blocks, data, element, components, editor } = wp;
const { registerBlockType } = blocks;
const { dispatch, select } = data;
const { Fragment } = element;
const { PanelBody, BaseControl, Icon, RangeControl, IconButton, Toolbar, SelectControl } = components;
const { InnerBlocks, RichText, InspectorControls, PanelColorSettings, MediaUpload, BlockControls } = editor;
const __ = Drupal.t;

const settings = {
  title: __('Section Heading'),
  description: __('H2 Heading'),
  icon: 'heading',
  attributes: {
    title: {
      type: 'array',
      source: 'children',
      selector: 'h2',
    },
  },

  edit({ className, attributes, setAttributes, isSelected }) {
    const { title } = attributes;
    return (
      <Fragment>

            <RichText
              identifier="title"
              tagName="h2"
              value={ title }
              onChange={nextTitle => {
                setAttributes({
                  title: nextTitle,
                });
              }}
              placeholder={ __( 'Enter text...', 'custom-block' ) }
              keepPlaceholderOnFocus={true}
            />
        <InspectorControls>
          <PanelBody title={ __('Block Settings') }>
            { attributes.content }
          </PanelBody>
        </InspectorControls>
      </Fragment>
    );
  },
  save({ className, attributes }) {
    const { title } = attributes;
    return (
        <h2>{ title }</h2>
    );
  }
};
const category = {
  slug: 'myblocks',
  title: __('My Blocks'),
};

const currentCategories = select('core/blocks').getCategories().filter(item => item.slug !== category.slug);
dispatch('core/blocks').setCategories([ category, ...currentCategories ]);

registerBlockType(`${category.slug}/docs-blocks`, { category: category.slug, ...settings });

```

# Answer
You simply need to reproduce the structure of your block inside the module :

```
- Module root
-- blocks
--- first_block
---- js/index.js
---- js/index.es6.js
--- second_block
---- js/index.js
---- js/index.es6.js
- module.gutenberg.yml
# module.gutenberg.yml file content  = Libraries to be injected on editor (node edit)
libraries-edit:
  - module/first_block-edit
  - module/second_block-edit
- module.librairies.yml
# module.librairies.yml content = front libs :
first_block-edit:
  version: VERSION
  js:
    blocks/first_block/js/index.js: {}
  dependencies:
    - gutenberg/edit-node

second_block-edit:
  version: VERSION
  js:
    blocks/second_block/js/index.js: {}
  dependencies:
    - gutenberg/edit-node

```

You could add as many gutenberg block as you need as long as your register your block with a different category/slug in the `index.es6.js`

`registerBlockType( `${category.slug}/${block.slug}`, { category: category.slug, ...settings } );`
> 1 votes

---
Tags: blocks, 9
---


# Question
Title: How do I create a merge request for a project?
I'd like to create a merge request for a drupal.org project. I found a page on drupal.org titled Issue forks & merge requests which seems to address this but I'm not really getting anywhere. The page says:

> On an issue page, make sure the fork is created and you have access.
>
> 1. If the fork has not been created yet, click “Fork {project}”
> 2. If you do not have push access yet, click “Get push access”
> 3. If you see “✓ You have push access,” you’re all set!

The only place I could find a "Fork" button was on https://git.drupalcode.org/project/project\_name/-/forks. But when I click on that I see just see a namespace with my name, and a greyed out "select" button, which, if I hover over it, displays "You have reached your project limit". But I think I'm in the wrong place anyway.

The instructions start with "...on the issue page" so I created an issue on drupal.org for the project, on the issue page there are no links that mention "fork" anywhere to be seen.

What am I doing wrong?

# Answer
As said in Issue forks & merge requests drupal.org only implements (so far) issue forks and merge requests related to those issue forks. Issue forks are still in development and enabled only on those projects that opted-in, a list of which is available in Opt-in core issues into the Drupal.org Issue Forks and Merge Requests Beta: The list doesn't include the projects maintained from the Drupal Association, for which issue forks have been directly enabled from a Drupal Association employee.
The plan is making them available on all projects on November 10, 2020.

When issue forks are enabled, an issue opened for that project contains a button as in the following screenshot, taken from Show commit activity for issue fork branches without merge requests.

>

When an issue fork has been created, there will be a different button, as in the following screenshot.

>

I didn't watch at the code used on drupal.org, but I expect the code checks users have the *Git user* role on drupal.org, which is given to the users who accepts the Git access agreement, or a Git account on Drupal.org, before allowing them to create an issue fork or obtain push access on an issue fork.

>

Merge requests aren't tested on DrupalCI, until Test merge requests made in GitLab with DrupalCI is marked as fixed. That could happen before, or after, issue forks are enabled on every project.

As for today, December 24, 2020, issue forks and merge requests have been enabled on every project hosted on drupal.org. Merge requests are tested by the DrupalCI instance running for drupal.org.
> 4 votes

---
Tags: version-control, community-official-site
---


# Question
Title: Date field with selection back more than 20 years
I have a date field:

the year selection is possible **20** years back from now:

But I need it to be **25** years. Is this a fix Drupal setting or can I change that?

# Answer
> 2 votes
The default is actually -10 years

Since yours is longer, scroll own and select "Other" and type -25

Save settings

Now when you edit again, you should see

---
Tags: 7
---


# Question
Title: How to get the path to the translated node?
I've set up a multilanguage site in Drupal 7. In a template I need to add a link to a specific content node. This node has translations and path aliases like so:

```
node/42 = pathalias_de (german version)
node/43 = pathalias_en (english version)

```

In the template I'd like to call `url()` to get the link to the aliased version of the content node in the current language.

On the german page it should link to `/de/pathalias_de` on the english page to `/en/pathalias_en`

I've tried several variations, like calling the nid, calling the alias, passing the current language object to `url()`.

Is this something `url()` should be able to do or do I need a different function? Thanks!

# Answer
After looking at the source of the "Content translation" module I've come up with this solution:

```
    global $language;
    $translations = translation_path_get_translations("node/42");
    /*
    Returns the paths of all translations of a node, based on its Drupal path:
    array(2) {
        ["de"]=>string(7) "node/42"
        ["en"]=>string(7) "node/43"
    }
    */
    print l(t('Link Title'), $translations[$language->language]); //l() will return the alias to node/42

```

This will output the link to the correct node alias. Works and isn't overly complicated - still wonder if there's a function that already does this.
> 15 votes

# Answer
```
global $language;

$translation_node = translation_path_get_translations("42");

$nid = $translation_node[$language->language]->nid;

$path = "node/{$nid}";

```
> 5 votes

# Answer
Take a look at drupal\_get\_path\_alias fuction

> Given an internal Drupal path, return the alias set by the administrator.
>
> If no path is provided, the function will return the alias of the current page.
>
> Parameters
>
> $path: An internal Drupal path.
>
> $path\_language: An optional language code to look up the path in.

As you can make out it takes a path\_language parameter. So you could find out the current language and then pass on to this function.

Or simply use Multilink module

> 1. Designed for multi-language sites. Especially relevant for sites where not all content is available in all languages, or may be made available (translated) at a later time.
> 2. Allows you to easily create in-content links to other nodes, and those links automatically update to point to a suitable translated version of referenced nodes, when available.
> 4 votes

# Answer
None of the above answers seem to make correct use of i18n\_path. If you want to link to the translated node you should simply enable the i18n\_path module and format the path thus,

```
drupal_get_path_alias('node/'.$node->nid, $node->language);

```
> 3 votes

# Answer
You should probably use the link function. It allows you to pass the language as a parameter and it's the appropriate function to create a link with Drupal.
> 2 votes

# Answer
Using Translation Redirect you can link to nodes with language prefix.

This is what it says:

> The Translation redirect module redirects anonymous users (including web crawlers) to the translation of the page in the requested language, if it exists, using a 301 redirect code. For example, if the request is for /de/node/23 and node/23 corresponds to an English page, the user will be redirected to the German translation of the English node if it exists. If the translation does not exist, then the source node content is displayed.

For example if nid 22 is for English version and it has French and German ones then you can simply link to them as fr/node/22 and de/node/22. This 22 can also be any nid of the translated version.

If particular translation is not found for example if you've not added for Japanese, then ja/node/22 will give 404 for Anonymous users.
> 1 votes

# Answer
To get the correct prefix path, I use:

```
$node_url = url(drupal_get_path_alias('node/' . $row->nid));

```

The drupal\_get\_path\_alias() return only the alias, the url() is the one that add the prefix ("en/").
> 0 votes

# Answer
I used the below code snippet to fetch translation node path in a node page without a node load.

```
$current_path = current_path();
$tanslation_path = translation_path_get_translations(drupal_get_normal_path($current_path, '---TRANS LANGUAGE---'));
$node_path = url($tanslation_path['---TRANS LANGUAGE---'], array('language' => (object)array('language' => '---TRANS LANGUAGE---')));
$alias_path = drupal_get_path_alias($node_path);
```

First you have to get the current path and pass this to *translation\_path\_get\_translations()* or as specified above with translation language code. Then get the alias from its system path.

I used this in *hook\_preprocess\_html()* and works fine for me. Hope this helps.
> 0 votes

---
Tags: i18n-l10n, path-aliases
---


# Question
Title: Billing address missing for recurring orders
I'm using Datatrans module as payment gateway. Module does not fully support recurring payment, so I had to add some missing functionality. I added method createPayment:

```
  public function createPayment(PaymentInterface $payment, $capture = TRUE) {

    // Allowing only new payments
    $this->assertPaymentState($payment, ['new']);

    $payment_method = $payment->getPaymentMethod();
    $amount = strval(floor(floatval($payment->getAmount()->getNumber())*100));

    // Calling Datatrans API and handling response
    $transaction_data = [
      'apiUrl' => $this->configuration['r_api_url'],
      'errorEmail' => $this->configuration['r_error_email'],
      'successEmail' => $this->configuration['r_success_email'],
      'transactionRefNo' => $payment->getOrderId(),
      'merchantAccountId' => $this->configuration['r_merchant_id'],
      'merchantAccountPassword' => $this->configuration['r_merchant_password'],
      'sign' => $this->configuration['r_sign'],
      'req_type' => $this->configuration['req_type'],
      'expm' => $payment_method->expm->getValue()[0]['value'],
      'expy' => $payment_method->expy->getValue()[0]['value'],
      'amount' => $amount,
      'currencyCode' => $payment->getAmount()->getCurrencyCode(),
      'aliasCC' => $payment_method->getRemoteId(),
    ];

    $transaction = DatatransHelper::makeTransaction($transaction_data);

    // Saving payment locally

    $next_state = $capture ? 'completed' : 'authorization';
    $payment->setState($next_state);
    $payment->setRemoteId($transaction["uppTransactionId"]);
    $payment->setExpiresTime(strtotime('+5 days'));
    $payment->save();

  }

```

And it works, but problem I have is that all recurring orders created by recurring module do not have billing addresses.

Looked at database and condition for an order to have billing address (table "commerce\_order") it must have id of user profile set in fields "billing\_profile\__target\_id" and "billing\_profile\__target\_revision\_id". If I put some billing profile ids there directly, by editing database billing profile for that order appear and everything works as expected.

What (and where) should I add to my code to set that billing address?

Inside RecurringOrderManager() , refreshOrder() and createOrder() methods I saw row like:

```
 $billing_profile = $payment_method ? $payment_method->getBillingProfile() : NULL;

```

and that `getBillingProfile()` may be the method I'm missing (I do have null instead of billing profile id in database), but what confuses me is that it's defined in PaymentMethodInterface and Datatrans module is not implementing it at all?!

Is it up to Datatrans module to do something in order to set billing profile? How can I do that? I'm not very familiar with this and any hint is welcome.

# Answer
Some workaround:

```
  // For unknown reason recurring orders are not getting billing profiles.
  // This method is checking of order has billing profile and if not
  // it finds it's initial order over subscription and copies it's
  // billing profile.
  public static function addMissingBillingAddress (Order $order)
  {
    if ($order->getBillingProfile() == null) {
      $subscriptions = self::collectSubscriptions($order);
      $subscription = array_shift($subscriptions);
      $initialOrder = $subscription->getInitialOrder();
      $billingProfile = $initialOrder->getBillingProfile();
      $order->setBillingProfile($billingProfile);
      $order->save();
      return;
    }
  }

  public static function collectSubscriptions($order) {
    $subscriptions = [];
    foreach ($order->getItems() as $order_item) {
      if ($order_item->get('subscription')->isEmpty()) {
        // A recurring order item without a subscription ID is malformed.
        continue;
      }
      /** @var \Drupal\commerce_recurring\Entity\SubscriptionInterface $subscription */
      $subscription = $order_item->get('subscription')->entity;
      // Guard against deleted subscription entities.
      if ($subscription) {
        $subscriptions[$subscription->id()] = $subscription;
      }
    }
    return $subscriptions;
  }

```

When I'm processing recurring order I'm checking if billing address is missing and if it does I'm collecting one from initial order (over subscription) and injecting that one.

Of course, I would like to know why it's not added at first place.
> 0 votes

---
Tags: 8, commerce, payment-gateways
---


# Question
Title: How can i find out which form mode is used from hook_form_FORM_ID_alter?
I have created a new form mode (in the UI at `admin/structure/display-modes`) for a custom entity (form mode is entitled `apply`) and added `apply` to the form handler classes in the entity class.

The form is built and returned in a Controller, and specifies the form mode.

`$form = $this->entityFormBuilder()->getForm($entity, 'apply');`

Now I would like to be able to determine from a `hook_form_alter` whether this form is being displayed using the `apply` form mode, but I cannot work out how that is available from `$form` or `$form_state`.

# Answer
You can try EntityFormInterface::getOperation:

```
function mymodule_form_FORM_ID_alter(&$form, FormStateInterface $form_state) {
  // only continue if this is for form mode "apply"
  if ($form_state->getFormObject()->getOperation() !== 'apply') {
    return;
  }

  // do stuff

}

```
> 6 votes

# Answer
The accepted answer doesn't check form mode, but it checks the operation instead. To validate on form mode, you can use something like this:

```
function mymodule_form_FORM_ID_alter(&$form, FormStateInterface $form_state) {
  /** @var \Drupal\Core\Entity\Entity\EntityFormDisplay $formDisplay */
  $formDisplay = $form_state->getStorage()['form_display'];

  // Only continue if this is for form mode "apply".
  if ($formDisplay->getMode() !== 'apply') {
    return;
  }

  // do stuff

}

```
> 4 votes

---
Tags: 8, forms
---


# Question
Title: Hide or disable field after registration
I have 1 field on user registration page that should not be changed after account registration. It needs to be hidden or disabled. Which module can do this ?

# Answer
You can try **Field Permissions** Module

> **Features**
>
> * Enable field permissions on any entity, not just nodes.
> * Role-based field permissions allowing different viewing patterned based on what access the user has.
> * Author-level permissions allow viewing and editing of fields based on who the entity owner is.
> * Permissions for each field are not enabled by default. Instead, administrators can enable these permissions explicitly for the fields where this feature is needed.
> 4 votes

# Answer
If you want to do it in custom module

```
my_module_form_user_profile_form_alter(&$form, &$form_state) {
  // Since it is on registration form, field might be reqruied.
  $form['your_field']['#required'] = FALSE;
  $form['your_field']['#access'] = FALSE;
}

```
> 9 votes

# Answer
Try with this code:

```
function MODULENAME_form_alter(&$form_state, $form_id){
  $form[$field]['#access'] = FALSE;
  $form[$field]['#disabled'] = TRUE;
  }

```
> 1 votes

# Answer
```
function module_form_alter(&$form_state, $form_id) {
 if (isset($form['title'])) {
    $form['title']['#required'] = FALSE;
    $form['title']['#type'] = 'value';
  }
}

```
> 1 votes

# Answer
Make field visibility property for anonymous users only. So Value will be stored at time of registration. Then no one can access that field. Once you install field permission module, you will find this option to apply to only anonymous user only.
> 1 votes

# Answer
Try using `hook_form_alter`, see the example below:

```
function MODULENAME_form_alter(&$form_state, $form_id){
      global $user;
      //$form_id :: user detail edit form id
      if($user->uid != 0 && $form_id == "required_form_id"){
         //$field :: requied field name
          $form[$field]['#access'] = FALSE;

      }
   }

```
> 0 votes

# Answer
place this code in a custom module, field will be able on new content but will be disabled to edit it.

```
if ($form_id == 'YOUR_FORM_ID') {
//dsm($form);

// for user form use '#user' instead of '#node' and uid instead of nid
if (isset($form['#node']) && isset($form['#node']->nid)){

    // Prevent editing a field once the node has been created
    // hiding the field entirely on the edit form.
    $form['field_name']['#access'] = FALSE;

    // disable update field on the edit form (gray color).
    $form['field_name']['#disabled'] = TRUE;
}

```

}
> 0 votes

# Answer
I actually think, that #disabled is not required, when you already have #access to false. Also #disabled is not a really good thing because it adds disabled=disabled to the form field which you can easily delete as a user.

What you want to achieve is:

* Access false
* the field value which the user gave for example at the registration form is still there.

You can achieve this in your module with the following code:

```
use \Drupal\Core\Form\FormStateInterface;

/**
 * Implements hook_form_FORM_ID_alter() for form 'user_form' (user edit form).
 *  - disable the change of field field_MY_FIELD (Registrierungstyp)
 */
function YOUR_MODULE_form_user_form_alter(&$form, FormStateInterface $form_state, $form_id)
{
// in the example you see also how to make the administrator role can still change the field
  $currentuser = \Drupal::currentUser();
  $roles = $currentuser->getRoles();
  $authenticated = $currentuser->isAuthenticated();
  if ($authenticated &&
    !in_array('administrator', $roles)
  ) {
    $form['field_MY_FIELD']['#access'] = FALSE;
    $form['field_MY_FIELD']['#default_value'] = TRUE;
  }
}

```
> 0 votes

---
Tags: 7, entities
---


# Question
Title: Why does step 2 in migrating files to media try to insert to the nid from the source and not the target?
I am doing a migration as per this example https://www.drupal.org/project/migrate\_file\_to\_media/issues/3118471. Migration and step 1 work fine, I get this error when running step 2. I also tried running the migration with an --update flag (drush mim migd8\_blog\_media\_step2 --update) and get the same error . its trying to do an insert instead of an update. **And its trying to insert into the source nid, not the target nid.**

Drupal\Core\Database\IntegrityConstraintViolationException: SQLSTATE\[23000\]: Integrity constraint violation: 1048 Column 'title'\[error\]cannot be null: INSERT INTO {node\_field\_data} (nid, vid, type, langcode, status, uid, title, created, changed, promote, sticky, default\_langcode, revision\_translation\_affected, publish\_on, unpublish\_on) VALUES (:db\_insert\_placeholder\_0, :db\_insert\_placeholder\_1, :db\_insert\_placeholder\_2, :db\_insert\_placeholder\_3, :db\_insert\_placeholder\_4, :db\_insert\_placeholder\_5, :db\_insert\_placeholder\_6, :db\_insert\_placeholder\_7, :db\_insert\_placeholder\_8, :db\_insert\_placeholder\_9, :db\_insert\_placeholder\_10, :db\_insert\_placeholder\_11, :db\_insert\_placeholder\_12, :db\_insert\_placeholder\_13, :db\_insert\_placeholder\_14); Array ( \[:db\_insert\_placeholder\_0\] =\> 22610

This is the id of the source node, not the target The migration file I am using contains the following lines.

```
# This migration links the newly created media entities with entity reference field on the target bundle.
id: migd8_blog_media_step2
label: blog Media Mapping
source:
  key: migrate
  plugin: d7_node
  node_type: blog_entry
destination:
  plugin: entity:node
  default_bundle: blog
process:
  nid: nid
  changed: changed
  field_files:
    plugin: file_id_lookup
    source: field_main_image
    migration: migd8_blog_media_step1
    no_stub: true
migration_dependencies:
  optional:
  - migd8_node_blog
  - migd8_blog_media_step1

```

this is my step 1:

```
# File to Media Migration configuration for blog Media migration

id: migd8_blog_media_step1
label: blog Media Step 1
migration_group: cncs
source:
  key: migrate
  plugin: media_entity_generator_d7
  entity_type: node
  bundle: blog_entry
  langcode: und
  d7_file_url: 'https://www.nationalservice.gov/sites/default/files/'
  #d7_file_url: 'public://'
  field_names:
    - field_main_image

destination:
  plugin: entity:media

process:

  bundle:
    plugin: default_value
    default_value: image

  # Using the alt tag if available, else the file name.
  name:
    plugin: media_name
    source: file_name

  # This process plugin will skip the whole row if a existing media is found.
  existing_media_file:
    plugin: check_media_duplicate
    source: target_id

  # This process plugin will skip the whole row if a duplicate image is found.
  unique_file_id:
    plugin: check_duplicate
    source: target_id

  # Map the field

  field_media_image/alt: alt
  field_media_image/title: title
  field_media_image/target_id:
    plugin: media_file_copy
    move: FALSE
    reuse: TRUE
    path: 'public://images/'
    source: file_path

  langcode: langcode

  uid:
    plugin: default_value
    default_value: 1
`

```

here is my main migration yml :

```
id:

migd8_node_blog
label: Custom node migration from Drupal 7
migration_group: cncs
deriver: Drupal\node\Plugin\migrate\D7NodeDeriver
dependencies:
  enforced:
    module:
      - migd8_node_blog_node
source:
 plugin: migd8_node_blog_node # use custom source plugin
 node_type: blog_entry
destination:
  plugin: entity:node
  bundle: blog
process:
  'path/pathauto':
    plugin: default_value
    default_value: 0 # Disable pathauto.
  'path/alias': alias
  type:
    plugin: default_value
    default_value: blog
  status: status
  uid:
    plugin: default_value
    default_value: 1
  title: title
  langcode:
    plugin: static_map
    bypass: true
    source: language
    map:
      und: en
  created: created
  changed: changed
  promote: promote
  field_blog_tags:
    plugin: migration_lookup
    source: field_blog_tags
    migration: migrate_taxonomy
  sticky: sticky
  body:
      plugin: sub_process
      source: body
      process:
        value: value
        summary: summary
        format:
          plugin: default_value
          default_value: full_html
  field_blog_author: field_blog_author
  field_blog_post_date_:
    plugin: iterator
    source: field_blog_post_date_
    process:
      value:
        plugin: substr
        source: value
        start: 0
        length: 10

```

I realize its looking for the source nid in this query , not the target nid Drupal\Core\Database\IntegrityConstraintViolationException: SQLSTATE\[23000\]: Integrity constraint violation: 1048 Column 'title'\[error\]cannot be null: INSERT INTO {node\_field\_data} (nid, vid, type, langcode, status, uid, title, created, changed, promote, sticky, default\_langcode, revision\_translation\_affected, publish\_on, unpublish\_on) VALUES (:db\_insert\_placeholder\_0, :db\_insert\_placeholder\_1, :db\_insert\_placeholder\_2, :db\_insert\_placeholder\_3, :db\_insert\_placeholder\_4, :db\_insert\_placeholder\_5, :db\_insert\_placeholder\_6, :db\_insert\_placeholder\_7, :db\_insert\_placeholder\_8, :db\_insert\_placeholder\_9, :db\_insert\_placeholder\_10, :db\_insert\_placeholder\_11, :db\_insert\_placeholder\_12, :db\_insert\_placeholder\_13, :db\_insert\_placeholder\_14); Array ( \[:db\_insert\_placeholder\_0\] =\> 22610 **22610 is the node id in the source. in the target its a totally different nid**

# Answer
> 1 votes
i imported the new nodes with the same nid as the old ones using

```
nid: nid

```

in the process that fixed the problem!

# Answer
> 2 votes
As the Migrating nodes doc shows, it needs to be:

```
destination:
  plugin: entity:node
  default_bundle: blog

```

---
Tags: migrations
---


title: Why doesnt my migration plugin run?

text:
# Question
Title: Why doesnt my migration plugin run?
I made a plugin to add the alias to my node migration, using the instructions here https://www.calebthorne.com/blog/drupal/2016/07/16/drupal-8-migrate-pathauto

but i tried to put a drush break point and it never runs . How do i get this plugin to run during the migration? this is the plugin code below . and the yml is as follows:

```
id: migd8_node_blog
label: Custom node migration from Drupal 7
migration_group: cncs
dependencies:
  enforced:
    module:
      - migd8_node_blog
source:
 plugin: d7_node
 node_type: blog_entry
destination:
  plugin: entity:node
  bundle: blog_entry
process:
  type:
    plugin: default_value
    default_value: blog_entry
  'path/alias': alias

```

migd8\_node\_blog/src/Plugin/migrate/source/Node.php

```
<?php

    /**
     * @file
     * Contains \Drupal\migd8_node_blog\Plugin\migrate\source\Node.
     */

    namespace Drupal\migd8_node_blog\Plugin\migrate\source;

    use Drupal\migrate\Row;
    use Drupal\node\Plugin\migrate\source\d7\Node as D7Node;

    /**
     * Custom node source including url aliases.
     *
     * @MigrateSource(
     *   id = "migd8_node_blog"
     * )
     */
    class Node extends D7Node {

      /**
       * {@inheritdoc}
       */
      public function fields() {
        return ['alias' => $this->t('Path alias')] + parent::fields();
      }

      /**
       * {@inheritdoc}
       */
      public function prepareRow(Row $row) {
        // Include path alias.
        $nid = $row->getSourceProperty('nid');
        drush_print_r($nid);
        $query = $this->select('url_alias', 'ua')
          ->fields('ua', ['alias']);
        $query->condition('ua.source', 'node/' . $nid);
        $alias = $query->execute()->fetchField();
        if (!empty($alias)) {
          $row->setSourceProperty('alias', '/' . $alias);
        }
        return parent::prepareRow($row);
      }
    }

```

# Answer
> 0 votes
fixed it by uninstalling and reinstalling the module and deleting the configuration.

# Answer
> 1 votes
Article is correct, just noticed that it's a **source** plugin and not a process plugin.

Change yours to

```
id: migd8_node_blog
label: Custom node migration from Drupal 7
migration_group: cncs
dependencies:
  enforced:
    module:
      - migd8_node_blog

source:
 plugin: migd8_node_blog_node # use custom node source plugin
 node_type: blog_entry

destination:
  plugin: entity:node
  bundle: blog_entry

process:
  nid: nid
  type: type
  'path/pathauto':
    plugin: default_value
    default_value: 0 # Disable pathauto.
  'path/alias': alias

```

And

```
/**
 * Custom node source including url aliases.
 *
 * @MigrateSource(
 *   id = "migd8_node_blog_node"
 * )
 */

```

---
Tags: nodes, migrations
---


# Question
Title: Why is step 2 of file to media migrate giving me missing bundle error?
I am following the instruction on this page : https://www.drupal.org/project/migrate\_file\_to\_media/issues/3118471 , but with media of bundle document, not image. I have my step 1 yml on this issue How can i debug why files are not being created when using migrate files to media module? My nodes of type grants and step 1 files are being created as media correctly, however when i do step 2, using the yml below, I get this error :

Missing bundle for entity type node (/var/www/html/docroot/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php:108)

```
id: migd8_grants_media_step2
label: grants Media Mapping
migration_group: cncs
source:
  key: migrate
  plugin: d7_node
  node_type: grants

destination:
  plugin: entity:node

process:
  nid: nid
  changed: changed

  field_files:
    plugin: file_id_lookup
    source: field_files
    migration: migd8_grants_media_step1
    no_stub: true

migration_dependencies:
  optional:
  - migd8_node_grants
  - migd8_grants_media_step1

```

I made some changes as an experiment and started getting duplicate grants being created, one with the correct media files attached, and one without , these are the changes i made which caused the duplicates with one having the correct file attached:

```
id: migd8_grants_media_step2
label: grants Media Mapping
migration_group: cncs
source:
  key: migrate
  plugin: d7_node
  node_type: grants

destination:
  plugin: entity:node

process:
  nid: nid
  changed: changed
  title: title
  type:
    plugin: default_value
    default_value: grants
  field_files:
    plugin: file_id_lookup
    source: field_files
    migration: migd8_grants_media_step1
    no_stub: true

migration_dependencies:
  optional:
  - migd8_node_grants
  - migd8_grants_media_step1

```

# Answer
> 1 votes
in step2 i had to change it to this

```
destination:
  plugin: entity:node
  default_bundle: grants

```

---
Tags: media, migrations
---


# Question
Title: How can I alter Entity Auto Complete Results?
I want to alter entity auto complete results in hook\_form\_alter. My functionality is

> 1) I have a entity reference of taxonomy and bundle is tags which is Level 2 (Field Name: Product Child)
>
> 2) In this I select one term from Level2
>
> 3) I have another entity autocomplete which refers to taxonomy tags Level 1 (Field Name: Product Parent)

Now my question is if i select one Level 2 Term in Product Child Field , is there any option to show only parent terms of that Product child field in Product Parent Field Like :

```
Cabbage (L1)
 -> Fruit (L2)
 -> Flower (L2)

Lemon(L1)
 -> Lemon Fruit(L2)
 -> Eatable (L2)

```

If I select Fruit in **Product Child** field I want to show only Lemon in **Product Parent** Field. Is there any way to do this.

# Answer
I wrote a little module that shows how to alter entity autocomplete suggestions. You could try modify it to meet your needs. https://github.com/minnur/Alter-Entity-Autocomplete
> 3 votes

# Answer
Autocomplete currently doesn't receive the parent node as context.

You could try to use JS for different autocomplete configurations to pass that data long somehow. Have a look at how the Dynamic Entity Reference module switches out the autocomplete to support different target entity types.

You would also need to implement your own selection plugin I suppose, so that you can pass it some configuration that you can then act on. Additionally, the configuration is stored server-side and the client only gets a random key, so you likely actually need to use #ajax to rebuild the second field when the first changes.

So, not trivial and will require quite some custom code.

Possible alternatives:

* Hierarchical Select, operates on a single field and has no D8 port yet.
* Only allow the user to select the product child, implement a bit of custom code on hook\_node\_presave() to set the parent field automatically. We do similar things for e.g. a section vocabulary where we store the full hierarchy in a multi-value section\_all field so that it is easier to query on. This would work even better in combination with the HS module above, since users could still select the parent first if you want that. But again, unfortunately no port yet it seems.
> 2 votes

# Answer
A much quicker solution that will only change the reference fields you want is making an `EntityReferenceSelection` plugin and selecting it as the "Reference method" in the field settings. Just make sure your id matches your category in the docblock (otherwise it won't be available to select in field settings):

```
<?php

namespace Drupal\custom_module\Plugin\EntityReferenceSelection;

use Drupal\Core\Entity\Plugin\EntityReferenceSelection\DefaultSelection;
use Drupal\node\NodeInterface;

/**
 * Provides custom entity reference selection functionality.
 *
 * @EntityReferenceSelection(
 *   id = "custom",
 *   label = @Translation("Custom Reference Method"),
 *   group = "custom",
 *   weight = 1
 * )
 */
class CustomSelection extends DefaultSelection {
...

```

Then you can override the entity query or returned labels.
> 1 votes

---
Tags: 8, ajax
---


# Question
Title: 'Add new comment' link/button does not display on custom entity
A new comment type is created and added as a field (say x\_comments) to a custom entity. The comments show up and work as expected. However when the x\_comments field is configured to disable 'Show reply form on the same page as comments' the 'Add new comment' link/button does not get rendered on custom entity pages. When this setting is enabled the comment form is displayed as expected.

Inspecting Drupal core's Article content type has custom Links pseudo field where 'Add new comment' link/button is displayed. However my custom entity does not have such a pseudo field, even if it did comment module would not know about it.

Please share clues on how to make the 'Add new comment' link/button be displayed?

The buildCommentedEntityLinks method is not being called on custom entity which creates the 'Add new comment' link. Hmm

# Answer
> 0 votes
Turns out one needs to implement field formatter for other entities instead of hook\_links\_alter. So one can perhaps add a Pseudo field formatter to get this markup rendered on custom entities with an approach described here

The hidden gem is noted in the comments for the node entity.

```
  /**
   * Implements hook_node_links_alter().
   */
  function comment_node_links_alter(array &$links, NodeInterface $node, array &$context) {
    // Comment links are only added to node entity type for backwards
    // compatibility. Should you require comment links for other entity types you
    // can do so by implementing a new field formatter.
    // @todo Make this configurable from the formatter. See
    //   https://www.drupal.org/node/1901110.

    $comment_links = \Drupal::service('comment.link_builder')->buildCommentedEntityLinks($node, $context);
    $links += $comment_links;
  }

```

---
Tags: 8, comments
---


# Question
Title: How can I inject relevant/filtered ads every 5th result in a view?
I have a view that displays filtered results of Business Offers and Events as "cards". Now my boss wants to inject relevant custom ads into the results based on the user selected filters (basically, by city/region and business type)...

So, I created another custom content type called "Ads" with fields for multiple images (different sized ads), a click-through url, some tracking details, the type of business the ad is for (like Restaurant, or Venue, etc), and the "market location" (city/region) that the ad would be relevant to display in.

In the twig template file for this view, here are the relevant lines of code that basically inject an Ad placeholder every 5th row:

```
{% for row in rows %}
  {% if loop.index|trim|last == '5' or loop.index|trim|last == '0' %}
    <div class="card ad">
      <div class="ad_tag">AD</div>
      <div class="advert">ADVERTISEMENT</div>
    </div>
    {{- row.content -}}
  {% else %}
    {{- row.content -}}
  {% endif %}
  {# </div> #}
{% endfor %}

```

And here's what that looks like:

My question is...what's the best way to for me to grab as many unique Ads as possible that are filtered by user selected filters (location and/or business type) of the main offers view...and how can I inject these ads like I have in this twig template?

Some approaches I thought of:

1. Have the view's content filter simply get two content types: The original offers...AND the ads. Problem: I don't know how to control the order of these results such that they will render 5 offers, 1 ad, 5 offers, 1 ad etc...
2. Possibly create another view just for the Ads and somehow render only one result of this view for every 5th row of the main view Problem: I don't know how to access and render this data via the twig template, or how to pass the filter values of the main view to the view for the ads via the twig template.
3. Perhaps I could create a attachment view for the ads with filter values inherited from the main view, initially hide all the ads from view via css, and then use Javascript to move the ad card nodes in between the offer cards every 5th card and then set their css to display again. Problem: My page is already rife with Javascript trickery...and this will be yet another computational expense that will slow down client side responsiveness...

OR...is there a much better approach that I'm just ignorant of (which is very likely, as I only just started using Drupal 8+ this summer)?

Thanks in advance for your help.

> *NOTE: I realize there are several questions similar to this one, but my question is slightly unique because I'd like to use a few of the user selectable filter parameters of the main view to filter the ads that I'd like to display/inject every 5th row so they are targeted ads*

# Answer
What you could do is

1. Create view only for ads:
2. Create preprocess function in your custom theme/module that is loaded before the template.
3. In this preprocess function call your view programmatically with the filters from the current page. In Drupal 8 you can set filter programmatically like that:

> ViewExecutable::setHandler($display\_id, $type, $id, $item);

4. Order the results in the way you want and store them in an array variable that you can access from the twig.

Hopefully that helps.
> 2 votes

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


title: JSON:API authorization

text:
# Question
Title: JSON:API authorization
I'm at the early stages of trying to figure out the Drupal JSON API and can't figure out what's causing the following errors.

> Some resources have been omitted because of insufficient authorization.

> The current user is not allowed to GET the selected resource in the response.

Is there a header I should use? It's all local so there's no basic auth to get past.

The code I am using is the following one.

```
  fetch('http://localhost/domain-6/jsonapi/group_content/microsite-group_menu-main', {
    method: 'GET',
    mode: 'no-cors',
    headers: {
      'Accept': 'application/vnd.api+json',
      'Content-Type': 'application/vnd.api+json'
    }
  })
  .then(response => response.json())
  .then(data => {
    console.log(data);
  })
  .catch((error) => {
    console.error('Error: ', error);
  });

```

The response I get is the following one.

```
{"jsonapi":{"version":"1.0","meta":{"links":{"self":{"href":"http:\/\/jsonapi.org\/format\/1.0\/"}}}},"data":[],"meta":{"omitted":{"detail":"Some resources have been omitted because of insufficient authorization.","links":{"help":{"href":"https:\/\/www.drupal.org\/docs\/8\/modules\/json-api\/filtering#filters-access-control"},"item--XQp4cup":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/470ce30a-b29a-440c-9dfd-62ec2ecc2174","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--1lQ8DZL":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/fd53a4be-b3b3-4fba-8739-fe10bf3594b5","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--CMmFdif":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/44e9ad5d-f644-49d4-9b06-8393fe9f179b","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--35OuqGr":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/993f1dd0-7479-42f2-8cdb-4ad37f69b08d","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--S9wgeIz":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/5d327513-431a-47fc-ad60-7d118cb75e28","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--GadCb5C":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/73420f3a-f77b-40bc-b72c-0529c7c4a7e0","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--whX9lWC":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/186316e4-42a5-4551-b3e3-f2d8d0c924b3","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--0ZPeOPd":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/2b309be4-721d-4000-ab31-811b4c04b15e","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--rMyrlTp":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/bdfc8224-04a9-4795-add5-f363c2433229","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--59LnH0e":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/c5dcd0f8-b594-4567-b15f-a0a64b5186b7","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--3WKSiq1":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/9e76a2a3-e121-4650-b83e-f9425c17a4bf","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--jjQ9TP2":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/ae2b6217-be02-49de-9e01-b6c4a9b67e72","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--dhiafhE":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/61119a05-877c-44c8-940b-fab317c0dc5d","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--c7850PO":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/d636ea7f-f453-47e5-9536-c2b8f3f50c92","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--ngkUSZk":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/a0f48948-e846-4d23-9bbf-4d4b7ccc3507","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--2oP7rlR":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/a6625804-294c-493b-a904-fb1f561573c6","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--Sz5wBPb":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/267f6042-ad6e-411e-8925-006877a7c9ad","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--tMDa2oF":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/ccf1fd6a-921e-4144-97bf-e7d85b4dbdf8","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--vYs3zKx":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/7187c2ba-6ee7-435c-9166-3fc0ce8fb421","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--5vJmCru":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/10ba20cc-f0b6-4365-9b34-221452e4a0fc","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--23KRMeA":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/5096f25f-4359-4cb7-9ec0-335b088d24bd","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--exhD7mi":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/be7d1151-8f65-4eef-8fdc-a936a6cde937","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--UISA0FF":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/89fdb64c-6e33-4237-8137-fad965fb7614","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--Fzg9qbP":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/b69894a9-e7b0-4d63-aecb-069ae054c09f","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--qPBbJA1":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/ae6d8f9b-0725-4fee-9c5d-cd25c8bbe583","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--EqTCJ1N":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/f6c7d183-ff1a-497c-9dac-51d7bd927150","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--uF6v1TR":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/b2db9f2e-1907-4fdf-aa49-1370d0bcf54d","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--MV28pBq":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/8313b799-5f10-4416-ba20-49c2de2834a9","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--wVd7rsP":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/346bdf69-93ec-4127-a483-e7d687b20e2d","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--h0VjXuJ":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/052730d5-18f5-47fb-834f-8ed6898031e1","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--fcA5aED":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/fcdebbce-047b-4cfa-a185-67ae7e01262c","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--3dDdcIg":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/450b3119-4d70-41ee-aecf-6ffe77e76f71","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--3XlVwNC":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/917f9200-6a48-40e2-9c56-e3834fe392f3","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--QtN7OYa":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/de886c77-ebd0-41de-a4ee-872113f6afec","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--Q6uGw9H":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/9ede015d-28b5-4e61-a3ad-1a65b0527f34","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--qwxls5H":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/f12a3922-26c2-47b1-8e62-ff7efb9b005a","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--ngPbsJJ":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/e4540be8-f206-46de-94a6-57a5f7b20ab3","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--AnR0WT0":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/2d0a7020-11d4-4536-a446-fc8450a31559","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--AhzT8c6":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/402e1e71-ec7c-46c4-bc5b-a3b925df70b4","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--Wm2v9pb":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/4f475314-c892-43ab-bb0f-5e1d265cc4d0","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--kd0J901":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/78909988-22e9-491d-a47e-229c0a7a225e","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--404rztx":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/a13ff285-dfda-4949-b85d-67af424fdc92","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--G4zUasD":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/afc8da9b-b945-4013-b4e6-7cfda013b57c","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--hC13Nrz":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/61c25a67-06ed-4831-8f25-e4849632a91e","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--PWp8vHM":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/7c3fa94e-4d4c-4a74-b5ee-8909d2481054","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--Nzyn35g":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/cd43deee-5f44-4f23-9708-58163cd8150d","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--IZapjnH":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/5fc7df8b-1429-4c05-b0d0-6042f53992ad","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--Tf1PVbP":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/ae718931-248f-4d8f-9923-626445d66056","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--WIAJdC5":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/804424f6-bf31-4689-a935-dd92082325e2","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}},"item--g2Rs5um":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main\/24d7998f-eca9-4c7e-9ddc-f544e0cab691","meta":{"rel":"item","detail":"The current user is not allowed to GET the selected resource."}}}}},"links":{"next":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main?page%5Boffset%5D=50\u0026page%5Blimit%5D=50"},"self":{"href":"http:\/\/localhost\/domain-6\/jsonapi\/group_content\/microsite-group_menu-main"}}}

```

# Answer
probably no access due to the fact that:

"fetch won’t send cookies"

https://developer.mozilla.org/en-US/docs/Web/API/Fetch\_API/Using\_Fetch
> 1 votes

---
Tags: authentication-authorization
---


title: How do I use the flood interface?

text:
# Question
Title: How do I use the flood interface?
I'm working on a two-factor login form that sends a text message to the user. With a "did not recieve message" button. I would like to limit the number of times a user can request for a new text message.

I know that I can register flood for a certain event like: `\Drupal::flood()->register('eventName', 1800, $user->getUuid())`. The FloodInterface has a `isAllowed($name, $threshold, $window = 3600, $identifier = NULL);` method that, I imagine, tests when a user tries to do a certain `$name` event, it tests if that event has been done more often than the `$threshold` within the `$window`. If so then the method returns `false` and otherwise `true`.

In my example I can register the event somewhere in the chain of events/functions that send the message and also check there if the event `isAllowed`?

Something like so:?

```
public function someFunction() {
  \Drupal::flood()->register('send message', 900, $user->getUuid());
  $token = 12345;
  $this->sendMessage($token);
}

private function sendMessage($token) {
  $is_allowed = \Drupal::flood()->isAllowed('send message', 3, 900, $user->getUuid());
  if ($is_allowed) {
    // Send the message
  }
}

```

What I don't get is why I also have to indicate the `$window` in the `isAllowed` method when I already assign that in the register method?

# Answer
> 4 votes
They are two different parameters that share the same name (and can share the same value by default). Per the docs for `FloodInterface`:

* `::register()`:

+ > int $window: (optional) Number of seconds before this event expires. Defaults to 3600 (1 hour). Typically uses the same value as the isAllowed() $window parameter. Expired events are purged on cron run to prevent the flood table from growing indefinitely.
* `::isAllowed()`:

+ > int $window: (optional) Number of seconds in the time window for this event (default is 3600 seconds, or 1 hour).

The `register()` parameter specifies how long that flood event will be stored (remembered) after the time of registration before `::garbageCollection()` clears it out. The `isAllowed()` parameter specifies the max number of instances that can occur within that window before `isAllowed()` will return `FALSE`

---
Tags: 8
---


# Question
Title: How should I get data from the node table?
I am creating a Drupal Dashboard (PHP 7.2, Drupal 8.9). I'm trying to query the *node* table using the following code.

```
$database = \Drupal::database();

$query = $database->query("SELECT field_advertiser_id_value FROM {node__field_advertiser_id} WHERE entity_id = :entity_id", [
    ':entity_id' => $entity_id
]);

$result = $query->execute();
print_r($result);

```

I am not sure why `$query->execute()` returns `false`. This table exists in the l database.

The database contains both the *node* and the *node\__field\_advertiser\_id* tables.

Inserting data in the *node\__field\_advertiser\_id* works correctly.

```
$database = \Drupal::database();

$database->insert('node__field_advertiser_id')
    ->fields([
      'entity_id' => $advertiser_nid,
      'revision_id' => 1,
      'delta' => 0,
      'field_advertiser_id_value' => $advertiser_id
    ])
    ->execute();

```

I am trying to fetch details from the *node\__field\_advertiser\_id* from `hook_node_delete()`. The data is still there in the table when I try to run the SQL query.

It would be great if anyone can help me out with this.

# Answer
> 2 votes
I don't think you need an execute on a regular query like that. But the way to get any node data would be loading the node with the entityTypeManager service, and getting the field values with $node-\>get('field\_advertiser\_id')-\>getValue(). Its rare to have to get a value that way from the database.

The same goes for the insert or update - **you definitely should be using the API for this** and not direct database writes.

On top of that you don't need to do anything with the database at all in this example, because the node being deleted is already passed to this hook. Just look at its field values. https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Entity%21entity.api.php/function/hook\_entity\_delete/9.1.x

But in a case like this, hook\_entity\_delete may be too late to get values depending on what you need to do. Instead, you probably want to use hook\_entity\_predelete: https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Entity%21entity.api.php/function/hook\_entity\_predelete/9.1.x

Example:

```
function mymodule_entity_predelete(Drupal\Core\Entity\EntityInterface $entity) {
  if ($entity instanceof NodeInterface && $entity->getType() == 'your_content_type') {
    $advertiser_id = $entity->get('field_advertiser_id')->first()->getValue();

    // do something with id, like load that node
    if (isset($advertiser_id)) {
      $entityTypeManager = \Drupal::entityTypeManager();
      $advertiser = $entityTypeManager->getStorage('node')->load($advertiser_id);

      if ($advertiser instanceof NodeInterface && $entity->getType() == 'advertiser_content_type') {
        // Delete this other node.
        $advertiser->delete();
      }
    }
  }
}

```

You have to be careful with these hooks though, because it can trigger an infinite loop since these hooks are fired every time the actions are performed. Meaning, if you wanted to delete the advertiser node here for example, this hook would get called on that action too. So you need to be specific with your conditions check each value and logic. People get caught in presave hooks all the time.

---
Tags: 8, database
---


# Question
Title: How to programmatically get webform drafts for a specific user?
How to fetch all the webform drafts saved based on user id?

I tried the below query and it gives me all submission id's but how do I get just the drafts

```
$query = \Drupal::entityQuery('webform_submission')
      ->condition('uid', 123)
      ->condition('webform_id','test_1');

```

# Answer
You could try this:

```
$query = \Drupal::entityQuery('webform_submission')
  ->condition('uid', 123)
  ->condition('webform_id, 'test_1')
  ->condition('in_draft', TRUE);

$entity_ids = $query->execute();

```

Or via the `EntityTypeManager`:

```
/** @var \Drupal\webform\WebformSubmissionInterface[] $submissions */
$submissions = \Drupal::entityTypeManager()
  ->getStorage('webform_submission')
  ->loadByProperties([
    'uuid' => 123,
    'webform_id' => 'test_1',
    'in_draft' => TRUE,
  ]);

```
> 4 votes

---
Tags: 8, webforms
---


# Question
Title: Get media image url in views-view-unformatted template
I'm using drupal 8, in my template I created a file named **views-view-unformatted--block-12.html.twig** and this is it's content:

```
<div class="row list">
{% for row in rows %}
  {%
    set body = row.content['#row']._entity.get('body').value|striptags|render
  %}
  {%
    set title = row.content['#row']._entity.get('title').value
  %}
      <div class="col-md-3">
        <div class="promotion-slider-item">
          <p class="mb-0">
            <img alt="Title" data-src="/themes/porto/img/abc/maxresdefault.png" class="lazy-load">
          </p>
          <div class="inner">
            <p class="title mb-1">{{ title }}</p>
            <div class="desc">{{ body|length > 70 ? body|slice(0, 70)|raw ~ '...' : body|raw }}</div>
            <a href="/node/{{row.content['#row']._entity.get('nid').value}}" title="{{row.content['#row']._entity.get('title').value}}" class="btn-read-more"><span class="">Read more</span></a>
          </div> <!-- end .inner -->
        </div> <!-- end .promotion-slider-item -->
    </div> <!-- end .col -->
    {{dump(row.field_images)}}
{% endfor %}
</div>

```

I try to **dump(row.field\_images)** but null and **dump(file\_url(row.content\['#row'\].\_entity.get('field\_images').value\['0'\]\['target\_id'\]))** but url is **/id\_sample**. How do I fix it?

# Answer
> 1 votes
I did with this code `row.content['#row']._entity.YOUR_FIELD_NAME.entity.fileuri`

---
Tags: views, theming, media
---


# Question
Title: Entity browser or Media Library for PDF uploads?
Client has a custom Content Type for downloadable PDF document detail pages, which contains an entity reference to the File media type.

They want *Full* and *Teaser* view modes to display a thumbnail image, user-friendly title (not PDF filename), and link to download.

I added the `field_media_image` to the media file bundle, and I used the media file's Name field for the "friendly name" of the file.

The image and name could be edited by clicking the pencil icon on the image field to edit the media file directly, but it is a better user experience for content editors to edit the media file attributes from the detail page node, along with the rest of the document details.

If I change the field widget for the entity reference in the detail page's form display from **Media library** to **Entity browser** these fields are easy to edit from the detail page node by clicking the Edit button.

This works well for editing existing content, but this widget is not configured correctly for uploading new PDF content:

There are some customizable widget settings in the form display configuration, but nothing that seems useful for my purposes.

The client is accustomed to uploading PDFs using the **Media library** widget, which looks like this:

Unfortunately, the **Media library** widget does not allow user to edit fields on the media file bundle. This widget does not offer any customizable settings in the form display configuration.

Is it possible to write a small Drupal 8 module to use the **Media library** widget on this field if it is empty (or `NULL`), but use the **Entity browser** widget on this field if a PDF has been uploaded?

That would be ideal. If it's not possible, perhaps someone can help me to configure the **Entity browser** widget to behave more like the **Media library** widget for PDF uploads.

**EDIT:**

I think I might be on the right track:

**Home \>\> Administration \>\> Configuration \>\> Content authoring \>\> Entity Browsers**

I'm trying to add a new PDF Entity Browser. But when I use that, I get the following error message:

What am I missing?

# Answer
Going to Home \>\> Administration \>\> Configuration \>\> Content authoring \>\> Entity Browsers and creating a new entity browser is the right way actually.

But you probably have attached a media entity field to your content type therefore you must configure your new entity browser to be used on the **media form display** level.

Lets say your new entity browser is called *Document browser* you should go to the **media form display** for *document media* and make use of the new entity browser.

Here is a screenshot that might help understand better

The content type form display widget for the respective field should remain as **Media library** in order to pick up the media library from previous step.
> 1 votes

---
Tags: 8, media, files
---


# Question
Title: How do I limit use of Simple XML Sitemap to certain roles?
For my **Page** content type, I want nodes created by administrators to be in my Simple XML Sitemap, but I want to exclude nodes created by non-admin users.

Administrators have permission to administer Simple XML Sitemap, and regular users do not.

On the Page content type settings, I have checked the box to index nodes of type Page by default. (You must enable indexing by default in order to get the Simple XML Sitemap settings widget on the node edit screen.)

I tried to change the settings in `hook_form_alter()`, but it doesn't work because non-admin users do not ever see the Simple Sitemap widget (it is not attached to the form if the user does not have admin permission for Simple XML Sitemap).

So I'm stuck:

* I have to enable indexing by default on the node edit page to get the widget to appear.
* Non-admin users do not have access to the widget, so I cannot change the value for non-admin users with `hook_form_alter()` (I have to accept the default).

How do I resolve this paradox?

# Answer
> 0 votes
I found the answer based on this drupal.org comment.

You have to use `hook_node_insert()`.

```
/**
 * Implements hook_node_insert() for MYMODULE.
 */
function MYMODULE_node_insert(EntityInterface $node) {
  switch ($node->getType()) {
    case 'page':
      $user = Drupal::currentUser();
      if (!$user->hasPermission('administer content')) {
        // Turn off Simple XML sitemap.
        $generator = \Drupal::service('simple_sitemap.generator');
        $generator->setEntityInstanceSettings('node', $node->id(), ['index' => FALSE]);
      }
      break;
  }
}

```

---
Tags: xml-sitemap
---


# Question
Title: Split custom form into smaller functions for re usability
I've a created a custom form in custom module. I have couple fields in the form that can be reused to other form. Also as per the coding standards, the function should not be more than 150 lines. So I tried to split the form into multiple (2) functions. But that didn't work. (the second form didn't render into the first form) Here is what I tried

```
  public function buildForm(array $form, FormStateInterface $form_state) {
    $form['some_element'] = [
       '#type' => 'textfield',
    ];
    ..
    ..
    if ($some_condition_exists) {
    //include second form here.
    $this->secondForm($param);
    }
    return $form;
    }

  public function secondForm($param) {
       $form['some_element'] = [
       '#type' => 'textfield',
       '#default_value' => $param
    ];
    return $form;
 }

```

Do I really need to put this form into another Form file and call `\Drupal::formBuilder()->getForm('Drupal\module\Form\xxxForm');` ?

# Answer
> 6 votes
You just seem to be getting confused with the form build concept and PHP logic - what you need to do is add to the existing form array (`$form` in `buildForm`), not create a second separate form/array.

With that in mind, this:

```
$this->secondForm($param);

```

Doesn't do anything. You're not merging the return value back into `$form`, and you're not passing `$form` to `secondForm`, so that line and the lines in `secondForm` are basically a no-op.

There a bunch of different ways PHP will let you do what you need to, probably the simplest would be to pass the `$form`:

```
public function secondForm($param, array &$form) {
  $form['some_element'] = [
    '#type' => 'textfield',
    '#default_value' => $param
  ];
}

```

And then

```
$this->secondForm($param, $form);

```

As an aside, it might be wise to rename `secondForm` to something like `addAdditionalElements`, just to avoid future confusion.

# Answer
> 0 votes
Without any details about what "it's not working" means, and if this is your real code, you call `$this->secondForm()` which returns something, but you don't set it to a variable.

If you update that line to something like `$form = $this->secondForm();`, this should technically work.

---
Tags: 8, forms
---


# Question
Title: Skip updating certain themes/modules on composer update
My setup is composer driven.

I have overridden some module with patches and customised a contributed theme (boostrap\_sass).

Now I want to avoid overbidding these modules and theme when I perform composer update in console, so as to skip updating those modules and themes and perform update on only the modules, themes and core that I want to update via composer.

# Answer
If you want to pin a version of a module you could do so by running this in your console:

```
composer require drupal/module_name:1.1

```

In case you have many modules also see my contributed module: Composer Update Assistent

This module generates a commando to update all modules to the recommended version which can be copied to your console.
> 1 votes

---
Tags: 8, composer
---