Adding a Drupal page as a Tab to the CiviCRM Contacts page.

Over the last month or so I have doing a project which involved installing CiviCRM for my clients contact managements needs. So because of this I have been crawling around CiviCRM quite a bit.

One of my clients requirements to a membership system which is linked to both CiviCRM and Drupals E-Commerce, and gives access to the parts of the system, and some other items such as books, and resources.

I developed this membership system in Drupal, and used the CiviCRM API to update contacts. But when enquiring on contacts the problem was getting easy access to the membership information and history. The obvious answer was to add a tab to the contacts page for the membership information.

The problem with this is that the membership system was developed in Drupal, and not in CiviCRM, so adding the tab, and making the tab look like a CiviCRM page was going to be a challenge.

It was actually easier than I thought. The first step was to add the tab to the contact page.

if ($may_cache || substr($_GET['q'], 0, 20) == 'civicrm/contact/view') {
    require_once 'CRM/Utils/Menu.php';

    $items[] = array(
      'path' => 'civicrm/contact/view/membership',
      'qs' => 'reset=1&cid=%%cid%%',
      'title' => t('Membership'),
      'type' => MENU_CALLBACK,
      'crmType' => CRM_Utils_Menu::LOCAL_TASK,
      'callback' => 'membership_contact',
      'weight' => 6,


CiviCRM's menu system is based upon Drupal (AFAICT) and the menu entry can just be pushed into Drupal with no problems. But because CiviCRM doesn't cache the menu items like Drupal does, you need to execute this code when ever you are on the civicrm/contact/view page. 

First you need to initalize CiviCRM api, and include the menu object so that you can do the CiviCRM things. 

The menu items are just set up as a MENU_CALLBACK since CiviCRM is going to be handling all the tabs. There are a couple of additonal options, qs which passes the contact id and crmType which tell it this item is a local task which adds the tab.


Lastly you need to add this menu item to the CiviCRM menu system so that the tab will be added. Use the CRM_Utils_Menu::add() to do this.

Once you have completed this you should have the new tab on the Contacts page.

Next you need to create the page, and have it call the CiviCRM templates to create the page.

function membership_contact() {

Firstly we initialise the CiviCRM API. 

  require_once 'CRM/Utils/Menu.php';
  require_once 'CRM/Core/BAO/CustomGroup.php';

Because there are custom tabs attached to the contact page, You need to add these to the menu as well. 

  CRM_Utils_Menu::addParam('cid', $_GET['cid']);

As a part of the menu item above there was a replacable field for cid -> %%cid%% so that when the url is displayed it will be replaced with the correct contact id. So this above will add the information so the %%cid%% will be replaced in the menu. 

  $contact = crm_get_contact(array('contact_id' => $_GET['cid']));

  $image = CRM_Contact_BAO_Contact::getImage($contact->contact_type);
  CRM_Utils_System::setTitle( $image . ' ' . $contact->display_name );

We then set the title so that it will display the name of the contact and the image which displays the contact. 

  $startWeight = CRM_Utils_Menu::getMaxWeight('civicrm/contact/view');
  CRM_Core_BAO_CustomGroup::AddMenuTabs($contact->contact_type, 'civicrm/contact/view/cd', $startWeight);

Here the block above will add the custom tabs to the menu so that it looks the same as the other parts of the contact pages. 

  $membership = membership_load($contact->id);

  $template =& CRM_Core_Smarty::singleton();
  $template->assign_by_ref('contact', $contact);
  $template->assign_by_ref('membership', $membership);
  $template->assign('tplFile', 'CRM/Contact/Page/View/Membership.tpl');

  $output.= $template->fetch('CRM/index.tpl');

  return $output;

Lastly we create the CiviCRM Smarty object and set and vars that need to be past to the template. It is important to set the tplFile which is a pointer to the template file for this page. Then call the fetch('CRM/index.tpl') and the page will be rendered for you, and it will look like it is a CiviCRM page that was always meant to be there.



my projects: 


membership system

Hi Gordon,

Nice work!

Would you or your client consider releasing the membership system developed on drupal that integrates with CiviCRM? I'd be happy to donate/contribute/sponsor/pay for it.

Please could you contact me via my email if this is possible?




membership system ...

hey gordon:

 could you publish any details of the membership system / compare it against CiviMember. Would love to get your input and wisdom since you've already built a membership system :)

 The CiviMember spec is at: