location_cck.module in Location 7.3
Same filename and directory in other branches
Defines location field type.
File
contrib/location_cck/location_cck.moduleView source
<?php
/**
* @file
* Defines location field type.
*/
/**
* Implements hook_theme().
*/
function location_cck_theme() {
return array(
'location_cck_field_all' => array(
'variables' => array(
'location' => NULL,
'hide' => array(),
'field' => NULL,
'instance' => NULL,
),
),
'location_cck_field_map' => array(
'variables' => array(
'locations' => NULL,
'field' => NULL,
'instance' => NULL,
),
),
'location_cck_field_popup' => array(
'variables' => array(
'location' => NULL,
'instance' => NULL,
),
),
);
}
/**
* Implements hook_field_info().
*/
function location_cck_field_info() {
return array(
'location' => array(
'label' => t('Location', array(), array(
'context' => 'geolocation',
)),
'description' => t('Store a location.module location.'),
'settings' => array(),
'instance_settings' => array(),
'default_widget' => 'location',
'default_formatter' => 'location_default',
),
);
}
/**
* Implement hook_field_settings_form().
*/
function location_cck_field_settings_form($field, $instance, $has_data) {
$settings = isset($field['settings']['location_settings']) ? $field['settings']['location_settings'] : array();
$form = array();
$form['location_settings'] = location_settings($settings);
// Multiple is handled by CCK.
unset($form['location_settings']['multiple']);
// CCK handles weight, and collapsibility is not changeable.
unset($form['location_settings']['form']['weight']);
unset($form['location_settings']['form']['collapsible']);
unset($form['location_settings']['form']['collapsed']);
unset($form['location_settings']['display']['weight']);
// We want to see the settings, so uncollapse them.
$form['location_settings']['#collapsible'] = FALSE;
$form['location_settings']['form']['#collapsed'] = FALSE;
$form['location_settings']['display']['#collapsed'] = FALSE;
// Add some GMap settings, if GMap is enabled.
if (module_exists('gmap')) {
$form['gmap_macro'] = array(
'#type' => 'textarea',
'#title' => t('GMap Macro'),
'#rows' => 2,
'#maxlength' => 500,
'#description' => t('A macro to be used as a base map for this field. This map will be recentered on the location, so the center is not that important.'),
'#default_value' => !empty($field['settings']['gmap_macro']) ? $field['settings']['gmap_macro'] : '[gmap ]',
);
$options = gmap_get_marker_titles();
$form['gmap_marker'] = array(
'#type' => 'select',
'#title' => t('GMap marker'),
'#options' => $options,
'#default_value' => !empty($field['settings']['gmap_marker']) ? $field['settings']['gmap_marker'] : 'drupal',
);
}
else {
// Preserve existing data, apply defaults even if gmap is disabled.
$form['gmap_macro'] = array(
'#type' => 'value',
'#value' => !empty($field['settings']['gmap_macro']) ? $field['settings']['gmap_macro'] : '[gmap ]',
);
$form['gmap_marker'] = array(
'#type' => 'value',
'#value' => !empty($field['settings']['gmap_marker']) ? $field['settings']['gmap_marker'] : 'drupal',
);
}
return $form;
}
/**
* Implements hook_field_settings().
*/
function location_cck_field_settings($op, $field) {
switch ($op) {
case 'views data':
// We want to for the most part use the CCK stuff, but we also want to
// patch in a relationship so location's views support can target
// cck fields directly.
$table = content_views_tablename($field);
$db_info = content_database_info($field);
$field_alias = $db_info['columns']['lid']['column'];
$data = content_views_field_views_data($field);
$data[$table][$field_alias]['relationship'] = array(
'base' => 'location',
'field' => 'lid',
'handler' => 'views_handler_relationship',
'label' => t('Location', array(), array(
'context' => 'geolocation',
)),
);
return $data;
}
}
/**
* Implement hook_field_schema().
*/
function location_cck_field_schema($field) {
switch ($field['type']) {
case 'location':
$columns = array(
'lid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => FALSE,
),
);
break;
}
return array(
'columns' => $columns,
'indexes' => array(
'lid' => array(
'lid',
),
),
);
}
/**
* Implement hook_field_insert().
*/
function location_cck_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) {
if (empty($items)) {
return;
}
$criteria = array();
// @todo refactoring, code of this function is duplicated in other places in
// this module, and could be used in one place, instead of different places.
if ($entity_type == 'node') {
// Store instances of locations by field name and vid.
$criteria = array(
'genid' => 'cck:' . $field['field_name'] . ':' . $entity->vid,
'vid' => $entity->vid,
'nid' => $entity->nid,
);
}
elseif ($entity_type == 'user') {
// Store instances of locations by field name and vid.
$criteria = array(
'genid' => 'cck:' . $field['field_name'] . ':' . $entity->uid,
'uid' => $entity->uid,
);
}
else {
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
// Store instances of locations by field name and vid.
$criteria = array(
'genid' => 'field:' . $field['field_name'] . ':' . $entity_type . ':' . $id,
'vid' => $vid ? $vid : $id,
'nid' => $id,
);
}
if (!empty($criteria)) {
location_save_locations($items, $criteria);
}
}
/**
* Implement hook_field_update().
*/
function location_cck_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
$criteria = array();
if ($entity_type == 'node') {
if (!empty($items)) {
// Store instances of locations by field name and vid.
$criteria = array(
'genid' => 'cck:' . $field['field_name'] . ':' . $entity->vid,
'vid' => $entity->vid,
'nid' => $entity->nid,
);
location_save_locations($items, $criteria);
}
}
elseif ($entity_type == 'user') {
if (!empty($items)) {
// Store instances of locations by field name and vid.
$criteria = array(
'genid' => 'cck:' . $field['field_name'] . ':' . $entity->uid,
'uid' => $entity->uid,
);
location_save_locations($items, $criteria);
}
}
elseif (!empty($items)) {
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
// Store instances of locations by field name and vid.
$criteria = array(
'genid' => 'field:' . $field['field_name'] . ':' . $entity_type . ':' . $id,
'vid' => $vid ? $vid : $id,
'nid' => $id,
);
location_save_locations($items, $criteria);
}
}
/**
* Implement hook_field_delete().
*/
function location_cck_field_delete($entity_type, $entity, $field, $instance, $langcode, &$items) {
if ($entity_type == 'node') {
// @TODO: Fix this properly.
// Use the CCK storage to figure out the vids that need to be deleted,
// and clean up all the applicable references.
// $db_info = content_database_info($field);
$result = db_query('SELECT vid FROM {node_revision} WHERE nid = :nid', array(
':nid' => $entity->nid,
));
foreach ($result as $row) {
$genid = 'cck:' . $field['field_name'] . ':' . $row->vid;
$locs = array();
location_save_locations($locs, array(
'genid' => $genid,
));
}
}
elseif ($entity_type == 'user') {
$genid = 'cck:' . $field['field_name'] . ':' . $entity->uid;
$locs = array();
location_save_locations($locs, array(
'genid' => $genid,
));
}
else {
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
$genid = 'field:' . $field['field_name'] . ':' . $entity_type . ':' . $id;
$locs = array();
location_save_locations($locs, array(
'genid' => $genid,
));
}
}
/**
* Implement hook_field_delete_revision().
*/
function location_cck_field_delete_revision($entity_type, $entity, $field, $instance, $langcode, &$items) {
if ($entity_type == 'node') {
$genid = 'cck:' . $field['field_name'] . ':' . $entity->vid;
$locs = array();
location_save_locations($locs, array(
'genid' => $genid,
));
}
elseif ($entity_type == 'user') {
$genid = 'cck:' . $field['field_name'] . ':' . $entity->uid;
$locs = array();
location_save_locations($locs, array(
'genid' => $genid,
));
}
else {
list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
$genid = 'field:' . $field['field_name'] . ':' . $entity_type . ':' . $id;
$locs = array();
location_save_locations($locs, array(
'genid' => $genid,
));
}
}
/**
* Implement hook_field_validate().
*/
function location_cck_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
// @@@ Fixme
}
/**
* Implement hook_field_load().
*/
function location_cck_field_load($entity_type, $entities, $field, $instances, $langcode, &$items, $age) {
foreach ($entities as $id => $entity) {
foreach ($items[$id] as $delta => $item) {
$location = array();
// Load the location if it exists.
// If we are previewing a new node it will not.
if (!empty($item['lid'])) {
$location = location_load_location($item['lid']);
}
// Combine the item with the location loaded from the database.
// This will allow $item to display in the case of previewing a node.
$items[$id][$delta] = array_merge($location, $item);
}
}
}
/**
* Implements hook_field().
*/
function location_cck_field($op, &$node, $field, &$items, $teaser, $page) {
switch ($op) {
case 'sanitize':
// Get the location information for the lid if it hasn't already been
// loaded (in the hook_field() load $op using location_load_location()).
// This is necessary for Views and any other modules that use the
// content_format() function to render CCK fields because content_format()
// doesn't call the "load" $op.
foreach ($items as $delta => $item) {
if (!isset($item['latitude'])) {
$items[$delta] = array_merge($items[$delta], location_load_location($item['lid']));
}
}
break;
}
}
/**
* Implementation of hook_field_formatter_info().
*/
function location_cck_field_formatter_info() {
$info = array(
'location_default' => array(
'label' => t('Default (address)'),
'field types' => array(
'location',
),
),
);
if (module_exists('gmap')) {
$info['location_all'] = array(
'label' => t('Address with map'),
'field types' => array(
'location',
),
);
$info['location_map'] = array(
'label' => t('Map only'),
'field types' => array(
'location',
),
);
// @@@ How to do in D7?
$info['location_multiple'] = array(
'label' => t('Multiple field values on a single map'),
'field types' => array(
'location',
),
);
}
return $info;
}
/**
* Implements hook_field_formatter_view().
* @Todo: This.
*/
function location_cck_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
$element = array();
$settings = $field['settings']['location_settings'];
$hide = isset($settings['display']['hide']) ? array_keys(array_filter($settings['display']['hide'])) : array();
switch ($display['type']) {
case 'location_default':
foreach ($items as $delta => $item) {
if (!empty($item['lid']) || !empty($entity->in_preview)) {
$element[$delta]['#theme'] = 'location';
$element[$delta]['#location'] = $item;
$element[$delta]['#hide'] = $hide;
}
}
break;
case 'location_all':
foreach ($items as $delta => $item) {
if (!empty($item['lid']) || !empty($entity->in_preview)) {
$element[$delta]['#theme'] = 'location_cck_field_all';
$element[$delta]['#location'] = $item;
$element[$delta]['#hide'] = $hide;
$element[$delta]['#field'] = $field;
$element[$delta]['#instance'] = $instance;
}
}
break;
case 'location_map':
foreach ($items as $delta => $item) {
if (!empty($item['lid']) || !empty($entity->in_preview)) {
$element[$delta]['#theme'] = 'location_cck_field_map';
$element[$delta]['#locations'] = array(
$item,
);
$element[$delta]['#field'] = $field;
$element[$delta]['#instance'] = $instance;
}
}
break;
case 'location_multiple':
$element[0]['#theme'] = 'location_cck_field_map';
$element[0]['#locations'] = $items;
$element[0]['#field'] = $field;
$element[0]['#instance'] = $instance;
break;
}
return $element;
}
/**
* Implements hook_field_widget_info().
*/
function location_cck_field_widget_info() {
return array(
'location' => array(
'label' => t('Location Field'),
'field types' => array(
'location',
),
),
);
}
/**
* Implement hook_field_widget_form().
*/
function location_cck_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
if ($field['type'] == 'location') {
$settings = isset($field['settings']['location_settings']) ? $field['settings']['location_settings'] : array();
if (isset($form_state['values'])) {
$form_state_field_values = drupal_array_get_nested_value($form_state['values'], $element['#field_parents']);
}
// Load location data for existing locations.
if ($form_state['rebuild'] && !empty($form_state_field_values[$field['field_name']][$langcode][$delta])) {
$location = $form_state_field_values[$field['field_name']][$langcode][$delta];
}
elseif (isset($items[$delta]['lid']) && $items[$delta]['lid']) {
$location = location_load_location($items[$delta]['lid']);
}
else {
if (isset($items[$delta]) && is_array($items[$delta]) && !empty($items[$delta])) {
// Initialize empty location.
$location = location_empty_location($settings);
foreach ($items[$delta] as $k => $v) {
$location[$k] = $v;
}
// We can't trust that CCK is giving us the right information.
// It can't tell us whether $items is defaults or multistep values.
// Location *needs* the defaults to match the initial field values,
// so we re-calculate the defaults here and stash them into the settings.
// @@@ There is still a bug here!
// If you go back and edit something, and you hadn't set a location the
// first time, CCK fails to set up the defaults properly!
// I'm just going to leave it like that for now, because I don't know how
// to work around it.s
$temp = NULL;
if (is_array($temp) && isset($temp[$delta]) && is_array($temp[$delta])) {
foreach ($temp[$delta] as $k => $v) {
$settings['form']['fields'][$k]['default'] = $v;
}
}
}
else {
$location = location_empty_location($settings);
}
}
$element = array(
'#type' => 'location_element',
'#has_garbage_value' => TRUE,
'#value' => '',
'#title' => t($instance['label']),
'#description' => t($instance['description']),
'#required' => $instance['required'],
'#location_settings' => $settings,
'#default_value' => $location,
'#delta' => $delta,
);
return $element;
}
}
/**
* Implements hook_field_is_empty().
*/
function location_cck_field_is_empty($item, $field) {
$fields = array();
if (location_is_empty($item, $fields)) {
return TRUE;
}
return FALSE;
}
/**
* Return both an address and a map for an individual item.
*/
function theme_location_cck_field_all($variables) {
$content = theme('location', array(
'location' => $variables['location'],
'hide' => $variables['hide'],
));
$content .= theme_location_cck_field_map(array(
'locations' => array(
$variables['location'],
),
'field' => $variables['field'],
'instance' => $variables['instance'],
));
return $content;
}
/**
* Alternate function to return a map with all multiple values in the same map.
*/
function theme_location_cck_formatter_combined($variables) {
$element = $variables['element'];
$field = content_fields($element['#field_name'], $element['#type_name']);
$locations = $element['#items'];
return theme_location_cck_field_map(array(
'locations' => $locations,
'field' => $field,
));
}
/**
* Generate a GMap map for one or more location field values.
*
* Mostly just cut and paste from gmap_location
* block view.
*/
function theme_location_cck_field_map($variables) {
$locations = $variables['locations'];
$field = $variables['field'];
$instance = $variables['instance'];
$count = 0;
$content = '';
foreach ($locations as $location) {
if (location_has_coordinates($location)) {
$count++;
$markername = isset($field['settings']['gmap_marker']) ? $field['settings']['gmap_marker'] : 'drupal';
$markers[] = array(
'latitude' => $location['latitude'],
'longitude' => $location['longitude'],
'markername' => $markername,
'offset' => $count - 1,
'text' => theme('location_cck_field_popup', array(
'location' => $location,
'instance' => $instance,
)),
);
}
}
if (!empty($markers)) {
$macro = !empty($field['settings']['gmap_macro']) ? $field['settings']['gmap_macro'] : '[gmap ]';
$map = array_merge(gmap_defaults(), gmap_parse_macro($macro));
$map['latitude'] = $markers[0]['latitude'];
$map['longitude'] = $markers[0]['longitude'];
$map['markers'] = $markers;
$map['id'] = gmap_get_auto_mapid();
// Render a map element.
$location_map = array(
'#type' => 'gmap',
'#gmap_settings' => $map,
);
$content = drupal_render($location_map);
}
return $content;
}
/**
* Theme the GMap popup text for the field.
*/
function theme_location_cck_field_popup($variables) {
$location = $variables['location'];
$instance = $variables['instance'];
$hide = isset($field['location_settings']['display']['hide']) ? array_keys(array_filter($field['location_settings']['display']['hide'])) : array();
// Don't use a map link in a popup!
// We're making the name into a title.
$hide[] = 'map_link';
$hide[] = 'name';
$markertitle = $instance['label'];
if (!empty($location['name'])) {
$markertitle = $location['name'];
}
return '<h4>' . $markertitle . '</h4>' . theme('location', array(
'location' => $location,
'hide' => $hide,
));
}
/**
* Implements hook_token_info().
*/
function location_cck_token_info() {
$info = array();
// Load all available fields.
// In Drupal 7.22 the field_info_field_map() function was added, which is more
// memory-efficient in certain cases than field_info_fields().
// @see https://drupal.org/node/1915646
$field_map_available = version_compare(VERSION, '7.22', '>=');
$entities = $field_map_available ? field_info_field_map() : field_info_fields();
// Loop through our fields and remove all but location types.
foreach ($entities as $key => $value) {
if ($value['type'] != 'location') {
unset($entities[$key]);
}
}
// If we have location fields available, setup our tokens.
if (sizeof($entities)) {
// Get the available location field names.
$fields = location_field_names(TRUE);
// Loop through the location fields and setup tokens.
foreach ($entities as $key => $value) {
$info['tokens']['node'][$key] = array(
'name' => t('Location field: !field', array(
'!field' => $key,
)),
'description' => t('Tokens for the field: !field. Replace the "?" with the delta you want. Defaults to delta 0.', array(
'!field' => $key,
)),
'type' => $key,
);
$info['types'][$key] = array(
'name' => t('Location field: !field', array(
'!field' => $key,
)),
'description' => t('Tokens for the field: !field. Replace the "?" with the delta you want. Defaults to delta 0.', array(
'!field' => $key,
)),
'needs-data' => 'node',
);
foreach ($fields as $field_key => $field_value) {
$info['tokens'][$key][$field_key . ":?"] = array(
'name' => t($field_value),
'description' => t($field_value),
);
}
}
}
return $info;
}
/**
* Implements hook_tokens().
*/
function location_cck_tokens($type, $tokens, array $data = array(), array $options = array()) {
// Setup our replacements array.
$replacements = array();
// Make sure we have a node.
if (isset($data['entity_type']) && $data['entity_type'] == 'node') {
$node = $data['entity'];
// Get the available fields from the location module.
$available = location_field_names(TRUE);
// Loop through the tokens.
foreach ($tokens as $name => $original) {
// Break token into an array.
$pieces = explode(':', str_replace(array(
'[',
']',
), '', $original));
// Must have entity, field, and item to be a location cck token.
if (count($pieces) < 3) {
continue;
}
$entity = $pieces[0];
$field = $pieces[1];
$field_info = field_info_field($field);
// Make sure this is a field and it is a location field.
if (!isset($field_info) || isset($field_info) && $field_info['type'] != 'location') {
continue;
}
$item = $pieces[2];
// Allow for a position at the end of the token. If no position is given
// default it to the first element.
$position = isset($pieces[3]) && is_numeric($pieces[3]) ? $pieces[3] : 0;
// If the token is in the $available, replace it.
if (array_key_exists($item, $available) && $available[$item]) {
$replacements[$original] = isset($node->{$field}[LANGUAGE_NONE][$position][$item]) ? $node->{$field}[LANGUAGE_NONE][$position][$item] : '';
}
}
}
return $replacements;
}
/**
* Implements hook_locationapi().
*/
function location_cck_locationapi(&$obj, $op, $a3 = NULL, $a4 = NULL, $a5 = NULL) {
switch ($op) {
case 'instance_links':
foreach ($obj as $k => $v) {
if (substr($v['genid'], 0, 4) == 'cck:') {
$data = explode(':', $v['genid']);
$obj[$k]['href'] = 'node/' . $data[2];
$obj[$k]['title'] = t('CCK location');
$obj[$k]['type'] = t('CCK location');
}
}
}
}
/**
* Implements hook_field_views_data().
*/
function location_cck_field_views_data($field) {
$data = field_views_field_default_views_data($field);
foreach ($data as $table_name => $table_data) {
foreach ($table_data as $field_name => $field_data) {
// Check for fieldapi value fields.
if (isset($field_data['filter']['field_name'])) {
$data[$table_name][$field_name]['relationship'] = array(
'handler' => 'views_handler_relationship',
'base' => 'location',
'base field' => 'lid',
'label' => t('Location from !field_name', array(
'!field_name' => $field['field_name'],
)),
);
}
}
}
return $data;
}