You are here

node_recur.pages.inc in Node recur 7.2

Same filename and directory in other branches
  1. 7 node_recur.pages.inc

File

node_recur.pages.inc
View source
<?php

/**
 * The node recur form
 */
function node_recur_node_recur_form($form, &$form_state, $node) {

  // Check if the form was already submitted
  if ($form_state['executed']) {
    return node_recur_node_recur_confirm($form, $form_state, $node);
  }

  // Build form
  _node_recur_node_recur_form($node, $form, $form_state);
  return $form;
}

/**
 * Helper function to provide the basics of the form
 *
 * This helps make it available for other modules to use when we might
 * not have a full node yet
 *
 * @param $type
 *   The node type
 */
function _node_recur_node_recur_form($node = NULL, &$form, &$form_state) {
  $field_name = node_recur_get_date_field_name($node->type);
  $field_inst = field_info_instance('node', $field_name, $node->type);
  $start_date = $node->{$field_name}[LANGUAGE_NONE][0]['value'];
  $field_view = field_view_field('node', $node, $field_name, $start_date);
  $lang = $field_view['#language'];

  // Add fieldset wrapper
  $form['node_recur'] = array(
    '#type' => 'fieldset',
    '#title' => t('Repeat'),
    '#weight' => isset($form[$field_name]) ? $form[$field_name]['#weight'] + 0.1 : 1,
  );
  $form['node_recur']['recur_date'] = array(
    '#type' => 'value',
    '#value' => $start_date,
  );
  $form['node_recur']['node_date_display'] = array(
    '#type' => 'item',
    '#title' => t('Start Date'),
    '#markup' => $field_view[0]['#markup'],
    '#weight' => -10,
  );

  // Get field data
  $form['#parents'] = [];

  // Less PHP Warnings
  $data = _field_invoke_default('form', 'node', $node, $form, $form_state, array(
    'field_id' => $field_inst['field_id'],
    'timezone' => 'UTC',
  ));

  // Faux set the field as repeatable
  $form_state['field'][$field_name][$lang]['field']['settings']['repeat'] = 1;

  // Add rrule field
  $form['node_recur'][$field_name] = $data[$field_name];
  $form['node_recur'][$field_name][$lang][0]['#theme'] = array(
    'date_textfield',
  );
  $form['node_recur'][$field_name][$lang][0]['#attributes']['id'] = $field_name;
  $form['node_recur'][$field_name][$lang][0]['#element_validate'][] = 'node_recur_node_recur_element_validate';
  $form['node_recur'][$field_name][$lang][0]['#element_validate'][] = 'date_repeat_field_widget_validate';
  $form['node_recur'][$field_name][$lang][0]['#date_is_default'] = TRUE;
  $form['node_recur'][$field_name][$lang][0]['show_repeat_settings'] = array(
    '#type' => 'hidden',
    '#value' => 1,
  );
  $form['node_recur'][$field_name][$lang][0]['rrule'] = array(
    '#type' => 'date_repeat_rrule',
    '#theme_wrappers' => array(
      'date_repeat_rrule',
    ),
    '#default_value' => '',
    '#date_timezone' => date_default_timezone(),
    '#date_label_position' => 'above',
    '#date_format' => 'm/d/Y - H:i',
    '#date_increment' => 1,
    '#date_year_range' => '-0:+1',
    '#date_repeat_widget' => $field_inst['widget']['type'],
    '#date_flexible' => 0,
    '#date_repeat_collapsed' => TRUE,
  );

  // Add missing weekend exclusion
  $form['node_recur'][$field_name]['exclude_weekends'] = array(
    '#type' => 'checkbox',
    '#title' => 'Exclude Weekends',
    '#default_value' => 1,
    '#return_value' => 1,
  );
  $form['#pre_render'][] = 'node_recur_pre_render';
  $form['submit'] = array(
    '#type' => 'submit',
    '#weight' => 99,
    '#value' => t('Generate'),
  );
  $form['#node'] = $node;
  $form['#entity_type'] = 'node';
  $form['#bundle'] = $node->type;

  // @kludge CSS to fix display the Date Repeat form
  // Some cosmetic fixes and a hacky, solution to hiding the faux field
  // Date Repeat Field can't see the show_repeat_settings option to display the form
  $form['#attached']['css'][] = drupal_get_path('module', 'node_recur') . '/node_recur.css';
  $form['#attached']['js'][] = drupal_get_path('module', 'node_recur') . '/node_recur.js';

  // Set a JS variable to handle defaults based on the original date
  $date_obj = new DateObject(is_array($start_date) ? $start_date['value'] : $start_date);
  $settings = $date_obj
    ->toArray();
  $settings['dow'] = strtolower(substr($date_obj
    ->format('D'), 0, 2));
  $settings['field_name'] = $field_name;
  $form['#attached']['js'][] = array(
    'data' => array(
      'node_recur' => $settings,
    ),
    'type' => 'setting',
  );
  return $form;
}

/**
 * Validate the node recur form
 * All validation is handled in date_repeat_field_widget_validate
 */
function node_recur_node_recur_form_validate(&$form, &$form_state) {

  // Allow other modules to validate the dates
  $errors = module_invoke_all('node_recur_validate_dates', $form['#node'], $form_state);
  foreach ($errors as $error) {
    form_set_error($error['field'], $error['message']);
  }
}

/**
 * Submit the node recur form
 */
function node_recur_node_recur_form_submit(&$form, &$form_state) {

  // Rebuild so we can present the confirm form
  $form_state['rebuild'] = TRUE;
}

/**
 * Confirm form for node recur
 */
function node_recur_node_recur_confirm($form, $form_state, $node) {
  $field_name = node_recur_get_date_field_name($node->type);
  $node_type = strtolower(node_type_get_name($node));

  // Store dates in the form
  $date_values = $form_state['values'][$field_name][LANGUAGE_NONE];

  // Remove the first start date, to prevent dupe node creation
  unset($date_values[0]);

  // Do we have dates to work with?
  // Date repeat returns the dates on success or the rule definition if errors occure during generation
  if (empty($date_values) || !empty($date_values) && isset($date_values['FREQ'])) {
    drupal_set_message(t('No dates were generated with the information provided.'), 'warning');
    return _node_recur_node_recur_form($node, $form, $form_state);
  }
  else {

    // Instructions/Warning
    $count = count($date_values);
    $form['node_recur_title']['#type'] = 'item';
    $form['node_recur_title']['#markup'] = '<h4>' . t('The following dates will be generated. Please review before continuing.') . '</h4>';
    $vars = array(
      '!count' => $count,
      '!node_type' => $node_type,
    );
    $form['node_recur_instruction']['#type'] = 'item';
    $form['node_recur_instruction']['#markup'] = t('<b>!count</b> new !plural will be generated.', $vars + array(
      '!plural' => format_plural($count, $node_type, "{$node_type}s"),
    ));

    // Display the dates to the user
    $max_default = variable_get('node_recur_max_confirm_display', 10);
    $screen_max = $count > $max_default ? $max_default - 1 : $count;
    $display_dates = array_slice($date_values, 0, $screen_max);
    foreach ($display_dates as $val) {

      // Remove rrule to prevent rule description from printing
      // which gets long and wonky when exclude weekends is selected
      unset($val['rrule']);
      $view = field_view_value('node', $node, $field_name, $val);
      $items[] = $view['#markup'];
    }

    // Display the last date if not on screen
    if ($count > $max_default) {
      $items[] = t('&hellip; <b>!remaining</b> additional &hellip;', $vars + array(
        '!remaining' => $count - $screen_max - 1,
      ));
      $end = $date_values[$count];
      unset($end['rrule']);
      $view = field_view_value('node', $node, $field_name, $end);
      $items[] = $view['#markup'];
    }
    $item_list = array(
      'title' => '',
      'type' => 'ul',
      'items' => $items,
      'attributes' => array(
        'id' => 'node-recur-date-list',
      ),
    );
    $form['node_recur_date_verify']['#type'] = 'item';
    $form['node_recur_date_verify']['#markup'] = theme_item_list($item_list);
    $form['node_recur_date_values']['#type'] = 'value';
    $form['node_recur_date_values']['#value'] = $date_values;

    // Clean up the output
    $form['#attached']['css'][] = drupal_get_path('module', 'node_recur') . '/node_recur.css';

    // Store the node
    $form['#node'] = $node;

    // Add the submit handler
    $form['#submit'][] = 'node_recur_node_recur_confirm_submit';

    // Node type name
    $msg = t('This action cannot be undone. Please confirm that the dates above are accurate and that the !node_type information is correct. Editing this !node_type afterwards will not edit every !node_type generated here.', $vars);

    // Supply the confirm form
    return confirm_form($form, t('Are you sure you want to generate these items?'), "node/{$node->nid}/recur", '<strong>' . $msg . '</strong>', t('Submit'));
  }
}

/**
 * Submit the node recur confirmation form
 */
function node_recur_node_recur_confirm_submit(&$form, &$form_state) {

  // Log this action
  $opts = array(
    '%type' => $form['#node']->type,
    '%title' => check_plain($form['#node']->title),
    '%count' => isset($form['#start_dates']) ? count($form['#start_dates']) : 0,
  );
  watchdog('node_recur', t('Recurring the %type "%title" %count times.', $opts));

  // Start the batch
  node_recur_node_batch_start($form['#node'], $form_state['values']['node_recur_date_values']);
}

/**
 * Make some adjustments for UI additions
 */
function node_recur_node_recur_element_validate($element, &$form_state, &$form) {
  $field_name = node_recur_get_date_field_name($element['#entity']->type);
  $rrule_str = $form_state['values'][$field_name][LANGUAGE_NONE][0]['rrule'];
  $rrule_arr = $form_state['input'][$field_name][LANGUAGE_NONE][0]['rrule'];
  $start_date = $form_state['input'][$field_name][LANGUAGE_NONE][0]['value'];
  $date_time_str = $start_date['date'] . (isset($start_date['time']) ? " - {$start_date['time']}" : '');
  $date_create = date_create_from_format($element['rrule']['#date_format'], $date_time_str);
  $rrule_weekend = array(
    "BYDAY=SA,SU",
  );
  $end_date = isset($rrule_arr['until_child']['datetime']) ? $rrule_arr['until_child']['datetime']['date'] : NULL;
  $count = (int) $rrule_arr['count_child'];
  $end_date_obj = NULL;

  // Catch weekend day selections  if exclude weekends is checked
  $found = $rrule_arr['FREQ'] == 'WEEKLY' && !empty(array_intersect($rrule_arr['weekly']['BYDAY'], array(
    'SA',
    'SU',
  )));
  if (!empty($found) && $form_state['input'][$field_name]['exclude_weekends']) {
    form_set_error($field_name . '][exclude_weekends', t('You may not select a weekend day while excluding weekends.'));
    return;
  }

  // Check for errors, but return empty to allow date fields to set the messages
  if ($rrule_arr['range_of_repeat'] == 'COUNT' && !$count) {

    // Error. Return to allow date fields to throw empty error
    return;
  }
  if ($rrule_arr['range_of_repeat'] == 'UNTIL') {
    if (empty($end_date)) {

      // Error. Return to allow date fields to throw empty error
      return;
    }

    // Parse the rule logic:
    $end_date_obj = date_create_from_format('m/d/Y', $rrule_arr['until_child']['datetime']['date']);

    // Check for invalid until date in the past
    if ($end_date_obj
      ->format(DATE_FORMAT_ICAL) <= $date_create
      ->format(DATE_FORMAT_ICAL)) {
      $error_field_base = implode('][', $element['#parents']);
      $error_field_until = $error_field_base . '][rrule][until_child][datetime';
      $message = t('Until date may not be set to a date in the past');
      form_set_error($error_field_until, $message);
      return;
    }
  }
  else {

    // Because date_repeat includes the start date in it's list we need to
    // increment the COUNT by 1, to meet the expected behavior of requesting
    // N times from the start date, without create a duplicate node
    $count++;
    $form_state['values'][$field_name][LANGUAGE_NONE][0]['rrule'] = preg_replace('/(COUNT=\\d+)/', "COUNT={$count}", $rrule_str);
  }

  /*
   * Weekends cannot be dynamically excluded in the Date Repeat ICAL RRule simply by stating remove SA and SU
   * We must explicitly set each individual weekend date in the RRule via the EXDATE attribute
   */
  if ((bool) $form_state['input'][$field_name]['exclude_weekends']) {
    $weekends_start = clone $date_create;
    $weekends_end_date = isset($end_date_obj) ? clone $end_date_obj : clone $date_create;

    // Find the end date which changes based on the type of request sent
    // Either UNTIL an end date or given OR an explict COUNT is given
    if (empty($end_date_obj)) {
      $interval_count = $count;
      if ($rrule_arr['FREQ'] == 'DAILY') {
        $interval_count = (int) ceil($count / 5);
        $interval_freq = 'WEEKS';
      }
      else {
        $interval_freq = substr_replace($rrule_arr['FREQ'], '', -2);
      }
      $interval = date_interval_create_from_date_string($interval_count . ' ' . $interval_freq);
      $weekends_end_date
        ->add($interval);
      $rrule_weekend[] = "INTERVAL=1";
      $rrule_weekend[] = "FREQ={$interval_freq}";
    }

    // Add the dates to the RRule
    $ex_dates = date_repeat_calc(implode(';', $rrule_weekend), $weekends_start
      ->getTimestamp(), $weekends_end_date
      ->getTimestamp());
    if (!empty($ex_dates)) {
      $form_state['values'][$field_name][LANGUAGE_NONE][0]['rrule'] .= "\nEXDATE:" . implode(',', $ex_dates);
    }
  }
}

/**
 * Clean up the returned date repeat form
 */
function node_recur_pre_render($element) {
  $field_name = node_recur_get_date_field_name($element['#entity']->type);

  // Hide actual date field
  $element['node_recur'][$field_name][LANGUAGE_NONE][0]['value']['#prefix'] = '<div style="display:none">';
  $element['node_recur'][$field_name][LANGUAGE_NONE][0]['value']['#suffix'] = '</div>';
  if (isset($element['node_recur'][$field_name][LANGUAGE_NONE][0]['show_todate'])) {
    $element['node_recur'][$field_name][LANGUAGE_NONE][0]['show_todate']['#prefix'] = '<div style="display:none">';
    $element['node_recur'][$field_name][LANGUAGE_NONE][0]['show_todate']['#suffix'] = '</div>';
  }
  if (isset($element['node_recur'][$field_name][LANGUAGE_NONE][0]['value2'])) {
    $element['node_recur'][$field_name][LANGUAGE_NONE][0]['value2']['#prefix'] = '<div style="display:none">';
    $element['node_recur'][$field_name][LANGUAGE_NONE][0]['value2']['#suffix'] = '</div>';
  }
  $rrule =& $element['node_recur'][$field_name][LANGUAGE_NONE][0]['rrule'];

  // @todo Fix JS issues with adding more values
  $rrule['additions']['#access'] = FALSE;
  $rrule['show_additions']['#access'] = FALSE;
  $rrule['exceptions']['#access'] = FALSE;
  $rrule['show_exceptions']['#access'] = FALSE;

  // Hide the month selections and update the labels to improve UX
  $rrule['monthly']['day_month']['BYMONTHDAY_BYMONTH_child']['BYMONTH']['#access'] = FALSE;
  $rrule['monthly']['day_month']['BYMONTHDAY_BYMONTH_child']['BYMONTHDAY']['#field_suffix'] = t('of each month');
  $rrule['monthly']['day_month']['BYDAY_BYMONTH_child']['BYMONTH']['#access'] = FALSE;
  $rrule['monthly']['day_month']['BYDAY_BYMONTH_child']['BYDAY_DAY']['#title'] = t('of each month');

  // Remove the confusing bits & set some defaults
  $val = $element['node_recur']['recur_date']['#value'];
  $date_obj = new DateObject(is_array($val) ? $val['value'] : $val);
  $dow = substr($date_obj
    ->format('D'), 0, 2);
  $rrule['weekly']['BYDAY'][strtoupper($dow)]['#checked'] = TRUE;
  $vals = array_combine(range(1, 31), range(1, 31));
  $rrule['monthly']['day_month']['BYMONTHDAY_BYMONTH_child']['BYMONTHDAY']['#options'] = $vals;
  $rrule['monthly']['day_month']['BYMONTHDAY_BYMONTH_child']['BYMONTHDAY']['#default_value'] = $date_obj
    ->format('d');
  $rrule['monthly']['day_month']['BYDAY_BYMONTH_child']['BYDAY_DAY']['#default_value'] = strtoupper(substr($date_obj
    ->format('D'), 0, 2));
  $rrule['yearly']['day_month']['BYMONTHDAY_BYMONTH_child']['BYMONTHDAY']['#options'] = $vals;
  $rrule['yearly']['day_month']['BYMONTHDAY_BYMONTH_child']['BYMONTHDAY']['#default_value'] = $date_obj
    ->format('d');
  return $element;
}

Functions

Namesort descending Description
node_recur_node_recur_confirm Confirm form for node recur
node_recur_node_recur_confirm_submit Submit the node recur confirmation form
node_recur_node_recur_element_validate Make some adjustments for UI additions
node_recur_node_recur_form The node recur form
node_recur_node_recur_form_submit Submit the node recur form
node_recur_node_recur_form_validate Validate the node recur form All validation is handled in date_repeat_field_widget_validate
node_recur_pre_render Clean up the returned date repeat form
_node_recur_node_recur_form Helper function to provide the basics of the form