You are here

node_registration.api.inc in Node registration 7

Registration API functions.

File

includes/node_registration.api.inc
View source
<?php

/**
 * @file
 * Registration API functions.
 */

/**
 * Returns all node registration bundles in the node_registration type.
 */
function _node_registration_bundles() {
  $bundles = _node_registration_node_types();
  $nodes = db_query('SELECT nid FROM {node_registration_node} WHERE private_fields <> 0');
  foreach ($nodes as $node) {
    $nid = $node->nid;
    $type = 'node_' . $nid;
    $name = 'Node # ' . $nid;
    $bundles[$type] = $name;
  }
  return $bundles;
}

/**
 * Returns a message for the strtotime format to show the user in admin forms.
 */
function _node_registration_strtotime_debug($setting_value, $relative_utc = 0) {
  $YMD = 'Y-m-d H:i:s';
  $relative_utc or $relative_utc = REQUEST_TIME;
  $relative_datetime = date('Y-m-d H:i:s', $relative_utc);
  $time = _node_registration_strtotime($relative_datetime, $setting_value);
  return date($YMD, $relative_utc) . ' ' . _node_registration_strtotime_format($setting_value) . ' = ' . date($YMD, $time);
}

/**
 * Calculates a time from a reference time and strotime offset.
 */
function _node_registration_strtotime($reference, $offset) {
  $offset = _node_registration_strtotime_format($offset);
  if (is_numeric($reference)) {
    return strtotime($offset, $reference);
  }
  return strtotime($reference . ' ' . $offset);
}

/**
 * Reformats strtotime format to all negatives (to be used in strtotime()).
 */
function _node_registration_strtotime_format($format) {
  $format = preg_replace('#(^|[^\\+\\d\\-])(\\d+)#', '$1-$2', $format);
  return $format;
}

/**
 * Helper: whether the current request is made with XHR.
 */
function _node_registration_request_is_ajax() {
  return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest';
}

/**
 * Reset waitinglist flags for an event.
 */
function _node_registration_reset_waitinglist($node) {
  $capacity = (int) $node->registration
    ->capacity();
  $registration_ids = db_select('node_registration', 'nr')
    ->fields('nr', array(
    'registration_id',
  ))
    ->condition('nid', $node->nid)
    ->condition('cancelled', 0)
    ->orderBy('weight', 'ASC')
    ->addTag('nr_waitinglist')
    ->execute()
    ->fetchCol();
  $registrations = node_registration_load_multiple($registration_ids);
  $change = FALSE;
  $slots = 0;
  foreach ($registrations as $registration) {
    $slots += $registration->slots;
    $old_waitinglist = (bool) $registration->waitinglist;
    $new_waitinglist = $capacity && $slots > $capacity;

    // Waitinglist status changed.
    if ($old_waitinglist != $new_waitinglist) {
      $registration->waitinglist = (int) $new_waitinglist;
      $registration
        ->save();

      // Save change for return value.
      $change = TRUE;
    }
  }
  return $change;
}

/**
 * The size of an event's waitinglist. Always >= 0.
 */
function _node_registration_waitinglist_size($node) {
  $capacity = $node->registration
    ->capacity();
  if ($capacity) {
    $slots = db_query('SELECT SUM(slots) FROM {node_registration} WHERE nid = ? AND cancelled = 0', array(
      $node->nid,
    ))
      ->fetchField();
    if ($slots >= $capacity) {
      return $slots - $capacity;
    }
  }
  return 0;
}

/**
 * The current, active waitinglist of an event.
 */
function _node_registration_get_waitinglist($node) {
  $ids = db_select('node_registration', 'r')
    ->fields('r', array(
    'registration_id',
  ))
    ->condition('nid', $node->nid)
    ->condition('cancelled', 0)
    ->condition('waitinglist', 1)
    ->execute()
    ->fetchColumn();
  $registrations = node_registration_load_multiple($ids);
  ksort($registrations, SORT_NUMERIC);
  return $registrations;
}

/**
 * Helper to keep URI strings DRY.
 */
function _node_registration_type_to_uri($type) {
  return $type;
}

/**
 * Helper to keep TYPE strings DRY.
 */
function _node_registration_uri_to_type($uri) {
  return $uri;
}

/**
 * Saves or retrieves with events have been sent reminders.
 */
function _node_registration_reminders_sent($nids = array()) {
  $sent = variable_get('node_registration_reminders_sent', array());
  if ($nids) {
    $sent = array_merge($sent, (array) $nids);
    variable_set('node_registration_reminders_sent', $sent);
  }
  return $sent;
}

/**
 * Returns all event node objects (for certrain node types).
 */
function _node_registration_event_nodes($types = array()) {
  $types or $types = array_keys(_node_registration_node_types());
  $nodes = node_load_multiple(FALSE, array(
    'type' => (array) $types,
  ));
  return $nodes;
}

/**
 * Default e-mail header From name.
 */
function _node_registration_default_from_name() {
  return variable_get('site_name');
}

/**
 * Send an email to all registrations for a given node.
 */
function node_registration_send_broadcast($node, $subject, $message, $registrations = array(), $options = array()) {

  // Registrations.
  $registrations or $registrations = node_registration_load_multiple(FALSE, array(
    'nid' => $node->nid,
    'cancelled' => 0,
  ));

  // Options.
  $notify = isset($options['notify']) ? $options['notify'] : FALSE;
  if ($registrations) {

    // Send e-mails.
    $sent_to = 0;
    foreach ($registrations as $registration) {
      $registration->node = $node;
      $token_data = array(
        'node' => $node,
        'node-registration' => $registration,
      );
      if (isset($options['alter'])) {
        $context = array(
          'node' => $node,
          'registration' => $registration,
        );
        $type = array(
          'node_registration_email',
          'node_registration_email_' . $options['alter'],
        );
        drupal_alter($type, $options, $context);
      }
      $result = _node_registration_send_email($registration->email, $subject, $message, $token_data, $options);
      if ($result) {
        $sent_to++;
      }
      else {

        // Log failure.
        watchdog('node_registration', 'Failed to send registration broadcast e-mail to %email.', array(
          '%email' => $registration->email,
        ), WATCHDOG_ERROR);
      }
    }
    if ($sent_to) {
      $params = array(
        '@total' => count($registrations),
        '@sent_to' => $sent_to,
      );
      if ($notify) {

        // Notify user of success.
        drupal_set_message(t('The message has been sent to @sent_to / @total registrees.', $params));
      }

      // Log success.
      watchdog('node_registration', 'Registration e-mail sent to @sent_to / @total registrants.', $params);
    }
    else {
      if ($notify) {

        // Notify user of failure.
        drupal_set_message(t('There was an error sending the message.'), 'error');
      }
    }
  }
  else {
    if ($notify) {
      drupal_set_message(t('There are no participants registered for this %type.', array(
        '%type' => $node->type,
      )), 'warning');
    }
  }
}

/**
 * Internal send e-mail function.
 */
function _node_registration_send_email($recipient, $subject, $message, $token_data = array(), $options = array()) {
  $recipient_account = user_load_by_mail($recipient);
  $language = $recipient_account ? user_preferred_language($recipient_account) : language_default();

  // Set up from. Fill in later.
  $from_name = '';
  $from_email = '';

  // Auto complete token objects.
  if (isset($token_data['node-registration'])) {
    if (!empty($token_data['node-registration']->author_uid) && empty($token_data['node-registration']->author)) {
      $token_data['node-registration']->author = user_load($token_data['node-registration']->author_uid);
    }
    if (!empty($token_data['node-registration']->uid) && empty($token_data['node-registration']->user)) {
      $token_data['node-registration']->user = user_load($token_data['node-registration']->uid);
    }
    if (!empty($token_data['node-registration']->node) && empty($token_data['node'])) {
      $token_data['node'] = $token_data['node-registration']->node;
    }
  }

  // From name + e-mail.
  if (isset($token_data['from'])) {
    $from_name = $token_data['from']['from_name'];
    $from_email = $token_data['from']['from_email'];
  }
  elseif (isset($token_data['node'])) {
    $settings = $token_data['node']->registration;
    $settings->sender_name && ($from_name = token_replace($settings->sender_name, $token_data, array(
      'clear' => TRUE,
    )));
    $settings->sender_mail && ($from_email = token_replace($settings->sender_mail, $token_data, array(
      'clear' => TRUE,
    )));
  }

  // Add defaults and combine.
  $from_name or $from_name = _node_registration_default_from_name();
  $from_email or $from_email = variable_get('site_mail');
  $from = $from_name . ' <' . $from_email . '>';

  // Translate subject & message.
  $subject = t($subject, array(), array(
    'context' => 'node_registration',
    'langcode' => $language->language,
  ));
  $message = t($message, array(), array(
    'context' => 'node_registration',
    'langcode' => $language->language,
  ));

  // Token data.
  if ($token_data) {
    $subject = token_replace($subject, $token_data, array(
      'clear' => TRUE,
      'language' => $language,
    ));
    $message = token_replace($message, $token_data, array(
      'clear' => TRUE,
      'language' => $language,
    ));
  }

  // Mail params.
  $params = array(
    'subject' => $subject,
    'message' => $message,
  ) + $options;
  $type = isset($options['alter']) ? $options['alter'] : 'broadcast';
  $result = drupal_mail('node_registration', $type, $recipient, $language, $params, $from);
  return $result['result'];
}

/**
 * Implements hook_node_access().
 *
 * Ignores existing node access permissions and creates a few new.
 */
function node_registration_node_access($node, $op, $account = NULL, &$reason = NULL) {
  if (!in_array($op, array(
    'register',
    'register others',
    'administer',
    'registered',
    'registration settings',
  ))) {
    return;
  }
  global $user;
  $account or $account = $user;
  if (!is_object($node)) {
    $node = node_load((string) $node);
    if (!is_object($node)) {
      return;
    }
  }
  $nid = empty($node->nid) ? 0 : $node->nid;
  $nid_key = $nid ?: $node->type;
  $reasons =& drupal_static(__FUNCTION__ . ':reasons');
  $cache_key = __FUNCTION__ . ':' . $nid_key . ':' . $op . ':' . $account->uid;
  $access = _node_registration_cache($cache_key, function () use ($cache_key, $node, $nid, $op, $account, &$reason) {
    switch ($op) {
      case 'register':
        $registration = entity_get_controller('node_registration')
          ->create(array(
          'nid' => $nid,
        ));
        return node_registration_access($registration, 'add', $account, $reason);
        break;
      case 'register others':
        return user_access('other node registration', $account) || node_registration_node_access($node, 'administer');
        break;
      case 'administer':
        return user_access('administer node registration', $account);
        break;
      case 'registered':
        return ($registration = _node_registration_user_registered($node, $account)) && node_registration_access($registration, 'view', $account, $reason);
        break;
      case 'registration settings':
        return _node_registration_node_type_enabled($node->type) && user_access('administer node registration', $account);
        break;
    }
    return FALSE;
  }, function ($result, $cache_key) use ($node, $op, $account, &$reason) {

    // Allow other modules to override and change this result.
    $reason or $reason = $result ? '' : '?';
    $alt_access = module_invoke_all('node_registration_node_access', $node, $op, $account, $reason);
    if (in_array(!$result, $alt_access, TRUE)) {
      return !$result;
    }
    return $result;
  });
  if ($reason) {
    $reasons[$cache_key] = $reason;
  }
  else {
    $reason = @$reasons[$cache_key];
  }
  return $access;
}

/**
 * All Registration access callbacks. Just like node.module has node_access.
 */
function node_registration_access($registration, $op, $account = NULL, &$reason = NULL) {
  global $user;
  $account or $account = $user;

  // Rules can get here with a 'view' op without a Registration object.
  // @see entity_rules_integration_event_access()
  if (!$registration || !is_object($registration) || empty($registration->nid)) {
    return $op == 'view' && user_access('administer node registration', $account);
  }
  $reasons =& drupal_static(__FUNCTION__ . ':reasons');
  $cache_key = implode(':', array(
    __FUNCTION__,
    $account->uid,
    $registration->nid,
    $registration->registration_id,
    $op,
  ));
  $access = _node_registration_cache($cache_key, function () use ($cache_key, $registration, $op, $account, &$reason) {

    // There's definitely a node involved.
    $node = $registration->node ? $registration->node : ($registration->node = node_load($registration->nid));
    $type = $node->type;
    $settings = $node->registration;
    $enabled = $settings
      ->enabled();
    $debug = isset($_GET['debug']);
    $reason = '';
    $is_admin = user_access('administer node registration', $account);
    switch ($op) {
      case 'delete':

        // Must be cancelled.
        if (!$registration->cancelled) {
          return FALSE;
        }

        // Admins: always.
        if ($is_admin) {
          return TRUE;
        }

        // Others, only with the right permission.
        if (user_access('delete own ' . $type . ' node registration', $account)) {
          return TRUE;
        }
        break;
      case 'view':

        // Admins: always.
        if ($is_admin) {
          return TRUE;
        }

        // Never if: not enabled, cancelled or not verified.
        if (!$enabled || $registration->cancelled) {
          return FALSE;
        }

        // Semi view admins: no matter the owner.
        if (user_access('view ' . $type . ' node registration', $account)) {
          return TRUE;
        }

        // The rest: only if they're the owner.
        if (user_access('view own ' . $type . ' node registration', $account)) {
          return _node_registration_secret_access($registration, $account);
        }
        break;
      case 'edit':
      case 'update':

        // Admins: always.
        if ($is_admin) {
          return TRUE;
        }

        // If enabled, cancellable and access: only if they're the owner.
        $cancellable = $settings
          ->max_cancel_time_passed();
        if ($enabled && !$cancellable && user_access('edit own ' . $type . ' node registration', $account)) {
          return _node_registration_secret_access($registration, $account);
        }
        break;
      case 'cancel':

        // Must not be cancelled.
        if ($registration->cancelled) {
          return FALSE;
        }

        // Admins: always.
        if ($is_admin) {
          return TRUE;
        }

        // Not if past cancel time.
        if ($settings
          ->max_cancel_time_passed()) {
          return FALSE;
        }

        // If enabled and access: only if they're owner.
        if ($enabled && user_access('cancel own ' . $type . ' node registration', $account)) {
          return _node_registration_secret_access($registration, $account);
        }
        break;
      case 'add':
      case 'create':

        // Check: enabled.
        if ($enabled) {

          // Registration min date.
          $allow_min_date = $settings
            ->min_registration_time_passed($disallow_min_date_reason);

          // Registration max date.
          $allow_max_date = !$settings
            ->max_registration_time_passed($disallow_max_date_reason);

          // Check: start and end date.
          if ($allow_min_date && $allow_max_date) {

            // Check: capacity.
            if (_node_registration_event_has_room($node) || $settings->allow_exceeding_capacity) {

              // Fetch existing registration for current user.
              $registration = _node_registration_user_registered($node, $account);

              // Check: already registered.
              if (!$registration || node_registration_node_access($node, 'register others', $account)) {

                // The following are indirect settings from registration type and node.
                $user_access = user_access('add ' . $type . ' node registration', $account);
                $registration_access = $account->uid ? $settings->allow_authenticated : $settings->allow_anonymous;

                // Check: user access.
                if ($is_admin || $user_access && $registration_access) {
                  return TRUE;
                }
                else {

                  // Fail: user access.
                  $debug && watchdog('node_registration', 'user access insufficient');
                  $reason = 'access';
                }
              }
              else {

                // Fail: already registered.
                $debug && watchdog('node_registration', 'user already registered');
                $reason = 'registered';
              }
            }
            else {

              // Fail: capacity.
              $debug && watchdog('node_registration', 'no room left and no exceeding capacity');
              $reason = 'capacity';
            }
          }
          else {

            // Fail: invalid start or end date.
            $debug && watchdog('node_registration', 'registration date invalid: start date (@min_date) or end date (@max_date)', array(
              '@min_date' => $disallow_min_date_reason,
              '@max_date' => $disallow_max_date_reason,
            ));
            $reason = 'date';
          }
        }
        else {

          // Fail: registration not enabled.
          $debug && watchdog('node_registration', 'event registration not enabled');
          $reason = 'disabled';
        }
        break;
    }
    return FALSE;
  }, function ($result, $cache_key) use ($registration, $op, $account, &$reason) {

    // Allow other modules to override and change this result.
    $reason or $reason = $result ? '' : '?';
    $alt_access = module_invoke_all('node_registration_access', $registration, $op, $account, $reason);
    if (in_array(!$result, $alt_access, TRUE)) {
      return !$result;
    }
    return $result;
  });
  if ($reason) {
    $reasons[$cache_key] = $reason;
  }
  else {
    $reason = @$reasons[$cache_key];
  }
  return $access;
}

/**
 * Checks whether the given or global user has access to a registration by
 * checking ownership or the registration's secret.
 */
function _node_registration_secret_access($registration, $account = NULL) {
  global $user;
  $account or $account = $user;

  // Authenticated.
  if ($account->uid) {

    // User is author.
    if ($registration->uid == $account->uid) {
      return TRUE;
    }
  }
  else {

    // Anonymous registration with matching secret.
    if (!$registration->uid && isset($_GET['secret']) && $_GET['secret'] == $registration->secret) {
      return TRUE;
    }
  }
  return FALSE;
}

/**
 * Creates a secret (to save in the registration to identify anonymous registrations).
 */
function _node_registration_secret() {
  $secret = drupal_hash_base64(uniqid(mt_rand(), TRUE) . drupal_random_bytes(55));
  $secret = str_replace(array(
    '-',
    '_',
  ), '', $secret);
  $secret = substr($secret, 0, 40);
  return $secret;
}

/**
 * Retrieves existing registration for a certain user (in a certain event).
 */
function _node_registration_user_registered($node, $account = NULL) {
  global $user;
  $account or $account = $user;
  if ($account->uid) {
    $registrations = $registrations = node_registration_load_multiple(FALSE, array(
      'uid' => $account->uid,
      'nid' => $node->nid,
      'cancelled' => 0,
    ));
    if ($registrations) {
      return reset($registrations);
    }
  }
}

/**
 * Retrieves existing registration for a certain e-mail address (in a certain event).
 */
function _node_registration_email_registered($node, $email) {
  $registrations = node_registration_load_multiple(FALSE, array(
    'email' => $email,
    'nid' => $node->nid,
    'cancelled' => 0,
  ));
  if ($registrations) {
    $registration = reset($registrations);
    return $registration;
  }
}

/**
 * Return the number of registrations for a given node.
 */
function node_registration_event_count($node) {
  $nid = $node->nid;
  $count = db_query("SELECT sum(slots) FROM {node_registration} WHERE nid = ? AND cancelled = 0", array(
    $nid,
  ))
    ->fetchField();
  return (int) $count;
}

/**
 * Helper to determine if a node has any slots left.
 */
function _node_registration_event_has_room($node) {
  if ($node->registration
    ->enabled()) {
    $capacity = $node->registration
      ->capacity();
    $registrations = node_registration_event_count($node);
    return !$capacity || $capacity > $registrations;
  }
  return FALSE;
}

/**
 * Node types for which registration is enabled.
 */
function _node_registration_node_types() {
  $types = array();
  foreach (node_type_get_names() as $type => $name) {
    if (_node_registration_node_type_enabled($type)) {
      $types[$type] = $name;
    }
  }
  return $types;
}

/**
 * Change base registration settings for a given node/registration type.
 *
 * Only for `enabled` and date and capacity fields.
 */
function _node_registration_node_type_enable($type, $status, $settings) {
  $settings['status'] = (int) $status;
  return _node_registration_node_type_settings($type, $settings);
}

/**
 * Whether an event's registration is enabled.
 */
function _node_registration_node_type_enabled($type) {
  if ($enabled = _node_registration_node_type_settings($type)) {
    return $enabled->status;
  }
  return 0;
}

/**
 * Saves or retrieves registration type settings.
 */
function _node_registration_node_type_settings($type, $settings = array()) {
  $var_name = 'node_registration_type_settings_' . $type;
  if ($settings) {
    $settings = (array) $settings;
    $settings += variable_get($var_name, array());
    return variable_set($var_name, $settings);
  }
  $settings = variable_get($var_name, array()) + _node_registration_defaults();
  return (object) $settings;
}

Functions

Namesort descending Description
node_registration_access All Registration access callbacks. Just like node.module has node_access.
node_registration_event_count Return the number of registrations for a given node.
node_registration_node_access Implements hook_node_access().
node_registration_send_broadcast Send an email to all registrations for a given node.
_node_registration_bundles Returns all node registration bundles in the node_registration type.
_node_registration_default_from_name Default e-mail header From name.
_node_registration_email_registered Retrieves existing registration for a certain e-mail address (in a certain event).
_node_registration_event_has_room Helper to determine if a node has any slots left.
_node_registration_event_nodes Returns all event node objects (for certrain node types).
_node_registration_get_waitinglist The current, active waitinglist of an event.
_node_registration_node_types Node types for which registration is enabled.
_node_registration_node_type_enable Change base registration settings for a given node/registration type.
_node_registration_node_type_enabled Whether an event's registration is enabled.
_node_registration_node_type_settings Saves or retrieves registration type settings.
_node_registration_reminders_sent Saves or retrieves with events have been sent reminders.
_node_registration_request_is_ajax Helper: whether the current request is made with XHR.
_node_registration_reset_waitinglist Reset waitinglist flags for an event.
_node_registration_secret Creates a secret (to save in the registration to identify anonymous registrations).
_node_registration_secret_access Checks whether the given or global user has access to a registration by checking ownership or the registration's secret.
_node_registration_send_email Internal send e-mail function.
_node_registration_strtotime Calculates a time from a reference time and strotime offset.
_node_registration_strtotime_debug Returns a message for the strtotime format to show the user in admin forms.
_node_registration_strtotime_format Reformats strtotime format to all negatives (to be used in strtotime()).
_node_registration_type_to_uri Helper to keep URI strings DRY.
_node_registration_uri_to_type Helper to keep TYPE strings DRY.
_node_registration_user_registered Retrieves existing registration for a certain user (in a certain event).
_node_registration_waitinglist_size The size of an event's waitinglist. Always >= 0.