Commit e72ccbf3 authored by paulvandenburg's avatar paulvandenburg
Browse files

Refactor webform components to their own files

parent b0d4a40b
Loading
Loading
Loading
Loading
+83 −0
Original line number Diff line number Diff line
<?php

/**
 * @file
 * WA date time component.
 *
 * Loads an appointment.
 */

/**
 * Implements _webform_edit_COMPONENT().
 */
function _webform_defaults_wa_appointment() {
  return array(
    'name' => '',
    'form_key' => NULL,
    'required' => 0,
    'pid' => 0,
    'weight' => 0,
    'extra' => array(
      'options' => '',
      'questions' => '',
      'optrand' => 0,
      'qrand' => 0,
      'description' => '',
      'private' => FALSE,
      'analysis' => TRUE,
    ),
  );
}

/**
 * Implements _webform_edit_COMPONENT().
 */
function _webform_edit_wa_appointment($component) {
  return array('extra' => array());
}

/**
 * Implements _webform_submit_COMPONENT().
 */
function _webform_submit_wa_appointment($component, $value) {
  return $value['wa_appointment'];
}

/**
 * Implements _webform_display_COMPONENT().
 */
function _webform_display_wa_appointment($component, $value, $format = 'html') {
  return array(
    '#title' => $component['name'],
    '#weight' => $component['weight'],
    '#required' => $component['required'],
    '#theme' => 'dvg_appointments',
    '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
    '#post_render' => array('webform_element_wrapper'),
    '#component' => $component,
    '#format' => $format,
    '#value' => $value,
  );
}

/**
 * Implements _webform_render_COMPONENT().
 */
function _webform_render_wa_appointment($component, $value = NULL, $filter = TRUE) {
  $form_item['wa_appointment'] = array(
    '#type' => 'wa_appointment',
    '#title' => $filter ? webform_filter_xss($component['name']) : $component['name'],
    '#required' => $component['required'],
    '#weight' => $component['weight'],
    '#description'   => $filter ? webform_filter_descriptions($component['extra']['description']) : $component['extra']['description'],
    '#default_value' => $filter ? webform_replace_tokens($component['value']) : $component['value'],
    '#prefix' => '<div class="webform-component-textfield" id="webform-component-' . $component['form_key'] . '">',
    '#suffix' => '</div>',
  );

  if (isset($value)) {
    $form_item['wa_appointment']['#default_value'] = $value;
  }

  return $form_item;
}
+408 −0
Original line number Diff line number Diff line
<?php

/**
 * @file
 * WA date time component.
 *
 * Used for appointment date/time selection.
 */

/**
 * Implements _webform_defaults_COMPONENT().
 */
function _webform_defaults_wa_date_time() {
  return array(
    'name' => '',
    'form_key' => NULL,
    'required' => 0,
    'pid' => 0,
    'weight' => 0,
    'extra' => array(
      'options' => '',
      'questions' => '',
      'optrand' => 0,
      'qrand' => 0,
      'description' => '',
      'private' => FALSE,
      'analysis' => TRUE,
    ),
  );
}

/**
 * Implements _webform_edit_COMPONENT().
 */
function _webform_edit_wa_date_time($component) {
  return array('extra' => array());
}

/**
 * Implements _webform_submit_COMPONENT().
 */
function _webform_submit_wa_date_time($component, $value) {
  return $value['wa_date_time'];
}

/**
 * Implements _webform_display_COMPONENT().
 */
function _webform_display_wa_date_time($component, $value, $format = 'html') {
  return array(
    '#title' => $component['name'],
    '#weight' => $component['weight'],
    '#required' => $component['required'],
    '#theme' => 'webform_display_textfield',
    '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
    '#post_render' => array('webform_element_wrapper'),
    '#component' => $component,
    '#format' => $format,
    '#value' => isset($value[0]) ? dvg_appointments_format_date($value[0], 'day_date_time') : '',
  );
}

/**
 * Implements _webform_render_COMPONENT().
 */
function _webform_render_wa_date_time($component, $value = NULL, $filter = TRUE) {
  $form_item['wa_date_time'] = array(
    '#type' => 'wa_date_time',
    '#title' => $filter ? webform_filter_xss($component['name']) : $component['name'],
    '#required' => $component['required'],
    '#weight' => $component['weight'],
    '#description'   => $filter ? webform_filter_descriptions($component['extra']['description']) : $component['extra']['description'],
    '#default_value' => $filter ? webform_replace_tokens($component['value']) : $component['value'],
    '#prefix' => '<div class="webform-component-textfield" id="webform-component-' . $component['form_key'] . '">',
    '#suffix' => '</div>',
  );

  if (isset($value) && !empty($value[0])) {
    $form_item['wa_date_time']['#default_value'] = $value[0];
  }

  return $form_item;
}

/**
 * Element process callback for the wa_date_time elements.
 */
function dvg_appointments_date_time_element_process($element, &$form_state, $form) {
  $client_api = dvg_appointments_get_client_api();
  $selected_products = dvg_appointments_get_selected_products($form_state, $form);
  $selected_location = dvg_appointments_get_selected_location($form_state, $form);
  $product_link_ids = $product_names = $products_durations = $persons = array();
  foreach ($selected_products as $selected_product) {
    $product_id = $selected_product['id'];

    $product_link_ids[] = $product_id;
    $product_names[] = '<strong>' . check_plain($selected_product['name']) . '</strong>';
    $persons[$product_id] = $selected_product['count'];
    if (isset($selected_product['additional_customer_duration']) && $selected_product['count'] > 1) {
      $total_product_duration = $selected_product['duration'] + $selected_product['additional_customer_duration'] * ($selected_product['count'] - 1);
    }
    else {
      $total_product_duration = $selected_product['duration'] * $selected_product['count'];
    }
    $products_durations[$product_id] = $total_product_duration;

    $rows[] = array(
      'product_name' => $selected_product['name'],
      'duration' => $total_product_duration,
    );
  }

  $last_product = array_pop($product_names);
  $product_text = $product_names ? implode(', ', $product_names) . ' ' . t('and') . ' ' . $last_product : $last_product;
  $total_duration = array_sum($products_durations);

  $output_info = '<div class="duration"><p>' . t('You are booking an appointment for !product_text. This appointment takes about <strong>@total_duration minutes</strong>.', array(
      '!product_text' => $product_text,
      '@total_duration' => $total_duration,
    )) . '</p></div>';

  $element['header'] = array(
    '#markup' => '<h2>' . t("Choose a date and time") . '</h2>' . $output_info,
  );

  drupal_alter('dvg_appointments_date_time', $client_api, $form_state);

  $dates_all = _dvg_appointments_load_date_time_dates($element, $form_state, $product_link_ids, $products_durations, $persons, $selected_location);
  $dates = array();
  foreach ($dates_all as $unic_id => $date_array) {
    if ($unic_id == 0 || $date_array[0]['date'] != $dates_all[$unic_id - 1][0]['date']) {
      $dates[$date_array[0]['date']] = array();
    }
    $dates[$date_array[0]['date']][$unic_id] = $date_array[0]['time'];
  }
  ksort($dates);
  $full_date = $element['slots'] = array();
  $timezone = new DateTimeZone($client_api->get_timezone() ? $client_api->get_timezone() : drupal_get_user_timezone());

  $afternoon_start_hour = variable_get('dvg_appointments_afternoon_start_hour', '12');
  $evening_start_hour = variable_get('dvg_appointments_evening_start_hour', '18');

  foreach ($dates as $date => $time_array) {
    $unix_date = strtotime($date);
    $morning_time = array();
    $midday_time = array();
    $evening_time = array();
    $full_time = array();

    $options = array('' => t('-- Select time --'));

    foreach ($time_array as $time) {

      if (strpos($time, 'T') === FALSE) {
        $unix_time = strtotime(dvg_appointments_format_date($unix_date, 'custom', 'Y-m-d') . 'T' . $time);
      } else {
        $unix_time = date("U", strtotime($time));
        $parts = explode('T', $time);
        $time = $parts[1];
      }

      $datetime = new DateTime($time, $timezone);
      $hour = (int) $datetime->format('H');

      $index = $unix_time;
      if ($hour < $afternoon_start_hour) {
        $morning_time[$index] = dvg_appointments_format_date($unix_time, 'time');
      }
      elseif ($hour < $evening_start_hour) {
        $midday_time[$index] = dvg_appointments_format_date($unix_time, 'time');
      }
      else {
        $evening_time[$index] = dvg_appointments_format_date($unix_time, 'time');
      }

      if (!empty($morning_time)) {
        $full_time['morning'] = TRUE;
        $options[t('Morning')] = $morning_time;
      }
      if (!empty($midday_time)) {
        $full_time['midday'] = TRUE;
        $options[t('Midday')] = $midday_time;
      }
      if (!empty($evening_time)) {
        $full_time['evening'] = TRUE;
        $options[t('Evening')] = $evening_time;
      }
    }

    $element['slots'][] = array(
      '#title' => t('Time'),
      '#title_display' => 'invisible',
      '#type' => 'select',
      '#options' => $options,
      '#default_value' => $element['#default_value'],
    );

    $full_date[] = array(
      'date' => $unix_date,
      'date_time' => $full_time,
    );
  }

  $element['appointments_full_date'] = array(
    '#type' => 'value',
    '#value' => $full_date,
  );

  return $element;
}

/**
 * Helper for loading the dates used for filling the wa_date_time webform component.
 */
function _dvg_appointments_load_date_time_dates($element, $form_state, $product_ids, $products_durations, $persons, $location = FALSE) {
  $client_api = dvg_appointments_get_client_api();
  $overrides = module_invoke_all('appointment_date_overrides');

  // Location has been set and the location should be used in the client.
  if ($location) {
    $client_api->set_location($location);
  }

  // Default dates.
  $dates = $client_api->get_dates_times($product_ids, $products_durations, $persons);
  if (!empty($overrides) && is_array($overrides)) {
    // Loop through all overrides and if the form has a date value ask the available times for that date from the API.
    foreach ($overrides as $possible_value) {
      if (isset($form_state['input'][$possible_value])
        && !empty($form_state['input'][$possible_value])
        && dvg_appointment_validate_date($form_state['input'][$possible_value])) {
        $date = new DateTime($form_state['input'][$possible_value], new DateTimeZone(drupal_get_user_timezone()));
        // Adding 12 hours because DvG appointments doesn't handle ISO timezones well.
        $time = $date->format('U') + 60 * 60 * 12;
        $date = date('c', $time);
        $new_date = $client_api->get_times_by_date($product_ids, $products_durations, $persons, $date);
        $dates = array_merge_recursive($dates, $new_date);
      }
    }
  }
  $slots_element = $form_state['values'];
  foreach ($element['#parents'] as $form_key) {
    if (!isset($slots_element[$form_key])) {
      $slots_element = NULL;
      break;
    }
    $slots_element = $slots_element[$form_key];
  }
  if (isset($slots_element)) {
    if (!empty($slots_element) && !is_array($slots_element)) {
      $date = date('c', $slots_element);
      // Strip the time of date.
      // Otherwise its unique in the array of dates.
      $date = substr($date, 0, strpos($date, 'T')) . 'T00:00:00Z';
      $submitted_date = $client_api->get_times_by_date($product_ids, $products_durations, $persons, $date);
      $dates = array_merge_recursive($dates, $submitted_date);
    }
  }

  return $dates;
}

/**
 * Helper to set the error state on all slots.
 */
function _dvg_appointments_date_time_error(&$element, $message, $ignore_value = TRUE) {
  form_set_error('wa_date_time', $message);

  foreach (element_children($element['slots']) as $child) {
    $slot = $element['slots'][$child];

    if (!empty($slot['#value']) || $ignore_value) {
      form_error($element['slots'][$child]);
    }
  }
}

/**
 * Element validate callback for the wa_date_time elements.
 */
function dvg_appointments_date_time_element_validate($element, &$form_state) {
  if (end($form_state['clicked_button']['#parents']) == 'previous') {
    return;
  }

  $slots = array();
  $slots_element = $form_state['values'];
  foreach ($element['#parents'] as $form_key) {
    if (!isset($slots_element[$form_key])) {
      $slots_element = NULL;
      break;
    }
    $slots_element = $slots_element[$form_key];
  }
  if (isset($slots_element) && is_array($slots_element['slots'])) {
    $submitted_slots = $slots_element['slots'];
    foreach ($submitted_slots as $index => $value) {
      if (!empty($value) && is_numeric($value)) {
        $slots[$index] = $value;
      }
    }
  }

  // Validate, and set errors.
  $slot_count = count($slots);
  if (!$slot_count) {
    _dvg_appointments_date_time_error($element, t('No time selected.'));
  }
  elseif ($slot_count > 1) {
    _dvg_appointments_date_time_error($element, t('Only one selection possible.'), FALSE);
  }

  // Set the selected values as element value.
  drupal_array_set_nested_value($form_state['values'], $element['#array_parents'], array_values($slots));
}

/**
 * Returns HTML for a start/end date combination on form.
 */
function theme_dvg_appointments_date_time($variables) {
  $output = render($variables['element']['header']);
  $slots = $variables['element']['slots'];
  $appointments_full_date = $variables['element']['appointments_full_date'];

  $day_parts = array(
    'morning' => t('Morning'),
    'midday' => t('Midday'),
    'evening' => t('Evening'),
  );

  if (isset($appointments_full_date['#value']) && !empty($appointments_full_date['#value'])) {
    $table_data = array(
      'header' => array(
        t('Date'),
        $day_parts['morning'],
        $day_parts['midday'],
        $day_parts['evening'],
        t('Time'),
      ),
      'rows' => array(),
      'attributes' => array(
        'class' => array('dvg-appointments-date-selection'),
      ),
      'sticky' => FALSE,
    );

    $count = 0;
    // First show this amount of date/times (before the more dates button).
    $limit = variable_get('dvg_appointments_datetime_limit', FALSE);
    $use_show_more_button = variable_get('dvg_appointments_default_show_more_dates', TRUE);
    foreach ($appointments_full_date['#value'] as $index => $date) {

      $table_cells = array();

      $table_cells[] = array(
        'data' => drupal_ucfirst(dvg_appointments_format_date($date['date'], 'day_date')),
        'header' => TRUE,
      );

      foreach ($day_parts as $day_part_key => $day_part_label) {
        $available = isset($date['date_time'][$day_part_key]);
        $table_cells[] = array(
          'data' => t('!day_part !available', array(
            '!day_part' => '<span class="day-part-label">' . $day_part_label . '</span>',
            '!available' => '<span class="availability">' . ($available ? t('available') : t('unavailable')) . '</span>',
          )),
          'class' => array(
            $day_part_key,
            $available ? 'available' : 'unavailable',
          ),
          'title' => array(
            $available ? t('available') : t('unavailable'),
          ),
        );
      }

      $table_cells[] = array(
        'data' => render($slots[$index]),
        'class' => array(
          'time-selector-appointments',
        ),

      );
      $table_data['rows'][$count]['data'] = $table_cells;
      if ($limit && $count > ($limit - 1) && $use_show_more_button) {
        $table_data['rows'][$count]['class'] = array('element-invisible');
      }
      $count++;
    }

    $output .= theme('table', $table_data);

    if ($limit && $use_show_more_button && $count > $limit) {
      $output .= '<input type="button" class="appointments-show-more-dates" id="dvg-show-more-dates" value="' . t('Show more dates') . '">';
    }
  }
  else {
    $markup = array(
      '#markup' => t('No suitable date and time for this request. Please register appointments separately.'),
      '#prefix' => '<div class="error">',
      '#suffix' => '</div>',
      '#weight' => 4,
    );
    $output .= render($markup);
  }

  return $output;
}
+39 −0
Original line number Diff line number Diff line
<?php

/**
 * @file
 * WA date time component.
 *
 * Loads an email address from an appointment.
 */

/**
 * Implements _webform_edit_COMPONENT().
 */
function _webform_edit_wa_email($component) {
  return array('extra' => array());
}

/**
 * Implements _webform_display_COMPONENT().
 */
function _webform_display_wa_email($component, $value, $format = 'html') {
  return array(
    '#title' => $component['name'],
    '#weight' => $component['weight'],
    '#required' => $component['required'],
    '#theme' => 'webform_display_textfield',
    '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
    '#post_render' => array('webform_element_wrapper'),
    '#component' => $component,
    '#format' => $format,
    '#value' => isset($value[0]) ? $value[0] : '',
  );
}

/**
 * Implements _webform_render_COMPONENT().
 */
function _webform_render_wa_email($component, $value = NULL, $filter = TRUE) {
  return array('#type' => 'value');
}
+2 −0
Original line number Diff line number Diff line
@@ -3,6 +3,8 @@
/**
 * @file
 * WA location component.
 *
 * Used for appointment location selection.
 */

/**
+232 −0
Original line number Diff line number Diff line
<?php

/**
 * @file
 * WA product component.
 *
 * Used for appointment product selection.
 */

/**
 * Implements _webform_defaults_COMPONENT().
 */
function _webform_defaults_wa_product() {
  return array(
    'name' => '',
    'form_key' => NULL,
    'required' => 0,
    'pid' => 0,
    'weight' => 0,
    'extra' => array(
      'options' => '',
      'questions' => '',
      'optrand' => 0,
      'qrand' => 0,
      'description' => '',
      'private' => FALSE,
      'analysis' => TRUE,
    ),
  );
}

/**
 * Implements _webform_edit_COMPONENT().
 */
function _webform_edit_wa_product($component) {
  $form = array();

  // Disabling the description if not wanted.
  $form['extra']['description'] = array();

  return $form;
}

/**
 * Implements _webform_submit_COMPONENT().
 */
function _webform_submit_wa_product($component, $value) {
  if (!module_exists('wfm') || !_wfm_is_multiple($component)) {
    $value = array($value);
  }

  return $value;
}

/**
 * Implements _webform_display_COMPONENT().
 */
function _webform_display_wa_product($component, $values, $format = 'html') {
  if (isset($values['product'])) {
    $values = array($values);
  }

  $client = dvg_appointments_get_client_api();
  $products = $client->get_filtered_available_products();
  $display = array(
    '#title' => $component['name'],
    '#weight' => $component['weight'],
    '#required' => $component['required'],
    '#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
    '#post_render' => array('webform_element_wrapper'),
    '#component' => $component,
  );

  foreach ($values as $value) {
    $product = $products[$value['product']];
    if ($format == 'html') {
      $display_value = theme('table', array(
        'rows' => array(
          array(t('Product'), $product['name']),
          array(t('Number of persons'), $value['count']),
        ),
      ));
    }
    else {
      $display_value = t('Product') . ' = ' . $product['name'] . "\n" . t('Number of persons') . ' = ' . $value['count'];
    }

    $display[] = array(
      '#format' => $format,
      '#markup' => $display_value,
      '#parents' => array(FALSE),
      '#value' => $value,
    );
  }

  return $display;
}

/**
 * Implements _webform_render_COMPONENT().
 */
function _webform_render_wa_product($component, $value = NULL, $filter = TRUE) {
  $form_item = array(
    '#type' => 'wa_product',
    '#title' => $filter ? webform_filter_xss($component['name']) : $component['name'],
    '#required' => $component['required'],
    '#weight' => $component['weight'],
    '#description'   => $filter ? webform_filter_descriptions($component['extra']['description']) : $component['extra']['description'],
    '#default_value' => $filter ? webform_replace_tokens($component['value']) : $component['value'],
    '#prefix' => '<div class="webform-component-textfield" id="webform-component-' . $component['form_key'] . '">',
    '#suffix' => '</div>',
  );

  if (!empty($value)) {
    $form_item['#default_value'] = $value;
  }

  return $form_item;
}

/**
 * Element process callback for the wa_product elements.
 */
function dvg_appointments_product_element_process($element, &$form_state, $form) {
  $client = dvg_appointments_get_client_api();
  $product_options = array('' => t('Select a product'));
  $invisible_values = array(array('value' => ''));

  $available_products = $client->get_filtered_available_products();
  // Let other modules lookup the appointment.
  drupal_alter('dvg_appointments_available_products', $available_products, $element, $form_state, $form);

  foreach ($available_products as $product) {
    $product_options[$product['id']] = $product['name'];

    if (isset($product['max_persons']) && $product['max_persons'] == 1) {
      $invisible_values[] = array('value' => strval($product['id']));
    }
  }

  $element['product'] = array(
    '#type' => 'select',
    '#title' => t('Product'),
    '#required' => TRUE,
    '#options' => $product_options,
    '#attributes' => array('class' => array('wa-product-product')),
  );

  if (!empty($element['#default_value']['product'])) {
    $element['product']['#default_value'] = $element['#default_value']['product'];
  }
  elseif (isset($_GET['product_code']) && isset($product_options[$_GET['product_code']])) {
    $element['product']['#default_value'] = $_GET['product_code'];
  }

  $element['count'] = array(
    '#type' => 'textfield',
    '#title' => t('Number of persons'),
    '#required' => TRUE,
    '#size' => 4,
    '#maxlength' => 4,
    '#default_value' => !empty($element['#default_value']['count']) ? $element['#default_value']['count'] : 1,
    '#element_validate' => array('element_validate_integer_positive'),
    '#attributes' => array('class' => array('wa-product-count')),
  );

  if (count($invisible_values) > 1) {
    $element['count']['#states']['invisible'] = array(
      ':input[name="' . $element['#name'] . '[product]"]' => $invisible_values,
    );

    $element['#attached']['js'][] = array(
      'type' => 'setting',
      'data' => array(
        'dvg_appointments' => array(
          $element['#name'] . '[count]' => TRUE,
        ),
      ),
    );

  }

  $element['#element_validate'][] = '_dvg_appointments_element_validate_person_count';

  return $element;
}

/**
 * Element validate callback for the wa_product elements.
 */
function dvg_appointments_product_element_validate($element, &$form_state) {
  $clash_found = FALSE;
  $products = array();
  $form_key = $element['#webform_component']['form_key'];

  if (module_exists('wfm') && _wfm_is_multiple($element['#webform_component']) && !empty($form_state['values']['submitted'][$form_key])) {
    foreach ($form_state['values']['submitted'][$form_key] as $index => $product) {
      if (isset($product['product']) && !empty($product['product'])) {
        if (isset($products[$product['product']])) {
          $clash_found = TRUE;
        }
        $products[$product['product']] = $product;
      }
    }
    if ($clash_found) {
      form_error($element['product'], t('Product must be unique'));
    }
  }
}

/**
 * Returns HTML for the product/count fields on form.
 */
function theme_dvg_appointments_product($variables) {
  $element = $variables['element'];

  // Group start/end items together in fieldset.
  $fieldset = array(
    '#title' => t($element['#title']) . ' ' . ($element['#delta'] > 0 ? intval($element['#delta'] + 1) : ''),
    '#value' => '',
    '#description' => !empty($element['#fieldset_description']) ? $element['#fieldset_description'] : '',
    '#attributes' => array(),
    '#children' => $element['#children'],
  );

  // Add marker to required date fields.
  if ($element['#required']) {
    $fieldset['#title'] .= ' ' . theme('form_required_marker');
  }

  return theme('fieldset', array('element' => $fieldset));
}
Loading