This page's contents are old and now superceded by the "Settings Reference" page.

Introduction

Sometimes you want to provide a page for settings for your module. Also modules should not have custom field id's hardcoded if possible since it makes it awkward to move the code between test installations. Instead you can provide a UI form with these simple steps.

Prerequisites

Steps

  1. Make a .xml file for the path to your settings page.

    <?xml version="1.0" encoding="iso-8859-1" ?>
    <menu>
      <item>
        <path>civicrm/admin/setting/mysettings</path>
        <page_callback>CRM_Admin_Form_Setting_MySettingsForm</page_callback>
        <title>My Settings</title>
        <!-- Change this as desired <adminGroup>CiviCase</adminGroup> -->
        <access_arguments>administer CiviCRM</access_arguments>
      </item>
    </menu>
    
  2. Make a module to implement hook_civicrm_xmlMenu. After you enable the module you will need to visit <your_site>/civicrm/menu/rebuild&reset=1 to pick up this addition.

    function mymodule_civicrm_xmlMenu( &$files ) {
        // If not using drupal or you've put it somewhere else then change this to the path to your xml file.
        $files[] = drupal_get_path('module', 'mymodule') . "/mymodule.menu.xml";
    }
    
  3. At this point we will be creating a PHP class and Smarty template file similar to the ones found in core under civicrm/CRM.  In order for CiviCRM to pick up your new files, you'll either have to configure the directory settings at Administer > Configure > Global Settings > Directories or you'll need to implement hook_civicrm_config to programatically tell CiviCRM where to find your PHP classes and Smarty templates.  Here is an example implementation of hook_civicrm_config that tells CiviCRM to look in the module folder for the PHP classes and Smarty templates:

    /**
     * Implements hook_civicrm_config()
     * Add's our template directory to the Smarty path so that it finds our
     * templates first.  Also adds our PHP directory to the PHP include path so that
     * it finds our PHP classes and other files first on require or include.
     */
    function mymodule_civicrm_config(&$config) {
      $template =& CRM_Core_Smarty::singleton();
    
      $root = dirname(__FILE__) . DIRECTORY_SEPARATOR;
      $template_dir = $root . 'templates';
    
      // Add our template directory to the Smarty templates path
      if (is_array($template->template_dir)) {
        array_unshift($template->template_dir, $template_dir);
      }
      else {
        $template->template_dir = array($template_dir, $template->template_dir);
      }
    
      // Also fix php include path
      $include_path = $root . 'php' . DIRECTORY_SEPARATOR . PATH_SEPARATOR . get_include_path();
      set_include_path($include_path);
    }
    

    This assumes that your module has a directory structure like so:

    /mymodule
      /php
        /CRM
          /Admin
            /Form
              /Setting
                MySettingsForm.php
      /templates
        /CRM
          /Admin
            /Form
              /Setting
                MySettingsForm.tpl
      mymodule.module
      mymodule.info
    

    This method has the advantage of not requiring your module users to set the global directories path, which allows multiple modules to package their CiviCRM PHP classes and Smarty templates in different directories.

  4. Make a custom form with something like this in it (change class to match where you are putting it). Of course you are not limited to custom fields in your select dropdowns - this is an example.

    <?php
    
    require_once 'CRM/Admin/Form/Setting.php';
    require_once 'CRM/Core/BAO/CustomField.php';
    
    class CRM_Admin_Form_Setting_MySettingsForm extends CRM_Admin_Form_Setting
    {
        public function buildQuickForm( ) {
            CRM_Utils_System::setTitle(ts('Settings - My Custom Settings'));
    
    
            $customFields = CRM_Core_BAO_CustomField::getFields();
            $cf = array();
            foreach ($customFields as $k => $v) {
                $cf[$k] = $v['label'];
            }
    
            $this->addElement('select',
                              'mysettings_hcp_specialty',
                              ts('Custom field for HCP specialty'),
                              array('' => ts('- select -')) + $cf
                              );
    
            $this->addElement('text',
                              'mysettings_recipient',
                              ts('Email address to receive module notices')
                              );
    
            parent::buildQuickForm();
        }
    }
    
  5. Make a corresponding template with something like this in it.

    <div class="crm-block crm-form-block crm-mysettings-form-block">
      <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location="top"}</div>
    
    <fieldset>
        <table class="form-layout">
            <tr class="crm-mysettings-form-block-specialty">
              <td class="label">{$form.mysettings_hcp_specialty.label}</td>
              <td>
                {$form.mysettings_hcp_specialty.html}
              </td>
            </tr>
             <tr class="crm-mysettings-form-block-recipient">
              <td class="label">{$form.mysettings_recipient.label}</td>
              <td>
                {$form.mysettings_recipient.html}
              </td>
            </tr>
       </table>
    
        <div class="crm-submit-buttons">{include file="CRM/common/formButtons.tpl" location="bottom"}</div>
    </fieldset>
    
    </div>
    
  6. Use it in your module / custom form like so

    $config = CRM_Core_Config::singleton();
    $hcp_specialty_custom_field_id = $config->mysettings_hcp_specialty;
    $email_recipient = $config->mysettings_recepient;
    

These steps will store the settings in config_backend. If you don't want it there you can override postProcess in your class above and store them somewhere else, and then of course you will need to retrieve them differently too.