diff --git a/includes/menu.inc b/includes/menu.inc
index 060c354c95b9619a300c939b6584e379019943de..ad6090b25287224b66bc6c5e62dd64e0a187075b 100644
--- a/includes/menu.inc
+++ b/includes/menu.inc
@@ -2140,6 +2140,9 @@ function _menu_delete_item($item, $force = FALSE) {
     }
     db_delete('menu_links')->condition('mlid', $item['mlid'])->execute();
 
+    // Notify modules we have deleted the item.
+    module_invoke_all('menu_link_delete', $item);
+
     // Update the has_children status of the parent.
     _menu_update_parental_status($item);
     menu_cache_clear($item['menu_name']);
@@ -2334,7 +2337,13 @@ function menu_link_save(&$item) {
     if ($existing_item && $menu_name != $existing_item['menu_name']) {
       menu_cache_clear($existing_item['menu_name']);
     }
-
+    // Notify modules we have acted on a menu item.
+    $hook = 'menu_link_insert';
+    if ($existing_item) {
+      $hook = 'menu_link_update';
+    }
+    module_invoke_all($hook, $item);
+    // Now clear the cache.
     _menu_clear_page_cache();
   }
   return $item['mlid'];
@@ -2437,21 +2446,11 @@ function menu_link_maintain($module, $op, $link_path, $link_title) {
       return menu_link_save($menu_link);
       break;
     case 'update':
-      db_update('menu_links')
-        ->fields(array('link_title' => $link_title))
-        ->condition('link_path', $link_path)
-        ->condition('customized', 0)
-        ->condition('module', $module)
-        ->execute();
-      $result = db_select('menu_links')
-        ->fields('menu_links', array('menu_name'))
-        ->condition('link_path', $link_path)
-        ->condition('customized', 0)
-        ->condition('module', $module)
-        ->groupBy('menu_name')
-        ->execute()->fetchCol();
-      foreach ($result as $menu_name) {
-        menu_cache_clear($menu_name);
+      $result = db_query("SELECT * FROM {menu_links} WHERE link_path = :link_path AND module = :module AND customized = 0", array(':link_path' => $link_path, ':module' => $module))->fetchAll(PDO::FETCH_ASSOC);
+      foreach ($result as $link) {
+        $link['link_title'] = $link_title;
+        $link['options'] = unserialize($link['options']);
+        menu_link_save($link);
       }
       break;
     case 'delete':
diff --git a/modules/menu/menu.api.php b/modules/menu/menu.api.php
index bf37caae3071a37767ac62be2f1ff64a29cb7189..99fcb8e209dac6c7de9850621257a8f00aca3e6d 100644
--- a/modules/menu/menu.api.php
+++ b/modules/menu/menu.api.php
@@ -147,6 +147,78 @@ function hook_translated_menu_link_alter(&$item, $map) {
   }
 }
 
+ /**
+ * Inform modules that a menu link has been created.
+ *
+ * This hook is used to notify module that menu items have been
+ * created. Contributed modules may use the information to perform
+ * actions based on the information entered into the menu system.
+ *
+ * @param $link
+ *   The $link record saved into the {menu_links} table.
+ * @return
+ *   None.
+ *
+ * @see hook_menu_link_update()
+ * @see hook_menu_link_delete()
+ */
+function hook_menu_link_insert($link) {
+  // In our sample case, we track menu items as editing sections
+  // of the site. These are stored in our table as 'disabled' items.
+  $record['mlid'] = $link['mlid'];
+  $record['menu_name'] = $link['menu_name'];
+  $record['status'] = 0;
+  drupal_write_record('menu_example', $record);
+}
+
+/**
+ * Inform modules that a menu link has been updated.
+ *
+ * This hook is used to notify module that menu items have been
+ * updated. Contributed modules may use the information to perform
+ * actions based on the information entered into the menu system.
+ *
+ * @param $link
+ *   The $link record saved into the {menu_links} table.
+ * @return
+ *   None.
+ *
+ * @see hook_menu_link_insert()
+ * @see hook_menu_link_delete()
+ */
+function hook_menu_link_update($link) {
+  // If the parent menu has changed, update our record.
+  $menu_name = db_result(db_query("SELECT mlid, menu_name, status FROM {menu_example} WHERE mlid = :mlid", array(':mlid' => $link['mlid'])));
+  if ($menu_name != $link['menu_name']) {
+    db_update('menu_example')
+      ->fields(array('menu_name' => $link['menu_name']))
+      ->condition('mlid', $link['mlid'])
+      ->execute();
+  }
+}
+
+/**
+ * Inform modules that a menu link has been deleted.
+ *
+ * This hook is used to notify module that menu items have been
+ * deleted. Contributed modules may use the information to perform
+ * actions based on the information entered into the menu system.
+ *
+ * @param $link
+ *   The $link record saved into the {menu_links} table.
+ * @return
+ *   None.
+ *
+ * @see hook_menu_link_insert()
+ * @see hook_menu_link_update()
+ */
+function hook_menu_link_delete($link) {
+  // Delete the record from our table.
+  db_delete('menu_example')
+    ->condition('mlid', $link['mlid'])
+    ->execute();
+}
+
 /**
  * @} End of "addtogroup hooks".
  */
diff --git a/modules/simpletest/tests/menu.test b/modules/simpletest/tests/menu.test
index 889d877d3b219fd973c6710c097f7142e733e973..26443f74e08e588a3530608b965eade6cd0ad099 100644
--- a/modules/simpletest/tests/menu.test
+++ b/modules/simpletest/tests/menu.test
@@ -132,6 +132,21 @@ class MenuIncTestCase extends DrupalWebTestCase {
     $this->assertEqual($compare_item, $item, t('Modified menu item is equal to newly retrieved menu item.'), 'menu');
   }
 
+  /**
+   * Test menu maintainance hooks.
+   */
+  function testMenuItemHooks() {
+    // Create an item.
+    menu_link_maintain('menu_test', 'insert', 'menu_test_maintain/4', 'Menu link #4');
+    $this->assertEqual(menu_test_static_variable(), 'insert', t('hook_menu_link_insert() fired correctly'));
+    // Update the item.
+    menu_link_maintain('menu_test', 'update', 'menu_test_maintain/4', 'Menu link updated');
+    $this->assertEqual(menu_test_static_variable(), 'update', t('hook_menu_link_update() fired correctly'));
+    // Delete the item.
+    menu_link_maintain('menu_test', 'delete', 'menu_test_maintain/4', '');
+    $this->assertEqual(menu_test_static_variable(), 'delete', t('hook_menu_link_delete() fired correctly'));
+  }
+
 }
 
 /**
diff --git a/modules/simpletest/tests/menu_test.module b/modules/simpletest/tests/menu_test.module
index 0428fd76352c4622446a9ec3d35dc16f96eb74e1..15ecea71a1d11cac2a43473f4a365eaf8812e66c 100644
--- a/modules/simpletest/tests/menu_test.module
+++ b/modules/simpletest/tests/menu_test.module
@@ -73,3 +73,49 @@ function menu_test_menu_name($new_name = '') {
   }
   return $name;
 }
+
+/**
+ * Implement hook_menu_link_insert().
+ *
+ * @return
+ *  A random string.
+ */
+function menu_test_menu_link_insert($item) {
+  menu_test_static_variable('insert');
+}
+
+/**
+ * Implement hook_menu_link_update().
+ *
+ * @return
+ *  A random string.
+ */
+function menu_test_menu_link_update($item) {
+  menu_test_static_variable('update');
+}
+
+/**
+ * Implement hook_menu_link_delete().
+ *
+ * @return
+ *  A random string.
+ */
+function menu_test_menu_link_delete($item) {
+  menu_test_static_variable('delete');
+}
+
+/**
+ * Static function for testing hook results.
+ *
+ * @param $value
+ *   The value to set or NULL to return the current value.
+ * @return
+ *   A text string for comparison to test assertions.
+ */
+function menu_test_static_variable($value = NULL) {
+  static $variable;
+  if (!empty($value)) {
+    $variable = $value;
+  }
+  return $variable;
+}