You are here

birthdays.module in Birthdays 5

Same filename and directory in other branches
  1. 6 birthdays.module
  2. 7 birthdays.module

The Birthdays module allows users to add their birthday to their profile. It lists birthdays on a seperate page and in different blocks. Users can receive an e-mail on their birthday automatically, and the administrator can receive daily reminders of who are having their birthday. Requires Profile Module.

File

birthdays.module
View source
<?php

/**
 * @file
 * The Birthdays module allows users to add their birthday to their
 * profile. It lists birthdays on a seperate page and in different
 * blocks. Users can receive an e-mail on their birthday automatically,
 * and the administrator can receive daily reminders of who are having
 * their birthday. Requires Profile Module.
 */

/**
 * @TODO - Fire events
 * @TODO - Add postcards
 * @TODO - Make visible to VIEWS module
 * @TODO - Regulate visibility of birthdays, birthday blocks, etc
 *   by means of the settings of the profile field
 * @TODO - Make optional size & frequency:
 *   Once a week (on first day of week) == 7 days in advance or Every day == only today
 * @TODO - Make POT file(s)
 * @TODO - Proper DOCSYS commenting of functions
 */
global $_birthdays_field;

/**
 * Admin e-mails disabled. Default.
 */
define('BIRTHDAYS_ADMIN_MAIL_DISABLED', '0');

/**
 * Admin e-mails should be sent dayly.
 */
define('BIRTHDAYS_ADMIN_MAIL_DAILY', '1');

/**
 * Admin e-mails should be sent weekly, on the first day of the week defined
 * by 'admin/settings/date-time'.
 */
define('BIRTHDAYS_ADMIN_MAIL_WEEKLY', '2');

/**
 * Admin e-mails should be sent monthly, on the first day of the month.
 */
define('BIRTHDAYS_ADMIN_MAIL_MONTHLY', '3');

/**
 * Do not show starsigns. Default.
 */
define('BIRTHDAYS_STARSIGN_OFF', '0');

/**
 * Show starsigns, with link to Yahoo.
 */
define('BIRTHDAYS_STARSIGN_LINK', '1');

/**
 * Show starsigns, image only.
 */
define('BIRTHDAYS_STARSIGN_NOLINK', '2');

/**
 * Do not hide the year of birth and age of users. This goes for all pages
 * generated by the Birthdays module. Default.
 */
define('BIRTHDAYS_HIDE_YEAR_NO', '0');

/**
 * Hide the year of birth and age of users. This goes for all pages
 * generated by the Birthdays module.
 */
define('BIRTHDAYS_HIDE_YEAR_YES', '1');

/**
 * Hiding or showing the year of birth and age is up to the user. This goes
 * for all pages generated by the Birthdays module.
 */
define('BIRTHDAYS_HIDE_YEAR_USER', '2');

/**
 * User does not want the birth year and age to be hidden. Used when
 * hiding the year is an user option.
 */
define('BIRTHDAYS_HIDE_YEAR_USER_NO', '0');

/**
 * User wants birth year and age to be hidden. Used when
 * hiding the year is an user option.
 */
define('BIRTHDAYS_HIDE_YEAR_USER_YES', '1');

/**
 * Do not show users without a birthday on the Birthdays listing
 * and sort by birthday. Default.
 */
define('BIRTHDAYS_PAGE_FILTER_SORT_DATE', '0');

/**
 * Do not show users without a birthday on the Birthdays listing
 * and sort by username.
 */
define('BIRTHDAYS_PAGE_FILTER_SORT_USER', '1');

/**
 * Show all users on the Birthdays listing and sort by username.
 */
define('BIRTHDAYS_PAGE_NOFILTER_SORT_USER', '2');

/**
 * Do not send an e-mail to the user on their birthday. Default.
 */
define('BIRTHDAYS_USER_MAIL_NO', '0');

/**
 * Send an e-mail to the user on their birthday.
 */
define('BIRTHDAYS_USER_MAIL_YES', '1');

/**
 * Sending an e-mail to the user depends on that user's preference.
 */
define('BIRTHDAYS_USER_MAIL_USER', '2');

/**
 * User doesn't want to be e-mailed on their birthday.
 */
define('BIRTHDAYS_USER_MAIL_USER_NO', '1');

/**
 * User wants to be e-mailed on their birthday.
 */
define('BIRTHDAYS_USER_MAIL_USER_YES', '0');

/**
 * Implementation of hook_help().
 */
function birthdays_help($section = '') {
  $output = '';
  switch ($section) {
    case 'admin/help#birthdays':
      $output = t("Allows users to store their birthdays and displays blocks of upcoming birthdays. Sends out e-mail on user's birthday automatically as cron job. Also sends admin reminder e-mails of upcoming user's birthdays. Most functionality is configurable in the admin settings and user settings. Requires profile.module and a present date field dedicated to the birthdays.");
      break;
  }
  return $output;
}

/**
 * Implementation of hook_menu().
 */
function birthdays_menu($may_cache) {
  $items = array();
  if ($may_cache) {
    $items[] = array(
      'path' => 'admin/settings/birthdays',
      'title' => t('Birthdays'),
      'description' => t('Set user birthday mail and toggle user mail, upcoming birthdays mail and more.'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'birthdays_admin_settings',
      ),
      'access' => user_access('administer site configuration'),
      'type' => MENU_NORMAL_ITEM,
    );
    $items[] = array(
      'path' => 'admin/settings/birthdays/settings',
      'title' => t('Settings'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'birthdays_admin_settings',
      ),
      'access' => user_access('administer site configuration'),
      'type' => MENU_DEFAULT_LOCAL_TASK,
    );
    $items[] = array(
      'path' => 'admin/settings/birthdays/sync',
      'title' => t('Synchronize'),
      'description' => t('Synchronize birthdays information of Profile module and Birthdays module. Used either when updating to a newer version of Birthdays or when integrating with an existing Profile Field.'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        'birthdays_sync_form',
      ),
      'access' => user_access('administer site configuration'),
      'type' => MENU_LOCAL_TASK,
    );
    $items[] = array(
      'path' => 'birthdays',
      'title' => t('Birthdays'),
      'description' => t('List the birthdays of all users.'),
      'callback' => 'birthdays_view_page',
      'access' => user_access('access birthdays'),
      'type' => MENU_SUGGESTED_ITEM,
    );
  }
  return $items;
}
function birthdays_init() {
  global $_birthdays_field;
  $_birthdays_field = _birthdays_get_field(variable_get('birthdays_field_id', NULL));
}

/**
 * Implementation of hook_form().
 */
function birthdays_sync_form() {
  global $_birthdays_field;
  if (!isset($_birthdays_field)) {
    drupal_set_message(t('No date field has been selected as birthdays field. Please visit the <a href="@birthdays-settings">birthdays settings page</a>.', array(
      '@birthdays-settings' => url('admin/settings/birthdays'),
    )), 'warning');
    return array();
  }
  $form['profile_fieldset'] = array(
    '#type' => 'fieldset',
  );
  $form['profile_fieldset']['description'] = array(
    '#type' => 'item',
    '#title' => t('Profile to Birthdays'),
    '#value' => t('Fill the Birthdays module\'s table with data from the Profile module\'s table. This is needed when you (re-)installed the Birthdays module, but already collected birthdays with the Profile module (e.g. for age verification).'),
  );
  $form['profile_fieldset']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Copy Profile data to Birthdays module'),
  );
  $form['birthdays_fieldset'] = array(
    '#type' => 'fieldset',
  );
  $form['birthdays_fieldset']['description'] = array(
    '#type' => 'item',
    '#title' => t('Birthdays to Profile'),
    '#value' => t('Fill the Profile module\'s table with data from the Birthdays module\'s table. You can use this to copy the old data collected by a previous version of the Birthdays module after an update. It is only necessary when updating from a version of the Birthdays module which didn\'t use the Profile module.'),
  );
  $form['birthdays_fieldset']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Copy Birthdays data to Profile module'),
  );
  return $form;
}

/**
 * Implementation of hook_form_submit().
 */
function birthdays_sync_form_submit($form_id, $values) {
  global $_birthdays_field;
  if ($values['op'] == t('Copy Profile data to Birthdays module')) {
    $result = db_query('SELECT uid FROM {users}');
    while ($uid = db_fetch_object($result)) {
      $account = user_load(array(
        'uid' => $uid->uid,
      ));
      if ($account->{$_birthdays_field->name}) {
        user_save($account, array(
          $_birthdays_field->name => $account->{$_birthdays_field->name},
        ));
      }
    }
  }
  else {
    switch ($GLOBALS['db_type']) {
      case 'mysql':
      case 'mysqli':
        $result = db_query('SELECT {dob}.uid, MONTH({dob}.birthday) AS month, YEAR({dob}.birthday) AS year, DAYOFMONTH({dob}.birthday) AS day FROM {dob}');
        break;
      case 'pgsql':
        $result = db_query("SELECT uid, date_part('month',{dob}.birthday) AS month, date_part('year',{dob}.birthday) AS year, date_part('day',{dob}.birthday) AS day FROM {dob}");
        break;
    }
    while ($birthday = db_fetch_object($result)) {
      $account = user_load(array(
        'uid' => $birthday->uid,
      ));
      $dob = array(
        'day' => $birthday->day,
        'month' => $birthday->month,
        'year' => $birthday->year,
      );
      user_save($account, array(
        $_birthdays_field->name => $dob,
      ));
    }
  }
  drupal_set_message(t('Modules have been synchronized.'));
}

/**
 * Show birthdays page
 * @desc Callback for birthdays menu item to show a page which lists all users.
 * @return string
 *   Themed birthday listing of all users
 */
function birthdays_view_page() {
  global $_birthdays_field;

  // Nothing to see when the birthdays profile field hasn't been set yet.
  if (!isset($_birthdays_field)) {
    return NULL;
  }
  drupal_add_css(drupal_get_path('module', 'birthdays') . '/birthdays.css');
  if (isset($_REQUEST['birthdays_filter_month'])) {
    $filter_month = (int) $_REQUEST['birthdays_filter_month'];
  }
  if (isset($_REQUEST['birthdays_filter_year'])) {
    $filter_year = (int) $_REQUEST['birthdays_filter_year'];
  }
  $output = '';
  $filter = '';
  switch ($GLOBALS['db_type']) {
    case 'mysql':
    case 'mysqli':

      // Right join = filtering out
      $join = variable_get('birthdays_page_settings', BIRTHDAYS_PAGE_FILTER_SORT_DATE) != BIRTHDAYS_PAGE_NOFILTER_SORT_USER || !empty($filter_month) || !empty($filter_year) ? 'RIGHT JOIN' : 'LEFT JOIN';
      $sort = variable_get('birthdays_page_settings', BIRTHDAYS_PAGE_FILTER_SORT_DATE) == BIRTHDAYS_PAGE_FILTER_SORT_DATE || !empty($filter_month) || !empty($filter_year) ? 'MONTH({dob}.birthday), DAYOFMONTH({dob}.birthday), YEAR({dob}.birthday), ' : '';
      $filter .= !empty($filter_month) ? " AND MONTH({dob}.birthday) = " . $filter_month : '';
      $filter .= !empty($filter_year) ? " AND YEAR({dob}.birthday) = " . $filter_year : '';
      break;
    case 'pgsql':

      // Right join = filtering out
      $join = variable_get('birthdays_page_settings', BIRTHDAYS_PAGE_FILTER_SORT_DATE) != BIRTHDAYS_PAGE_NOFILTER_SORT_USER || !empty($filter_month) || !empty($filter_year) ? 'RIGHT JOIN' : 'LEFT JOIN';
      $sort = variable_get('birthdays_page_settings', BIRTHDAYS_PAGE_FILTER_SORT_DATE) == BIRTHDAYS_PAGE_FILTER_SORT_DATE || !empty($filter_month) || !empty($filter_year) ? "date_part('month', {dob}.birthday), date_part('day', {dob}.birthday), date_part('year', {dob}.birthday), " : '';
      $filter .= !empty($filter_month) ? " AND date_part('month', {dob}.birthday) = " . $filter_month : '';
      $filter .= !empty($filter_year) ? " AND date_part('year', {dob}.birthday) = " . $filter_year : '';
      break;
  }

  // Select all users (but ignore blocked and never logged in ones)
  $result = pager_query("SELECT {users}.uid FROM {users} " . $join . " {dob} ON {users}.uid = {dob}.uid  WHERE {users}.status <> 0 AND {users}.access <> 0" . $filter . " ORDER BY " . $sort . "{users}.name", variable_get('birthdays_page_list_number', 25));
  while ($uid = db_fetch_object($result)) {

    // load the user objects
    $account = user_load(array(
      'uid' => $uid->uid,
    ));
    if (empty($filter_year) || !$account->birthdays_user_hide_year) {
      $accounts[] = $account;
    }
  }

  // Call theme_birthdays_page
  $output .= theme('birthdays_page', $accounts, $filter_month, $filter_year);

  // Get the filter form
  return $output;
}

/**
 * Return a form containing a select box to filter users by month of birth
 *
 * @param $filter_month
 * @param int $filter_year
 * @return array of the form
 */
function birthdays_page_filter($filter_month = NULL, $filter_year = NULL) {
  $options_months[0] = '';
  for ($i = 1; $i <= 12; $i++) {
    $options_months[$i] = format_date(gmmktime(0, 0, 0, $i, 2, 1970), 'custom', 'F', 0);
  }
  $options_years[0] = '';
  $options_years = $options_years + drupal_map_assoc(range(date('Y'), 1900));
  $form['#prefix'] = '<div class="container-inline birthdays-filter">';
  $form['#suffix'] = "</div>\n";
  $form['birthdays_filter_month'] = array(
    '#type' => 'select',
    '#options' => $options_months,
    '#default_value' => $filter_month,
    '#title' => t('Filter by month'),
    '#attributes' => array(
      'onchange' => 'submit()',
    ),
  );
  if (variable_get('birthdays_hide_year', BIRTHDAYS_HIDE_YEAR_NO) != BIRTHDAYS_HIDE_YEAR_YES) {
    $form['birthdays_filter_year'] = array(
      '#type' => 'select',
      '#options' => $options_years,
      '#default_value' => $filter_year,
      '#title' => t('Year'),
      '#attributes' => array(
        'onchange' => 'submit()',
      ),
    );
  }
  $form['button'] = array(
    '#type' => 'button',
    '#prefix' => '<noscript>',
    '#value' => t('Filter'),
    '#suffix' => '</noscript>',
  );
  return $form;
}

/**
 * @desc Theme a birthdays page
 * @var $accounts array of loaded user objects
 */
function theme_birthdays_page($accounts = array(), $filter_month, $filter_year) {
  global $_birthdays_field;

  // $header contains the column headers
  $header = array(
    t('User'),
    t('Birthday'),
  );
  if (variable_get('birthdays_hide_year', BIRTHDAYS_HIDE_YEAR_NO) != BIRTHDAYS_HIDE_YEAR_YES) {
    $header[] = t('Age');
  }
  if (variable_get('birthdays_show_starsign', BIRTHDAYS_STARSIGN_OFF)) {
    $header[] = t('Starsign');
  }
  if (variable_get('user_pictures', false)) {
    $header[] = '&nbsp;';
  }

  // $rows contains all cells
  $rows = array();
  $accounts = is_array($accounts) ? $accounts : array();
  $i = 0;

  // For each user
  foreach ($accounts as $account) {

    //Theme username (with link and such) and format date field.
    $rows[$i] = array(
      theme('username', $account),
      _birthdays_show_date($account->{$_birthdays_field->name}, $account),
    );
    if (variable_get('birthdays_hide_year', BIRTHDAYS_HIDE_YEAR_NO) != BIRTHDAYS_HIDE_YEAR_YES) {
      $age = _birthdays_show_age($account);
      $rows[$i][] = isset($age) ? $age : '';
    }
    if (variable_get('birthdays_show_starsign', BIRTHDAYS_STARSIGN_OFF)) {
      $rows[$i][] = birthdays_get_starsign_image($account->birthdays_starsign, variable_get('birthdays_show_starsign', BIRTHDAYS_STARSIGN_OFF));
    }
    if (variable_get('user_pictures', false)) {
      $rows[$i][] = theme_user_picture($account);
    }
    $i++;
  }
  $output = '';

  // Return a table. Everthing else (titles, and such) is handled by the page system.
  if (variable_get('birthdays_page_show_filters', 1)) {
    $output .= drupal_get_form('birthdays_page_filter', $filter_month, $filter_year);
  }
  $output .= theme('table', $header, $rows);
  $output .= theme('pager', NULL, variable_get('birthdays_page_list_number', 25), 0);
  return $output;
}

/**
 * Implementation of hook_form().
 */
function birthdays_admin_settings() {

  // Get fields from the profile.module of the type 'date'.
  $options = _birthdays_get_date_fields();

  // If there aren't any 'date' fields, throw an error and return an empty form
  if (empty($options)) {
    drupal_set_message(t('No profile fields of type \'date\' were found, please <a href="@profile-settings-page">create one here</a>.', array(
      '@profile-settings-page' => url('admin/user/profile/add/date'),
    )), 'error');
    return array();
  }
  $form['birthdays_general_settings'] = array(
    '#type' => 'fieldset',
    '#title' => t('General settings'),
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
  );
  $options = array(
    '' => '',
  ) + $options;
  $form['birthdays_general_settings']['birthdays_field_id'] = array(
    '#type' => 'select',
    '#title' => t('Profile field'),
    '#default_value' => variable_get('birthdays_field_id', NULL),
    '#description' => t('Choose the profile field of type \'date\' you want to use as date of birth.'),
    '#options' => $options,
    '#required' => TRUE,
  );
  $form['birthdays_general_settings']['birthdays_show_starsign'] = array(
    '#type' => 'select',
    '#title' => t('Show star signs'),
    '#default_value' => variable_get('birthdays_show_starsign', BIRTHDAYS_STARSIGN_OFF),
    '#options' => array(
      BIRTHDAYS_STARSIGN_OFF => t('No'),
      BIRTHDAYS_STARSIGN_LINK => t('Yes, with link to Yahoo Astrology'),
      BIRTHDAYS_STARSIGN_NOLINK => t('Yes, without link to Yahoo Astrology'),
    ),
    '#description' => t('Select whether the star signs should be enabled.'),
  );
  $form['birthdays_general_settings']['birthdays_hide_year'] = array(
    '#type' => 'select',
    '#title' => t('Hide year and age'),
    '#default_value' => variable_get('birthdays_hide_year', BIRTHDAYS_HIDE_YEAR_NO),
    '#description' => t('Select whether the birth year and age should be shown.'),
    '#options' => array(
      BIRTHDAYS_HIDE_YEAR_NO => t('No'),
      BIRTHDAYS_HIDE_YEAR_YES => t('Yes'),
      BIRTHDAYS_HIDE_YEAR_USER => t('User optional, \'No\' by default'),
    ),
  );
  $form['birthdays_page'] = array(
    '#type' => 'fieldset',
    '#title' => t('Birthdays page settings'),
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
  );
  $form['birthdays_page']['birthdays_page_settings'] = array(
    '#type' => 'select',
    '#title' => t('Set birthdays page settings'),
    '#default_value' => variable_get('birthdays_page_settings', BIRTHDAYS_PAGE_FILTER_SORT_DATE),
    '#description' => t('Select whether users that haven\'t entered their date of birth should be shown on the birthdays page, and whether the list should be ordered by birthday or by username.'),
    '#options' => array(
      BIRTHDAYS_PAGE_FILTER_SORT_DATE => t('Filter users, sort by date'),
      BIRTHDAYS_PAGE_FILTER_SORT_USER => t('Filter users, sort by username'),
      BIRTHDAYS_PAGE_NOFILTER_SORT_USER => t('Do not filter users, sort by username'),
    ),
  );
  $form['birthdays_page']['birthdays_page_list_number'] = array(
    '#type' => 'textfield',
    '#title' => t('Birthdays page size'),
    '#default_value' => variable_get('birthdays_page_list_number', 25),
    '#description' => t('How many users should be listed on each page on the Birthdays listing?'),
    '#required' => TRUE,
  );
  $form['birthdays_page']['birthdays_page_show_filters'] = array(
    '#type' => 'select',
    '#title' => t('Show filter options'),
    '#default_value' => variable_get('birthdays_page_show_filters', 1),
    '#description' => t('Show the "Filter by month and year" selections on the birthdays page. When "Hide year" has been set to "Yes", the year filter isn\'t shown.'),
    '#options' => array(
      0 => t('No'),
      1 => t('Yes'),
    ),
  );

  // Fieldset for e-mails.
  $form['birthdays_email'] = array(
    '#type' => 'fieldset',
    '#title' => t('E-mail settings'),
    '#description' => t('Adjust the e-mail settings for both e-mails to users and e-mails to the administrator. This requires cron jobs to be active. Please see the <a href="@cron_url">online handbook</a> for more information on how to accomplish that.', array(
      '@cron_url' => 'http://drupal.org/cron',
    )),
    '#collapsible' => TRUE,
    '#collapsed' => FALSE,
  );

  // Fieldset for Admin E-mails
  $form['birthdays_email']['admin'] = array(
    '#type' => 'fieldset',
    '#title' => t('Admin'),
    '#collapsible' => TRUE,
    '#collapsed' => variable_get('birthdays_remind', BIRTHDAYS_ADMIN_MAIL_DISABLED) == BIRTHDAYS_ADMIN_MAIL_DISABLED,
  );
  $days = array(
    0 => t('sunday'),
    1 => t('monday'),
    2 => t('tuesday'),
    3 => t('wednesday'),
    4 => t('thursday'),
    5 => t('friday'),
    6 => t('saturday'),
  );
  $form['birthdays_email']['admin']['birthdays_remind'] = array(
    '#type' => 'select',
    '#title' => t('Send upcoming birthdays to admin'),
    '#default_value' => variable_get('birthdays_remind', BIRTHDAYS_ADMIN_MAIL_DISABLED),
    '#options' => array(
      BIRTHDAYS_ADMIN_MAIL_DISABLED => t('Disabled'),
      BIRTHDAYS_ADMIN_MAIL_DAILY => t('Daily'),
      BIRTHDAYS_ADMIN_MAIL_WEEKLY => t('Weekly, on @first_weekday', array(
        '@first_weekday' => $days[variable_get('date_first_day', 0)],
      )),
      BIRTHDAYS_ADMIN_MAIL_MONTHLY => t('Monthly'),
    ),
    '#description' => t('Do you want a daily e-mail containing all upcoming birthdays?'),
  );

  // Fieldset for User E-mails
  $form['birthdays_email']['users'] = array(
    '#type' => 'fieldset',
    '#title' => t('User'),
    '#collapsible' => TRUE,
    '#collapsed' => variable_get('birthdays_send_user', BIRTHDAYS_USER_MAIL_NO) == BIRTHDAYS_USER_MAIL_NO,
  );
  $form['birthdays_email']['users']['birthdays_send_user'] = array(
    '#type' => 'select',
    '#title' => t('Send user e-mail on day of birth'),
    '#default_value' => variable_get('birthdays_send_user', BIRTHDAYS_USER_MAIL_NO),
    '#options' => array(
      BIRTHDAYS_USER_MAIL_NO => t('No'),
      BIRTHDAYS_USER_MAIL_YES => t('Yes'),
      BIRTHDAYS_USER_MAIL_USER => t('User optional, \'Yes\' by default'),
    ),
    '#description' => t('Should users that have their birthday today receive an e-mail?'),
  );
  $form['birthdays_email']['users']['birthdays_send_user_subject'] = array(
    '#type' => 'textfield',
    '#title' => t('Subject'),
    '#default_value' => variable_get('birthdays_send_user_subject', t('Happy Birthday!')),
  );
  $form['birthdays_email']['users']['birthdays_send_user_message'] = array(
    '#type' => 'textarea',
    '#title' => t('Message'),
    '#default_value' => variable_get('birthdays_send_user_message', t("Hey @name,\nHappy birthday!\nHope you have a great day!")),
    '#description' => t('@name will be replaced by the username.'),
  );
  return system_settings_form($form);
}

/**
 * Implementation of hook_perm().
 * Permissions:
 * - accesss birthday
 * - delete birthday
 */
function birthdays_perm() {
  return array(
    'access birthdays',
  );
}

/**
 * Implementation of hook_cron().
 */
function birthdays_cron() {
  global $_birthdays_field;

  // Either user mail or admin mail is activated, and the birthdays profile field has been set.
  if (isset($_birthdays_field) && (variable_get('birthdays_send_user', BIRTHDAYS_USER_MAIL_NO) != BIRTHDAYS_USER_MAIL_NO || variable_get('birthdays_remind', BIRTHDAYS_ADMIN_MAIL_DISABLED) != BIRTHDAYS_ADMIN_MAIL_DISABLED)) {

    // Perform the check just once a day
    $time = time();
    if (variable_get('birthdays_last_cron', 0) <= $time - 24 * 3600) {

      // Reset time limit, round to nearest 100 seconds.
      variable_set('birthdays_last_cron', floor($time / 100) * 100);
      $remind_frequency = variable_get('birthdays_remind', BIRTHDAYS_ADMIN_MAIL_DISABLED);

      // Send user e-mails.
      _birthdays_send_user_message();

      // Send admin message if frequency is daily.
      if ($remind_frequency == BIRTHDAYS_ADMIN_MAIL_DAILY) {
        _birthdays_send_admin_message(1);
      }
      elseif ($remind_frequency == BIRTHDAYS_ADMIN_MAIL_WEEKLY && date('w', $time) == variable_get('date_first_day', 0)) {
        _birthdays_send_admin_message(7);
      }
      elseif ($remind_frequency == BIRTHDAYS_ADMIN_MAIL_MONTHLY && date('j', $time) == 1) {
        _birthdays_send_admin_message(date('t', $time));
      }
    }
  }
}

/**
 * Implementation of hook_block
 */
function birthdays_block($op = 'list', $delta = 'by_days', $edit = array()) {
  global $_birthdays_field;

  // Nothing to do when the field hasn't been set yet.
  if (!isset($_birthdays_field)) {
    return;
  }
  switch ($op) {

    // List the blocks on the blocks settings page
    case 'list':
      $blocks['by_days']['info'] = t('Birthdays Block: Next N days');
      $blocks['by_birthdays']['info'] = t('Birthdays Block: N upcoming birthdays');
      return $blocks;

    // Configure the blocks
    case 'configure':
      switch ($delta) {
        case 'by_days':
          $form["birthdays_block_settings"] = array(
            '#type' => 'textfield',
            '#title' => t("Number of days to show"),
            '#default_value' => variable_get("birthdays_block_number_by_days", 7),
            '#size' => 2,
            '#maxlength' => 2,
            '#description' => t("Number of days looking forward for upcoming birthdays. Use 1 for today's birthdays only. Note: it might show more or less birthday items than the specified number of days, because not all days have birthdays, and some days have multiple birthdays."),
            '#required' => true,
          );
          $form['birthdays_block_hide'] = array(
            '#type' => 'radios',
            '#title' => t('Hide block when no birthdays'),
            '#default_value' => variable_get("birthdays_block_hide_empty", 0),
            '#options' => array(
              t('No'),
              t('Yes'),
            ),
            '#description' => t("Should the block be hidden when there are no upcoming birthdays, or should it show a message."),
          );
          return $form;
        case 'by_birthdays':
          $form["birthdays_block_settings"] = array(
            '#type' => 'textfield',
            '#title' => t("Number of birthdays to show"),
            '#default_value' => variable_get("birthdays_block_number_by_birthdays", 6),
            '#size' => 2,
            '#maxlength' => 2,
            '#description' => t("Number of upcoming birthdays to list in the block. It will show exactly the specified number of birthdays, even if more people have their birthday on the same day. In that case, there will be people who will never be shown."),
            '#required' => true,
          );
          $form['birthdays_block_hide'] = array(
            '#type' => 'radios',
            '#title' => t('Hide block when no birthdays'),
            '#default_value' => variable_get("birthdays_block_hide_empty", 0),
            '#options' => array(
              t('No'),
              t('Yes'),
            ),
            '#description' => t("Should the block be hidden when there are no upcoming birthdays, or should it show a message."),
          );
          return $form;
      }

    // Save the block's configuration
    case 'save':
      variable_set('birthdays_block_number_' . $delta, $edit['birthdays_block_settings']);
      variable_set('birthdays_block_hide_empty', $edit['birthdays_block_hide']);
      return;

    // View a block
    case 'view':
      $block = array();

      // Don't show anything when the current user doesn't have the rights.
      if (user_access('access birthdays')) {
        switch ($delta) {
          case 'by_days':

            // Get desired amount of birthdays
            $amount = variable_get("birthdays_block_number_by_days", 7);
            $birthdays = birthdays_get_birthdays_by_days($amount);
            if (count($birthdays) > 0 || variable_get('birthdays_block_hide_empty', 0) == 0) {

              // Prepare block
              $block['subject'] = t('Upcoming Birthdays');
              $block['content'] = theme('birthdays_block', $birthdays, $amount, $delta);
            }
            break;
          case 'by_birthdays':

            // Get desired amount of  birthdays
            $amount = variable_get("birthdays_block_number_by_birthdays", 6);
            $birthdays = birthdays_get_birthdays($amount);
            if (count($birthdays) > 0 || variable_get('birthdays_block_hide_empty', 0) == 0) {

              // Prepare block
              $block['subject'] = t('Upcoming Birthdays');
              $block['content'] = theme('birthdays_block', $birthdays, $amount, $delta);
            }
            break;
        }
        return $block;
      }
  }
}

/**
 * Theme function of the blocks
 *
 * @returns themed content
 */
function theme_birthdays_block($birthdays, $amount, $delta) {
  global $_birthdays_field;
  if (!empty($birthdays)) {
    $output = '<table>';
    foreach ($birthdays as $b) {
      $account = user_load(array(
        'uid' => $b,
      ));
      $age = _birthdays_show_age($account);

      // +1 when the birthday isn't today, because it shows the age the person will be on his/her birthday
      $age = isset($age) ? '(' . ($account->age + !($account->{$_birthdays_field->name}['day'] == format_date(time(), 'custom', 'j') && $account->{$_birthdays_field->name}['month'] == format_date(time(), 'custom', 'n'))) . ')' : '';
      $account->{$_birthdays_field->name}['year'] = NULL;

      // Don't show the year in blocks
      $output .= '<tr><td>' . theme('username', $account) . '&nbsp;<small>' . $age . '</small></td><td>' . _birthdays_show_date($account->{$_birthdays_field->name}, $account) . '</td></tr>';
    }
    $output .= '</table>';
  }
  else {
    $output = '<p>' . t('Nobody is having their birthday soon.') . '</p>';
  }
  $output .= '<div class="more-link">' . l(t('more'), 'birthdays', array(
    'title' => t('Show all birthdays.'),
  )) . '</div>';
  return $output;
}

/**
 *  Helper function for displaying only todays birthdays
 *  @return An array containing all user objects that have their birthday today
 */
function birthdays_get_todays_birthdays() {
  return birthdays_get_birthdays_by_days(1);
}

/**
 * Get all birthdays of the upcomming X days
 *
 * @var $amount
 *   Integer stating the amount of days to look forward, including today.
 * @return array
 *   An array containing user objects meeting the criteria
 */
function birthdays_get_birthdays_by_days($amount) {
  $birthdays = array();

  // Current user, needed for timezone information
  global $user;

  // $amount should be larger or equal to 1
  if ($amount < 1) {
    $amount = 1;
  }

  // Get user time zone
  // needed to determine what day 'today' is in the timezone of the user/website.
  if (variable_get('configurable_timezones', 1) && $user->uid && strlen($user->timezone)) {
    $timezone = $user->timezone;
  }
  else {

    // else use timezone of Drupal installation
    $timezone = variable_get('date_default_timezone', 0);
  }

  // MySQL prior to 4.1.1 has no option to use UTC, while drupal uses UTC.
  // This is compensated by subtracting the machines timezone from the Drupal timezone.
  // I believe the assumption is that the HTTP-server has the same timezone as the MySQL server.
  $timezone -= date('Z');

  // Hack to look further than the end of the year, if needed.
  $current_year = date('Y');
  $next_year = $current_year + 1;

  /* Query:
     - All dates are compensated for the timezone. This makes sure that someone in Asia will see the birthdays
       of day 2 while someone in America still sees day 1.
     - Blocked users are not shown
     - Users that haven't logged in yet are also not shown (Drupal prohibits accessing their profile, thus showing
       a link to the profile is unwanted). This is a anti spammers method.
     - First part selects all birthdays that are in the next year when the interval exceeds the end of this year.
     - Second part selects all birthdays that are between the begin and end date and are in the current year
     */
  switch ($GLOBALS['db_type']) {
    case 'mysql':
    case 'mysqli':
      $result = db_query("SELECT {dob}.uid FROM {dob}, {users} WHERE {users}.uid = {dob}.uid AND {users}.status <> 0 AND {users}.access <> 0\n        AND (\n        (\n          DATE_FORMAT({dob}.birthday,'{$next_year}%%m%%d') - DATE_FORMAT(ADDDATE(ADDDATE(NOW(),INTERVAL %d SECOND),INTERVAL %d DAY),'%%Y%%m%%d') < 0\n        )\n        OR\n        (\n          DATE_FORMAT({dob}.birthday,'{$current_year}%%m%%d') - DATE_FORMAT(ADDDATE(NOW(),INTERVAL %d SECOND),'%%Y%%m%%d') >= 0\n          AND\n          DATE_FORMAT({dob}.birthday,'{$current_year}%%m%%d') - DATE_FORMAT(ADDDATE(ADDDATE(NOW(),INTERVAL %d SECOND),INTERVAL %d DAY),'%%Y%%m%%d') < 0\n        ) )\n        ORDER BY MONTH({dob}.birthday), DAYOFMONTH({dob}.birthday), YEAR({dob}.birthday), {users}.name", $timezone, $amount, $timezone, $timezone, $amount);
      break;
    case 'pgsql':
      $result = db_query("SELECT {dob}.uid FROM {dob}, {users} WHERE {users}.uid = {dob}.uid AND {users}.status <> 0 AND {users}.access <> 0\n        AND (\n        (\n          cast(to_char({dob}.birthday,'{$next_year}MMDD') as integer) - cast(to_char(current_timestamp + INTERVAL '%d seconds' + INTERVAL '%d days','YYYYMMDD') as integer) < 0\n        )\n        OR\n        (\n          cast(to_char({dob}.birthday,'{$current_year}MMDD') as integer) - cast(to_char(current_timestamp + INTERVAL '%d seconds','YYYYMMDD') as integer) >= 0\n          AND\n          cast(to_char({dob}.birthday,'{$current_year}MMDD') as integer) - cast(to_char(current_timestamp + INTERVAL '%d seconds' + INTERVAL '%d days','YYYYMMDD') as integer) < 0\n        ) )\n        ORDER BY date_part('month', {dob}.birthday), date_part('day', {dob}.birthday), date_part('year', {dob}.birthday), {users}.name", $timezone, $amount, $timezone, $timezone, $amount);
      break;
  }
  while ($account = db_fetch_object($result)) {
    $birthdays[] = $account->uid;
  }

  // Return array of uids that have their birthday
  return $birthdays;
}

/**
 * Get the next X birthdays
 *
 * @var $amount
 *   Integer stating the amount of birthdays to retrieve.
 * @return array
 *   An array containing user objects meeting the criteria
 */
function birthdays_get_birthdays($amount) {
  $birthdays = array();

  // Current logged in user
  global $user;

  // Get user time zone
  // Needed to determine what day 'today' is in the timezone of the user/website.
  if (variable_get('configurable_timezones', 1) && $user->uid && strlen($user->timezone)) {
    $timezone = $user->timezone;
  }
  else {
    $timezone = variable_get('date_default_timezone', 0);
  }

  // $amount should be larger or equal to 1
  if ($amount < 1) {
    $amount = 1;
  }

  // MySQL prior to 4.1.1 has no option to use UTC, while drupal uses UTC.
  // This is compensated by subtracting the machines timezone from the Drupal timezone.
  // I believe the assumption is that the HTTP-server has the same timezone as the MySQL server.
  $timezone -= date('Z');

  /* Query:
      - Select all active users that have their birthday today or in the future (stops at 31-12)
      - return at most $amount users
      - Don't show blocked users
      - Users that haven't logged in yet are also not shown (Drupal prohibits accessing their profile, thus showing
        a link to the profile is unwanted). This is a anti spammers method.
    */
  switch ($GLOBALS['db_type']) {
    case 'mysql':
    case 'mysqli':
      $result = db_query_range("SELECT {dob}.uid FROM {dob}, {users} WHERE {users}.uid = {dob}.uid AND {users}.status <> 0 AND {users}.access <> 0\n        AND DATE_FORMAT({dob}.birthday,'%%c%%d') - DATE_FORMAT(ADDDATE(NOW(),INTERVAL %d SECOND),'%%c%%d') >= 0\n        ORDER BY MONTH({dob}.birthday), DAYOFMONTH({dob}.birthday), YEAR({dob}.birthday), {users}.name", $timezone, 0, $amount);
      break;
    case 'pgsql':
      $result = db_query_range("SELECT {dob}.uid FROM {dob}, {users} WHERE {users}.uid = {dob}.uid AND {users}.status <> 0 AND {users}.access <> 0\n        AND cast(to_char({dob}.birthday,'FMMMDD') as integer) - cast(to_char(current_timestamp + INTERVAL '%d seconds','FMMMDD') as integer) >= 0\n        ORDER BY date_part('month', {dob}.birthday), date_part('day', {dob}.birthday), date_part('year', {dob}.birthday), {users}.name", $timezone, 0, $amount);
      break;
  }
  while ($account = db_fetch_object($result)) {
    $birthdays[] = $account->uid;
  }

  // If less than $amount results returned, look for more after 31-12
  // return at most the difference between the number already found and
  if (db_num_rows($result) < $amount) {
    switch ($GLOBALS['db_type']) {
      case 'mysql':
      case 'mysqli':
        $result = db_query_range("SELECT {dob}.uid FROM {dob}, {users} WHERE {users}.uid = {dob}.uid AND {users}.status <> 0 AND {users}.access <> 0\n          AND DATE_FORMAT({dob}.birthday,'%%c%%d') - DATE_FORMAT(ADDDATE(NOW(),INTERVAL %d SECOND),'%%c%%d') < 0\n          ORDER BY MONTH({dob}.birthday), DAYOFMONTH({dob}.birthday), YEAR({dob}.birthday), {users}.name", $timezone, 0, $amount - db_num_rows($result));
        break;
      case 'pgsql':
        $result = db_query_range("SELECT {dob}.uid FROM {dob}, {users} WHERE {users}.uid = {dob}.uid AND {users}.status <> 0 AND {users}.access <> 0\n          AND cast(to_char({dob}.birthday,'FMMMDD') as integer) - cast(to_char(current_timestamp + INTERVAL '%d seconds','FMMMDD') as integer) < 0\n          ORDER BY date_part('month', {dob}.birthday), date_part('day', {dob}.birthday), date_part('year', {dob}.birthday), {users}.name", $timezone, 0, $amount - db_num_rows($result));
        break;
    }
    while ($account = db_fetch_object($result)) {
      $birthdays[] = $account->uid;
    }
  }

  // Return array of uids that have their birthday
  return $birthdays;
}

/**
 * Implementation of hook_user().
 */
function birthdays_user($op, &$edit, &$account, $category = NULL) {
  global $_birthdays_field;

  // Do nothing with user when birthdays_field_id is not yet set
  if (!isset($_birthdays_field)) {
    return;
  }
  switch ($op) {
    case 'load':
      return birthdays_load_user($account);
    case 'update':
    case 'insert':
      return birthdays_save_user($edit, $account, $category);
    case 'form':
      return birthdays_form_user($edit, $account, $category);
    case 'register':
      return birthdays_form_user($edit, $account, $category, TRUE);
    case 'delete':

      // Delete from {dob} table, other information is handled by profile.module and user.module
      db_query('DELETE FROM {dob} WHERE uid = %d', $account->uid);
  }
}

/**
 * Inject information on a user load
 *
 * @param object $account
 *   User object passed by reference
 */
function birthdays_load_user(&$account) {
  global $_birthdays_field;

  // Pre-load birthday-information into $account
  profile_load_profile($account);

  // If it was set by the user
  if ($account->{$_birthdays_field->name}) {

    // Set the user's age
    $account->age = _birthdays_get_age($account->{$_birthdays_field->name});
  }
}

/**
 * Inject information and save birthday when editing or adding a user
 */
function birthdays_save_user(&$edit, &$account, $category) {
  global $_birthdays_field;

  // Only continue when the field is present in the form results
  if (isset($_birthdays_field) && array_key_exists($_birthdays_field->name, $edit)) {

    // Extract the date information
    if (is_array($edit[$_birthdays_field->name])) {
      extract($edit[$_birthdays_field->name]);
    }

    // Delete the old
    db_query("DELETE FROM {dob} where uid = %d", $account->uid);
    if ($day && $year && $month) {

      // Set the starsign for the user.module to save in the {users}.data field
      $edit['birthdays_starsign'] = _birthdays_get_starsign($day, $month);

      // Insert the new
      db_query("INSERT INTO {dob} (uid, birthday) VALUES (%d, '%d-%d-%d');", $account->uid, $year, $month, $day);
    }
    else {
      unset($edit[$_birthdays_field->name]);
      $edit['birthdays_starsign'] = '';
    }
  }
}

/**
 * Alter the way the birthday is shown. This is fired after every module has filled the profile.
 */
function birthdays_profile_alter(&$account, &$fields) {
  global $_birthdays_field;

  // If the birthdays_field_id hasn't been set yet, do not continue
  if (!isset($_birthdays_field)) {
    return;
  }

  // If the field existed, and is amongst the profile fields to be shown, it is save to continue
  if (array_key_exists($_birthdays_field->category, $fields) && array_key_exists($_birthdays_field->name, $fields[$_birthdays_field->category])) {

    // Do you have access to see birthdays?
    if (user_access('access birthdays')) {

      // Show starsign (will be hidden when needed)
      $starsign = '<span class="birthdays-starsign">' . birthdays_get_starsign_image($account->birthdays_starsign, variable_get('birthdays_show_starsign', BIRTHDAYS_STARSIGN_OFF)) . '</span>&nbsp;&nbsp;&nbsp;';

      // Show age (when allowed by user and administrator)
      if (variable_get('birthdays_hide_year', BIRTHDAYS_HIDE_YEAR_NO) == BIRTHDAYS_HIDE_YEAR_NO || variable_get('birthdays_hide_year', BIRTHDAYS_HIDE_YEAR_NO) == BIRTHDAYS_HIDE_YEAR_USER && $account->birthdays_user_hide_year != BIRTHDAYS_HIDE_YEAR_USER_YES) {
        $age = '&nbsp;&nbsp;&nbsp;<span class="birthdays-age">(' . $account->age . ')</span>';
      }

      // Alter the profile field setup by profile.module. Show medium format in stead of short format
      $fields[$_birthdays_field->category][$_birthdays_field->name]['value'] = $starsign . _birthdays_show_date($account->{$_birthdays_field->name}, $account, 'medium') . $age;
    }
    else {

      // No access? Remove from profile to show
      unset($fields[$_birthdays_field->category][$_birthdays_field->name]);
    }
  }
}

/**
 * Adds user options to the profile form which are saved in {users}.data and
 * are loaded during a user_load().
 * @return fields for the form
 */
function birthdays_form_user($edit, $account, $category, $register = FALSE) {
  global $_birthdays_field;
  drupal_add_css(drupal_get_path('module', 'birthdays') . '/birthdays.css', 'module');

  // If the called category is the category of the form field
  if ($category == $_birthdays_field->category || $_birthdays_field->register && $register) {

    // If the hiding of the year is a user option: show the option
    if (variable_get('birthdays_hide_year', BIRTHDAYS_HIDE_YEAR_NO) == BIRTHDAYS_HIDE_YEAR_USER) {
      $form[$_birthdays_field->category][$_birthdays_field->name]['birthdays_user_hide_year'] = array(
        '#type' => 'checkbox',
        '#title' => t("Hide age and birth year"),
        '#default_value' => (int) $account->birthdays_user_hide_year,
        '#description' => t("Do not show your age and your year of birth."),
        '#return_value' => BIRTHDAYS_HIDE_YEAR_USER_YES,
        '#weight' => 1,
        '#prefix' => '<div class="birthdays-container">',
        '#suffix' => '</div>',
      );
    }

    // If the birthday user mail is optionally, show the option
    if (variable_get('birthdays_send_user', BIRTHDAYS_USER_MAIL_NO) == BIRTHDAYS_USER_MAIL_USER) {
      $form[$_birthdays_field->category][$_birthdays_field->name]['birthdays_user_send_mail'] = array(
        '#type' => 'checkbox',
        '#title' => t("Do not send birthday mail"),
        '#default_value' => (int) $account->birthdays_user_send_mail,
        '#description' => t("Do not send me an e-mail or e-card when it's my birthday."),
        '#return_value' => BIRTHDAYS_HIDE_YEAR_USER_NO,
        '#weight' => 2,
        '#prefix' => '<div class="birthdays-container">',
        '#suffix' => '</div>',
      );
    }
  }
  return $form;
}

/**
 * Implementation of hook_form_alter().
 */
function birthdays_form_alter($form_id, &$form) {
  global $_birthdays_field;
  if ($form_id == 'user_edit' && isset($form[$_birthdays_field->category]) || $form_id == 'user_register' && $_birthdays_field->register) {
    $form[$_birthdays_field->category][$_birthdays_field->name]['#process'] = array(
      'expand_birthdays_date' => array(),
    );
    $form[$_birthdays_field->category][$_birthdays_field->name]['#validate'] = array(
      'birthdays_date_validate' => array(),
    );
  }
}

/**
 * Process the birthday field (based on a regular date element) to
 * limit it to past birthdays and make it the entire element optional
 * by adding empty options for days, months and years.
 */
function expand_birthdays_date($element) {
  if (empty($element['#value'])) {
    $element['#value'] = array(
      'day' => '',
      'month' => '',
      'year' => '',
    );
  }
  $element = expand_date($element);
  $element['month']['#options'] = array(
    '' => '--',
  ) + $element['month']['#options'];
  $element['day']['#options'] = array(
    '' => '--',
  ) + $element['day']['#options'];
  $element['year']['#options'] = array(
    '' => '--',
  ) + drupal_map_assoc(range(date('Y'), 1900));
  return $element;
}

/**
 * Validate the birthday field.
 */
function birthdays_date_validate($element) {
  extract($element['#value']);
  if (empty($month) || empty($year) || empty($day)) {
    if ($element['#required']) {
      form_error($element, t('!name field is required.', array(
        '!name' => $element['#title'],
      )));
    }
    elseif (!(empty($month) && empty($year) && empty($day))) {
      form_error($element, t('The specified date is invalid.'));
    }
  }
  elseif (!checkdate($month, $day, $year)) {
    form_error($element, t('The specified date is invalid.'));
  }
}

/**
 * Sends e-mail to administrator once a day as reminder about the upcoming 7 days
 *
 * @todo Add frequency options (once a day, every week, monthly)
 */
function _birthdays_send_admin_message($days = 1) {
  global $_birthdays_field;

  // Only proceed when admin messages are enabled
  if ($frequency = variable_get('birthdays_remind', BIRTHDAYS_ADMIN_MAIL_DISABLED)) {

    // Select period string.
    switch ($frequency) {
      case BIRTHDAYS_ADMIN_MAIL_DAILY:
        $period = t('Today');
        break;
      case BIRTHDAYS_ADMIN_MAIL_WEEKLY:
        $period = t('This week');
        break;
      case BIRTHDAYS_ADMIN_MAIL_MONTHLY:
        $period = t('This month');
        break;
    }

    // Get birthdays
    $accounts = birthdays_get_birthdays_by_days($days);

    // Set message
    $message = t("@period, the following users are having their birthday:\n", array(
      '@period' => $period,
    ));

    // Build list of users
    foreach ($accounts as $uid) {
      $account = user_load(array(
        'uid' => $uid,
      ));

      // Don't show the year
      $account->{$_birthdays_field->name}['year'] = NULL;

      // Calculate the age to be.
      $age = $account->age + !($account->{$_birthdays_field->name}['day'] == format_date(time(), 'custom', 'j') && $account->{$_birthdays_field->name}['month'] == format_date(time(), 'custom', 'n'));

      // Set the message. In the case of daily messages, don't add the date.
      $message .= "\n" . ($frequency == BIRTHDAYS_ADMIN_MAIL_DAILY ? '' : _birthdays_show_date($account->{$_birthdays_field->name}, $account) . ': ') . $account->name . ' (' . $age . ')';
    }

    // If there were any users:
    if (count($accounts) > 0) {

      // Get site e-mail to send reminder to and from
      $from = variable_get('site_mail', ini_get('sendmail_from'));
      $to = $from;
      $subject = t("Upcoming Birthdays");

      // Send the mail
      drupal_mail('birthdays_admin_mail', $to, $subject, $message, $from);

      // Log action
      watchdog('Birthdays', t('Sent birthday overview e-mail to admin.'), WATCHDOG_NOTICE, '&#160;');
    }
  }
}

/**
 * Send all birthdays on this day a message
 *
 * @todo re-introduce the postcard module to make it fun
 */
function _birthdays_send_user_message() {

  // If messaging is enabled
  if (variable_get('birthdays_send_user', BIRTHDAYS_USER_MAIL_NO) > BIRTHDAYS_USER_MAIL_NO) {

    // Get all users having their birthday today
    $accounts = birthdays_get_todays_birthdays();

    // Get site address
    $from = variable_get('site_mail', ini_get('sendmail_from'));
    $subject = variable_get('birthdays_send_user_subject', t('Happy Birthday!'));
    $message = variable_get('birthdays_send_user_message', t("Hey @name,\nHappy birthday!\nHope you have a great day!"));
    foreach ($accounts as $uid) {

      // Load user
      $account = user_load(array(
        'uid' => $uid,
      ));

      // If user and/or administrator allow sending messages
      if (variable_get('birthdays_send_user', BIRTHDAYS_USER_MAIL_NO) == BIRTHDAYS_USER_MAIL_YES || variable_get('birthdays_send_user', BIRTHDAYS_USER_MAIL_NO) == 2 && $account->birthdays_user_send_mail != BIRTHDAYS_USER_MAIL_USER_NO) {

        // Replace @name in message by username
        $parsed_message = strtr($message, array(
          '@name' => check_plain($account->name),
        ));

        // Send mail
        drupal_mail('birthdays_user_message', $account->name . ' <' . $account->mail . '>', $subject, $parsed_message, $from);

        // Log actions
        watchdog('Birthdays', t('Sent @name a birthday e-mail.', array(
          '@name' => $account->name,
        )), WATCHDOG_NOTICE, '&#160;');
      }
    }
  }
}

/**
 * Get starsign based on date of birth
 *
 * @var $day and $month, decribing date of birth
 * @return The name of the starsign
 */
function _birthdays_get_starsign($day, $month) {
  switch ($month) {
    case 1:
      $starsign = $day < 20 ? 'capricorn' : 'aquarius';
      break;
    case 2:
      $starsign = $day < 19 ? 'aquarius' : 'pisces';
      break;
    case 3:
      $starsign = $day < 21 ? 'pisces' : 'aries';
      break;
    case 4:
      $starsign = $day < 20 ? 'aries' : 'taurus';
      break;
    case 5:
      $starsign = $day < 21 ? 'taurus' : 'gemini';
      break;
    case 6:
      $starsign = $day < 22 ? 'gemini' : 'cancer';
      break;
    case 7:
      $starsign = $day < 23 ? 'cancer' : 'leo';
      break;
    case 8:
      $starsign = $day < 23 ? 'leo' : 'virgo';
      break;
    case 9:
      $starsign = $day < 23 ? 'virgo' : 'libra';
      break;
    case 10:
      $starsign = $day < 23 ? 'libra' : 'scorpio';
      break;
    case 11:
      $starsign = $day < 23 ? 'scorpio' : 'sagittarius';
      break;
    case 12:
      $starsign = $day < 22 ? 'sagittarius' : 'capricorn';
      break;
  }
  return $starsign;
}

/**
 * Retrieve all fields of type 'date' from the profile.module's tables
 * @return array with fieldnames
 */
function _birthdays_get_date_fields() {
  $options = array();
  $result = db_query("SELECT fid, name FROM {profile_fields} WHERE type = 'date'");
  while ($field = db_fetch_object($result)) {
    $options[$field->fid] = $field->name;
  }
  return $options;
}

/**
 * Retrieve profile field object
 *
 * @var string $field
 *   name of field to retrieve
 * @return object
 *   profile field object
 */
function _birthdays_get_field($fid) {
  if (isset($fid)) {
    $field = db_fetch_object(db_query("SELECT * FROM {profile_fields} WHERE fid = %d", $fid));
    return empty($field) ? NULL : $field;
  }
  else {
    return NULL;
  }
}

/**
 * Return the picture of a starsign, given the name. Links to Yahoo! when option is selected.
 *
 * @param string starsign
 *   name of the starsign to show (not translated)
 * @param int $show
 *   Show starsigns for values > 0
 * @return string
 *   HTML of a picture link to Yahoo horoscopes
 */
function birthdays_get_starsign_image($starsign, $show = BIRTHDAYS_STARSIGN_OFF) {
  $output = '';

  // Only show starsign when enabled
  if ($show > BIRTHDAYS_STARSIGN_OFF && !empty($starsign)) {

    // Image based on thme path.
    $output = '<img src="' . base_path() . drupal_get_path('module', 'birthdays') . '/starsigns/' . $starsign . '.gif" alt="' . t(ucfirst($starsign)) . '" />';

    // If link should be shown: update $output
    if ($show == BIRTHDAYS_STARSIGN_LINK) {
      $output = '<a href="http://astrology.yahoo.com/astrology/general/dailyoverview/' . $starsign . '" target="_blank" title="' . t(ucfirst($starsign)) . '">' . $output . '</a>';
    }
  }

  // Return HTML
  return $output;
}

/**
 * Get age from a given date of birth
 *
 * @var array $date
 *   array containing fields with keys 'year', 'month' and 'day'
 * @return int
 *   age
 */
function _birthdays_get_age($date) {

  // If date is not empty
  if (is_array($date)) {

    // extract date
    extract($date);

    // call main age function (no overloading in PHP)
    return _birthdays_calculate_age($day, $month, $year);
  }
  else {
    return NULL;
  }
}

/**
 * Calculate age
 *
 * @var $year, $month, $day as expected
 * @return int
 *   age
 */
function _birthdays_calculate_age($day, $month, $year) {
  if ($year && $month && $day) {

    // age = (current year - birthyear) - 1 (when the birthday hasn't arrived yet).
    return format_date(time(), 'custom', 'Y') - $year - (format_date(time(), 'custom', 'nd') < $month . str_pad($day, 2, 0, STR_PAD_LEFT));
  }
  else {
    return NULL;
  }
}

/**
 * Return age if user has agreed to show it
 *
 * @param object $account
 *   a user object having attribute "age" set
 * @return int
 */
function _birthdays_show_age($account) {
  $age = NULL;
  if (isset($account->age) && (variable_get('birthdays_hide_year', BIRTHDAYS_HIDE_YEAR_NO) == BIRTHDAYS_HIDE_YEAR_NO || variable_get('birthdays_hide_year', BIRTHDAYS_HIDE_YEAR_NO) == BIRTHDAYS_HIDE_YEAR_USER && $account->birthdays_user_hide_year != BIRTHDAYS_HIDE_YEAR_USER_YES)) {
    $age = $account->age;
  }
  return $age;
}

/**
 * Format date array
 */
function _birthdays_show_date($date, $account, $format = 'small') {
  if (is_array($date)) {

    // Extract date
    extract($date);

    // Call main format function
    return _birthdays_show_date_2($day, $month, $year, $account, $format);
  }
  else {
    return NULL;
  }
}

/**
 * Format date, optionally hide year
 */
function _birthdays_show_date_2($day, $month, $year, $account, $type = 'small') {
  $output = '';

  // Determine format type
  switch ($type) {
    case 'medium':
      $format = variable_get('date_format_medium', 'D, m/d/Y - H:i');
      break;
    case 'small':
    default:
      $format = variable_get('date_format_short', 'm/d/Y - H:i');
  }

  // remove time from (- H:i)

  //$format = substr($format, 0, -6);

  // If admin or user decide to hide the age&year: hide year
  if ($year && (variable_get('birthdays_hide_year', BIRTHDAYS_HIDE_YEAR_NO) == BIRTHDAYS_HIDE_YEAR_YES || variable_get('birthdays_hide_year', BIRTHDAYS_HIDE_YEAR_NO) == BIRTHDAYS_HIDE_YEAR_USER && $account->birthdays_user_hide_year == BIRTHDAYS_HIDE_YEAR_USER_YES)) {
    $year = NULL;
  }

  // Replacement array (can't use date() because of 1970 limitations in e.g. Windows PHP4)
  $replace = array(
    'd' => sprintf('%02d', $day),
    'D' => NULL,
    'j' => $day,
    'm' => sprintf('%02d', $month),
    'M' => map_month($month),
    'Y' => $year,
    'H:i' => NULL,
    'G:i' => NULL,
    'g:ia' => NULL,
    'F' => t(gmdate('F', mktime(0, 0, 0, $month, 15, 2000))),
  );

  // Translate string to correct format
  $output .= strtr($format, $replace);
  $output = trim($output, '/ ,.:-');
  return $output;
}

// Dummy function to translate star signs.
function birthdays_potx() {
  $strings = array(
    t('Aquarius'),
    t('Pisces'),
    t('Aries'),
    t('Taurus'),
    t('Gemini'),
    t('Cancer'),
    t('Leo'),
    t('Virgo'),
    t('Libra'),
    t('Scorpio'),
    t('Sagittarius'),
    t('Capricorn'),
  );

  // No return value needed, since this is a dummy function.
}

Functions

Namesort descending Description
birthdays_admin_settings Implementation of hook_form().
birthdays_block Implementation of hook_block
birthdays_cron Implementation of hook_cron().
birthdays_date_validate Validate the birthday field.
birthdays_form_alter Implementation of hook_form_alter().
birthdays_form_user Adds user options to the profile form which are saved in {users}.data and are loaded during a user_load().
birthdays_get_birthdays Get the next X birthdays
birthdays_get_birthdays_by_days Get all birthdays of the upcomming X days
birthdays_get_starsign_image Return the picture of a starsign, given the name. Links to Yahoo! when option is selected.
birthdays_get_todays_birthdays Helper function for displaying only todays birthdays
birthdays_help Implementation of hook_help().
birthdays_init
birthdays_load_user Inject information on a user load
birthdays_menu Implementation of hook_menu().
birthdays_page_filter Return a form containing a select box to filter users by month of birth
birthdays_perm Implementation of hook_perm(). Permissions:
birthdays_potx
birthdays_profile_alter Alter the way the birthday is shown. This is fired after every module has filled the profile.
birthdays_save_user Inject information and save birthday when editing or adding a user
birthdays_sync_form Implementation of hook_form().
birthdays_sync_form_submit Implementation of hook_form_submit().
birthdays_user Implementation of hook_user().
birthdays_view_page Show birthdays page @desc Callback for birthdays menu item to show a page which lists all users.
expand_birthdays_date Process the birthday field (based on a regular date element) to limit it to past birthdays and make it the entire element optional by adding empty options for days, months and years.
theme_birthdays_block Theme function of the blocks
theme_birthdays_page @desc Theme a birthdays page
_birthdays_calculate_age Calculate age
_birthdays_get_age Get age from a given date of birth
_birthdays_get_date_fields Retrieve all fields of type 'date' from the profile.module's tables
_birthdays_get_field Retrieve profile field object
_birthdays_get_starsign Get starsign based on date of birth
_birthdays_send_admin_message Sends e-mail to administrator once a day as reminder about the upcoming 7 days
_birthdays_send_user_message Send all birthdays on this day a message
_birthdays_show_age Return age if user has agreed to show it
_birthdays_show_date Format date array
_birthdays_show_date_2 Format date, optionally hide year

Constants

Namesort descending Description
BIRTHDAYS_ADMIN_MAIL_DAILY Admin e-mails should be sent dayly.
BIRTHDAYS_ADMIN_MAIL_DISABLED Admin e-mails disabled. Default.
BIRTHDAYS_ADMIN_MAIL_MONTHLY Admin e-mails should be sent monthly, on the first day of the month.
BIRTHDAYS_ADMIN_MAIL_WEEKLY Admin e-mails should be sent weekly, on the first day of the week defined by 'admin/settings/date-time'.
BIRTHDAYS_HIDE_YEAR_NO Do not hide the year of birth and age of users. This goes for all pages generated by the Birthdays module. Default.
BIRTHDAYS_HIDE_YEAR_USER Hiding or showing the year of birth and age is up to the user. This goes for all pages generated by the Birthdays module.
BIRTHDAYS_HIDE_YEAR_USER_NO User does not want the birth year and age to be hidden. Used when hiding the year is an user option.
BIRTHDAYS_HIDE_YEAR_USER_YES User wants birth year and age to be hidden. Used when hiding the year is an user option.
BIRTHDAYS_HIDE_YEAR_YES Hide the year of birth and age of users. This goes for all pages generated by the Birthdays module.
BIRTHDAYS_PAGE_FILTER_SORT_DATE Do not show users without a birthday on the Birthdays listing and sort by birthday. Default.
BIRTHDAYS_PAGE_FILTER_SORT_USER Do not show users without a birthday on the Birthdays listing and sort by username.
BIRTHDAYS_PAGE_NOFILTER_SORT_USER Show all users on the Birthdays listing and sort by username.
BIRTHDAYS_STARSIGN_LINK Show starsigns, with link to Yahoo.
BIRTHDAYS_STARSIGN_NOLINK Show starsigns, image only.
BIRTHDAYS_STARSIGN_OFF Do not show starsigns. Default.
BIRTHDAYS_USER_MAIL_NO Do not send an e-mail to the user on their birthday. Default.
BIRTHDAYS_USER_MAIL_USER Sending an e-mail to the user depends on that user's preference.
BIRTHDAYS_USER_MAIL_USER_NO User doesn't want to be e-mailed on their birthday.
BIRTHDAYS_USER_MAIL_USER_YES User wants to be e-mailed on their birthday.
BIRTHDAYS_USER_MAIL_YES Send an e-mail to the user on their birthday.

Globals

Namesort descending Description
$_birthdays_field @TODO - Fire events @TODO - Add postcards @TODO - Make visible to VIEWS module @TODO - Regulate visibility of birthdays, birthday blocks, etc by means of the settings of the profile field @TODO - Make optional size & frequency: Once a week (on…