Postponed on #2407505: [meta] Finalize the menu links (and other user-entered paths) system
Problem/Motivation
Despite issue 1879120, link URL entry in the WYSIWYG is inconsistent with link URL entry in the link field. To link to a specific node: in the WYSIWYG, you have to write "/node/#", in the Link URL field, you would write "node/#".
This is a user experience issue.
Proposed resolution
Make link URL entry consistent between how URLs are added in the WYSIWYG, and how link URLs are added in the link field.
EditorLinkDialog should transform Drupal paths, like the Link field does (LinkWidget::massageFormValues
| Task | Novice task? | Contributor instructions | Complete? |
|---|
(); ). EditorLinkDialog should not do this by pre-prending a slash because this would not work for sites installed in a subdirectory.
Remaining tasks
1. Complete patch - was blocked by #2347469 ?
2. Continue discussion of how it should work (output should be root relative path, drupal would store whatever user entered)
3. Add tests
User interface changes
In LinkDialog a user would enter "node/#" like they would do in a link field.
Original report by Charles Belov
Postponed on #2407505: [meta] Finalize the menu links (and other user-entered paths) system
Despite issue 1879120, link URL entry in the WYSIWYG is not consistent with link URL entry in the link field.
Steps:
1. Create a new Drupal 8.0-alpha12 website at simplytest.me
2. Enable the Link module
3. Add a link field to the Basic Page content type
4. Add content > Basic page > title "test page 1" (without the quotes) > Save and publish
5. Add content > Basic page
6. Title "test page 2"
7. Body - type "test page 1"
8. Select text "test page 1"
9. Click link icon
10. In the URL field, type "node/1"
11. Click OK
12. In the Basic Page Link URL field, type "node/1"
13. In the Basic Page Link Link text field, type "test page 1"
14. Click Save and publish
15. Click "test page 1" link corresponding to link field
Actual and expected results: Goes to [protocol]://[domain]/node/1 and displays test page 1
16. Click Back button
17. Click "test page 1" link corresponding to link in Body text
Actual result: Goes to [protocol]://[domain]/node/node/1 and displays not found error
Expected result: Goes to [protocol]://[domain]/node/1 and displays test page 1
| Comment | File | Size | Author |
|---|---|---|---|
| #31 | drupal_core-validate_urls-2292159-31.patch | 3.25 KB | emclaughlin |
| #16 | Drupal_core-validate_urls-2292159-16.patch | 2.7 KB | emclaughlin |
Comments
Comment #1
charles belovAdded Usability tag
Comment #2
wim leersComment #3
wim leersComment #4
tomogden commentedEven stranger, when I hover over the saved link in the body field while in Edit, the URL it sees is [protocol]://[domain]/node/2/node/1 . Does that offer any clues?
Comment #5
tomogden commentedSo the rendered link in the body is being written without the leading slash,
node/1which the browser interprets as relative to the current page url.By contrast the link field is adding a leading slash,
/node/1which the browser sees as relative to the domain's root.This is the way Drupal has worked for years, so I am wont to say "It works as designed." But then you have to admit that there is a blatant inconsistency with the way the link field works.
So now that we know what the difference is, what is the prevailing community guideline or opinion on what behavior we should preserve and which to deprecate?
Comment #6
charles belovThe biggest argument in making the two link methods consistent is that the inconsistency is a barrier to newcomers and a training issue.
Comment #7
cs_shadow commentedIssue exists in the latest dev release also.
Comment #8
cs_shadow commentedAdding a slash at the beginning of
node/1will not work if your site is located in a subdirectory like: example.com/drupal. In that case the field the URL field adds the subdirectory path also which is not done in case of CKEditor.Changing the component to CKEditor since the anchor element is created by CKEditor plugin and not by EditorLinkDialog (correct me if I'm wrong). Relevant code is in
core/modules/ckeditor/js/plugins/drupallink/plugin.js:Comment #9
wim leersEverything in the issue summary can be summarized as follows:
It's not CKEditor's responsibility to transform these, so this definitely belongs in the editor.module component.
Comment #10
wim leersSee
LinkWidget::massageFormValues(); that's how the Link field does it, and how we should do it.Comment #11
wim leersOh, and as cs_shadow says in #8: merely prepending a slash will not work when Drupal is installed in a subdirectory.
Comment #12
emclaughlin commentedI tried to take a look at this but I'm having a problem as I'm not quite sure where to put any of the code. There's
LinkWidget::massageFormValues(), yes, butLinkWidgetand the other classes with this function extendWidgetBasewhereasEditorLinkDialogextendsFormBase. Would the validation and the transformation both occur in avalidateFormfunction? That feels weird to me, to be editing a value while in a validate function, but I may be wrong about that.Any insights, anyone?
Comment #13
wim leers#12: Thanks for jumping in and giving it a shot! :)
I can understand your confusion.
LinkWidgetis a Field API widget. Therefore it is more abstract, and it indeed doesn't translate 1:1. A Field API widget is sufficiently abstract so that Drupal can automatically build forms that include one or more link widgets.Compare that to
EditorLinkDialog, which indeed just extendsFormBase, which indicates what it is: it is nothing more than a simple form.So rather than being an abstract building block of a form (
LinkWidget), we're dealing with a simple form (EditorLinkDialog).Basically,
LinkWidget::massageFormValues()is more complex than what we need: it needs to handle multiple values (because a link field may be configured to accept multiple values, hence multiple URLs), but we only need to handle *one*!So you can take the key parts of the logic:
and indeed put it in a new
EditorLinkDialog::validateForm()method. I agree that it can feel bizarre to be altering/"cleaning up"/"massaging" a form value during the "validate" phase, but since Drupal's Form API doesn't have a separate "massage" stage, that's how it's done!So: you were very close, your hunches were right, your sense of code smell was right, but… it's in fact how it needs to be done in today's Drupal — hopefully we can remove that code smell in the future — perhaps with your help! :)
Comment #14
emclaughlin commentedThanks @wim-leers!
I think I managed to take the key parts of the logic and apply them to
validateForm(). I've attached a patch for review.Comment #16
emclaughlin commentedOkay, let's try this again with a patch that isn't corrupted.
Comment #17
wim leersWonderful, this is looking great already! :)
There's one problem though: if I enter
node/1, that gets converted correctly to/node/1, but if I then try to edit that link again, save immediately without changing it (so saving/node/1instead ofnode/1), it tells me it's invalid. We'll need to fix that. I think the easiest solution would be to tellUrl::createFromPath()to generate an absolute URL (http://example.com/node/1)for us instead of arelativeone (/node/1). You can do that by calling->setAbsolute(TRUE).Code review:
Setting the target attribute is not necessary here, because
Urlwill ignore it. After all the<a target="something">attribute is something that lives at the link level, not theURLlevel.First: we don't want to expose the exception's error message to the end user — this is information for developers, not for end users. It would only be very confusing. :)
I think think the message could just say something like in all cases, without any further information.
Second: there's a lot of repetition here! But we cannot combine the catching of all these exceptions in a single "catch" block. However… because Drupal 8 requires a fairly recent version of PHP, we can just create an anonymous function inside of this function to always do the same thing.
So at the top of
validateForm()you could do something like:And then call
$show_error($e)in all three "catch" blocks!Comment #18
emclaughlin commentedUsing
->setAbsolute(TRUE)creates a problem, though, in cases where you don't want WYSIWYG links to be absolute URLs. Say you were having a user create content on a dev site and then merging the database up to the prod site pre-launch. The links would all still be pointing to the dev site, right?Comment #19
charles belovSame thing goes for if the staff user logged in using a www-free address and other staff users log in with the www version.
Same thing goes for http (anonymous) vs. https (staff). That said, we do expect to migrate to https for all because of Google's announced plan to rank https sites higher.
But it doesn't belong in the in-page links.
Comment #20
wim leersThis is true, but:
/mysubdirectory/node/1instead of/node/1.data-attributes that a filter transforms to the correct links for the current site.This issue is the first step: make
EditorLinkDialoggenerate URLs at all from "Drupal paths" (likenode/1anduser/5). The next step would be to create links in such a way that they're automatically adapted to the current site.That being said, I'm fine with root-relative URLs, but then you will have to adjust the validation :) That's why I suggested absolute URLs as the solution: it's the easiest solution.
As you can tell, there is no solution that is both simple and solves everything!
Comment #21
charles belov@Wim Leers #17
Actually, the field should always display
node/1to the user in the WYSIWYG dialog to be consistent with a link (module) URL field. If Drupal wants to store it internally as/node/1, that's fine, but the complexity needs to be hidden from the user.The whole idea of this issue was to make data entry and presentation consistent for the user between the link module URL field an the WYSIWYG URL field.
The last thing we want is having the user see the WYSIWYG URL field "corrected" to
/node/1, thinking they also need to do that in a link URL field, and getting the wrong results.We often have both WYSIWYG and link URLs in the same content type, so it's important that they be presented consistently to the user.
Comment #22
wim leersGood points regarding usability in #21. But that does mean this patch becomes more complex, because it will be necessary to store the path corresponding on the
<a>element then, e.g.:<a data-drupal-internal-link="node/1">.Are you still up for that, emclaughlin? :)
Comment #23
emclaughlin commentedWouldn't the link needing to be whatever the user put in in the WYSIWYG and needing to be an absolute path when outputted suggest that the validation function should be used purely to validate the link, not change it, and then the link be updated to the absolute URL of the site it's on during rendering?
Comment #24
wim leers#23: that's absolutely correct!
Comment #25
charles belovFor clarification, does #23 mean that internal code
<a data-drupal-internal-link="node/1">would be output for site http://www.example.com as<a href="http://www.example.com/node/1"?Comment #26
wim leersIt could either be absolute (as in your example) or root-relative (
/node/1). It could be configurable on the filter.Comment #27
charles belovI'd prefer
/node/1on output, but that's a community decision. As long as it'snode/1in the dialog box, either way on output is fine with me.Comment #28
bneil commentedIn testing #16, the patch applied, with the following message: "Hunk #2 succeeded at 85 (offset 1 line)."
I tried testing the current functionality of patch #16 before working on addressing some of the points in #17-27, but I am running into an ajax error and the dialog will not close or present the error messages:
I'd be interested in working through addressing the points in the above comments if someone would be willing to help me figure out the dialog issues.
Tested with Drupal 8.0.x with the Standard profile.
Comment #29
emclaughlin commentedSo, I'm having some trouble figuring out where the links are rendered so that I can make sure that links are being rendered as /node/1 and not node/1. Does anyone have any guidance on this?
Comment #30
emclaughlin commented@bneil The patch no longer applies correctly because code has been changed since it was created. I've got an updated patch for validation but was waiting to attach it to the post until I could figure out the rendering issue.
Comment #31
emclaughlin commentedOkay, nope, new question, figured out the last one by rereading the thread and realizing I was doing everything wrong.
I've attached the patch I've created so far here. The problem is, when you input an invalid URL, and then input a valid one, the form redirects off to /editor/dialog/link/basic_html. I'm not sure how to track down where/why this is happening. Can anyone provide any guidance?
Comment #32
bneil commented@mclaughlin -
I believe the issue you're running into is #2347469: Rendering forms in AjaxResponses does not attach assets automatically. Note the "referenced by" issues all relating to that problem.
Comment #33
wim leersActually, the lack of clarity of how this should work points to a wider problem. And that wider problem is captured in #2407505: [meta] Finalize the menu links (and other user-entered paths) system, which is going to be the focus of the D8 criticals in New Jersey next week. Hopefully we'll come out of that week with all of those things solved, which should make this issue significantly simpler!
Comment #34
wim leersComment #35
yesct commentedComment #36
wim leersComment #37
xjmI think this should be unpostponed now? The majority of the critical work for #2407505: [meta] Finalize the menu links (and other user-entered paths) system is done. If there's a specific issue that's still blocking this, let's postpone it on that rather than the meta. Thanks!
Comment #38
disasm commentedThis could use an issue summary update with clear next tasks to be done in the issue. Also need tests written for this.
Comment #39
anonSo there is no need for me to port the Linkit module to D8 now?
Comment #40
adriancotter commentedComment #41
emclaughlin commentedComment #42
charles belovHas it been decided that Drupal will be consistent as to whether a user will enter /node/1 or node/1 throughout the Drupal UI? I don't care what it is internally, but my whole point in filing #2292159: EditorLinkDialog should validate URLs, and autocomplete like the Link widget was that it's confusing to enter /node/1 in one place in the Drupal UI and node/1 in another place in the Drupal UI. Now I read an article (linked to from the latest Drupal Association newsletter) that says Drupal 8 is inconsistent on leading slashes.
I looked at the meta issue #2407505: [meta] Finalize the menu links (and other user-entered paths) system and the linked-to meeting notes appear to be at a more technical level than my Drupal experience level.
Is there a plain-english meta issue that says whether the goal is to resolve to leading slashes, resolve to no leading slashes, or not to resolve these inconsistencies? If there is some other meta, please relate this issue to it.
What is the impact of the closing of #2407505: [meta] Finalize the menu links (and other user-entered paths) system on #2292159: EditorLinkDialog should validate URLs, and autocomplete like the Link widget?
Comment #43
sidharthapIt seems Novice tag was added 2 years back. As per #42 it required more discussion on it. It is no longer to be a Novice issue. I am removing the Novice tag.
Comment #44
wim leersA similar feature request was posted at #2689661: Add CKEditor Link to core.
Comment #45
Benia commentedIndeed I suggested it for several reasons but the most important I think, is when a domain and or a TLD is changed, and there is no suitable page2page redirecting mechanism. Moreover, when someone moves from Drupal 7 to Drupal 8 and needs to work with another similar module and copy all links manually in each page (No one can promise, I guess, that a similar situation won't happen in Drupal 9).
From an SEO viewpoint, Non-boilerplate internal-links ("content region" internal-links in Drupalese), are important and can give better information-navigation and lower a chance for what can be defined as "fast remission", that is, when the client goes back to fast to Google and checking other results.
Making this tool most accessible and intuitive in Drupal could benefit all users in these contexts.
Comment #56
larowlanThis sounds like adding link-it to core, which feels like a feature request rather than a bug
Comment #60
wim leersObsolete since #3306584: [11.x] Remove EditorLinkDialog, EditorImageDialog and EditorMediaDialog in Drupal 11. Spiritual successor: #3317769: Add support for linking to entities in CKEditor 5.