Problem/Motivation

In layout_builder_system_breadcrumb_alter() it assumes that all links are routable. However, breadcrumbs happily supports external links as well. When a non-routable link is encountered in that function the URL class will throw "UnexpectedValueException: External URLs do not have an internal route name".

Proposed resolution

Wrap the call to Url::getRouteName() with a check against Url::isRouted first.

Remaining tasks

Patch needed.

User interface changes

None.

API changes

None.

Data model changes

None.

Steps to reproduce

  1. Enable layout_builder module
  2. Check 'Use Layout Builder' from 'Layout Options' for a specific content type from its manage display settings page, for example for 'article' from /admin/structure/types/manage/article/display
  3. Then you have to open the page where you manage the layout via the layout builder from the 'Manage layout' button in the content type's 'manage display' page. For example for article from '/admin/structure/types/manage/article/display/default/layout'

I wasn't sure how to add an external link to the breadcrumbs via the UI, so I did it programmatically by adding two lines at the very top of layout_builder_system_breadcrumb_alter function inside of 'layout_builder.module'

  $external_link = $link = Link::fromTextAndUrl('External link', Url::fromUri('http://www.example.com'));
  $breadcrumb->addLink($external_link);

Comments

rwohleb created an issue. See original summary.

tim.plunkett’s picture

Version: 8.7.x-dev » 8.9.x-dev
Issue tags: +Needs tests

Oh, that's a really good point! Good find.

tim.plunkett’s picture

Issue tags: +Novice, +Blocks-Layouts

Tentatively tagging for novices. Please find me on Drupal Slack if you need guidance!

kris_nikolov’s picture

I am currently trying to do a first time contributation at Drupalcon Amsterdam 2019, and will have a look at this.

kris_nikolov’s picture

Issue tags: +Amsterdam2019
kris_nikolov’s picture

StatusFileSize
new1.17 KB

Checked to see if the URL has a Drupal route first, before calling getRouteName() on it.

kris_nikolov’s picture

Status: Active » Needs review
dinesh18’s picture

#6 looks good to me, it uses Url::isRouted which returns bool and Indicates if this Url has a Drupal route.
@breakfull , can you provide me steps how can I test it ?

kris_nikolov’s picture

Issue summary: View changes
kris_nikolov’s picture

@Dinesh18 Updated the issue's description with 'Steps to reproduce'

rwohleb’s picture

The patch in #6 should always return a bool from the anonymous function even when it's not a routed link. Otherwise looks good to me.

jmdeleon’s picture

StatusFileSize
new1.19 KB

Small fix to patch in #6, incorporating the suggestion in #11.

amd.miri’s picture

StatusFileSize
new1012 bytes

Alternative solution/code.

tim.plunkett’s picture

Status: Needs review » Needs work
Issue tags: -Novice, -Amsterdam2019

NW for tests

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

raman.b’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests
StatusFileSize
new2.46 KB
new3.64 KB
new2.69 KB

Adding some test coverage to demonstrate the reported bug

The last submitted patch, 17: 3090941-17-test-only.patch, failed testing. View results

tim.plunkett’s picture

Thanks! A couple small nits, and then I think this is ready.

  1. +++ b/core/modules/layout_builder/layout_builder.module
    @@ -336,7 +336,12 @@ function layout_builder_system_breadcrumb_alter(Breadcrumb &$breadcrumb, RouteMa
    +      if ($link->getUrl()->isRouted()) {
    +        // Check if URL is routable by Drupal. Because
    +        // external URLs do not have an internal route name.
    +        return $link->getUrl()->getRouteName() !== "entity.entity_view_display.$entity_type_id.default";
    +      }
    +      return TRUE;
    

    Can this be swapped?
    Adding if (!isRouted()) { return TRUE; } as a guard condition makes more sense to me. Returning TRUE at the end reads confusingly to me.

  2. +++ b/core/modules/layout_builder/tests/modules/layout_builder_test/layout_builder_test.module
    @@ -106,3 +110,23 @@ function layout_builder_entity_form_display_alter(EntityFormDisplayInterface $fo
    +  $external_link = $link = Link::fromTextAndUrl('External link', Url::fromUri('http://www.example.com'));
    +  $breadcrumb->addLink($external_link);
    

    No need for a local variable

  3. +++ b/core/modules/layout_builder/tests/modules/layout_builder_test/layout_builder_test.module
    @@ -106,3 +110,23 @@ function layout_builder_entity_form_display_alter(EntityFormDisplayInterface $fo
    +function layout_builder_test_module_implements_alter(&$implementations, $hook) {
    +  if ($hook === 'system_breadcrumb_alter') {
    +    $group = $implementations['layout_builder_test'];
    +    $implementations = [
    +      'layout_builder_test' => $group,
    +    ] + $implementations;
    +  }
    +}
    

    What is this moving to be after? Please add that as an inline comment.

raman.b’s picture

StatusFileSize
new3.57 KB
new2.35 KB

Thanks for the review!

Addressing the pointers from #19

What is this moving to be after? Please add that as an inline comment.

We need to move our hook_system_breadcrumb_alter implementation before layout_builder_system_breadcrumb_alter. Should we be more explicit about this? Currently we are moving before all the implementations

tim.plunkett’s picture

  1. +++ b/core/modules/layout_builder/layout_builder.module
    @@ -336,6 +336,11 @@ function layout_builder_system_breadcrumb_alter(Breadcrumb &$breadcrumb, RouteMa
    +        // Check if URL is routable by Drupal. Because
    +        // external URLs do not have an internal route name.
    

    I don't know that this comment is valuable, and I'm tempted to remove it. If you think it's worth keeping, then it should be outside the if() and should be one sentence. But to me "Check if the URL has a route before checking the route name" isn't helpful.

  2. +++ b/core/modules/layout_builder/tests/modules/layout_builder_test/layout_builder_test.module
    @@ -106,3 +110,24 @@ function layout_builder_entity_form_display_alter(EntityFormDisplayInterface $fo
    +    // Move our hook_system_breadcrumb_alter() implementation to run before
    +    // layout_builder_system_breadcrumb_alter().
    

    This is perfect, thanks!

raman.b’s picture

StatusFileSize
new3.46 KB
new706 bytes

Seems reasonable to get rid of it. We have several similar instances across core

joseph.olstad’s picture

Finally found this patch, we totally need to fix this... we just made a sandbox module called cms_breadcrumbs for leading external breadcrumbs, and we need an external link in our breadcrumbs

Lets get this patch in asap, what is left to do?

I will be testing this patch on D8.9.x

joseph.olstad’s picture

Status: Needs review » Reviewed & tested by the community

Great fix, tested on D8.9.x , same patch applies (with fuzz and some .orig files leftover, but applies cleanly)

  • catch committed 4676911 on 9.2.x
    Issue #3090941 by raman.b, breakfull, jmdeleon, amd.miri, tim.plunkett,...

  • catch committed 8b8ad2e on 9.1.x
    Issue #3090941 by raman.b, breakfull, jmdeleon, amd.miri, tim.plunkett,...
catch’s picture

Version: 9.2.x-dev » 9.1.x-dev
Status: Reviewed & tested by the community » Fixed

Committed/pushed to 9.2.x and cherry-picked to 9.1.x, thanks!

joseph.olstad’s picture

Thanks @catch and everyone above, team work!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.