Hello,

I'm creating a site that has dozens of different menus, many of which have various items in common. Rather than making each menu manually from scratch, I'd like to be able to clone a menu "template" and then just modify the unique parts (paths/titles) of each menu I create.

I use the node clone and block clone modules to clone blocks and nodes, but those don't facilitate menu cloning.

I've read the post asking how to clone menus at http://drupal.org/node/147404, but no satisfactory solution arose from that post. Since it's been about year, does anyone have any new ideas? Is the menu creation process faster in D6.2?

Thanks very much for any help.

Comments

halloffame’s picture

I'm also interested in duplicating Menus automatically. Have you found a way yet?

jkestler’s picture

I never got an answer, so I stopped using the Drupal menu system and just created my menus with PHP. It ended up being much faster.

sudeepg’s picture

Subscribing!!! Even I did not find any answer for that and had to manually do that process. Writing a script to do the same was another option.......but, since i didnot have too many menus to clone I chose to go the manual.

Sudeep Goyal,
Ebizon NetInfo Pvt. Ltd.
Delhi, India
http://ebizontek.com
Ph: +91120 4546843
Email: info@ebizontek.com
skype id : am.ebizon

Joshua Sortino’s picture

Would love to know if this is possible. It's a pain to re-create a menu with 100's of links.

inforeto’s picture

subscribing

tavin.cole’s picture

This is a quick hack I wrote after studying the structure of the menu_links table for my drupal-6.9 mysql installation. I'm not sure how that table might differ across drupal versions but the script only needs to treat certain columns specially; the rest are copied through as is. I don't know why the table is designed so that each row references its own autoincrement pkey. That made the script a lot harder to write.

Usage: drupal6-mysql-menu-clone.php <menu-source-name> <menu-target-name>

<?php
# LICENSE
#
# Copyright (c) 2009 Tavin Cole
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

define('MYSQL_DSN''mysql:host=host;dbname=dbname;');
define('MYSQL_USER', 'user');
define('MYSQL_PASS', 'pass');

if (
3 != $argc) die("Usage: $argv[0] <menu-source-name> <menu-target-name>\n");

$menu1 = $argv[1];
$menu2 = $argv[2];

$pdo = new PDO(MYSQL_DSN, MYSQL_USER, MYSQL_PASS,
               array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));

$pdo->beginTransaction();

$sth1 = $pdo->prepare('SELECT * FROM menu_links WHERE menu_name=? ORDER BY depth');
$sth1->execute(array($menu1));

$sth2 = $pdo->prepare(sprintf(
   
'INSERT INTO menu_links VALUES (%s)',
   
implode(',', array_fill(0, $sth1->columnCount(), '?'))
));

$sth3 = $pdo->prepare('UPDATE menu_links SET plid=?,p1=?,p2=?,p3=?,p4=?,p5=?,p6=?,p7=?,p8=?,p9=? WHERE mlid=?');

$mlid_map = array(0 => 0);

foreach (
$sth1->fetchAll(PDO::FETCH_ASSOC) as $row) {
   
$mlid = $row['mlid'];
   
$mlid_map[$mlid] = 0;
   
$row['menu_name'] = $menu2;
   
$row['mlid'] = null;
   
$p = array();
    foreach (
qw('plid p1 p2 p3 p4 p5 p6 p7 p8 p9') as $k) {
       
$p[] = $mlid_map[ $row[$k] ];
       
$row[$k] = 0;
    }
   
$sth2->execute(array_values($row));
   
$mlid = $mlid_map[$mlid] = $pdo->lastInsertId();
    for (
$i = 1; $i < 10; ++$i) {
        if (
0 == $p[$i]) {
           
$p[$i] = $mlid;
            break;
        }
    }
   
$p[] = $mlid;
   
$sth3->execute($p);
}

$pdo->commit();

// utility functions

function qw($_) {return preg_split('/\s+/',$_,-1,PREG_SPLIT_NO_EMPTY);}
?>
walktalker’s picture

Thanks a lot tavin.cole,

I know this would be useful for me, but after I create the file drupal6-mysql-menu-clone.php, which contain the code you've post, where should I put this file and how should I execute it?

Some explaination would be highly appreciated.

Regards,
Kent

rayzzz’s picture

How does it work? Do you need to edit something in the file (besides the MySQL information)

citronica’s picture

Does the Menu Block module accomplish what you're looking for?

http://drupal.org/project/menu_block

rayzzz’s picture

No, I don't want to clone the menu block, I want to clone the actual menu.

Garrett Albright’s picture

I encountered this problem as well. I wanted to clone a menu fourteen times… Rather than recreating fourteen menus, I hacked together a module to clone menus. It's D6-only, though. Give it a try - but back up your database first.

http://drupal.org/project/menu_clone

(If you don't see the link to download the dev release yet, wait a few hours and come back.)

fkiner’s picture

Works like a charm. Thanks

hermes_costell’s picture

$my_new_menu = array();
$my_new_menu['menu_name'] = 'SET-YOUR-NAME-HERE';
$my_new_menu['title'] = 'YOUR TITLE';
$my_new_menu['description'] =  'DESCRIPTION';
menu_save($my_new_menu);

$my_old_menu_name = 'THE-OLD-MENU-NAME';

$old_links = menu_load_links($my_old_menu_name);
$array_of_old_mlids = array();
  foreach($old_links as $this_old_link){
    $array_of_old_mlids[] = $this_old_link['mlid'];
  }
$cloned_array = menu_links_clone($old_links, $my_new_menu['menu_name']);
$array_of_broken_menu_items = array();
$counter = 0;
if(count($cloned_array) >0){
  foreach($cloned_array as $new_menu_item){
    $possibly_broken_menu_item_mlid = menu_link_save($new_menu_item);
    $array_matching_old_mlid_and_new[$array_of_old_mlids[$counter]] = $possibly_broken_menu_item_mlid;
    $counter++;
  }
}
//reload the old menu - which is going to contain the incorrectly saved, nested links
$old_links = menu_load_links($my_old_menu_name);
foreach($old_links as $this_old_link){
  if(in_array($this_old_link['mlid'], $array_matching_old_mlid_and_new)){
    //this menu item was supposed to be in the new menu, but we're finding it here in the old one
    //fix its plid
    $new_value = $array_matching_old_mlid_and_new[$this_old_link["plid"]];
    $this_old_link["plid"] = $new_value;
    menu_link_save($this_old_link);
  }
}

menu_cache_clear_all();

czek’s picture

I think you're looking for this:

http://drupal.org/project/menu_clone

kris-o3’s picture

sandbox project.
http://drupal.org/sandbox/kris-o3/1853420
optionally copies a user-defined ("menu-" prefix in menu_name) menu's items from the Add Menu form.

the project review thread:
http://drupal.org/node/1853440#comment-6804732