Problem/Motivation
LinkWidget does some path massaging which is broken -- try to install Drupal in a subdirectory and add an entity with a linkitem pointing to some path. Upon edit the path will contain the subdir, that's broken. The path massaging is necessary because Drupal 8 does not store user entered paths but rather denormalizes them to routes immediately. This presumes a path will always resolve to the same route. This is not so:
- Create an alias say
legal_noticepointing tonode/1. - Store a link pointing to
legal_notice. - Delete
legal_noticeand addlegal_noticepointing tonode/2(we have a new legal notice).- Note: Cache may need to be refreshed when manually testing with menu links and url aliases.
- Your link still points to
node/1
You can say the above was a problem in D7. But:
- Link to
somepage. It is implemented as view idfoowith displaybar. - Delete the display
bar - Add a new display (or a new view) with a page display to
somepage - Your link is broken.
There is also another issue related to that:
- Create node/1 with alias: this-is-my-node
- Enable link
- Add link field to basic_page
- Create node/2 with link to this-is-my-node
- Now change alias of node/1 to this-was-my-node
- Create node/3 with alias: this-is-my-node
- Result: On view of node/3 you see /this-was-my-node
- Result: On edit of node/3 you see /this-is-my-node in the link field
- Expected result, the data is synchronized
Proposed resolution
New solution
@effulgentsia, @chx, @dawehner came up with the following critical point:
There are usecase that people want to link to the path and there are many usecases where people want to link on the object(entity) for example.
Given that we have to ask the user somehow which of the two ways is prefered.
Before doing that we need though a couple of subissues in first (not completed yet):
Old solution
- Add a custom route much like
<front>where the path is<user_entered_path>/{path} - When saving a link item , put the user entered path into the
{path}parameter. - When generating the link, use a (stupid simple) outbound processor to generate the link pointing to the path.
- When access checking, simply (as simply as the wonderful Drupal 8 router API allows such a thing) access check the path as if it were an incoming path.
- Now that we have a proper access check we can do proper edit access as well: while we do not want to allow 403 paths to be edited, 404 should be editable so that typos can be fixed.
.
Remaining tasks
Do a followup about access checking in LinkFormatter.
Resolve the two outstanding issues to use LinkItem since after this issue LinkItem is no longer broken.
User interface changes
API changes
| Comment | File | Size | Author |
|---|---|---|---|
| #45 | interdiff.txt | 2.13 KB | chx |
| #45 | 2346189_45.patch | 16.73 KB | chx |
| #27 | 2346189_27_test_only.patch | 4.04 KB | chx |
| #1 | 2346189_1.patch | 1.77 KB | chx |
Comments
Comment #1
chx commentedDemo test patch attached. It has two fails.
Comment #2
tim.plunkettComment #3
chx commentedComment #4
chx commentedComment #5
chx commentedComment #7
chx commentedComment #8
tim.plunkettPlease explain how this is critical and fits into https://www.drupal.org/node/45111
Comment #9
chx commentedI will demote if the decision is to keep the router system. Right now the picture is this:
#2327277: [Meta] Page rendering meta discussion
#2339219: [meta] Finalize URL generation API (naming, docs, deprecation)
#2344645: The order of event subscribers should not be affected by definition order
Comment #10
chx commentedComment #11
chx commentedComment #12
chx commentedComment #13
chx commentedComment #14
chx commentedComment #15
chx commentedComment #16
dawehnerAdressing the views usecase. It was always recommended to use the views built in way to create menu link.
Comment #17
chx commented> It was always recommended to use the views built in way to create menu link.
Aye but also link field item, shortcut and who knows what else?
Comment #18
fabianx commentedI thought long about this, but does that not expose over all that our 'alias' system is totally broken?
In Drupal 7 at least I very good remember that message from the menu system saying:
- "Your alias was resolved to node/1 and stored that way."
I think the usage of routes everywhere just made that problem more present.
So the real question is:
- When a link is input, why do we need to resolve aliases to routes?
It is perfectly fine to resolve node/1 to node.get, array(':node' => 1) as that represents the same and here if node changes to article, we gain the advantage that the route is still correct.
But if we internally store:
legal_page => node/1 in a table, then that is broken as it needs to be a route now as we don't support _any_ paths anymore. (I do accept that ...)
As soon as we have a mapping:
legal_page => node.get | parameters
there is no more reason to resolve aliases to their source routes, but instead we should resolve them to a special alias route, which can then resolve the real route via the alias table.
store:
alias.get | array(':alias' => 'legal_page')
That way we don't have to change our storage mechanism for links, use routes nicely and we are consistent in the whole system.
Comment #19
yukare commentedJust one thing, when we delete the alias in step 3, it do not exist anymore, and all links/menus/references must be deleted or make some way invalid, or make the links point direct to path(node/1). This is not new and can happen on Drupal 7, so what Drupal 7 does when an alias is deleted ?
Comment #20
fabianx commented@yukare: I could also delete a route by disabling and removing a module, so that is a question for another major or critical issue IMHO.
If you delete an alias in D7, it gets removed, too - unless you use redirect module, e.g.
As there is no solution in D7 => other issue, I think.
As we don't have paths anymore, if the user input an alias, we need to store that alias.
If we wanted to make that clear on the interface, we would provide a select list of routes (alias being one of them) and a way to input parameters.
e.g. node.get, put 1 into Node 1 field - loaded via AJAX, but that is also another issue, if we have routes everywhere we should not expose any paths anymore to input in the first place, On the UX consequences of that others will need to chime in.
But as I said that is a different issue, as long as we automatically resolve routes we should resolve aliases to an alias route - as simple as that.
Comment #21
chx commented> I thought long about this, but does that not expose over all that our 'alias' system is totally broken?
But this bug report is so much more than the aliases. For example #2277753: Shortcuts should handle missing routes elegantly is handled separately pretending it's fixable. You can fix the symptoms but not the root cause: storing routes is denormalization and denormalization tends to get out of sync. We can slice and dice this any way we want, we can try to point to other subsystems, we can try to patch individual subsystems to cover this fact up but the fact will never go away: the whole notion of storing route/parameters is a bad idea. Much like the whole routing system, it buys us nothing and just causes problems.
Comment #22
tim.plunkettComment #23
fabianx commentedThe bug in the issue summary is a critical bug in D8.
This has nothing to do with D9.
@timplunkett:
I know you have an agenda to get routes in and I am totally okay with that, but please don't flag issues if someone points out issues in our current system - as much as you disagree with the way it is presented.
> You can fix the symptoms but not the root cause: storing routes is denormalization and denormalization tends to get out of sync.
And I respectfully disagree:
We have a user input a path:
- If they input node/1 in D7 that is essentially storing a route definition, node/% is registered for node/1, so there is no harm in de-normalizing this to node.canonical.view, node => 1
Yes that is de-normalization, but definitely what the user wanted to use. If we had a UI he would choose a node from the system and we would automatically create a route for them to that.
- If they input 'legacy_page', that is an alias and we need to store it as such. Such alias rules.
I will take a look at the shortcut one, but routes for the first time allow to nicely store aliases within other routes and don't need to do de-normalization as in Drupal 7.
TBH the same strategy works in D7 (in contrib) to finally fix that UX problem that your aliases are de-normalized in the menu input screen.
Just register alias/%, have the user input alias/whatever in the path field, then url_outobund_alter alias/* -> *. Done for D7 as well. Much nicer UX than de-normalization to node/1, etc.
So for this issue my proposal solves it both for D7 and D8, so I think that is great :).
@chx:
If there are other issues with the router let's fix them. The router won't go away, so lets accept that and file criticals - as you did, which is great!
Comment #24
chx commentedComment #25
fabianx commentedNew issue summary looks great to me! Thanks so much, chx!
Comment #26
chx commentedWe could take this further and simplify vastly: if a user enters a path it is stored with a route
system.user_entered.pathand the argument is the path entered.The access checker is simply routing the path and running the relevant access checker, same as with the suggested alias route.
This route is never used for incoming routing.
The end. No need to change anything in the system.
How's that?
Comment #27
chx commentedI implemented #26 and fixed link item to demo. UserEnteredPathAccessCheck is not used at the moment, we need to discuss that the field item formatter doesn't use any access checking... but that's a different issue.
Comment #28
chx commentedComment #29
chx commentedComment #31
mradcliffeI didn't look at the test code, but these lines popped out at me in the patch.
Shouldn't this be
$this->pathValidator()->isValid($path)?Is the bitwise operator here working in all cases?
Comment #32
mradcliffeI did a quick manual test. I had to refresh cache to test #4 in the steps to reproduce in the issue summary (4. Your link still points to node/1).
Comment #33
pwolanin commentedI don't think the path in #27 is the right approach at all.
I think the basic problem as outlined in the summary has been present since Drupal 6.0 - we store the "system path" not the user-entered path.
Here's a path forward I would suggest:
Comment #34
pwolanin commentedCreated issues:
Comment #35
chx commented> In this issue, we should work to make the UI clearer to help the user understand that they are linking to an object, not a path.
That's quite some fail there. The user certainly doesn't link to an object. I want to see a legal notice. Just because this was broken for aliases in D6 doesn't mean it needs to be broken for everything in D8 either. Cos this time it's not just aliases. See the summary for other problems.
> Re-resolving the path every time we render a link is not supportable
And why is that? Already the only way to get D8 to any reasonable speed is to have (quite some) caching, how is this different? Also, currently we do not re-resolve the path at all; if we add access checking to the formatter then we will but right now we don't.
So, onwards.
I fixed another bug and added a test for it: if you are trying to edit an existing but inaccessible internal path only then we should remove the widget. Non-existing paths should be editable. While you can't save a non-existing path due to LinkTypeConstraint it doesn't mean external actions (deleting an alias, uninstalling a module) does not render a link invalid. Then you want to fix your link, presumably. This needed a small change to pathValidator even.
Now
$node->field_link->urlworks because I implementedonChangetoo. AlsosetValue($string)works.Fixed some doxygen too, pathValidator in #31 as well.
Also, I found a bug in WebTestBase:
this passes :) although it's not in the patch but believe me, it does. It doesn't break with
field_link[0][title]. Yay, weirdo bugs.Comment #36
fabianx commented#33 it is okay to denormalize node/1 to a route,, because there are no paths anymore.
But: There are still aliases and inputting an alias needs to be stored as an alias, else you propagate one of the biggest UX WTF all over the system.
Example:
- Legal-advice alias linked from 1000s of pages
- Now alias points to node/1
- Now alias is updated to link to panel/1 (another entity)
Expected behavior:
Links to the alias go to the new route
We need an alias route anyway, because you cannot link to aliases at all from twig right now and Drupal::fromUri('base://legal-advice') feels strange ...
So #27 could be a little too much, but for aliases we need to denorm to alias route and do lookup at run time.
No way around that.
On the other hand we can also just store paths as long as user enters paths and not routes as explained earlier.
But at the minimum we need to solve it for aliases.
( not reviewed new patch, yet)
Comment #37
pwolanin commented@FabianX - I don't understand how this is possible "Legal-advice alias linked from 1000s of pages" - if you put a manually link in the content area it's just HTTP and will be resolved to the new target of the alias. If you linked to it via e.g. a node reference, then linked to the content, not the path, and I don't think you expect node references to be updated if a path alias changes.
Comment #38
chx commentedSo I link to a page, it's implemented by a certain view+dispay, if I change which view+display implements that page, are you certain you want to break that link?
Comment #40
fabianx commented#37 For the 1000s of pages:
So you would expect if you change the alias that your links in the fields link to the old content, while your links in the content areas directly link to the new content.
So now all your content area links are right, while your alias links are wrong.
I personally find that confusing ...
The story is completely different if I select a node from somewhere, then I have selected content, but if I input a path explicitly, would I not expect it to always link to that path?
@Bojhan also said that another use case is that users want to link to content that does not yet exist - in preparation for the content strategy.
Comment #41
fabianx commentedComment #42
fabianx commented@chx: Spoke with catch.
There is a good technical reason we might want to denormalize paths to routes to be able to automatically delete menu and other links once the node is removed. This is a use case we need to support and its difficult to do if we just store the path.
There is also another feature / bug in the issue queue somewhere to extend the LinkField to put in any path, which is basically what we are doing here in a clean way.
I was not able to find that issue, yet.
This - like your approach - will fix the issue to link to not-yet-existent-content.
Speaking with Bojhan revealed that:
He wants to spend more time reviewing that.
Speaking with catch:
- We need a way in core to link to arbitrary paths, too - just the implementation might be different.
- It is way better for the general case to just have a "content browser" to link to content
From all of that:
What we are doing is okay and supported by at least catch, it just might not be the only way to create paths / links in core, but that might just matter with the order of things we get them in.
e.g. if the reference browser needs to first get in that the path widget can be arbitrary by default. Or there are three different types, like:
Vertical Tabs:
- Content | Content Browser or Content link (there aliases are resolved)
- Internal Link | Arbitrary internal link
- External Link | External link url
Thanks for all your work!
Comment #43
chx commentedSo, please tell me cleanly: is this patch going to go forward or should I just delete the link field migration? Because that's the only reason I mess with this. That's the only thing in core right now that actually uses a link field item and as such it's broken. Either is fine but I need a clear answer.
This version adds
<front>support to make the rdf test pass; adds a test for it to the widget. Also, the currently unusedUserEnteredPathAccessChecknow returns the relevant access result object for caching purposes. It is a copy ofAccessAwareRoute::matchwith twocatchfromPathValidator. I can copy-paste like the best of them :)Comment #44
fabianx commentedI think we go forward, but should have that as the new 'Internal Link' plugin and rename the current plugin to 'Content Link' , which then can do de-normalization as a user will expect it then.
The patch looks good to me.
Comment #45
chx commentedThis version makes sure route_parameters is always set.
I also added
$accounttoUserEnteredPathAccessCheckwhich wasn't easy because the handbook page https://www.drupal.org/node/2122195 was broken but I think I fixed this part of it and emailed Wim to clarify / fix the results because the examples still return boolean and that doesn't look right.To clarify, to use this access check all is needed is an
$element[$delta]['#access_callback'] = array('Drupal\Core\Url', 'renderAccess');inLinkFormatter::viewElementsline 171 but that's an entirely different bag of hurt so I am not doing it in this issue. I am runningdrush ev 'var_dump(Drupal::accessManager()->checkNamedRoute("system.user_entered_path", array("path" => "admin/config"), Drupal\user\Entity\User::load(1), TRUE));'to test the access checker and it works.Comment #46
catchReviewing issue from phone but general +1 for #44 to support the non-entity-coupled case for adding links.
Comment #47
chx commentedIf you want to link to an entity use an entity reference...?
Comment #48
catchThat'd make tonnes of sense, temporarily forgot that content menu links could have entity references.
Still leaves the 'just a path' problem but allowing any internal path for that seems OK.
Comment #49
catchOpened #2351379: [meta] Define, then support exact use cases for link generation and storage to flesh out the use cases and API overall.
Comment #50
pwolanin commentedI'm not clear what the patch actually proposes now, or what the problem you are trying to solve with this as relates to link fields. Please update the summary.
Anything like the route "system.user_entered_path" seems like a bad hack at a technical level and not something we should add to core.
I still am not understanding what is critical about this since it seems basically the same as past Drupal versions, where only linking to a valid path was allowed, not to an arbitrary path.
Comment #51
catchDrupal 7, the following path is valid:
/blog
But the route it's associated could be provided by Views, Blog module, Panels etc. The routing system is 1-1 coupled between path and route so referencing the path always points to the correct route.
In Drupal 8, all of those would be different routes altogether. If you store the route, it could change out from underneath the path, despite the path itself still being valid.
i.e. /blog
Can be handled by:
blog.blog
views.view.blog
panels.page.blog
This is an inversion of the problem that routes are supposed to solve. If you want to link to admin/config/devel/devel from PHP and the path moves to admin/config/development/devel with the route staying the same, then in 7.x you'd have to update your code but in 8.x you wouldn't.
However if you want an entry pointing to /blog which is agnostic about which route/module provides the listing at that path (which is exactly a site administration task), then this works fine in 7.x but you'd have to do a complex data migration in 8.x
Also the bug in 8.x is site-builder/user facing, whereas the limitation in 7.x is developer-facing only.
Comment #52
tim.plunkettActually, right now page_manager will re-use the existing route name if you override it. See http://cgit.drupalcode.org/page_manager/tree/src/Routing/PageManagerRout...
Comment #53
chx commentedStill, route parameters change.
Comment #54
dawehnerAdding a kind of related issue, at least it solved quite some usecases for me.
Comment #55
catch@Tim so given the following, excuse the gherkin but it helps here at least for me:
Given Blog module defines blog
When I create new page manager route for /blog
Then the page will be served by the same route (pass)
Then I disable blog module
Then the page will be served by the same route (pass or fail here?)
Given Blog module defines /blog
When I disable blog module
And I create a new page manager page for /blog
Then the page will be served by a different route (seems like definite fail given the code you linked)
Comment #56
pwolanin commented@catch - it seems that you are talking about menu link or link fields here. So "served" isn't really the relevant question?
In terms of menu link content entities, we roughly want to store the user-entered path but re-resolve the route every time we execute plugins discovery?
This is also a good motivation to try to finish implementing the menu link field and NOT use menu link content entities for node links.
Comment #57
catch@pwolanin, no not menu links, I'm talking about which route is responsible for a particular path changing within the lifetime of a site (which affects any stored reference to that path if it gets converted to a specific route, mainly menu links but also shortcuts).
This is the exact opposite problem to the same route changing path during the lifetime of a site.
Using routes everywhere fixes the latter problem, the former problem is what I understand this issue to be about.
Comment #58
pwolanin commented@catch - yes, I understand the issue is that the path -> route + params resolution might change if a new route becomes higher priority for a given path
My suggestion for menu links, at least, is that we could attempt to handle this in the specific plugin class by re-resolving the path to route mapping when the plugins are discovered, which should happen usually at the same time as a menu rebuild, which generally would need to be triggered to add/remove routes?
Comment #59
chx commentedThe menu link subsystem in D6 was designed for (and benchmarked with) hundreds of thousands of links. You want to re-resolve that?? Are you saying the system tries to discover all that????
Comment #60
fabianx commentedYes, from a scalability standpoint and comparing to sites seen in the wild, it will not be possible to de-normalize during menu rebuild.
Also this does still not solve the problem that a user entered path could have two routes with different access checks in which case the only way is to resolve it at runtime anyway:
Module A, route: blog.A path: /blog, language: en, access check: 'per role', role: 'general subscription'
Module B, route: blog.B path: /blog, language: es, access check: 'per role', role: 'spanish subscription'
Module C, route: blog.C path: /blog, language: de, access check: 'per role', role: 'german subscription'
It is not possible to resolve /blog at router re-generation time to one route. Unless you store all the possible routes, in which case you can do the path resolving also at run time in the first place - as you don't gain much anymore by the stored route.
Comment #61
fabianx commentedComment #62
dawehnerEven that doesn't work if you install a module in the meantime providing the same path.
Comment #63
chx commentedWe have a nice green patch. I tried to make the issue summary more clear.
Comment #64
dawehnerUpdated the issue summary about a conv. @effulgentsia, @chx and @dawehner had.
Comment #65
pwolanin commentedI strongly disagree with this approach, but it is a WTF that loading a menu link and saving it could potentially change the route that's stored and hence change the access controls, etc.
Or equally a WTF if your module supplies e.g. a route with the node/{node} pattern, that won't be respected by existing links.
I think we need to set up a meeting to talk through the use cases and approaches before we can move forward.
Comment #66
webchickI really would love to avoid doing this. This is merely pushing our inability to make a decision here to not even just site builder level (which is bad enough) but even potentially content author level as well. There is no reason that either of those audiences should know/care how Drupal's path resolution system works, or even that there is such a system.
We could gain a lot of simplicity if we built into core the following assumptions (overridable from contrib):
- If you fill in a link (link field, menu link, etc.) from a UI, you want that to go to a path. If you rename a path, you're going to break your links. That's how other authoring tools (e.g. Dreamweaver) work, and that's how links in general work on the internet at large: rename/remove a page? broken link. (Bonus points if we can somehow notify people on node save or whatever that they're about to break N links if they do this.)
- If you want a link that follows along the object/entity regardless of what it's titled or what its path is, use an entity reference field. There, the user expectation is clear that that's exactly what they want.
Regular link fields that automagically discover that something they're linking to is a node and then become in effect an entity reference field behid the scenes sounds both a) confusing and b) like a nice to have, to me. (Though I do realize from testing that this is how Drupal 7 works.)
Comment #67
mpdonadioBut this isn't how Drupal 7 works currently with the out of the box menu system (and not using pathauto, globalredirect, pathalogic, or another of the other modules that make aliases easier to deal with).
If I create a node, create an alias for it, and add the node to a menu from node/add, then the internal path (node/nid) gets stored in menu system. I can change the path alias all I want, and the menu will always generate the aliased path.
If you create a menu entry from the menu itself, you can enter the alias, but the internal path gets saved and you get a nifty message:
The only real way to generate a broken link when you rename something is if you manually author an a element in a textarea, and use the alias instead of the internal path.
And as a side note, your recollection of DreamWeaver is incorrect. If you rename a file through the Files window, it will rewrite all HTML references to that file. I can't recall if you delete a file through the Files window whether it warns you about creating orphans (though there is a orphaned file report available).
Comment #68
webchickYes, I realize that Drupal 7 had this magic. It is indeed nifty. But if retaining this magic means pushing a nonsensical option in the face of every person who adds a link, I'd rather the nifty magic be available from contrib, TBH. I'm also fine with us deciding that links always follow objects around. But not deciding, and instead forcing the user (who is way less equipped with background knowledge) to choose is very sub-optimal. :\
Also, fair point about renaming things inside of Dreamweaver itself. I was more referring to editing things from outside but that comparison doesn't totally hold up here.
Comment #69
chx commentedWhat about external links pointing to Drupal pages? Those will be path links. Obviously, you do not want to break external links so perhaps when you remove an alias you will add it to another object or employ redirect or something. For this reason alone, I would say that links following objects make no sense especially as entityreferences can be formatted as links (perhaps the "Display the label of referenced entities" + "link to" checkbox should be a separate formatter called "Link to the referenced entities" to make this more evident).
Note that D7 didn't have entityreference in core. Also the audience was somewhat different and we more expected people to put up with Drupal...
Comment #70
dawehnerJust explorer some other systems how they deal with menus.
Explored two systems for now how they deal with the problem.
Wordpress
:
For something like a site tree you can create pages.
On creating pages you define where they appear.
You have a listing of all pages, witch shows the hierarchy
Typo3 Neo
(new version of typo3)
You have a site-preview as central UI. In that UI you have the site tree
as central element and you add things into that, pages, chapters, shortcuts (afaik references to other parts).
Paths are configuration for each individual thing.
Comment #71
fabianx commented>Yes, I realize that Drupal 7 had this magic. It is indeed nifty.
> But if retaining this magic means pushing a nonsensical option in the face of every person who adds a link, I'd rather the nifty magic be available from contrib, TBH.
It is, however there are other voices that said that this is an still open regression from Drupal 5 (!), so your mileage definitely might vary.
Please also see:
https://www.drupal.org/node/2351379#comment-9316867 AND https://www.drupal.org/node/2351379#comment-9317579
I still like the proposal of:
https://www.drupal.org/node/2351379#comment-9379075
BEST, but a user interface change is needed in any case and DX improvements, too.
Even if we decided that everything is a route (and I can see merits for that), currently to link to an alias, you would use an external link and put in base://.
What we currently have is unfortunately buggy, inconsistent and broken.
And #2351379: [meta] Define, then support exact use cases for link generation and storage and the other path issues is the most developer _and_ user facing Drupal 8 issue at the moment. (meaning there is likely API additions and maybe how-to-do-things and UX changes around this).
Currently there is just confusion (we also see this a lot with themers).
I am at peace with both proposals (yay!) but we need to:
a) decide routes everywhere OR paths everywhere, where something is user entered (example: views path / route for a dynamically created display). paths everywhere could be accompanied with a simple entity reference field for "linking" to content.
b) Ensure base:// is approximately abstracted both in the UI (external link to link to something internal feels 'interesting') and in DX (Url::fromPath('alias')).
The BIG BIG misunderstanding with pwolanin and others for routes vs. paths is that:
- I never ever wanted something that you can still do: function('node/1') and out comes 'de/ein-alias' or 'en/an-alias', so the old path generation. (which is what generateFromPath (deprecated) still does.
- What I wanted was to link to aliases and final urls (which is what base:// does), but with having access checks.
Comment #72
Crell commentedGenerally speaking, I think it's entirely reasonable that if you type a string into the UI that we standardize it as a path, not a route, and that it stays that way.
From *code*, we should be pushing everyone and everything to routes. I don't see that as a problem to have the UI and code use different models since they are doing, well, different things.
For those cases where you might want something stored as a route... build a UI (Entity Reference and similar). That's the Drupal Way, isn't it? When in doubt, add a UI? :-)
Comment #73
chx commentedAs long as there's an easy way to convert a path to a route. This code right now can be found in the menu link codebase ... and the route plugin in migrate ... and I have no idea where else probably the link field item widget contains a slightly less functioning version of it as well.
Comment #74
pwolanin commentedIf we want to make fundamental changes, we need to set up a meeting and really go over the use cases and expectations.
In any case, the patch does not make sense to me as is - we would really need to redefine how we are handling the data, not stuffing user data into a fake route.
Comment #75
dawehnerWe (@amateescu, @yched, @dawehner) talked a bit about this issue
we got quite convinced that the "/legal" case in the issue summary exists in D7 as well.
It exists as well, because /legal would have been translated to /node/1. At least in D7
we have shown then "/node/1", let's ensure that we do the same in D8.
On the other hand there are examples like "/blog" pointing to a view initially but poiniting to a panel page later,
but the menu link still points to the old route.
Our "solution" was that for some specific routes (all entity routes, all /admin routes ... we could introduce a flag on routes) we store the route name/parameters, for all other ones, we store the path. To make the later case faster, we can leverage #2370651: Make (routing by path) cacheable.
This solution would not fix the first problem described in the issue summary (but nor does D7 solves that), but removes the general brittleness,
while keeping common usecases, like path aliases to specific nodes correct.
On top of that someone could write a contrib module which allows the creation of menu links, which never does the route name resolving, but
always stores the path, so we could even provide the usecase of creating the menu before the actual functionality.
Comment #76
wim leers+1 to this.
That'd allow Drupal 8 core to always resolve paths and path aliases to routes, and hence always "link by route". The complex and confusing case — while possible — is then deferred to contrib and doesn't have to pollute the UX that we ship with anymore.
Comment #77
dawehner... Note: You misread: I wrote that we convert some of them ... the contrib module would just NEVER resolve it. You kind of assumed that the opposite of never is always.
Comment #78
fabianx commented#76: The UX will be polluted in any case, because base:// as external link, while it might work (not tested) is "problematic" for UX of content editors.
There is unfortunately no solution without drawbacks.
The first question to answer is (as stated already):
- Routes everywhere (de-normalized to whatever is active at this point in time) or paths everywhere? (in all widgets where a path is entered by the user)
( the definition of path here is the already aliased and language-coded path, i. e. what you provide after base://)
We cannot have both at the same time (chx, me and others discussed this long and wide).
There are as many valid use cases for having paths be the thing that is the abstraction or having routes be the thing that is the abstraction.
I suggest we give this over to core committers unless someone else has something to say that has not been said, yet.
So that a decision is made on the 'core' of the issue.
-----------------------------------
To summarize again there are two proposals:
1) Paths are entered and stored as paths (D7 contrib link module field, D5 behavior)
Any internal path entered is treated as prefixed with base:// and passed to Uri::fromUri(), corresponding links having such paths are access checked (this is an actionable item we need in both proposals).
In this case if you wanted to link to content, you would use an entity reference field (and possibly a contrib entity reference browser).
As such Content Linking would be a new Menu Link plugin using entity references. (if I understood that correctly)
If you wanted to link to a specific route, this would not be possible unless put in programatically.
The patch in this issue would theoretically achieve that (even though another implementation would be possible).
Proposals side effects / bugs:
- Not too many known as what the user enters keeps what he has entered.
- Links to e.g. a view providing 'events/' would keep going to whatever provides that path.
- Would solve quite a few issues kinda automatically as the UI in general expects paths currently.
- Putting in /legal would always link to whatever provides /legal (alias or path or whatever), if content moves to another location, the menu link, path link would _not_ follow along (unless an explicit Entity Reference is used).
Needed UI changes:
- Linking to content (what editors really want according to UX team) would need to be done explicity using entity references
- UX improved with an Entity Reference Browser possibly in 8.X.0 (X >= 1)
2) Paths are entered and stored as de-normalized routes (menu link D6/D7 behavior, D8 behavior for everything)
- Any internal path entered is stored as de-normalized route using whatever information is available to resolve to the route.
The route is generated and access checked via Uri::fromRoute() as usual using the stored route information.
UI/UX changes needed:
- Need a way for content editors to put in arbitrary links. Possible: Use external link with base:// prefix. (but UX is awkward)
Current side effects / bugs / open questions:
- Routes can be very specific, e.g. a display of views and might not be what the site builder expects, needs content migration when a view is replaced by another view or renamed.
- The path widget needs to find the current path at run-time, need to re-add a message to what path the entered path was changed now. Currently it shows what the user originally entered, but it is no longer taken into account at all. (hence very misleading)
- Language negotiation is unclear (e.g. is the current interface language needed to be switched to link to the german version of a page, how do we store that information, etc.)
- A path to /legal is de-normalized to /node/1 and any change to the alias /legal won't be reflected into what the menu item links to
Disclaimer:
I am still biased towards 1) as it feels more logical to me and is the workflow that our clients content editors generally use (link field for external and arbitrary, entity reference with content browser for internal content links).
Also my personal feeling is that going with 1) would let us resolve this issue easier, while still providing contrib with the means to both enable direct route linking. (e.g. a route browser - how awesome!) and core in 8.1 or later with a really good Entity Reference Browser to support content linking properly.
I can understand however that Peter, et al. feel strongly about the D6/D7 menu link workflow that de-normalized things to content from a path automatically. And I see that it is an equally valid workflow.
So:
Routes or Path storage for user entered paths.
Let the core committers decide. Tentatively assigning to alexpott as I assume he is the most neutral on that topic so far.
(If someone feels all this arguments belong to an IS that we all together massage, that is also fine.)
--
Meanwhile we can open two child issues (and also discuss whether we want to do those):
- Create Url::fromPath() -- for better base:// DX
- Make Link objects with base:// in it access checkable
We need those regardless (or decide not to do it) of the decision of how to store user entered paths here.
Comment #79
catchI actually think #2351379: [meta] Define, then support exact use cases for link generation and storage is the better issue to discuss this on, or at least we should close one or the other issue as duplicate. This definitely needs a decision on the way forward quite soon, and we need to figure out exactly how much of it and which bits are upgrade path blocking/critical.
Comment #80
dawehner@fabianx
It is too bad, that you haven't responded to the mixed approach in #75 (note: wim have misread it). It also solves most of the problems.
Here is a slightly different proposal to number 1 described by @Fabianx:
Really at this point of the release we should just have the same behaviour, as we had in D6/D7. It worked for people, it would simplify migrations,
as well as allows us to focus on the other issues in D8.
We do have a flexible menu system in place already, so in contrib you can do things like resolve the routes on save time, allow to link to a non existing thing, or simply use config for storing menu links.
More congrete this means the following points:
/legalproblem described in the issue summary, mostly because this conflicts with point 2) and is an existing problem since D6. There is no reason we can't ship D8 with that problem still existing.Comment #81
pwolanin commentedSo, I kind of hate bringin system path back into this, and I really don't like the idea of needing to find the route each time.
However, storing the system path instead of the user-entered (alias) might allow us to do a better job of resolving the correct route during discovery.
Note - I still think we need a different system for links to entities vs. user-entered links here. The menu link field is an option, or an additional bundle or menu link entity type
Comment #82
catchMarking this postponed on #2351379: [meta] Define, then support exact use cases for link generation and storage - we need to figure out exactly what we can/should support in core at all before trying to accommodate the current behaviour.
Comment #83
xjmComment #84
dawehnerComment #85
webchickI like that we're brainstorming on in what exact way we should ask users this question in order to make it less confusing, but the simple fact is that no other website/CMS/framework/whatever ask their users this question. Ever. So we're inventing a "Drupalism" here, and a Drupalism that is pushed directly to the forefront of some of the least technical people on a Drupal site (the people making press releases or whatever).
Nevertheless, tagging Usability. But I still am missing the key reason we need to push this question to users at all, since no other thing I've ever used does this.
Comment #86
webchickOk, based on a mega-call today with various people outlined at these meeting minutes, I believe that #2407505: [meta] Finalize the menu links (and other user-entered paths) system defines an actionable way forward on this general problem space. Therefore, closing this issue in favour of that one.
Comment #87
xjmComment #88
xjm