You are here

civicrm_entity_discount_field.module in CiviCRM Entity 7.2

Provide CiviCRM Entity Discount Field Type. Provides a widget for selecting custom discount options

Should be considered a template for creating custom interfaces to discounts used by the CiviCRM Price Set Field

File

modules/civicrm_entity_discount/civicrm_entity_discount_field.module
View source
<?php

/**
 * @file
 * Provide CiviCRM Entity Discount Field Type.  Provides a widget for selecting custom discount options
 *
 * Should be considered a template for creating custom interfaces to discounts used by the CiviCRM Price Set Field
 */

/**
 * Implements hook_menu().
 * code example taken from https://www.drupal.org/project/multiple_fields_remove_button module
 */
function civicrm_entity_discount_field_menu() {
  $items = array();
  $items['civicrm_entity_discount_field_multiple_fields_remove_button/ajax'] = array(
    'title' => 'Remove item callback',
    'page callback' => 'civicrm_entity_discount_field_multiple_fields_remove_button_js',
    'delivery callback' => 'ajax_deliver',
    'access callback' => TRUE,
    'theme callback' => 'ajax_base_page_theme',
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Page callback for civicrm_entity_profile_multiple_fields_remove_button/ajax path.
 * code example taken from https://www.drupal.org/project/multiple_fields_remove_button module
 */
function civicrm_entity_discount_field_multiple_fields_remove_button_js() {
  if (isset($_POST['ajax_html_ids'])) {
    unset($_POST['ajax_html_ids']);
  }
  list($form, $form_state) = ajax_get_form();
  drupal_process_form($form['#form_id'], $form, $form_state);

  // Get the information on what we're removing.
  $button = $form_state['triggering_element'];

  // Go two levels up in the form, to the whole widget.
  $element = drupal_array_get_nested_value($form, array_slice($button['#array_parents'], 0, -3));

  // Now send back the proper AJAX command to replace it.
  $return = array(
    '#type' => 'ajax',
    '#commands' => array(
      ajax_command_replace('#' . $element['#id'], drupal_render($element)),
    ),
  );

  // Because we're doing this ourselves, messages aren't automatic. We have
  // to add them.
  $messages = theme('status_messages');
  if ($messages) {
    $return['#commands'][] = ajax_command_prepend('#' . $element['#id'], $messages);
  }
  return $return;
}

/**
 * Implements hook_field_info().
 *
 * @return array
 */
function civicrm_entity_discount_field_field_info() {
  return array(
    'civicrm_entity_discount_field' => array(
      'label' => t('CiviCRM Entity Discount Settings'),
      'description' => t('This field provides a widget for selecting discount options for the Price Set registration form.'),
      'settings' => array(),
      'instance_settings' => array(),
      'default_widget' => 'civicrm_entity_discount_field_default_widget',
      'default_formatter' => 'civicrm_entity_discount_field_default_formatter',
    ),
  );
}

/**
 * Implements hook_form_FORMID_alter().
 *
 * form id : field_ui_field_edit_form
 *
 * Alter Field Settings form to set cardinality to 1 and disable the select widget
 *
 * @param $form
 * @param $form_state
 */
function civicrm_entity_discount_field_form_field_ui_field_edit_form_alter(&$form, &$form_state) {
  if ($form['#field']['type'] == 'civicrm_entity_discount_field') {

    // field settings mods

    //$form['field']['cardinality']['#default_value'] = 1;

    //$form['field']['cardinality']['#disabled'] = TRUE;
  }
}

/**
 * Implements hook_form_FORMID_alter().
 *
 * form id : field_ui_field_overview_form
 *
 * Only allow creation of civicrm_entity_discount_fields to civicrm_event entity type
 *
 * @param $form
 * @param $form_state
 */
function civicrm_entity_discount_field_form_field_ui_field_overview_form_alter(&$form, &$form_state) {
  if ($form['#entity_type'] != 'civicrm_event') {
    unset($form['fields']['_add_new_field']['type']['#options']['civicrm_entity_discount_field']);
    foreach ($form['fields']['_add_existing_field']['field_name']['#options'] as $field_name => $description) {
      if (strpos($description, 'CiviCRM Entity Discount Settings') !== FALSE) {
        unset($form['fields']['_add_existing_field']['field_name']['#options'][$field_name]);
      }
    }
  }
}

/**
 * Implements hook_field_widget_info().
 */
function civicrm_entity_discount_field_field_widget_info() {
  return array(
    'civicrm_entity_discount_field_default_widget' => array(
      'label' => t('Default'),
      'field types' => array(
        'civicrm_entity_discount_field',
      ),
      'settings' => array(),
      'behaviors' => array(
        'multiple values' => FIELD_BEHAVIOR_DEFAULT,
        'default value' => FIELD_BEHAVIOR_DEFAULT,
      ),
    ),
  );
}

/**
 * Implements hook_field_is_empty().
 *
 * @param $item
 * @param $field
 * @return bool
 */
function civicrm_entity_discount_field_field_is_empty($item, $field) {
  if (empty($item['price_field_id'])) {
    return TRUE;
  }
  return FALSE;
}

/**
 * Implements hook_field_widget_form().
 */
function civicrm_entity_discount_field_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  $orig_element = $element;
  switch ($instance['widget']['type']) {
    case 'civicrm_entity_discount_field_default_widget':
      $price_field_options = array(
        '' => '- None -',
      );
      if (!empty($form['#entity']->id)) {
        $event_id = $form['#entity']->id;
        $query = db_select('civicrm_price_set_entity', 'pce');
        $price_set_id = $query
          ->fields('pce', array(
          'price_set_id',
        ))
          ->condition('entity_table', 'civicrm_event')
          ->condition('entity_id', $event_id)
          ->execute()
          ->fetchField();
        $price_field_options = array(
          '' => '- None -',
        );
        if (!empty($price_set_id)) {
          $price_field_query = new EntityFieldQuery();
          $price_field_result = $price_field_query
            ->entityCondition('entity_type', 'civicrm_price_field')
            ->propertyCondition('price_set_id', $price_set_id)
            ->propertyOrderBy('weight', 'ASC')
            ->execute();
          if (!empty($price_field_result['civicrm_price_field'])) {
            foreach ($price_field_result['civicrm_price_field'] as $id => $pf_id) {
              $pf = entity_load_single('civicrm_price_field', $id);
              $price_field_options[$id] = $pf->label;
            }
          }
        }
      }
      $pf_id_widget = array();
      $pf_id_widget = $orig_element + array(
        '#type' => 'select',
        '#default_value' => isset($items[$delta]['price_field_id']) ? $items[$delta]['price_field_id'] : NULL,
        // '#disabled' => TRUE,
        '#options' => $price_field_options,
      );
      $pf_id_widget['#title'] = t('Price Field');
      $pf_id_widget['#weight'] = 0;
      $discount_widget = array();
      $discount_widget = $orig_element + array(
        '#type' => 'textfield',
        '#default_value' => isset($items[$delta]['discount']) ? $items[$delta]['discount'] : NULL,
      );
      $discount_widget['#title'] = t('Discount');
      $discount_widget['#weight'] = 1;
      $discount_type_widget = $orig_element + array(
        '#type' => 'select',
        '#default_value' => isset($items[$delta]['discount_type']) ? $items[$delta]['discount_type'] : '',
        '#options' => array(
          '' => '- None - ',
          'percentage' => 'Percentage',
          'fixed' => 'Fixed Amount',
        ),
      );
      $discount_type_widget['#title'] = t('Discount Type');
      $discount_type_widget['#weight'] = 2;
      $role_options = array(
        -1 => '- Any -',
      );
      $role_options += user_roles();
      $role_widget = $orig_element + array(
        '#type' => 'select',
        '#default_value' => isset($items[$delta]['rid']) ? $items[$delta]['rid'] : -1,
        '#options' => $role_options,
      );
      $role_widget['#title'] = t('Role to apply discount for');
      $role_widget['#weight'] = 3;
      $element['price_field_id'] = $pf_id_widget;
      $element['discount'] = $discount_widget;
      $element['discount_type'] = $discount_type_widget;
      $element['rid'] = $role_widget;
      break;
  }

  // add remove button for fields with unlimited field values
  // code example taken from https://www.drupal.org/project/multiple_fields_remove_button module
  $field_parents = isset($element['#field_parents']) ? $element['#field_parents'] : array();
  $field_name = isset($element['#field_name']) ? $element['#field_name'] : NULL;
  $parents = array_merge($field_parents, array(
    $field_name,
    $langcode,
    $delta,
  ));
  if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED) {
    $element['remove_button'] = array(
      '#delta' => $delta,
      '#name' => implode('_', $parents) . '_remove_button',
      '#type' => 'submit',
      '#value' => t('Remove'),
      '#validate' => array(),
      '#attributes' => array(
        'class' => array(
          'multiple-fields-remove-button',
        ),
      ),
      '#submit' => array(
        '_civicrm_entity_discount_field_remove_button_submit_handler',
      ),
      '#limit_validation_errors' => array(),
      '#ajax' => array(
        'path' => 'civicrm_entity_discount_field_multiple_fields_remove_button/ajax',
        'effect' => 'fade',
      ),
      '#weight' => 1000,
    );
  }
  return $element;
}

/**
 * Implements hook_field_validate().
 */
function civicrm_entity_discount_field_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
  if ($field['type'] == 'civicrm_entity_discount_field') {
    foreach ($items as $delta => $item) {
      if (!empty($item['price_field_id'])) {
        if (empty($item['discount'])) {
          $errors[$field['field_name']][$langcode][$delta][] = array(
            'error' => 'civicrm_entity_discount_field_discount_required',
            'message' => t('Discount must have value if you select a price field'),
          );
        }
        if (empty($item['discount_type'])) {
          $errors[$field['field_name']][$langcode][$delta][] = array(
            'error' => 'civicrm_entity_discount_field_discount_type_required',
            'message' => t('Discount type must have value if you select a price field'),
          );
        }
      }
      if (!empty($item['discount']) && !is_numeric($item['discount'])) {
        $errors[$field['field_name']][$langcode][$delta][] = array(
          'error' => 'civicrm_entity_discount_field_discount_non_numeric',
          'message' => t('Discount must be a numeric value'),
        );
      }
    }
  }
}

/**
 * Implements hook_field_widget_error().
 */
function civicrm_entity_discount_field_field_widget_error($element, $error, $form, &$form_state) {
  switch ($error['error']) {
    case 'civicrm_entity_discount_field_discount_non_numeric':
      form_error($element['discount'], $error['message']);
    case 'civicrm_entity_discount_field_discount_required':
      form_error($element['discount'], $error['message']);
    case 'civicrm_entity_discount_field_discount_type_required':
      form_error($element['discount'], $error['message']);
  }
}

/**
 * Submit callback to remove an item from the field UI multiple wrapper.
 *
 * code example taken from https://www.drupal.org/project/multiple_fields_remove_button module
 *
 * When a remove button is submitted, we need to find the item that it
 * referenced and delete it. Since field UI has the deltas as a straight
 * unbroken array key, we have to renumber everything down. Since we do this
 * we *also* need to move all the deltas around in the $form_state['values']
 * and $form_state['input'] so that user changed values follow. This is a bit
 * of a complicated process.
 */
function _civicrm_entity_discount_field_remove_button_submit_handler($form, &$form_state) {
  $button = $form_state['triggering_element'];
  $delta = $button['#delta'];
  $inpt = 'input';

  // Where in the form we'll find the parent element.
  $address = array_slice($button['#array_parents'], 0, -2);

  // Go one level up in the form, to the widgets container.
  $parent_element = drupal_array_get_nested_value($form, $address);
  $field_name = $parent_element['#field_name'];
  $langcode = $parent_element['#language'];
  $parents = $parent_element['#field_parents'];
  $field_state = field_form_get_state($parents, $field_name, $langcode, $form_state);

  // Go ahead and renumber everything from our delta to the last
  // item down one. This will overwrite the item being removed.
  for ($i = $delta; $i <= $field_state['items_count']; $i++) {
    $old_element_address = array_merge($address, array(
      $i + 1,
    ));
    $new_element_address = array_merge($address, array(
      $i,
    ));
    $moving_element = drupal_array_get_nested_value($form, $old_element_address);
    $moving_element_value = drupal_array_get_nested_value($form_state['values'], $old_element_address);
    $moving_element_input = drupal_array_get_nested_value($form_state[$inpt], $old_element_address);

    // Tell the element where it's being moved to.
    $moving_element['#parents'] = $new_element_address;

    // Move the element around.
    form_set_value($moving_element, $moving_element_value, $form_state);
    drupal_array_set_nested_value($form_state[$inpt], $moving_element['#parents'], $moving_element_input);

    // Move the entity in our saved state.
    if (isset($field_state['entity'][$i + 1])) {
      $field_state['entity'][$i] = $field_state['entity'][$i + 1];
    }
    else {
      unset($field_state['entity'][$i]);
    }
  }

  // Replace the deleted entity with an empty one. This helps to ensure that
  // trying to add a new entity won't ressurect a deleted entity
  // from thev trash bin.
  // $count = count($field_state['entity']);
  // Then remove the last item. But we must not go negative.
  if ($field_state['items_count'] > 0) {
    $field_state['items_count']--;
  }

  // Fix the weights. Field UI lets the weights be in a range of
  // (-1 * item_count) to (item_count). This means that when we remove one,
  // the range shrinks; weights outside of that range then get set to
  // the first item in the select by the browser, floating them to the top.
  // We use a brute force method because we lost weights on both ends
  // and if the user has moved things around, we have to cascade because
  // if I have items weight weights 3 and 4, and I change 4 to 3 but leave
  // the 3, the order of the two 3s now is undefined and may not match what
  // the user had selected.
  $input = drupal_array_get_nested_value($form_state[$inpt], $address);

  // Sort by weight.
  uasort($input, '_field_sort_items_helper');

  // Reweight everything in the correct order.
  $weight = -1 * $field_state['items_count'];
  foreach ($input as $key => $item) {
    if ($item) {
      $input[$key]['_weight'] = $weight++;
    }
  }
  drupal_array_set_nested_value($form_state[$inpt], $address, $input);
  field_form_set_state($parents, $field_name, $langcode, $form_state, $field_state);
  $form_state['rebuild'] = TRUE;
}

/**
 * Implements hook_field_formatter_info().
 */
function civicrm_entity_discount_field_field_formatter_info() {
  return array(
    'civicrm_entity_discount_field_default_formatter' => array(
      // Machine name of the formatter
      'label' => t('Default'),
      'field types' => array(
        'civicrm_entity_discount_field',
      ),
      'settings' => array(),
    ),
  );
}

/**
 * Implements hook_field_formatter_settings_form().
 *
 * @param $field
 * @param $instance
 * @param $view_mode
 * @param $form
 * @param $form_state
 * @return array
 */
function civicrm_entity_discount_field_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
  if ($field['type'] == 'civicrm_entity_discount_field') {
    $display = $instance['display'][$view_mode];
    $settings = $display['settings'];
    $element = array();

    //return $element;
  }
}

/**
 * Implements hook_field_formatter_settings_summary().
 *
 * @param $field
 * @param $instance
 * @param $view_mode
 * @return string
 */
function civicrm_entity_discount_field_field_formatter_settings_summary($field, $instance, $view_mode) {
  if ($field['type'] == 'civicrm_entity_discount_field') {
    $display = $instance['display'][$view_mode];
    $settings = $display['settings'];
    $summary = '';
    if ($display['type'] == 'civicrm_entity_discount_field_default_formatter') {

      /*$summary = t('Link to group: @data', array(
          '@data' => !empty($settings['link_to_groups']) ? 'Yes' : 'No',
        ));*/
    }

    //return $summary;
  }
}

/**
 * Implements hook_field_formatter_view().
 */
function civicrm_entity_discount_field_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  $element = array();
  $settings = $display['settings'];
  switch ($display['type']) {
    case 'civicrm_entity_discount_field_default_formatter':
      foreach ($items as $delta => $item) {
        $markup = '';
        $element[$delta] = array(
          '#markup' => $markup,
        );
      }
      break;
  }
  return $element;
}

/**
 * Implements hook_civicrm_entity_price_set_field_registration_form_price_set_data_alter().
 *
 * @param $price_set_data
 * @param $context
 */
function civicrm_entity_discount_field_civicrm_entity_price_set_field_registration_form_price_set_data_alter(&$price_set_data, $context) {
  if ($context['entity_type'] == 'civicrm_event' && $context['participant_count'] === 0) {
    $discount_field_key = '';
    foreach ($context['entity'] as $key => $value) {
      if (strpos($key, 'field_') === 0) {
        $field = field_info_field($key);
        if ($field['type'] == 'civicrm_entity_discount_field') {
          $discount_field_key = $key;
          break;
        }
      }
    }
    if (!empty($discount_field_key)) {
      if (!empty($context['entity']->{$discount_field_key}[LANGUAGE_NONE])) {
        foreach ($context['entity']->{$discount_field_key}[LANGUAGE_NONE] as $index => $discount_setting) {
          $apply_discount = 0;
          if ($discount_setting['rid'] >= 0) {
            global $user;
            if (array_key_exists($discount_setting['rid'], $user->roles)) {
              $apply_discount = 1;
            }
          }
          else {
            $apply_discount = 1;
          }
          if ($apply_discount) {
            if ($discount_setting['discount'] > 0) {
              switch ($discount_setting['discount_type']) {
                case 'percentage':
                  if (!empty($price_set_data['price_fields'][$discount_setting['price_field_id']]['price_field_values'])) {
                    foreach ($price_set_data['price_fields'][$discount_setting['price_field_id']]['price_field_values'] as $pfv_id => $pfv_data) {
                      $price_set_data['price_fields'][$discount_setting['price_field_id']]['price_field_values'][$pfv_id]->amount = number_format($price_set_data['price_fields'][$discount_setting['price_field_id']]['price_field_values'][$pfv_id]->amount - $price_set_data['price_fields'][$discount_setting['price_field_id']]['price_field_values'][$pfv_id]->amount * ($discount_setting['discount'] / 100), 2);
                    }
                  }
                  break;
                case 'fixed':
                  if (!empty($price_set_data['price_fields'][$discount_setting['price_field_id']]['price_field_values'])) {
                    foreach ($price_set_data['price_fields'][$discount_setting['price_field_id']]['price_field_values'] as $pfv_id => $pfv_data) {
                      if ($price_set_data['price_fields'][$discount_setting['price_field_id']]['price_field_values'][$pfv_id]->amount - $discount_setting['discount'] >= 0) {
                        $price_set_data['price_fields'][$discount_setting['price_field_id']]['price_field_values'][$pfv_id]->amount = number_format($price_set_data['price_fields'][$discount_setting['price_field_id']]['price_field_values'][$pfv_id]->amount - $discount_setting['discount'], 2);
                      }
                      else {
                        $price_set_data['price_fields'][$discount_setting['price_field_id']]['price_field_values'][$pfv_id]->amount = 0;
                      }
                    }
                  }
                  break;
              }
            }
          }
        }
      }
    }
  }
}

Functions

Namesort descending Description
civicrm_entity_discount_field_civicrm_entity_price_set_field_registration_form_price_set_data_alter Implements hook_civicrm_entity_price_set_field_registration_form_price_set_data_alter().
civicrm_entity_discount_field_field_formatter_info Implements hook_field_formatter_info().
civicrm_entity_discount_field_field_formatter_settings_form Implements hook_field_formatter_settings_form().
civicrm_entity_discount_field_field_formatter_settings_summary Implements hook_field_formatter_settings_summary().
civicrm_entity_discount_field_field_formatter_view Implements hook_field_formatter_view().
civicrm_entity_discount_field_field_info Implements hook_field_info().
civicrm_entity_discount_field_field_is_empty Implements hook_field_is_empty().
civicrm_entity_discount_field_field_validate Implements hook_field_validate().
civicrm_entity_discount_field_field_widget_error Implements hook_field_widget_error().
civicrm_entity_discount_field_field_widget_form Implements hook_field_widget_form().
civicrm_entity_discount_field_field_widget_info Implements hook_field_widget_info().
civicrm_entity_discount_field_form_field_ui_field_edit_form_alter Implements hook_form_FORMID_alter().
civicrm_entity_discount_field_form_field_ui_field_overview_form_alter Implements hook_form_FORMID_alter().
civicrm_entity_discount_field_menu Implements hook_menu(). code example taken from https://www.drupal.org/project/multiple_fields_remove_button module
civicrm_entity_discount_field_multiple_fields_remove_button_js Page callback for civicrm_entity_profile_multiple_fields_remove_button/ajax path. code example taken from https://www.drupal.org/project/multiple_fields_remove_button module
_civicrm_entity_discount_field_remove_button_submit_handler Submit callback to remove an item from the field UI multiple wrapper.