Hook “More” Link In Views For Drupal 8

PROBLEM
I needed a way to modify the Custom URL in the Pager for the More link for Views. I added a Contextual Filter so that the particular display can be put onto any page and it will display items from the current office.

This URL needs to be dynamic and change depending on where on the site this view is placed.

What Views needs here is some tokens!

SOLUTION
I looked into hooking “container” but that seems messy as I’m not sure where else it’s used. It’s doable that way but I primarily tried to look at the view.

In my preprocess, THEME_views_pre_render, I tried to dig further into the ViewsExecutable class. It turns out, the display_plugin method was what I was looking for.

Here’s some of my code:

function THEME_views_pre_render(\Drupal\views\ViewExecutable $view) {
	// target your view and display
	if (($view->id() == "my_view") && ($view->current_display == "my_display")) {
		// this is just what's actually in the field to begin with... i.e. "success-story" in my screen shot above
		$ending_part_of_uri = $view->display_handler->getOption("link_url");

		// call a function or whatever to get the part of your uri
		$starting_part_of_uri = _THEME_function_to_dynamically_get_URI();

		// now set the link to whatever
		$view->display_handler->setOption("link_url", $starting_part_of_uri . $ending_part_of_uri));
	}
}

Turn Off Caching For Paragraph In Drupal 8

PROBLEM
I had an interesting one. I’ve blogged about Drupal 8 caching before and how annoying it is. I’ve solved the issue of caching custom blocks and caching on a specific view.

But what about paragraphs?

We needed to make content in a paragraph dynamic and change on every page load. We want a random item to display from an array of items. I noticed that this happened when the user was logged in but not anonymously. I don’t know if I have caching turned off for authenticated users or what.

After some digging, I couldn’t find a good way to turn caching off with config and I wasn’t about to turn caching off for every page. I also couldn’t find a way to do it in the preprocess of the paragraph.

SOLUTION
There’s kinda like a kill switch, I guess. I think we should probably use this sparingly as there’s a reason why Drupal caches so much (it is a HUGE footprint!).

I put this code inside my preprocess function of my paragraph in my .theme file:

\Drupal::service("page_cache_kill_switch")->trigger();

It took me a bit to find it as googling around doesn’t yield the results I’d expect.

And note, this is how I solved it for the mean time… This way might not be the best but it works. Until I am told that this is very dangerous or whatever and we find a better way, this is what I did.

Default Value Of Today’s Date On Timestamp In Drupal 8

PROBLEM
One of the stupidest things in Drupal 8 is the Timestamp field. If only I knew what I know now. All well, we’re stuck with it.

Its default value cannot be set to today’s date. When saving the field as null in the field settings, going to a new node, the default value is when you saved the field. Ours was two years ago since that’s when I started working on this site. So default dates for our Timestamp field were like “09/04/2018 02:43:02 PM”.

Not good. How do you make the default date the date of the new node (like today’s date and time)?

SOLUTION
I first installed a patch on a ticket on drupal.org (patch #14) which allowed the field setting to be null. Since the Timestamp field sucks, it doesn’t give you the option of today’s date like the Date field does. So, I had to add to our custom module the way to do that.

// see if we need to worry about the publication date
if (isset($form["field_publication_date"])) {
	// if it's a new form
	if ($form_id == "node_" . $your_content_type . "_form") {
		// set the default value to a new date time (which by default is "now")
		$form["field_publication_date"]["widget"][0]["value"]["#default_value"] = new DrupalDateTime();
	}

	// end if we have a field_publication_date
}

This code can go inside a preprocess like hook_form_alter. You also don’t need to do it the way that I did with the $form_id. There might be a new node hook somewhere that you could add this to maybe. Obviously, the field_publication_date should be changed to whatever your Timestamp field machine name is called.

Drupal 8 Composer/Drush Errors Plugin Does Not Exist

PROBLEM
You done f**ked up. You did a $ composer uninstall on a module before you disabled it. You go in to import or do something else drush related. Boom! Errors!

I recently received the following errors when I accidentally did this with views_accordion:

The "views_accordion" plugin does not exist. Valid plugin IDs for Drupal\views\Plugin\ViewsPluginManager are: entity_reference_revisions, serializer, data_export, default, default_summary, entity_reference, grid, html_list, opml, rss, table, unformatted_summary
[warning] include_once(/appdata/www/web/modules/contrib/views_accordion/views_accordion.module): failed to open stream: No such file or directory Extension.php:147
[warning] include_once(): Failed opening '/appdata/www/web/modules/contrib/views_accordion/views_accordion.module' for inclusion (include_path='/appdata/www/vendor/pear/archive_tar:/appdata/www/ACF-D8-Enterprise/vendor/pear/console_getopt:/appdata/www/ACF-D8-Enterprise/vendor/pear/pear-core-minimal/src:/appdata/www/vendor/pear/pear_exception:.:/opt/rh/rh-php72/root/usr/share/pear:/opt/rh/rh-php72/root/usr/share/php') Extension.php:147

SOLUTION
You will need to re-install the missing module again, then do a cache refresh, then disable it, then uninstall it. Like the following:

$ composer require drupal/views_accordion
$ drush cr
$ drush pm:uninstall views_accordion
$ composer remove views_accordion
$ drush cr

The first $ drush cr refreshes the cache. This step is very important because it then determines the link to the “newer” views_accordion module. That was the key! Then you should be able to disable the module and uninstall it the correct way.

Need to remember this for later! Mistakes happen.

BONUS
Here’s a command I put in my “to remember” pile as well. If you get an error about The following module is missing from the file system... error, you can do the following:

$ drush sql-query "DELETE FROM key_value WHERE collection='system.schema' AND name='the_module_its_bitching_about';"

The the_module_its_bitching_about is the machine name of the module it’s bitching about.

Including Bold Font In Drupal 8 Library

PROBLEM
In Drupal 8, we are now putting everything in a libraries.yml file for the theme. When using fonts from other sources (HTML5), you need to list it like such:

global:
  css:
    theme:
      dist/style.css: {}
      https://fonts.googleapis.com/css?family=Source+Sans+Pro: {  }

Doing it this way is just fine and dandy but what about the specific bold? If you look at the Source Sans Pro on google fonts, you see this font does Bold, Italic, etc.

However, in practice, if you just include the code above and make the font bold in the style, bold isn’t really bold. It’s more like a different color; not really bolding the text:

SOLUTION
There are no notes in Drupal of how to include the different boldnesses or anything of different fonts. I kinda had to play around with it. With the google fonts, you can select the different fonts you want and it will give you what you need to include in your page… But that’s no good for Drupal 8 since it’s using yml files!

In order to get the font to be the real bold text, I put the following in the yml:

global:
  css:
    theme:
      dist/style.css: {}
      https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700: {  }

So just add the :400,700 to the end of the thing. Now it looks correct:

Views Exposed Filter Query Strings

PROBLEM
This post is just something I found out that I will need to remember.

With views, when I have an exposed filter, what ends up in the query string matters. I would like a clean look so the URL can be something like ?taxonomy=research . I had set up a view so it does do that but then when I went to set up another one, it used the taxonomy id (i.e. ?taxonomy=345 ). How did it get that way?

SOLUTION
It’s all about how you set up the exposed filter in the view. In your view, in the Settings, next to the exposed filter under FILTER CRITERIA, when you select what specific taxonomy to use, you’ll see an option at the very bottom for Selection type. This could be either Dropdown or Autocomplete.

If you select Dropdown, it will use the taxonomy id in the URL (i.e. ?taxonomy=345 ).

If you select Autocomplete, it will use the taxonomy name in the URL (i.e. ?taxonomy=research ).

Have to remember that. But it would be nice for Views to give you the option.

Disable Caching On Specific View In Drupal 8

PROBLEM
I may be the only one who struggled with this for a good half hour… and in Drupal time, that’s nothing. I have a view that’s a block of a random piece of content. Unfortunately, Drupal caches everything. So how do I get it to display something random if it doesn’t display something random until the cache is refreshed in Drupal?

SOLUTION
Turns out it was easy! (It’s not easy to find the solution online, however.)

In the view, under the Advanced tab, there is Caching. Turn that to none.

That’s it! Have to remember that!

Composer/Drush Notes For Drupal 8

While I had some time, I thought I might create a post for some composer and drush commands that I use A LOT… just for a reference.

This list is for Drupal 8/Drush 9.

Command What It Does
$ drush cr Clears cache (duh; you use this 1239832 times a day)
$ drush en coder Enable a module (ex. coder)
$ drush pmu coder Disable a module (ex. coder)
$ composer require drupal/coder Install a module (ex. coder)
$ composer remove drupal/coder Uninstall a module (ex. coder); disable it first!
$ composer require drupal/migrate_tools:4.0-beta2 Install a specific version of a module (ex. migrate tools)
$ drush sql-dump –-result-file=’/path/to/database/backup.sql’ Get a database backup easy
$ composer update Update Drupal 8 core & modules
$ drush updb Update the database after a Drupal core and/or module update (duh; do this after you run composer update; same as going to update.php)
$ drush status Tells you important information such as Drupal version, Drush version, etc.
$ drush sqlc Get into the database (MySQL) easy
$ drush uli Get a temporary URL to log into the Drupal site to bypass password; good for when you lose your password or your account is locked out and you’re the admin
$ drush sql-connect The database connection string
$ composer -V The version of composer you’re running
$ drush user-create janny_m –mail=”my_email@yahoo.com” –password=”my_p@ssword” Create a user (janny_m) with email and password
$ drush user-add-role “administrator” janny_m Make a user an administrator
$ sudo /usr/local/bin/composer self-update Update composer

Making Nice Menus Responsive In Drupal 8

PROBLEM
I highly recommend Nice Menus for Drupal. I’ve been using it for years and it makes things pretty simple. It’s easy to style over to what you need plus it’s keyboard accessible (which is the huge one)!

Anyway, as far as mobile-friendly/responsive goes, Nice Menus is not. It lacks in that department.

So how do we style it? How do we make it a hamburger menu?

SOLUTION
Turn off nice menus! It kinda sucks, but you need to do it in order to put things into your menu template and create mobile-friendly design.

First, to turn off nice menus, put something like this in a JS file (which DOES use JQuery so be careful; that’s another post for another day):

// we only need to do stuff if it's on mobile
if ($(window).width() < 770) {
	// let's remove the nice menus on resolutions that are 768 and lower
	$("ul.office_main_top_level").removeClass("nice-menu");
	$("ul.office_main_top_level").removeClass("nice-menu-acf-main-navigation");
	$("ul.office_main_top_level").removeClass("nice-menu-down");
	$("ul.office_main_top_level").removeClass("sf-js-enabled");
	$("ul.office_main_top_level").removeClass("sf-arrows");
	$("ul.office_main_top_level").removeClass("nice-menus-processed");
}

Removing those classes will turn off the JavaScript that’s happening with Nice Menus for keyboard accessibility, styles, etc.

Now add something like this to your template at the top (but outside the first ul):

<a href="javascript: void(0);" id="office_main_nav_hamburger"><i class="fa fa-bars"></i><span class="hide">Expand</span></a>

(I’m using Font Awesome for icons.)

Add this after the links in the lis (only if they have children):

<a href="javascript: void(0);" class="expand_link"><i class="fa fa-angle-down"></i><span class="hide">Expand</span></a>

Add some more stuff to your JS file (I did it inside the if window statement above):

$("#office_main_nav_hamburger", context).click(function() {
	$("ul.office_main_top_level").toggle();
	$("#office_main_nav_hamburger i").toggleClass("fa-times fa-bars");
});

$(".expand_link", context).click(function() {
	$(this).closest("li").children("ul.office_main_sub_level").toggleClass("expand_sub_level_mobile");
	$(this).children("i").toggleClass("fa-angle-down fa-angle-up");
});

I won’t walk you through all the CSS but at least now you may have a understanding of how to make Nice Menu’s responsive.

Empty Region Displaying In Drupal 8

PROBLEM
This is a strange one but one that’s been talked about many, many times in Drupal 8. In Drupal 8, if you check to see if a region is empty, it isn’t empty. You need to do a few things to adequately determine if a region is truly empty.

See these:

https://www.drupal.org/node/953034
https://www.drupal.org/forum/support/module-development-and-code-questions/2016-04-07/drupal-8-regions-with-and-empty
https://drupal.stackexchange.com/questions/175389/how-do-i-properly-detect-if-region-is-empty

So, I would like to determine if a block is empty before I display it so I can put a Bootstrap wrapper around it. However, just using:

{% if page.left_sidebar %}
	<div class="left_sidebar">{{ page.left_sidebar }}</div>
{% endif %}

won’t work. To give some background, the region on most pages has a secondary menu (a left navigation). However, on pages where this doesn’t display, it would display an empty region even though there was no menu there.

SOLUTION
At first I did:

{% if page.left_sidebar|render|striptags|trim %}
	<div class="left_sidebar">{{ page.left_sidebar }}</div>
{% endif %}

and this seemed to work! However, today, when I got to putting a webform in a region, it didn’t work. Striptags for some reason took out everything. Come to find out Webforms use some sort of “lazyBuilding” or something I don’t give a shit about. #stupidShit

Anyway, now my code above doesn’t work. But buried deep in one of those links above is this gem:

{% if page.left_sidebar|render|striptags("<drupal-render-placeholder>")|trim %}
	<div class="left_sidebar">{{ page.left_sidebar }}</div>
{% endif %}

This seems to work for the moment. If this turns out to not work later (which it might not for other needs… or in like 10 minutes when I find another issue), I will re-evaluate.