features.menu.inc 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. <?php
  2. /**
  3. * Implements hook_features_api().
  4. */
  5. function menu_features_api() {
  6. return array(
  7. 'menu_custom' => array(
  8. 'name' => t('Menus'),
  9. 'default_hook' => 'menu_default_menu_custom',
  10. 'feature_source' => TRUE,
  11. 'default_file' => FEATURES_DEFAULTS_INCLUDED,
  12. ),
  13. 'menu_links' => array(
  14. 'name' => t('Menu links'),
  15. 'default_hook' => 'menu_default_menu_links',
  16. 'feature_source' => TRUE,
  17. 'default_file' => FEATURES_DEFAULTS_INCLUDED,
  18. ),
  19. // DEPRECATED
  20. 'menu' => array(
  21. 'name' => t('Menu items'),
  22. 'default_hook' => 'menu_default_items',
  23. 'default_file' => FEATURES_DEFAULTS_INCLUDED,
  24. 'feature_source' => FALSE,
  25. ),
  26. );
  27. }
  28. /**
  29. * Implements hook_features_export().
  30. * DEPRECATED: This implementation simply migrates deprecated `menu` items
  31. * to the `menu_links` type.
  32. */
  33. function menu_features_export($data, &$export, $module_name = '') {
  34. $pipe = array();
  35. foreach ($data as $path) {
  36. $pipe['menu_links'][] = "features:{$path}";
  37. }
  38. return $pipe;
  39. }
  40. /**
  41. * Implements hook_features_export_options().
  42. */
  43. function menu_custom_features_export_options() {
  44. $options = array();
  45. $result = db_query("SELECT * FROM {menu_custom} ORDER BY title", array(), array('fetch' => PDO::FETCH_ASSOC));
  46. foreach ($result as $menu) {
  47. $options[$menu['menu_name']] = $menu['title'];
  48. }
  49. return $options;
  50. }
  51. /**
  52. * Implements hook_features_export().
  53. */
  54. function menu_custom_features_export($data, &$export, $module_name = '') {
  55. // Default hooks are provided by the feature module so we need to add
  56. // it as a dependency.
  57. $export['dependencies']['features'] = 'features';
  58. $export['dependencies']['menu'] = 'menu';
  59. // Collect a menu to module map
  60. $pipe = array();
  61. $map = features_get_default_map('menu_custom', 'menu_name');
  62. foreach ($data as $menu_name) {
  63. // If this menu is provided by a different module, add it as a dependency.
  64. if (isset($map[$menu_name]) && $map[$menu_name] != $module_name) {
  65. $export['dependencies'][$map[$menu_name]] = $map[$menu_name];
  66. }
  67. else {
  68. $export['features']['menu_custom'][$menu_name] = $menu_name;
  69. }
  70. }
  71. return $pipe;
  72. }
  73. /**
  74. * Implements hook_features_export_render()
  75. */
  76. function menu_custom_features_export_render($module, $data) {
  77. $code = array();
  78. $code[] = ' $menus = array();';
  79. $code[] = '';
  80. $translatables = array();
  81. foreach ($data as $menu_name) {
  82. $row = db_select('menu_custom')
  83. ->fields('menu_custom')
  84. ->condition('menu_name', $menu_name)
  85. ->execute()
  86. ->fetchAssoc();
  87. if ($row) {
  88. $export = features_var_export($row, ' ');
  89. $code[] = " // Exported menu: {$menu_name}.";
  90. $code[] = " \$menus['{$menu_name}'] = {$export};";
  91. $translatables[] = $row['title'];
  92. $translatables[] = $row['description'];
  93. }
  94. }
  95. if (!empty($translatables)) {
  96. $code[] = features_translatables_export($translatables, ' ');
  97. }
  98. $code[] = '';
  99. $code[] = ' return $menus;';
  100. $code = implode("\n", $code);
  101. return array('menu_default_menu_custom' => $code);
  102. }
  103. /**
  104. * Implements hook_features_revert().
  105. */
  106. function menu_custom_features_revert($module) {
  107. menu_custom_features_rebuild($module);
  108. }
  109. /**
  110. * Implements hook_features_rebuild().
  111. */
  112. function menu_custom_features_rebuild($module) {
  113. if ($defaults = features_get_default('menu_custom', $module)) {
  114. foreach ($defaults as $menu) {
  115. menu_save($menu);
  116. }
  117. }
  118. }
  119. /**
  120. * Implements hook_features_export_options().
  121. */
  122. function menu_links_features_export_options() {
  123. global $menu_admin;
  124. // Need to set this to TRUE in order to get menu links that the
  125. // current user may not have access to (i.e. user/login)
  126. $menu_admin = TRUE;
  127. $menu_links = menu_parent_options(menu_get_menus(), array('mlid' => 0));
  128. $options = array();
  129. foreach ($menu_links as $key => $name) {
  130. list($menu_name, $mlid) = explode(':', $key, 2);
  131. if ($mlid != 0) {
  132. $link = menu_link_load($mlid);
  133. $identifier = menu_links_features_identifier($link);
  134. $options[$identifier] = "{$menu_name}: {$name}";
  135. }
  136. }
  137. $menu_admin = FALSE;
  138. return $options;
  139. }
  140. /**
  141. * Callback for generating the menu link exportable identifier.
  142. */
  143. function menu_links_features_identifier($link) {
  144. return isset($link['menu_name'], $link['link_path']) ? "{$link['menu_name']}:{$link['link_path']}" : FALSE;
  145. }
  146. /**
  147. * Implements hook_features_export().
  148. */
  149. function menu_links_features_export($data, &$export, $module_name = '') {
  150. // Default hooks are provided by the feature module so we need to add
  151. // it as a dependency.
  152. $export['dependencies']['features'] = 'features';
  153. $export['dependencies']['menu'] = 'menu';
  154. // Collect a link to module map
  155. $pipe = array();
  156. $map = features_get_default_map('menu_links', 'menu_links_features_identifier');
  157. foreach ($data as $identifier) {
  158. if ($link = features_menu_link_load($identifier)) {
  159. // If this link is provided by a different module, add it as a dependency.
  160. if (isset($map[$identifier]) && $map[$identifier] != $module_name) {
  161. $export['dependencies'][$map[$identifier]] = $map[$identifier];
  162. }
  163. else {
  164. $export['features']['menu_links'][$identifier] = $identifier;
  165. }
  166. // For now, exclude a variety of common menus from automatic export.
  167. // They may still be explicitly included in a Feature if the builder
  168. // chooses to do so.
  169. if (!in_array($link['menu_name'], array('features', 'primary-links', 'secondary-links', 'navigation', 'admin', 'devel'))) {
  170. $pipe['menu_custom'][] = $link['menu_name'];
  171. }
  172. }
  173. }
  174. return $pipe;
  175. }
  176. /**
  177. * Implements hook_features_export_render()
  178. */
  179. function menu_links_features_export_render($module, $data) {
  180. $code = array();
  181. $code[] = ' $menu_links = array();';
  182. $code[] = '';
  183. $translatables = array();
  184. foreach ($data as $identifier) {
  185. if ($link = features_menu_link_load($identifier)) {
  186. // Replace plid with a parent path.
  187. if (!empty($link['plid']) && $parent = menu_link_load($link['plid'])) {
  188. $link['parent_path'] = $parent['link_path'];
  189. }
  190. unset($link['plid']);
  191. unset($link['mlid']);
  192. $code[] = " // Exported menu link: {$identifier}";
  193. $code[] = " \$menu_links['{$identifier}'] = ". features_var_export($link, ' ') .";";
  194. $translatables[] = $link['link_title'];
  195. }
  196. }
  197. if (!empty($translatables)) {
  198. $code[] = features_translatables_export($translatables, ' ');
  199. }
  200. $code[] = '';
  201. $code[] = ' return $menu_links;';
  202. $code = implode("\n", $code);
  203. return array('menu_default_menu_links' => $code);
  204. }
  205. /**
  206. * Implements hook_features_revert().
  207. */
  208. function menu_links_features_revert($module) {
  209. menu_links_features_rebuild($module);
  210. }
  211. /**
  212. * Implements hook_features_rebuild().
  213. */
  214. function menu_links_features_rebuild($module) {
  215. if ($menu_links = features_get_default('menu_links', $module)) {
  216. menu_links_features_rebuild_ordered($menu_links);
  217. }
  218. }
  219. /**
  220. * Generate a depth tree of all menu links.
  221. */
  222. function menu_links_features_rebuild_ordered($menu_links, $reset = FALSE) {
  223. static $ordered;
  224. static $all_links;
  225. if (!isset($ordered) || $reset) {
  226. $ordered = array();
  227. $unordered = features_get_default('menu_links');
  228. // Order all links by depth.
  229. if ($unordered) {
  230. do {
  231. $current = count($unordered);
  232. foreach ($unordered as $key => $link) {
  233. $identifier = menu_links_features_identifier($link);
  234. $parent = isset($link['parent_path']) ? "{$link['menu_name']}:{$link['parent_path']}" : '';
  235. if (empty($parent)) {
  236. $ordered[$identifier] = 0;
  237. $all_links[$identifier] = $link;
  238. unset($unordered[$key]);
  239. }
  240. elseif (isset($ordered[$parent])) {
  241. $ordered[$identifier] = $ordered[$parent] + 1;
  242. $all_links[$identifier] = $link;
  243. unset($unordered[$key]);
  244. }
  245. }
  246. } while (count($unordered) < $current);
  247. }
  248. asort($ordered);
  249. }
  250. // Ensure any default menu items that do not exist are created.
  251. foreach (array_keys($ordered) as $identifier) {
  252. $link = $all_links[$identifier];
  253. $existing = features_menu_link_load($identifier);
  254. if (!$existing || in_array($link, $menu_links)) {
  255. // Retrieve the mlid if this is an existing item.
  256. if ($existing) {
  257. $link['mlid'] = $existing['mlid'];
  258. }
  259. // Retrieve the plid for a parent link.
  260. if (!empty($link['parent_path']) && $parent = features_menu_link_load("{$link['menu_name']}:{$link['parent_path']}")) {
  261. $link['plid'] = $parent['mlid'];
  262. }
  263. else {
  264. $link['plid'] = 0;
  265. }
  266. menu_link_save($link);
  267. }
  268. }
  269. }
  270. /**
  271. * Load a menu link by its menu_name:link_path identifier.
  272. */
  273. function features_menu_link_load($identifier) {
  274. list($menu_name, $link_path) = explode(':', $identifier, 2);
  275. $link = db_select('menu_links')
  276. ->fields('menu_links', array('menu_name', 'mlid', 'plid', 'link_path', 'router_path', 'link_title', 'options', 'module', 'hidden', 'external', 'has_children', 'expanded', 'weight'))
  277. ->condition('menu_name', $menu_name)
  278. ->condition('link_path', $link_path)
  279. ->execute()
  280. ->fetchAssoc();
  281. if ($link) {
  282. $link['options'] = unserialize($link['options']);
  283. return $link;
  284. }
  285. return FALSE;
  286. }