--- 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.
+
+- The label is what users will see in the legend and what editors will see when editing the calendar.
+- The class must be unique and will be used for the css.
+- The weight defines the order in the legend and in the dropdown on the edit form.
+- Make a label empty to remove the row.
+- If there are no more empty lines to add new states, save the form and you will be able to add another state.
+- Changes made to existing classes will not be updated in existing calendars! So do not change on live sites.
+- If you define your own classes, you will also have to provide your own css.
+
'),
+ '#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;
}