View on GitHub

com.joineryhq.profcond

CiviCRM Profile Conditionals

This CiviCRM extension adds support for dynamic in-page behaviors based on changes to form fields in CiviCRM forms, including showing/hiding form elements and setting field values.

Current support includes the following forms:

Patch welcome for other forms.

Installation

Please see the CiviCRM Documentation on “How to install an extension”. Because this extension is not yet “available in-app”, you can skip the section on “Installing extensions that are available in-app” and use one of the methods explained in the sections that follow it.

Configuration

This extension has no browser-based configuration form within CiviCRM. Configuration is by PHP arrays in code within the civicrm.settings.php file.

You need to add in your civicrm.settings.php a new config variable

global $civicrm_setting;
$civicrm_setting['com.joineryhq.profcond']['com.joineryhq.profcond'] = array(
  'event' => array(
    81 => array (
      'onload' => array(
        'selectors' => array(
          '#priceset' => array(
            'display' => 'hide',
          ),
        ),
        'profiles' => array (
          '1' => array(
            'display' => 'hide',
          ),
        ),
      ),
      '1_swim' => array(
        'conditions' => array(
          'all_of' => array(
            array(
              'id' => 'custom_261',
              'op' => 'value_is',
              'value' => '1', //"swim"
            ),
          )
        ),
        'states' => array(
          'pass' => array (
            'profiles' => array (
              '108' => array(
                'display' => 'show',
              ),
            ),
            'selectors' => array(
              '#priceset' => array(
                'display' => 'show',
              ),
              'div.price-set-row.ticket_selection-row1' => array(
                'display' => 'show',
              ),
              'div.price-set-row.ticket_selection-row1 input[type="radio"]' => array(
                'is_price_change' => TRUE,
                'properties' => array(
                  'checked' => TRUE,
                ),
              ),
              'div.price-set-row.ticket_selection-row2' => array(
                'display' => 'hide',
              ),
            ),
          ),
          'fail' => array(
            'profiles' => array (
              '1' => array(
                'display' => 'hide',
              ),
              '108' => array(
                'display' => 'hide',
              ),
            ),
            'selectors' => array(
              'div.price-set-row.ticket_selection-row1 input[type="radio"]' => array(
                'is_price_change' => TRUE,
                'properties' => array(
                  'checked' => FALSE,
                ),
              ),
            ),
          ),
        ),
      ),
    ),
  ),
  'contribution' => array(
  ),
);

This nested array format is fragile but explicit, expressing any number of rules, and for each rule, any number of conditions (either AND or OR), and any number of field states to manifest when the condition tests as either true or false.

$civicrm_setting['com.joineryhq.profcond']['com.joineryhq.profcond'] = array(
  '[entity-type]' => array(
    [entity-id] => array (
      '[rule-name]' => array(
        'limit' => array(
          'formId' => array(
            'pattern' => [limit-regex-pattern],
            'flags' => [limit-regex-flags],
          ),
        ),
        'conditions' => array(
          '[condition-type]' => array(
            array(
              '[subject-identifier-type]' => '[subject-identifier]',
              'op' => '[operator]',
              'value' => '[test-value]',
              'negate' => [negate-value],
            ),
          )
        ),
        'states' => array(
          '[condition-success]' => array (
            '[state-type]' => array (
              '[state-selector]' => array(
                '[state-property]' => '[state-property-value]',
              ),
            ),
          ),
        ),
      ),
    ),
  ),
);

[entity-type]

Must be ‘event’, ‘contribution’, or ‘priceset’ to indicate that this section applies to event registration pages, contribution pages, or one of those using a specific price set. In future, other values may be supported.

[entity-id]

Must be one these:

[rule-name]

“limit”

Optional array defining a regular expression that will limit the forms on which this rule is applied. Because [entity-type] and [entity-id] already facilitate limiting rules to specific events and contribution pages, this “limit” array is only expected to be useful in the case of multi-participant event registrations, in which the primary participant form has the formId “Register”, and additional participant forms have the formId “Participant_1”, “Participant_2”, etc. The “limit” definition provides a way to specify that certain rules should only apply on the “Register” formId, or one or more of the additional participant forms.

[limit-regex-pattern]

String. A pattern to be used in `new RegExp(‘[limit-regex-pattern]’, ‘[limit-regex-flags]’). If the regular expression does not match the formId value (see “limit”), this rule will not be used at all on the page.

[limit-regex-flags]

String. A pattern to be used in `new RegExp(‘[limit-regex-pattern]’, ‘[limit-regex-flags]’). Optional, and probably not needed in any case; but see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Advanced_searching_with_flags for more on regular expression flags.

[condition-type]

A string indicating how the conditions should be joined. One of:

Combining any_of with all_of has not been tested and may produce unpredictable results. It’s recommended to use one or the other. Nesting of conditions is not supported. Thus, under ‘conditions’, you should have one array element, keyed to either any_of or all_of, containing any number of conditions.

[subject-identifier-type]

One of:

[subject-identifier]

One of these, depending on the value of [subject-identifier-type]:

[operator]

The type of comparison to be performed for this field. One of:

[test-value]

The value to be compared in this condition.

[negate-value]

Boolean TRUE or FALSE. If TRUE, the condition evaluation (a boolean) will be negated. Default is FALSE (no negation). For example, the following condition will pass if the value of #custom_261 is NOT ‘1’:

  array(
    'id' => 'custom_261',
    'op' => 'value_is',
    'value' => '1',
    'negate' => TRUE,
  ),

[condition-success]

One of:

[state-type]

One of:

[state-selector]

Depending on the value of [state-type], one of the following:

[state-property]

The attribute of the given element to be changed. One of:

See [state-property-value] for more information.

[state-property-value]

Depending on the value of [state-property], the appropriate value for that property. For these values of [state-property], the possible [state-property] values are:

Variables

ProfileConditionals defines the following JavaScript variables in CRM.vars.profcond.

To examine these variables in the context of a live browser session, run this bit of code in your browser’s JavaScript console:

console.log(CRM.vars.profcond);

More examples

A more involved example is contained in CONFIG_EXAMPLE.md.

Debug mode

When CiviCRM’s “Enable Debugging” setting is enabled (Administer > System Settings > Debugging and Error Handling), this extension will attempt to explain the ligical processing of all rules by printing informative messages in the browser’s JavaScript console.

Developer hooks

This extension provides hook_civicrm_profcond_alterConfig(), which can be implemented like so:

/**
 * Implements hook_civicrm_profcond_alterConfig().
 * @link https://twomice.github.io/com.joineryhq.profcond/#developer-hooks
 */
function myextension_civicrm_profcond_alterConfig(&$config, $pageType, $entityId) {
  // $config contains the full value of $civicrm_setting['com.joineryhq.profcond']['com.joineryhq.profcond'];
  //    change as needed.
  // $pageType will be either 'contribution' or 'event'.
  // $entityId will be the current event ID or contribution page ID.

  // If we're on an event registration page, and my custom decider says to do so,
  // create a rule for showing/hiding profile id=1.
  if ($pageType == 'event') {
    if (_myextensionWantsToAlterProfCondForEvent($entityId)) {
      $config['event'][$entityId]['myextension_1'] = [
        'conditions' => [
          'all_of' => [
            [
              'id' => 'myExtensionCustomField',
              'op' => 'value_is_one_of',
              'value' => [1, 2, 3, 4],
            ],
          ],
        ],
        'states' => [
          'pass' => [
            'profiles' => [
              1 => [
                'display' => 'show',
              ],
            ],
          ],
          'fail' => [
            'profiles' => [
              1 => [
                'display' => 'hide',
              ],
            ],
          ],
        ],
      ];
    }
  }
}

FAQs

  1. What about wildcards? I want to apply the same rules to several different events.
    Use the string all as the entity-id; see notes on [entity-id], above.

    Alternately, you could do something like this for a more specific set of events (or the equivalent, for contribution pages):

    $eventsConfig = array(
      // whatever
    );
    
    $civicrm_setting['com.joineryhq.profcond']['com.joineryhq.profcond'] = array(
      'event' => array(
        '81' => $eventsConfig,
        '82' => $eventsConfig,
        '83' => $eventsConfig,
      ),
    );
    
  2. I want to conditionally display and require a field. How can I do that?
    Configure the field as required within the profile. Configure this extension to show/hide the field according to your own rules. If this extension hides a required field, it will also ensure that the field is not required when hidden.

Support

screenshot

Joinery provides services for CiviCRM including custom extension development, training, data migrations, and more. We aim to keep this extension in good working order, and will do our best to respond appropriately to issues reported on its github issue queue. In addition, if you require urgent or highly customized improvements to this extension, we may suggest conducting a fee-based project under our standard commercial terms. In any case, the place to start is the github issue queue. Let us hear what you need and we’ll be glad to help however we can.

And, if you need help with any other aspect of CiviCRM – from hosting to custom development to strategic consultation and more – please contact us directly via https://joineryhq.com