Problem/Motivation

Menu items accept paths with a language but ignore that information when rendering. Steps to reproduce:

1. Install Drupal and have with at least two languages (say English without a path prefix and Norwegian with 'no' as prefix).
2. Create two menu items in main navigation to /node and /no/node. Both are saved.
3. Edit both, they retained what you entered.
4. Go to the front page. Both link to either /node (if page is in English) or /no/node (if page is displayed in Norwegian).

It is expected that they would consider the language entered in the path.

Proposed resolution

Validate the entered path and error if the path is going to be modified by Drupal (e.g. remove language prefix).
The workaround for linking to content in a different language must be using absolute urls.

Remaining tasks

Add a test.

User interface changes

If a url is going to be modified by Drupal on rendering, prevent it and show an error instead of ignoring it (and link to something the user does not expect).

API changes

None.

Beta phase evaluation

Reference: https://www.drupal.org/core/beta-changes
Issue category Bug because a url is allowed on a menu but then another one is rendered.
Issue priority Major because ... Critical/Not critical because ...
Prioritized changes The main goal of this issue is usability. Avoids confusion of people creating multilingual sites./td>
CommentFileSizeAuthor
#146 drupal-menu_link_path_lang_prefix-2462279-146.diff12.81 KBjames.williams
#145 menu_link_path_lang_prefix-2462279-145.patch12.81 KBsteven jones
#144 menu_link_path_lang_prefix-2462279-144.patch12.81 KBjames.williams
#137 menu_link_path_lang_prefix-2462279-137.patch12.74 KBjames.williams
#134 interdiff_133-134.txt3.24 KBnikitagupta
#134 2462279-134.patch12.77 KBnikitagupta
#133 interdiff_132-133.txt1.32 KBravi.shankar
#133 2462279-133.patch12.73 KBravi.shankar
#132 interdiff_130-132.txt3.05 KBravi.shankar
#132 2462279-132.patch12.7 KBravi.shankar
#131 interdiff_130-131.txt3.05 KBravi.shankar
#131 2462279-131.patch12.67 KBravi.shankar
#130 rawdiff.txt10.35 KBravi.shankar
#130 2462279-130.patch12.67 KBravi.shankar
#127 menu_link_path_lang_prefix-2462279-127.patch12.61 KBjames.williams
#125 menu_link_path_lang_prefix-2462279-125.patch12.42 KBjames.williams
#123 h_1517300540_2545325_9ff462e0e6.jpeg45.65 KBqzmenko
#118 2462279-118.patch12.62 KBjofitz
#116 2462279-116.patch12.58 KBjofitz
#111 interdiff-2462279-101-111.txt13.2 KBeiriksm
#111 language_prefix_for-2462279-111.patch13.08 KBeiriksm
#101 language_prefix_for-2462279-101.patch7.54 KBmac_weber
#97 interdiff-2462279-94-97.txt5.16 KBeiriksm
#97 language_prefix_for-2462279-97.patch7.52 KBeiriksm
#94 interdiff-92-94.txt8.15 KBeiriksm
#94 language_prefix_for-2462279-94.patch5 KBeiriksm
#92 interdiff-89-92.2462279.txt2.52 KBeiriksm
#92 language_prefix_for-2462279-92.patch5.73 KBeiriksm
#89 interdiff-2462279-87-89.txt1.42 KBeiriksm
#89 language_prefix_for-2462279-89.patch5.56 KBeiriksm
#87 interdiff-81-88.txt2.28 KBeiriksm
#87 language_prefix_for-2462279-87.patch5.32 KBeiriksm
#81 language_prefix_for-2462279-81.patch4.94 KBeiriksm
#78 interdiff-2462279-72-78.txt1.89 KBeiriksm
#78 language_prefix_for-2462279-78.patch4.91 KBeiriksm
#72 interdiff-2462279-67-72.txt1.03 KBeiriksm
#72 language_prefix_for-2462279-72.patch4.72 KBeiriksm
#65 interdiff-2462279-57-65.txt2.44 KBeiriksm
#65 language_prefix_for-2462279-65.patch4.63 KBeiriksm
#57 language_prefix_for-2462279-57-test-only.patch1.1 KBeiriksm
#57 language_prefix_for-2462279-57.patch4.71 KBeiriksm
#47 language_prefix_for-2462279-47.patch3.61 KBeiriksm
#46 language_prefix_for-2462279-46.patch3.74 KBeiriksm
#41 interdiff2462279-25-41.txt3.71 KBeiriksm
#41 language_prefix_for-2462279-41.patch3.35 KBeiriksm
#39 interdiff2462279-25-39.txt3.05 KBeiriksm
#39 language_prefix_for-2462279-39.patch2.69 KBeiriksm
#25 not_able_to_create_menu-2462279-25.patch2.08 KBeiriksm
#10 2462279-10.patch950 byteseiriksm
#7 2462279-7.patch632 byteseiriksm
#5 2462279-5.patch650 byteseiriksm
#1 do-issue.gif601.65 KBeiriksm

Comments

eiriksm’s picture

StatusFileSize
new601.65 KB

Added an animated gif for illustration

dawehner’s picture

Interesting, yes, I'd expected the same behaviour

eiriksm’s picture

OK, so I took a second look at this, digging a bit deeper. I can reproduce this with the following code (run with drush scr):

$link = entity_create('menu_link_content', array(
  'title' => 'test',
  'link' => ['uri' => 'internal:/en']
));
$link->save();
print_r($link->getUrlObject()->toString());

Expected output: "/en"
Output: "/node"

I have confirmed that this is saved like "internal:/en" in the database, so it has to be while transforming from the Url object to a string. Or something. In some way. But wow, there's a lot of code to go through in Drupal 8 :p

eiriksm’s picture

So, seems that the problem is that /en (correctly) resolves to the route that is front page (in my case "view.frontpage.page_1"). The problem is that when I am loading this, some module should be responsible for also passing in the language code to the Url constructor. If I hack in language code for my route, the output is as expected.

Not sure how it is done, but seems to me that some module (language maybe?) should hook into the loading of the Url object inside the entity MenuLinkContent and make it understand that paths with /en prefixes are in english? Because it is when I call getUrlObject that the path gets loaded into a Url object (without the language).

Any pointers here, and I would be glad to provide a patch!

Or maybe this is just me...?

eiriksm’s picture

Status: Active » Needs review
StatusFileSize
new650 bytes

So, the problem, as explained, is that links generated by the menu module does not get a language attribute passed on to the link function.

I am a little unsure about what the solution is here, but it seems to me that it is supposed to be possible to link to the english frontpage. Also, this could be regarded a regression from Drupal 7, where you were in fact not able to save a menu link with the path "en".

Now, I tested 2 solutions that both worked. The first one is adding the language from linkItem to the url generation. This also had the (possibly) unwanted effect of generating english shortcut URLs on a default install. But somehow I feel that makes sense as well, as they are marked as english entities. Anyway, that seems like the wrong thing to do, and it possibly has a lot of other issues as well.

The other solution was to set the lanuage option in the MenuLinkContent entity. As attached in a patch, just to see if I break some tests.

I guess the third and fourth option would be:
- Add some logic to the Url class, to strip language prefixes, and hand them in as options.
- Just make it illegal to enter language based URLs in one form or another (not sure if this should be in menulinkcontent, linkitem or url)

Any thoughts? Anyone?

gábor hojtsy’s picture

Issue tags: +D8MI, +language-base

I agree that you should be able to create menu items to pages in specific languages. At least you should definitely be able to link whatever content on your site with absolute links, those would not be resolved as internal and not converted like this. As a workaround to begin with.

As for why this resolves to the front page, if you use a /content/my-first-article path, that would also resolve to its proper internal link. Resolving language is one of the processing stages happening, so not sure how should we ignore it when processing the link from the menu item. In my view it makes more sense to try to get the language from the link and pass it over. I don't have enough knowledge about the menu system to be able to tell if your patch fixes the right place the right way, but it makes sense looking at it in isolation at least :)

eiriksm’s picture

StatusFileSize
new632 bytes
you should definitely be able to link whatever content on your site with absolute links, those would not be resolved as internal and not converted like this. As a workaround to begin with.

Sure, workarounds are plenty, that's really no problem for my specific case. I was more considered about other (possible first-time) users trying the same.

In my view it makes more sense to try to get the language from the link and pass it over.

That was actually my first considered patch (this also works), but I was worried about the implications. I mean, links are used all over the place. But out of curiosity (and because this is a good point), here is a patch trying to do that instead. Let's see if that breaks something :)

Status: Needs review » Needs work

The last submitted patch, 7: 2462279-7.patch, failed testing.

gábor hojtsy’s picture

+++ b/core/modules/link/src/Plugin/Field/FieldType/LinkItem.php
@@ -170,7 +170,11 @@ public static function mainPropertyName() {
+    $link->setOption('language',
+      \Drupal::languageManager()->getLanguage($this->getLangcode())
+    );

Not sure how is this expected to work. What would ensure that the parent's langcode is the langcode of this URL?

eiriksm’s picture

Status: Needs work » Needs review
StatusFileSize
new950 bytes

There is nothing that ensure that, actually. I tried to point it out in the above posts. As a matter of fact, the only way to still for example be able to "manually" (as in programatically) link to /en, when you try to resolve the path via Url:fromRoute is if the resolving of the route would handle this. Which is a pretty "low-level" fix for this issue, I would say.

So let's try that, see what the tests say about that.

Turns out, it is the processing of incoming requests that strips the prefix. Which is both used for the handling of routes and of links. Which, of course, is a great thing. So this patch just tries to override that after the stripping has taken place.

This also fixes the issue, by the way. Let's see if I break some tests with it :)

Status: Needs review » Needs work

The last submitted patch, 10: 2462279-10.patch, failed testing.

gábor hojtsy’s picture

@eiriksm: I am not sure you are on the right track, the language codes are absolutely not necessarily the path prefixes and there also may be any other number of path prefixes, suffixes, eg. you might have a language negotiation provider that works off of a path suffix (which I've seen at a client for example). Assuming position and content of a language path prefix at such a general place as the Url class sounds like way too many assumptions.

Comparing the three proposed variants, your first patch still looks like the sanest to me if it works :)

eiriksm’s picture

I Agree. And the last one was more a test I would say. :p

So lets try #1. Probably needs a failing test then...

gábor hojtsy’s picture

Issue tags: +sprint
gábor hojtsy’s picture

Issue tags: +medium
gábor hojtsy’s picture

Issue summary: View changes

Updated the issue summary with easier steps to reproduce, and a problem with the expectation is apparent to me. If you create a link to /node and the default path prefix is empty then should it always stick to your site default language? Then how is it possible to create menu items that are NOT tied to a specific language? Would that only be possible if all languages have a prefix? That sounds like a problem. So it looks to me that this needs to be somehow configurable other than in the path itself.

gábor hojtsy’s picture

Issue summary: View changes
dom.’s picture

Hi !
Actually using /node I would expect to be moved to homepage in the langue of the last seen page, because going to admin/config/regional/language/detection you will see that this is as expected :
1- Move to /no/node this is your translated homepage
2- Move to /node by clicking on the link. Because of detection you will

  • URL : check the language to point to from URL. It is not, so moving to next detection
  • Session: because you were on the no page, I guess your session is now somehow linked to this language

Am I the only one that think this is expected ?

However, trying with specific language links: having three links to enabled languages : /ar/node, /fr/node, /en/node moving from one to the other does not work properly, both with and without patch #5.

eiriksm’s picture

Yes, it is expected.

But I would also want it to be possible to link to /en from my current language in a menu containing other things that are related to the current language. And it is counter-intuitive that if I enter /en the link ends up as /node (especially for a new user).

Re your last paragraph (I should probably explain this in the summary). It works with #5 if you do this (granted not great UX, but still better than not being able to link to it):
- Enable content translation for menu items.
- Create your menu in the language you want.
- Add a language specific link specifying it as a item in that language

So, for example. If my site is in Norwegian and this is specified with no prefix. I add a menu item linking to /en, and specify this link item as english. This item will link to /node without the patch, and /en/node (given that the front page is /node, of course) with the patch.

Was that understandable?

gábor hojtsy’s picture

@eiriksm: each menu item has their own language, you don't need content translation module to have language on menu items; I removed the content translation module step from the steps to reproduce because it complicates it unnecessarily;

The menu item language was/is supposed to specify the language in which you specified the title and the description. It is still possible that you may translate the menu item or the same title may lead to a page in another language, eg. a menu item "Drupal" even if untranslated would lead to pages in other languages if a different language is used. How should the language configured for the menu item enforce anything about what language is used to display the menu item? (Each menu item entity has a language itself).

eiriksm’s picture

I am not sure, really. And I agree, it might not be the best approach.

So, let's turn it a bit upside down:

The question is, what should happen if I have a language with path prefix /en and I try to make a link to /en. To me, either one of these makes sense:
1. The menu item creation shows an error (Sorry, this is not a valid path), thus forcing the user to probably solve it other ways (i.e creating a link with an absolute path). This is by the way how it works in Drupal 7 if I remember correctly.
2. The menu item resolves to the /en frontpage, regardless of language on the menu item (as pointed out by @Gábor Hojtsy, this is for translation, not saving metadata about the path).

I think not, however, that it should be saved to the database as /en, but result in a user thinking they saved a link to the /en frontpage, when it ends up showing the frontpage of the current language. This is how it works now.

If we go for one of these solutions, I think these should be solved like this:
1. A form validation of some sort. Not sure how this could be implemented less ugly than the hacky Url test i did eariler.
2. The Url class should be responsible of resolving /en to /en/node. As @Gábor Hojtsy pointed out, this is probably not the easiest solution.

Other solutions? Thoughts? And please feel free to tell me if this just sounds like an edge case, and it probably is just me :)

gábor hojtsy’s picture

@eiriksm: If it is not possible to save a menu link with language metadata and the menu link is resolved without language, then it should throw an error AFAIS.

eiriksm’s picture

@Gábor Hojtsy: Hey. Been on vacation the last week and just saw this (and your ping in IRC 5 days ago)

Your suggestion makes kind of sense. I'll see if I can try to create a patch for that. I have a feeling it will break some tests though, but I'll make a try one of the next days. Do you mean it should throw an error when it tries to display it or when you save it? Because ideally the user should also be warned in some way, I think, when the they try to hit the save button on the create menu link page.

gábor hojtsy’s picture

When you save it. Not worth bothering site users with errors IMHO :)

eiriksm’s picture

Status: Needs work » Needs review
StatusFileSize
new2.08 KB

OK, so here is a completely different take on it.

I tested this with a range of inputs, and all links still saves as expected, except for if you try to save for example (as described in the summary) a link to the front page of another language. This will now show an error (currently "The link you entered is not valid").

Still needs tests, but at least this is the current take on it :)

Edit: I meant completely different from the last patch, not from the last few comments :)

Status: Needs review » Needs work

The last submitted patch, 25: not_able_to_create_menu-2462279-25.patch, failed testing.

eiriksm’s picture

Some of the fails poses an interesting question, which makes me question I am attacking this the right way:

If I have a multilingual site -say, English and Norwegian - and use the site in Norwegian (with a /nb prefix). I then go to add a new menu link, which I want to be /admin/structure/menu.

Now, in the Norwegian context, should trying to add /admin/structure/menu be an error? In theory that is the same thing as linking to the front page of another language (except it is linking to an admin page in another language)

In Drupal 7 this would result in adding the link, and linking it to /nb/admin/structure/menu if viewing the site in Norwegian, and /admin/structure/menu in English. Should we aim for that?

That would imply also that if you try to add a link to /nb it would be an error, but /node would be cool, and also end up linking to their respective language frontpage when switching context.

gábor hojtsy’s picture

Title: Not able to create menu link to the frontpage of other languages » Language specific menu items are saved but not used, always rendered with current language

Let's retitle this. I'll try to get a menu maintainer on this, so we know what they expected instead of wondering around different approaches that all have some legitimacy on their own.

pwolanin’s picture

So, I would agree that we should be storing the language option and using it to render the link if the user enters an explicit valid prefix.

However, the code in #10 looks a bit too crude.

The bug seems to rather be in PathValidator::getUrlIfValidWithoutAccessCheck() and/or in \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl which is detecting a language prefix but not setting anything onto the request.

It's not clear how Drupal actually sets the current page language from that.

gábor hojtsy’s picture

Priority: Normal » Major

@pwolanin: well, there may be many ways a link might have language, it may be domain based or a suffix in the URL (with a contrib language negotiation plugin), etc.

One idea above was to inherit the language of the item for generating the URL. I think that has limited use because the language of the item is supposed to tell us what language was used to input the label/description of the menu item. It should be possible to create an English menu item to node/1 and have it translate to other languages with its title and description and link to /fr/node/1, /de/node/1, etc. So whether we want to force the link language vs. whether we want to just define the language of the label/description look to be two different things.

And indeed, the menu item has a langcode on its own, while the link has an options array stored which lets us specify explicit language for the link if desired. So currently the problem is even if we attempt to specify that in the input (with path prefix), that is saved but is not actually used. One potential answer is menu items are not supposed to be possible to be forced to a language unless a full URL is specified, which would not be very deployment/staging environment friendly (to hardcode live links). Another potential answer is we should have a separate UI element to force it. And finally an answer could be to let it be specified at least with a prefix/suffix/whatever the language negotiators support and then adhered.

pwolanin’s picture

Title: Language specific menu items are saved but not used, always rendered with current language » Language prefix for custom menu link paths are saved but not used,
Version: 8.0.x-dev » 8.1.x-dev
Category: Bug report » Feature request
Status: Needs work » Postponed

So, I think this is really a feature request.

The inbound path processor will strip off a valid language and find the route in a language independent way.

This doesn't seem like a regression from Drupal 7

Adding options to select the language to the menu link edit form sounds like a new feature that is out of scope for beta.

If anything is in scope for 8.0.x, it would be adding a validation error if the path includes a language prefix, or just a DSM saying it will not be used.

pwolanin’s picture

Title: Language prefix for custom menu link paths are saved but not used, » Language prefix for custom menu link paths are saved but not used, need a way to select link language
gábor hojtsy’s picture

Title: Language prefix for custom menu link paths are saved but not used, need a way to select link language » Language prefix for custom menu link paths are saved but not used
Version: 8.1.x-dev » 8.0.x-dev
Category: Feature request » Bug report
Status: Postponed » Needs work

Let's at least fix the bug? The title I gave was a perfect explanation of the bug... The prefix is saved but then not used. We can resolve the bug without introducing a feature. (We could also resolve the bug with introducing a checkbox or something along the lines to force the language, but I can see how that is 8.1.x if the menu maintainers did not intend to support that in 8.0.0).

@pwolanin: what do you think of #25 for validation?

eiriksm’s picture

For the record, I have a local modification which fixes a couple of the tests errors from #25 (those regarding "front" as a link).

To add to the discussion if this is a regressions or not:

In Drupal 7 (clean install) if you enter "asdasda3wedwad" as a new custom link, it will tell you it is invalid.

In Drupal 8 you can enter whatever, for example "asdasda3wedwad" as a new custom link.

Not sure if that makes it more of a regression or not, just adding it to the discussion :)

pwolanin’s picture

In 8, indeed, we allow you to enter a not-yet or maybe-never valid path. That is an important feature compared to 7.

#25 seems ok as a start, though the error message is not very helpful. Is language prefix the only case we care about?

penyaskito’s picture

Issue summary: View changes

Summarized last agreements in IS.

gábor hojtsy’s picture

In 8, indeed, we allow you to enter a not-yet or maybe-never valid path. That is an important feature compared to 7.

All right, this makes it pretty hard then to figure this out, no? I mean the current validation attempts to resolve the path and if it was not-yet or maybe never valid, then those paths would not be allowed according to this validation, no? Or would Url::fromUserInput() allow and return nonexistent paths?

penyaskito’s picture

Actually, there is some weird behavior on the not-yet valid path. If I create a menu link linking to node/2 and it does not exist yet, and I create it later, the menu item is not marked as active. Editing the menu link and saving again fixes it.

Tested with patch and without the patch attached, and the behavior in both cases for adding a link to /es/node/1 is the same, the link is created, but it does not switch the language but point to the current language instead.

eiriksm’s picture

Ok here is a new one.

I see the validate function has been removed in favour of validateForm (which probably was why you did not see any difference when testing it).

So, here i am allowing unknown paths, but setting an error if the path resolves to a path without a prefix. (for example /nb/node resolves to /node). Let's see if the tests are green :)

Now, on to the problems:

- Say I create a new menu link called /nb/node/1. This does not exist. Then I create node 1. The menu is now pointing to /nb/node/1 regardless, but only until the cache for the menu is cleared.
- Problem 2. When the cache is cleared, the menu link will point to /node/1 in the respective language. Which is what we want. Except the link that is saved (and working as we want) is still /nb/node/1. So editing for example the title on this link will now throw a validation error. Very confusing for a user.

Not sure what the best solution for these problems are?

For problem 1, I guess we could clear the menu cache on a node save. In some clever way.
For problem 2, I guess we could loop through (or select individually) menu items which corresponds to the new node, and then warn the user. But then again, this does not only apply to nodes, but to all menu paths. Or I guess we could change the menu path on form load, to be the one it resolves to?

I must say, both these things seem unwanted. Anyway, I guess validation at this point is one step forward at least. Thoughts?

eiriksm’s picture

Status: Needs work » Needs review
eiriksm’s picture

Disregard that last one, forgot to put back a few lines.

The last submitted patch, 39: language_prefix_for-2462279-39.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 41: language_prefix_for-2462279-41.patch, failed testing.

The last submitted patch, 41: language_prefix_for-2462279-41.patch, failed testing.

eiriksm’s picture

StatusFileSize
new3.74 KB

OK, so this should hopefully fix the tests.

eiriksm’s picture

Status: Needs work » Needs review
StatusFileSize
new3.61 KB

Sorry, that was with a couple of debug statements.

Status: Needs review » Needs work

The last submitted patch, 47: language_prefix_for-2462279-47.patch, failed testing.

eiriksm’s picture

Hm. A bit at loss here. The tests that are marked as failing is passing for me (same php minor version). And that other test run (the jenkins one) is passing all of them. Not sure which one to believe :)

Of course it is true that this one still "needs work", as we need a test, but would have been nice to get some reliable test results...

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 47: language_prefix_for-2462279-47.patch, failed testing.

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 47: language_prefix_for-2462279-47.patch, failed testing.

penyaskito’s picture

It passes for me locally too. jthorson had a look at the testbot machine at NYCCamp and we could not find anything obvious about the difference.

eiriksm’s picture

Hm. So what do we do? Ignore the failing tests?

penyaskito’s picture

One difference between de new CI bots, my local env and the "old" QA bots is that the latter use directories in the installation. Tried to run the tests doing the same locally, and they passed. So this was not the culprit.

eiriksm’s picture

Status: Needs work » Needs review
StatusFileSize
new4.71 KB
new1.1 KB

OK, so here is with a real simple test also. No interdiff, since the test is the actual diff (and it is provided as a test-only patch).

Let's see if the old testbot does something exciting with this one.

The last submitted patch, 57: language_prefix_for-2462279-57.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 57: language_prefix_for-2462279-57-test-only.patch, failed testing.

eiriksm’s picture

Status: Needs work » Needs review

I'm marking as needs review anyway. Should we do more tests, should we change the error message, for example?

The last submitted patch, 57: language_prefix_for-2462279-57.patch, failed testing.

gábor hojtsy’s picture

One substantial review item, one minor:

  1. +++ b/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
    @@ -91,6 +100,49 @@ public function form(array $form, FormStateInterface $form_state) {
    +    // other errors, because to try to construct a URL from invalid input throws
    +    // an exception, and the user will already be notified from the parent
    +    // validation.
    +    if (!$form_state->hasAnyErrors()) {
    

    We should check if *this* element had an error already no? If some other element had an error (eg. you forgot to fill in a required field), this error is not supposed to be "kept secret" IMHO.

  2. +++ b/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
    @@ -91,6 +100,49 @@ public function form(array $form, FormStateInterface $form_state) {
    +        // changing this URL to something else. For example if a user is entering
    

    More than 80 chars

gábor hojtsy’s picture

One substantial review item, one minor:

  1. +++ b/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
    @@ -91,6 +100,49 @@ public function form(array $form, FormStateInterface $form_state) {
    +    // other errors, because to try to construct a URL from invalid input throws
    +    // an exception, and the user will already be notified from the parent
    +    // validation.
    +    if (!$form_state->hasAnyErrors()) {
    

    We should check if *this* element had an error already no? If some other element had an error (eg. you forgot to fill in a required field), this error is not supposed to be "kept secret" IMHO.

  2. +++ b/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
    @@ -91,6 +100,49 @@ public function form(array $form, FormStateInterface $form_state) {
    +        // changing this URL to something else. For example if a user is entering
    

    More than 80 chars

eiriksm’s picture

Cool. Also found another line with more than 80 chars.

Let's see if the strange failures on the testbot will change now.

Status: Needs review » Needs work

The last submitted patch, 65: language_prefix_for-2462279-65.patch, failed testing.

eiriksm’s picture

Status: Needs work » Needs review

Hm, these pass for me locally. Trying once more...

Status: Needs review » Needs work

The last submitted patch, 65: language_prefix_for-2462279-65.patch, failed testing.

The last submitted patch, 65: language_prefix_for-2462279-65.patch, failed testing.

The last submitted patch, 65: language_prefix_for-2462279-65.patch, failed testing.

eiriksm’s picture

Thanks to pwolanin I was able to reproduce the test failures (finally). For reference, they were related to running Drupal in a subdirectory (which in hindsight, as alway, seems so obvious :p).

Also fixed another theoretical issue pointed out while at it.

eiriksm’s picture

Status: Needs work » Needs review

Forgot status change

Status: Needs review » Needs work

The last submitted patch, 72: language_prefix_for-2462279-72.patch, failed testing.

pwolanin’s picture

I think global $base_path is probably deprecated and let's just check for <front> and replace it instead of using str_replace()

The last submitted patch, 72: language_prefix_for-2462279-72.patch, failed testing.

eiriksm’s picture

Thanks, and yes, that makes much more sense.
Although now that i figured out the test failures a couple of other interesting cases has come up. I'll try to get another patch in today.

eiriksm’s picture

Hm, I am having a problem with the failing tests now. Not sure what the best solution is.

So, let's consider the following valid use-cases, which we do not want to break with this patch:
- A user should be able to add a link to /node/1
- A user should be able to add a link to /node-alias-for-node-1 (an alias for the above, if it was not clear:))
- A user should be able to add a link to /node/1#fragment-1
- A user should be able to add a link to /node-alias-for-node-1#fragment-1

Now, if the user enters /node/1#fragment-1 the current code first sees that this (when constructed to a URL object and output as a string) is not the same as /node-alias-for-node-1#fragment-1. Which makes sense. Then we try to compare it to an unaliased version of /node/1#fragment-1, which does not resolve to a path. So I tried to create 2 Url objects, but printing them to a string will always use the alias version. So that was a problem.

This problem also manifests itself on query params. Or a combination of both.

So here is a bit more crude patch that uses strpos. Is that the way to go? Like a less strict checking of a valid URL, i would say :)

eiriksm’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 78: language_prefix_for-2462279-78.patch, failed testing.

eiriksm’s picture

StatusFileSize
new4.94 KB

Hah, only failing my own test now. Better, I guess :p

This should do the trick, I hope.

eiriksm’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 81: language_prefix_for-2462279-81.patch, failed testing.

The last submitted patch, 78: language_prefix_for-2462279-78.patch, failed testing.

The last submitted patch, 81: language_prefix_for-2462279-81.patch, failed testing.

pwolanin’s picture

It seems like there will be a lot of possible failures if there is a base path? I'm surprised the rest aren't failing.

eiriksm’s picture

Status: Needs work » Needs review
StatusFileSize
new5.32 KB
new2.28 KB

Not saying this makes the patch prettier, but at least now the failing tests are not failing.

Also, had to go back to strpos and str_replace, because there is a test case that tries to save <front>?arg1=value#fragment as URL.

Edit: Just changed it so the front part was not being stripped as a html tag

gábor hojtsy’s picture

Status: Needs review » Needs work

Reviewed:

  1. +++ b/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
    @@ -28,6 +29,13 @@ class MenuLinkContentForm extends ContentEntityForm {
    +   * The language manager.
    +   *
    +   * @var \Drupal\Core\Language\LanguageManagerInterface
    +   */
    +  protected $language_manager;
    +
    +  /**
        * The parent form selector service.
        *
        * @var \Drupal\Core\Menu\MenuParentFormSelectorInterface
    @@ -57,6 +65,7 @@ public function __construct(EntityManagerInterface $entity_manager, MenuParentFo
    
    @@ -57,6 +65,7 @@ public function __construct(EntityManagerInterface $entity_manager, MenuParentFo
         parent::__construct($entity_manager, $language_manager);
         $this->menuParentSelector = $menu_parent_selector;
         $this->pathValidator = $path_validator;
    +    $this->language_manager = $language_manager;
       }
    

    So these looked strange to me, because we don't change the constructor, but we save the language manager. So we already got it but did not save it. We did invoke the parent constructor with it. However, looking at ContentEntityForm its constructor does not actually take a language manager. So we should not pass it to the parent. Otherwise it looks odd, because one would assume the parent takes care of keeping it in a property, but we are adding it on here.

  2. +++ b/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
    @@ -91,6 +100,58 @@ public function form(array $form, FormStateInterface $form_state) {
    +      $link_url = Url::fromUri($link_uri, [
    +        'language' => $language,
    +      ]);
    ...
    +        $user_url = $this->pathValidator
    +          ->getUrlIfValid($user_link);
    

    These should go on the same line IMHO, they fit. Easier to review.

eiriksm’s picture

Status: Needs work » Needs review
StatusFileSize
new5.56 KB
new1.42 KB

Good points. I guess the least disruptive for this patch is to just change the parent constructor invocation, so I did that. Not sure if the language manager should have been in the parent?

Fixed both 1 and 2, anyway.

Sprinting on a plane, yay! Hope the internet stays up while posting this...

gábor hojtsy’s picture

Status: Needs review » Reviewed & tested by the community

Looks good to me now, thanks for addressing my complaints and working with @pwolanin :)

catch’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
    @@ -91,6 +100,57 @@ public function form(array $form, FormStateInterface $form_state) {
    +    // marked as having an error (for example an invalid link).
    +    if (!$form_state->getError($form['link']['widget'][0]['uri'])) {
    +      $link_uri = $form_state->getValue('link')[0]['uri'];
    +      $language = $this->language_manager->getLanguage($form_state->getValue('langcode')[0]['value']);
    +      $link_url = Url::fromUri($link_uri, [
    +        'language' => $language,
    +      ]);
    

    Do we not have a way to find these without hardcoding the array structure? Also is this really specific to menu links?

  2. +++ b/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
    @@ -91,6 +100,57 @@ public function form(array $form, FormStateInterface $form_state) {
    +        // Find the user input and validate that no URL negotiator will end up
    +        // changing this URL to something else. For example if a user is
    +        // entering a URL with a language prefix.
    

    Why throw an error and not try to resolve it as an inbound path and use that? Or at least show that as a suggestion?

  3. +++ b/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
    @@ -91,6 +100,57 @@ public function form(array $form, FormStateInterface $form_state) {
    +          // Compare the generated link from what Drupal thinks this is. We want
    

    Compare ... with what

  4. +++ b/core/modules/menu_link_content/src/Tests/MenuLinkContentTranslationUITest.php
    @@ -100,6 +100,22 @@ function testTranslationLinkTheme() {
    +      // Link to the italian front page. Should not be allowed.
    +      'link[0][uri]' => '/it/admin/structure/menu',
    +    );
    

    Comment says Italian front page, code is menu admin.

eiriksm’s picture

Status: Needs work » Needs review
StatusFileSize
new5.73 KB
new2.52 KB

1. I must say, I don't know if we have any better way. I tried to research other places in core that might include that, but could not find any. At least not that specific. So this patch does not address your concern here, but if anyone knows a better way, I agree that sounds like something we should do.
2. Well, we show an error because we do not want to just blindly _save_ the resolved path. Because then the user might get confused as to what the result is, compared to what they wanted (this was the initial report also). But I agree we could do better in the error message. Changed it to display the resolved path as a suggestion.
3 and 4: Thanks! Fixed.

gábor hojtsy’s picture

Status: Needs review » Needs work

Re @catch we could make the validation a form element specific validator in which case we don't need to dig into the array structure. We may still keep it encapsulated in the class in that case. @eiriksm wanna give that a try?

+++ b/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
@@ -91,6 +100,59 @@ public function form(array $form, FormStateInterface $form_state) {
+            $form_state->setErrorByName('link', $this->t('The link you entered is not valid. One reason can be that you entered a link with a language prefix. Did you for example mean to link to @url?', [

+++ b/core/modules/menu_link_content/src/Tests/MenuLinkContentTranslationUITest.php
@@ -100,6 +100,22 @@ function testTranslationLinkTheme() {
+    $this->assertRaw(t('The link you entered is not valid'));

The t() of the shorter string may very well not end up being the first part of the t() of the longer string. We should IMHO assert for the full t()-ed string. If you don't want to do that for some reason, we should not t() this short string.

eiriksm’s picture

Status: Needs work » Needs review
StatusFileSize
new5 KB
new8.15 KB

Here is a patch that adds the validation to the link widget.

The t() of the shorter string may very well not end up being the first part of the t() of the longer string. We should IMHO assert for the full t()-ed string. If you don't want to do that for some reason, we should not t() this short string.

Also fixed that :)

Status: Needs review » Needs work

The last submitted patch, 94: language_prefix_for-2462279-94.patch, failed testing.

The last submitted patch, 94: language_prefix_for-2462279-94.patch, failed testing.

eiriksm’s picture

Status: Needs work » Needs review
StatusFileSize
new7.52 KB
new5.16 KB

OK, this fixes the tests.

Now, looking at the patch, you might wonder why I have removed something from a completely different test. And that is because I see no reason why we should allow people to add links like "//alias-path" and save that for them. Because that will resolve to an internal path. If anything, personally, i would expect i could link to //google.com and then get the protocol-agnostic href (so that it would be https if the site was on https).

Should I open a different issue for that?

The last submitted patch, 57: language_prefix_for-2462279-57.patch, failed testing.

gábor hojtsy’s picture

@eiriksm: yes that looks like a different issue to me. Let's not change unrelated things, this one is complicated enough in itself :)

gábor hojtsy’s picture

Status: Needs review » Needs work
mac_weber’s picture

Status: Needs work » Needs review
StatusFileSize
new7.54 KB
+++ b/core/modules/shortcut/src/Tests/ShortcutLinksTest.php
@@ -54,7 +54,6 @@ public function testShortcutLinkAdd() {
-      '/' . $path['alias'],

Reverted this line.

Status: Needs review » Needs work

The last submitted patch, 101: language_prefix_for-2462279-101.patch, failed testing.

eiriksm’s picture

Guess that proves that we are blocked on fixing that test.

Created another issue for that over here: #2636424: Avoid testing for relative link starting with // in shortcut module. One line review anyone? :)

eiriksm’s picture

Status: Needs work » Needs review

OK, so #2636424: Avoid testing for relative link starting with // in shortcut module is fixed. Back to needs review? Tests should pass I guess?

gábor hojtsy’s picture

Status: Needs review » Reviewed & tested by the community

Given #2636424: Avoid testing for relative link starting with // in shortcut module landing and the prior concerns addressed since the last RTBC, looks good again :)

alexpott’s picture

Status: Reviewed & tested by the community » Needs work

What about an upgrade path - if you have existing links now they can not be saved once the patch is applied? Also don't we need some help text in the widget description to tell users before they press submit?

eiriksm’s picture

Hm. I was thinking about this. It is kind of hard to provide an upgrade path I think, given we don't really know the intention of the person who saved the particular menu item.

These are the scenarios:

- A person tries to save for example a link to the english frontpage.
- The person sees that this is saved, and goes on with their life not knowing it links to the default front page.
- An upgrade path comes and fixes a bug they did not know they had.
- Everyone is happy.

Now, this first scenario is not so likely, I would say :) More likely is it that this person would see the bug, maybe utter a swear word or two, save an absolute link instead, and go on with their life.

Now, here is another scenario:

- A person unintentially enters the english frontpage when he means to enter the default front page.
- The person tests this link, thinking all things went as planned.
- An upgrade path comes and changes the link they were so pleased after creating.
- Not happy :(

Maybe this is not so likely either. But either way, this is the exact opposite of scenario 1. And we would have no way of knowing.

Now given this, I would say the upgrade path would just have to warn users that they *might* want to check the menu item so and so. Because the fact is, if they now try to save this item (for example change the text), that has happily worked for months, it will not get saved. So best to warn them, I guess.

Now, these are just my thoughts. Very open to suggestions. And happy to provide the upgrade path or upgrade warning, whatever we decide :)

Thoughts?

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

fran seva’s picture

Assigned: Unassigned » fran seva

Working on it on NewOrleans2016 sprint.

fran seva’s picture

Assigned: fran seva » Unassigned
eiriksm’s picture

Status: Needs work » Needs review
Issue tags: +DevDaysMilan
StatusFileSize
new13.08 KB
new13.2 KB

OK, here is a first take at adding the following:

- An upgrade path that warns users having saved invalid links.
- A description on the link widget about what kind of problem could arise.

Comments are very welcome. And let's see if it does not break anything at least :)

Status: Needs review » Needs work

The last submitted patch, 111: language_prefix_for-2462279-111.patch, failed testing.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

jofitz’s picture

Assigned: Unassigned » jofitz
Issue tags: +Needs reroll
jofitz’s picture

Assigned: jofitz » Unassigned
Status: Needs work » Needs review
Issue tags: -Needs reroll
StatusFileSize
new12.58 KB

Re-rolled.

Status: Needs review » Needs work

The last submitted patch, 116: 2462279-116.patch, failed testing.

jofitz’s picture

Status: Needs work » Needs review
StatusFileSize
new12.62 KB

Patch no longer applies. Re-rolled.

Status: Needs review » Needs work

The last submitted patch, 118: 2462279-118.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

qzmenko’s picture

.

qzmenko’s picture

StatusFileSize
new45.65 KB

Patch from #118 works not correct for me

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

james.williams’s picture

Version: 8.6.x-dev » 8.7.x-dev
StatusFileSize
new12.42 KB

Here's a re-rolled version, now for 8.7.x. It's true that it still appears to need some work generally though.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.9 was released on November 6 and is the final full bugfix release for the Drupal 8.7.x series. Drupal 8.7.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.8.0 on December 4, 2019. (Drupal 8.8.0-beta1 is available for testing.)

Bug reports should be targeted against the 8.8.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.9.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.

james.williams’s picture

StatusFileSize
new12.61 KB

Time for another re-roll.

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

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should 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.

lawxen’s picture

Version: 8.9.x-dev » 9.2.x-dev
ravi.shankar’s picture

StatusFileSize
new12.67 KB
new10.35 KB

Added patch for Drupal 9.2.x

ravi.shankar’s picture

StatusFileSize
new12.67 KB
new3.05 KB

Fixed some CS issues.

ravi.shankar’s picture

StatusFileSize
new12.7 KB
new3.05 KB

Please ignore patch #131.
Fixed some CS issues.

ravi.shankar’s picture

StatusFileSize
new12.73 KB
new1.32 KB

Trying to fix failed tests.

nikitagupta’s picture

Status: Needs work » Needs review
StatusFileSize
new12.77 KB
new3.24 KB

fixed the test cases.

Status: Needs review » Needs work

The last submitted patch, 134: 2462279-134.patch, failed testing. View results

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

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

james.williams’s picture

Status: Needs work » Needs review
StatusFileSize
new12.74 KB

Needed a re-roll. I imagine the tests will still fail in the same way, but at least this one will apply again.

Status: Needs review » Needs work

The last submitted patch, 137: menu_link_path_lang_prefix-2462279-137.patch, failed testing. View results

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

lendude’s picture

Issue tags: +Bug Smash Initiative

This came up as a bug smash daily triage target.

Some observations:

  • The validation is now happening at the form level, there are other ways to create menu links (custom code, migrations) but we are not protecting against that
  • The upgrade path is only fixing the menu items at the time of running the update, nothing prevents adding more invalid links. Wouldn't an entity level constraint be better at protecting us here?
  • None of this needs to happen is there are not multiple langagues, but I don't see anything checking for that right?
  • The test coverage seems very slim for the proposed changes.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

james.williams’s picture

StatusFileSize
new12.81 KB

Just re-rolling to apply to 9.5.x for now. But yes, @Lendude, those look like pretty valid points to address. Thanks for taking the time to review!

steven jones’s picture

StatusFileSize
new12.81 KB

Here's a trivially re-rolled patch for 10.2.x

james.williams’s picture

StatusFileSize
new12.81 KB

Re-rolled for 10.3. (I don't expect this to get into core any more; it's just most useful for sites needing to upgrade that have been using the previous patches here.)

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.