array( 'title' => t('Declare emergencies'), ), 'administer emergency levels' => array( 'title' => t('Administer emergency levels'), ), ); } /** * Implements hook_menu(). */ function emergency_menu() { $items['admin/emergency'] = array( 'title' => 'Emergency', 'description' => 'Declare an emergency.', 'page callback' => 'drupal_get_form', 'page arguments' => array('emergency_form_manage'), 'access arguments' => array('declare emergencies'), 'file' => 'emergency.admin.inc', ); $items['admin/structure/emergency'] = array( 'title' => 'Emergency levels', 'description' => 'Configure what parts of the layout should be overridden when an emergency is declared.', 'page callback' => 'drupal_get_form', 'page arguments' => array('emergency_overview_levels'), 'access arguments' => array('administer emergency levels'), 'file' => 'emergency.admin.inc', ); $items['admin/structure/emergency/list'] = array( 'title' => 'List', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); $items['admin/structure/emergency/add'] = array( 'title' => 'Add emergency level', 'page callback' => 'drupal_get_form', 'page arguments' => array('emergency_form_level'), 'access arguments' => array('administer emergency levels'), 'type' => MENU_LOCAL_ACTION, 'file' => 'emergency.admin.inc', ); $items['admin/structure/emergency/%emergency_level'] = array( 'title callback' => 'emergency_level_title_callback', 'title arguments' => array(3), 'page callback' => 'drupal_get_form', 'page arguments' => array('emergency_form_response', 3), 'access arguments' => array('administer emergency levels'), 'file' => 'emergency.admin.inc', ); $items['admin/structure/emergency/%emergency_level/edit'] = array( 'title' => 'Edit', 'page callback' => 'drupal_get_form', 'page arguments' => array('emergency_form_level', 3), 'access callback' => 'emergency_level_edit_access', 'access arguments' => array(3), 'type' => MENU_LOCAL_TASK, 'weight' => -10, 'file' => 'emergency.admin.inc', ); $items['admin/structure/emergency/%emergency_level/delete'] = array( 'title' => 'Delete', 'page callback' => 'drupal_get_form', 'page arguments' => array('emergency_level_confirm_delete', 3), 'access callback' => 'emergency_level_edit_access', 'access arguments' => array(3), 'type' => MENU_LOCAL_TASK, 'file' => 'emergency.admin.inc', ); $items['admin/structure/emergency/%emergency_level/configure'] = array( 'title' => 'Configure response', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -20, ); // Theme configuration subtabs. $default_theme = variable_get('theme_default', 'bartik'); foreach (list_themes() as $theme) { if ($theme->status) { $items['admin/structure/emergency/%emergency_level/configure/' . $theme->name] = array( 'title' => check_plain($theme->info['name']), 'page callback' => 'drupal_get_form', 'page arguments' => array('emergency_form_response', 3, $theme->name), 'access arguments' => array('administer emergency levels'), 'type' => $theme->name == $default_theme ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, 'weight' => $theme->name == $default_theme ? -10 : 0, 'file' => 'emergency.admin.inc', ); } } return $items; } /** * Menu access callback; edit or delete an emergency level. * * @param $level * An emergency level object. * * @return * Boolean indicating access. */ function emergency_level_edit_access($level) { // Prevent the user from altering the active emergency level. return user_access('administer emergency levels') && $level->type != variable_get('emergency', '0'); } /** * Implements hook_block_info(). */ function emergency_block_info() { $blocks['details'] = array( 'info' => t('Emergency details'), 'cache' => DRUPAL_CACHE_GLOBAL, ); $blocks['summary'] = array( 'info' => t('Emergency summary'), 'cache' => DRUPAL_CACHE_GLOBAL, ); $blocks['additional'] = array( 'info' => t('Emergency additional details'), 'cache' => DRUPAL_CACHE_GLOBAL, ); return $blocks; } /** * Implements hook_block_view(). */ function emergency_block_view($delta = '') { $block = array(); // Blocks only appear during an active emergency. $type = variable_get('emergency', '0'); if ($type && $emergency = emergency_load($type)) { switch ($delta) { case 'details': $block['subject'] = check_plain($emergency->title); $block['content'] = check_markup($emergency->details, $emergency->format, '', TRUE); break; case 'summary': $link_path = variable_get('emergency_link_path', ''); $block['subject'] = ''; $block['content'] = empty($link_path) ? check_plain($emergency->title) : l($emergency->title, $link_path); break; case 'additional': if (!empty($emergency->additional)) { $block['subject'] = ''; $block['content'] = check_markup($emergency->additional, $emergency->format, '', TRUE); } break; } } return $block; } /** * Implements hook_preprocess_html(). */ function emergency_preprocess_html(&$variables) { if ($type = variable_get('emergency', '0')) { $variables['classes_array'][] = 'emergency'; $variables['classes_array'][] = drupal_html_class('emergency-level-' . $type); } } /** * Implements hook_page_alter(). */ function emergency_page_alter(&$page) { // Only react during an active emergency. $type = variable_get('emergency', '0'); if ($type) { // Get the response settings for the current emergency level and theme. global $theme; $regions = variable_get('emergency_' . $type . '_regions_' . $theme, array()); // Get a list of our emergency blocks. $block_info = module_invoke('emergency', 'block_info'); $block_keys = array(); foreach (array_keys($block_info) as $delta) { $block_keys[] = 'emergency_' . $delta; } // Remove disabled regions from the page. foreach ($regions as $region => $disabled) { if ($disabled && !empty($page[$region])) { // Skip if only disabled for the front page and we're somewhere else. if ($disabled == EMERGENCY_REGION_DISABLED_FRONTPAGE && !drupal_is_front_page()) { continue; } // Check whether the region contains one of our blocks. $hide_blocks = FALSE; foreach ($block_keys as $block_key) { $hide_blocks |= array_key_exists($block_key, $page[$region]); } if ($hide_blocks) { // If one of our blocks is present, remove all other blocks. foreach ($page[$region] as $key => $value) { if (is_array($value) && !in_array($key, $block_keys)) { unset($page[$region][$key]); } } } else { // Otherwise, remove the entire region. unset($page[$region]); } } } } } /** * Returns a list of available emergency levels. * * @param $normal * TRUE to include the normal, non-emergency level; FALSE to exclude it. * Defaults to TRUE. * * @return * An array of emergency levels as type => name. */ function emergency_levels($normal = TRUE) { $levels = $normal ? array('0' => t('Normal (no emergency)')) : array(); foreach (emergency_level_load_all() as $record) { $levels[$record->type] = $record->name; } return $levels; } /** * Loads all emergency levels. * * @return * An array of emergency level objects, or array() if none defined. */ function emergency_level_load_all() { if (module_exists('ctools')) { ctools_include('export'); $levels = ctools_export_load_object('emergency_level'); usort($levels, '_emergency_level_sort'); return $levels; } return db_query('SELECT * FROM {emergency_level} ORDER BY weight, name')->fetchAllAssoc('type'); } /** * Load an emergency level. * * @param $type * The machine-readable name of the emergency level. * * @return * An emergency level object, or FALSE if the specified level does not exist. */ function emergency_level_load($type) { if (module_exists('ctools')) { ctools_include('export'); $level = ctools_export_load_object('emergency_level', 'names', array($type)); return reset($level); } return db_query('SELECT * FROM {emergency_level} WHERE type = :type ORDER BY weight, name', array(':type' => $type))->fetchObject(); } /** * usort callback; sorts emergency level records by weight and name. */ function _emergency_level_sort($a, $b) { if ($a->weight < $b->weight) { return -1; } elseif ($a->weight > $b->weight) { return 1; } return strcmp($a->name, $b->name); } /** * Load emergency information. * * @param $type * The machine-readable name of an emergency level. * * @return * The emergency information object for $type, or FALSE if not found. */ function emergency_load($type) { $query = db_select('emergency', 'e')->fields('e')->condition('e.type', $type); $result = $query->execute(); return $result->fetchObject(); } /** * Returns the emergency level name for a given emergency level object. */ function emergency_level_title_callback($level) { return check_plain($level->name); } /** * Saves an emergency level to the database. * * @param $level * An emergency level object. * * @return * The result of the save as SAVED_NEW, SAVED_UPDATED, or FALSE. */ function emergency_level_save($level) { // Clean-up whitespace. foreach (array('type', 'name') as $key) { if (!empty($level->$key)) { $level->$key = trim($level->$key); } } // Ensure that the machine name does not evaluate to FALSE (such as '0'), // since that is reserved for the "Normal" level. if (!empty($level->type)) { // Check whether we're changing the machine name on this save. $type = isset($level->old_type) ? $level->old_type : $level->type; // Do not change the machine name of an active emergency state. if ($type != $level->type && variable_get('emergency', '0') == $type) { return FALSE; } // Update the emergency level record. $status = db_merge('emergency_level') ->key(array('type' => $type)) ->fields(array( 'type' => $level->type, 'name' => $level->name, 'weight' => $level->weight, )) ->execute(); // If the machine name changed, we also need to update the emergency table. if ($type != $level->type) { db_update('emergency') ->fields(array( 'type' => $level->type, )) ->condition('type', $type) ->execute(); } return $status; } else { return FALSE; } } /** * Saves emergency information to the database. * * @param $emergency * An emergency information object. * * @return * The result of the save as SAVED_NEW, SAVED_UPDATED, or FALSE. */ function emergency_save($emergency) { // Clean-up whitespace. foreach (array('type', 'title') as $key) { if (!empty($emergency->$key)) { $emergency->$key = trim($emergency->$key); } } // Ensure that the emergency type does not evaluate to FALSE (such as '0'), // since that is reserved for the "Normal" level, which has no details. if (!empty($emergency->type)) { // Set the fields to include in the insert/update. $fields = array( 'title' => $emergency->title, 'details' => $emergency->details, 'format' => $emergency->format, 'end' => $emergency->end, 'additional' => $emergency->additional, ); // Only update the start time if a new one was specified; NULL indicates // that the current start time should remain unchanged. if (isset($emergency->start)) { $fields['start'] = $emergency->start; } // Update the emergency record. return db_merge('emergency') ->key(array('type' => $emergency->type)) ->fields($fields) ->execute(); } else { return FALSE; } } /** * Declares an emergency. * * @param $type * The machine-readable name of an emergency level. * @param $title * The title or brief summary. Defaults to an empty string. * @param $details * The details. Defaults to an empty string. * @param $format * The text input format to use for filtering $details. Defaults to the * system default format (typically "Plain Text"). * @param $end * The scheduled end time as a UNIX timestamp or a string representing a time. * Defaults to 0, meaning no scheduled end time. * @param $additional * Additional details. Defaults to an empty string. */ function emergency_declare($type, $title = '', $details = '', $format = NULL, $end = 0, $additional = '') { // Format the end time as a UNIX timestamp. if (!is_int($end)) { $end = strtotime(trim($end)); $end = empty($end) ? 0 : $end; } $emergency = (object) array( 'type' => $type, 'title' => $title, 'details' => $details, 'format' => $format, 'start' => NULL, 'end' => $end, 'additional' => $additional, ); // Only set the 'start' timestamp if this is a new declaration (rather than an // update of a current emergency's information). if ($emergency->type != variable_get('emergency', '0')) { $emergency->start = time(); } // Save the emergency information and set the current level. emergency_save($emergency); variable_set('emergency', $emergency->type); emergency_cache_clear(); if (empty($emergency->start)) { drupal_set_message(t('Updated emergency %title.', array('%title' => $emergency->title))); watchdog('emergency', 'Updated emergency %title.', array('%title' => $emergency->title), WATCHDOG_NOTICE, l(t('edit'), 'admin/emergency')); } else { drupal_set_message(t('Declared emergency %title.', array('%title' => $emergency->title))); watchdog('emergency', 'Declared emergency %title.', array('%title' => $emergency->title), WATCHDOG_NOTICE, l(t('edit'), 'admin/emergency')); } } /** * Cancels a current emergency. */ function emergency_cancel() { $current = variable_get('emergency', '0'); if ($current) { // Clear the scheduled end time, if any, so it isn't preset next time. db_update('emergency') ->fields(array('end' => 0)) ->condition('type', $current) ->execute(); // Return to normal. variable_set('emergency', '0'); emergency_cache_clear(); drupal_set_message(t('Emergency level has returned to normal.')); watchdog('emergency', 'Emergency level has returned to normal.'); } } /** * Deletes an emergency level from the database. * * @param $type * The machine-readable name of the emergency level. * * @return * The number of records deleted; should be 0 or 1. */ function emergency_level_delete($type) { // Do not delete an active emergency state. if (variable_get('emergency', '0') == $type) { return FALSE; } // Clean up the emergency table first, then the levels table. db_delete('emergency')->condition('type', $type)->execute(); return db_delete('emergency_level')->condition('type', $type)->execute(); } /** * Clears relevant caches, if cache clearing is enabled. */ function emergency_cache_clear() { if (variable_get('emergency_cache_clear', TRUE)) { cache_clear_all('emergency:', 'cache_block', TRUE); cache_clear_all('*', 'cache_page', TRUE); } } /** * Implements hook_cron(). */ function emergency_cron() { $type = variable_get('emergency', '0'); // Check for an active emergency. if ($type) { $emergency = emergency_load($type); // If an end time was set, and that time has past, cancel the emergency. if (!empty($emergency->end) && $emergency->end <= time()) { emergency_cancel(); } } } /** * Implements hook_theme(). */ function emergency_theme() { return array( 'emergency_overview_levels' => array( 'render element' => 'form', ), ); } /** * Implements hook_help(). */ function emergency_help($path, $arg) { switch ($path) { // Main module help. case 'admin/help#emergency': return '

' . t('The emergency module allows you to override the normal layout and content of your site in the event of an emergency. It is primarily designed for institutional websites (such as universities), but could be used by any site that needs to broadcast urgent information to visitors.') . '

' . t('By declaring an emergency, non-essential block regions can be hidden or overridden, and information related to the emergency (such as contact numbers) can be shown instead. You may also define multiple emergency levels with different sets of overrides. For example, you could have a "closure" level that is used to declare a normal closure due to inclement weather, and a separate "danger" level for life-threatening conditions.', array('@declare' => url('admin/emergency'), '@define' => url('admin/structure/emergency'))) . '

'; // Help for the management page. case 'admin/emergency': // @todo Improve this help text. return '

' . t('Use this page to declare an emergency.') . '

'; // Help for the configuration page. case 'admin/structure/emergency': // @todo Improve this help text. return '

' . t('Use this page to configure emergency levels.') . '

'; // Help for the emergency level response form. case 'admin/structure/emergency/%': case 'admin/structure/emergency/%/configure/' . $arg[5]: return '

' . t('Use this page to control how the site layout should change when an emergency is declared. This emergency response is configured per theme, per emergency level. Block regions in the theme can be set to remain visible ("Enabled," the default), to be hidden only on the front page ("Disabled on front page"), or to be hidden across the site ("Disabled").') . '

' . t('These settings work in conjunction with the "Emergency details" and "Emergency summary" blocks, configured on the block administration page. If you disable a region that contains one of the emergency blocks, only the emergency block will be shown, and all other blocks assigned to that region will be hidden. Otherwise, the region will be hidden entirely.', array('@blocks' => url('admin/structure/block'))) . '

'; } } /** * Implements hook_features_pipe_component_alter(). * Automatically adds associated variables when exporting emergency levels. */ function emergency_features_pipe_emergency_level_alter(&$pipe, $data, $export) { if (module_exists('strongarm') && !empty($data)) { foreach ($data as $level) { foreach (list_themes() as $theme) { $pipe['variable'][] = 'emergency_' . $level . '_regions_' . $theme->name; } } } }