? 457450-menu-objects.patch ? test.patch ? sites/all/modules/admin_menu ? sites/all/modules/coder ? sites/all/modules/devel ? sites/all/modules/devel 2 ? sites/all/modules/devel-7.x-1.x-dev.tar.gz ? sites/default/files Index: includes/menu.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/menu.inc,v retrieving revision 1.328 diff -u -p -r1.328 menu.inc --- includes/menu.inc 10 Jun 2009 21:52:36 -0000 1.328 +++ includes/menu.inc 30 Jun 2009 19:07:16 -0000 @@ -2065,6 +2065,9 @@ function _menu_delete_item($item, $force // Update the has_children status of the parent. _menu_update_parental_status($item); menu_cache_clear($item['menu_name']); + // Notify modules we have deleted the item. + module_invoke_all('menu_item_delete', $item); + // Clear the cache. _menu_clear_page_cache(); } } @@ -2105,7 +2108,10 @@ function menu_link_save(&$item) { 'module' => 'menu', 'customized' => 0, 'updated' => 0, + 'object_type' => '', + 'object_id' => '', ); + $existing_item = FALSE; if (isset($item['mlid'])) { if ($existing_item = db_query("SELECT * FROM {menu_links} WHERE mlid = :mlid", array(':mlid' => $item['mlid']))->fetchAssoc()) { @@ -2176,6 +2182,8 @@ function menu_link_save(&$item) { 'options' => serialize($item['options']), 'customized' => $item['customized'], 'updated' => $item['updated'], + 'object_type' => $item['object_type'], + 'object_id' => $item['object_id'], )) ->execute(); } @@ -2209,11 +2217,16 @@ function menu_link_save(&$item) { if (empty($item['router_path']) || !$existing_item || ($existing_item['link_path'] != $item['link_path'])) { if ($item['external']) { $item['router_path'] = ''; + // External links are marked as objects, but have no IDs. + $item['object_type'] = 'external_link'; + $item['object_id'] = ''; } else { // Find the router path which will serve this path. $item['parts'] = explode('/', $item['link_path'], MENU_MAX_PARTS); $item['router_path'] = _menu_find_router_path($item['link_path']); + // Find the object type of this item. + _menu_find_object_type($item); } } // If every value in $existing_item is the same in the $item, there is no @@ -2247,6 +2260,8 @@ function menu_link_save(&$item) { 'link_title' => $item['link_title'], 'options' => serialize($item['options']), 'customized' => $item['customized'], + 'object_type' => $item['object_type'], + 'object_id' => $item['object_id'], )) ->condition('mlid', $item['mlid']) ->execute(); @@ -2257,6 +2272,13 @@ function menu_link_save(&$item) { menu_cache_clear($existing_item['menu_name']); } + // Notify modules we have acted on a menu item. + $hook = 'menu_item_insert'; + if ($existing_item) { + $hook = 'menu_item_update'; + } + module_invoke_all($hook, $item); + _menu_clear_page_cache(); } return $item['mlid']; @@ -2334,6 +2356,43 @@ function _menu_find_router_path($link_pa } /** + * Determine the object type and ID for a menu item. + * + * This function takes the menu $item by reference and attempts + * to deduce the object type and ID based on the router and its + * associated loader functions. + * + * @param &$item + * The menu item to be saved. + */ +function _menu_find_object_type(&$item) { + // If there is no router path, we cannot continue. + if (empty($item['router_path'])) { + return; + } + $loader = unserialize(db_query("SELECT load_functions FROM {menu_router} WHERE path = :path", array(':path' => $item['router_path']))->fetchField()); + if (isset($loader) && is_array($loader)) { + $item['object_type'] = str_replace('_load', '', current($loader)); + $parts = explode('/', $item['router_path']); + $data = explode('/', $item['link_path']); + // The router_path uses path/% and the link_path uses path/ID. + // Find the first % array element and match it to the path. + // This should be sufficient in most cases, since items with multiple + // loaders are not normally in the {menu_links} table. + foreach ($parts as $key => $value) { + // There are items in the {menu_links} table with no IDs. + if ($value == '%' && $data[$key] != '%') { + $item['object_id'] = $data[$key]; + } + } + } + // Remove unknown types. + if (empty($item['object_id']) && $item['object_type'] != 'external_link') { + $item['object_type'] = ''; + } +} + +/** * Insert, update or delete an uncustomized menu link related to a module. * * @param $module Index: modules/system/system.install =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.install,v retrieving revision 1.349 diff -u -p -r1.349 system.install --- modules/system/system.install 30 Jun 2009 17:15:09 -0000 1.349 +++ modules/system/system.install 30 Jun 2009 19:07:20 -0000 @@ -1148,12 +1148,27 @@ function system_schema() { 'default' => 0, 'size' => 'small', ), + 'object_type' => array( + 'description' => 'An object type identifier for connecting menu items to external systems.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'object_id' => array( + 'description' => 'A foreign key for objects stored in external systems.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), ), 'indexes' => array( 'path_menu' => array(array('link_path', 128), 'menu_name'), 'menu_plid_expand_child' => array('menu_name', 'plid', 'expanded', 'has_children'), 'menu_parents' => array('menu_name', 'p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8', 'p9'), 'router_path' => array(array('router_path', 128)), + 'object' => array('object_type', 'object_id'), ), 'primary key' => array('mlid'), ); @@ -2223,7 +2238,24 @@ function system_update_7028() { return $ret; } - +/** + * Add foreign object tracking data to {menu_links}. + */ +function system_update_7029() { + $ret = array(); + db_add_field($ret, 'menu_links', 'object_type', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => '')); + db_add_field($ret, 'menu_links', 'object_id', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => '')); + db_add_index($ret, 'menu_links', 'object', array('object_type', 'object_id')); + // Update the {menu_links} table, ignoring items with wildcard link_path entries. + $links = db_query("SELECT mlid, link_path, router_path, object_type, object_id FROM {menu_links} WHERE router_path LIKE '%/\%' AND router_path != link_path")->fetchAll(PDO::FETCH_ASSOC); + foreach ($links as $item) { + _menu_find_object_type($item); + if (isset($item['object_type'])) { + $ret[] = update_sql(sprintf("UPDATE {menu_links} SET object_type = '%s', object_id = '%s' WHERE mlid = %d", $item['object_type'], $item['object_id'], $item['mlid'])); + } + } + return $ret; +} /** * @} End of "defgroup updates-6.x-to-7.x"