array( 'arguments' => array('element' => NULL), ), 'location_cck_formatter_all' => array( 'arguments' => array('element' => NULL), ), 'location_cck_formatter_map' => array( 'arguments' => array('element' => NULL), ), 'location_cck_formatter_multiple' => array( 'arguments' => array('element' => NULL), ), 'location_cck_formatter_multiple_all' => array( 'arguments' => array('element' => NULL), ), 'location_cck_field_map' => array( 'arguments' => array('locations' => NULL, 'field' => NULL), ), 'location_cck_field_popup' => array( 'arguments' => array('location' => NULL, 'field' => NULL), ), ); } /** * Implementation of hook_field_info(). */ function location_cck_field_info() { return array( 'location' => array( 'label' => t('Location'), 'description' => t('Store a location.module location.'), ), ); } /** * Implementation of hook_field_settings(). */ function location_cck_field_settings($op, $field) { switch ($op) { case 'form': $form = array(); $settings = isset($field['location_settings']) ? $field['location_settings'] : FALSE; $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['gmap_macro']) ? $field['gmap_macro'] : '[gmap ]', ); $options = gmap_get_marker_titles(); $form['gmap_marker'] = array( '#type' => 'select', '#title' => t('GMap marker'), '#options' => $options, '#default_value' => !empty($field['gmap_marker']) ? $field['gmap_marker'] : 'drupal', ); } else { // Preserve existing data, apply defaults even if gmap is disabled. $form['gmap_macro'] = array( '#type' => 'value', '#value' => !empty($field['gmap_macro']) ? $field['gmap_macro'] : '[gmap ]', ); $form['gmap_marker'] = array( '#type' => 'value', '#value' => !empty($field['gmap_marker']) ? $field['gmap_marker'] : 'drupal', ); } return $form; case 'save': return array('location_settings', 'gmap_macro', 'gmap_marker'); case 'database columns': return array( 'lid' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, ), ); 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' => $field_alias, 'handler' => 'views_handler_relationship', 'label' => t('Location'), // @@@ Some sort of better name? ); return $data; } } /** * Implementation of hook_field(). */ function location_cck_field($op, &$node, $field, &$items, $teaser, $page) { switch ($op) { case 'insert': case 'update': // Store instances of locations by field name and vid. $criteria = array( 'genid' => 'cck:'. $field['field_name'] .':'. $node->vid, 'vid' => $node->vid, 'nid' => $node->nid, ); location_save_locations($items, $criteria); // CCK automatically picks up the new lids and stores them in its own tables. break; case 'load': $locations = array(); // Locations are being cached by CCK now. Load the full location. foreach ($items as $item) { $locations[] = location_load_location($item['lid']); } return array($field['field_name'] => $locations); 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(location_load_location($item['lid']), $items[$delta]); } } break; case 'delete': // 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 {'. $db_info['table'] .'} WHERE nid = %d', $node->nid); while ($row = db_fetch_object($result)) { $genid = 'cck:'. $field['field_name'] .':'. $row->vid; $locs = array(); location_save_locations($locs, array('genid' => $genid)); } break; case 'delete revision': $genid = 'cck:'. $field['field_name'] .':'. $node->vid; $locs = array(); location_save_locations($locs, array('genid' => $genid)); break; } } /** * Implementation of hook_field_formatter_info(). */ function location_cck_field_formatter_info() { $info = array( 'default' => array( 'label' => t('Default (address)'), 'field types' => array('location'), ), ); if (module_exists('gmap')) { $info['all'] = array( 'label' => t('Address with map'), 'field types' => array('location'), ); $info['map'] = array( 'label' => t('Map only'), 'field types' => array('location'), ); $info['multiple'] = array( 'label' => t('Multiple field values on a single map'), 'field types' => array('location'), 'multiple values' => CONTENT_HANDLE_MODULE, ); $info['multiple_all'] = array( 'label' => t('Addresses with multiple field values on a single map'), 'field types' => array('location'), 'multiple values' => CONTENT_HANDLE_MODULE, ); } return $info; } /** * Implementation of hook_widget_info(). */ function location_cck_widget_info() { return array( 'location' => array( 'label' => t('Location Field'), 'field types' => array('location'), // Location will set its own custom values. 'callbacks' => array( //'default value' => CONTENT_CALLBACK_MODULE, ), ), ); } /** * Implementation of hook_widget(). */ function location_cck_widget(&$form, &$form_state, $field, $items, $delta = 0) { if ($field['widget']['type'] == 'location') { $settings = $field['location_settings']; // Load location data for existing locations. if (isset($items[$delta]['lid']) && $items[$delta]['lid']) { // Are we in a node preview? // If we aren't, then we only have the lid, because that's all cck // actually knows about internally. So, we have to pull in the location // at this point. if (empty($items[$delta]['cck_preview_in_progress'])) { $location = location_load_location($items[$delta]['lid']); } else { // Otherwise, the data was already populated and we're running // off the form state. $location = $items[$delta]; } } // Load location data passed by cck. 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. $temp = content_default_value($form, $form_state, $field, 0); if (is_array($temp) && isset($temp[$delta]) && is_array($temp[$delta])) { foreach ($temp[$delta] as $k => $v) { $settings['form']['fields'][$k]['default'] = $v; } } // unset($location['location_settings']); // unset($location['locpick']); } $element = array( '#type' => 'location_element', '#title' => t($field['widget']['label']), '#description' => t($field['widget']['description']), '#required' => $field['required'], '#location_settings' => $settings, '#default_value' => $location, ); // This is used to determine whether we are in a preview or not, because // several pieces of code work differently when previewing. $element['cck_preview_in_progress'] = array( '#type' => 'value', '#value' => TRUE, ); return $element; } } /** * CCK Emptiness check. */ function location_cck_content_is_empty($item, $field) { $fields = array(); if (location_is_empty($item, $fields)) { return TRUE; } return FALSE; } /** * Return an address for an individual item. */ function theme_location_cck_formatter_default($element) { $field = content_fields($element['#field_name'], $element['#type_name']); $hide = (isset($field['location_settings']['display']['hide'])) ? array_keys(array_filter($field['location_settings']['display']['hide'])) : array(); $location = $element['#item']; if (!empty($location['cck_preview_in_progress'])) { // Our canary field is in place, we are in a node preview. $fields = array(); // If the location isn't "empty", then theme it based on the current state // of the item. if (!location_is_empty($location, $fields)) { return theme('location', $location, $hide); } } else if (isset($location['lid']) && $location['lid']) { // "normal" viewing. // Location is already cached by CCK, so no need to load it. return theme('location', $location, $hide); } } /** * Return both an address and a map for an individual item. */ function theme_location_cck_formatter_all($element) { $content = ''; $field = content_fields($element['#field_name'], $element['#type_name']); $hide = (isset($field['location_settings']['display']['hide'])) ? array_keys(array_filter($field['location_settings']['display']['hide'])) : array(); $location = $element['#item']; if (!empty($location['cck_preview_in_progress'])) { // Our canary field is in place, we are in a node preview. $fields = array(); // If the location isn't "empty", then theme it based on the current state // of the item. if (!location_is_empty($location, $fields)) { $content = theme('location', $location, $hide); } } else if (isset($location['lid']) && $location['lid']) { // "normal" viewing. // Location is already cached by CCK, so no need to load it. $content = theme('location', $location, $hide); } $content .= theme_location_cck_field_map(array($location), $field); return $content; } /** * Returns a map for an individual field value. */ function theme_location_cck_formatter_map($element) { $field = content_fields($element['#field_name'], $element['#type_name']); $location = $element['#item']; return theme_location_cck_field_map(array($location), $field); } /** * Alternate function to return a map with all * multiple values in the same map. */ function theme_location_cck_formatter_multiple($element) { $field = content_fields($element['#field_name'], $element['#type_name']); $locations = array(); foreach (element_children($element) as $key) { $locations[$key] = $element[$key]['#item']; } return theme_location_cck_field_map($locations, $field); } /** * Return both a addresses and a single map with all location items. */ function theme_location_cck_formatter_multiple_all($element) { $content = ''; $field = content_fields($element['#field_name'], $element['#type_name']); $hide = (isset($field['location_settings']['display']['hide'])) ? array_keys(array_filter($field['location_settings']['display']['hide'])) : array(); $locations = array(); foreach (element_children($element) as $key) { $location = $element[$key]['#item']; $locations[$key] = $location; if (!empty($location['cck_preview_in_progress'])) { // Our canary field is in place, we are in a node preview. $fields = array(); // If the location isn't "empty", then theme it based on the current state // of the item. if (!location_is_empty($location, $fields)) { $content .= theme('location', $location, $hide); } } else if (isset($location['lid']) && $location['lid']) { // "normal" viewing. // Location is already cached by CCK, so no need to load it. $content .= theme('location', $location, $hide); } } $content .= theme_location_cck_field_map($locations, $field); return $content; } /** * 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($locations, $field) { $count = 0; $content = ''; foreach ($locations as $location) { if (location_has_coordinates($location)) { $count++; $markername = isset($field['gmap_marker']) ? $field['gmap_marker'] : 'drupal'; $markers[] = array( 'latitude' => $location['latitude'], 'longitude' => $location['longitude'], 'markername' => $markername, 'offset' => $count-1, 'text' => theme('location_cck_field_popup', $location, $field), ); } } if (!empty($markers)) { $macro = !empty($field['gmap_macro']) ? $field['gmap_macro'] : '[gmap ]'; $map = gmap_parse_macro($macro); $map['latitude'] = $markers[0]['latitude']; $map['longitude'] = $markers[0]['longitude']; $map['markers'] = $markers; $map['id'] = gmap_get_auto_mapid(); $content = theme('gmap', array('#settings' => $map)); } return $content; } /** * Theme the GMap popup text for the field. */ function theme_location_cck_field_popup($location, $field) { $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 = $field['widget']['label']; if (!empty($location['name'])) { $markertitle = $location['name']; } return '