title: Add All link on view pager

text:
# Question
Title: Add All link on view pager
Is there a way to add a Show All Link on my view pager.

I have a Block View.

I would like to be able to add an All Link button when the number of Items reach 5. I do not need the pager "Next, last 1,2,3" only the All Link button.

Thanks a lot

# Answer
Yes see the `Pager Options` for your Block or Views Page:

**EDIT:**

Allow me to clarify. This gets the `- All -` text option into the configuration of the View. If you want custom business logic that says if there is 5 or more results I want to show/hide the Next and Previous and include the All options I recommend you look at `hook_views_pre_view()` or other Views hooks to implement hiding/removing options as needed. For ease I recommend hiding/showing the options using CSS or Javascript.

Again, this option builds the option into the page -- but doesn't really enforce your business logic.

**EDIT 2:**

The module Pagerer might get you a little further, but probably not all of your custom logic is possible out of the box with that module. It has an API to fine-grain it's custom pager views.
> 0 votes

# Answer
If you want to do this via coding then this can be helpful to you. I've used this in my own projects.

1. Add a new file with ".views.inc" extension. Preferred module\_name.views.inc.
2. Theme the block form and add a variable to the views output via HOOK views\_pre\_render.
3. Then with the HOOK\_pre\_build function you can modify the pager items per page.

````
function MYMODULE_views_pre_render(&$view) {
  if ($view->name = 'your_view') {
    $view->attachment_after['show_all'] = l('Title', 'show-all', array('query' => array('view_name' => $view->name)));
  }
}

function MYMODULE_views_pre_build(&$view) {
  if ($view->name == 'your_view' && (isset($_GET['view_name']) && $_GET['view_name'] == $view->name)) {
    if (arg(0) == 'show-all') {
      $view->pager['items_per_page'] = 200; //The number you want to have.
    }
  }
}

````
> 0 votes

# Answer
I think the Views Flexible Pager module should solve this for you: https://www.drupal.org/project/views\_flexible\_pager
> 0 votes

---
Tags: views, paging
---


# Question
Title: I can't set a default value on hook_form_alter()
I have form alter for the `user_register_form` which is called. In this hook, I try to set a default value for a textfield but it doesn't work. I've tried all the syntax possible `#default_value`, `#value`, `value`, `default_value`, event in the attributes array.

This is my code.

```
function pre_registration_form_user_register_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  $form['field_code_postal']['#cache']['max-age'] = 0;
  $form['field_code_postal']['#default_value'] = 'COUCOU';
  $form['field_code_postal']['#value'] = 'COUCOU';
  $form['field_code_postal']['value'] = 'COUCOU';
  $form['field_code_postal']['value']['#value'] = 'COUCOU';
  $form['field_code_postal']['value']['#default_value'] = 'COUCOU';
  $form['field_code_postal']['value']['value'] = 'COUCOU';
  $form['field_code_postal']['value']['default_value'] = 'COUCOU';
  $form['field_code_postal']['default_value'] = 'COUCOU';

  $form['field_code_postal']['#attributes']['value'] = 'COUCOU1';
  $form['field_code_postal']['#attributes']['#value'] = 'COUCOU2';
  $form['field_code_postal']['#attributes']['default_value'] = 'COUCOU3';
  $form['field_code_postal']['#attributes']['#default_value'] = 'COUCOU4';
}

```

How can I set a default value?

# Answer
According to the documentation for `hook_form_alter()` you can use code similar to the following one.

```
 $form['field_code_postal']['widget'][0]['value']['#default_value'] ='COUCOU1';

```

If you want to, for example, fill an entity referenced field, you can use the following code.

```
$form['field_image']['widget'][0]['target_id']['#default_value'] = $file;

```
> 11 votes

# Answer
For my case, I wanted a default automatic node title for a custom content type.

In `hook_form_FORM_ID_alter()`, I added a line containing `kint($form['title']);` which allowed me to see the array structure for the node title field. Then, I changed the default value for the node title with the following code.

```
$form['title']['widget'][0]['value']['#default_value'] = 'my custom node title';`

```
> 2 votes

# Answer
To set the default value in `hook_form_alter()`, make a dump of `$form`. You will see the array structure and what you need to target.

In my case with a webform form, actually I can do this.

```
/**
 * Implements hook_form_alter().
 */
function my_module_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  if ($form_id == 'form_submission_lorem_add_form') {
    if (isset($form['elements']['nom'])) {
      $form['elements']['nom']['#default_value'] = 'hello world!';
    }
  }
}

```

The form markup then includes the following lines.

>
> 0 votes

# Answer
**In Drupal 8:**

```
$form['field_name']['widget']['#default_value'][0] = 'value';

```
> -2 votes

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


# Question
Title: How to redirect all images in the files folder to a default file?
Because we work with DTAP environments, our developers work on their own laptops. To get a good view of the live environment, we copy the PROD database and the files to our own laptop. However, this takes up a lot of space because of all the files directories.

What I'd love to have is a simple .htaccess or Apache redirect rule that points all files in the files directory to one default image, so that we don't have to copy the files directory contents (which can be gigabytes sometimes).

There is one exception though for JS/CSS files and files in the files/styles directory. Those callbacks must keep working.

Any thoughts on *if* and *how* this would be achievable?

# Answer
> 1 votes
You could use the stage\_file\_proxy module or cdn module

# Answer
> 0 votes
I don't know if this suits your problem, but the module "Media placeholder" replaces the missing images with a default image. Thus, you would not have to always update the pictures folder.

> This module will replace missing(404) images with a placeholder image.

---
Tags: files, redirection, .htaccess
---


# Question
Title: How do I replace a string when altering the output of a view field?
I have some text I'd like to alter in the custom output of a view field. For example, if the output contains *pancakes*, I'd like to replace it with *waffles*.

The following code doesn't work.

```
{{ field_recipe_prep__value | replace({'pancakes': "waffles"}) }}

```

How do I replace a string when altering the output of a view field?

# Answer
Remove the `__value` part.

```
{{ field_recipe_prep | replace({'pancakes': "waffles"}) }}

```
> -1 votes

---
Tags: views, 8
---


# Question
Title: How to get comment cid in hook_comment_insert?
I've read the answers to How to get the node id the comment is attached to in hook\_comment\_insert? and found how to extract the `nid` of the node the comment is associated with.

However, I also need to get the `cid` of the comment itself from inside `hook_comment_insert()`. How do I do that?

# Answer
All entities have an `id()` method:

```
$cid = $entity->id();

```
> 3 votes

---
Tags: comments, 9
---


# Question
Title: How to patch your htaccess file correctly in composer.json
After running `composer update` my Drupal installation gets updated, but it also changes the content of the .htaccess file each time.

How can I correctly patch it?

# Answer
In the `scripts` section of your composer.json file, use the following:

```
"post-drupal-scaffold-cmd": [
  "patch -p0 < patches/htaccess.patch"
]

```

(this requires "drupal-composer/drupal-scaffold" in your `require` section)
> 5 votes

# Answer
Here is how I have successfully done it. This is for Drupal 8.9, but it should work for a typical composer setup.

Make sure you have patching enabled and ready to go, and know how to make a patch for drupal.

## Short version:

Patch the **core/assets/scaffold/files/htaccess** file and scope the patch as **'drupal/core'**.

## Long Version:

There are probably variations in method, but this is the gist of it:

1. Within the \[web-root\] folder create a temporary repo (if there is not already one there); e.g. `cd html && git init`
2. Add the core file to the repo; e.g. `git add core/assets/scaffold/files/htaccess`
3. Make your changes to `core/assets/scaffold/files/htaccess` and save it
4. Save the patch; e.g. `git diff core/assets/scaffold/files/htaccess > ../patches/htaccess.patch`
5. If you are not still using your repo, delete it; e.g. `rm -Rf [web-root]/.git`
6. Move back to your composer directory
7. Add the patch to the composer file "patches" section

```
"patches": {
  "drupal/core": {
      "Description of patch": "patches/htaccess.patch"
  }
}

```
8. Run `composer install`

That should patch the core "htaccess" file, and then the normal scaffolding flow will copy that file over to your \[web-root\]/.htaccess file. Output should look similar to:

```
Gathering patches for root package.
Loading composer repositories with package information
Installing dependencies (including require-dev) from lock file
Package operations: 1 install, 0 updates, 0 removals
Gathering patches for root package.
Gathering patches for dependencies. This might take a minute.
  - Installing drupal/core (8.9.13): Loading from cache
  - Applying patches for drupal/core
    patches/htaccess.patch (Description of patch)
...
Scaffolding files for drupal/core:
 - Copy [web-root]/.htaccess from assets/scaffold/files/htaccess

```
> 7 votes

---
Tags: 8, composer
---


title: Alter block content

text:
# Question
Title: Alter block content
I am trying to alter a custom module block, without success. My block class is the following.

```
class BreadcrumbBlock extends BlockBase {

public function build() {
    return [
        '#theme' => 'breadcrumb_block',
        '#content' => array('value' => 1),
        '#cache' => array(
                'contexts' => array('url'),
        ),
    ];
 }
 .....

```

In `hook_block_view_alter()` where can I alter the block? Where are `$data['subject']` and `$data['content']` in a block?

# Answer
> 6 votes
I think you must use a 'pre\_render'. Some thing like : http://www.drupal8.ovh/en/tutoriels/150/change-drupal-8-powered-by-block

```
use Drupal\Core\Render\Markup;

function yourmodule_block_view_alter(array &$build, \Drupal\Core\Block\BlockPluginInterface $block) {
  if ($block->getBaseId() === 'system_powered_by_block') {
    $build['#pre_render'][] = '_yourmodule_block_poweredby_prerender';
  }
}

function _yourmodule_block_poweredby_prerender(array $build) {
    $build['content']['#markup'] = Markup::create('Your text');
    return $build;
}

```

# Answer
> 5 votes
Another method which can be used to alter any block without additional #pre\_render callback. Note that custom callback function is called after the system Drupal\block\BlockViewBuilder::preRender() and may be not rendered properly. For example, if you add content to an empty block using the method from the above comment then the title of the block will be lost.

Go to the **src** folder in your module and create **OverrideAnyBlockContent.php** file with the following content:

```
<?php

namespace Drupal\YOUR_MODULE;

/**
 * Helper class to override any block content in a HOOK_block_view_alter().
 */
class OverrideAnyBlockContent {

  /**
   * The render array.
   *
   * @var array
   * @see https://www.drupal.org/docs/8/api/render-api/render-arrays
   */
  protected $renderArray;

  public function __construct(array $render_array) {
    $this->renderArray = $render_array;
  }

  public function getPlugin() {
    return $this;
  }

  public function build() {
    return $this->renderArray;
  }

}

```

Then in **YOUR\_MODULE.module** file:

```
function YOUR_MODULE_block_view_alter(array &$build, Drupal\Core\Block\BlockPluginInterface $block) {
if ($block->getBaseId() === 'system_powered_by_block') {
    // @see https://www.drupal.org/docs/8/api/render-api/render-arrays
    $render_array = [
      '#type' => 'markup',
      '#markup' => "Any text, HTML, {$php_variable}, whatever, ....",
    ];

    // Uncomment the line below if you need to merge new render array with the
    // original one.
    // $render_array += $build['#block']->getPlugin()->build();

    // Now block will be pre-rendered using new render array. No additional
    // '#pre_render' callback is required.
    // @see Drupal\block\BlockViewBuilder::preRender()
    $build['#block'] = new Drupal\YOUR_MODULE\OverrideAnyBlockContent($render_array);
  }
}

```

Taken from examples module issue: https://www.drupal.org/node/2893964

# Answer
> 1 votes
The simpliest way would be to use a preprocess hook like so:

```
function MODULE_OR_THEME_NAME_preprocess_block__BLOCK_ID(&$variables, $hook) {
   // block render array is here to be altered => $variables['content']
}

```

---
Tags: 8, blocks, hooks
---


# Question
Title: Why a module has a package for a 8.x-3.0-beta1+54-dev version?
I'm currently in the process of composerizing a quite old project where I ran into some strange module versions, which seem to have been packaged by drupal, ie.

```
# Information added by Drupal.org packaging script on 2019-03-28
version: '8.x-3.0-beta1+54-dev'
core: '8.x'
project: 'field_group'
datestamp: 1553812385

```

or

```
# Information added by Drupal.org packaging script on 2017-03-13
version: '8.x-1.1+1-dev'
core: '8.x'
project: 'login_emailusername'
datestamp: 1489402385

```

In the case of login\_emailusername, it turned out that the code of the project is identical to 8.x-1.1 plus 1 follow-up commit from dev, and so could be brought into the project via composer as:

```
"drupal/login_emailusername": "1.x-dev#f152ee7"

```

However, I wonder how these modules were "packaged" in these versions, because they don't seem to exist. Maybe someone has an idea? Might this be complete custom syntax or where they maybe imported via drush:make?

# Answer
The *8.x-3.0-beta1+54-dev* version means the development snapshot is 54 commits ahead of the *8.x-3.0-beta1* version. Similarly, *8.x-1.1+1-dev* means the development snapshot is one commit ahead of the *8.x-1.1* version. Since development snapshots can change much during time, it is probably a way to make better understand how much the development snapshot changed.

Notice that *8.x-3.0-beta1+54-dev* is the version reported from the page listing all the installed modules or in the .info file for the module (as you shown, between the lines added by the Drupal.org packaging script).

> (The screenshot is for Drupal 7.)

It's not the title you see for a release node in the page showing all the releases (for the *Field Group* module, in this case), where the release is field\_group 8.x-3.x-dev.

To answer your question, it's not a custom syntax. It's the version that the Drupal packaging script adds in the .info file for development snapshot releases, which could then be used in the user interface when showing the installed project version.

The Drupal.org packaging scripts alter the .info file when creating the archive for the project. If the project is copied using Git or Composer, for example, that information is not present, and the version wouldn't appear as *8.x-3.0-beta1+54-dev*. (This is also the reason why I took the screenshot from a Drupal 7 instance created on simplytest.me.)
> 1 votes

---
Tags: community-official-site
---


# Question
Title: Address book not visible to registered users
We are using drupal commece 2 on drupal 8 and I noticed, that `/user/xx/address-book/` only works for the admin user, not for normal registered ones.

It appears to be a permission issue, but under `/admin/people/permissions` I can not find anything billing / shipping / address related.

Anybody knows which box to tick?

# Answer
Ok, after checking a bit the commerce module, it looks like this is tied to profile view permissions and also addresses are linked to customer profiles.

I made it work by enabling the following **Profile** permissions under `/admin/people/permissions`:

* Customer: Create profiles (enables to create new addresses)
* Customer: Delete own profiles (enables to delete addresses)
* Customer: Update own profiles
* Customer: View own profiles (enables the address-book tab for registered customers)
* View own profiles (just in case)
> 5 votes

---
Tags: 8, commerce
---


title: How to uninstall OAuth2 OR fix it

text:
# Question
Title: How to uninstall OAuth2 OR fix it
i'm new in Drupal.

I tried on my site to mount the OAuth2 world but I can't get to the top. The website shows an error that is

> Error: Interface 'League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface' not found in include() (line 13 of modules/simple\_oauth/src/Repositories/AccessTokenRepository.php).

same applies if I try to manually uninstall the module via ssh with **drush pm-uninstall simple\_oauth oauth2\_server** it is impossible:

> In Connection.php line 896:
> SQLSTATE\[42S02\]: Base table or view not found: 1146 Table 'site\_drupal.oauth2\_token' doesn't exist: SELECT "base\_table"."id" AS "id", "base\_table"."id" AS "base\_table\_id" FROM
> {oauth2\_token} "base\_table"
> LIMIT 1 OFFSET 0; Array
> (
> )
>
> In StatementWrapper.php line 116:
> SQLSTATE\[42S02\]: Base table or view not found: 1146 Table 'site\_drupal.oauth2\_token' doesn't exist

Basically: the site (or the admin section to uninstall the module) cannot be accessed unless this module presents this kind of error, and via ssh I can't figure out how to remove it.

I thank you for any kind of help.

# Answer
You can do the following:

1. Run `drush cex` to export your configuration to the config directory.
2. Find the core.extensions.yml file in the configuration directory. Edit it, and remove the line that says: `oauth2_server: 0`
3. Run `drush cim`  to import your configuration. It should disable that module.
> 1 votes

---
Tags: drush
---


title: How to create a protected RSS feed

text:
# Question
Title: How to create a protected RSS feed
We are planning to provide an RSS feed (from a Drupal site) to someone who is going to display it on their site. Is there a way to limit access to only this specific site or maybe provide an embed code they can use to display the feed on their site?

# Answer
> 0 votes
If you use Views to provide it then you can limit it by role/ permission (which will tie your user to a REST login process), else the Views RSS Feed Authentication module allows you to utilise basic auth or oauth.

There are plenty of options, but what you do will depend on how you intend to implement the feed.

---
Tags: feeds
---


# Question
Title: How can I have a taxonomy list that shows parent child relationship, but not allow the parent to be selected?
Now and then I will come across a Drupal site that shows taxonomy terms to be selected in a drop down list. Example:

```
Fruit
    apple
    pear
Meat
    pork
    beef
..etc

```

And, while the child of each parent is selectable, the parent is not. How is this achieved, and how can I apply it to my taxonomy vocabulary who terms are 1 child deep?

# Answer
> 1 votes
Download and install this module http://drupal.org/project/term\_reference\_tree and check the Leaves only(Don't allow the user to select items that have children) option under manage fields-\> click edit on taxonomy field.

It will select only the children not parent.

# Answer
> 1 votes
@Bala has a great answer for the node edit form, but it doesn't help with views filters or other front end displays. For views filters try https://drupal.org/project/better\_exposed\_filters combined with the code I gave for another question. This allows your view to limit the selection of parents on the filter and is also keyboard accessible.

I will be working on a node hierarchical DISPLAY today and when completed I will post it as a comment on my answer that I linked above.

# Answer
> 0 votes
Use the Simple Hierarchical Select module.

From the project page:

1. Create a new field (type "Term reference") and select "Simple hierarchical select" as widget type.
2. In the field settings, check the box *Force selection of deepest level*. This forces users to select terms from the deepest level, the leaf nodes in the tree.

The code looks cleaner than Client-side hierarchical select, which is another option. Both modules are available for use in Drupal 8 and 9. I don't recommend the original module, Hierarchical Select because there's no upgrade path beyond Drupal 7.

*Thanks to @mparker17 for help on this one.*

---
Tags: 7, taxonomy-terms
---


# Question
Title: Clearing the render cache after being programmatically logged in
This issue is on a Drupal 8 site. I have no reason to think it would behave any different on Drupal 9.

We have a scenario where we are having trouble with caching of a custom block.

This block replicates the behaviour of the Login block in that it checks for an authentication state and presents either the `user.login` or `user.logout` route.

This looks to be working.

We also have a login state happening where a user account can be "Not Quite Logged In" (NQLI). We are extending Terms with an IP field and adding an IP Login User user reference field. When a user hits the site and they are not logged in we check if their IP matches one mentioned on the Terms and if one matches the referenced user is programatically logged in.

When a NQLI user is logged in the Login block still presents a "Log in" link. This is the desired behaviour so the user can still access the login page to log in as an actual personal account.

This is working also.

When a user has accessed the site from a known IP and is logged in as a NQLI user, clicks the "Log in" link, logs in as their own account and is redirected back to the page they came from they are successfully logged in as that personal account.

This process works.

What doesn't work is that the "Log in" menu item is still present in that block on this page. If you click off to any other page the "My account" and "Log out" links are there as expected. If you click back to the page you logged in from it still holds the "Log in" link.

We've tried clearing the cache with `\Drupal::service('cache.render')->invalidateAll();` in `hook_user_login()` and `hook_preprocess_html()`.

This is one of my first Drupal 8+ projects. I've been working with Drupal since 4.6 and am very much feeling like a junior dev once again. I'm hoping that there is an obvious place or process to do this that I've just not been able to find. My google-fu has reached its limit on this one.

# Answer
> 1 votes
You need to add cache contexts to your block. Cache contexts tell Drupal that the block needs to be cached differently for different circumstances. In your case, whether or not the user is logged in. you can read more about it here: https://www.drupal.org/docs/drupal-apis/cache-api/cache-contexts

---
Tags: 8, blocks, caching
---


title: create link to download file

text:
# Question
Title: create link to download file
I'm adapting a drupal module to my need. It permit to upload a pdf to insert it into a node add form in an iframe to display the file. I've need also to insert a link to download the previous uploaded file.

I've read and try thousand of guide and documentation but nothing that work or adapt to my scope.

Is possible to find an easy way to create by code a download link?

# Answer
It sounds like you're looking for the PDF Reader module.

> PDF Reader will render your entity file fields with the selected PDF renderer.
>
> The implemented PDF renderers are:
>
> * Google Docs Viewer
> * pdf.js - PDF Reader in JavaScript
> * Direct PDF embed
> * Microsoft Office Web Viewer(7.x-2.x-dev)

This module lets you specify any file field to render as a PDF embedded in a node. It also has an option to display download link for the embedded PDF.
> 0 votes

# Answer
You can use below HTML to download files:

```
$content = "<a href='FILE LINK' download='FILE NAME TO DOWNLOAD WITH EXTENSION'">Click here to download</a>';

```
> -1 votes

---
Tags: files
---


# Question
Title: Webform/SwiftMailer - TypeError in encodeString() - must be of the type string, array given
I got into an issue while using a combination of SwiftMailer and Webform on Drupal 9. I found nothing helpfull while Googling and it took me a few days to figure out the issue. Hence I am sharing my findings with you.

**My context:**

* Drupal 9.1.3
* Webform 6.0.2 - with an Email Notification Handler
* MailSystem 8.x-4.3 - Module: Webform / Formatter: Swift Mailer / Sender: Swift Mailer
* Swift Mailer 8.x-2.0 - set up to send SMTP emails

**The problem:**

```
The website encountered an unexpected error. Please try again later.
TypeError: Argument 1 passed to Swift_AddressEncoder_IdnAddressEncoder::encodeString() must be of the type string, array given, called in /home/www/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php on line 378 in Swift_AddressEncoder_IdnAddressEncoder->encodeString() (line 32 of vendor/swiftmailer/swiftmailer/lib/classes/Swift/AddressEncoder/IdnAddressEncoder.php).

```

**How the problem manifested:**

* the error above was displayed on the website's footer
* none of the AJAX driven features of the website didn't work - the ones from the admin interface, nor the ones in the front-end

# Answer
**The cause and my solution:**

In the Webform's email handler configuration -\> "*Send from (website/domain)*" section -\> "*From email*" field, I used the `[site:mail]` token.

In **EsmtpTransport.php** line 378, the $address parameter had a value of `["email@example.com" => string(0)]`.

By modifying the "*From email*" field to "*Site email address*" solved my problem.

Hope this helps.
> 0 votes

---
Tags: webforms
---


# Question
Title: Find a URL inside Twig file from content made from another content type (referenced/ has a relationship)
I'm new to Drupal, and have stumbled across this issue. I have entries (entry) that are blogs held inside a blog\_listing. E.g.

```
{% for entry in content.field_blog_listing_entries|field_target_entity %}
    <h2>{{ entry.title.value }}</h2>
    <p>{{ entry.field_blog_excerpt.value }}</p>
{% endfor %}

```

The blogs are their own entries that have automatically generated URLs. e.g. Blog 1 can be found on blogs/blog-1.

How do I get this URL from within the code block above?

I want to access the value in the same manner that I do with the `title` and `field_blog_excerpt` to put into an anchor tag. I've tried the same method e.g. `entry.url.value` & `entry.field_blog_url.value` but these don't return anything.

How do I access this URL value? Any help appreciated.

# Answer
> 0 votes
You can use path function with the route for node (entity.node.canonical) and the entry.id (node id) for your blog entry. Pass the id in with the "node" parameter.

`<a href="{{ path('entity.node.canonical', {'node': entry.id}) }}"></a>`

More info here: https://www.drupal.org/docs/theming-drupal/twig-in-drupal/functions-in-twig-templates

---
Tags: uri
---


# Question
Title: Why does rest export give bad result when executed in batch?
In a custom module (association), I have two content entities (member and person).
A function `_export_association()` is called at the end of each hook\_entity (insert, update, delete) in order to run the corresponding View that export as csv the content of the entity:

```
function _export_association($entityType)
{
  switch ($entityType) {
    case "member":
      _export_association_CSV('association_members', 'rest_export_1');
      _export_association_CSV('association_persons', 'rest_export_3');
      break;
    case "person":
      _export_association_CSV('association_persons', 'rest_export_1');
      _export_association_CSV('association_persons', 'rest_export_2');
      break;
    default:
  }
  _export_association_CSV('association_members_and_persons', 'rest_export_1');
}

function _export_association_CSV($view, $page)
{
  $view = Views::getView($view);
  $view->setDisplay($page);
  $path = $view->getPath();
  $data = $view->preview($page)['#markup'];
  file_unmanaged_save_data($data, 'private://' . $path, FILE_EXISTS_REPLACE);
}

```

When it is executed from CRUD forms, everything is OK.
For example, here is the first 3 lines of export\_members.csv:

```
id,designation,addresssupplement,street,postalcode,city,country,telephone,status,startdate,enddate,contact_id,comment,owner_id,created,changed
4,Adhérent004,,,78700,,,,0,2010-01-31,2014-12-31,,,,,
6,Adhérent006,,,78300,,,,0,2010-01-31,2013-12-31,,,,,

```

I have also a MemberImportStatus that allows to select a file with a list of members to run on each one an update, this being done using batch API.
And here is the problem! In that case, the export is not properly formatted.
The same export\_members.csv is giving:

```
[{"id":[{"value":4}],"designation":[{"value":"Adh\u00e9rent004"}],"addresssupplement":[],"street":[],"postalcode":[{"value":"78700"}],"city":[],"country":[],"telephone":[],"status":[{"value":0}],"startdate":[{"value":"2010-01-31"}],"enddate":[{"value":"2014-12-31"}],"contact_id":[],"comment":[],"owner_id":[],"created":[],"changed":[]},{"id":[{"value":6}],"designation":[{"value":"Adh\u00e9rent006"}],"addresssupplement":[],"street":[],"postalcode":[{"value":"78300"}],"city":[],"country":[],"telephone":[],"status":[{"value":0}],"startdate":[{"value":"2010-01-31"}],"enddate":[{"value":"2013-12-31"}],"contact_id":[],"comment":[],"owner_id":[],"created":[],"changed":[]}

```

Any idea what is wrong?

EDIT:
Some details about the Views... The format they use is "Serializer" (from module csv\_serialization) with 'csv' as 'Accepted request formats'.
As far as I understand, the 'wrong' format when the Views are run from batch appears to correspond to the 'json' request format...

EDIT2:
as requested by @sonfd, my batch API code:

```
$fileId = $form_state->getValue('file_to_import')[0];
$file = File::load($fileId);
$filename = $file->filename->value;

$members = file('sites/default/files/_private/' . $filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$file->delete();
$temp = array_shift($members);
$operations = [];
foreach ($members as $member) {
  $operations[] = ['\Drupal\association\Form\MemberImportStatus::updateMember', [$member]];
}

$batch = [
  'operations'       => $operations,
  'title'            => t('Members Status Import'),
  'init_message'     => t('Members status import is starting.'),
  'progress_message' => t('Processed @current out of @total. Estimated time: @estimate.'),
  'error_message'    => t('The importation process has encountered an error.'),
  'finished'         => '\Drupal\association\Form\MemberImportStatus::end_of_update',
];
batch_set($batch);

```

with an example of input file:

```
id;status;enddate
104;0;2014-12-31
205;0;2014-12-31
400;0;2014-12-31

```

and the code for operation:

```
public static function updateMember($member, &$context)
{
  $aTemp = explode(";", $member);
  $storage = \Drupal::entityTypeManager()->getStorage('member');
  $member = $storage->load($aTemp[0]);
  if ($member) {
    $member->set("status", $aTemp[1]);
    $member->set("enddate", $aTemp[2]);
    $member->save();
    $context['results'][] = $aTemp[0];
    $context['message'] = t('Updating status for member @id', array('@id' => $aTemp[0]));
  }
}

```

The purpose is to "mass"-update the entity (one member per batch operation).
The View that outputs results as a csv file is triggered by hook\_entity, the same way it is when I update the entity thru CRUD form...

# Answer
Finally, I found this solution: I installed Views data export and I changed "Format" from 'Serializer' to 'Data export' for all the views that may be run from batch.
> 0 votes

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


title: How to add the twig debug extension

text:
# Question
Title: How to add the twig debug extension
in `/var/aegir/platforms/drupal8-dev/sites/my_site_uri/settings.php`

```
  # Additional site configuration settings.
  if (is_readable('/var/aegir/platforms/drupal8-dev/sites/my_site_uri/local.settings.php')) {
    include_once('/var/aegir/platforms/drupal8-dev/sites/my_site_uri/local.settings.php');
  }

```

in `/var/aegir/platforms/drupal8-dev/sites/my_site_uri/local.settings.php`

```
       <?php # local settings.php

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

```

in `/var/aegir/platforms/drupal8-dev/sites/default/services.yml`

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

```

The firebug console comments are working meaning twig debug is activated but dump (twig debug extension ) is not working . **why? is there anything more to set in services.yml ?**

```
  {{ dump() }}

```

# Answer
> 3 votes
Turn on Twig debugging by:

Copy sites/example.settings.local.php and rename to sites/default/settings.local.php

Go to sites/default/settings.php and uncomment the code below the comment relating to "Local development override configuration" and save the file.

```
if (file_exists(__DIR__ . '/settings.local.php')) {
  include __DIR__ . '/settings.local.php';
}

```

Open sites/development.services.yml in a text editor and add the following to the bottom of the file and then save the file

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

```

Note: use two spaces for each indent. Twig can be fussy with this.

The debugging will be visible as html comments in the source code. Use Chrome or other developer tools to inspect.

If you want to inspect the data structure of variables in a speciifc template, install Devel module and enable Devel and Devel Kint and then add this in your template:

```
# to dump everything
{{ kint() }}

# to dump a variable
{{ kint(page) }}

# to dump lower down in the data structure
{{ kint(page.content) }}

```

# Answer
> 1 votes
try

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

```

or to keep it more generic:

```
$settings['container_yamls'][] = __DIR__ . '/services.yml';

```

# Answer
> 1 votes
> As for kint i have activated devel kint module and putting {{ kint() }} gives me a WSOD with nothing in the logs !??

I had the same problem as mentioned here, the solution is:

> According to Devel maintainer **willzyx**:
>
> *you can edit `modules/devel/kint/kint/config.default.php` and set `$_kintSettings['maxLevels']` to a lower number (the default is 7)*

So try lowering it to 5 or 4.

# Answer
> 0 votes
In addition to my comment above - the dump twig function is available, if in the twig.config:debug is set to true (as mentioned already in the original question)

If enabled, you can try `{{ dump('just a string') }}` in your template, and it should output the correct value.

However, according to this article it is pretty likely, that you'll end up with a whitescreen of death due to a out of memory exception if you try to debug a complex class like node or \_context. To get around this, you can try something like:

`{{ dump(_context|keys) }}`

This will at least give some insight of what can be accessed.

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


# Question
Title: Adding ThirdPartySetting to webform entity results in wrong module dependency
I added my ThirdPartySetting to webform element like this:

Added my config schema to file `config/schema/mymodule.schema.yml` like this:

```
webform.settings.third_party.mymodule:
  type: mapping
  label: 'Webform mymodule configuration'
  mapping:
    settings:
      type: mapping
      label: 'mymodule settings'
      mapping:
        email_element:
          type: string
          label: 'E-mail element'

```

and then in `mymodule.module` I've altered webform settings form like this:

```
function mymodule_form_alter(&$form, &$form_state, $form_id) {
  if ($form_id === 'webform_settings_form') {
    $entity = $form_state->getFormObject()->getEntity();
    $elements = $entity->getElementsInitializedAndFlattened();
    $options = [];
    foreach ($elements as $element_name => $element) {
      $options[$element_name] = $element['#title'];
    }
    $form['third_party_settings']['email_element'] = [
      '#type' => 'details',
      '#title' => t('E-mail element'),
      '#open' => TRUE,
    ];
    $form['third_party_settings']['email_element']['email_element'] = [
      '#type' => 'select',
      '#options' => $options,
      '#title' => t('E-mail element'),
      '#default_value' => $entity->getThirdPartySetting('mymodule', 'email_element'),
    ];
    $form['actions']['submit']['#submit'][] = '_mymodule_form_submit';
  }
}

function _mymodule_form_submit(&$form, FormStateInterface $form_state) {
  /** @var \Drupal\webform\WebformInterface $entity */
  if ($entity = $form_state->getFormObject()->getEntity()) {
    $entity->setThirdPartySetting('mymodule', 'email_element', $form_state->getValue(['third_party_settings','email_element','email_element']));
    $entity->save();
  }
}

```

This works fine, but when I export my configuration, I see that dummy module has been added to my module dependencies.

```
 dependencies:
   module:
+    - mymodule
+    - email_element

```

This also reflects in `third_party_settings`

```
+third_party_settings:
+  email_element:
+    email_element: e_mail
+  mymodule:
+    email_element: e_mail

```

What am I doing wrong?

# Answer
Answer from @baikho is almost correct. In the end this worked for me:

```
function mymodule_webform_third_party_settings_form_alter(&$form, FormStateInterface $form_state) {

  /** @var \Drupal\webform\WebformInterface $webform */
  $webform = $form_state->getFormObject()->getEntity();
  $elements = $webform->getElementsInitializedAndFlattened();
  $options = [];
  foreach ($elements as $element_name => $element) {
    $options[$element_name] = $element['#title'];
  }

  $form['third_party_settings']['mymodule'] = [
    '#type' => 'details',
    '#title' => t('My module'),
    '#open' => TRUE,
  ];

  $form['third_party_settings']['mymodule']['email_element'] = [
    '#type' => 'select',
    '#options' => $options,
    '#title' => t('E-mail element'),
    '#default_value' => $webform->getThirdPartySetting('mymodule', 'email_element'),
  ];
}

```

And warning for anyone finding this in the future - if you do it wrong like I did, just download your database from another environment and start again, otherwise you might end up pulling your hair.
> 0 votes

# Answer
Please use `hook_webform_third_party_settings_form_alter()` in `mymodule.module` and discard the submit handler which should solve that:

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

/**
 * Implements hook_webform_third_party_settings_form_alter().
 */
function mymodule_webform_third_party_settings_form_alter(&$form, FormStateInterface $form_state) {

  /** @var \Drupal\webform\WebformInterface $webform */
  $webform = $form_state->getFormObject()->getEntity();

  // Do stuff.

  $form['third_party_settings']['email_element'] = [
    '#type' => 'select',
    '#options' => $options,
    '#title' => t('E-mail element'),
    '#default_value' => $webform->getThirdPartySetting('mymodule', 'email_element'),
  ];

  // Do not add submit handler.
}

```

Webform's `WebformThirdPartySettingsManagerInterface` wraps Core's `ThirdPartySettingsInterface` which is why I think you're seeing that behaviour.
> 2 votes

---
Tags: 8, webforms
---


# Question
Title: How can I upload and read an unmanaged file?
On a custom form I have a file field. Having chosen the file and clicked Submit, I'm trying to find how to access and read the uploaded file. I have no need to move or save it, I simply need to open it and use fgetcsv on it.

I've looked at the online examples I can find, but they seem to either deal with managed files or moving and saving unmanaged ones. The Examples module seems to only have a placeholder with regards to the file example.

# Answer
Maybe have a look at the CSVimportForm.php in `xurizaemon/csvimport` sample module. I wrote the D8 version. There an unmanaged uploaded CSV file gets read line by line. This happens in a batch process to prevent a timeout when you process a CSV with let's say a million lines.

The file gets uploaded to the temp dir and will be added to the files table as a temporary file. We make a copy of the file to the public files dir and let the garbage collector delete the original upload. You need to put the file somewhere on the server to read it in PHP. You don't have access to the browser in PHP. You then can delete the file when you are done.
> 3 votes

---
Tags: forms, files, 9
---


# Question
Title: Why does Drupal insist on deleting my configuration?
I've got a custom module with a configuration file `config/schema/entity.schema.yml`. This configuration file is imported via an update hook :

```
  $active_storage = \Drupal::service('config.storage');
  foreach ($files as $file) {
    $active_storage->write($file, $source->read($file));
  }

```

This works well, and the configuration in that file gets imported.

However, during the subsequent run of `drush cim` drush insist on deleting that configuration. I assume that is because the `entity.schema.yml` does not exist in the root `config/synch` folder.

Is this the expected behaviour? How can I make Drupal not delete the configuration which is imported from the module's `/config/schema` folder?

# Answer
> 3 votes
Yes this is the expected behavior. Config import and export is really quite simplistic (uncharitably: dumb). All it is doing when you run a config operation is to look at the active state of the DB and the active state of the sync directory and make one look like the other. On `config:import` (cim) you are saying "make the DB match those files in the sync directory". Export does the opposite. It's completely ignorant of other factors. So to make your changes "stick"

1. enable the module
2. run `drush cex`
3. afterwards `drush cim` should show "no changes"

**NOTE:** you can override config in settings.php files by using \`$config\[my.config.file\]\[a\_key\] = "my\_value". This bypasses whatever the files say to do. This is very useful for per environment overrides or sensitive inputs like API keys.

A simpler approach over this manual writing you have is to add your config yml file to `your_module/config/install/`. When the module is installed this will be imported to the active config state. As commenters have mention when you then run drush config:export (cex) the files will be added to your config sync directory.

Note that your config in /install are essentially one time use. They should also not include the uuid in the top line of the file that is generated in cex built yml.

If you want to install config again from your /install directory you can accomplish this manually by running: `drush cim -y --partial --source=modules/custom/my_module/config/install`

--partial means do not *remove* any configuration.

---
Tags: 8, configuration
---


title: Form type markup states

text:
# Question
Title: Form type markup states
Is it possible to create a form element of type markup and add a state to it, to make it visible on a condition? I can't seem to make it work and get the error:

Warning: Undefined array key "#type" in Drupal\Core\Form\FormHelper::processStates() (line 211 of core/lib/Drupal/Core/Form/FormHelper.php)

I need to just add some text in the form and make it visible on a condition.

Any help would be appreciated, thanks!

# Answer
> 1 votes
Adding form type item did the trick:

```
$form['my_incomes'] = [
      '#type' => 'item',
      '#markup' => $this->t('My other incomes'),
      '#states' => [
        'visible' => [
          [':input[name="marital_status"]' => [['value' => 'single'],['value'=>'couple']]],
        ]
      ]
    ];

```

---
Tags: forms
---


# Question
Title: Which releases are supported with semantic versioning of modules?
I have a site running Drupal 8.9.13. I have enabled email notification for "only security updates" and it generally works well - I get a mail for "urgent things" otherwise I wait and apply ordinary updates once a month.

Today I received a warning about an unsupported release. When I check, all I find is an available update. For this module, I am running 2.0.0 and 2.1.0 is available. According to the documentation about semantic versioning for Drupal modules, this is a change to the *minor* version. I don't see any reason why 2.0.0 is in fact unsupported.

From my perspective, this isn't an "urgent thing" and I don't want to be mailed about it every day. Under the "old" version numbering scheme, a minor version change such as 8.x-3.5 to 8.x-3.6 **does not** cause the same behaviour.

Is this a bug in the Drupal core update module? Or have I misunderstood how the new semantic version mechanism works?

Screenshot:

# Answer
You got an email because the 2.0.0 version has been marked as unsupported by the project maintainer when, on the *Releases* edit form, the *Supported* checkbox for that version has been deselected.

>

On Drupal.org, 2.x, 2.0.x, and 2.1.x are considered like three different branches, and have each the setting to make them supported or recommended.
It's not just a matter of version number, but also what the project maintainers mark as supported. Probably, if you had version 2.1.0 installed, that were marked as unsupported, and version 2.0.0 were marked supported, you would get an email telling you the version you installed got unsupported.
> 1 votes

---
Tags: updating
---


# Question
Title: How can I bypass required fields when saving a node as unpublished?
I am saving node as unpublished without adding data in required fields. How can I achieve that in Drupal 8 ? Basically I need something like Save as Draft option for nodes in content types.

# Answer
> 2 votes
There's the Require on Publish module. You'll have a checkbox for all fields beneath the default "Required" checkbox that says "Require on publish". So you need to have the default "Required" checkbox unchecked and just check "Require on publish". Quite clever actually.

> This module allows fields to be required only when the piece of content is being published or is already published. This can be used when you have fields such as tags or SEO information on your piece of content that editors really don't need to fill in until the content is going live.
>
> ### Usage
>
> When managing a field for a content type, click the new 'Required on Publish' checkbox instead of the 'Required field' checkbox.

# Answer
> 0 votes
The save\_draft module should make this possible for Drupal 7 (with the patch from issue 1786442 applied). Issue 2220659 describes a Drupal 8 version, but the patch does not include the fix for allowing missing required fields. In Drupal 8, this has become a bit more difficult because of the added Entity validation.

I created following ugly solution for my use case, which eliminates NotNull errors related to required fields and sets the entity as validated if no errors remain. I make use of disable\_html5\_validation to disable client side validation.

```
function find_element_by_path($element, $path) {
  if (count($path) === 0) {
    return $element['widget'];
  }
  if (is_numeric($path[0])) {
    return find_element_by_path($element['widget'][array_shift($path)], $path);
  }
  return find_element_by_path($element[array_shift($path)], $path);
}

function save_draft_validate($form, &$form_state) {
  // Remove all NotNull validation errors related to required fields.
  $errors = $form_state->getErrors();
  foreach ($errors as $key => $error) {
    if ($error->getUntranslatedString() === 'This value should not be null.') {
      $element = find_element_by_path($form, explode('][', $key));
      if (isset($element['#originallyRequired']) && $element['#originallyRequired']) {
        unset($errors[$key]);
      }
      elseif ($element['#theme'] === 'file_widget_multiple') {
        if (isset($element[0]['#originallyRequired']) && $element[0]['#originallyRequired']) {
          unset($errors[$key]);
        }
      }
    }
    elseif ($error->getUntranslatedString() === 'An illegal choice has been detected. Please contact the site administrator.') {
      $element = find_element_by_path($form, explode('][', $key));
      if (isset($element['#originallyRequired']) && $element['#originallyRequired'] && $element['#value'] === '_none') {
        unset($errors[$key]);
      }
    }
  }
  $form_state->clearErrors();
  if (count($errors) === 0) {
    $form_state->setTemporaryValue('entity_validated', TRUE);
  } else {
    foreach($errors as $key => $error) {
      $form_state->setErrorByName($key, $error);
    }
  }
}

function save_draft_unpublish_submit($form, &$form_state) {
  $form_state->setValue('status', ['value' => 0]);
}

function remove_required(&$element) {
  // Recurse through all children.
  foreach (Element::children($element) as $key) {
    if (isset($element[$key]) && $element[$key]) {
      remove_required($element[$key]);
    }
  }
  // Make sure the title field remains required.
  $titleIds = ['edit-title', 'edit-title-0', 'edit-title-0-value'];
  if (!empty($element['#required']) && !in_array($element['#id'], $titleIds)) {
    $element['#originallyRequired'] = $element['#required'];
    $element['#required'] = FALSE;
  }
}

function MY_MODULE_form_after_build($form, &$form_state) {
  // Check that the form has been submitted.
  if ($form_state->isProcessingInput()) {
    // If the save draft button was pressed.
    if ($form_state->getTriggeringElement()['#id'] === 'edit-draft') {
      remove_required($form);
    }
  }
  return $form;
}

function add_draft(&$form) {
  $form['actions']['draft'] = [
    '#type' => 'submit',
    '#value' => t('Save draft'),
    '#weight' => '9',
    '#submit' => $form['actions']['submit']['#submit'],
    '#validate' => ['save_draft_validate'],
  ];
  array_unshift($form['actions']['draft']['#submit'], 'save_draft_unpublish_submit');
  $form['#after_build'][] = 'MYMODULE_form_after_build';
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function MYMODULE_form_node_MYFORM_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  add_draft($form);
}

```

---
Tags: 8, nodes
---


# Question
Title: Instantiating a Class in the Custom Module
I am trying to build a custom module to process credit card payments through a PHP Rest Client created by the credit card processing vendor. Here is that Rest Client:

```
<?php
/**
* CardConnect PHP REST Client Library
* Version: 1.0
Copyright 2014, CardConnect (http://www.cardconnect.com)

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/

require_once 'pest/PestJSON.php';
class CardConnectRestClient {
    private $url = "";
    private $user = "";
    private $password = "";

    private $OP_POST   = "POST";
    private $OP_PUT    = "PUT";
    private $OP_GET    = "GET";
    private $OP_DELETE = "DELETE";

    private $ENDPOINT_AUTH       = "auth";
    private $ENDPOINT_CAPTURE    = "capture";
    private $ENDPOINT_VOID       = "void";
    private $ENDPOINT_REFUND     = "refund";
    private $ENDPOINT_INQUIRE    = "inquire";
    private $ENDPOINT_SETTLESTAT = "settlestat";
    private $ENDPOINT_DEPOSIT    = "deposit";
    private $ENDPOINT_PROFILE    = "profile";

    private $USER_AGENT     = "CardConnectRestClient-PHP";
    private $CLIENT_VERSION = "1.0";

    /**
    * Constructor to create a new CardConnectRestClient object
    *
    * @param string $ccurl CardConnect REST URL (https://sitename.cardconnect.com:6443/cardconnect/rest/)
    * @param string $user Username
    * @param string $pass Password
    */
    public function __construct($ccurl, $user, $pass) {
        if (self::isEmpty($ccurl)) throw new InvalidArgumentException("url parameter is required");
        if (self::isEmpty($user)) throw new InvalidArgumentException("username parameter is required");
        if (self::isEmpty($pass)) throw new InvalidArgumentException("password parameter is required");

        if (!self::endsWith($ccurl, "/")) $ccurl .= "/";

        $this->url = $ccurl;
        $this->username = $user;
        $this->password = $pass;
    }

    /**
    * Sends an Authorize Transaction request via REST
    *
    * @param array $request Array representing an authorization request
    * @return array Array representing an authorization response
    */
    public function authorizeTransaction($request) {
        return self::send($this->ENDPOINT_AUTH, $this->OP_PUT, $request);
    }

    /**
    * Sends a Capture Transaction request via REST
    *
    * @param array $request Array representing a capture request
    * @return array Array representing a capture response
    */
    public function captureTransaction($request) {
        return self::send($this->ENDPOINT_CAPTURE, $this->OP_PUT, $request);
    }

    /**
    * Sends a Void Transaction request via REST
    *
    * @param array $request Array representing a void request
    * @return array Array representing a void response
    */
    public function voidTransaction($request) {
        return self::send($this->ENDPOINT_VOID, $this->OP_PUT, $request);
    }

    /**
    * Sends a Refund Transaction request via REST
    *
    * @param array $request Array representing a refund request
    * @return array Array representing a refund response
    */
    public function refundTransaction($request) {
        return self::send($this->ENDPOINT_REFUND, $this->OP_PUT, $request);
    }

    /**
    * Sends an Inquire Transaction request via REST
    *
    * @param string $merchid Merchant ID
    * @param string $retref RetRef from previous authorization/capture response
    * @return array Array representing an inquire response
    */
    public function inquireTransaction($merchid, $retref) {
        if (self::isEmpty($merchid)) throw new InvalidArgumentException("Missing required parameter: merchid");
        if (self::isEmpty($retref)) throw new InvalidArgumentException("Missing required parameter: retref");

        $url = $this->ENDPOINT_INQUIRE . "/" . $retref . "/" . $merchid;
        return self::send($url, $this->OP_GET, null);
    }

    /**
    * Sends a Settlement Status request via REST
    *
    * @param string $merchid Merchant ID
    * @param string $date Settlement Date
    * @return array Array representing the requested settlement status
    */
    public function settlementStatus($merchid = "", $date = "") {
        if ((!self::isEmpty($merchid) && self::isEmpty($date)) || (self::isEmpty($merchid) && !self::isEmpty($date)))
            throw new InvalidArgumentException("Both merchid and date parameters are required, or neither");

        $url;
        if (self::isEmpty($merchid) || self::isEmpty($date)) {
            $url = $this->ENDPOINT_SETTLESTAT;
        } else {
            $url = $this->ENDPOINT_SETTLESTAT . "?date=" . $date . "&merchid=" . $merchid;
        }

        return self::send($url, $this->OP_GET, null);
    }

    /**
    * Sends a Deposit Status request via REST
    *
    * @param string $merchid Merchant ID
    * @param string $date Deposit Date
    * @return array Array representing the requested deposit status
    */
    public function depositStatus($merchid = "", $date = "") {
        if ((!self::isEmpty($merchid) && self::isEmpty($date)) || (self::isEmpty($merchid) && !self::isEmpty($date)))
            throw new InvalidArgumentException("Both merchid and date parameters are required, or neither");

        $url;
        if (self::isEmpty($merchid) || self::isEmpty($date)) {
            $url = $this->ENDPOINT_DEPOSIT;
        } else {
            $url = $this->ENDPOINT_DEPOSIT . "?merchid=" . $merchid . "&date=" . $date;
        }
        return self::send($url, $this->OP_GET, null);
    }

    /**
    * Retrieves the specified profile via REST
    *
    * @param string $profileid Profile ID
    * @param string $accountid Optional Account ID
    * @param string $merchid Merchant ID
    * @return array Array representing the retrieved profile
    */
    public function profileGet($profileid, $accountid = "", $merchid) {
        if (self::isEmpty($profileid)) throw new InvalidArgumentException("Missing required parameter: profileid");
        if (self::isEmpty($merchid)) throw new InvalidArgumentException("Missing required parameter: merchid");

        $url = $this->ENDPOINT_PROFILE . "/" . $profileid . "/" . $accountid . "/" . $merchid;
        return self::send($url, $this->OP_GET, null);
    }

    /**
    * Deletes the specified profile via REST
    *
    * @param string $profileid Profile ID
    * @param string $accountid Optional Account ID
    * @param string $merchid Merchant ID
    * @return array Array representing the results of the profile deletion
    */
    public function profileDelete($profileid, $accountid = "", $merchid) {
        if (self::isEmpty($profileid)) throw new InvalidArgumentException("Missing required parameter: profileid");
        if (self::isEmpty($merchid)) throw new InvalidArgumentException("Missing required parameter: merchid");

        $url = $this->ENDPOINT_PROFILE . "/" . $profileid . "/" . $accountid . "/" . $merchid;
        return self::send($url, $this->OP_DELETE, null);
    }

    /**
    * Creates or updates a profile via REST
    *
    * @param array $request Array representing the Profile create/update request
    * @return array Array representing the profile creation
    */
    public function profileCreate($request) {
        return self::send($this->ENDPOINT_PROFILE, $this->OP_PUT, $request);
    }

    // Returns true if a string is null or empty string
    static function isEmpty($s) {
        if (is_null($s)) return true;
        if (strlen($s) <= 0) return true;
        return false;
    }

    // Checks the last character of a string
    static function endsWith($s, $char) {
        return $char === "" || substr($s, -strlen($char)) === $char;
    }

    // Private method for sending HTTP REST request to CardConnect
    private function send($endpoint, $operation, $request) {
        $pest = new PestJSON($this->url);
        $pest->setupAuth($this->username, $this->password);
        $pest->curl_opts[CURLOPT_FOLLOWLOCATION] = false; // Not supported on hosts running safe_mode!
        $pest->curl_opts[CURLOPT_HTTPHEADER] = "Content-Type: application/json";
        $pest->curl_opts[CURLOPT_USERAGENT] = $this->USER_AGENT . " (v" . $this->CLIENT_VERSION . ")";

        $response = "";
        try {
            // Send request to rest service
            switch ($operation) {
                case ($this->OP_PUT):
                    $response = $pest->put("/$endpoint", $request);
                    break;
                case ($this->OP_GET):
                    $response = $pest->get("/$endpoint", $request);
                    break;
                case ($this->OP_POST):
                    $response = $pest->post("/$endpoint", $request);
                    break;
                case ($this->OP_DELETE):
                    $response = $pest->delete("/$endpoint", $request);
                    break;
            }
        } catch (Exception $e) {
            echo "Caught exception when sending request : " .  $e->getMessage();
        }

        return $response;
    }
}

?>

```

Here is my custom module:

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

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

/**
 * Form submission handler.
 *
 * @WebformHandler(
 *   id = "cc_processing_cardconnect",
 *   label = @Translation("CC Processing CardConnect"),
 *   category = @Translation("Webform Handler"),
 *   description = @Translation("Process credit card payemnt through CardConnect."),
 *   cardinality =
 *       \Drupal\webform\Plugin\WebformHandlerInterface::CARDINALITY_SINGLE,
 *   results =
 *    \Drupal\webform\Plugin\WebformHandlerInterface::RESULTS_PROCESSED,
 * )
 */

require_once 'CardConnectRestClient.php';

class CCProcessingCardConnectWebformHandler extends WebformHandlerBase {
/**
* {@inheritdoc}
*/

    // Username
        static $devUser = "testing";
        static $prodUser = "";

    // Password
        static $devPassword = "testing123";
        static $prodPassword = "";

    // Merchant ID
        static $devMID = '000000000000';
        static $prodMID = '';

    // Site's REST URL
        static $devURL = 'https://fts-uat.cardconnect.com/cardconnect/rest/';
        static $prodURL = '';

    // Environment (live or dev)
        static $env = 'dev';

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

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

    public function confirmForm(array &$form, FormStateInterface $form_state, WebformSubmissionInterface $webform_submission) {
        $form_id = 'webform_submission_' . $webform_submission->getWebform()->id() . '_form';
        ini_set('display_errors', 1);

        // Variables
        if($env == 'dev'){
            $url = $this->$devURL;
            $user = $this->$devUser;
            $password = $this->$devPassword;
            $MID = $this->$devMID;
        }
        else {
            $url = $this->$prodURL;
            $user = $this->$prodUser;
            $password = $this->$prodPassword;
            $MID = $this->$prodMID;
        }

        $client = new CardConnectRestClient($url, $user, $password);

        $request = array(
            'merchid'   => $MID,
            'account'   => "4111111111111111",
            'expiry'    => "0922",
            'cvv2'      => "123",
            'amount'    => "999.99",
            'currency'  => "USD",
            'name'      => "Test User",
            'street'    => "Test Address",
            'city'      => "Test City",
            'region'    => "HI",
            'country'   => "US",
            'postal'    => "96822",
            'tokenize'  => "Y",
            'capture'   => "Y",
        );

        $response = $client->authorizeTransaction($request);
        $form_state->setRedirect('entity.node.canonical', ['node' => 1765 ]);
    }
}
?>

```

This line of code: **$client = new CardConnectRestClient($url, $user, $password);** throws this error when I test the form: **Fatal error: Cannot declare class CardConnectRestClient, because the name is already in use in D:\drupal\modules\custom\cc\_processing\_cardconnect\src\Plugin\WebformHandler\CardConnectRestClient.php on line 21**

What am I doing wrong in trying to instantiate the Rest Client class? Does Drupal automatically instantiate the class for me when I require it above the class declaration? I am pretty new to developing in Drupal.

# Answer
You should use namespace (https://www.php.net/manual/en/language.namespaces.rationale.php) to load your classes and avoid using `require_once()`

Place your `CardConnectRestClient` class in a `CardConnectRestClient.php` in a folder inside your module and declare the namespace accordingly, i.e.

Folder : `your_module/src/Librairies/Rest`

Namespace : `Drupal\your_module\Librairies\Rest;`

(the `src` folder is kind of the "root" namespace of your module)

in your `CCProcessingCardConnectWebformHandler` class, declare the dependency :(https://www.php.net/manual/en/language.namespaces.importing.php) with `use` instructions :

`use Drupal\your_module\Librairies\Rest\CardConnectRestClient;`

Then PHP will automatically load the file when you call the class on the line `$client = new CardConnectRestClient($url, $user, $password);`

And more importantly, it will do it only once.

You could do the same with the dependency `pest/PestJSON.php` hard-loaded in the `CardConnectRestClient` class.
> 0 votes

---
Tags: 8
---


# Question
Title: Dynamic value in Twig or preprocess based on config and getHost()
I need to dynamically change a value in Twig templates by selecting a configuration value (defined in a custom module) based on the current environment.

I did the following in a Form class, which works fine:

```
  /**
   * Generate parent site Base URL per environment.
   *
   * @return string
   *   The parent site Base URL used in all templates site-wide.
   */
  public function getBaseUrl() {
    $host = $this->requestStack->getCurrentRequest()->getHost();
    $config = $this->configFactory->get('clientname_cta_base.settings');
    switch ($host) {
      case 'resources.clientname.com':
        // Set $base_url to Production environment.
        $base_url = $config->get('prod_base_url') ?? [];
        break;
      case 'uatresources.clientname.com':
        // Set $base_url to UAT environment.
        $base_url = $config->get('uat_base_url') ?? [];
        break;
      case 'qaresources.agencyname.com':
      case 'qaresources.clientname.com':
        // Set $base_url to QA environment.
        $base_url = $config->get('qa_base_url') ?? [];
        break;
      default:
        // All other environments, including local, link to Dev.
        $base_url = $config->get('dev_base_url') ?? [];
        break;
    }
    return $base_url;
  }
  /**
   * Generate CTA destination URL per environment.
   *
   * @return string
   *   The CTA destination URL used in all templates site-wide.
   */
  public function getCtaUrl() {
    $config = $this->configFactory->get('clientname_cta_base.settings');
    $cta_url = $this->getBaseUrl() . $config->get('default_link_dest') ?? [];
    return $cta_url;
  }

```

This displays the desired URL paths in two disabled fields when the `SettingsForm` is loaded:

```
    // Active URL configuration (read-only).
    $form['clientname_cta_base_computed_base_url'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Computed base URL (Preview)'),
      '#default_value' => $this->getBaseUrl(),
      '#description' => $this->t('Read-only value based on config and environment. Save changes to update.'),
      '#attributes' => ['disabled' => 'disabled'],
    ];
    $form['clientname_cta_base_computed_cta_url'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Computed CTA URL (Preview)'),
      '#default_value' => $this->getCtaUrl(),
      '#description' => $this->t('Read-only value based on config and environment. Save changes to update.'),
      '#attributes' => ['disabled' => 'disabled'],
    ];

```

I could even store these updated values in configuration when the `SettingsForm` is submitted -- but that will not quite get me what I want.

These values need to be computed dynamically by selecting config based on the value of `$host` at runtime -- importing the Dev environment's computed config values to the QA environment won't do. So I am looking for a way to call the module's public methods (`getBaseUrl()` and `getCtaUrl()`) in either theme preprocess or Twig templates.

Is this approach possible? Should I have used theme settings instead?

# Answer
> 0 votes
In case you wondered, I resolved this by adding my methods to a service class, as @Hudri suggested in comments.

I can call those methods in a Form or other OO class by dependency injection of the `Drupal\clientname_cta_base\Cta\Base` service class:

```
    // Active URL configuration (read-only).
    $form['clientname_cta_base_computed_base_url'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Computed base URL (Preview)'),
      '#default_value' => $this->ctaBase->getBaseUrl(),
      '#description' => $this->t('Read-only value based on config and environment. Save changes to update.'),
      '#attributes' => ['disabled' => 'disabled'],
    ];
    $form['clientname_cta_base_computed_cta_url'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Computed CTA URL (Preview)'),
      '#default_value' => $this->ctaBase->getCtaUrl(),
      '#description' => $this->t('Read-only value based on config and environment. Save changes to update.'),
      '#attributes' => ['disabled' => 'disabled'],
    ];

```

The example above does not include all the dependency injection setup in the Form class, or the service class setup, services YAML config, and other steps that are well documented elsewhere.

In theme preprocess, dependency injection is not available, so I get the computed values via `\Drupal::service('cta.base')` like so:

```
/**
 * Implements template_preprocess_page().
 */
function clientname_preprocess_page(&$variables) {
  $service = \Drupal::service('cta.base');
  $config = \Drupal::config('clientname_cta_base.settings');
  $variables['computed_base_url'] = $service->getBaseUrl();
  $variables['computed_cta_url'] = $service->getCtaUrl();
  $variables['default_link_text'] = $config->get('default_link_text');
}

```

Note that for configuration values that are not dynamically generated, I can just access them directly in theme preprocess via `\Drupal::config('clientname_cta_base.settings')`.

---
Tags: 8, theming, configuration
---


title: Negate a block visibility condition

text:
# Question
Title: Negate a block visibility condition
Found it useful and did not find any contributed module to fulfil this so, adding it for knowledge purpose.

Stuck in a situation where we need to hide a block for certain role.

Drupal 8 has functionality to hide for certain URLS.

But, for Roles, only show condition is there not Hide.

I have already answered my question and I would love to accept another solutions.

Please suggest a solution.

# Answer
> 2 votes
There are visibility settings where, we can select which pages the block can be visible.

The same form has an underlying functionality to hide the block for the selected list or URLs.

Unfortunately, the same is not the case of Roles.

You can select which roles you want to display the block, but, you cannot select which Roles you want to hide the block.

Following code adds a Negate condition to Roles Visibility:

```
/* Negate the condition to decide visibility of which user roles to show/hide. */
function YOUR_MODULE_form_block_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
    $form['visibility']['user_role']['negate'] = [
        '#type' => 'radios',
        '#title' => '',
        '#default_value' => 0,
        '#options' => [
            t('Show for selected Roles'),
            t('Hide for selected Roles'),
        ],
    ];
}

```

This adds two radio buttons under the form: Roles.

Following is the screen shot for the same:

Reference:

---
Tags: 8, blocks
---


title: Alternative title for Easy Breadcrumb

text:
# Question
Title: Alternative title for Easy Breadcrumb
I'm using Drupal 9 and recently discovered Easy Breadcrumb. I'd like to use a custom field to overwrite the page title to be displayed in the breadcrumbs, but it seems to be not working.

In the configuration, there is a field "Alternative title field name for breadcrumb" with "field\_breadcrumb\_title" by default, but when I add a field with this name to my page, it does not display. Instead, I get the page title or (when I disable "Use the real page title") the formated URL segment.

There is a discription of every configuration field in the documentation, but of all things this one is missing.

Does anyone know, if this functionality is working?

Thank you!

# Answer
> 0 votes
This could certainly be a bug in the new future. For those who come across this in the future, the follow up is on Drupal.org:

https://www.drupal.org/project/easy\_breadcrumb/issues/2931141

---
Tags: breadcrumbs, 9
---


# Question
Title: How to add active class to title which is loaded via views in block
I have created content type XXX, via views I collected all titles of XXX content. That all titles works as links, I want to add active class to title which was clicked in block. It should works as menu item.

# Answer
> 0 votes
If you are generating a menu structure, I would suggest the following:

Your view should use the HTML List format. In the settings use the unordered list type. You probably should add a list class setting like nav (or your theme's default class for nav elements).

The Format Show type should be Fields. In the settings, uncheck the "Provide default field wrapper..." option.

The results should be a straight forward ul list with li wrapped anchors. E.g.

```
<ul class="nav">
  <li>
     <a href='node/1'>Node Title</a>
  </li>
// other titles below
</ul>

```

Standard menu CSS should work on this, e.g .nav \> li \> a to select anchors and the like.

If you still need a css class on the label, you can add a hidden path field above the title. Remove the link to content option. Then rewrite the title field with something like:

```
<a href='{{ path }}' class='foo'>{{ title }}</a>

```

To get an active class, you have several options.

If you are using a bootstrap based theme, you can just add the appropriate bootstrap classes to this since it's a simple ul structure. See the bootstratp docs for more details.

It is easy to add a bit of jQuery script to do this. See this for some sample code.

Or you can do it with the template\_preprocess\_views\_view\_field() hook. Here's some sample code for that. You will probably have to dump the output value to see how it is layed out. Note that you will have to play some games with getting the current path into the same format, especially if you are using path aliases. Some sample code.

# Answer
> 0 votes
There is a patch in the Drupal core issue queue to fix this: https://www.drupal.org/project/drupal/issues/2652000

---
Tags: views, 8, navigation
---


# Question
Title: How can I allow users to specify image dimensions in CKEditor
I have a use case where content editors have a requirement to specify the height and the width of an image in the image properties. Sticking to styles is not possible because $reasons.

The CKEditor demo has this:

But the Drupal 8 image properties looks rather sparse in comparison.

I've looked all over for this for hours, and this is the first time I've had to write my own Drupal Answers question!

Does anyone know how we can expose the option to set width and height in CKEditor?

I'm aware of IMCE - but expected behaviour of the module in Drupal 8 is to use IMCE for file upload only - once an image is placed, image properties is handled by CKEditor.

There is a YouTube video where someone has managed to configure CKEditor to use IMCE's image properties: https://www.youtube.com/watch?v=2eTfQ7Y4i5I but there's no instructions on how this was achieved, and a comment-or asked but has been hanging for a year.

I'm totally new to Drupal 8, would like to know how to do this! And then I'll figure out how to make sure the change isn't reverted next time core is patched :)

# Answer
> 1 votes
Depending on how far you are in your project consider using Paragraphs instead, to have your content built modularly. **Inline images are bad practice** IMHO. They are difficult to preprocess and difficult to apply responsive image styles.

With Paragraphs (or in general with a **fieldable approach instead** like Kevin pointed out in the comments) you'd provide certain paragraph types, for example one for pure text and one for an image. Editor then places an image paragraph, followed by a text paragraph and the text simply floates around the image. You can have responsive image styles for the image, you can build the image field according your logic to provide certain crop types etc.

---

I know this is not the answer you might look for, but honestly for the sake of maintainability think about it if putting everything into the body field will still work in the long run. In my experience this can lead to a very big mess. Hard to disassemble, hard to migrate, hard to maintain.

# Answer
> 0 votes
The paragraphs approach mentioned above could definitely work depending on your use case - another pattern is using **Drupal Media entities** and **Media Library or entity browser** to embed images. This also allows you to use things like the Focal Point module. (overview here: Core Media Overview)

With this design pattern you can utilize **image styles** which allows users to pick sizes and cropping rules you've defined (image styles are config). It also gives you (often significant) performance improvements because derivative optimized images are created, meaning a big source file isn't served in the browser. Let's walk through how this might look "in the wild":

> Someone is editing content in a blog post and they want to use an image within their text. They would click the entity embed button (configured by you) and can either browse other images already in the site, or upload a new one. When a user picks the "medium image" style after choosing/uploading their image, they receive a resized 150px max width image - based on your configuration of the "medium image" image style. Instead they realize they want the image to be square, they change the image style to "square crop (150)" instead. You have configured this to scale and crop the image, respecting the focal point the user has chosen.

> They can see this previewed in the WYSIWYG and then can also use this image elsewhere as the blog node's teaser in the media reference field there.

> When they load the page, it loads quickly because the images have been generated at the specified size using the media kit, even though they uploaded 3MB images to use. (actual file: my\_image\_sq\_crop\_150.png)

This type of design pattern is a generally accepted best practice in D8 that does require a bit of upfront configuration to get in place. It can also work very well along side the more constrained paragraphs approach which allows you to treat the content there as a "content and image component". I hope that helps!

---
Tags: 8, media, wysiwyg
---


# Question
Title: Webform submit button disappearing if I change it from default
I have a weird issue with the webform module, whenever I modify the submit button (change text, add class) it doesn't show up on the page anymore (nor in the source code). If I delete it, it is back to default (the label in the OPERATION column says customize again) and it works, but it disappear if I change it again.

I didn't do any weird modification beside creating a Twig template for it. But since it works when I leave it to its default value I doubt the issue lies there. Hopefully one of you guys can help me.

Here is the YAML code:

```
markup:
  '#type': markup
  '#markup': |
    <h2>
      Je publie une offre d'emploi
    </h2>
job:
  '#type': textfield
  '#title': job
  '#title_display': invisible
  '#placeholder': 'Fonction recherchée*'
  '#required': true
  '#required_error': 'Fill in a valid job'
  '#pattern': '^[a-zA-Z]*$'
company:
  '#type': textfield
  '#title': company
  '#title_display': invisible
  '#placeholder': 'Nom de la société*'
  '#required': true
  '#required_error': 'Fill in a company'
  '#pattern': '^[a-zA-Z0-9]*$'
job_description:
  '#type': textarea
  '#title': 'job description'
  '#title_display': invisible
  '#placeholder': 'Description de la fonction'
  '#required': true
  '#required_error': 'Fill in a description'
contract:
  '#type': select
  '#title': contract
  '#title_display': invisible
  '#options':
    CDD: CDD
    CDI: CDI
  '#empty_value': 'Type de contrat*'
  '#required': true
  '#required_error': 'Choose a contract type'
job_schedule:
  '#type': select_other
  '#title': 'job schedule'
  '#title_display': invisible
  '#options':
    'Full time': 'Full time'
    'Part time': 'Part time'
  '#empty_value': 'Travail à temps...*'
  '#required': true
  '#required_error': 'Select a schedule'
location:
  '#type': textfield
  '#title': location
  '#title_display': invisible
  '#placeholder': 'Lieu*'
  '#required': true
publication_date:
  '#type': date
  '#title': 'publication date'
  '#description': 'Date de publication*'
  '#title_display': invisible
  '#description_display': before
  '#prepopulate': true
  '#required': true
  '#required_error': 'Select a publication day'
  '#min': today
  '#max': '+2 months'
  '#step': '1'
end_date:
  '#type': date
  '#title': 'end date'
  '#description': 'Date limite*'
  '#title_display': invisible
  '#description_display': before
  '#required': true
  '#min': '+2 weeks'
url:
  '#type': url
  '#title': URL
  '#title_display': invisible
  '#placeholder': 'URL: RH ou Plateforme pour postuler*'
  '#required': true
  '#required_error': 'Fill in a valid URL'
phone:
  '#type': tel
  '#title': phone
  '#description': 'Mes informations (pour être contacté par BioWin)'
  '#title_display': invisible
  '#description_display': before
  '#placeholder': 'Numéro de téléphone*'
email:
  '#type': email
  '#title': email
  '#title_display': invisible
  '#placeholder': 'Adresse email'
  '#pattern': '^([a-zA-Z0-9_.+-])+\@(([a-zA-Z0-9-])+\.)+([a-zA-Z0-9]{2,10})+$'
actions:
  '#type': webform_actions
  '#title': 'Submit button(s)'
  '#submit__label': Envoyer
  '#submit__attributes':
    class:
      - cta

```

# Answer
> 0 votes
If you have a custom twig template for your webform the custom submit button needs to be referenced differently than the default.

Default Submit Button reference:

{{ element.actions }}

Customized Submit Button reference:

{{ element.elements.actions }}

---
Tags: webforms
---


# Question
Title: Views exposed filter by season from date field (date range group as month only?)
How would I create an exposed filter by season (spring, summer, fall, winter) based on date fields? I figured the approach would be to create date range filter groups by MONTH only (no year), but I don't see a straightforward way of doing this.

Any tips or advice for getting a workable solution? Thanks!

# Answer
> 0 votes
You can create the season date ranges and name them by using the Grouping Exposed filter option.

You can find it in the regular Date filter, select `Grouped filters` instead of `Single filter` for the `Filter type to expose` option.

Set the operators to **Is between** and enter the dates for your seasons (you can see spring and summer in this screnshot):

Drupal will assume midnight if you ommit the time and only include the date. You can look up the exact astronomical times for the seasons if you like :)

Be careful not to exclude some results if you use the same date in separate grouped options but not the same time.

---
Tags: 8, datetime
---


title: Alter field widget settings fields

text:
# Question
Title: Alter field widget settings fields
I'm trying to alter the settings fields displayed when you click on the cog icon on the "Manage form display" admin screen.

More specifically I want to alter fields set by another module through `hook_field_widget_third_party_settings_form`

I finally found the solution so i'll share it here as an answer.
It was not that hard, but I had to dig a bit into the core code in order to figure out how to do it.

# Answer
The form\_id we want to alter is `entity_form_display_edit_form`
Because the settings fields are loaded through ajax, we first have to check if we are currently editing a field settings by checking  `$form_state->get('plugin_settings_edit')`
The fields we are looking for are then located at  `$form['fields'][FIELD_NAME]['plugin']['settings_edit_form']`

```
function fintech_form_entity_form_display_edit_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  $field_name = $form_state->get('plugin_settings_edit');
  if(!empty($field_name) && isset($form['fields'][$field_name]['plugin']['settings_edit_form'])) {
    $form['fields'][$field_name]['plugin']['settings_edit_form']['third_party_settings']['whatever_you_want_to_alter'] = '';
  }
}

```
> 0 votes

---
Tags: 8
---


# Question
Title: What is the alternative to file_unmanaged_save_data()?
In a custom module, I use this code.

```
file_unmanaged_save_data($data, 'private://' . $path, FILE_EXISTS_REPLACE);

```

The documentation for that function has the following warning.

> Deprecated
> In drupal:8.7.0 and is removed from drupal:9.0.0. Use \Drupal\Core\File\FileSystemInterface::saveData().

So I tried using `\Drupal\Core\File\FileSystemInterface::saveData($data, 'private://' . $path, FileSystemInterface::EXISTS_REPLACE)`, which gives the *Non-static method cannot be called statically*.

Adding `use Drupal\Core\File\FileSystemInterface;` at the beginning of the module doesn't change anything.

In the same page, I found also a link to Unmanaged file functions replaced with a service which explains that `file_unmanaged_save_data($data, $destination, $replace)`
has been replaced by `\Drupal::service('file_system')->saveData($data, $destination, $replace)`. It seems to work, but I'm still wandering:

Is there another way to do it?

# Answer
> -1 votes
Drupal always documents all the methods using `::`, so you need to look closer as to whether the method is static or not.

So if you look at FileSystem::saveData

>

Its public and not a static. Therefore using `::saveData()` is incorrect, needs to be called with the arrow `->saveData()`

Also, as pointed out, you need to call it with a **class** that implements the `FileSystemInterface`.

---
Tags: 8
---


# Question
Title: add role to existing user on hook_entity_presave
I'm trying to add a role to a user but I can't save the change.

```
function hook_entity_presave(Drupal\Core\Entity\EntityInterface $entity){
  $user = \Drupal\user\Entity\User::load(\Drupal::currentUser()->id());
  $user->addRole('foobar');
}

```

I would like to save the change, but `$user->save()` causes a fatal error.

What would be the correct way?

Note: The error is a whitescreen of death. I guess $user-\>save() is for creating new users. I would need to update an existing one.

# Answer
> 1 votes
Code should look like this, as an example:

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

function mymodule_entity_presave(EntityInterface $entity) {
  if ($entity instanceof AccountInterface && $entity->isTranslatable()) {
    $user = User::load($entity->id());
    $user->addRole('foobar');
  }
}

```

Or better yet, hook\_ENTITY\_TYPE\_presave():

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

function mymodule_user_presave(EntityInterface $entity) {
  $user = User::load($entity->id());
  $user->addRole('foobar');
}

```

Simply using hook\_entity\_presave will fire for any entity about to be saved. So you either need to check the incoming entity, or use the second hook to isolate it.

# Answer
> 1 votes
I received an error using `hook_user_presave` saying unable to flip string and integer on `$entity_id`. Looks like it doesn't have the id on presave, worked when I switched to `hook_user_insert`.

```
<?php
use Drupal\Core\Entity\EntityInterface;
use Drupal\user\Entity\User;

function mymodule_user_insert(EntityInterface $entity) {
  $user = User::load($entity->id());
  $user->addRole('foobar');
  $user->save();
}

```

# Answer
> 1 votes
For the code shown in the question, notice that:

* `hook_entity_presave()` is called before every entity is saved, including a view, a block, a contact form, or an image style (just to list a few entities Drupal core implements)
* With `\Drupal::currentUser()->id()`, you are getting the ID of the currently logged-in user; in the case a user is creating an entity for another user, that code would not assign the new role to the right account, which I assume should be the user who created the entity
* The code is not checking the user account is for the anonymous user; supposing that anonymous users are allowed to create an entity, that code would not work as expected, since anonymous users don't have roles assigned

For the rest, the correct code to assign a role to `$user` requires to call `$user->addRole()` and `$user->save()`.

Since you want to assign the new role when a node is saved, I would rather use the following code.

```
use Drupal\Core\Entity\EntityInterface;
use Drupal\node\NodeInterface;
use Drupal\user\EntityOwnerInterface;
use Drupal\user\UserInterface;

/**
 * Implements hook_ENTITY_TYPE_presave() for nodes.
 */
function mymodule_node_presave(EntityInterface $entity) {
  if ($entity instanceof EntityOwnerInterface) {
    /** @var \Drupal\user\UserInterface $user */
    $user = User::load($entity->getOwnerId());
    $user->addRole('foobar');
    $user->save();
  }
}

```

# Answer
> 0 votes
I couldn't assign new roles in hook\_entity\_presave(). Maybe I just didn't find the right way to do it. Anyway, $entity-\>id() doesn't work for new users because the entity has not been saved yet, so there's no id...

My solution was to alter the registration form and add #default\_values for the roles I needed to be automatically assigned for the new user.

For example, I needed the new users to be created with the role Client (client).

```
function hook_form_user_register_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  $form['account']['roles']['#access'] = FALSE;
  $form['account']['roles']['client'] = array(
      '#default_value' => TRUE,
      '#disabled' => TRUE,
    );
}

```

This way, there's no need to make any alterations in hook\_entity\_presave().

---
Tags: 8, users
---


# Question
Title: How to find the url of a media library image field in a preprocess function?
I have a preprocess function that I am using to grab one of 3 profile images. Basically we're replacing a couple stand-alone image fields with a media library reference field. The goal is to display the media library image if it is populated at a particular style (called '65x65') or fall back to other stand-alone legacy image fields if it is not populated.

I'm having a heck of a time building the media library image url. I have tried quite a few things based on this discussion and this discussion and others. The image field on the image media type is called field\_media\_image (screenshot). I'm not sure if it's useful to post my half-baked code attempts so I will leave the parts that I know are working.

```
foreach ($block->field_block_contributors->referencedEntities() as $contributor) {
    # prefer the media library reference profile image if it exists

    if (!$contributor->field_profile_image->isEmpty()) { # check if there is a media library profile image

      $my_profile_image = $contributor->get('field_profile_image')->referencedEntities(); # seems to load this field ok
      if (!empty($my_profile_image)) {
      // kint($my_profile_image);
      # load a media library image using the '65x65' image style
      // $styled_image_url = ??????

      }
    } elseif (!$contributor->field_user_image->isEmpty()) { # or fall back to one of two legacy image fields.  This part works
      $styled_image_url = ImageStyle::load('65x65')->buildUrl($contributor->field_user_image->entity->getFileUri());
    } elseif (!$contributor->field_speaker_image->isEmpty()) {
      $styled_image_url = ImageStyle::load('65x65')->buildUrl($contributor->field_speaker_image->entity->getFileUri());
    } else {
      $styled_image_url = '';
    }
    $variables['profiles'][] = [
      'title' => $contributor->field_user_title->value,
      'link' => "/user/" . $contributor->uid->value,
      'image' => ['src' => $styled_image_url, 'attributes' => $contributor->field_user_image->value],
      'name' => $contributor->field_user_first_name->value . " " . $contributor->field_user_last_name->value,
    ];
  }

```

PS. Bonus beer for anyone who knows off the top of their heads how I can link to the user's name instead of their user id # ( the line 'link' =\> "/user/" . $contributor-\>uid-\>value, ) Thanks!!!

# Answer
> 4 votes
Maybe not the most elegant solution but I was able to pull it off with...

```
      if (!$contributor->field_profile_image->isEmpty()) { # check if there is a media library profile image

      $my_image = $contributor->get('field_profile_image')->getValue()[0]['target_id']; # Find ID of media library image
      $media_entity_load = Media::load($my_image); // Loading media entity.
      $uri = $media_entity_load->field_media_image->entity->getFileUri(); // It is for image media.
      $styled_image_url = ImageStyle::load('65x65')->buildUrl($uri); // Here you will get URL of uploaded image.

```

# Answer
> 2 votes
I wrote a little module I found helpful to get the media entity image values.

**custom\_helper\_services/custom\_helper\_services.info.yml:**

```
name: 'Custom Helper Services'
type: module
description: 'Provides useful services for working with themes.'
core_version_requirement: ^8 || ^9
package: 'Custom'

```

**custom\_helper\_services/custom\_helper\_services.module:**

```
<?php

/**
 * @file
 * Contains custom_helper_services.module.
 */

use Drupal\Core\Routing\RouteMatchInterface;

/**
 * Implements hook_help().
 */
function custom_helper_services_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    // Main module help for the custom_helper_services module.
    case 'help.page.custom_helper_services':
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('Provides helpful service functions.') . '</p>';
      return $output;

    default:
  }
}

```

**custom\_helper\_services/custom\_helper\_services.services.yml:**

```
services:
  chs:
    class: Drupal\custom_helper_services\Services\CustomHelperServices
    arguments: ['@entity_type.manager','@current_route_match','@entity.repository']

```

**custom\_helper\_services/src/Services/CustomHelperServices.php:**

```
<?php

namespace Drupal\custom_helper_services\Services;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Entity\EntityRepositoryInterface;

/**
 * Class CustomHelperServices.
 *
 * Provides helper services to be used in modules and themes.
 */
class CustomHelperServices {

  /**
   * Entity Type Manager.
   *
   * @var Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * Route Match Interface.
   *
   * @var Drupal\Core\Routing\RouteMatchInterface
   */
  protected $routeMatch;

  /**
   * Entity Repository Interface.
   *
   * @var Drupal\Core\Entity\EntityRepositoryInterface
   */
  protected $entityRepository;

  /**
   * CustomHelperServices constructor.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   Entity Type Manager.
   * @param \Drupal\Core\Routing\RouteMatchInterface $routeMatch
   *   The Route Match Interface.
   * @param \Drupal\Core\Entity\EntityRepositoryInterface $entityRepository
   *   The Entity Repository Interface.
   */
  public function __construct(EntityTypeManagerInterface $entityTypeManager, RouteMatchInterface $routeMatch, EntityRepositoryInterface $entityRepository) {
    $this->entityTypeManager = $entityTypeManager;
    $this->routeMatch = $routeMatch;
    $this->entityRepository = $entityRepository;
  }

  /**
   * Build our media style path service.
   */
  public function getMediaFieldImage($mediaField = NULL, $imageStyle = NULL) {

    if ($mediaField && $mediaField->entity) {

      // Get image.
      $mediaImage = $mediaField->entity
        ->get('field_media_image');

      // Get image attributes.
      $alt = $mediaImage->alt;
      $width = $mediaImage->width;
      $height = $mediaImage->height;
      $title = $mediaImage->title;

      // Get image entity.
      $mediaImageEntity = $mediaImage->entity;

      // Get image mime type.
      $mediaImageMimeType = $mediaImageEntity->getMimeType();

      // Get image file URI.
      $mediaImageEntityUri = $mediaImageEntity->getFileUri();

      // Set image URL.
      $url = file_create_url($mediaImageEntityUri);

      // Set image style URL.
      if ($imageStyle && $mediaImageMimeType !== "image/svg+xml") {
        $url = $this->entityTypeManager
          ->getStorage('image_style')
          ->load($imageStyle)
          ->buildUrl($mediaImageEntityUri);
      }

      return [
        'url' => $url,
        'alt' => $alt,
        'width' => $width,
        'height' => $height,
        'mimeType' => $mediaImageMimeType,
        'title' => $title,
      ];
    }

    return NULL;
  }

}

```

Then in preprocess node, for example, you can call:

```
$variables['myVariable'] = \Drupal::service('chs')->getMediaFieldImage($node->get("field_name"), "image_style");
// Just need to pass the media field name. The image style is optional.

```

Then in a Twig template you can access as:

```
{{ myVariable.url }}
{{ myVariable.alt }}
{{ myVariable.width }}
{{ myVariable.height }}
{{ myVariable.mimeType }}
{{ myVariable.title }}

```

I've stripped down this code to show just the media entity image get, so let me know if there are issues and I can update if needed.

---

A shorter version (using preprocess\_node()):

```
use Drupal\image\Entity\ImageStyle;
function MYTHEME_preprocess_node(&$variables) {
  $node = $variables['node'];
  if ($node->hasField('field_name') && $node->get('field_name')->entity) {
    $imageURI = $node->field_name->entity->getFileUri();
    $variables['myVariable'] = ImageStyle::load('image_style')->buildUrl($imageURI);
  }
}

```

---
Tags: theming, media
---