You are here

availability_calendars.install in Availability Calendars 7.2

Install, update and uninstall functions for the Availability Calendars module.

@author Dan Karran (geodaniel) <dan at karran dot net> @author Nicholas Alipaz (nicholas.alipaz) @author Erwin Derksen (http://drupal.org/user/750928)

File

availability_calendars.install
View source
<?php

/**
 * @file
 * Install, update and uninstall functions for the Availability Calendars module.
 *
 * @author Dan Karran (geodaniel) <dan at karran dot net>
 * @author Nicholas Alipaz (nicholas.alipaz)
 * @author Erwin Derksen (http://drupal.org/user/750928)
 */

/**
 * Implements hook_schema().
 * @see http://api.drupal.org/api/drupal/modules--system--system.api.php/function/hook_schema/7
 *
 * @todo?: http://drupal.org/node/111011: Add foreign keys to core
 */
function availability_calendars_schema() {
  $schema = array();
  $schema['availability_calendars_day'] = array(
    'description' => 'The table with availability states per day.',
    'fields' => array(
      'nid' => array(
        'description' => 'The primary identifier for a node.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ),
      // [#747036]: using date instead of separate fields allows for better querying
      // [#1083198]: Mysql, pgsql, mssql, and oracle support DATE type. Sqlite uses TEXT as type but has a lot of date functions
      // 'yyyy-mm-dd' (iso 8601) is accepted by mysql, pgsql, mssql, sqlite. For oracle I could not find this information.
      // The 'between' operator is inclusive on both sides on all databases: date between from and to  <=>  from <= date and date <= to
      'date' => array(
        'description' => 'Date of availability state.',
        'mysql_type' => 'DATE',
        'pgsql_type' => 'DATE',
        'sqlite_type' => 'TEXT',
        // SQLite does not have a DATE type
        'not null' => TRUE,
      ),
      'status' => array(
        'description' => 'The status.',
        'type' => 'varchar',
        'length' => 55,
      ),
    ),
    'primary key' => array(
      'nid',
      'date',
    ),
  );
  $schema['availability_calendars_week'] = array(
    'description' => 'The table for calendar week notes.',
    'fields' => array(
      'nid' => array(
        'description' => 'The primary identifier for a node.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ),
      'year' => array(
        'description' => 'The number of the year.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ),
      'month' => array(
        'description' => 'The number of the month.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ),
      'week' => array(
        'description' => 'The number of the week.',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ),
      'note' => array(
        'description' => 'The note.',
        'type' => 'varchar',
        'length' => 200,
      ),
    ),
    'primary key' => array(
      'nid',
      'year',
      'month',
      'week',
    ),
  );
  $schema['availability_calendars_states'] = array(
    'description' => 'Store classes and labels for the possible states in availability calendars',
    'fields' => array(
      // @todo: rename to css class in a next update (and remove the conversion code)
      'class' => array(
        'description' => 'The CSS class used for this state',
        'type' => 'varchar',
        'length' => 24,
        'not null' => TRUE,
      ),
      'label' => array(
        'description' => 'The label as displayed to users for this state',
        'type' => 'varchar',
        'length' => 64,
        // should not be too long: will give display problems
        'not null' => TRUE,
      ),
      'weight' => array(
        'type' => 'int',
        'not null' => TRUE,
        'default' => 0,
        'size' => 'tiny',
        'description' => 'The weight of this state',
      ),
      'is_available' => array(
        'type' => 'int',
        'not null' => TRUE,
        'default' => 0,
        'description' => 'Boolean indicating whether this state is to be treated as available',
      ),
    ),
    'primary key' => array(
      'class',
    ),
  );
  return $schema;
}

/**
 * Implements hook_install().
 * @see http://api.drupal.org/api/drupal/modules--system--system.api.php/function/hook_install/7
 */
function availability_calendars_install() {

  // Fill schema: add a default (starter, example) set of states to the database
  $states = array(
    array(
      'class' => 'calav',
      'label' => 'Available',
      'weight' => 1,
      'is_available' => 1,
    ),
    array(
      'class' => 'calna',
      'label' => 'Fully booked',
      'weight' => 2,
      'is_available' => 0,
    ),
    array(
      'class' => 'calopt',
      'label' => 'Provisionally booked',
      'weight' => 3,
      'is_available' => 0,
    ),
  );
  foreach ($states as $state) {
    db_insert('availability_calendars_states')
      ->fields($state)
      ->execute();
  }
  variable_set('availability_calendars_styles_generate', 1);
  $styles = array(
    'table' => array(
      'font-size' => 'smaller',
      'color' => '#000000',
      'background-color' => '',
      'border-width' => '1px',
      'border-color' => '#000000',
    ),
    'caption' => array(
      'font-weight' => 'bold',
      'font-style' => 'inherit',
      'font-size' => 'smaller',
    ),
    'header' => array(
      'height' => '',
      'font-weight' => 'bold',
      'font-style' => 'inherit',
      'font-size' => 'inherit',
      'text-align' => 'center',
    ),
    'week_notes' => array(
      'width' => '90px',
    ),
    'days' => array(
      'width' => '28px',
      'height' => '28px',
      'text-align' => 'center',
      'vertical-align' => 'middle',
    ),
    'states' => array(
      'split-day' => '/',
    ),
  );

  // Fill default states
  $styles['states']['calav'] = '#90ee90';
  $styles['states']['calna'] = '#ffb6c1';
  $styles['states']['calopt'] = '#ffffe0';
  variable_set('availability_calendars_styles', $styles);
  $link = l(st('Availability Calendars') . ' ' . st('Styling'), 'admin/config/content/availability-calendars/styling');
  drupal_set_message(st("Please visit the '!link' page to generate a CSS file.", array(
    '!link' => $link,
  )), 'warning');
}

/**
 * Utility function that is an altered version of variable_del, it will delete
 * a set of variables set by a module.
 *
 * @param string $name The variables' namespace for which to delete
 */
function availability_calendars_variable_del_all($name) {
  db_delete('variable')
    ->condition('name', addcslashes($name, '%_') . '%', 'LIKE')
    ->execute();
  cache_clear_all('variables', 'cache');
}

/**
 * Implements hook_uninstall().
 * @see http://api.drupal.org/api/drupal/modules--system--system.api.php/function/hook_uninstall/7
 */
function availability_calendars_uninstall() {
  availability_calendars_variable_del_all('availability_calendars_');
  file_unmanaged_delete_recursive('public://availability_calendars');
}

/**
 * Implements hook_update_last_removed().
 * @see http://api.drupal.org/api/drupal/modules--system--system.api.php/function/hook_update_last_removed/7
 *
 * Returns the highest removed N number for removed hook_update_N hooks.
 */
function availability_calendars_update_last_removed() {
  return 6103;
}

/**
 * Add custom states (issue #306461)
 */
function availability_calendars_update_6200(&$sandbox) {

  // Change type of field status of table availability_calendars_day,
  // as it (kind of) refers to the class field of the new states table
  db_change_field('availability_calendars_day', 'status', 'status', array(
    'type' => 'varchar',
    'length' => 64,
  ));

  // Add table to store configurable statuses
  $tables = availability_calendars_schema();

  //DRY: get table def from schema
  $table_name = 'availability_calendars_states';
  db_create_table($table_name, $tables[$table_name]);

  // Add existing (hard-coded) states to the database
  $states = array(
    array(
      'class' => 'calavailable',
      'label' => 'Available',
      'weight' => 1,
    ),
    array(
      'class' => 'calnotavailable',
      'label' => 'Fully booked',
      'weight' => 2,
    ),
    array(
      'class' => 'calnotavailableprov',
      'label' => 'Provisionally booked',
      'weight' => 3,
    ),
  );
  foreach ($states as $state) {
    db_insert('availability_calendars_states')
      ->fields($state)
      ->execute();
  }

  // Update split statuses: the old way of assembling them did not work any more with configurable states and classes
  $split_state_updates = array(
    'calsplit cal-available_notavailable' => 'calsplit cal-calavailable_calnotavailable',
    'calsplit cal-available_notavailableprov' => 'calsplit cal-calavailable_calnotavailableprov',
    'calsplit cal-notavailable_available' => 'calsplit cal-calnotavailable_calavailable',
    'calsplit cal-notavailable_notavailableprov' => 'calsplit cal-calnotavailable_calnotavailableprov',
    'calsplit cal-notavailableprov_available' => 'calsplit cal-calnotavailableprov_calavailable',
    'calsplit cal-notavailableprov_notavailable' => 'calsplit cal-calnotavailableprov_calnotavailable',
  );
  foreach ($split_state_updates as $state_update_old => $state_update_new) {
    db_update('availability_calendars_day')
      ->fields(array(
      'status' => $split_state_update_new,
    ))
      ->condition('status', $split_state_update_old)
      ->execute();
  }
}

/**
 * Allow to disable per node override (issue #764406)
 * On upgrades, the default should be set to 'on'.
 */
function availability_calendars_update_6201(&$sandbox) {
  variable_set('availability_calendars_settings_system_pernodeoverride', 1);
  return t('Update of Availability Calendars ran succesfully.');
}

/**
 * Allow to define custom colors using administration interface (issue #660502)
 * The upgrade should mimic the current availability_calendars.css as much as possible.
 */
function availability_calendars_update_6202(&$sandbox) {

  // Update statuses: shorten them
  $state_updates = array(
    'calavailable' => 'calav',
    'calnotavailable' => 'calna',
    'calnotavailableprov' => 'calopt',
  );
  $update_states_query = "UPDATE {availability_calendars_states} SET class = '%s' WHERE class = '%s'";
  $update_days_query = "UPDATE {availability_calendars_day} SET status = '%s' WHERE status = '%s'";
  $success = TRUE;
  foreach ($state_updates as $state_update_old => $state_update_new) {
    db_query($update_states_query, $state_update_new, $state_update_old);
    db_query($update_days_query, $state_update_new, $state_update_old);
  }

  // Update statuses: redefine storage for split day states
  // - get unique split day statuses
  // - for each of these: define an update entry/query (taking into account above renamings)
  // - execute querys
  $split_state_updates = array();
  $result = db_query("SELECT distinct status FROM {availability_calendars_day} WHERE status LIKE 'calsplit %'");
  while ($state = db_fetch_array($result)) {
    $state = $state['status'];

    // state = 'calsplit cal-<am>_<pm>'
    // algorithm will fail with underscores in class names (not likely as 6200 to 6203 will be released at the same time)
    $underscore = strpos($state, '_');
    $am_state = substr($state, strlen('calsplit cal-'), $underscore - strlen('calsplit cal-'));
    if (array_key_exists($am_state, $state_updates)) {
      $am_state = $state_updates[$am_state];
    }
    $pm_state = substr($state, $underscore + 1);
    if (array_key_exists($pm_state, $state_updates)) {
      $pm_state = $state_updates[$pm_state];
    }
    $split_state_updates[$state] = "{$am_state}-am {$pm_state}-pm";
  }
  $update_query = "UPDATE {availability_calendars_day} SET status = '%s' WHERE status = '%s'";
  foreach ($split_state_updates as $state_update_old => $state_update_new) {
    db_query($update_query, $state_update_new, $state_update_old);
  }
  variable_set('availability_calendars_settings_system_generate', 1);
  $styles = array(
    'table' => array(
      'font-size' => 'smaller',
      'color' => '#000000',
      'background-color' => '#ffffff',
      'border-width' => '0px',
      'border-color' => '#000000',
    ),
    'caption' => array(
      'text-align' => 'left',
      'font-weight' => 'bold',
      'font-style' => 'inherit',
      'font-size' => 'smaller',
    ),
    'header' => array(
      'height' => '',
      'font-weight' => 'bold',
      'font-style' => 'inherit',
      'font-size' => '',
      'text-align' => 'center',
    ),
    'week_notes' => array(
      'width' => '90px',
    ),
    'days' => array(
      'width' => '28px',
      'height' => '28px',
      'text-align' => 'center',
      'vertical-align' => 'middle',
    ),
    'states' => array(
      'split-day' => '/',
    ),
  );

  // Fill (default) states
  $styles['states']['calav'] = '#90ee90';
  $styles['states']['calna'] = '#ffb6c1';
  $styles['states']['calopt'] = '#ffffe0';
  variable_set('availability_calendars_styles', $styles);
  $link = l(st('Availability Calendars') . ' ' . st('Styling'), 'admin/config/content/availability-calendars/styling');
  drupal_set_message(st("Please visit the '!link' page to generate a CSS file.", array(
    '!link' => $link,
  )), 'warning');
}

/**
 * Optimize storage for Availability Calendars (issue #1083198).
 */
function availability_calendars_update_7201(&$sandbox) {

  // Change datetime field to date (text on sqlite).
  // Assumption is that the conversion will be executed nicely by the database, keeping the date part...
  db_change_field('availability_calendars_day', 'date', 'date', array(
    'description' => 'Date of availability state.',
    'mysql_type' => 'DATE',
    'pgsql_type' => 'DATE',
    'sqlite_type' => 'TEXT',
    // SQLite does not have a DATE type
    'not null' => TRUE,
  ));

  // Add primary key on nid, date.
  // Drop first: people might already have added a primary key themselves (e.g. to edit the table in a db-tool).
  db_drop_primary_key('availability_calendars_day');
  db_add_primary_key('availability_calendars_day', array(
    'nid',
    'date',
  ));

  // Remove fields year, month, day. Do this after changing the type of the datetime field,
  // and after setting the primary key (possibly removing it from these fields if set manually earlier).
  // We will only get here if the prior operations succeeded and thus we can be quite sure that we will not
  // loose any availability data.
  db_drop_field('availability_calendars_day', 'day');
  db_drop_field('availability_calendars_day', 'month');
  db_drop_field('availability_calendars_day', 'year');

  // Warn about a change that needs a regeneration of the CSS file
  $link = l(st('Availability Calendars') . ' ' . st('Styling'), 'admin/config/content/availability-calendars/styling');
  drupal_set_message(st("Please visit the '!link' page to regenerate the CSS file.", array(
    '!link' => $link,
  )), 'warning');
}

/**
 * Optimize storage for Availability Calendars settings (issue #31107230).
 */
function availability_calendars_update_7202(&$sandbox) {

  // - Combine these settings in 1 array in the variables table:
  //   - startofweek
  //   - showteaser
  //   - showkey
  //   - firstletter
  //   - hideold
  //   - defaultstatus
  //   - monthcount
  //   - splitday
  //   - nodeview (exists only on system level)
  //   Notes: the settings pernodeoverride and supported content types are kept separate
  // - Rename availability_calendars_settings_system_generate to availability_calendars_styles_generate.
  // - Rename availability_calendar_settings_system-type_... to availability_calendar_settings_content_type_....
  // Group system settings
  $settings_system = availability_calendars_get_settings_old();
  $settings_styles_generate = variable_get('availability_calendars_settings_system_generate');
  $settings_system_pernodeoverride = variable_get('availability_calendars_settings_system_pernodeoverride');

  // Get content type settings into variables.
  // Get each unique node id for which there are settings and group these in a 2nd loop further on
  global $conf;
  $settings_nodes = array();
  $settings_content_types = array();
  foreach ($conf as $name => $value) {
    $matches = array();
    if (preg_match('/^availability_calendars_settings_node_([0-9]+)_/', $name, $matches) > 0) {
      $settings_nodes[$matches[1]] = TRUE;
    }
    else {
      if (preg_match('/^availability_calendars_settings_system-type_(.+)$/', $name, $matches) > 0) {
        $settings_content_types[$matches[1]] = $value;
      }
    }
  }

  // Group node settings
  foreach ($settings_nodes as $nid => &$value) {
    $value = availability_calendars_get_settings_old($nid);
  }

  // We now have all settings (except the styles) in local variables:
  // - Remove old settings.
  availability_calendars_variable_del_all('availability_calendars_settings_');

  // - Store settings under their new names
  variable_set('availability_calendars_settings_system', $settings_system);
  variable_set('availability_calendars_settings_system_pernodeoverride', $settings_system_pernodeoverride);
  variable_set('availability_calendars_styles_generate', $settings_styles_generate);
  foreach ($settings_content_types as $content_type => $value) {
    variable_set('availability_calendars_settings_content_type_' . $content_type, $value);
  }

  // The following loop *might* be time consuming, but for now I'm not rewriting it to do this in batches.
  foreach ($settings_nodes as $nid => $value) {
    variable_set('availability_calendars_settings_node_' . $nid, $value);
  }
}

/**
 * Helper method for availability_calendars_update_7202.
 * Returns setings, at node or system scope, based on the old way of storing the settings.
 */
function availability_calendars_get_settings_old($arg = NULL) {
  $settings = new stdClass();
  if ($arg !== NULL) {

    // Per node settings
    $settings->startofweek = variable_get('availability_calendars_settings_node_' . $arg . '_startofweek');
    $settings->showteaser = variable_get('availability_calendars_settings_node_' . $arg . '_showteaser');
    $settings->showkey = variable_get('availability_calendars_settings_node_' . $arg . '_showkey');
    $settings->firstletter = variable_get('availability_calendars_settings_node_' . $arg . '_firstletter');
    $settings->hideold = variable_get('availability_calendars_settings_node_' . $arg . '_hideold');
    $settings->defaultstatus = variable_get('availability_calendars_settings_node_' . $arg . '_defaultstatus');
    $settings->monthcount = variable_get('availability_calendars_settings_node_' . $arg . '_monthcount');
    $settings->splitday = variable_get('availability_calendars_settings_node_' . $arg . '_splitday');
  }
  else {
    $settings->startofweek = variable_get('availability_calendars_settings_system_startofweek');
    $settings->showteaser = variable_get('availability_calendars_settings_system_showteaser');
    $settings->showkey = variable_get('availability_calendars_settings_system_showkey');
    $settings->firstletter = variable_get('availability_calendars_settings_system_firstletter');
    $settings->hideold = variable_get('availability_calendars_settings_system_hideold');
    $settings->defaultstatus = variable_get('availability_calendars_settings_system_defaultstatus');
    $settings->monthcount = variable_get('availability_calendars_settings_system_monthcount');
    $settings->splitday = variable_get('availability_calendars_settings_system_splitday');
    $settings->nodeview = variable_get('availability_calendars_settings_system_nodeview');
  }

  // Remove NULL values
  $settings = (array) $settings;

  // PHP4 cannot iterate objects
  $result = array();
  foreach ($settings as $key => $value) {
    if ($value !== NULL) {
      $result[$key] = $value;
    }
  }
  return $result;
}

/**
 * Change storage defining content types enabled for Availability Calendars.
 */
function availability_calendars_update_7203(&$sandbox) {
  $content_types = array();
  foreach (node_type_get_names() as $content_type => $content_type_name) {
    if (variable_get('availability_calendars_settings_content_type_' . $content_type, 0) == 1) {
      $content_types[] = $content_type;
    }
  }
  availability_calendars_variable_del_all('availability_calendars_settings_content_type_');
  variable_set('availability_calendars_settings_content_types', $content_types);
}

/**
 * Add a boolean field indicating if a state "is available" to the states table of Availability Calendars.
 */
function availability_calendars_update_7204(&$sandbox) {
  $tables = availability_calendars_schema();

  //DRY: get table def from schema
  $table_name = 'availability_calendars_states';
  $field_name = 'is_available';
  db_add_field($table_name, $field_name, $tables[$table_name]['fields'][$field_name]);

  // Warn user to define states as available/not-available
  $link = l(st('Availability Calendars') . ' ' . st('Settings'), 'admin/config/content/availability-calendars/settings');
  drupal_set_message(st("Please visit the '!link' page to define what states to treat as available.", array(
    '!link' => $link,
  )), 'warning');
}

/**
 * Increase length of week notes field.
 */
function availability_calendars_update_7205(&$sandbox) {
  db_change_field('availability_calendars_week', 'note', 'note', array(
    'description' => 'The note.',
    'type' => 'varchar',
    'length' => 200,
  ));
}

Functions

Namesort descending Description
availability_calendars_get_settings_old Helper method for availability_calendars_update_7202. Returns setings, at node or system scope, based on the old way of storing the settings.
availability_calendars_install Implements hook_install().
availability_calendars_schema Implements hook_schema().
availability_calendars_uninstall Implements hook_uninstall().
availability_calendars_update_6200 Add custom states (issue #306461)
availability_calendars_update_6201 Allow to disable per node override (issue #764406) On upgrades, the default should be set to 'on'.
availability_calendars_update_6202 Allow to define custom colors using administration interface (issue #660502) The upgrade should mimic the current availability_calendars.css as much as possible.
availability_calendars_update_7201 Optimize storage for Availability Calendars (issue #1083198).
availability_calendars_update_7202 Optimize storage for Availability Calendars settings (issue #31107230).
availability_calendars_update_7203 Change storage defining content types enabled for Availability Calendars.
availability_calendars_update_7204 Add a boolean field indicating if a state "is available" to the states table of Availability Calendars.
availability_calendars_update_7205 Increase length of week notes field.
availability_calendars_update_last_removed Implements hook_update_last_removed().
availability_calendars_variable_del_all Utility function that is an altered version of variable_del, it will delete a set of variables set by a module.