menu_example.module 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. <?php
  2. /**
  3. * @file
  4. * Module file for menu_example.
  5. */
  6. /**
  7. * @defgroup menu_example Example: Menu
  8. * @ingroup examples
  9. * @{
  10. * Demonstrates uses of the Menu APIs in Drupal.
  11. *
  12. * The Page Example module also talks about the menu system, as well
  13. * as how to use menu arguments to generate pages.
  14. *
  15. * @see hook_menu()
  16. * @see hook_menu_alter()
  17. * @see hook_menu_link_alter()
  18. * @see page_example
  19. * @see page_example_menu()
  20. */
  21. /**
  22. * Implements hook_menu().
  23. *
  24. * A simple example which defines a page callback and a menu entry.
  25. */
  26. function menu_example_menu() {
  27. // Menu items are defined by placing them in an $items array. The array key
  28. // (in this case 'menu_example') is the path that defines the menu router
  29. // entry, so the page will be accessible from the URL
  30. // example.com/examples/menu_example.
  31. $items['examples/menu_example'] = array(
  32. // We are using the default menu type, so this can be omitted.
  33. // 'type' => MENU_NORMAL_ITEM,
  34. //
  35. // The menu title. Do NOT use t() which is called by default. You can
  36. // override the use of t() by defining a 'title callback'. This is explained
  37. // in the 'menu_example/title_callbacks' example below.
  38. 'title' => 'Menu Example',
  39. // Description (hover flyover for menu link). Does NOT use t(), which is
  40. // called automatically.
  41. 'description' => 'Simplest possible menu type, and the parent menu entry for others',
  42. // Function to be called when this path is accessed.
  43. 'page callback' => '_menu_example_basic_instructions',
  44. // Arguments to the page callback. Here's we'll use them just to provide
  45. // content for our page.
  46. 'page arguments' => array(t('This page is displayed by the simplest (and base) menu example. Note that the title of the page is the same as the link title. You can also <a href="!link">visit a similar page with no menu link</a>. Also, note that there is a hook_menu_alter() example that has changed the path of one of the menu items.', array('!link' => url('examples/menu_example/path_only')))),
  47. // If the page is meant to be accessible to all users, you can set 'access
  48. // callback' to TRUE. This bypasses all access checks. For an explanation on
  49. // how to use the permissions system to restrict access for certain users,
  50. // see the example 'examples/menu_example/permissioned/controlled' below.
  51. 'access callback' => TRUE,
  52. // If the page callback is located in another file, specify it here and
  53. // that file will be automatically loaded when needed.
  54. // 'file' => 'menu_example.module',
  55. //
  56. // We can choose which menu gets the link. The default is 'navigation'.
  57. // 'menu_name' => 'navigation',
  58. //
  59. // Show the menu link as expanded.
  60. 'expanded' => TRUE,
  61. );
  62. // Show a menu link in a menu other than the default "Navigation" menu.
  63. // The menu must already exist.
  64. $items['examples/menu_example_alternate_menu'] = array(
  65. 'title' => 'Menu Example: Menu in alternate menu',
  66. // Machine name of the menu in which the link should appear.
  67. 'menu_name' => 'main-menu',
  68. 'page callback' => '_menu_example_menu_page',
  69. 'page arguments' => array(t('This will be in the Main menu instead of the default Navigation menu')),
  70. 'access callback' => TRUE,
  71. );
  72. // A menu entry with simple permissions using user_access().
  73. //
  74. // First, provide a courtesy menu item that mentions the existence of the
  75. // permissioned item.
  76. $items['examples/menu_example/permissioned'] = array(
  77. 'title' => 'Permissioned Example',
  78. 'page callback' => '_menu_example_menu_page',
  79. 'page arguments' => array(t('A menu item that requires the "access protected menu example" permission is at <a href="!link">examples/menu_example/permissioned/controlled</a>', array('!link' => url('examples/menu_example/permissioned/controlled')))),
  80. 'access callback' => TRUE,
  81. 'expanded' => TRUE,
  82. );
  83. // Now provide the actual permissioned menu item.
  84. $items['examples/menu_example/permissioned/controlled'] = array(
  85. // The title - do NOT use t() as t() is called automatically.
  86. 'title' => 'Permissioned Menu Item',
  87. 'description' => 'This menu entry will not appear and the page will not be accessible without the "access protected menu example" permission.',
  88. 'page callback' => '_menu_example_menu_page',
  89. 'page arguments' => array(t('This menu entry will not show and the page will not be accessible without the "access protected menu example" permission.')),
  90. // For a permissioned menu entry, we provide an access callback which
  91. // determines whether the current user should have access. The default is
  92. // user_access(), which we'll use in this case. Since it's the default,
  93. // we don't even have to enter it.
  94. // 'access callback' => 'user_access',
  95. //
  96. // The 'access arguments' are passed to the 'access callback' to help it
  97. // do its job. In the case of user_access(), we need to pass a permission
  98. // as the first argument.
  99. 'access arguments' => array('access protected menu example'),
  100. // The optional weight element tells how to order the submenu items.
  101. // Higher weights are "heavier", dropping to the bottom of the menu.
  102. 'weight' => 10,
  103. );
  104. /*
  105. * We will define our own "access callback" function. We'll use
  106. * menu_example_custom_access() rather than the default user_access().
  107. *
  108. * The function takes a "role" of the user as an argument.
  109. */
  110. $items['examples/menu_example/custom_access'] = array(
  111. 'title' => 'Custom Access Example',
  112. 'page callback' => '_menu_example_menu_page',
  113. 'page arguments' => array(t('A menu item that requires the user to posess a role of "authenticated user" is at <a href="!link">examples/menu_example/custom_access/page</a>', array('!link' => url('examples/menu_example/custom_access/page')))),
  114. 'access callback' => TRUE,
  115. 'expanded' => TRUE,
  116. 'weight' => -5,
  117. );
  118. $items['examples/menu_example/custom_access/page'] = array(
  119. 'title' => 'Custom Access Menu Item',
  120. 'description' => 'This menu entry will not show and the page will not be accessible without the user being an "authenticated user".',
  121. 'page callback' => '_menu_example_menu_page',
  122. 'page arguments' => array(t('This menu entry will not be visible and access will result in a 403 error unless the user has the "authenticated user" role. This is accomplished with a custom access callback.')),
  123. 'access callback' => 'menu_example_custom_access',
  124. 'access arguments' => array('authenticated user'),
  125. );
  126. // A menu router entry with no menu link. This could be used any time we
  127. // don't want the user to see a link in the menu. Otherwise, it's the same
  128. // as the "simplest" entry above. MENU_CALLBACK is used for all menu items
  129. // which don't need a visible menu link, including services and other pages
  130. // that may be linked to but are not intended to be accessed directly.
  131. //
  132. // First, provide a courtesy link in the menu so people can find this.
  133. $items['examples/menu_example/path_only'] = array(
  134. 'title' => 'MENU_CALLBACK example',
  135. 'page callback' => '_menu_example_menu_page',
  136. 'page arguments' => array(t('A menu entry with no menu link (MENU_CALLBACK) is at <a href="!link">!link</a>', array('!link' => url('examples/menu_example/path_only/callback')))),
  137. 'access callback' => TRUE,
  138. 'weight' => 20,
  139. );
  140. $items['examples/menu_example/path_only/callback'] = array(
  141. // A type of MENU_CALLBACK means leave the path completely out of the menu
  142. // links.
  143. 'type' => MENU_CALLBACK,
  144. // The title is still used for the page title, even though it's not used
  145. // for the menu link text, since there's no menu link.
  146. 'title' => 'Callback Only',
  147. 'page callback' => '_menu_example_menu_page',
  148. 'page arguments' => array(t('The menu entry for this page is of type MENU_CALLBACK, so it provides only a path but not a link in the menu links, but it is the same in every other way to the simplest example.')),
  149. 'access callback' => TRUE,
  150. );
  151. // A menu entry with tabs.
  152. // For tabs we need at least 3 things:
  153. // 1) A parent MENU_NORMAL_ITEM menu item (examples/menu_example/tabs in this
  154. // example.)
  155. // 2) A primary tab (the one that is active when we land on the base menu).
  156. // This tab is of type MENU_DEFAULT_LOCAL_TASK.
  157. // 3) Some other menu entries for the other tabs, of type MENU_LOCAL_TASK.
  158. $items['examples/menu_example/tabs'] = array(
  159. // 'type' => MENU_NORMAL_ITEM, // Not necessary since this is the default.
  160. 'title' => 'Tabs',
  161. 'description' => 'Shows how to create primary and secondary tabs',
  162. 'page callback' => '_menu_example_menu_page',
  163. 'page arguments' => array(t('This is the "tabs" menu entry.')),
  164. 'access callback' => TRUE,
  165. 'weight' => 30,
  166. );
  167. // For the default local task, we need very little configuration, as the
  168. // callback and other conditions are handled by the parent callback.
  169. $items['examples/menu_example/tabs/default'] = array(
  170. 'type' => MENU_DEFAULT_LOCAL_TASK,
  171. 'title' => 'Default primary tab',
  172. 'weight' => 1,
  173. );
  174. // Now add the rest of the tab entries.
  175. foreach (array(t('second') => 2, t('third') => 3, t('fourth') => 4) as $tabname => $weight) {
  176. $items["examples/menu_example/tabs/$tabname"] = array(
  177. 'type' => MENU_LOCAL_TASK,
  178. 'title' => $tabname,
  179. 'page callback' => '_menu_example_menu_page',
  180. 'page arguments' => array(t('This is the tab "@tabname" in the "basic tabs" example', array('@tabname' => $tabname))),
  181. 'access callback' => TRUE,
  182. // The weight property overrides the default alphabetic ordering of menu
  183. // entries, allowing us to get our tabs in the order we want.
  184. 'weight' => $weight,
  185. );
  186. }
  187. // Finally, we'll add secondary tabs to the default tab of the tabs entry.
  188. //
  189. // The default local task needs very little information.
  190. $items['examples/menu_example/tabs/default/first'] = array(
  191. 'type' => MENU_DEFAULT_LOCAL_TASK,
  192. 'title' => 'Default secondary tab',
  193. // The additional page callback and related items are handled by the
  194. // parent menu item.
  195. );
  196. foreach (array(t('second'), t('third')) as $tabname) {
  197. $items["examples/menu_example/tabs/default/$tabname"] = array(
  198. 'type' => MENU_LOCAL_TASK,
  199. 'title' => $tabname,
  200. 'page callback' => '_menu_example_menu_page',
  201. 'page arguments' => array(t('This is the secondary tab "@tabname" in the "basic tabs" example "default" tab', array('@tabname' => $tabname))),
  202. 'access callback' => TRUE,
  203. );
  204. }
  205. // All the portions of the URL after the base menu are passed to the page
  206. // callback as separate arguments, and can be captured by the page callback
  207. // in its argument list. Our _menu_example_menu_page() function captures
  208. // arguments in its function signature and can output them.
  209. $items['examples/menu_example/use_url_arguments'] = array(
  210. 'title' => 'Extra Arguments',
  211. 'description' => 'The page callback can use the arguments provided after the path used as key',
  212. 'page callback' => '_menu_example_menu_page',
  213. 'page arguments' => array(t('This page demonstrates using arguments in the path (portions of the path after "menu_example/url_arguments". For example, access it with <a href="!link1">!link1</a> or <a href="!link2">!link2</a>).', array('!link1' => url('examples/menu_example/use_url_arguments/one/two'), '!link2' => url('examples/menu_example/use_url_arguments/firstarg/secondarg')))),
  214. 'access callback' => TRUE,
  215. 'weight' => 40,
  216. );
  217. // The menu title can be dynamically created by using the 'title callback'
  218. // which by default is t(). Here we provide a title callback which adjusts
  219. // the menu title based on the current user's username.
  220. $items['examples/menu_example/title_callbacks'] = array(
  221. 'title callback' => '_menu_example_simple_title_callback',
  222. 'title arguments' => array(t('Dynamic title: username=')),
  223. 'description' => 'The title of this menu item is dynamically generated',
  224. 'page callback' => '_menu_example_menu_page',
  225. 'page arguments' => array(t('The menu title is dynamically changed by the title callback')),
  226. 'access callback' => TRUE,
  227. 'weight' => 50,
  228. );
  229. // Sometimes we need to capture a specific argument within the menu path,
  230. // as with the menu entry
  231. // 'example/menu_example/placeholder_argument/3333/display', where we need to
  232. // capture the "3333". In that case, we use a placeholder in the path provided
  233. // in the menu entry. The (odd) way this is done is by using
  234. // array(numeric_position_value) as the value for 'page arguments'. The
  235. // numeric_position_value is the zero-based index of the portion of the URL
  236. // which should be passed to the 'page callback'.
  237. //
  238. // First we provide a courtesy link with information on how to access
  239. // an item with a placeholder.
  240. $items['examples/menu_example/placeholder_argument'] = array(
  241. 'title' => 'Placeholder Arguments',
  242. 'page callback' => '_menu_example_menu_page',
  243. 'page arguments' => array(t('Demonstrate placeholders by visiting <a href="!link">examples/menu_example/placeholder_argument/3343/display</a>', array('!link' => url('examples/menu_example/placeholder_argument/3343/display')))),
  244. 'access callback' => TRUE,
  245. 'weight' => 60,
  246. );
  247. // Now the actual entry.
  248. $items['examples/menu_example/placeholder_argument/%/display'] = array(
  249. 'title' => 'Placeholder Arguments',
  250. 'page callback' => '_menu_example_menu_page',
  251. // Pass the value of '%', which is zero-based argument 3, to the
  252. // 'page callback'. So if the URL is
  253. // 'examples/menu_example/placeholder_argument/333/display' then the value
  254. // 333 will be passed into the 'page callback'.
  255. 'page arguments' => array(3),
  256. 'access callback' => TRUE,
  257. );
  258. // Drupal provides magic placeholder processing as well, so if the placeholder
  259. // is '%menu_example_arg_optional', the function
  260. // menu_example_arg_optional_load($arg) will be called to translate the path
  261. // argument to a more substantial object. $arg will be the value of the
  262. // placeholder. Then the return value of menu_example_id_load($arg) will be
  263. // passed to the 'page callback'.
  264. // In addition, if (in this case) menu_example_arg_optional_to_arg() exists,
  265. // then a menu link can be created using the results of that function as a
  266. // default for %menu_example_arg_optional.
  267. $items['examples/menu_example/default_arg/%menu_example_arg_optional'] = array(
  268. 'title' => 'Processed Placeholder Arguments',
  269. 'page callback' => '_menu_example_menu_page',
  270. // Argument 3 (4rd arg) is the one we want.
  271. 'page arguments' => array(3),
  272. 'access callback' => TRUE,
  273. 'weight' => 70,
  274. );
  275. $items['examples/menu_example/menu_original_path'] = array(
  276. 'title' => 'Menu path that will be altered by hook_menu_alter()',
  277. 'page callback' => '_menu_example_menu_page',
  278. 'page arguments' => array(t('This menu item was created strictly to allow the hook_menu_alter() function to have something to operate on. hook_menu defined the path as examples/menu_example/menu_original_path. The hook_menu_alter() changes it to examples/menu_example/menu_altered_path. You can try navigating to both paths and see what happens!')),
  279. 'access callback' => TRUE,
  280. 'weight' => 80,
  281. );
  282. return $items;
  283. }
  284. /**
  285. * Page callback for the simplest introduction menu entry.
  286. *
  287. * @param string $content
  288. * Some content passed in.
  289. */
  290. function _menu_example_basic_instructions($content = NULL) {
  291. $base_content = t(
  292. 'This is the base page of the Menu Example. There are a number of examples
  293. here, from the most basic (like this one) to extravagant mappings of loaded
  294. placeholder arguments. Enjoy!');
  295. return '<div>' . $base_content . '</div><br /><div>' . $content . '</div>';
  296. }
  297. /**
  298. * Page callback for use with most of the menu entries.
  299. *
  300. * The arguments it receives determine what it outputs.
  301. *
  302. * @param string $content
  303. * The base content to output.
  304. * @param string $arg1
  305. * First additional argument from the path used to access the menu
  306. * @param string $arg2
  307. * Second additional argument.
  308. */
  309. function _menu_example_menu_page($content = NULL, $arg1 = NULL, $arg2 = NULL) {
  310. $output = '<div>' . $content . '</div>';
  311. if (!empty($arg1)) {
  312. $output .= '<div>' . t('Argument 1=%arg', array('%arg' => $arg1)) . '</div>';
  313. }
  314. if (!empty($arg2)) {
  315. $output .= '<div>' . t('Argument 2=%arg', array('%arg' => $arg2)) . '</div>';
  316. }
  317. return $output;
  318. }
  319. /**
  320. * Implements hook_permission().
  321. *
  322. * Provides a demonstration access string.
  323. */
  324. function menu_example_permission() {
  325. return array(
  326. 'access protected menu example' => array(
  327. 'title' => t('Access the protected menu example'),
  328. ),
  329. );
  330. }
  331. /**
  332. * Determine whether the current user has the role specified.
  333. *
  334. * @param string $role_name
  335. * The role required for access
  336. *
  337. * @return bool
  338. * True if the acting user has the role specified.
  339. */
  340. function menu_example_custom_access($role_name) {
  341. $access_granted = in_array($role_name, $GLOBALS['user']->roles);
  342. return $access_granted;
  343. }
  344. /**
  345. * Utility function to provide mappings from integers to some strings.
  346. *
  347. * This would normally be some database lookup to get an object or array from
  348. * a key.
  349. *
  350. * @param int $id
  351. * The integer key.
  352. *
  353. * @return string
  354. * The string to which the integer key mapped, or NULL if it did not map.
  355. */
  356. function _menu_example_mappings($id) {
  357. $mapped_value = NULL;
  358. static $mappings = array(
  359. 1 => 'one',
  360. 2 => 'two',
  361. 3 => 'three',
  362. 99 => 'jackpot! default',
  363. );
  364. if (isset($mappings[$id])) {
  365. $mapped_value = $mappings[$id];
  366. }
  367. return $mapped_value;
  368. }
  369. /**
  370. * The special _load function to load menu_example.
  371. *
  372. * Given an integer $id, load the string that should be associated with it.
  373. * Normally this load function would return an array or object with more
  374. * information.
  375. *
  376. * @param int $id
  377. * The integer to load.
  378. *
  379. * @return string
  380. * A string loaded from the integer.
  381. */
  382. function menu_example_id_load($id) {
  383. // Just map a magic value here. Normally this would load some more complex
  384. // object from the database or other context.
  385. $mapped_value = _menu_example_mappings($id);
  386. if (!empty($mapped_value)) {
  387. return t('Loaded value was %loaded', array('%loaded' => $mapped_value));
  388. }
  389. else {
  390. return t('Sorry, the id %id was not found to be loaded', array('%id' => $id));
  391. }
  392. }
  393. /**
  394. * Implements hook_menu_alter().
  395. *
  396. * Changes the path 'examples/menu_example/menu_original_path' to
  397. * 'examples/menu_example/menu_altered_path'.
  398. * Changes the title callback of the 'user/UID' menu item.
  399. *
  400. * Change the path 'examples/menu_example/menu_original_path' to
  401. * 'examples/menu_example/menu_altered_path'. This change will prevent the
  402. * page from appearing at the original path (since the item is being unset).
  403. * You will need to go to examples/menu_example/menu_altered_path manually to
  404. * see the page.
  405. *
  406. * Remember that hook_menu_alter() only runs at menu_rebuild() time, not every
  407. * time the page is built, so this typically happens only at cache clear time.
  408. *
  409. * The $items argument is the complete list of menu router items ready to be
  410. * written to the menu_router table.
  411. */
  412. function menu_example_menu_alter(&$items) {
  413. if (!empty($items['examples/menu_example/menu_original_path'])) {
  414. $items['examples/menu_example/menu_altered_path'] = $items['examples/menu_example/menu_original_path'];
  415. $items['examples/menu_example/menu_altered_path']['title'] = 'Menu item altered by hook_menu_alter()';
  416. unset($items['examples/menu_example/menu_original_path']);
  417. }
  418. // Here we will change the title callback to our own function, changing the
  419. // 'user' link from the traditional to always being "username's account".
  420. if (!empty($items['user/%user'])) {
  421. $items['user/%user']['title callback'] = 'menu_example_user_page_title';
  422. }
  423. }
  424. /**
  425. * Title callback to rewrite the '/user' menu link.
  426. *
  427. * @param string $base_string
  428. * string to be prepended to current user's name.
  429. */
  430. function _menu_example_simple_title_callback($base_string) {
  431. global $user;
  432. $username = !empty($user->name) ? $user->name : t('anonymous');
  433. return $base_string . ' ' . $username;
  434. }
  435. /**
  436. * Title callback to rename the title dynamically, based on user_page_title().
  437. *
  438. * @param object $account
  439. * User account related to the visited page.
  440. */
  441. function menu_example_user_page_title($account) {
  442. return is_object($account) ? t("@name's account", array('@name' => format_username($account))) : '';
  443. }
  444. /**
  445. * Implements hook_menu_link_alter().
  446. *
  447. * This code will get the chance to alter a menu link when it is being saved
  448. * in the menu interface at admin/build/menu. Whatever we do here overrides
  449. * anything the user/administrator might have been trying to do.
  450. */
  451. function menu_example_menu_link_alter(&$item, $menu) {
  452. // Force the link title to remain 'Clear Cache' no matter what the admin
  453. // does with the web interface.
  454. if ($item['link_path'] == 'devel/cache/clear') {
  455. $item['link_title'] = 'Clear Cache';
  456. };
  457. }
  458. /**
  459. * Loads an item based on its $id.
  460. *
  461. * In this case we're just creating a more extensive string. In a real example
  462. * we would load or create some type of object.
  463. *
  464. * @param int $id
  465. * Id of the item.
  466. */
  467. function menu_example_arg_optional_load($id) {
  468. $mapped_value = _menu_example_mappings($id);
  469. if (!empty($mapped_value)) {
  470. return t('Loaded value was %loaded', array('%loaded' => $mapped_value));
  471. }
  472. else {
  473. return t('Sorry, the id %id was not found to be loaded', array('%id' => $id));
  474. }
  475. }
  476. /**
  477. * Utility function to provide default argument for wildcard.
  478. *
  479. * A to_arg() function is used to provide a default for the arg in the
  480. * wildcard. The purpose is to provide a menu link that will function if no
  481. * argument is given. For example, in the case of the menu item
  482. * 'examples/menu_example/default_arg/%menu_example_arg_optional' the third argument
  483. * is required, and the menu system cannot make a menu link using this path
  484. * since it contains a placeholder. However, when the to_arg() function is
  485. * provided, the menu system will create a menu link pointing to the path
  486. * which would be created with the to_arg() function filling in the
  487. * %menu_example_arg_optional.
  488. *
  489. * @param string $arg
  490. * The arg (URL fragment) to be tested.
  491. */
  492. function menu_example_arg_optional_to_arg($arg) {
  493. // If our argument is not provided, give a default of 99.
  494. return (empty($arg) || $arg == '%') ? 99 : $arg;
  495. }
  496. /**
  497. * @} End of "defgroup menu_example".
  498. */