You are here

location_taxonomize.module in Location Taxonomize 7

Same filename and directory in other branches
  1. 7.2 location_taxonomize.module

File

location_taxonomize.module
View source
<?php

/**
 * @file
 * A module which creates and maintains a Location Taxonomy
 * which is synchronized with Data from the Location module
 */
require_once 'location_taxonomize.inc';
require_once 'location_taxonomize.admin.inc';

/**
 * Implements hook_menu().
 */
function location_taxonomize_menu() {
  $items = array();

  // module configuration page
  $items['admin/config/content/location_taxonomize'] = array(
    'title' => 'Location Taxonomize',
    'description' => 'Configuration for Location taxonomize module',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'location_taxonomize_form',
    ),
    'access callback' => 'location_taxonomize_access_admin',
    'type' => MENU_NORMAL_ITEM,
  );
  return $items;
}

/**
 * custom access callback for admin form
 */
function location_taxonomize_access_admin() {
  $access_lt = user_access('administer location_taxonomize');
  $access_admin = user_access('access administration pages');
  return $access_lt && $access_admin;
}

/**
 * Implements hook_permission().
 */
function location_taxonomize_permission() {
  return array(
    'administer location_taxonomize' => array(
      'title' => t('Administer Location taxonomize'),
    ),
  );
}

/**
 * Implements hook_locationapi(). Used to save terms when locations are saved.
 */
function location_taxonomize_locationapi(&$obj, $op, $a3, $a4, $a5) {
  $settings = variable_get('location_taxonomize_settings');
  $vid = variable_get('location_taxonomize_vid');

  // check module settings
  if (!$settings['enable'] || !$vid) {
    return;
  }
  switch ($op) {

    // if we're saving a location, synchronize with vocab
    case 'save':
      $saved = location_taxonomize_process_loc($obj);
      $msg = t('Location taxonomize: @num new terms saved', array(
        '@num' => $saved,
      ));
      drupal_set_message($msg);
  }
}

/**
 * Called from the configuration form. Handles initialization of Location Taxonomy
 */
function location_taxonomize_initialize($form, $form_state) {
  $values_init = $form_state['values']['location_taxonomize_vocab'];
  $method = $values_init['method'];
  switch ($method) {
    case 'new':

      // create a new location vocabulary
      taxonomy_vocabulary_save((object) array(
        'name' => t('Location'),
        'machine_name' => LOCATION_TAXONOMIZE_VOCAB_NAME,
        'description' => t('This vocabulary is synchronized with Location data automatically by the @name module', array(
          '@name' => LOCATION_TAXONOMIZE_MODULE_NAME,
        )),
      ));

      // save the vid of the newly created vocabulary
      $vocab = taxonomy_vocabulary_machine_name_load(LOCATION_TAXONOMIZE_VOCAB_NAME);
      $vid = $vocab->vid;
      variable_set('location_taxonomize_vid', $vid);
      $msg = t('Successfully created the Location taxonomy (vid @vid)', array(
        '@vid' => $vid,
      ));
      break;
    case 'existing':

      // save the vid of the existing vocabulary
      $vid = $values_init['possible_vid'];
      variable_set('location_taxonomize_vid', $vid);
      $msg = t('Successfully connected to the existing Location Vocabulary (vid @vid)', array(
        '@vid' => $vid,
      ));
      break;
  }
  drupal_set_message($msg);

  // Create the 'taxonomy_longname' field if it doesn't already exist.
  if (!field_info_field('location_taxonomize_longname')) {
    $field = array(
      'field_name' => 'location_taxonomize_longname',
      'type' => 'text',
    );
    field_create_field($field);
  }

  // Create the instance on the bundle.
  if (!field_info_instance('taxonomy_term', 'location_taxonomize_longname', 'location_taxonomize')) {
    $instance = array(
      'field_name' => 'location_taxonomize_longname',
      'label' => t('Long Name'),
      'entity_type' => 'taxonomy_term',
      'bundle' => 'location_taxonomize',
    );
    field_create_instance($instance);
  }

  // set defaults for longname fields
  _location_taxonomize_init_var_longname();
  drupal_set_message(t('Location Taxonomize initialized successfully'));
}

/**
 * Determines if a location needs to be taxonomized. 
 * If so, it saves the appropriate terms
 * Returns the number of terms added
 * @param $obj - a location object
 */
function location_taxonomize_process_loc($obj) {

  // get settings
  $settings = variable_get('location_taxonomize_settings');
  $vid = variable_get('location_taxonomize_vid');
  $hierarchy = _location_taxonomize_get_hierarchy();

  // holds the number of newly saved terms
  $saved = 0;

  // make sure the province_name and country_name fields are there and updated
  $location = _fix_loc_tmp($obj);
  $tids = array();

  // this loops through hierarchy levels and saves terms if necessary
  for ($hlevel = 0; $hlevel < count($hierarchy); $hlevel++) {
    $hlevel_name = $hierarchy[$hlevel];

    // set the term name
    $name = _location_taxonomize_create_term_name($hlevel_name, $location, $settings);

    // find if the term exists already or not
    if ($hlevel == 0) {
      $parentid = -1;
    }
    else {
      $parentid = $tids[$hlevel - 1];
    }
    $findterm = _find_term($name, $hlevel, $parentid);

    // save if necessary
    if (!$findterm) {
      $term = (object) array(
        'name' => $name,
        'vid' => $vid,
      );

      // add longname if necessary
      if ($settings['longname_enable']) {
        _location_taxonomy_add_longname($term, $hlevel, $location);
      }

      // set term parent
      if ($hlevel == 0) {
        $term->parent = array(
          0,
        );
      }
      else {
        $term->parent = array(
          $parentid,
        );
      }
      taxonomy_term_save($term);
      $tids[] = $term->tid;
      $saved++;
    }
    elseif ($findterm->tid) {
      $tids[] = $findterm->tid;
    }
  }
  return $saved;
}

/**
 * Given a name, hierarchy level, and parent id, this function returns the term
 * if such a term exists, and FALSE if it doesn't.
 * It returns TRUE if the term was not found but should not be saved
 * NOTE: $parentid is only necessary if $hlevel > 0. Otherwise, set $parentid = -1
 */
function _find_term($name, $hlevel, $parentid) {
  $terms = taxonomy_get_term_by_name($name);

  // there are no terms that match
  if (!$terms) {
    return FALSE;
  }
  $withparent = array();

  // look through matching terms and find the one(s) with the right parent
  foreach ($terms as $term) {
    $parents = taxonomy_get_parents($term->tid);

    // for root-level terms:
    if (!$parents && $parentid == -1) {
      $withparent[] = $term;
    }
    elseif (array_pop($parents)->tid == $parentid) {
      $withparent[] = $term;
    }
  }

  // there is one term that matches exactly
  if (count($withparent) == 1) {
    return array_pop($withparent);
  }
  elseif (count($withparent) == 0) {
    return FALSE;
  }

  // if we get here, there was more than one term that matched exactly
  $msg = t("Location taxonomize couldn't save a term because its hierarchy\n          location was ambiguous.");
  drupal_set_message($msg);
  return TRUE;
}

/**
 * A temporary fix for an apparent bug, due to which the country_name field is
 * not updated during the call to hook_locationapi. Also used to add the
 * country_name and province_name fields to the location in cases where the
 * location is read from the database
 */
function _fix_loc_tmp($loc) {
  $loc['country_name'] = location_country_name($loc['country']);
  $loc['province_name'] = location_province_name($loc['country'], $loc['province']);
  return $loc;
}

/**
 * Adds a longname field to a term
 */
function _location_taxonomy_add_longname(&$term, $hlevel, $location) {

  // get settings
  $settings = variable_get('location_taxonomize_settings');
  $ln_settings = $settings['longname'];
  $fields = $ln_settings['fields'];
  $hierarchy = _location_taxonomize_get_hierarchy();

  // the name of the hierarchy level of this term (i.e. 'city')
  $term_hlevel_name = $hierarchy[$hlevel];

  // no Long Name if this hlevel_name is not selected in config
  if (!$fields[$term_hlevel_name]) {
    return '';
  }
  $longname = array();

  // collect parts of longname
  for ($i = 0; $i <= $hlevel; $i++) {
    $hlevel_name = $hierarchy[$i];
    $include = $fields[$hlevel_name];
    if ($include) {
      switch ($hlevel_name) {
        case 'country':
          if ($term_hlevel_name == 'country' || $ln_settings['country_naming'] == 'name') {
            $longname[] = $location['country_name'];
          }
          elseif ($location['country'] == 'us' && $ln_settings['usa']) {
            $longname[] = 'USA';
          }
          else {
            $longname[] = drupal_strtoupper($location['country']);
          }
          break;
        case 'province':
          if ($term_hlevel_name == 'province' || $ln_settings['province_naming'] == 'name') {
            $longname[] = $location['province_name'];
          }
          else {
            $longname[] = $location['province'];
          }
          break;
        default:
          $longname[] = $location[$hlevel_name];
          break;
      }
    }
  }

  // construct longname string from parts
  $longname = array_reverse($longname);
  $count = count($longname);
  $longname_str = '';
  for ($i = 0; $i < $count; $i++) {
    $longname_str .= $longname[$i];
    if ($i != $count - 1) {
      $longname_str .= check_plain($ln_settings['separator']) . ' ';
    }
  }

  // add the longname to the term
  $term->location_taxonomize_longname['und'][0]['value'] = $longname_str;
}

/**
 * Initializes the longname fields variable
 */
function _location_taxonomize_init_var_longname() {
  $hierarchy = _location_taxonomize_get_hierarchy(FALSE);
  $defaults = array();
  foreach ($hierarchy as $field) {
    if ($field != 'country' && $field != 'province' && $field != 'city') {
      $defaults[$field] = 0;
    }
    else {
      $defaults[$field] = $field;
    }
  }
  $settings = variable_get('location_taxonomize_settings');
  $settings['longname']['fields'] = $defaults;
  variable_set('location_taxonomize_settings', $settings);
}

/**
 * Helper function to create the name for a term to be saved, considering
 * all the config options
 */
function _location_taxonomize_create_term_name($hlevel_name, &$location, &$settings) {
  switch ($hlevel_name) {
    case 'country':
      if (empty($location['country'])) {
        $name = $settings['na_text'];
      }
      elseif ($settings['naming']['country'] == 'name') {
        $name = $location['country_name'];
      }
      elseif ($location['country'] == 'us' && $settings['naming']['usa']) {
        $name = 'USA';
      }
      else {
        $name = drupal_strtoupper($location['country']);
      }
      break;
    case 'province':
      if (empty($location['province'])) {
        $name = $settings['na_text'];
      }
      elseif ($settings['naming']['province'] == 'name') {
        $name = $location['province_name'];
      }
      else {
        $name = $location['province'];
      }
      break;
    default:
      if (empty($location[$hlevel_name])) {
        $name = $settings['na_text'];
      }
      else {
        $name = $location[$hlevel_name];
      }
      break;
  }
  return $name;
}

////////////////////////////////////////

// Feature: Bulk taxonomize

////////////////////////////////////////

/**
 * Calls the Batch API to run the bulk operation function
 */
function location_taxonomize_bulk_taxonomize($form, $form_state) {
  $batch = array(
    'operations' => array(
      array(
        'location_taxonomize_bulk_taxonomize_op',
        array(
          $form_state,
        ),
      ),
    ),
    'finished' => 'location_taxonomize_bulk_taxonomize_finished',
    'title' => t('Taxonomizing Locations'),
    'init_message' => t('Initializing...'),
    'progress_message' => t('Processed @current out of @total.'),
    'error_message' => t('Bulk Taxonomize encountered an error.'),
  );
  batch_set($batch);
}

/**
 * Runs the bulk taxonomize operation
 */
function location_taxonomize_bulk_taxonomize_op($form_state, &$context) {

  // initialize progress, max, and current if this is the first iteration
  if (!isset($context['sandbox']['progress'])) {
    $max = db_query('SELECT COUNT(DISTINCT lid) FROM {location}')
      ->fetchField();
    $context['sandbox']['progress'] = 0;
    $context['sandbox']['max'] = $max;
    $context['sandbox']['current'] = 0;
    $context['results']['added'] = 0;
    $context['results']['processed'] = 0;
  }

  // maximum 20 locations per function iteration
  $limit = 10;

  // iterate through the next group of locations
  $result = db_query_range("SELECT lid, name, street, additional, city, province, postal_code, country, latitude, longitude, source FROM {location} WHERE lid <> 0 ORDER BY lid ASC", $context['sandbox']['current'], $limit, array());
  $result_array = $result
    ->fetchAllAssoc('lid');
  foreach ($result_array as $row) {
    $added = location_taxonomize_process_loc((array) $row);
    $context['results']['added'] += $added;
    $context['sandbox']['progress']++;
    $context['message'] = t('Processing location lid %n', array(
      '%n' => $row->lid,
    ));
    $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
  }
  $context['sandbox']['current'] = $context['sandbox']['progress'];
  $context['results']['processed'] = $context['sandbox']['progress'];
}

/**
 * Last function to run after bulk taxonomize operation
 */
function location_taxonomize_bulk_taxonomize_finished($success, $results, $operations) {
  $success = t('Bulk Taxonomize completed successfully!');
  $locs = t('Processed @num locations', array(
    '@num' => $results['processed'],
  ));
  $terms = t('Saved @num new terms to the Location Vocabulary', array(
    '@num' => $results['added'],
  ));
  drupal_set_message($success);
  drupal_set_message($locs);
  drupal_set_message($terms);
}

Functions

Namesort descending Description
location_taxonomize_access_admin custom access callback for admin form
location_taxonomize_bulk_taxonomize Calls the Batch API to run the bulk operation function
location_taxonomize_bulk_taxonomize_finished Last function to run after bulk taxonomize operation
location_taxonomize_bulk_taxonomize_op Runs the bulk taxonomize operation
location_taxonomize_initialize Called from the configuration form. Handles initialization of Location Taxonomy
location_taxonomize_locationapi Implements hook_locationapi(). Used to save terms when locations are saved.
location_taxonomize_menu Implements hook_menu().
location_taxonomize_permission Implements hook_permission().
location_taxonomize_process_loc Determines if a location needs to be taxonomized. If so, it saves the appropriate terms Returns the number of terms added
_find_term Given a name, hierarchy level, and parent id, this function returns the term if such a term exists, and FALSE if it doesn't. It returns TRUE if the term was not found but should not be saved NOTE: $parentid is only necessary if $hlevel > 0.…
_fix_loc_tmp A temporary fix for an apparent bug, due to which the country_name field is not updated during the call to hook_locationapi. Also used to add the country_name and province_name fields to the location in cases where the location is read from the database
_location_taxonomize_create_term_name Helper function to create the name for a term to be saved, considering all the config options
_location_taxonomize_init_var_longname Initializes the longname fields variable
_location_taxonomy_add_longname Adds a longname field to a term