# How to add a new tab (e.g. like "Dashboard" or "Reports")

**URL:** <https://farmos.discourse.group/t/how-to-add-a-new-tab-e-g-like-dashboard-or-reports/570>\
**Category:** Development\
**Created:** [December 19, 2020, 10:38pm UTC](https://farmos.discourse.group/t/how-to-add-a-new-tab-e-g-like-dashboard-or-reports/570 "2020-12-19T22:38:25Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![braught](https://yyz2.discourse-cdn.com/free1/user_avatar/farmos.discourse.group/braught/32/294_2.png) [@braught](https://farmos.discourse.group/u/braught)\
**Post date:** [December 19, 2020, 10:38pm UTC](https://farmos.discourse.group/t/how-to-add-a-new-tab-e-g-like-dashboard-or-reports/570/1 "2020-12-19T22:38:25Z")

</div>

I was watching the development videos ([Video: Developing farmOS](https://farmos.discourse.group/t/video-developing-farmos/299)) and it was mentioned briefly at the end of the first one that it is possible to have a page appear as a tab (i.e. alongside “Dashboard”, “Quick forms” and “Reports”). Is there a short example to work from or some documentation that would help me get started with adding such a tab for a custom module? Any pointers or nudges in the right direction would be appreciated. Thanks.

---

<div class="post-metadata">

**Author:** ![braught](https://yyz2.discourse-cdn.com/free1/user_avatar/farmos.discourse.group/braught/32/294_2.png) [@braught](https://farmos.discourse.group/u/braught)\
**Post date:** [December 20, 2020, 8:03pm UTC](https://farmos.discourse.group/t/how-to-add-a-new-tab-e-g-like-dashboard-or-reports/570/2 "2020-12-20T20:03:02Z")

</div>

I think I figured this out… If it is of interest to anyone later the key insights were:

- Use the `farm/reports` and `farm/quick` modules as a guide.
- In `hook_menu` use: `$items['farm/tabs'] = ...` where `tabs` is the name that appears in the URL.
- Changes to the `hook_menu` method appear to require a module off/on cycle to take effect.
- Changes to the `page callback` method are live and do not require a module off/on cycle.

Here is a minimum implementation of a new tab with 2 sub-tabs that can appear in the `www/sites/all/modules/` directory.

```auto
<?php
/**
 * @file
 * Tab Test module.
 */

 function tab_test_permission() {
   return array(
     'view tab test' => array(
       'title' => t('View tab_test'),
     ),
     'configure tab test' => array(
       'title' => t('Configure tab_test'),
     ),
   );
 }

 function tab_test_farm_access_perms($role) {
   $perms = array();

   // Load the list of farm roles.
   $roles = farm_access_roles();

   // If this role has 'edit' access, allow them to see these tabs.
   if (!empty($roles[$role]['access']['edit'])) {
     $perms[] = 'view tab test';
   }

   // If this role has 'config' access, allow them to also configure tab test.
   if (!empty($roles[$role]['access']['config'])) {
     $perms[] = 'configure tab test';
   }

   return $perms;
 }

 /**
 * Implements hook_menu().
 */
function tab_test_menu() {
  $items = array();

  $items['farm/tabs'] = array(
    'title' => 'Tabs',
    'page callback' => 'tab_test_view',
    'access arguments' => array('view tab test'),
    'type' => MENU_LOCAL_TASK,
  );
  $items['farm/tabs/info'] = array(
    'title' => 'Info',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -100,
  );
  $items['farm/tabs/info2'] = array(
    'title' => 'Info2',
    'type' => MENU_LOCAL_TASK,
    'page callback' => 'tab_test_view2',
    'access arguments' => array('view tab test'),
    'weight' => 100,
  );

  return $items;
};

// Changes to the callback functions below are live.  
// The function for the callback set above requires a module off/on cycle.

function tab_test_view() {
  return t('Select a tab from the menu above.');
};

function tab_test_view2() {
  return t('Some different other stuff.');
};

```

---

<div class="post-metadata">

**Author:** ![mstenta](https://yyz2.discourse-cdn.com/free1/user_avatar/farmos.discourse.group/mstenta/32/4_2.png) [@mstenta](https://farmos.discourse.group/u/mstenta)\
**Post date:** [December 21, 2020, 10:44am UTC](https://farmos.discourse.group/t/how-to-add-a-new-tab-e-g-like-dashboard-or-reports/570/3 "2020-12-21T10:44:41Z")

</div>

Hi @braught - welcome to the farmOS forum!

Sounds like you figured it out. Well done! 🙂

For future reference, the documentation for Drupal 7’s `hook_menu()` is available here: [https://api.drupal.org/api/drupal/modules!system!system.api.php/function/hook\_menu/7.x](https://api.drupal.org/api/drupal/modules%21system%21system.api.php/function/hook_menu/7.x)

> Changes to the `hook_menu` method appear to require a module off/on cycle to take effect.

The menu router item information is only read when Drupal has reason to believe changes have been made to it, and are then stored in the `{menu_router}` table for faster lookups. Enabling modules triggers this, as you noticed. You can also simply submit the modules form (/admin/module) without turning any off/on to achieve the same affect. Alternatively, if you have [Drush](https://www.drush.org/) installed, you can run `drush cc menu`.

Note: this all changes in Drupal 9 / farmOS 2.x (in active development), but the concepts are mostly the same. This might help when it comes time to upgrade: [https://www.lullabot.com/articles/what-happened-to-hook\_menu-in-drupal-8](https://www.lullabot.com/articles/what-happened-to-hook_menu-in-drupal-8)

Hope that helps! Thanks for sharing your lessons with the community!
