I'm not sure that this function needs to still exist, and if it does then it shouldn't remain a floating utility function. For now, however, it needs to be adjusted to check both the menu_router table and the new router system for legal paths, since legal paths could live in both for the moment.

I'm not sure if this belongs in the menu system component or routing system component, so flagging for menu for now.

Comments

catch’s picture

Priority: Normal » Major

Bumping to major since this blocks the conversion of menu links to the new router. See #1848648: [meta] Convert menu system to new routing system for related issues.

catch’s picture

This also depends on the #access key from menu_get_item(). That'll need to check both systems as well.

klausi’s picture

Status: Active » Needs review
StatusFileSize
new3.6 KB
new37.08 KB

Ok, so here is a first patch that updates drupal_valid_path() to look at both routing systems. Added a helper function _drupal_valid_path_new_router() for that.

Depends on #1793520: Add access control mechanism for new router system.

I also included a special key for hook_menu() that we can use for conversion until menu links are sorted out.

  $items['user/register'] = array(
    'title' => 'Create new account',
    'type' => MENU_LOCAL_TASK,
    'router_converted' => TRUE,
  );

Please review the interdiff.

Crell’s picture

Do we need to still define menu items in the old router as no-ops? Hm. Well, I guess we do for menu links for the time being. Blargh.

However, if we need to do that... instead of flagging it as "the princess is in the other router" let's be nice to Mario and say where. Vis, have a route_name property that is the machine name of the route that corresponds to this menu_item. We may be able to eliminate the subrequest then, no? Since we can just grab the route by name?

catch’s picture

Issue tags: +needs profiling

The current patch has potentially two performance issues, I'm assuming the first will be more severe than the second:

- every access check requires an individual database query to find the router item associated with the link, we've previously had that attached to menu links so it didn't require a separate lookup.

- every access check requires mocking the request object.

Since this patch will be the first opportunity to actually test this with the new router system, I'm adding the 'needs profiling' tag.

It'd be good to compare before/after with a large-ish menu tree (admin/config with a lot of modules enabled for example) - we might need to fake it with some dummy router items/links to have enough access checks to be comparable.

moshe weitzman’s picture

FYI devel_generate can generate menu links. Might need updating for D8.

catch’s picture

Title: Update drupal_valid_path() for new router » Update menu link access checks for new router

This came up in irc and I remembered why I thought the issue title was strange.

menu_valid_item() is one place this gets checked, but for menu link access when they're rendered, it's

http://api.drupal.org/api/drupal/core%21includes%21menu.inc/function/_me...

and

http://api.drupal.org/api/drupal/core%21includes%21menu.inc/function/_me... (which loads menu objects ready for the access check).

Since this is going to be similar, I think we should do the two places at the same time, but could be persuaded otherwise if there's a really good reason to split.

chx’s picture

If you need a different access check then your point of attack is the _menu_link_translate call in _menu_tree_check_access. You need to replace _menu_link_translate with something else. You will have a world of pain with links containing % signs. Otherwise, not so much. Obviously, the _menu_check_access call also needs to be replaced in this _menu_link_translate replacement .

klausi’s picture

Status: Needs review » Needs work

I'm currently busy with getting rest.module into shape, so unfortunately I don't have time to continue with this patch. The posted patch is really far from ready, so setting to needs work.

g.oechsler’s picture

StatusFileSize
new5.6 KB

I am not entirely sure if I understood what we need to do here. So for starters I tried to port the patch from #3 (or merely just the interdiff) to the new routing system.

To document what I think it should possibly do I added a Test for it. :)

Further advice is very welcome.

g.oechsler’s picture

Status: Needs work » Needs review
Crell’s picture

+++ b/core/modules/system/lib/Drupal/system/Tests/Menu/MenuLinkAccessTest.php
@@ -0,0 +1,51 @@
+    $form = array(
+      'source' => 'router_test/test7',
+      'alias' => 'menu_link_test_alias/foo'
+    );
+    $this->drupalPost('admin/config/search/path/add', $form, t('Save'));

This should be possible without making a POST request. Just call the appropriate APIs, no?

I am not sure that this approach is still valid. Unfortunately I fear this patch is realistically blocked on #916388: Convert menu links into entities, since that's going to change how menu links work at a fundamental level. :-/

webchick’s picture

StatusFileSize
new3.66 KB

One of the user.module hunks was failing, so here's a free re-roll, courtesy of porting pants module to D8.

I'm still not sure how to make a menu item both have its route defined in a YML file, and also appear as a clickable link under admin/foo* though. This patch doesn't seem to help.

webchick’s picture

Hm. Nevermind, I was able to hack around it with the same hack user/register is currently doing, of defining its access callback twice: once in YAML and once in hook_menu(). I don't think we can ship this way though, so I wonder if this needs to be critical.

Crell’s picture

Splitting this off from #1889790: [Meta] Allow modules to register links for menus, breadcrumbs and tabs (if not with hook_menu)

We take hook_menu as it exists now and add a "route" key. That "route" key refers to a route to which that menu link points. The path of the route *overrides* the path that the menu item has. Then we remove the now-duplicated parts of the menu item definition: page_callback, page_arguments, access control, etc.

Then we modify the link build process to pull in the corresponding route and build out the same structure we have now, more or less. This would also partially if not entirely resolve #1845402: Update menu link access checks for new router.

Benefit: We're no longer duplicating route information between hook_menu and routes. hook_menu is now defining just, er, menus.

-----

That relates to that issue, so let's do it here as one.

catch’s picture

@webchick this issue is in the summary of #1848648: [meta] Convert menu system to new routing system which is marked as critical, although it's definitely critical in its own right as well.

@Crell that sounds like a good plan with the current menu link system, although I have no idea how it'll interact with menu links as entities.

amateescu’s picture

@catch, the interaction with menu links as entities should be pretty minor, we can load all the needed routes (at once) and reconstruct the current structure in attachLoad().

Crell’s picture

amateescu: So is it safe to work on this issue in parallel with the links-as-entities patch, or would one going in screw over the other?

amateescu’s picture

This one will definitely screw the menu links as entities patch because there are a lot of places where we do db queries for getting menu links instead of using api functions (like menu_link_load())..

Even more, because our current access checks are closely tied to upcasting, I would say that this is also blocked by #1798214: Upcast request arguments/attributes to full objects and maybe #1906810: Require type hints for automatic entity upcasting too.

Crell’s picture

Assigned: Unassigned » Crell

Taking a stab at this on the plane today.

Crell’s picture

StatusFileSize
new17.01 KB

OK, let's see how well the bot likes this.

I've extended the code above. It now binds a route to a menu link, and pulls data from there as necessary. I also modified the Link entity to include that route object, and mass-load them when the Link is loaded. There may be a better way there; I'm open to suggestions. However, this does, I think, get us working access control and a thinner hook_menu definition.

Note: In order to make access control work, I had to include the code from #1907750: Symfony update exposes fatal error during installation due to 'access_manager' service depending on 'request', which we need anyway. If that goes in first we can rebase it out of my branch here, or if this goes in first that issue can move on to other things.

Status: Needs review » Needs work

The last submitted patch, 1845402-menu-links-access.patch, failed testing.

Crell’s picture

Status: Needs work » Needs review
StatusFileSize
new17.54 KB

Now with 100% less failing installer!

Status: Needs review » Needs work

The last submitted patch, 1845402-menu-links-access.patch, failed testing.

Crell’s picture

Status: Needs work » Needs review
StatusFileSize
new18.67 KB

Now I've got real failures to fix! This should do it, I think. Book module apparently hadn't been fully converted to entity links; I did a bit more of that.

amateescu’s picture

+++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php
@@ -324,6 +324,31 @@ public function loadModuleAdminTasks() {
   /**
+   * Implements \Drupal\Core\Entity\EntityStorageControllerInterface::load().
+   */
+  public function load(array $ids = NULL) {
+    $entities = parent::load($ids);
+
+    // For all links that have an associated route, load the route object now
+    // and save it on the object. That way we avoid a select N+1 problem later.
+    $routes = array();
+    foreach ($entities as $id => $entity) {
+      if ($entity->route) {
+        $routes[$id] = $entity->route;
+      }
+    }
+
+    if ($routes) {
+      $route_objects = drupal_container()->get('router.route_provider')->getRoutesByNames($routes);
+      foreach ($routes as $entity_id => $route) {
+        $entities[$entity_id]->setRouteObject($route_objects[$route]);
+      }
+    }
+
+    return $entities;

This should go into attachLoad().

+++ b/core/modules/menu_link/lib/Drupal/menu_link/Plugin/Core/Entity/MenuLink.php
@@ -232,6 +234,20 @@ class MenuLink extends Entity implements \ArrayAccess, ContentEntityInterface {
   /**
+   * The name of the route associated with this Link, if any.
+   *
+   * @var string
+   */
+  public $route;

No, no, no.. this is mixing the route property of a router item with a menu link property, and I thought the goal was to untangle them :/

Or.. did you actually want to add the 'route' column to the {menu_links} instead of {router}?

Crell’s picture

StatusFileSize
new19.06 KB

Talked to amateescu in IRC. Attached patch fixes load() vs. attachLoad() (thanks!). Also moves the route information to the menu_links table, as that's nominally more proper. That keeps the property public, but should reduce future work to rip out menu_router when the time comes.

Status: Needs review » Needs work
Issue tags: -WSCCI, -needs profiling

The last submitted patch, 1845402-menu-links-access.patch, failed testing.

Crell’s picture

Status: Needs work » Needs review

#27: 1845402-menu-links-access.patch queued for re-testing.

Status: Needs review » Needs work
Issue tags: +WSCCI, +needs profiling

The last submitted patch, 1845402-menu-links-access.patch, failed testing.

jthorson’s picture

From testbot apache error log:

PHP Fatal error: Call to a member function setRouteObject() on a non-object in /var/lib/drupaltestbot/sites/default/files/checkout/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php on line 84

Crell’s picture

That's fascinating, because I am able to install, logout, and login just fine without issue on my local.

jthorson’s picture

Status: Needs work » Needs review
Issue tags: -WSCCI, -needs profiling

#27: 1845402-menu-links-access.patch queued for re-testing.

Status: Needs review » Needs work
Issue tags: +WSCCI, +needs profiling

The last submitted patch, 1845402-menu-links-access.patch, failed testing.

Crell’s picture

Status: Needs work » Needs review
StatusFileSize
new19.06 KB

Rerolling for update hook increment.

Status: Needs review » Needs work

The last submitted patch, 1845402-menu-links-access.patch, failed testing.

jthorson’s picture

From testbot checkout watchdog log:

Notice: Undefined variable: id in Drupal\menu_link\MenuLinkStorageController->attachLoad() (line 76 of /var/lib/drupaltestbot/sites/default/files/checkout/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php).

Notice: Undefined index: in Drupal\menu_link\MenuLinkStorageController->attachLoad() (line 84 of /var/lib/drupaltestbot/sites/default/files/checkout/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php).

Along with the PHP fatal listed above.

Just for completeness sake ... drupalLogin() is returning 3 passes, 3 fails. I'm also seeing warnings in the tesbot log itself (though those appear somewhat more randomly):

Warning: simplexml_import_dom(): Invalid Nodetype to import in DrupalWebTestCase->parse() (line 1594 of /var/lib/drupaltestbot/sites/all/modules/simpletest/drupal_web_test_case.php). x2

dawehner’s picture

Status: Needs work » Needs review
StatusFileSize
new769 bytes
new19.07 KB

Let's see.

andypost’s picture

+++ b/core/modules/menu_link/menu_link.installundefined
@@ -199,6 +199,11 @@ function menu_link_schema() {
+      'route_name' => array(
+        'description' => 'The machine name of a defined Symfony Route this menu item represents.',
...
+        'length' => 255,

+++ b/core/modules/system/system.installundefined
@@ -2095,6 +2095,19 @@ function system_update_8048() {
+function system_update_8050() {
+  $spec = array(
...
+    'type' => 'varchar',
+    'length' => 255,

Why they are in different modules? Also not sure that 255 is enough for route as urls could be x4 longer

Possibly caused by #1239370: Module updates of new modules in core are not executed

+++ b/core/modules/menu_link/menu_link.moduleundefined
@@ -117,7 +117,7 @@ function menu_link_delete_multiple(array $mlids, $force = FALSE, $prevent_repare
-  $menu_link->save();
+  return $menu_link->save();

Do not need to touch deprecated functions!

+++ b/core/modules/user/user.moduleundefined
@@ -879,22 +879,15 @@ function user_menu() {
   $items['user/autocomplete'] = array(
     'title' => 'User autocomplete',
     // _menu_router_build() denies access to paths without a page callback.
...
+    'route_name' => 'user_autocomplete',
...
   $items['user/autocomplete/anonymous'] = array(
     'title' => 'User autocomplete including anonymous',
     // _menu_router_build() denies access to paths without a page callback.
...
+    'route_name' => 'user_autocomplete_anonymous',

Maybe better to replace it with @todo and link?

This entries looks useless

+++ b/core/modules/user/user.moduleundefined
@@ -922,13 +915,7 @@ function user_menu() {
   $items['user/register'] = array(
     'title' => 'Create new account',
     'type' => MENU_LOCAL_TASK,
...
+    'route_name' => 'user_register',

Needs @todo for #1889790: [Meta] Allow modules to register links for menus, breadcrumbs and tabs (if not with hook_menu)

dawehner’s picture

StatusFileSize
new18.68 KB
new3.02 KB

Why they are in different modules?

Yeah you are probably right on that point.

Also not sure that 255 is enough for route as urls could be x4 longer

That's the router name, not the path, so for example "edit_metadata", see edit.routing.yml

Maybe better to replace it with @todo and link?

This entries looks useless

There is already a @todo in the beginning of user_menu().

Needs @todo for #1889790: [Meta] Allow modules to register links for menus, breadcrumbs and tabs (if not with hook_menu)

Not sure whether it really makes sense to add the todo, because, you would have to add one to all menu items already.

Fixed some other pieces.

Crell’s picture

Status: Needs review » Needs work

Thanks, @dawehner! I've added you to the WSCCI sandbox. :-) Please push your changes to the branch that is there for safe keeping.

Can we get a review of the approach in general? What else is needed here to move forward? This is only aiming to be an incremental improvement.

Crell’s picture

Status: Needs work » Needs review

Bah, Drupal.

dawehner’s picture

I'm wondering whether we want to have explicit tests for this kind of functionality?

Crell’s picture

Can you clarify what you mean by "this kind of functionality?"

dawehner’s picture

This has been a bad comment from mine.

Create an explicit test for a hook_menu entry with a connected route, that has an access check and test, for example, that _menu_translate() returns what's expected.

amateescu’s picture

Assigned: Crell » amateescu

I'll take a stab at that test tomorrow.

amateescu’s picture

Assigned: amateescu » Unassigned
Status: Needs review » Reviewed & tested by the community
StatusFileSize
new3.21 KB
new19.46 KB

Discussed this with @dawehner and we agreed that the expected functionality is already tested by the 'user/register' route (which has to stay in hook_menu() because it's a MENU_LOCAL_TASK) and there's really no point in testing _menu_link_translate() since it will be going away soon-ish anyway.

I found a couple of other doxygen issues and also that we don't need hook_menu() entries anymore for user autocomplete routes, since this patch fixes the @todo that was keeping them in place!

Therefore, I think this is good enough for now and allows the conversion of MENU_CALLBACK router items to be crowdsourced this weekend :)

Status: Reviewed & tested by the community » Needs work

The last submitted patch, drupal-1845402-47.patch, failed testing.

amateescu’s picture

Status: Needs work » Reviewed & tested by the community
StatusFileSize
new19.45 KB

Merged HEAD.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, drupal-1845402-49.patch, failed testing.

amateescu’s picture

Status: Needs work » Needs review
StatusFileSize
new19.46 KB

This is what happens when we work with and without branches :( We were missing the fix from #38.

amateescu’s picture

Status: Needs review » Reviewed & tested by the community

Finally!

dries’s picture

It's not clear whether catch's performance concern in #5 was addressed?

webchick’s picture

Assigned: Unassigned » catch

Tossing it at him. :)

Crell’s picture

The query-per-check should have been addressed by the way we load the routes with the menu links. We're still mocking the request, but not with a full subrequest but just a direct call to the matcher so it should be reasonably expedient. I'm not sure how to make that any faster at the moment.

catch’s picture

Assigned: catch » Unassigned

msonnabaum said he'd be profiling this today. The query-per-route was my main concern and that's fixed, I think smaller regressions we can make up if we do #1805054: Cache localized, access filtered, URL resolved, and rendered menu trees (since current menu link caching in 7/8 is not that efficient).

Assuming profiling comes back OK I'm happy for someone else to commit this (and I know people are keen to have it in tomorrow for conversions - I won't be around for most of tomorrow so don't want to hold it up).

tim.plunkett’s picture

#51: drupal-1845402-51.patch queued for re-testing.

webchick’s picture

Issue tags: +SprintWeekend2013
msonnabaum’s picture

Issue tags: -SprintWeekend2013

I profiled this patch on new d8 install, hitting user/register 30 times each. That seemed to be a decent place to trigger the new code path, but please correct me if I'm wrong here.

I'm actually showing a slight improvement in wall time with the patch. I dug into it a bit and it looks like there's some savings from not calling things like _menu_check_access, which in turn makes a bunch of config calls, etc.

I could dig further, but it doesn't seem worth it. This patch feels very low risk performance-wise to me.

webchick’s picture

Title: Update menu link access checks for new router » Change notice: Update menu link access checks for new router
Status: Reviewed & tested by the community » Active
Issue tags: +Needs change record, +SprintWeekend2013

Ok, looks like the poor patch survived both testbot and msonnabaum :D so going to go ahead and get this in to help people attending the Drupal Global Sprint Weekend crank on conversion patches! :)

I love love love that this patch gets rid of totally awkward stuff like the NOT_USED value. It's a little unfortunate you need to manually specify the route name for visual links, but I can't think of another way now that path is no longer unique between routes.

Committed and pushed to 8.x. Thanks!

This'll need an update to the routing system change notice. We might be getting close to be able to stop updating that now. :P

Crell’s picture

Woo! Thanks all!

I will update the change notice for this and #1934832: Provide a dedicated approach for using forms in routes at the Chicago sprint tomorrow.

Crell’s picture

Change notice updated.

Crell’s picture

Title: Change notice: Update menu link access checks for new router » Update menu link access checks for new router
Status: Active » Fixed
Issue tags: -Needs change record

.

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