<?php

function date_api_set_variables() {
  // Set absolute minimum and maximum year for dates on this site.
  // There is actually no maximum and minimum year in PHP 5, but a date with
  // a year less than 0 would result in negative ISO and DATETIME dates,
  // like -1250-01-01T00:00:00, which probably won't make sense or work
  // correctly anywhere.
  // 
  // The odd construct of using variable_get() instead of variable_set()
  // is so we don't accidentally write over an existing value. If
  // no value is set, variable_get() will set it.
  variable_get('date_max_year', 4000);
  variable_get('date_min_year', 1);
  variable_get('date_php_min_year', 1901);

  // Set an API version in a way that other modules can test for compatibility.
  variable_set('date_api_version', '5.2');

  if (version_compare(PHP_VERSION, '5.2', '<') && !module_exists('date_php4')) {
    module_enable(array('date_php4'));
  }
  // The timezone module was originally going to be optional
  // but too many things break without it.
  if (!module_exists('date_timezone')) {
    module_enable(array('date_timezone'));
  }

  // NULL is used for the default setting of date_default_timezone_name
  // to have a way to tell that no site timezone name has been implemented.
  // Otherwise, many functions would use 'UTC' incorrectly and
  // produce unreliable and odd results. This way functions can test for a
  // value and not use this if it is empty.
  // 
  // The odd construct of using variable_get() instead of variable_set()
  // is so we don't accidentally write over an existing value. If
  // no value is set, variable_get() will set it to NULL.
  variable_get('date_default_timezone_name', NULL);
}

/**
 * Implementation of hook_schema().
 */
function date_api_schema() {
  $schema['date_format_types'] = array(
    'description' => 'For storing configured date format types.',
    'fields' => array(
      'type' => array(
        'description' => 'The date format type, e.g. medium.',
        'type' => 'varchar',
        'length' => 200,
        'not null' => TRUE,
      ),
      'title' => array(
        'description' => 'The human readable name of the format type.',
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
      ),
      'locked' => array(
        'description' => 'Whether or not this is a system provided format.',
        'type' => 'int',
        'size' => 'tiny',
        'default' => 0,
        'not null' => TRUE,
      ),
    ),
    'primary key' => array('type'),
  );

  $schema['date_formats'] = array(
    'description' => 'For storing configured date formats.',
    'fields' => array(
      'dfid' => array(
        'description' => 'The date format identifier.',
        'type' => 'serial',
        'not null' => TRUE,
        'unsigned' => TRUE,
      ),
      'format' => array(
        'description' => 'The date format string.',
        'type' => 'varchar',
        'length' => 100,
        'not null' => TRUE,
      ),
      'type' => array(
        'description' => 'The date format type, e.g. medium.',
        'type' => 'varchar',
        'length' => 200,
        'not null' => TRUE,
      ),
      'locked' => array(
        'description' => 'Whether or not this format can be modified.',
        'type' => 'int',
        'size' => 'tiny',
        'default' => 0,
        'not null' => TRUE,
      ),
    ),
    'primary key' => array('dfid'),
    'unique keys' => array('formats' => array('format', 'type')),
  );

  $schema['date_format_locale'] = array(
    'description' => 'For storing configured date formats for each locale.',
    'fields' => array(
      'format' => array(
        'description' => 'The date format string.',
        'type' => 'varchar',
        'length' => 100,
        'not null' => TRUE,
      ),
      'type' => array(
        'description' => 'The date format type, e.g. medium.',
        'type' => 'varchar',
        'length' => 200,
        'not null' => TRUE,
      ),
      'language' => array(
        'description' => 'A {languages}.language for this format to be used with.',
        'type' => 'varchar',
        'length' => 12,
        'not null' => TRUE,
      ),
    ),
    'primary key' => array('type', 'language'),
  );

  return $schema;
}

/**
 * Implementation of hook_schema_alter(). We alter $schema by reference.
 *
 * @param $schema
 * The system-wide schema collected by drupal_get_schema().
 */
function date_api_schema_alter(&$schema) {
  // Add field to existing schema.
  $schema['users']['fields']['timezone_name'] = array(
    'type' => 'varchar',
    'length' => 50,
    'not null' => TRUE,
    'default' => '',
    'description' => t('Per-user timezone name.'),
  );
}

/**
 * Implementation of hook_install().
 */
function date_api_install() {
  drupal_install_schema('date_api');

  // date_api_set_variables can install date_timezone and date_php4.  The
  // date_timezone_install() function does a module_enable('date_api').  This
  // means that date_api_enable() can be called before date_api_install()
  // finishes!  So the date_api schema needs to be installed before this line!
  date_api_set_variables();

  $ret = array();
  db_add_field($ret, "users", "timezone_name", array('type' => 'varchar', 'length' => 50, 'not null' => TRUE, 'default' => ''));

  // Make sure MYSQL does not stupidly do case-insensitive 
  // searches and indexes on our formats.
  // @see http://pure.rednoize.com/2006/11/26/mysql-collation-matters-when-using-unique-indexes/
  // @see http://jjinux.blogspot.com/2009/03/mysql-case-sensitivity-hell.html
  // @see http://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html
  global $db_type;
  if ($db_type == 'mysql' || $db_type == 'mysqli') {
    $sql = "ALTER TABLE {date_formats} CHANGE format format VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL";
    $ret[] = update_sql($sql);
    $sql = "ALTER TABLE {date_format_locale} CHANGE format format VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL";
    $ret[] = update_sql($sql);
  }

  return $ret;
}

/**
 * Implementation of hook_enable().
 */
function date_api_enable() {
  // When module is enabled, build the list of date formats and types.  This
  // includes those provided by this module and other contrib modules.  As the
  // date_format tables are created but the schema hasn't been updated, force
  // a refresh so we can use the schema API. 
  drupal_get_schema('', TRUE);
  // Ensure schema has been installed - order of things gets out of sync because
  // date_api_set_variables() in date_api_install() enables the 'date_timezone'
  // module, which in return enables the 'date_api' module!
  if (db_table_exists('date_format_types')) {
    date_formats_rebuild();
  }
  date_api_set_variables();

}

/**
 * Implementation of hook_uninstall().
 */
function date_api_uninstall() {
  $ret = array();
  db_drop_field($ret, "users", "timezone_name");

  cache_clear_all('date_timezone_identifiers_list', 'cache');
  $variables = array(
    'date_api_version',
    'date_min_year',
    'date_max_year',
    'date_php_min_year',
    'date_db_tz_support',
    'date_api_use_iso8601',
    );
  foreach ($variables as $variable) {
    variable_del($variable);
  }

  if (db_table_exists('views_display')) {
    $displays = array(
      'date_nav', 
    );
    db_query("DELETE FROM {views_display} WHERE display_plugin IN ('". implode("','", $displays) ."')");
    db_query("DELETE FROM {cache_views}");
  }

  drupal_uninstall_schema('date_api');
  return $ret;
}

/**
 * Implementation of hook_requirements().
 * Make sure Date PHP4 is installed if running less than PHP 5.2.
 */
function date_api_requirements($phase) {
  $requirements = array();
  $t = get_t();
  switch ($phase) {
    case 'runtime':
      $tz_name = variable_get('date_default_timezone_name', NULL);
      $error = FALSE;
      if (version_compare(PHP_VERSION, '5.2', '<') && !module_exists('date_php4')) {
        $error = TRUE;
        $severity = REQUIREMENT_ERROR;
        $value = $t('The Date API module requires the <a href="@link">Date PHP4 module</a> for PHP versions less than 5.2.', array('@link' => url('admin/build/modules')));
      }
      if ($error) {
        $requirements['date_php4'] = array(
          'title' => $t('Date API requirements'),
          'value' => $value,
          'severity' => $severity,
          );
      }
      break;
     case 'install':
      break;
  }
  return $requirements;
}

function date_api_update_last_removed() {
  return 5201;
}

/**
 * Make sure all the appropriate modules get enabled.
 * Repeated again just to be sure they are set.
 */
function date_api_update_6000() {
  $ret = array();
  // don't attempt to upgrade if views is not yet upgraded.
  if (module_exists('views') && drupal_get_installed_schema_version('views', TRUE) < 6000) {
    $ret = array();
    drupal_set_message(t('date module cannot be updated until after Views has been updated. Please return to <a href="@update-php">update.php</a> and run the remaining updates.', array('@update-php' => base_path() .'update.php?op=selection')), 'warning', FALSE);
    $ret['#abort'] = array('success' => FALSE, 'query' => t('date.module has updates, but cannot be updated until views.module is updated first.'));

    return $ret;
  }
  date_api_set_variables();
  return $ret;
}

/**
 * Rebuild the theme registry and all the caches.
 * needed to pick up changes created by updated Views API.
 */
function date_api_update_6001() {
  $ret = array();
  // don't attempt to upgrade if views is not yet upgraded.
  if (module_exists('views') && drupal_get_installed_schema_version('views', TRUE) < 6000) {
    $ret = array();
    drupal_set_message(t('date module cannot be updated until after Views has been updated. Please return to <a href="@update-php">update.php</a> and run the remaining updates.', array('@update-php' => base_path() .'update.php?op=selection')), 'warning', FALSE);
    $ret['#abort'] = array('success' => FALSE, 'query' => t('date.module has updates, but cannot be updated until views.module is updated first.'));

    return $ret;
  }
  if (db_table_exists('cache_content')) {
    db_query('DELETE FROM {cache_content}');
  }
  if (db_table_exists('cache_views')) {
    db_query('DELETE FROM {cache_views}');
  }
  if (db_table_exists('views_object_cache')) {
    db_query('DELETE FROM {views_object_cache}');
  }    
  db_query("DELETE FROM {cache} where cid LIKE 'theme_registry%'");
  return $ret;
}

/**
 * Create new date format tables.
 */
function date_api_update_6002() {
  $ret = array();
  // don't attempt to upgrade if views is not yet upgraded.
  if (module_exists('views') && drupal_get_installed_schema_version('views', TRUE) < 6000) {
    $ret = array();
    drupal_set_message(t('date module cannot be updated until after Views has been updated. Please return to <a href="@update-php">update.php</a> and run the remaining updates.', array('@update-php' => base_path() .'update.php?op=selection')), 'warning', FALSE);
    $ret['#abort'] = array('success' => FALSE, 'query' => t('date.module has updates, but cannot be updated until views.module is updated first.'));

    return $ret;
  }

  $schema['date_format_types'] = array(
    'fields' => array(
      'type' => array(
        'type' => 'varchar',
        'length' => 200,
        'not null' => TRUE,
      ),
      'title' => array(
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
      ),
      'locked' => array(
        'type' => 'int',
        'size' => 'tiny',
        'default' => 0,
        'not null' => TRUE,
      ),
    ),
    'primary key' => array('type'),
  );

  $schema['date_format'] = array(
    'fields' => array(
      'dfid' => array(
        'type' => 'serial',
        'not null' => TRUE,
        'unsigned' => TRUE,
      ),
      'format' => array(
        'type' => 'varchar',
        'length' => 100,
        'not null' => TRUE,
      ),
      'type' => array(
        'type' => 'varchar',
        'length' => 200,
        'not null' => TRUE,
      ),
      'locked' => array(
        'type' => 'int',
        'size' => 'tiny',
        'default' => 0,
        'not null' => TRUE,
      ),
    ),
    'primary key' => array('dfid'),
    'unique keys' => array('format' => array('format', 'type')),
  );

  $schema['date_format_locale'] = array(
    'fields' => array(
      'format' => array(
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
      ),
      'type' => array(
        'type' => 'varchar',
        'length' => 200,
        'not null' => TRUE,
      ),
      'language' => array(
        'type' => 'varchar',
        'length' => 12,
        'not null' => TRUE,
      ),
    ),
    'primary key' => array('type', 'language'),
  );

  db_create_table($ret, 'date_format_types', $schema['date_format_types']);
  db_create_table($ret, 'date_format', $schema['date_format']);
  db_create_table($ret, 'date_format_locale', $schema['date_format_locale']);

  return $ret;
}

function date_api_update_6003() {
  $ret = array();
  db_change_field($ret, 'date_format_types', 'type', 'type', array('type' => 'varchar', 'length' => 200, 'not null' => TRUE));
  db_change_field($ret, 'date_format', 'type', 'type', array('type' => 'varchar', 'length' => 200, 'not null' => TRUE));
  db_change_field($ret, 'date_format', 'format', 'format', array('type' => 'varchar', 'length' => 100, 'not null' => TRUE));
  db_change_field($ret, 'date_format_locale', 'type', 'type', array('type' => 'varchar', 'length' => 200, 'not null' => TRUE));
  db_change_field($ret, 'date_format_locale', 'format', 'format', array('type' => 'varchar', 'length' => 100, 'not null' => TRUE));
  db_drop_unique_key($ret, 'date_format', 'format');
  db_add_unique_key($ret, 'date_format', 'format', array('format', 'type'));
  return $ret;
}

/**
 * The "date_format" table is missing on boxes having MySQL 5.0.67 installed.
 * There seems to be a bug in MySQL that prevents the creation of tables with
 * a name "date_format" and indexes with the name "format".
 * 
 * We rename the table and index as a workaround.
 */
function date_api_update_6004() {
  $ret = array();

  $schema['date_formats'] = array(
    'description' => 'For storing configured date formats.',
    'fields' => array(
      'dfid' => array(
        'description' => 'The date format identifier.',
        'type' => 'serial',
        'not null' => TRUE,
        'unsigned' => TRUE,
      ),
      'format' => array(
        'description' => 'The date format string.',
        'type' => 'varchar',
        'length' => 100,
        'not null' => TRUE,
      ),
      'type' => array(
        'description' => 'The date format type, e.g. medium.',
        'type' => 'varchar',
        'length' => 200,
        'not null' => TRUE,
      ),
      'locked' => array(
        'description' => 'Whether or not this format can be modified.',
        'type' => 'int',
        'size' => 'tiny',
        'default' => 0,
        'not null' => TRUE,
      ),
    ),
    'primary key' => array('dfid'),
    'unique keys' => array('formats' => array('format', 'type')),
  );

  // Create missing table.
  if (!db_table_exists('date_format')) {
    db_create_table($ret, 'date_formats', $schema['date_formats']);
    date_formats_rebuild();
  }
  // Rename existing table and index.
  else {
    db_drop_unique_key($ret, 'date_format', 'format');
    if (db_table_exists('date_formats')) {
      db_drop_table($ret, 'date_format');
    } 
    else {
      db_rename_table($ret, 'date_format', 'date_formats');
      db_add_unique_key($ret, 'date_formats', 'formats', array('format', 'type'));
    }    
  }

  return $ret;  
}

/**
 * Make sure MYSQL does not stupidly do case-insensitive 
 * searches and indexes on our formats.
 * @see http://pure.rednoize.com/2006/11/26/mysql-collation-matters-when-using-unique-indexes/
 * @see http://jjinux.blogspot.com/2009/03/mysql-case-sensitivity-hell.html
 * @see http://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html
 */
function date_api_update_6005() {
  global $db_type;
  $ret = array();
  if ($db_type == 'mysql' || $db_type == 'mysqli') {
    $sql = "ALTER TABLE {date_formats} CHANGE format format VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL";
    $ret[] = update_sql($sql);
    $sql = "ALTER TABLE {date_format_locale} CHANGE format format VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL";
    $ret[] = update_sql($sql);
  }
  return $ret;
}

/**
 * Rename the date_format_dfid_seq to date_formats_dfid_seq, as this was missed in 6004
 * and causes inserts via the UI to fail on PostgreSQL.
 */
function date_api_update_6006() {
  global $db_type;
  $ret = array();
  if ($db_type == 'pgsql' && db_table_exists('date_format_dfid_seq')) {
    $sql = "ALTER SEQUENCE {date_format}_dfid_seq RENAME TO {date_formats}_dfid_seq";
    $ret[] = update_sql($sql);
  }
  return $ret;
}
