Index: weather.module =================================================================== --- weather.module (revision 9) +++ weather.module (working copy) @@ -56,7 +56,10 @@ * Implementation of hook_perm(). */ function weather_perm() { - return array('administer custom weather block'); + return array( + 'administer custom weather block', + 'view weather' + ); } @@ -147,6 +150,19 @@ 'callback' => 'weather_get_places_xml', 'access' => user_access('administer custom weather block'), 'type' => MENU_CALLBACK, + ); + $items[] = array( + 'path' => 'weather', + 'title' => t('Weather'), + 'callback' => 'weather_get_weather', + 'access' => user_access('view weather'), + 'type' => MENU_CALLBACK, + ); + $items[] = array( + 'path' => 'weather/autocomplete', + 'title' => t('Weather AJAX Autocomplete'), + 'callback' => 'weather_location_ajax', + 'access' => user_access('view weather'), ); } @@ -404,6 +420,33 @@ $content .= ''; } return $content; +} + +/** + * Custom theme function for the table of potential search results when a user + * searches for a weather location but there is more than one result. + * + * @param array $data An array of arrays containing table data. + */ +function theme_weather_disambiguation($data) { + $output = "

"; + $output .= t('Your search returned more than one result:'); + $output .= "

"; + + $output .= "\n"; + + return $output; } @@ -1562,4 +1605,224 @@ } return $metar_raw; +} + +/** + * Gets the weather for the specified location, whether it is a place name or + * an ICAO code. For example, waether/bristol will display the weather for + * bristol. + * + * @param string $arg The argument passed in the URL that specifies the + * location for which to get the weather. + */ +function weather_get_weather($arg = NULL) { + if ($arg) { + // We don't know whether the argument is a location, an ICAO code or + // a load of gibberish, so figure this out first. + if (strlen($arg) < 3) { + // Argument is not at least 3 characters, so don't bother to + // search. + drupal_set_message( + 'Please enter at least 3 characters when searching for weather.', + 'error' + ); + drupal_goto('weather'); + } + if (strlen($arg) > 64) { + // Argument is too long. I mean, come on! 64 characters? + drupal_set_message( + 'Please enter less than 64 characters when searching for weather.', + 'error' + ); + drupal_goto('weather'); + } + + $arg = urldecode($arg); + + // Try for an exact match on the ICAO code. + $result = db_query_range( + "SELECT icao, country, name FROM {weather_icao} " . + "WHERE icao = '%s'", + $arg, + 0, 1 + ); + + $exact_match = NULL; + $row_total = 1; + + while ($row = db_fetch_object($result)) { + $exact_match = $row; + } + + if (!$exact_match) { + // Perform the database search to get close matches. + $result = db_query_range( + "SELECT icao, country, name FROM {weather_icao} " . + "WHERE LOWER(icao) LIKE LOWER('%%%s%%') " . + "OR LOWER(country) LIKE LOWER('%%%s%%') " . + "OR LOWER(name) LIKE LOWER('%%%s%%')", + $arg, + $arg, + $arg, + 0, 10 + ); + + $row_total = db_num_rows($result); + } + + if ($exact_match || $row_total == 1) { + // There is one match, so we can show the weather for this match. + $icao = ''; + $real_name = ''; + + if ($exact_match) { + $icao = $exact_match->icao; + $real_name = $exact_match->name; + } + else { + $row = db_fetch_object($result); + $icao = $row->icao; + $real_name = $row->name; + } + + // Create a config specially for this weather display. + $config['icao'] = strtoupper($icao); + $config['real_name'] = $real_name; + $config['units'] = array( + 'temperature' => 'celsius', + 'windspeed' => 'mph', + 'pressure' => 'hpa', + 'visibility' => 'miles', + ); + $config['settings'] = array( + 'show_unconverted_metar' => FALSE, + 'show_abbreviated_directions' => FALSE, + 'show_directions_degree' => FALSE, + 'show_sunrise_sunset' => TRUE, + 'show_compact_block' => FALSE, + 'full_mode' => TRUE, + ); + $config['weight'] = 0; + + return theme('weather', $config, weather_get_metar($icao)) . drupal_get_form('weather_selection_form'); + } + else if ($row_total == 0) { + drupal_set_message('Your weather search returned no results.'); + drupal_goto('weather'); + } + else { + // More than one row. We'll have to show a disambiguation page + // where the user can choose some results. + $disambig = array(); + + // Put each result into a custom array to pass to a custom + // theme function. + while ($row = db_fetch_object($result)) { + $disambig[] = array( + 'icao' => strtolower($row->icao), + 'title' => check_plain( + sprintf( + '%s, %s (%s)', + $row->name, + $row->country, + $row->icao + ) + ), + ); + } + return theme('weather_disambiguation', $disambig) . drupal_get_form('weather_selection_form'); + } + } + else { + // There is no argument, so the user has nagivated to /weather instead + // of /weather/foo. + return drupal_get_form('weather_selection_form'); + } +} + +/** + * Display a form for the user to choose a weather location or search for + * him/herself. + * + */ +function weather_selection_form() { + $form = array(); + + $form['search'] = array( + '#type' => 'textfield', + '#title' => 'Search', + '#description' => 'Start typing a location or ICAO code to find weather.', + '#autocomplete_path' => 'weather/autocomplete', + ); + + $form['submit'] = array( + '#type' => 'submit', + '#value' => 'Search', + ); + + return $form; +} + +function weather_selection_form_validate($form_id, $form_values) { + $search = $form_values['search']; + + if (strlen($search) < 3) { + form_set_error( + 'search', + t('Please enter at least 3 characters when searching for weather.') + ); + } + else if (strlen($search) > 64) { + form_set_error( + 'search', + t('Please enter less than 64 characters when searching for weather.') + ); + } } + +/** + * Submit handler for the weather_selection_form() form. + * + */ +function weather_selection_form_submit($form, $form_values) { + // Just redirect the user to the weather URL with the search term stuffed + // on the end of it. We've been through validation but make sure the + // search contains no dodgy characters here. + return 'weather/' . check_plain(urlencode($form_values['search'])); +} + +/** + * Given a partial string, search for a location or ICAO code matching that + * string. Provides AJAX auto-complete for forms. + * + * @param string $input The partial text to search for. + */ +function weather_location_ajax($input) { + $matches = array(); + + // In this query we search on ICAO, country AND name of place. + $result = db_query_range( + "SELECT icao, country, name FROM {weather_icao} " . + "WHERE LOWER(icao) LIKE LOWER('%%%s%%') " . + "OR LOWER(country) LIKE LOWER('%%%s%%') " . + "OR LOWER(name) LIKE LOWER('%%%s%%')", + $input, + $input, + $input, + 0, 10 + ); + + while($match = db_fetch_object($result)) { + $matches[strtolower($match->icao)] = check_plain( + sprintf( + "%s, %s (%s)", + $match->name, + $match->country, + $match->icao + ) + ); + } + + print drupal_to_js($matches); + exit(); +} \ No newline at end of file