Looks like I'll be needing this for a project. In effect, I have an XML document that defines a menu hierarchy. Parsing that as a source is handled, but we need a target mapping for the menu system.

Filing this issue in case anyone is also working on this problem.

EDIT: Since I cannot guarantee that all items will be nodes, I don't think I can group this logic with a node importer. I think a generic menu destination would be more useful.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

agentrickard’s picture

Looking again. Is it possible to just fake this by using the MigrateDestinationTable class?

That is, define the {menu_links} table in the import class and leverage MigrateDestinationTable, since we're directly writing to a single table?

moshe weitzman’s picture

There can be a bunch of hooks that fire when you save menu items and menu links. I suggest writing a dest plugin that calls the right menu CRUD API functions.

Caveat: I dunno if those API functions call menu_rebuild() but if they do thats a performance killer.

agentrickard’s picture

Yeah, good point about the API functions.

And, yes, menu_link_save() does a cache clear, and clears the menu cache of the affected target menu. The code doesn't force a menu_rebuild() when it is called, but when the next page is generated.

I'll do some testing.

agentrickard’s picture

And we actually need menu.inc and menu_links.inc, which are separate targets.

agentrickard’s picture

Technical question here.

I have both plugins working (mostly) and wonder what the Migrate policy is here.

  1. menu_delete() will delete a menu and all its menu links.
  2. So MigrateDestinationMenu->rollback() will therefore delete menu links defined by MigrateDestinationMenuLinks->import().
  3. However, rolling back Menu does not rollback the corresponding items in MenuLinks. I suspect that it should.

How is this kind of cross-dependency normally handled? Is the migration class (for the specific project) supposed to reconcile this, or do I write a handler to track the dependency?

marvil07’s picture

Title: Add menu.inc as a destination plugin » Add menu destination plugins

I will be very glad to review/continue this.

@agentrickard: It would be great if you can post a patch soon, so I do not start from scratch.

agentrickard’s picture

Status: Active » Needs work
FileSize
7.7 KB
5.02 KB

Here they are. I didn't bother rolling as a patch, largely because I haven't figured out how to make a git patch for new files ;-(.

To use, download, rename to .inc and stick in a plugins directory for migrate. Be sure to register them in a .info, too.

marvil07’s picture

Status: Needs work » Needs review
FileSize
14.03 KB

Ok, I will test this soon, in the meantime a patch.

marvil07’s picture

Assigned: Unassigned » marvil07
Status: Needs review » Needs work

Sorry, I will include the info file changes on the next patch.

marvil07’s picture

Assigned: marvil07 » Unassigned
Status: Needs work » Needs review
FileSize
14.56 KB

Ok, here the same patch with the info file additions.

I have just tested MigrateDestinationMenu and it works fine.

As mentioned the only really thing to change could be replacing the menu_{save,delete}() functions with custom ones that clone its functionality except from the call to menu_cache_clear_all(), and then on MigrateDestinationMenu::postImport() for menu_save clone and on MigrateDestinationMenu::postRollback() for menu_delete clone call the cache clear.
I guess that's the only way to avoid rebuilding the cache for each menu_custom.

Please let me know about your opinions on that approach for menu_custom's and I could code that change(I am importing too little menu_custom's to really see the impact).

I will review the MigrateDestinationMenuLinks soon.

mikeryan’s picture

Status: Needs review » Needs work

Copied from table_copy.inc, eh?;) There's some cruft that's not relevant for your plugin - specifically, setting tableName and keySchema, for example, which are specific to table_copy.inc.

There's no reason to use bulkRollback without a bulk API to call - just implement rollback(), deleting one menu/link at a time.

In the prepare(Rollback) and complete(Rollback) methods, you're passing 'Menu' and 'MenuLinks' as the destination type - this corresponds to the values passed to registerTypes and the convention is lower-case underscore delimited: menu and menu_links.

Thanks.

agentrickard’s picture

Thanks. It does work, but needs some cleanup. Rollback support isn't real clear in the docs.

mgifford’s picture

Status: Needs work » Needs review
FileSize
12.76 KB

@mikeryan - So are you recommending deleting bulkRollback()? That's what I interpreted from your comment.

You also talk about prepare(Rollback) and complete(Rollback) methods, in moving the registerTypes convention to lower-case underscore delimited (menu and menu_links), I'm assuming this means:

    migrate_handler_invoke_all('Menu', 'completeRollback', $menu_names);
    migrate_handler_invoke_all('Menu', 'complete', $object, $row);

&

    migrate_handler_invoke_all('MenuLinks', 'prepare', $object, $row);
    migrate_handler_invoke_all('MenuLinks', 'complete', $object, $row);

but also in menu.inc the same functions need to be changed in prepareRollback() & completeRollback().

I'm still exploring this module, but need this function, so am trying to help this issue along.

Here's a patch for review though based on your feedback.

mikeryan’s picture

Status: Needs review » Needs work

I didn't mean to remove rollback capability completely - just implement rollback() (one item at a time) instead of bulkRollback() (which only makes sense where the core API has a bulk delete function).

In the MigrateDestinationMenuLinks prepareRollback() and completeRollback methods(), the first argument to migrate_handler_invoke_all() should be menu_links (matching the calls in prepare() and complete()).

agentrickard’s picture

Ah, the developer API / example docs weren't real clear on which rollback method to use, so I just picked one.

alanburke’s picture

Status: Needs work » Needs review
FileSize
7.61 KB

I've update the menu_links destination a little.

It's working well for saving menu link items.

What is needed is some help with Stub migrations.
Some menu items are imported before their parents.
I've implemented a createStub function in my custom class that extends MigrateDestinationMenuLinks.
And it does create a stub menu link when it runs.

But it doesn't update the Stub when the parent link is imported later.
Any pointers appreciated.

alanburke’s picture

FileSize
7.71 KB

This one works with Stub migrations too.

alanburke’s picture

FileSize
13.38 KB

Now with a proper patch

SamH’s picture

Status: Needs review » Needs work

MigrationDestinationMenuLinks should implement rollback(), instead of bulkRollback(), as per Mike's comment in #14.

MigrationDestinationMenu doesn't seem to implement rollback at all.

agentrickard’s picture

The original in #7 has bulkRollback support, but that needs to be replaced by rollback().

SamH’s picture

Thanks. I'll work on switching menu and menu_links to rollback(), then.

SamH’s picture

Ok, I took the patch in #18, implemented rollback() for menu.inc, and also tweaked and added comments to prepareRollback() and completeRollback(). The rest should be the same as before. I'll do the same for menu_links.inc tomorrow.

SamH’s picture

Status: Needs work » Needs review
FileSize
14.46 KB

And now with rollback() implemented for menu_links.inc.

Other than that, I did a basic implementation for menu and menu links, and it works for me so far. I'll try something more complicated next that forces menu link stubs to be generated.

SamH’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
2.31 KB
14.44 KB
14.44 KB

Alan, stub generation works for me as well. I've cleaned up the comments some more, and reordered a couple functions so that menu and menu_links are consistent. This patch is ready for review.

Here's how I implemented createStub(), in case anyone else wants to use it to test with. This works for a D6 to D7 migration. For other sources, just change plid and menu_name to the right names.

  /**
   * Creates a stub menu link, for when a child is imported before its parent
   *
   * @param $migration
   *  The source migration
   * @return
   *  int $mlid on success
   *  FALSE on failure
   */
  protected function createStub($migration) {
    // if plid is 0, that means it has no parent, so don't create a stub
    if (!$migration->sourceValues->plid) {
      return FALSE;
    }
    $menu_link = array (
      'menu_name' => $migration->sourceValues->menu_name,
      'link_path' => 'stub-path',
      'router_path' => 'stub-path',
      'link_title' => t('Stub title'),
    );
    $mlid = menu_link_save($menu_link);
    if ($mlid) {
      return array($mlid);
    }
    else {
      return FALSE;
    }
  }

Edit: removed menu_cache_clear_all(), as the cache is now cleared in postImport() per #25.

mikeryan’s picture

Some cleanup - comments, variable names changes, ... The one meaningful change is to do menu_cache_clear() in postImport/postRollback, so it only gets done once per import process, not for every item imported.

Untested... Sam?

SamH’s picture

Status: Needs review » Reviewed & tested by the community

Tested and works as expected.

For menu.inc:
menu_cache_clear_all() is called on import and rollback, once for each menu.

For menu_links.inc:
menu_cache_clear_all() is called once during postImport and postRollback, rather than for each link.

mikeryan’s picture

Version: 7.x-2.x-dev » 6.x-2.x-dev
Status: Needs review » Patch (to be ported)

Committed and pushed for D7, thanks to all who contributed!

Leaving open for backport to D6, should anyone be motivated...

marvil07’s picture

marvil07’s picture

Issue summary: View changes

Adds additional detail.

pifagor’s picture

Issue summary: View changes
Status: Patch (to be ported) » Closed (outdated)