array('access content'),
'page callback' => '_location_autocomplete',
'type' => MENU_CALLBACK,
);
$items['admin/settings/location'] = array(
'title' => 'Location',
'description' => 'Settings for Location module',
'page callback' => 'drupal_get_form',
'page arguments' => array('location_admin_settings'),
'file' => 'location.admin.inc',
'access arguments' => array('administer site configuration'),
);
$items['admin/settings/location/main'] = array(
'title' => 'Main settings',
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['admin/settings/location/maplinking'] = array(
'title' => 'Map links',
'page callback' => 'drupal_get_form',
'page arguments' => array('location_map_link_options_form'),
'access arguments' => array('administer site configuration'),
'file' => 'location.admin.inc',
'type' => MENU_LOCAL_TASK,
'weight' => 1,
);
$items['admin/settings/location/geocoding'] = array(
'title' => 'Geocoding options',
'page callback' => 'drupal_get_form',
'page arguments' => array('location_geocoding_options_form'),
'access arguments' => array('administer site configuration'),
'file' => 'location.admin.inc',
'type' => MENU_LOCAL_TASK,
'weight' => 2,
);
$items['admin/settings/location/geocoding/%/%'] = array(
'page callback' => 'location_geocoding_parameters_page',
'page arguments' => array(4, 5),
'access arguments' => array('administer site configuration'),
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Implementation of hook_perm().
*/
function location_perm() {
return array(
'submit latitude/longitude',
'administer user locations',
'set own user location',
'view own user location',
'view all user locations',
);
}
/**
* Implementation of hook_help().
*
* @TODO: check/fix this: admin/content/configure/types (still use %? still same url?)
*/
function location_help($path, $arg) {
switch ($path) {
case 'admin/help#location':
$output = '
'. t('The location module allows you to associate a geographic location with content and users. Users can do proximity searches by postal code. This is useful for organizing communities that have a geographic presence.') .'
';
$output .= '
'. t('To administer locative information for content, use the content type administration page. To support most location enabled features, you will need to install the country specific include file. To support postal code proximity searches for a particular country, you will need a database dump of postal code data for that country. As of June 2007 only U.S. and German postal codes are supported.') .'
'. t('For more information please read the configuration and customization handbook Location page.', array('@location' => 'http://www.drupal.org/handbook/modules/location/')) .'
' . t('If you wish to supply your own latitude and longitude, you may enter them above. If you leave these fields blank, the system will attempt to determine a latitude and longitude for you from the entered address. To have the system recalculate your location from the address, for example if you change the address, delete the values for these fields.'),
'#suffix' => '
',
'#value' => t('You may set the location by clicking on the map, or dragging the location marker. To clear the location and cause it to be recalculated, click on the marker.'),
'#suffix' => '
',
);
}
if (isset($dv['lid']) && $dv['lid'] !== FALSE) {
$element['delete_location'] = array(
'#type' => 'checkbox',
'#title' => t('Delete'),
'#default_value' => FALSE,
'#description' => t('Check this box to delete this location.'),
);
}
}
return $element;
}
/**
* Theme the location element.
* @ingroup themable
*/
function theme_location_elementzzz($element) {
drupal_add_css(drupal_get_path('module', 'location') .'/location.css');
/*
// @@@ This *DEFINATELY* needs revisited.
foreach (element_children($element) as $field_name) {
$row = array();
if ($element[$field_name]['#type'] == 'markup') {
$row[] = array('data' => $element[$field_name]['#value'], 'colspan' => 2);
}
elseif ($element[$field_name]['#type'] != 'hidden') {
$required = !empty($element[$field_name]['#required']) ? '*' : '';
$row[] = array('align' => 'right', 'data' => '");
unset($element[$field_name]['#title']);
$description = $element[$field_name]['#description'];
$row[] = array('align' => 'left', 'data' => drupal_render($element[$field_name]));
$rows[] = array(
'data' => $row,
'class' => 'odd'
);
}
}
$output = theme('table', NULL, $rows);
$output .= drupal_render($element);
return $output;*/
if ($element['#collapsible']) {
drupal_add_js('misc/collapse.js');
if (!isset($element['#attributes']['class'])) {
$element['#attributes']['class'] = '';
}
$element['#attributes']['class'] .= ' collapsible';
if ($element['#collapsed']) {
$element['#attributes']['class'] .= ' collapsed';
}
}
return '\n";
/*
$element['#type'] = 'fieldset';
return theme('form_element', $element, $element['#children']);
return drupal_render($element);
return theme_fieldset($element);
return theme('fieldset', $element, $element['#children']);
*/
}
function _location_expand_location_settings(&$element) {
// looks like core sets $element['#value'] to an empty array even though $element['#type'] == 'fieldset'
unset($element['#value']);
$element['#tree'] = TRUE;
$element['#type'] = 'fieldset';
if (!isset($element['#title'])) {
$element['#title'] = t('Location Fields');
}
if (!isset($element['#default_value']) || $element['#default_value'] == 0) {
$element['#default_value'] = array();
}
// Force #tree on.
$element['#tree'] = TRUE;
$defaults = location_invoke_locationapi($element, 'collection default');
$defaults = array_merge($defaults, $element['#default_value']);
$fields = location_field_names();
// Options for fields.
$options = array(
t('Do not collect'),
t('Allow'),
t('Require'),
);
foreach ($fields as $field => $title) {
$element[$field] = array(
'#type' => 'radios',
'#title' => $title,
'#default_value' => $defaults[$field],
'#options' => $options,
'#tree' => TRUE,
);
}
// Override country field.
$element['country'] = array(
'#type' => 'radios',
'#title' => t('Country names'),
'#description' => t('When using "Force Default", the country field will be set to the value of the "Default country selection" dropdown on the location settings page and hidden from the user.', array('@location_settings' => url('admin/settings/location'))),
'#default_value' => $defaults['country'],
'#options' => array(
// 0 is omitted so the behavior lines up properly with everything else.
1 => t('Use'),
2 => t('Require'),
3 => t('Force Default'),
),
);
// 'Street Additional' field should depend on 'Street' setting.
// It should never be required and should only display when the street field is 'allowed' or 'required'
unset($element['additional']);
// @@@ Alter here?
return $element;
}
function theme_location_settings($element) {
return theme('form_element', $element, $element['#children']);
}
function location_field_names($all = FALSE) {
static $fields;
static $allfields;
if ($all) {
if (empty($allfields)) {
$dummy = array();
$allfields = location_invoke_locationapi($dummy, 'fields');
}
return $allfields;
}
else {
if (empty($fields)) {
$dummy = '';
$fields = location_invoke_locationapi($dummy, 'fields');
$virtual = location_invoke_locationapi($dummy, 'virtual fields');
foreach ($virtual as $field) {
unset ($fields[$field]);
}
}
return $fields;
}
}
/**
* Implementation of hook_locationapi().
*/
function location_locationapi(&$obj, $op, $a3 = NULL, $a4 = NULL, $a5 = NULL) {
switch ($op) {
case 'fields':
return array('name' => t('Location name'), 'street' => t('Street location'), 'additional' => t('Additional'), 'city' => t('City'), 'province' => t('State/Province'), 'postal_code' => t('Postal code'), 'country' => t('Country'));
case 'virtual fields':
return array('province_name', 'country_name');
case 'collection default':
return array('name' => 1, 'street' => 1, 'additional' => 1, 'city' => 0, 'province' => 0, 'postal_code' => 0, 'country' => 1);
case 'default values':
return array(
'name' => '',
'street' => '',
'additional' => '',
'city' => '',
'province' => '',
'postal_code' => '',
'country' => variable_get('location_default_country', 'us'),
'latitude' => 0,
'longitude' => 0,
'source' => LOCATION_LATLON_UNDEFINED,
'is_primary' => 0, // @@@
);
case 'validate':
if (!empty($obj['country'])) {
if (!empty($obj['province'])) {
$provinces = location_get_provinces($obj['country']);
$found = FALSE;
$p = strtoupper($obj['province']);
foreach ($provinces as $k => $v) {
if ($p == strtoupper($k) || $p == strtoupper($v)) {
$found = TRUE;
break;
}
}
if (!$found) {
form_error($a3['province'], t('The specified province was not found in the specified country.'));
}
}
}
// Can't specify just latitude or just longitude.
if (_location_floats_are_equal($obj['locpick']['user_latitude'], 0) xor _location_floats_are_equal($obj['locpick']['user_longitude'], 0)) {
$ref = &$a3['locpick']['user_latitude'];
if (_location_floats_are_equal($obj['locpick']['user_longitude'], 0)) {
$ref = &$a3['locpick']['user_longitude'];
}
form_error($ref, t('You must fill out both latitude and longitude or you must leave them both blank.'));
}
break;
case 'field_expand':
switch ($a3) {
case 'name':
return array(
'#type' => 'textfield',
'#title' => t('Location name'),
'#default_value' => $obj,
'#size' => 64,
'#maxlength' => 64,
'#description' => t('e.g. a place of business, venue, meeting point'),
'#attributes' => NULL,
'#required' => ($a4 == 2),
);
case 'street':
return array(
'#type' => 'textfield',
'#title' => t('Street'),
'#default_value' => $obj,
'#size' => 64,
'#maxlength' => 64,
'#required' => ($a4 == 2),
);
// Additional is linked to street.
case 'additional':
return array(
'#type' => 'textfield',
'#title' => t('Additional'),
'#default_value' => $obj,
'#size' => 64,
'#maxlength' => 64,
// Required is forced OFF because this is technically part of street.
);
case 'city':
return array(
'#type' => 'textfield',
'#title' => t('City'),
'#default_value' => $obj,
'#size' => 64,
'#maxlength' => 64,
'#description' => NULL,
'#attributes' => NULL,
'#required' => ($a4 == 2),
);
case 'province':
drupal_add_js(drupal_get_path('module', 'location') .'/location_autocomplete.js');
return array(
'#type' => 'textfield',
'#title' => t('State/Province'),
'#autocomplete_path' => 'location/autocomplete/'. $a5['country'],
'#default_value' => $obj,
'#size' => 64,
'#maxlength' => 64,
'#description' => NULL,
// Used by province autocompletion js.
'#attributes' => array('class' => 'location_auto_province'),
'#required' => ($a4 == 2),
);
case 'country':
// Force default.
if ($a4 == 3) {
return array(
'#type' => 'value',
'#value' => variable_get('location_default_country', 'us'),
);
}
else {
$options = array_merge(array('' => t('Please select'), 'xx' => 'NOT LISTED'), location_get_iso3166_list());
return array(
'#type' => 'select',
'#title' => t('Country'),
'#default_value' => $obj,
'#options' => $options,
'#description' => NULL,
'#required' => ($a4 == 2),
// Used by province autocompletion js.
'#attributes' => array('class' => 'location_auto_country'),
);
}
break;
case 'postal_code':
return array(
'#type' => 'textfield',
'#title' => t('Postal code'),
'#default_value' => $obj,
'#size' => 16,
'#maxlength' => 16,
'#required' => ($a4 == 2),
);
}
break;
case 'isunchanged':
if ($a3 == 'latitude' || $a3 == 'longitude') {
if (_location_floats_are_equal($obj[$a3], $a4)) {
return TRUE;
}
}
if ($a3 == 'country') {
// Consider ' ' and '' to be equivilent, due to us storing country
// as char(2) in the database.
if (trim($obj[$a3]) == trim($a4)) {
return TRUE;
}
}
break;
}
}
function location_geocoding_parameters_page($country_iso, $service) {
drupal_set_title(t('Configure parameters for %service geocoding', array('%service' => $service)));
$breadcrumbs = drupal_get_breadcrumb();
$breadcrumbs[] = l('location', 'admin/settings/location');
$breadcrumbs[] = l('geocoding', 'admin/settings/location/geocoding');
$countries = location_get_iso3166_list();
$breadcrumbs[] = l($countries[$country_iso], 'admin/settings/location/geocoding', array('fragment' => $country_iso));
drupal_set_breadcrumb($breadcrumbs);
return drupal_get_form('location_geocoding_parameters_form', $country_iso, $service);
}
function location_geocoding_parameters_form(&$form_state, $country_iso, $service) {
location_load_country($country_iso);
$geocode_settings_form_function_specific = 'location_geocode_'. $country_iso .'_'. $service .'_settings';
$geocode_settings_form_function_general = $service .'_geocode_settings';
if (function_exists($geocode_settings_form_function_specific)) {
return system_settings_form($geocode_settings_form_function_specific());
}
location_load_geocoder($service);
if (function_exists($geocode_settings_form_function_general)) {
return system_settings_form($geocode_settings_form_function_general());
}
else {
return system_settings_form(array(
'#type' => 'markup',
'#value' => t('No configuration parameters are necessary, or a form to take such paramters has not been implemented.')
));
}
}
/**
* Alter the node_type_form form.
*/
function location_form_node_type_form_alter(&$form, &$form_state) {
$type = $form['#node_type']->type;
// Hook the form handlers so we can correctly extract our information;
// the node type form doesn't handle nested values correctly.
array_unshift($form['#validate'], 'location_node_settings_validate');
//array_unshift($form['#submit'], 'node_type_form_submit');
array_unshift($form['#submit'], '_location_node_type_save_submit');
$form['location'] = array_merge(
is_array($form['location']) ? $form['location'] : array(),
array(
'#type' => 'fieldset',
'#title' => t('Locative information'),
'#collapsible' => TRUE,
'#collapsed' => FALSE,
)
);
$form['location']['multiple_locations'] = array(
'#type' => 'fieldset',
'#title' => t('Number of locations'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['location']['multiple_locations']['location_maxnum'] = array(
'#type' => 'select',
'#title' => t('Maximum number of locations allowed for this type.'),
'#default_value' => variable_get('location_maxnum_'. $type, 0),
'#options' => drupal_map_assoc(range(0, 100)), // @@@ Why is this not just a freeform then?
'#description' => t('This setting determines the maximum number of locations that can be assigned to a node of this type. This number must be greater than or equal to the default number of location forms selected below. By selecting a number greater than zero, users will be able to add a full or partial address(es) to each node of this type (depending on which fields you enable or require below).')
);
$form['location']['multiple_locations']['location_defaultnum'] = array(
'#type' => 'select',
'#title' => t('Default number of location forms'),
'#default_value' => variable_get('location_defaultnum_'. $type, 0),
'#options' => drupal_map_assoc(range(0, 10)),
'#description' => t('This setting only applies when you have enabled locations for this node type. It determines how many blank location forms will show up on the original edit-screen for a node of this type. It also only applies when you have enabled a maximum of 1 or more locations to be submitted for this node type.')
);
$form['location']['collection'] = array(
'#type' => 'fieldset',
'#title' => t('Collection settings'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['location']['collection']['location_weight'] = array(
'#type' => 'weight',
'#title' => t('Location weight'),
'#default_value' => variable_get('location_weight_'. $type, 9),
'#description' => t('Weight of the location box in the input form. Lowest values will be displayed higher in the form.'),
);
$form['location']['collection']['location_collapsible'] = array(
'#type' => 'checkbox',
'#title' => t('Collapsible'),
'#default_value' => variable_get('location_collapsible_'. $type, 1),
'#description' => t('Make the location box collapsible.')
);
$form['location']['collection']['location_collapsed'] = array(
'#type' => 'checkbox',
'#title' => t('Collapsed'),
'#default_value' => variable_get('location_collapsed_'. $type, 1),
'#description' => t('Display the location box collapsed.')
);
$form['location']['collection']['required_field_notice'] = array(
'#type' => 'markup',
'#value' => '
'. t('NOTE:') .' '. t('Locations fields you choose to require will only be required for the first location if you have allowed more than one location to be submitted for this node type.') .'
'
);
$form['location']['location_fields'] = array(
'#type' => 'location_settings',
'#default_value' => variable_get('location_fields_'. $type, array()),
);
$form['location']['rss'] = array(
'#type' => 'fieldset',
'#title' => t('RSS Settings'),
'#description' => t('Here, you can change how locative data affects RSS feeds on nodes.'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['location']['rss']['location_rss'] = array(
'#type' => 'select',
'#title' => t('RSS mode'),
'#description' => t('Select how to use locations in RSS feeds for this content type.'),
'#options' => array(
'none' => t('None (Do not put locational data in RSS feeds)'),
'w3c' => t('W3C Geo (deprecated)'),
'w3c_bugcompat' => t('Location 1.x-2.x compatible (buggy W3C)'),
'simple' => t('GeoRSS-Simple'),
'gml' => t('GeoRSS GML'),
),
'#default_value' => variable_get('location_rss_'. $type, 'simple'), // #301540
);
$defaults = variable_get("location_display_$type", array(
'teaser' => TRUE,
'full' => TRUE,
'weight' => 0,
'hide' => array(),
));
$form['location']['location_display'] = array(
'#type' => 'fieldset',
'#title' => t('Display Settings'),
'#description' => t('Here, you can change how locative data appears in nodes when viewed.'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#tree' => TRUE,
);
$form['location']['location_display']['teaser'] = array(
'#type' => 'checkbox',
'#title' => 'Display location in teaser view',
'#default_value' => isset($defaults['teaser']) ? $defaults['teaser'] : TRUE,
);
$form['location']['location_display']['full'] = array(
'#type' => 'checkbox',
'#title' => 'Display location in full view',
'#default_value' => isset($defaults['full']) ? $defaults['full'] : TRUE,
);
$form['location']['location_display']['weight'] = array(
'#type' => 'weight',
'#title' => t('Display Weight'),
'#default_value' => isset($defaults['weight']) ? $defaults['weight'] : 0,
);
$fields = location_field_names();
$form['location']['location_display']['hide'] = array(
'#type' => 'checkboxes',
'#title' => t('Hide fields from display'),
'#collapsed' => TRUE,
'#default_value' => isset($defaults['hide']) ? $defaults['hide'] : array(),
'#options' => $fields,
);
// @@@ THIS IS NOT GOOD. --Bdragon
// clear the views cache in case anything was changed
if (function_exists('views_invalidate_cache')) {
views_invalidate_cache();
}
}
/**
* Validation function for node settings form.
* Logically, the default number of locations per node cannot
* be bigger than the max locations.
*
* @ingroup $form
*/
function location_node_settings_validate($form, &$form_state) {
if (!empty($form_state['values']['location_maxnum']) && empty($form_state['values']['location_defaultnum'])) {
form_set_error('location_defaultnum',
t("You must have at least 1 default location-form enabled if you are going to allow locations to be submitted for nodes of this type. If you don't intend to allow locations to be submitted for nodes of this type, set the maximum number of locations allowed for this type to 0."));
}
elseif ($form_state['values']['location_defaultnum'] > $form_state['values']['location_maxnum']) {
form_set_error('location_defaultnum',
t("Your default number of location-forms that show up can't be greater than the maximum number of locations allowed for this node type."));
}
}
/**
* Custom submit function to save location settings properly.
*/
function _location_node_type_save_submit($form, &$form_state) {
variable_set('location_fields_'. $form_state['values']['type'], $form_state['values']['location_fields']);
variable_set('location_display_'. $form_state['values']['type'], $form_state['values']['location_display']);
// Prevent the "normal" submit handler from stomping our variables.
unset($form_state['values']['location_fields']);
unset($form_state['values']['location_display']);
}
function _location_node_form_alter(&$form, &$form_state, $form_id) {
$node = $form['#node'];
$location_fields = array();
$required_fields = array();
foreach (array_keys(location_field_names()) as $field_name) {
$workflow_setting = variable_get('location_'. $field_name .'_'. $node->type, $field_name == 'country' ? 1 : 0);
if ($workflow_setting) {
$location_fields[] = $field_name;
if ($workflow_setting == 2) {
$required_fields[] = $field_name;
}
}
}
$numloc = isset($node->locations) ? count($node->locations) : 0;
$location_form_count = min(count($node->locations) + variable_get('location_defaultnum_'. $node->type, 1), variable_get('location_maxnum_'. $node->type, 1));
if ($location_form_count) {
$form['locations'] = array(
'#type' => 'fieldset',
'#title' => format_plural($location_form_count, 'Location', 'Locations'),
'#tree' => TRUE,
'#attributes' => array_merge((isset($form['locations']['#attributes']) && is_array($form['locations']['#attributes'])) ? $form['locations']['#attributes'] : array(), array('class' => 'locations')),
'#weight' => variable_get('location_weight_'. $form['type']['#value'], 9),
'#collapsible' => variable_get('location_collapsible_'. $form['type']['#value'], 0) == 0 ? FALSE : TRUE,
'#collapsed' => variable_get('location_collapsed_'. $form['type']['#value'], 0) == 0 ? FALSE : TRUE,
);
// If there is only one location, hide the outer fieldset.
if (variable_get('location_maxnum_'. $form['type']['#value'], 0) == 1) {
$form['locations']['#type'] = 'markup';
}
$settings = variable_get('location_fields_'. $node->type, array());
// Enforce required fields for the first location only. The rest should be optional.
if ($location_form_count > 1) {
$settings_sans_reqs = array();
foreach ($settings as $field => $setting) {
// Allow Allow (Use), Force Default settings through.
// Block 2 (Required).
$settings_sans_reqs[$field] = $setting & 1;
}
}
for ($i = 0; $i < $location_form_count; $i++) {
$form['locations'][$i] = array(
'#type' => 'location_element',
'#title' => t('Location #%number', array('%number' => $i + 1)),
'#default_value' => isset($node->locations[$i]) ? $node->locations[$i] : NULL,
// Use relaxed settings for all locations past the first one.
'#location_settings' => ($i > 0) ? $settings_sans_reqs : $settings
);
}
if ($location_form_count == 1) {
$form['locations'][0]['#title'] = t('Location');
}
}
}
/**
* Alter the user_admin_settings form.
*/
function location_form_user_admin_settings_alter(&$form, &$form_state) {
$form['user_locations'] = array(
'#type' => 'fieldset',
'#title' => t('User locations'),
);
$form['user_locations']['collection'] = array(
'#type' => 'fieldset',
'#title' => t('Collection settings'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['user_locations']['collection']['location_user_weight'] = array(
'#type' => 'weight',
'#title' => t('Location weight'),
'#default_value' => variable_get('location_user_weight', 9),
'#description' => t('Weight of the location box in the input form. Lowest values will be displayed higher in the form.'),
);
$form['user_locations']['collection']['location_user_collapsible'] = array(
'#type' => 'checkbox',
'#title' => t('Collapsible'),
'#default_value' => variable_get('location_user_collapsible', 1),
'#description' => t('Make the location box collapsible.')
);
$form['user_locations']['collection']['location_user_collapsed'] = array(
'#type' => 'checkbox',
'#title' => t('Collapsed'),
'#default_value' => variable_get('location_user_collapsed', 1),
'#description' => t('Display the location box collapsed.')
);
$form['user_locations']['location_user_fields'] = array(
'#type' => 'location_settings',
'#default_value' => variable_get('location_user_fields', array()),
);
}
/**
* Implementation of hook_form_alter().
*/
function location_form_alter(&$form, &$form_state, $form_id) {
switch ($form_id) {
case 'location_geocoding_options_form':
// Fix the submit handler.
if (!is_array($form['#submit'])) {
$form['#submit'] = array('system_settings_form_submit' => array());
}
array_unshift($form['#submit'], 'location_geocoding_options_form_submit');
break;
}
// Add the Location fields on the Node edit form
if (isset($form['type']['#value']) && $form['type']['#value'] .'_node_form' == $form_id && variable_get('location_maxnum_'. $form['type']['#value'], 0)) {
// @@@ This is a workaround for some bad interactions with usernode.
// @@@ Remove it as soon as we can figure out what's going on.
if ($form_id == 'usernode_node_form') {
return;
}
_location_node_form_alter($form, $form_state, $form_id);
}
}
/**
* Implementation of hook_nodeapi().
*/
function location_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
switch ($op) {
case 'delete revision':
db_query('DELETE FROM {location_instance} WHERE vid = %d', $node->vid);
break;
case 'delete':
db_query('DELETE FROM {location_instance} WHERE nid = %d', $node->nid);
break;
case 'load':
$locations = location_load_locations($node->vid);
$location = count($locations) ? $locations[0] : array();
return array('locations' => $locations, 'location' => $location);
case 'insert':
case 'update':
if (!empty($node->locations)) {
location_save_locations($node->locations, array('nid' => $node->nid, 'vid' => $node->vid));
}
break;
case 'view':
if (variable_get('location_display_location', 1)) {
$display = variable_get('location_display_'. $node->type, array('teaser' => TRUE, 'full' => TRUE, 'weight' => 0, 'hide' => array()));
if (($a3 && $display['teaser']) || (!$a3 && $display['full'])) {
$hide = array_keys(array_filter($display['hide']));
// Show all locations
if ($output = theme('locations', $node->locations, $hide)) {
$node->content['locations'] = array(
'#type' => 'markup',
'#value' => $output,
'#weight' => variable_get('location_display_weight_'. $node->type, 0),
);
}
}
}
break;
case 'rss item':
$items = array();
$mode = variable_get('location_rss_'. $node->type, 'simple'); // #301540
if ($mode == 'none') {
return;
}
if (is_array($node->locations)) {
foreach ($node->locations as $location) {
if (!is_null($location['latitude']) && !is_null($location['longitude'])) {
switch ($mode) {
// W3C Basic Geo Vocabulary
case 'w3c':
$items[] = array(
'key' => 'geo:Point',
'namespace' => array('xmlns:geo' => 'http://www.w3.org/2003/01/geo/wgs84_pos#'),
'value' => array(
array('key' => 'geo:lat', 'value' => $location['latitude']),
array('key' => 'geo:long', 'value' => $location['longitude']),
),
);
break;
// Location 1.x-2.x bug compatible.
// W3C Basic Geo Vocabulary with a misspelled longitude tag.
case 'w3c_bugcompat':
$items[] = array(
'key' => 'geo:Point',
'namespace' => array('xmlns:geo' => 'http://www.w3.org/2003/01/geo/wgs84_pos#'),
'value' => array(
array('key' => 'geo:lat', 'value' => $location['latitude']),
array('key' => 'geo:lon', 'value' => $location['longitude']),
),
);
break;
// GeoRSS-Simple
case 'simple':
$items[] = array(
'key' => 'georss:point',
'namespace' => array('xmlns:georss' => 'http://www.georss.org/georss'),
'value' => "$location[latitude] $location[longitude]",
);
break;
//
case 'gml':
$items[] = array(
'key' => 'georss:where',
'namespace' => array(
'xmlns:georss' => 'http://www.georss.org/georss',
'xmlns:gml' => 'http://www.opengis.net/gml',
),
'value' => array(
'gml:Point' => array(
'gml:pos' => "$location[latitude] $location[longitude]",
),
),
);
break;
}
}
}
}
return $items;
}
}
/**
* Load associated locations.
*
* @param $id The identifier to match. (An integer.)
* @param $key The search key for {location_instance} (usually vid or uid.)
* @return An array of loaded locations.
*/
function location_load_locations($id, $key = 'vid') {
$result = db_query('SELECT lid FROM {location_instance} WHERE '. db_escape_table($key) .' = %d', $id);
$locations = array();
while ($lid = db_fetch_object($result)) {
$locations[] = location_load_location($lid->lid);
}
return $locations;
}
/**
* Save associated locations.
*
* @param $id The identifier to store as. (An integer or string, depending on $key.)
* @param $criteria An array of instance criteria to save as. (Example: array('genid' => 'my_custom_1111'))
*/
function location_save_locations(&$locations, $criteria) {
if (!empty($locations) && !empty($criteria) && is_array($criteria)) {
foreach (array_keys($locations) as $key) {
location_save($locations[$key]);
}
$columns = array();
$placeholders = array();
$qfrags = array();
$args = array();
foreach (array('nid' => '%d', 'vid' => '%d', 'uid' => '%d', 'genid' => "'%s'") as $key => $placeholder) {
if (isset($criteria[$key])) {
$columns[] = $key;
$placeholders[] = $placeholder;
$args[] = $criteria[$key];
$qfrags[] = "$key = $placeholder";
}
}
$query = 'DELETE FROM {location_instance} WHERE '. implode(' AND ', $qfrags);
db_query($query, $args);
// Tack on the lid.
$columns[] = 'lid';
$placeholders[] = '%d';
foreach ($locations as $location) {
// Don't save "empty" locations.
// location_save() explicitly returns FALSE for empty locations,
// so it should be ok to rely on the data type.
if ($location['lid'] !== FALSE) {
$args[] = $location['lid'];
db_query('INSERT INTO {location_instance} ('. implode(', ', $columns) .') VALUES ('. implode(', ', $placeholders) .')', $args);
array_pop($args);
}
}
}
}
/**
* Load a single location by lid.
*
* @param $lid Location ID to load.
* @return A location array.
*/
function location_load_location($lid) {
$location = db_fetch_array(db_query('SELECT * FROM {location} WHERE lid = %d', $lid));
// @@@ Just thought of this, but I am not certain it is a good idea...
if (empty($location)) {
$location = array('lid' => $lid);
}
if (isset($location['source']) && $location['source'] == LOCATION_LATLON_USER_SUBMITTED) {
// Set up location chooser or lat/lon fields from the stored location.
$location['locpick'] = array(
'user_latitude' => $location['latitude'],
'user_longitude' => $location['longitude'],
);
}
$location['province_name'] = '';
$location['country_name'] = '';
if (!empty($location['country'])) {
$location['country_name'] = location_country_name($location['country']);
if (!empty($location['province'])) {
$location['province_name'] = location_province_name($location['country'], $location['province']);
}
}
$location = array_merge($location, location_invoke_locationapi($location, 'load', $lid));
return $location;
}
// @@@ There are significant changes in the megapatch.
/**
* Implementation of hook_user().
*/
function location_user($op, &$edit, &$account, $category = NULL) {
global $user;
switch ($op) {
case 'load':
$account->locations = location_load_locations($account->uid, 'uid');
$account->location = count($account->locations) ? $account->locations[0] : array();
break;
case 'insert':
case 'update':
if (!empty($edit['locations'])) {
location_save_locations($edit['locations'], array('uid' => $account->uid));
}
unset($edit['locations']);
break;
case 'delete':
db_query('DELETE FROM {location_instance} WHERE uid = %d', $account->uid);
break;
case 'form':
if ($category == 'account') {
if ((($user->uid == $account->uid) && user_access('set own user location')) || user_access('administer user locations')) {
// @@@ Multiple locations?
$settings = variable_get('location_user_fields', array());
$form['locations'] = array(
'#tree' => TRUE,
);
$form['locations'][0] = array(
'#type' => 'location_element',
'#title' => t('Location'),
'#default_value' => isset($account->locations[0]) ? $account->locations[0] : NULL,
'#attributes' => array('class' => 'location'),
'#collapsible' => variable_get('location_user_collapsible', FALSE),
'#collapsed' => variable_get('location_user_collapsed', FALSE),
'#weight' => variable_get('location_user_weight', 1),
'#location_settings' => $settings,
'#description' => t('Enter as much of your address as you are comfortable with. Your address will only be viewable by those who have the appropriate permissions. The site will be able to automatically link you to driving directions and other features if it already knows your address.'),
);
return $form;
}
}
break;
case 'view':
if ((($user->uid == $account->uid) && user_access('view own user location')) || user_access('administer users') || user_access('view all user locations') || user_access('administer user locations')) {
// @@@
if (variable_get('location_display_location', 1) && isset($account->locations) && count($account->locations)) {
$items[] = array(
'value' => theme('location', $account->location),
'class' => 'location',
);
// @@@
if (user_access('submit latitude/longitude')) {
if (!empty($account->location['latitude']) && !empty($user->location['longitude'])) {
// @@@ Theme!
$items[] = array(
'title' => t('Coordinates'),
'value' => t('lat: %latitude', array('%latitude' => $account->location['latitude'])) .' '. t('lon: %longitude', array('%longitude' => $account->location['longitude'])),
'class' => 'location',
);
}
}
return array(t('Location') => $items);
}
}
break;
}
}
/**
* Create a list of states from a given country.
*
* @param $country
* String. The country code
* @param $string
* String (optional). The state name typed by user
* @return
* Javascript array. List of states
*/
function _location_autocomplete($country, $string = '') {
$counter = 0;
$string = strtolower($string);
$string = '/^'. $string .'/';
$matches = array();
$provinces = location_get_provinces($country);
if (!empty($provinces)) {
while (list($code, $name) = each($provinces)) {
if ($counter < 5) {
if (preg_match($string, strtolower($name))) {
$matches[$name] = $name;
++$counter;
}
}
}
}
echo drupal_to_js($matches);
return;
}
/**
* Epsilon test.
* Helper function for seeing if two floats are equal. We could use other functions, but all
* of them belong to libraries that do not come standard with PHP out of the box.
*/
function _location_floats_are_equal($x, $y) {
$x = floatval($x);
$y = floatval($y);
return (abs(max($x, $y) - min($x, $y)) < pow(10, -6));
}
/**
* Invoke a hook_locationapi() operation on all modules.
*
* @param &$location A location object.
* @param $op A string containing the name of the locationapi operation.
* @param $a3, $a4, $a5 Arguments to pass on to the hook.
* @return The returned value of the invoked hooks.
*/
function location_invoke_locationapi(&$location, $op, $a3 = NULL, $a4 = NULL, $a5 = NULL) {
$return = array();
foreach (module_implements('locationapi') as $name) {
$function = $name .'_locationapi';
$result = $function($location, $op, $a3, $a4, $a5);
if (isset($result) && is_array($result)) {
$return = array_merge($return, $result);
}
else if (isset($result)) {
$return[] = $result;
}
}
return $return;
}
/**
* Save a location.
*
* This is the central function for saving a location.
* @param $location Location array to save.
* @param $cow Copy-on-write, i.e. whether or not to assign a new lid if something changes.
* @return The lid of the saved location, or FALSE if the location is considered "empty."
*/
function location_save(&$location, $cow = TRUE) {
$inhibit_geocode = FALSE;
$dummy = array();
$virtual = location_invoke_locationapi($dummy, 'virtual fields');
if (isset($location['delete_location']) && $location['delete_location']) {
// Location is being deleted.
// Stomp on it with the default values.
$dummy = array();
$location = location_invoke_locationapi($dummy, 'default values');
}
unset($location['delete_location']);
// Remove the "virtual fields" from the location.
foreach ($virtual as $field) {
unset($location[$field]);
}
$oldloc = array();
if ($location['lid']) {
$oldloc = (array)location_load_location($location['lid']);
// Remove the "virtual fields" from the old location too.
foreach ($virtual as $field) {
unset($oldloc[$field]);
}
}
// @@@ This isn't the best place to do this...
// But if the user only changes user lat/lon, we need to be able to detect it!
if (!empty($location['locpick'])) {
$location['locpick']['user_latitude'] = trim($location['locpick']['user_latitude']);
$location['locpick']['user_longitude'] = trim($location['locpick']['user_longitude']);
}
// If the user location was set, convert it into lat / lon.
if (!empty($location['locpick']['user_latitude']) && !empty($location['locpick']['user_longitude'])) {
$location['source'] = LOCATION_LATLON_USER_SUBMITTED;
$location['latitude'] = $location['locpick']['user_latitude'];
$location['longitude'] = $location['locpick']['user_longitude'];
$inhibit_geocode = TRUE;
}
// Pull in fields that hold data currently not editable directly by the user.
$location = array_merge($oldloc, $location);
$dummy = array();
$emptyloc = location_invoke_locationapi($dummy, 'default values');
// Effective location for determining "emptiness"
$shadow = $location;
unset($shadow['lid']);
// Determine what fields were filled in.
$filled = array();
foreach ($shadow as $k => $v) {
if ($k == 'locpick') {
// The locpick element is "special".
continue;
}
if (!isset($emptyloc[$k])) {
// @@@ This is an error condition, a module is declaring a field without
// a corresponding "empty" default!
watchdog('Location', 'Module declared field without empty default!', array(), WATCHDOG_ERROR);
continue;
}
if ($emptyloc[$k] == $v) {
// Exact match, no change.
continue;
}
else {
// It wasn't equal, but perhaps it was equivilent?
$results = location_invoke_locationapi($shadow, 'isunchanged', $k, $emptyloc[$k]);
$changed = TRUE; // First, assume changed.
foreach ($results as $r) {
if ($r) {
$changed = FALSE;
}
}
if ($changed) {
// Nobody answered affirmative, it must be different.
$filled[$k] = TRUE;
}
}
}
if (empty($filled)) {
// This location was empty.
return FALSE;
}
// Change detection.
$changed = array();
foreach ($location as $k => $v) {
if ($k == 'locpick') {
// The locpick element is "special".
continue;
}
if (!isset($oldloc[$k])) {
// Field missing from old location, automatic save.
$changed[$k] = TRUE;
}
if ($oldloc[$k] == $v) {
// Exact match, no change.
continue;
}
else {
// It wasn't equal, but perhaps it was equivilent?
$results = location_invoke_locationapi($location, 'isunchanged', $k, $oldloc[$k]);
$waschanged = TRUE; // First, assume changed.
foreach ($results as $r) {
if ($r) {
$waschanged = FALSE;
}
}
if ($waschanged) {
// Nobody okayed this difference.
$changed[$k] = TRUE;
}
}
}
if (empty($changed)) {
// We didn't actually need to save anything.
if (!empty($location['lid'])) {
return $location['lid'];
}
else {
// Unfilled location (@@@ Then how did we get here?)
return FALSE;
}
}
// Perform geocoding logic, coordinate normalization, etc.
_location_geo_logic($location, $changed, $filled, $inhibit_geocode);
// If we are in COW mode, we *probabaly* need to make a new lid.
if ($cow) {
// However, if it appears one or less times (i.e. is not shared), it's safe to recycle the lid.
// Therefore, skip unsetting the prior lid if there are one or less rows in {location_instance} matching the lid.
// See #306171 for more information.
if (isset($location['lid']) && db_result(db_query('SELECT COUNT(*) FROM {location_instance} WHERE lid = %d', $location['lid'])) > 1) {
unset($location['lid']);
}
}
if (!empty($location['lid'])) {
db_query('DELETE FROM {location} WHERE lid = %d', $location['lid']);
}
else {
unset($location['lid']);
}
drupal_write_record('location', $location);
location_invoke_locationapi($location, 'save');
return $location['lid'];
}
/**
* Perform geocoding logic, etc., prior to storing in the database.
*/
function _location_geo_logic(&$location, $changed, $filled, $inhibit_geocode = FALSE) {
if (!$inhibit_geocode) {
// Have any of the fields possibly affecting geocoding changed?
// Or, was the location previously user submitted but is no longer?
if ($changed['street'] || $changed['additional'] || $changed['city'] || $changed['province'] || $changed['country'] || $changed['postal_code'] || $location['source'] == LOCATION_LATLON_USER_SUBMITTED) {
// Attempt exact geocoding.
if ($data = location_latlon_exact($location)) {
$location['source'] = LOCATION_LATLON_GEOCODED_EXACT;
// @@@ How about an accuracy field here?
$location['latitude'] = $data['lat'];
$location['longitude'] = $data['lon'];
// @@@ How about address normalization?
}
// Attempt inexact geocoding against a local postcode database
elseif ($data = location_get_postalcode_data($location)) {
$location['source'] = LOCATION_LATLON_GEOCODED_APPROX;
$location['latitude'] = $data['lat'];
$location['longitude'] = $data['lon'];
}
else {
$location['source'] = LOCATION_LATLON_UNDEFINED;
$location['latitude'] = 0;
$location['longitude'] = 0;
}
}
}
// Normalize coordinates.
while ($location['latitude'] > 90) {
$location['latitude'] -= 180;
}
while ($location['latitude'] < -90) {
$location['latitude'] += 180;
}
while ($location['longitude'] > 180) {
$location['longitude'] -= 360;
}
while ($location['longitude'] < -180) {
$location['longitude'] += 360;
}
// If city and/or province weren't set, see if we can fill them in with
// postal data.
if (!empty($location['postal_code'])) {
if (empty($location['city']) || empty($location['province'])) {
if ($data = location_get_postalcode_data($location)) {
$location['city'] = $data['city'];
$location['province'] = $data['province'];
}
}
}
// Normalize province.
// Note: Validation is performed elsewhere. We assume that the province
// specified matches either the short or long form of a province.
if (!empty($location['province']) && !empty($location['country'])) {
$provinces = location_get_provinces($location['country']);
$p = strtoupper($location['province']);
foreach ($provinces as $k => $v) {
if ($p == strtoupper($k) || $p == strtoupper($v)) {
$location['province'] = $k;
break;
}
}
}
// @@@ Now would be a GREAT time to hook.
}
/*
Validation crap removed from nodeapi: move into the correct place.
case 'valiiiiiiiidate':
foreach ($node->locations as $index => $location) {
// For now, validation just makes sure that required fields have any value
// If syntax specific checks are implemented for locations in the future, they can be called as well.
foreach (location_field_names() as $field_name => $display_name) {
$workflow_setting = variable_get('location_'. $field_name .'_'. $node->type, $field_name == 'country' ? 1 : 0);
// We only enforce required fields on the first location if there are multiple locations
if ($index == 0) {
if (variable_get('location_'. $field_name .'_'. $node->type, 0) == 2) {
if (isset($node->locations[$index][$field_name]) && !strlen(trim($node->locations[$index][$field_name]))) {
form_set_error('locations]['. $index .']['. $field_name, t('The field %field is required.', array('%field' => $display_name)));
}
}
}
$node->locations[$index][$field_name] = trim($node->locations[$index][$field_name]);
}
}
break;
*/
/**
* Perform validation against a location fieldset.
*/
function location_element_validate($form, &$form_state) {
location_invoke_locationapi($form['#value'], 'validate', $form);
}
/**
* Convert decimal degrees to degrees,minutes,seconds.
*/
function location_dd_to_dms($coord) {
$negative = ($coord < 0) ? TRUE : FALSE;
$coord = abs($coord);
$degrees = floor($coord);
$coord -= $degrees;
$coord *= 60;
$minutes = floor($coord);
$coord -= $minutes;
$coord *= 60;
$seconds = round($coord, 6);
return array($degrees, $minutes, $seconds, $negative);
}
/**
* Display a coordinate.
*/
function theme_location_latitude_dms($latitude) {
$output = '';
list($degrees, $minutes, $seconds, $negative) = location_dd_to_dms($latitude);
$output .= "${degrees}° ${minutes}' ${seconds}\" ";
if (!$negative) {
$output .= 'N';
}
else {
$output .= 'S';
}
return $output;
}
function theme_location_longitude_dms($longitude) {
$output = '';
list($degrees, $minutes, $seconds, $negative) = location_dd_to_dms($longitude);
$output .= "${degrees}° ${minutes}' ${seconds}\" ";
if (!$negative) {
$output .= 'E';
}
else {
$output .= 'W';
}
return $output;
}
/**
* Implementation of hook_token_values().
*/
function location_token_values($type, $object = NULL) {
if ($type == 'node') {
$node = $object;
$countries = _location_supported_countries();
if (!empty($node->locations)) {
foreach ($node->locations as $key => $location) {
$l = $node->locations[$key];
$provinces = array();
if (!empty($l['country'])) {
$provinces = location_get_provinces($l['country']);
}
$values['location-name_'. $key] = check_plain($l['name']);
$values['location-street_'. $key] = check_plain($l['street']);
$values['location-additional_'. $key] = check_plain($l['additional']);
$values['location-city_'. $key] = check_plain($l['city']);
$values['location-provincename_'. $key] = isset($provinces[$l['province']]) ? check_plain($provinces[$l['province']]) : '';
$values['location-province_'. $key] = check_plain($node->locations[$key]['province']);
$values['location-postal_code_'. $key] = check_plain($node->locations[$key]['postal_code']);
$values['location-countryname_'. $key] = isset($countries[$l['country']]) ? check_plain($countries[$l['country']]) : '';
$values['location-country_'. $key] = check_plain($node->locations[$key]['country']);
$values['location-latitude_'. $key] = check_plain($node->locations[$key]['latitude']);
$values['location-longitude_'. $key] = check_plain($node->locations[$key]['longitude']);
}
}
return $values;
}
}
/**
* Implementation of hook_token_list().
*/
function location_token_list($type = 'all') {
if ($type == 'node' || $type == 'all') {
$tokens['location']['location-name_N'] = t('Location Name (If there are multiple locations per node, N is the iteration, starting with 0)');
$tokens['location']['location-street_N'] = t('Street (If there are multiple locations per node, N is the iteration, starting with 0)');
$tokens['location']['location-additional_N'] = t('Additional (If there are multiple locations per node, N is the iteration, starting with 0)');
$tokens['location']['location-city_N'] = t('City (If there are multiple locations per node, N is the iteration, starting with 0)');
$tokens['location']['location-province_N'] = t('State/Province (If there are multiple locations per node, N is the iteration, starting with 0)');
$tokens['location']['location-provincename_N'] = t('State/Province Name (If there are multiple locations per node, N is the iteration, starting with 0)');
$tokens['location']['location-postal_code_N'] = t('Postal Code (If there are multiple locations per node, N is the iteration, starting with 0)');
$tokens['location']['location-latitude_N'] = t('Latitude (If there are multiple locations per node, N is the iteration, starting with 0)');
$tokens['location']['location-longitude_N'] = t('Longitude (If there are multiple locations per node, N is the iteration, starting with 0)');
$tokens['location']['location-country_N'] = t('Country (If there are multiple locations per node, N is the iteration, starting with 0)');
$tokens['location']['location-countryname_N'] = t('Country Name (If there are multiple locations per node, N is the iteration, starting with 0)');
return $tokens;
}
}
/**
* Theme preprocess function for a location.
*/
function template_preprocess_location(&$variables) {
$location = $variables['location'];
foreach($variables['hide'] as $key) {
unset($location[$key]);
}
// Location name.
$variables['name'] = '';
if (!empty($location['name'])) {
$variables['name'] = theme('placeholder', $location['name']);
}
// Theme latitude and longitude as d/m/s.
$variables['latitude'] = '';
$variables['latitude_dms'] = '';
if (!empty($location['latitude'])) {
$variables['latitude'] = check_plain($location['latitude']);
$variables['latitude_dms'] = theme('location_latitude_dms', $location['latitude']);
}
$variables['longitude'] = '';
$variables['longitude_dms'] = '';
if (!empty($location['longitude'])) {
$variables['longitude'] = check_plain($location['longitude']);
$variables['longitude_dms'] = theme('location_longitude_dms', $location['longitude']);
}
$variables['map_link'] = '';
if (!in_array('map_link', $variables['hide'])) {
$variables['map_link'] = location_map_link($variables['location']);
}
foreach (array('street', 'additional', 'city', 'province', 'country', 'postal_code', 'province_name', 'country_name') as $key) {
$variables[$key] = '';
if (!empty($location[$key])) {
$variables[$key] = check_plain($location[$key]);
}
}
// Add a country-specific template suggestion.
if (!empty($location['country']) && location_standardize_country_code($location['country'])) {
// $location['country'] is normalized in the previous line.
$variables['template_files'][] = 'location-'. $location['country'];
}
}
/**
* Theme preprocess function for location_distance.
*/
function template_preprocess_location_distance(&$variables) {
$units = $variables['units'];
unset($variables['units']);
if ($units == 'km') {
$variables['shortunit'] = 'km';
$variables['longunit'] = 'kilometer(s)';
}
if ($units == 'mi') {
$variables['shortunit'] = 'mi';
$variables['longunit'] = 'mile(s)';
}
$variables['distance'] = (float)$variables['distance'];
}