'checkbox', '#title' => t('Store geo data for this vocabulary'), '#default_value' => variable_get("geotaxonomy_$vid", 0), '#description' => t('Allows latitude and longitude data to be stored with terms of this taxonomy.'), ); $form['#submit'][] = 'geotaxonomy_form_vocabulary_submit'; } /** * Additional submit handler for vocabulary form to accomodate geo setting. */ function geotaxonomy_form_vocabulary_submit($form, &$form_state) { $vid = $form_state['values']['vid']; variable_set("geotaxonomy_$vid", $form_state['values']['geotaxonomy_status']); } /** * Implementation of specific hook_form_alter(). * Allows entry of geo data on term edit page. */ function geotaxonomy_form_taxonomy_form_term_alter(&$form, &$form_state) { $vid = $form['vid']['#value']; if (!variable_get("geotaxonomy_".$vid, 0)) { return; } $form['submit']['#weight'] = $form['submit']['#weight'] + 1; $form['delete']['#weight'] = $form['delete']['#weight'] + 1; $latlon = geotaxonomy_get_term($form['tid']['#value']); $form['geotaxonomy'] = array( '#type' => 'fieldset', '#title' => t("Geotaxonomy data"), '#collapsible' => 1, ); $form['geotaxonomy']['lat'] = array ( '#type' => 'textfield', '#title' => t('Latitude'), '#default_value' => $latlon['lat'], ); $form['geotaxonomy']['lon'] = array ( '#type' => 'textfield', '#title' => t('Longitude'), '#default_value' => $latlon['lon'], ); $form['geotaxonomy']['bound_top'] = array ( '#type' => 'textfield', '#title' => t('Bound Top'), '#default_value' => $latlon['bound_top'], ); $form['geotaxonomy']['bound_right'] = array ( '#type' => 'textfield', '#title' => t('Bound Right'), '#default_value' => $latlon['bound_right'], ); $form['geotaxonomy']['bound_bottom'] = array ( '#type' => 'textfield', '#title' => t('Bound Bottom'), '#default_value' => $latlon['bound_bottom'], ); $form['geotaxonomy']['bound_left'] = array ( '#type' => 'textfield', '#title' => t('Bound Left'), '#default_value' => $latlon['bound_left'], ); // Helper map for lat/lon if OpenLayers UI is available. // Not compatiable with current openlayers module. if (module_exists('openlayers_ui')) { drupal_add_js(drupal_get_path('module', 'geotaxonomy') .'/js/geotaxonomy.admin.js', 'module'); $default_preset = openlayers_preset_load(variable_get('openlayers_default_preset', 'default')); $defaults = $default_preset->data; $form['geotaxonomy']['helpmap'] = array( '#value' => '
'. geotaxonomy_form_latlon_map($defaults) .'
' ); } } /** * Create LatLon Helper Map. */ function geotaxonomy_form_latlon_map($defaults = array()) { // Pass variables etc. to javascript $pass_values = array( 'geotaxonomy' => array(), ); drupal_add_js($pass_values, 'setting'); // Set up our map to help set lat and lon // This map will always be projected as 4326 and use just the default map preset $centermap_def = array( 'id' => 'geotaxonomy-latlon-helpmap', 'projection' => '4326', 'default_layer' => 'openlayers_default_wms', 'width' => '400px', 'height' => '200px', 'center' => array( 'lat' => ($defaults['center']['lat']) ? $defaults['center']['lat'] : 0, 'lon' => ($defaults['center']['lon']) ? $defaults['center']['lon'] : 0, 'zoom' => ($defaults['center']['zoom']) ? $defaults['center']['zoom'] : 2, ), 'layers' => array( 'openlayers_default_wms', ), 'behaviors' => array( 'openlayers_behavior_navigation' => array(), ), 'options' => array( 'displayProjection' => '4326', 'maxResolution' => '1.40625', 'maxExtent' => array( 'top' => 90, 'bottom' => -90, 'left' => -180, 'right' => 180 ), ), ); return openlayers_render_map($centermap_def); } /** * Implementation of hook_node_views(). */ function geotaxonomy_views_api() { return array( 'api' => 2, 'path' => drupal_get_path('module', 'geotaxonomy') .'/views', ); } /** * Little API helper function to retrieve geo data for a given term. */ function geotaxonomy_get_term($tid) { return db_fetch_array(db_query("SELECT lat, lon, bound_top, bound_right, bound_bottom, bound_left FROM term_geo WHERE tid = %d", $tid)); } /** * Implementation of hook_taxonomy(). */ function geotaxonomy_taxonomy($op = NULL, $type = NULL, $term = NULL){ if($type =='term' && $term && variable_get("geotaxonomy_".$term['vid'], 0)) { switch ($op) { case 'delete': db_query("DELETE FROM {term_geo} WHERE tid = %d", $term['tid']); break; case 'update': db_query("DELETE FROM {term_geo} WHERE tid = %d", $term['tid']); case 'insert': $term_geo = array( 'tid' => $term['tid'], 'lat' => is_numeric($term['lat']) ? $term['lat'] : NULL, 'lon' => is_numeric($term['lon']) ? $term['lon'] : NULL, 'bound_top' => is_numeric($term['bound_top']) ? $term['bound_top'] : NULL, 'bound_right' => is_numeric($term['bound_right']) ? $term['bound_right'] : NULL, 'bound_bottom' => is_numeric($term['bound_bottom']) ? $term['bound_bottom'] : NULL, 'bound_left' => is_numeric($term['bound_left']) ? $term['bound_left'] : NULL, ); drupal_write_record('term_geo', $term_geo); break; } } } /** * FEEDAPI INTEGRATION * Allows flexible geo and other term importation. */ /** * Implementation of hook_feedapi_settings_form(). * If a module provides parsers and processors it MUST evaluate the $type variable * to return different forms for parsers and processors. * There might be a better term for parsers and processors than $type. */ function geotaxonomy_feedapi_settings_form($type) { $form = array(); switch ($type) { case 'processors': $vocabularies = taxonomy_get_vocabularies(); $v_types = array(); foreach ($vocabularies as $vocabulary) { $v_types[$vocabulary->vid] = $vocabulary->name; } $default_v = current(array_keys($v_types)); $form['vocabulary'] = array( '#type' => 'select', '#title' => t('Vocabulary of stored terms'), '#default_value' => $default_v, '#options' => $v_types, '#description' => t('Choose the vocabulary for terms created by this feed. Feedapi term assumes complete freedom in this vocab, meaning it will delete all the vocabs terms when purging and will also update any terms with matching names.'), ); break; } return $form; } /** * Implementation of hook_feedapi_item(). */ function geotaxonomy_feedapi_item($op) { switch ($op) { case 'type': return array("XML feed"); case 'save': case 'expire': case 'update': case 'delete': case 'purge': case 'unique': default: if (function_exists('_geotaxonomy_'. $op)) { $args = array_slice(func_get_args(), 1); return call_user_func_array('_geotaxonomy_'. $op, $args); } } } /** * Check for expired items. * Not currently supported, taxonomy doesn't store dates. */ function _geotaxonomy_expire($feed, $settings) { return 0; } /** * Create a term from the feed item. * Stores geo data as well so long as some of it is set. */ function _geotaxonomy_save($feed_item, $feed_nid, $settings = array()) { $feed_node = node_load($feed_nid); if ($feed_item = feedapi_mapper_map($feed_node, 'geotaxonomy', $feed_item)) { if (isset($feed_item['term_name'])) { $term = array( 'name' => $feed_item['term_name'], 'vid' => $settings['vocabulary'], 'description' => $feed_item['term_description'], 'weight' => $feed_item['term_weight'], ); if (drupal_write_record('term_data', $term)) { _geotaxonomy_item_tid((!empty($feed_item['term_unique']) ? $feed_item['term_unique'] : $feed_item['term_name'] . crc32(serialize($feed_item))), $term['tid']); if (is_numeric($feed_item['term_lat']) || is_numeric($feed_item['term_lon'])) { $term_geo = array( 'tid' => $term['tid'], 'lat' => is_numeric($feed_item['term_lat']) ? $feed_item['term_lat'] : NULL, 'lon' => is_numeric($feed_item['term_lon']) ? $feed_item['term_lon'] : NULL, 'bound_top' => is_numeric($feed_item['term_bound_top']) ? $feed_item['term_bound_top'] : NULL, 'bound_right' => is_numeric($feed_item['term_bound_right']) ? $feed_item['term_bound_right'] : NULL, 'bound_bottom' => is_numeric($feed_item['term_bound_bottom']) ? $feed_item['term_bound_bottom'] : NULL, 'bound_left' => is_numeric($feed_item['term_bound_left']) ? $feed_item['term_bound_left'] : NULL, ); drupal_write_record('term_geo', $term_geo); } } } } return $term; } /** * Static cache function to temporarily resolve hierarchical imported information until everything is safely in the database. */ function _geotaxonomy_item_tid($key, $tid = NULL) { static $allkeys = array(); if ($tid) { $allkeys[$key] = $tid; } return $allkeys[$key]; } /** * Update a node which already assigned to a feed item */ function _geotaxonomy_update($feed_item, $feed_nid, $settings, $id) { // not yet supported return array(); } /** * Delete a term which already assigned to a feed item */ function _geotaxonomy_delete($feed_item, $feed_nid) { $feed_node = node_load($feed_nid); if ($feed_item = feedapi_mapper_map($feed_node, 'geotaxonomy', $feed_item)) { if (isset($feed_item['term_name'])) { $tid = db_result(db_query("SELECT tid FROM {term_data} WHERE vid = %d AND name = '%s'", $feed_node->feed->settings['processors']['geotaxonomy']['vocabulary'], $feed_item['term_name'])); if($tid) { taxonomy_del_term($tid); } } } } /** * Delete all terms associated with a feed's vocabulary. */ function _geotaxonomy_purge($feed) { $feed_node = node_load($feed->nid); $result = db_query('SELECT tid FROM {term_data} WHERE vid = %d', $feed_node->feed->settings['processors']['geotaxonomy']['vocabulary']); while ($term = db_fetch_object($result)) { taxonomy_del_term($term->tid); } cache_clear_all(); drupal_set_message("Deleted all terms in the vocabulary."); } /** * Tell if the feed item was seen before or not at the feed * Assumes every term in the vocabulary it uses is its own property. * * @param $feed_item * Feed item object * @param $feed_nid * Feed ID * @return * TRUE if the item is new, FALSE if the item is a duplicated one */ function _geotaxonomy_unique($feed_item, $feed_nid, $settings) { $feed_node = node_load($feed_nid); if ($feed_item = feedapi_mapper_map($feed_node, 'geotaxonomy', $feed_item)) { if (isset($feed_item['term_name'])) { if (!empty($feed_item['term_parent_name'])) { $tid = db_result(db_query("SELECT td.tid FROM {term_data} td LEFT JOIN {term_hierarchy} th ON td.tid = th.tid LEFT JOIN {term_data} td2 ON td2.tid = th.parent WHERE td.vid = %d AND td.name = '%s' AND td2.name = '%s'", $feed_node->feed->settings['processors']['geotaxonomy']['vocabulary'], $feed_item['term_name'], $feed_item['term_parent_name'])); } else { $tid = db_result(db_query("SELECT td.tid FROM {term_data} td LEFT JOIN {term_hierarchy} th ON td.tid = th.tid WHERE td.vid = %d AND td.name = '%s' AND th.parent = 0", $feed_node->feed->settings['processors']['geotaxonomy']['vocabulary'], $feed_item['term_name'])); } if ($tid) { return $tid; } } else { return FALSE; } } return TRUE; } /** * Implementation of hook_feedapi_after_refresh(). */ function geotaxonomy_feedapi_after_refresh($feed) { if (in_array('geotaxonomy', $feed->processors)) { $feed_node = node_load($feed->nid); // Refuse to continue if there are no items on the feed. if (empty($feed->items)) { // feedapi will already alert the user, we just return. return; } $allitems = array(); foreach($feed->items as $feed_item){ if ($feed_item = feedapi_mapper_map($feed_node, 'geotaxonomy', $feed_item)) { if (isset($feed_item['term_name'])) { $tid = _geotaxonomy_item_tid((!empty($feed_item['term_unique']) ? $feed_item['term_unique'] : $feed_item['term_name'] . crc32(serialize($feed_item)))); if ($tid) { $hierarchy = array( 'tid' => $tid, 'parent' => 0, ); $ptid = 0; if (isset($feed_item['term_parent_unique'])) { $ptid = _geotaxonomy_item_tid($feed_item['term_parent_unique']); } if (!$ptid && isset($feed_item['term_parent_name'])) { // Just find parent by name. $ptid = db_result(db_query("SELECT tid FROM {term_data} WHERE vid = %d AND name = '%s'", $feed_node->feed->settings['processors']['geotaxonomy']['vocabulary'], $feed_item['term_parent_name'])); } $hierarchy['parent'] = $ptid ? $ptid : 0; drupal_write_record('term_hierarchy', $hierarchy); } } } } } } /** * Implementation of hook_feedapi_mapper(). */ function geotaxonomy_feedapi_mapper($op, $feed_node, $processor, $target = NULL, $feed_element = array(), $field_name = '', $sub_field = '') { if ($processor != 'geotaxonomy') { return; } switch ($op) { case 'describe': return t('Maps feed elements to taxonomy term, parent, and description fields as well as geotaxonomy information fields.'); break; case 'list': return array( 'term_name' => 'Term name', 'term_description' => 'Term description', 'term_weight' => 'Term weight', 'term_parent_name' => 'Term parent name. Unreliable if names can be duplicated.', 'term_unique' => 'Unique field to identify this term if names can be repeated, for creating hierarchy.', 'term_parent_unique' => 'Unique field of the parent of this term. Requires the unique field to be mapped as well to be relevant.', 'term_lat' => 'Latitude', 'term_lon' => 'Longitude', 'term_bound_top' => 'Bound Top', 'term_bound_right' => 'Bound Right', 'term_bound_bottom' => 'Bound Bottom', 'term_bound_left' => 'Bound Left', ); break; case 'map': // Map $feed_element to the key specified by $field_name. $target[$field_name] = $feed_element; return $target; } }