--- availability_calendars.module.orig Sun Jan 23 05:35:46 2011 +++ availability_calendars.module Mon Feb 07 21:05:55 2011 @@ -102,8 +102,13 @@ * @return array */ function availability_calendars_admin_settings() { - $form = array(); $settings = availability_calendar_getsettings(); + + $form = array(); + $form['#validate'][] = 'availability_calendars_admin_settings_validate'; + $form['#submit'][] = 'availability_calendars_admin_settings_submit'; + $form['#tree'] = TRUE; + $form['display'] = array( '#type' => 'fieldset', '#title' => t('View settings'), @@ -146,11 +151,138 @@ '#title' => t('Show availability calendars within teasers.'), '#default_value' => $settings->showteaser, ); - //TODO: add status codes and css classes in here + + // Add states + $form['states'] = array( + '#type' => 'fieldset', + '#title' => t('States'), + '#description' => t('

You can modify the availability states here.

+'), + '#attributes' => array('class' => 'state-list'), + ); + + $element = &$form['states']; + $states = availability_calendars_get_states(); + $i = 0; + foreach ($states as $state) { + availability_calendars_admin_settings_add_state($element, $i, $state); + $i++; + } + + // Show a minimum of 4 available states with at least one empty state + do { + availability_calendars_admin_settings_add_state($element, $i, array('class' => '', 'label' => '', 'weight' => 0)); + $i++; + } while ($i < 4); + return system_settings_form($form); } /** + * Validate callback for the admin_settings form + * - at least one label should be filled + */ +function availability_calendars_admin_settings_validate($form, &$form_state) { + $op = isset($form_state['values']['op']) ? $form_state['values']['op'] : ''; + if ($op == t('Save configuration')) { + $element = $form_state['values']['states']; + $all_empty = true; + foreach ($element as $i => $state_fields){ + // Only add non-empty labels + if (!empty($state_fields['label'])) { + $all_empty = false; + break; + } + } + + if ($all_empty) { + form_set_error('states][0][label', t('At least 1 state should be defined.')); + } + } +} + +/** + * Helper function to add a state item to a form. + * Only the first item gets labels, as the fields will be presented below each other. + * + * @param array $element the form element to add the state item to + * @param int $i the state item count + * @param array $state array containing a state record + */ +function availability_calendars_admin_settings_add_state(&$element, $i, $state) { + static $max_weight = 0; + $element[$i]['label'] = array( + '#type' => 'textfield', + '#title' => $i == 0 ? t('Label') : '', + '#default_value' => $state['label'], + '#size' => 40, + '#prefix' => '
', + ); + $element[$i]['class'] = array( + '#type' => 'textfield', + '#title' => $i == 0 ? t('Class') : '', + '#default_value' => $state['class'], + '#size' => 24, + ); + $element[$i]['weight'] = array( + '#type' => 'select', + '#title' => $i == 0 ? t('Weight') : '', + '#default_value' => $state['weight'] > 0 ? $state['weight'] : ++$max_weight, + '#options' => array_combine(range(1, 20, 1), range(1, 20, 1)), + '#suffix' => '
', + ); + if ($state['weight'] > $max_weight) { + $max_weight = $state['weight']; + } +} + +/** + * Submit callback for the admin_settings form + * + * Processes the submitted form. the states are non system settings, and are handled here. + * Other values are handled by the default system settings form handling. + */ +function availability_calendars_admin_settings_submit($form, &$form_state) { + $op = isset($form_state['values']['op']) ? $form_state['values']['op'] : ''; + if ($op == t('Save configuration')) { + $element = $form_state['values']['states']; + // Do not process the states by the default submit handler for system settings forms + unset($form_state['values']['states']); + $states = array(); + foreach ($element as $i => $state_fields){ + // Only add non-empty labels + if (!empty($state_fields['label'])) { + if (empty($state_fields['class'])) { + $state_fields['class'] = availability_calendars_string_to_safe_id($state_fields['label']); + } + $states[$state_fields['class']] = array( + 'class' => $state_fields['class'], + 'label' => $state_fields['label'], + 'weight' => $state_fields['weight'], + ); + } + } + + $existing_States = availability_calendars_get_states(); + if ($states != $existing_States) { + // update states: dellete all existing, insert all states on the form + db_query("DELETE FROM {availability_calendars_states}"); + foreach ($states as $state) { + db_query("INSERT INTO {availability_calendars_states} (class,label,weight) VALUES ('%s','%s',%d)", $state['class'], $state['label'], $state['weight']); + } + } + } +} + +/** * Create tab to show node availability. * * @return string or FALSE @@ -631,8 +763,7 @@ $classes = array(); if ($today == $daystamp) $classes[] = 'caltoday'; // today if ($daystamp < $today) $classes[] = 'calpastdate'; // past date - if ($settings->hideold === 1 && $daystamp < $today) $classes[] = 'calnotavailable'; // past dates should be "fully booked" - else { + if ($settings->hideold !== 1 || $daystamp >= $today) { if ($day_status[$day] === NULL) $classes[] = ($settings->defaultstatus) ? $settings->defaultstatus : $options[0]; else $classes[] = $day_status[$day]; } @@ -688,6 +819,18 @@ } /** + * @return array array with records for all states + */ +function availability_calendars_get_states() { + $states = array(); + $result = db_query("SELECT * FROM {availability_calendars_states} ORDER BY weight"); + while ($row = db_fetch_array($result)) { + $states[$row['class']] = $row; + } + return $states; +} + +/** * Implementation of hook_form_alter(). * All form alterations needed for the calendars. * @@ -864,4 +1007,21 @@ } return $block; } +} + +/** +* Converts a string to a valid html id/class attribute. +* +* http://www.w3.org/TR/html4/struct/global.html#h-7.5.2 specifies what makes a +* valid id/class attribute in HTML. This function: +* +* - Ensure an ID starts with an alpha character by prefixing with 'cal'. +* - Replaces any character except a-z, A-Z, numbers, and underscores with dashes. +* - Converts entire string to lowercase. +* +* @param $string The string +* @return the converted string +*/ +function availability_calendars_string_to_safe_id($string) { + return 'cal' . strtolower(preg_replace('/[^a-zA-Z0-9_-]+/', '-', $string)); } --- availability_calendars.css.orig Tue Jan 18 05:10:01 2011 +++ availability_calendars.css Mon Feb 07 18:46:22 2011 @@ -31,3 +31,6 @@ td.cal-notavailableprov_available > .cal-split-bg{border-color:#FFFFE0 #90EE90 #90EE90 #FFFFE0;} td.cal-notavailableprov_notavailable,tr.odd td.cal-notavailableprov_notavailable,tr.even td.cal-notavailableprov_notavailable{background:#FFB6C1;} td.cal-notavailableprov_notavailable > .cal-split-bg{border-color:#FFFFE0 #FFB6C1 #FFB6C1 #FFFFE0;} + +fieldset.state-list .state-item { position: relative; width: 100%; overflow: auto;} +fieldset.state-list .form-item { float: left; margin-right: 1em;} \ No newline at end of file --- availability_calendars.install.orig Fri Jan 21 08:16:11 2011 +++ availability_calendars.install Mon Feb 07 21:18:44 2011 @@ -45,8 +45,8 @@ ), 'status' => array( 'description' => 'The status.', - 'type' => 'text', - 'size' => 'medium', + 'type' => 'varchar', + 'length' => 64, // status = class or (split day) calsplit cal-_ = 14 + 2 * length of a class ), 'date' => array( 'description' => 'Datetime representation of availability', @@ -89,6 +89,33 @@ ) ) ); + + $schema['availability_calendars_states'] = array( + 'description' => 'Store classes and labels for the possible states in availability calendars', + 'fields' => array( + 'class' => array( + 'description' => 'The 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', + ), + ), + 'primary key' => array('class'), + ); + return $schema; } @@ -96,8 +123,40 @@ * Implementation of hook_install(). */ function availability_calendars_install() { + // Install schema drupal_install_schema('availability_calendars'); - drupal_set_message(t('Availability Calendars module installed successfully.'), 'warning'); + + // Fill schema: add a default (starter, example) set of states to the database + // @TODO: shorten the classes if no css refers to these classes anymore + $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, + ), + ); + $insert_query = "INSERT INTO {availability_calendars_states} (class,label,weight) VALUES ('%s','%s',%d)"; + $success = true; + foreach ($states as $state) { + $success = db_query($insert_query, $state['class'], $state['label'], $state['weight']) !== false && $success; + } + + if (!$success) { + drupal_set_message(t('Availability Calendars module not installed successfully.'), 'error'); + } + else { + drupal_set_message(t('Availability Calendars module installed successfully.'), 'warning'); + } } /** @@ -197,7 +256,7 @@ ); return $ret; } - + /** * Implementation of hook_update_N(). * Change statuses to now use class strings instead of integers and alter defaultstatus setting if set. @@ -253,5 +312,52 @@ 'success' => TRUE, 'query' => 'Fixed variables for ' . $sandbox['max'] . ' availability calendar variable settings.', ); + return $ret; +} + +/** + * Implementation of hook_update_N(). + * Add custom states + * + * @return array + */ +function availability_calendars_update_6104(&$sandbox) { + $ret = array(); + + // 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($ret, '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($ret, $table_name, $tables[$table_name]); + + // Add existing (hard-coded) states to the database + // Note: We can never shorten in an update situation, unless we update the database *contents* as well. + $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, + ), + ); + $insert_query = "INSERT INTO {availability_calendars_states} (class,label,weight) VALUES ('%s','%s',%d)"; + $success = true; + foreach ($states as $state) { + $success = db_query($insert_query, $state['class'], $state['label'], $state['weight']) !== false && $success; + } + $ret[] = array('success' => $success, 'query' => $insert_query); + return $ret; }