yr_verdata.module in Yr Weatherdata 6
Same filename and directory in other branches
yr_verdata.module This file provides the yr_verdata forecast module.
File
yr_verdata.moduleView source
<?php
/**
* @file yr_verdata.module
* This file provides the yr_verdata forecast module.
*
* @see yr-forecast-page.tpl.php
* @see yr-forecast-block-location.tpl.php
*/
/**
* Implementation of hook_menu().
*/
function yr_verdata_menu() {
$items = array();
$items['yr_verdata'] = array(
'title' => 'Yr weatherdata',
'description' => 'Weather forecast from yr.no',
'page callback' => 'yr_verdata_page',
'access arguments' => array(
'access content',
),
'type' => MENU_NORMAL_ITEM,
);
$items['yr_verdata/%'] = array(
'title' => 'Yr weatherdata',
'description' => 'Weather forecast from yr.no',
'page callback' => 'yr_verdata_page',
'page arguments' => array(
1,
),
'access arguments' => array(
'access content',
),
'type' => MENU_CALLBACK,
);
$items['admin/content/yr_verdata'] = array(
'title' => 'Yr weatherdata',
'description' => 'Manage the weather forecast from yr.no',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'yr_verdata_add_location',
),
'access arguments' => array(
'manage yr_verdata',
),
'type' => MENU_NORMAL_ITEM,
);
$items['admin/content/yr_verdata/delete/%'] = array(
'title' => 'Delete location',
'description' => 'Delete a location from the local Yr weatherdata list',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'yr_verdata_delete_location',
4,
),
'access arguments' => array(
'manage yr_verdata',
),
'type' => MENU_CALLBACK,
);
$items['admin/settings/yr_verdata'] = array(
'title' => 'Yr weatherdata',
'description' => 'Options for yr_verdata.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'yr_verdata_settings',
),
'access arguments' => array(
'manage yr_verdata',
),
'type' => MENU_NORMAL_ITEM,
);
return $items;
}
/**
* Implementation of hook_perm().
*/
function yr_verdata_perm() {
return array(
'manage yr_verdata',
);
}
/**
* Function to display a page with yr_verdata stuff.
*
* @param $yid
* The {yr_verdata}.yid for this location, used to get info from the database,
* and subsequently from yr.no. If == 0, a list is displayed of all locations.
*
* @return
* Returns either a formatted page with forecast for a location, or a list of
* all locations.
*/
function yr_verdata_page($yid = 0) {
$sql = "SELECT * FROM {yr_verdata}";
if ($yid == 0) {
$sql .= " ORDER BY '%s', 'location' ASC";
$order = variable_get('yr_verdata_order', 'subregion');
$result = db_query($sql, $order);
$rows = array();
while ($row = db_fetch_array($result)) {
$tmp = explode('/', $row['url']);
$row['title'] = isset($tmp[4]) ? $tmp[4] : '';
$sep = isset($tmp[4]) ? ' - ' : '';
$row['title'] .= $tmp[4] == $tmp[3] ? '' : $sep . $tmp[3];
$row['title'] .= variable_get('yr_verdata_display_countryname', 'off') == 'on' ? ', ' . $tmp[2] . ', ' . $tmp[1] : '';
$row['title'] = str_replace('_', ' ', $row['title']);
$rows[] = l($row['title'], 'yr_verdata/' . $row['yid']);
}
if (count($rows) == 0) {
drupal_set_message(t('No locations are stored in the database.'), 'warning');
return FALSE;
}
else {
return theme('item_list', $rows);
}
}
elseif ($yid > 0) {
$sql .= " WHERE yid = %d";
$row = db_fetch_object(db_query($sql, $yid));
$output = db_affected_rows() == 1 ? theme('yr_verdata_location_page', $row) : t('No matching location was found.');
return $output;
}
else {
drupal_set_message(t('The location ID @id is unknown', array(
'@id' => $yid,
)));
}
}
/**
* Implementation of hook_block().
*/
function yr_verdata_block($op = 'list', $delta = 0, $edit = array()) {
$rows = yr_verdata_locations();
switch ($op) {
case 'list':
if (variable_get('yr_verdata_multiblock', 'off') == 'on') {
foreach ($rows as $location) {
$loc_name = $location->subregion . ', ' . $location->region . ', ' . $location->country;
if (!empty($location->location)) {
$loc_name = $location->location . ', ' . $loc_name;
}
$blocks[$location->yid]['info'] = t('Forecast for ') . $loc_name;
}
}
$blocks[0]['info'] = t('Yr.no Weather Forecast');
return $blocks;
case 'view':
if (user_access('access content')) {
if (variable_get('yr_verdata_multiblock', 'off') == 'on') {
foreach ($rows as $row) {
if ($delta == $row->yid) {
$loc_name = empty($row->location) ? $row->subregion : $row->location;
$block['subject'] = t('Forecast for ') . $loc_name;
$block['content'] = theme('yr_verdata_location_block', $row);
}
}
}
if ($delta == 0) {
$block['subject'] = t('Weather Forecast');
$block['content'] = theme('yr_verdata_location_block', $rows);
}
}
return $block;
}
}
/**
* Helper function to return all locations.
*
* @return
* Returns an array of locations as objects from the {yr_verdata} table.
*/
function yr_verdata_locations() {
$sql = "SELECT * FROM {yr_verdata} ORDER BY '%s', 'location' ASC";
$order = variable_get('yr_verdata_order', 'subregion');
$result = db_query($sql, $order);
$rows = array();
while ($row = db_fetch_object($result)) {
$rows[] = $row;
}
return !empty($rows) ? $rows : array();
}
/**
* Implementation of hook_theme().
*/
function yr_verdata_theme() {
return array(
'yr_verdata_location_page' => array(
'arguments' => array(
'row' => NULL,
),
),
'yr_verdata_location_block' => array(
'arguments' => array(
'rows' => NULL,
),
),
'yr_verdata_location_sheet' => array(
'arguments' => array(
'name' => NULL,
'tabs' => NULL,
'info' => NULL,
'links' => NULL,
'sun' => NULL,
'text_forecast' => NULL,
'symbol_forecast' => NULL,
'radar' => NULL,
'credit' => NULL,
),
'template' => 'yr-forecast-page',
),
'yr_verdata_block_location' => array(
'arguments' => array(
'location' => NULL,
),
'template' => 'yr-forecast-block-location',
),
);
}
/**
* Theming of a page with detailed forecast for a location.
*
* @param $row
* A complete row returned from a query against the database {yr_verdata} table.
*
* @return
* Returns variables to be put into the template file.
*/
function theme_yr_verdata_location_page($row) {
$modpath = drupal_get_path('module', 'yr_verdata');
drupal_add_css($modpath . '/yr_verdata.css');
// jquery_ui.module is not required, but highly recommended.
if (function_exists('jquery_ui_add')) {
jquery_ui_add('ui.tabs');
$ui_ver = variable_get('yr_jquery_ui_version', '17');
drupal_add_js($modpath . '/js/yr_verdata.' . $ui_ver . '.js');
}
// Make sure that there is no punctuation in the url, as this could possibly
// lead to a malicious user navigating elsewhere in the filesystem?
$row->url = str_replace('.', '_', $row->url);
// Load the xml file.
if (_yr_verdata_xml($row->url) != FALSE) {
$comps = explode('/', $row->url);
$local_dir = file_directory_path() . '/yr_verdata';
$local_file = $local_dir . '/' . implode('_', $comps) . '.xml';
$data = simplexml_load_file($local_file);
}
// Set up our forecast-array.
$location = array();
$location['info'] = t('@type, @altitude m.a.s. <br />Last updated !lastupdate', array(
'@type' => $data->location->type,
'@altitude' => $data->location->location['altitude'],
'!lastupdate' => _yr_format_time($data->meta->lastupdate),
));
// Links.
$location_at_yr = t('Forecast for <a href="!url" title="Go to @location at yr.no">@location at yr.no</a>', array(
'@location' => $data->location->name,
'!url' => check_url($data->links->link[1]['url']),
));
$gm_url = 'http://maps.google.com/maps?ie=UTF8&hl=en&z=12&ll=';
$gm_url .= $data->location->location['latitude'] . ',' . $data->location->location['longitude'];
$gmaps = t('Map over <a href="!url" title="Go to @location at Google Maps">@location at Google Maps</a>', array(
'@location' => $data->location->name,
'!url' => check_url($gm_url),
));
$wikipedia = t('Information about <a href="!url" title="Go to @location at Wikipedia">@location at Wikipedia</a>', array(
'@location' => $data->location->name,
'!url' => check_url('http://en.wikipedia.org/wiki/' . drupal_urlencode($data->location->name)),
));
$items = array(
$location_at_yr,
$gmaps,
$wikipedia,
);
$location['links'] = theme('item_list', $items);
// A credit notice.
$location['credit'] = l($data->credit->link['text'], $data->credit->link['url']);
// Sunrise and sunset times.
if ($data->sun['never_set'] == 'true') {
$location['sun'] = t('Midnight sun, the sun doesn’t set.');
}
elseif ($data->sun['never_rise'] == 'true') {
$location['sun'] = t('Polar night, the sun doesn’t rise.');
}
else {
$sunrise = _yr_format_time($data->sun['rise']);
$sunset = _yr_format_time($data->sun['set']);
$location['sun'] = t('Sunrise: !sunrise<br />Sunset: !sunset', array(
'!sunrise' => $sunrise,
'!sunset' => $sunset,
));
}
// Text forecast, if it exists (only for mainland Norway locations).
$location['text_forecast'] = array();
if (isset($data->forecast->text->location)) {
foreach ($data->forecast->text->location->time as $item) {
$location['text_forecast'][] = array(
'title' => drupal_ucfirst(check_plain($item->title)),
'body' => filter_xss($item->body),
);
}
}
// Set up an array with all the graphical forecast-periods.
$location['symbol_forecast'] = array();
$periods = _yr_periods();
$last_period = 0;
$i = 0;
foreach ($data->forecast->tabular->time as $tab) {
if ($i > 15 && $last_period == 3) {
break;
}
// Some of the values we are putting in needs to be "calculated".
// Main symbol:
$symbol = _yr_verdata_get_symbol($tab);
// Wind:
$wind = _yr_verdata_get_wind($tab);
// Temperature:
$temp = _yr_verdata_get_temp($tab);
// Pressure:
$pressure = $tab->pressure['value'] . $tab->pressure['unit'];
// Time:
$this_time = _yr_format_time($tab['from'], 'custom', 'D d M') . '<br />' . $periods[(int) $tab['period']];
// We need for the template to know if this was the last period of the day, to facilitate sexier styling.
$open = $i == 0 || $last_period == 3 || $tab['period'] == 2 && $last_period != 1 ? '<div class="yr-period-day clear-block">' : '';
$close = $tab['period'] == 3 || $tab['period'] == 2 && $last_period != 1 && $i != 0 ? '</div>' : '';
// Assign the calculated and other values.
$location['symbol_forecast'][] = array(
'open' => $open,
'close' => $close,
'class' => 'yr-period-' . (int) $tab['period'],
'time' => $this_time,
'symbol' => $symbol,
'wind' => $wind,
'precip' => t('@precip mm precipitation.', array(
'@precip' => $tab->precipitation['value'],
)),
'temp' => $temp,
'pressure' => check_plain($pressure),
);
$last_period = (int) $tab['period'];
$i++;
}
// If we are in mainland Norway, add a radarimage.
if (in_array($comps[1], array(
'Norway',
'Norge',
'Noreg',
))) {
$lat = (int) $data->location->location['latitude'];
$long = (int) $data->location->location['longitude'];
$radarsite = FALSE;
// Figure out which radarimage to use. Start narrow in the south and expand if lat/long is outside range.
// Finally set $showradar = FALSE; if no compatible lat/long range is found.
if ($lat >= 57 && $lat < 61 && $long >= 4 && $long < 7) {
$radarsite = 'southwest_norway';
$radartext = t('Southwest-Norway');
}
elseif ($lat >= 57 && $lat < 61 && $long >= 7 && $long <= 13) {
$radarsite = 'southeast_norway';
$radartext = t('Southeast-Norway');
}
elseif ($lat >= 60 && $lat < 62 && $long >= 4 && $long < 8) {
$radarsite = 'western_norway';
$radartext = t('Western Norway');
}
elseif ($lat >= 62 && $lat < 66 && $long >= 4 && $long <= 14) {
$radarsite = 'central_norway';
$radartext = t('Central Norway');
}
elseif ($lat >= 57 && $lat < 66 && $long >= 4 && $long <= 14) {
$radarsite = 'south_norway';
$radartext = t('Southern Norway');
}
elseif ($lat >= 65 && $lat < 69 && $long > 10 && $long <= 18) {
$radarsite = 'nordland_troms';
$radartext = t('Northern Norway');
}
elseif ($lat >= 68 && $lat < 72 && $long > 17 && $long <= 30) {
$radarsite = 'troms_finnmark';
$radartext = t('Northernmost Norway');
}
else {
$radarsite = FALSE;
}
$radar_url = check_url('http://api.yr.no/weatherapi/radar/1.2/?radarsite=' . $radarsite . ';width=460;type=animation');
$radar = '<img src="' . $radar_url . '" alt="' . $radartext . '" />';
$radarlink = !empty($data->links->link[5]['url']) ? check_url($data->links->link[5]['url']) : check_url($data->links->link[1]['url']);
$location['radar']['text'] = $radarsite == FALSE ? t('No image') : $radartext;
$location['radar']['image'] = $radarsite == FALSE ? t('This location is currently not covered by a radar.') : l($radar, $radarlink, array(
'html' => TRUE,
));
}
else {
$location['radar'] = '';
}
// Add the tabs titles.
$location['tabs'] = array(
t('Info'),
t('Text forecast'),
t('Detailed forecast'),
t('Radar image'),
);
// Add the location name, for those who want to customize the .tpl file.
$location['name'] = check_plain($data->location->name);
// Theme the output.
$output = theme('yr_verdata_location_sheet', $location['name'], $location['tabs'], $location['info'], $location['links'], $location['sun'], $location['text_forecast'], $location['symbol_forecast'], $location['radar'], $location['credit']);
// Set the page title.
drupal_set_title(t('Weather forecast for @location', array(
'@location' => $data->location->name,
)));
// If we don't have a valid location, $data will be empty, and the page should just reflect that.
if ($data != TRUE) {
$unavailable = t('Weather forecast unavailable');
drupal_set_title($unavailable);
$output = '';
}
return $output;
}
function theme_yr_verdata_location_block($rows) {
$modpath = drupal_get_path('module', 'yr_verdata');
drupal_add_css($modpath . '/yr_verdata.css');
$output = '';
// If we are just theming a single-location block, we still need to make it an array.
if (!is_array($rows)) {
$rows = array(
$rows,
);
}
foreach ($rows as $row) {
// Make sure there are no punctuation in the url, as this could possibly lead to a malicious user
// navigating elsewhere in the filesystem?
$row->url = str_replace('.', '_', $row->url);
_yr_verdata_xml($row->url);
// Load the xml file.
$local_dir = file_directory_path() . '/yr_verdata';
$comps = explode('/', $row->url);
$local_file = $local_dir . '/' . implode('_', $comps) . '.xml';
$data = simplexml_load_file($local_file);
// Some of the values we are putting in, needs to be "calculated".
// Main symbol:
$symbol = _yr_verdata_get_symbol($data->forecast->tabular->time[0]);
// Wind:
$wind = _yr_verdata_get_wind($data->forecast->tabular->time[0]);
// Temperature:
$temp = _yr_verdata_get_temp($data->forecast->tabular->time[0]);
// Pressure:
$pressure = $data->forecast->tabular->time[0]->pressure['value'] . $data->forecast->tabular->time[0]->pressure['unit'];
// Time:
$periods = _yr_periods();
$this_time = _yr_format_time($data->forecast->tabular->time[0]['from'], 'custom', 'D d M') . '<br />' . $periods[(int) $data->forecast->tabular->time[0]['period']];
$location = array(
'name' => l($data->location->name, 'yr_verdata/' . $row->yid),
'symbol' => $symbol,
'wind' => $wind,
'temp' => $temp,
'precip' => t('@precip mm precipitation.', array(
'@precip' => $data->forecast->tabular->time[0]->precipitation['value'],
)),
'pressure' => check_plain($pressure),
'time' => $this_time,
);
$output .= theme('yr_verdata_block_location', $location);
}
if (count($rows) > 0) {
$output .= '<div class="yr-credit"><p>' . t('<a href="!yrl" title="Go to yr.no to view forecasts for more than 7 million locations across the globe">Forecast from yr.no</a>', array(
'!yrl' => 'http://yr.no',
)) . '</p></div>';
}
return $output;
}
/**
* The administrative settings form.
*
* @return
* Returns a form array, processed by system_settings_form().
*/
function yr_verdata_settings() {
// Make sure that the file directory exists.
$local_dir = file_directory_path() . '/yr_verdata';
file_check_directory($local_dir, 1);
// Set up the form.
$form['yr_verdata_settings'] = array(
'#prefix' => '<div id="yr-admin-settings">',
'#suffix' => '</div>',
);
$form['yr_verdata_settings']['yr_verdata_lang'] = array(
'#type' => 'select',
'#title' => t('Default language'),
'#options' => array(
'place' => t('English'),
'sted' => t('Norwegian Bokmål'),
'stad' => t('Norwegian Nynorsk'),
),
'#default_value' => variable_get('yr_verdata_lang', 'place'),
'#description' => t('Note that for locations in mainland Norway, a text forecast is included, and this is only supplied in Norwegian.'),
);
$form['yr_verdata_settings']['yr_verdata_maxage'] = array(
'#type' => 'select',
'#title' => t('Maximum age of XML files'),
'#options' => array(
1 => t('Always update (debugging only)'),
900 => t('15 minutes'),
3600 => t('One hour'),
10800 => t('Three hours'),
21600 => t('Six hours'),
43200 => t('12 hours'),
86400 => t('24 hours'),
),
'#default_value' => variable_get('yr_verdata_maxage', 21600),
'#description' => t('Setting a value less than 3 hours is probably useless, since the forecast is not updated that often at yr.no. 3 hours should be plenty to keep your site up to date while avoiding unneccessary server load.'),
);
$form['yr_verdata_settings']['yr_verdata_order'] = array(
'#type' => 'select',
'#title' => t('Default sort order'),
'#options' => array(
'location' => t('Location name'),
'subregion' => t('Subregion/municipality'),
'region' => t('Region/state/county'),
'country' => t('Country/continent'),
),
'#default_value' => variable_get('yr_verdata_order', 'subregion'),
);
$form['yr_verdata_settings']['yr_verdata_display_countryname'] = array(
'#type' => 'radios',
'#title' => t('Show the region and country name in listings'),
'#options' => array(
'on' => t('On'),
'off' => t('Off'),
),
'#default_value' => variable_get('yr_verdata_display_countryname', 'off'),
);
$form['yr_verdata_settings']['yr_verdata_multiblock'] = array(
'#type' => 'radios',
'#title' => t('Multiple blocks'),
'#options' => array(
'on' => t('On'),
'off' => t('Off'),
),
'#default_value' => variable_get('yr_verdata_multiblock', 'off'),
'#description' => t('Yr verdata provides one block, listing all locations. If you want to, it can additionally provide one block for each location. Note that this may look cluttered, so it can be a good idea to use page-specific settings for these blocks.'),
);
$form['yr_verdata_settings']['yr_jquery_ui_version'] = array(
'#type' => 'radios',
'#title' => t('jQuery UI version'),
'#options' => array(
'16' => t('jQuery UI 1.6 and jQuery 1.2.x'),
'17' => t('jQuery UI 1.7 and jQuery 1.3.x'),
),
'#default_value' => variable_get('yr_jquery_ui_version', '17'),
'#description' => t('If you are using !jquery_update with <em>jQuery 1.3.x</em>, you should use <em>jQuery UI 1.7</em>. Otherwise, use <em>jQuery UI 1.6</em> (this will work with the default jQuery version shipped with Drupal).', array(
'!jquery_ui' => l('jQuery UI', 'http://drupal.org/project/jquery_ui'),
'!jquery_update' => l('the jQuery update module', 'http://drupal.org/project/jquery_update'),
)),
);
$form = system_settings_form($form);
unset($form['#submit']);
$form['#submit'][] = 'yr_verdata_settings_submit';
return $form;
}
/**
* Implementation of hook_submit().
*/
function yr_verdata_settings_submit($form, &$form_state) {
// If the user hit the 'Reset to defaults button' we do that then return.
if ($form_state['clicked_button']['#parents'][0] == 'reset') {
variable_set('yr_verdata_lang', 'place');
variable_set('yr_verdata_maxage', 21600);
variable_set('yr_verdata_order', 'subregion');
variable_set('yr_verdata_display_countryname', 'off');
variable_set('yr_verdata_multiblock', 'off');
variable_set('yr_jquery_ui_version', '17');
drupal_set_message(t('The configuration options have been reset to their default values.'));
}
else {
$lang = $form_state['values']['yr_verdata_lang'];
$maxage = (int) $form_state['values']['yr_verdata_maxage'];
$order = $form_state['values']['yr_verdata_order'];
$multiblock = $form_state['values']['yr_verdata_multiblock'];
variable_set('yr_verdata_lang', $lang);
variable_set('yr_verdata_maxage', $maxage);
variable_set('yr_verdata_order', $order);
variable_set('yr_verdata_display_countryname', $form_state['values']['yr_verdata_display_countryname']);
variable_set('yr_verdata_multiblock', $multiblock);
variable_set('yr_jquery_ui_version', $form_state['values']['yr_jquery_ui_version']);
drupal_set_message(t('Settings saved.'));
}
}
/**
* Function for adding locations.
*
* @return
* Returns a form array, to be processed by drupal_get_form().
*/
function yr_verdata_add_location() {
$form = array();
$form['add_new'] = array(
'#type' => 'fieldset',
'#title' => t('Add new location'),
'#collapsible' => TRUE,
);
$form['add_new']['language'] = array(
'#type' => 'select',
'#title' => t('Language'),
'#options' => array(
'place' => t('English'),
'sted' => t('Norwegian Bokmål'),
'stad' => t('Norwegian Nynorsk'),
),
'#default_value' => variable_get('yr_verdata_lang', 'place'),
'#description' => t('Select the language you want this forecast delivered in.'),
'#required' => TRUE,
);
$form['add_new']['country'] = array(
'#type' => 'textfield',
'#title' => t('Country'),
'#description' => t('The first part of the url at <a href="!yrl">yr.no</a> (after <em>http://yr.no/place/</em>). For example: http://yr.no/place/<strong>Norway</strong>/Vest-Agder/Kristiansand/Hamresanden.', array(
'!yrl' => 'http://yr.no',
)),
'#required' => TRUE,
);
$form['add_new']['region'] = array(
'#type' => 'textfield',
'#title' => t('Region'),
'#description' => t('The second part of the url at <a href="!yrl">yr.no</a> (after <em>http://yr.no/place/</em>). For example: http://yr.no/place/Norway/<strong>Vest-Agder</strong>/Kristiansand/Hamresanden.', array(
'!yrl' => 'http://yr.no',
)),
'#required' => TRUE,
);
$form['add_new']['subregion'] = array(
'#type' => 'textfield',
'#title' => t('Subregion/City'),
'#description' => t('The third part of the url at <a href="!yrl">yr.no</a> (after <em>http://yr.no/place/</em>). For example: http://yr.no/place/Norway/Vest-Agder/<strong>Kristiansand</strong>/Hamresanden.', array(
'!yrl' => 'http://yr.no',
)),
'#required' => TRUE,
);
$form['add_new']['location'] = array(
'#type' => 'textfield',
'#title' => t('Location'),
'#description' => t('The fourth part of the url at <a href="!yrl">yr.no</a> (after <em>http://yr.no/place/</em>). For example: http://yr.no/place/Norway/Vest-Agder/Kristiansand/<strong>Hamresanden</strong>.<br />Note that some locations do not have this part of the url and for those, this part is not required.', array(
'!yrl' => 'http://yr.no',
)),
);
$form['add_new']['submit'] = array(
'#type' => 'submit',
'#value' => t('Save location'),
);
// Get all existing locations for display in a table.
$order = variable_get('yr_verdata_order', 'subregion');
$result = db_query("SELECT * FROM {yr_verdata} ORDER BY '%s', 'location' ASC", $order);
$rows = array();
while ($row = db_fetch_array($result)) {
$tmp = explode('/', $row['url']);
$row['title'] = $tmp[3];
$row['title'] .= isset($tmp[4]) ? ' - ' . $tmp[4] : '';
$row['title'] .= ', ' . $tmp[2] . ', ' . $tmp[1];
$row['title'] = str_replace('_', ' ', $row['title']);
$rows[] = array(
$row['yid'],
l($row['title'], 'yr_verdata/' . $row['yid']),
l('X', 'admin/content/yr_verdata/delete/' . $row['yid']),
);
}
if (count($rows) == 0) {
$rows = array(
'data' => array(
t('No locations are stored in the database.'),
array(
'colspan' => 3,
),
),
);
}
$headers = array(
t('YID'),
t('Location'),
t('Delete'),
);
$form['existing'] = array(
'#type' => 'fieldset',
'#title' => t('Existing locations'),
'#collapsible' => TRUE,
);
$form['existing']['table'] = array(
'#type' => 'item',
'#value' => theme('table', $headers, $rows),
);
return $form;
}
/**
* Implementation of hook_validate().
*/
function yr_verdata_add_location_validate($form, &$form_state) {
// If the country is Norway, the location is required.
if (in_array($form_state['values']['country'], array(
'Norway',
'Norge',
'Noreg',
))) {
if (empty($form_state['values']['location'])) {
form_set_error('location', t('In Norway, a specific location name is required.'));
}
}
// And check to see if this location already exists in the database.
$url = $form_state['values']['language'] . '/' . $form_state['values']['country'] . '/' . $form_state['values']['region'] . '/' . $form_state['values']['subregion'];
$url .= !empty($form_state['values']['location']) ? '/' . $form_state['values']['location'] : '';
$result = db_query("SELECT yid FROM {yr_verdata} WHERE url = '%s'", $url);
if (db_affected_rows() > 0) {
form_set_error('location', t('This location already exists in the database.'));
}
}
/**
* Implementation of hook_submit().
*/
function yr_verdata_add_location_submit($form, &$form_state) {
$url = $form_state['values']['language'] . '/' . $form_state['values']['country'] . '/' . $form_state['values']['region'] . '/' . $form_state['values']['subregion'];
$url .= !empty($form_state['values']['location']) ? '/' . $form_state['values']['location'] : '';
$url = str_replace(' ', '_', $url);
// Attempt to underscore any spaces in names, for better usability.
// To make sure that locations without a locationname don't always show up first, we give them 'x' as location name.
$location = !empty($form_state['values']['location']) ? $form_state['values']['location'] : 'x';
db_query("INSERT INTO {yr_verdata} (url, location, subregion, region, country) VALUES ('%s', '%s', '%s', '%s', '%s')", $url, $location, $form_state['values']['subregion'], $form_state['values']['region'], $form_state['values']['country']);
}
/**
* Function for deleting locations.
*
* @param $yid
* The {yr_verdata}.yid for this location, taken from the url.
*/
function yr_verdata_delete_location($form, $yid) {
$sql = "SELECT * FROM {yr_verdata} WHERE yid = %d";
$row = db_fetch_object(db_query($sql, $yid));
$form = array();
if (db_affected_rows() == 1) {
$form['delete_location']['name'] = array(
'#type' => 'item',
'#value' => t('Warning: You are about to delete the location %location from the database. This action cannot be undone.', array(
'%location' => $row->url,
)),
);
$form['delete_location']['yid'] = array(
'#type' => 'hidden',
'#value' => $yid,
);
$form['delete_location']['submit'] = array(
'#type' => 'submit',
'#value' => t('Delete'),
);
$form['delete_location']['cancel'] = array(
'#type' => 'item',
'#value' => l(t('Cancel'), 'admin/content/yr_verdata'),
);
}
else {
drupal_set_message(t('An invalid location id was given. Please try again.'), 'error');
$form['delete_location']['cancel'] = array(
'#type' => 'item',
'#value' => l(t('Go back'), 'admin/content/yr_verdata'),
);
}
return $form;
}
/**
* Implementation of hook_validate().
*/
function yr_verdata_delete_location_validate($form, &$form_state) {
if (empty($form_state['values']['yid'])) {
form_set_error('yid', t('No ID for the location was given. Could not delete location.'));
}
}
/**
* Implementation of hook_submit().
*/
function yr_verdata_delete_location_submit($form, &$form_state) {
if (db_query("DELETE FROM {yr_verdata} WHERE yid = %d", $form_state['values']['yid'])) {
drupal_set_message(t('Location deleted.'));
}
drupal_redirect_form($form, 'admin/content/yr_verdata');
}
/**
* Function for retrieving XML data from yr.no
*
* @param $url
* The string to append to "http://yr.no/".
*
* @return
* An object made with simplexml based on the XML data from yr.no.
*/
function _yr_verdata_xml($url) {
$comps = explode('/', $url);
switch ($comps[0]) {
case 'place':
$xml = 'forecast.xml';
break;
case 'sted':
$xml = 'varsel.xml';
break;
case 'stad':
$xml = 'varsel.xml';
break;
}
// end switch lang
$remote_file = check_url('http://www.yr.no/' . drupal_urlencode($url) . '/' . $xml);
$local_dir = file_directory_path() . '/yr_verdata';
file_check_directory($local_dir, 1);
$local_file = $local_dir . '/' . implode('_', $comps) . '.xml';
$success = FALSE;
$local_file_age = @filemtime($local_file);
$now = time();
$maxage = variable_get('yr_verdata_maxage', 21600);
$timeout = 10;
if ($maxage > 0 && $now - $local_file_age > $maxage) {
if (function_exists('curl_init')) {
$c_handle = curl_init();
curl_setopt($c_handle, CURLOPT_URL, $remote_file);
curl_setopt($c_handle, CURLOPT_RETURNTRANSFER, TRUE);
$data = curl_exec($c_handle);
$valid_xml = simplexml_load_string($data);
$c_info = curl_getinfo($c_handle);
if ($c_info['http_code'] != '200' || $c_info['size_download'] < 5000 || !$valid_xml) {
// If the server is unresponsive, or down for maintenance, we provide an error.
drupal_set_message(t('The file retrieved from @url was not a valid XML-file for this data type. This may be because yr.no is down for maintenance. Also, make sure that the location you specified is spelled correctly.', array(
'@url' => $remote_file,
)), 'warning');
watchdog('yr_verdata', 'The file retrieved from @url was not a valid XML-file for this data type. This may be because yr.no is down for maintenance. Also, make sure that the location you specified is spelled correctly.', array(
'@url' => $remote_file,
));
$success = FALSE;
}
else {
// The server returned http = 200, we assume we can load the xml data.
$f_handle = fopen($local_file, 'w');
curl_setopt($c_handle, CURLOPT_FILE, $f_handle);
curl_setopt($c_handle, CURLOPT_HEADER, 0);
curl_setopt($c_handle, CURLOPT_CONNECTTIMEOUT, $timeout);
curl_setopt($c_handle, CURLOPT_TIMEOUT, $timeout);
curl_exec($c_handle);
fclose($f_handle);
$success = TRUE;
}
curl_close($c_handle);
}
else {
if ($message == 1) {
drupal_set_message(t('No method for retrieving forecast data was found. You must have PHP 5 with cURL available.'), 'error');
}
watchdog('yr_verdata', 'Could not retrieve remote XML data from @url, because cURL was not found.', array(
'@url' => $remote_file,
));
$success = FALSE;
}
}
else {
$success = 'not maxage';
}
return $success;
}
/**
* Helper function to resolve periods of forecast.
*
* @return
* Returns an array with simple hour notations in a 24H format.
*/
function _yr_periods() {
return array(
0 => '00-06',
1 => '06-12',
2 => '12-18',
3 => '18-00',
);
}
/**
* Helper function to change datetime from the xml into drupal time.
*
* @param $datetime
* A date and time given as yyyy-mm-ddThh:mm:ss
* @param $type
* The Drupal time format to use. Defaults to 'medium'.
* @param $format
* If $format = 'custom', use a PHP date string here.
*
* @return
* Returns a date and time formatted by Drupal's format_date().
* We pass 0 to the timezone parameter, because the datetime from
* yr.no is given in the location's local time.
*
* @see format_date()
* @see date()
*/
function _yr_format_time($datetime, $type = 'medium', $format = '') {
$items = explode('T', $datetime);
$date = explode('-', $items[0]);
$time = explode(':', $items[1]);
$tz = date_default_timezone_get();
date_default_timezone_set('UTC');
$timestamp = mktime($time[0], $time[1], $time[2], $date[1], $date[2], $date[0]);
date_default_timezone_set($tz);
return format_date($timestamp, $type, $format, 0);
}
/**
* Helper function for creating <img> tag for the symbol.
*/
function _yr_verdata_get_symbol($tab) {
$nsym = FALSE;
switch ($tab->symbol['number']) {
case '01':
case '02':
case '03':
case '05':
case '06':
case '07':
case '08':
$nsym = TRUE;
break;
}
$sym = drupal_strlen($tab->symbol['number']) > 1 ? $tab->symbol['number'] : '0' . $tab->symbol['number'];
if ($nsym == TRUE) {
$sym .= $tab['period'] === '0' ? 'n' : 'd';
}
$url = check_url('http://fil.nrk.no/yr/grafikk/sym/b38/' . $sym . '.png');
return '<img src="' . $url . '" alt="' . $tab->symbol['name'] . '" />';
}
/**
* Helper function for creating wind data and image.
*/
function _yr_verdata_get_wind($tab) {
$wind = array();
// Round degrees to nearest 5 and make sure it's a 3-digit number.
$m = $tab->windDirection['deg'] % 5;
$wind['dir']['deg'] = $tab->windDirection['deg'] - $m;
if (drupal_strlen($wind['dir']['deg']) == 2) {
$wind['dir']['deg'] = '0' . $wind['dir']['deg'];
}
elseif (drupal_strlen($wind['dir']['deg']) == 1) {
$wind['dir']['deg'] = '00' . $wind['dir']['deg'];
}
// 360 degrees won't work, so set to 0 in that case.
if ($wind['dir']['deg'] == 360) {
$wind['dir']['deg'] = '000';
}
$wind['dir']['name'] = $tab->windDirection['name'];
$wind['dir']['code'] = $tab->windDirection['code'];
$wind['speed']['name'] = $tab->windSpeed['name'];
// Speed is given in nearest 2.5, but in the url for the image, the number is multiplied by 10.
$wind['speed']['mps'] = $tab->windSpeed['mps'] * 10;
$wind['speed']['mps'] = round($wind['speed']['mps'] / 25) * 25;
// Add leading zeroes if needed.
if ($wind['speed']['mps'] > 99) {
$wind['speed']['mps'] = '0' . $wind['speed']['mps'];
}
elseif ($wind['speed']['mps'] > 9) {
$wind['speed']['mps'] = '00' . $wind['speed']['mps'];
}
else {
$wind['speed']['mps'] = '000' . $wind['speed']['mps'];
}
// And set up the image url.
$windurl = check_url('http://fil.nrk.no/yr/grafikk/vindpiler/32/vindpil.' . $wind['speed']['mps'] . '.' . $wind['dir']['deg'] . '.png');
$wind['img'] = '<img src="' . $windurl . '" alt="' . check_plain($wind['speed']['name']) . ' ' . check_plain($wind['dir']['code']) . '" />';
return $wind;
}
/**
* Helper function to create temperature data.
*/
function _yr_verdata_get_temp($tab) {
$temp = array();
$temp['val'] = check_plain($tab->temperature['value']) . '°';
$temp['val'] .= $tab->temperature['unit'] == 'celcius' ? 'C' : 'F';
$temp['class'] = (int) $tab->temperature['value'] >= 0 ? 'plus' : 'minus';
return $temp;
}
Functions
Name![]() |
Description |
---|---|
theme_yr_verdata_location_block | |
theme_yr_verdata_location_page | Theming of a page with detailed forecast for a location. |
yr_verdata_add_location | Function for adding locations. |
yr_verdata_add_location_submit | Implementation of hook_submit(). |
yr_verdata_add_location_validate | Implementation of hook_validate(). |
yr_verdata_block | Implementation of hook_block(). |
yr_verdata_delete_location | Function for deleting locations. |
yr_verdata_delete_location_submit | Implementation of hook_submit(). |
yr_verdata_delete_location_validate | Implementation of hook_validate(). |
yr_verdata_locations | Helper function to return all locations. |
yr_verdata_menu | Implementation of hook_menu(). |
yr_verdata_page | Function to display a page with yr_verdata stuff. |
yr_verdata_perm | Implementation of hook_perm(). |
yr_verdata_settings | The administrative settings form. |
yr_verdata_settings_submit | Implementation of hook_submit(). |
yr_verdata_theme | Implementation of hook_theme(). |
_yr_format_time | Helper function to change datetime from the xml into drupal time. |
_yr_periods | Helper function to resolve periods of forecast. |
_yr_verdata_get_symbol | Helper function for creating <img> tag for the symbol. |
_yr_verdata_get_temp | Helper function to create temperature data. |
_yr_verdata_get_wind | Helper function for creating wind data and image. |
_yr_verdata_xml | Function for retrieving XML data from yr.no |