Skip to content
views_natural_sort.module 14.8 KiB
Newer Older
 * Views Natural Sort module.
 */

/*
 * Provides a views filter that sorts titles by a more natural manner by
 * ignoring articles like "The" and "A.".
 *
 * Normal sort:
 * A Chorus Line
 * All American
 * Fiddler on the Roof
 * Oklahoma!
 *
 * Natural sort:
 * All American
 * A Chorus Line
 * Fiddler on the Roof
 * The King And I
 * Oklahoma!
 */

/**
 * Implements hook_menu().
 */
function views_natural_sort_menu() {
  $items = array();
  $items['admin/structure/views/settings/views_natural_sort'] = array(
    'description' => 'Set the settings for how particular transformations should behave.',
    'page callback' => 'views_natural_sort_settings_page',
    'access callback' => 'user_access',
    'access arguments' => array('administer views'),
    'file' => 'views_natural_sort.admin.inc',
    'type' => MENU_LOCAL_TASK,
 * Implements hook_views_api().
 */
function views_natural_sort_views_api() {
  return array(
 * Implements hook_implements_alter().
function views_natural_sort_module_implements_alter(&$implements, $hook) {
  if ($hook == 'views_data_alter') {
    // Make views natural sort always last so we get all the up to date info.
    $group = $implements['views_natural_sort'];
    unset($implements['views_natural_sort']);
    $implements['views_natural_sort'] = $group;
  }
}
 * Implements hook_views_natural_sort_get_entry_types().
function views_natural_sort_views_natural_sort_get_entry_types() {
  $supported_entity_properties = views_natural_sort_get_views_configurable_properties();
  $entry_types = array();
  foreach ($supported_entity_properties as $entity_type => $properties) {
    foreach ($properties as $property => $schema_info) {
      $entry_types[] = array(
        'entity_type' => $entity_type,
        'field' => $property,
      );
    }
  }
  return $entry_types;
}

 * Implements hook_views_natural_sort_queue_rebuild_data().
function views_natural_sort_views_natural_sort_queue_rebuild_data($entry_type) {
  $supported_entity_properties = views_natural_sort_get_views_configurable_properties();
  if (empty($supported_entity_properties[$entry_type['entity_type']]) ||
    empty($supported_entity_properties[$entry_type['entity_type']][$entry_type['field']])) {
  $queue = views_natural_sort_get_queue();
  $result = $query->entityCondition('entity_type', $entry_type['entity_type'])
    ->execute();
  $entity_ids = array();

  if (isset($result[$entry_type['entity_type']])) {
    $entity_ids = array_keys($result[$entry_type['entity_type']]);
  }

  foreach ($entity_ids as $entity_id) {
    $results = entity_load($entry_type['entity_type'], array($entity_id));
    $entity = reset($results);
Allan Chappell's avatar
Allan Chappell committed
    var_dump($entry_type);
    $queue->createItem(array(
      'entity_type' => $entry_type['entity_type'],
 *
 * This keeps our natural sort index up to date.
 */
function views_natural_sort_entity_insert($entity, $type) {
  $supported_entity_properties = views_natural_sort_get_views_configurable_properties();
  if (empty($supported_entity_properties[$type])) {
    return;
  }
  foreach ($supported_entity_properties[$type] as $property => $property_info) {
    // Proposed by hgoto in #2672538. Made it in before he got credit.
    if (isset($entity->{$property})) {
      views_natural_sort_store(views_natural_sort_entity_to_vns($entity, $type, $property));
    }
 * Implements hook_entity_update().
 *
 * This keeps our natural sort index up to date.
 */
function views_natural_sort_entity_update($entity, $type) {
  views_natural_sort_entity_insert($entity, $type);
 * Implements hook_entity_delete().
 *
 * This keep sour natural sort index clean.
 */
function views_natural_sort_entity_delete($entity, $type) {
  $entity_info = entity_get_info($type);
  $id_field = $entity_info['entity keys']['id'];
  views_natural_sort_remove($entry = array(
    'eid' => $entity->$id_field,
    'entity_type' => $type,
  ));
/**
 * Implements hook_cron_queue_info().
 */
function views_natural_sort_cron_queue_info() {
  return array(
    'views_natural_sort_index_queue' => array(
      'worker callback' => 'views_natural_sort_process_index_queue',
      'skip on cron' => TRUE,
    ),
  );
}

/**
 * Queue worker callback for views_natural_sort_index_queue.
 *
 * @see callback_queue_worker
 */
function views_natural_sort_process_index_queue($data) {
  if (is_array($data)) {
    views_natural_sort_store($data);
  }
}

 * Store Multiple views_natural_sort entries.
 * @param array $index_entries
 *   An array of entries to store in the views_natural_sort table.
 * @see views_natural_sort_store
function views_natural_sort_store_multiple(array $index_entries) {
  foreach ($index_entries as $entry) {
    views_natural_sort_store($entry);
  }
 * Save an entry to the database that represents a views_natural_sort index.
 *
 * @param array $index_entry
 *   Mirrors the views_natural_sort table
 *     $eid - Entity Id of the item referenced
 *     $entity_type - The Entity Type. Ex. node
 *     $field - reference to the property or field name
 *     $delta - the item number in that field or property
 *     $content - The transformed data that a field will
 *                be sorted by.
 */
function views_natural_sort_store(array $index_entry) {
  // This should take a formatted object and store it into the
  // views_natural_sort table.
  $string = views_natural_sort_transform($index_entry);

  // The size limit on the content field for views_natual_sort is sometimes not
  // enough. Lets truncate all data down to that size. I personally feel the
  // inaccuracy is an acceptable loss, as the bigger the string gets, the less
  // permanent the sort.
  //
  // TODO: Have this pick up off of the schema so if someone does a
  // hook_schema_alter() on me.
  return db_merge('views_natural_sort')
    ->key(array(
      'eid' => $index_entry['eid'],
      'entity_type' => $index_entry['entity_type'],
      'field' => $index_entry['field'],
      'delta' => $index_entry['delta'],
    ))
    ->fields(array(
      'eid' => $index_entry['eid'],
      'entity_type' => $index_entry['entity_type'],
      'field' => $index_entry['field'],
      'delta' => $index_entry['delta'],
      'content' => substr($string, 0, 255),
    ))
    ->execute();
}

/**
 * Remove a views_natural_sort index entry based on keys.
 *
 * @param array $index_entry
 *   Mirrors the views_natural_sort table
 *     $eid - Entity Id of the item referenced
 *     $entity_type - The Entity Type. Ex. node
 *     $field - (optional) reference to the property or field name
 *     $delta - (optional)the item number in that field or property
 *   If an optional parameter doesn't exist, this is treated as a wild care
function views_natural_sort_remove(array $index_entry) {
  $query = db_delete('views_natural_sort')
    ->condition('eid', $index_entry['eid'])
    ->condition('entity_type', $index_entry['entity_type']);
  if (isset($index_entry['field'])) {
    $query->condition('field', $index_entry['field']);
  }
  if (isset($index_entry['delta'])) {
    $query->condition('delta', $index_entry['delta']);
  }
  $query->execute();
 * Encodes a string into an ascii-sortable string.
 *
 * Encoding rquires a set of transformations. Those transformations perform
 * functionality such as:
 *  - Leading articles in common languages are ingored: The A An El La Le Il
 *  - Unimportant punctuation is ignored: # ' " ( )
 *  - Unimportant words are ignored: and of or.
 * @param array $index_entry
 *   Mirrors the views_natural_sort table
 *     $eid - Entity Id of the item referenced
 *     $entity_type - The Entity Type. Ex. node
 *     $field - reference to the property or field name
 *     $delta - the item number in that field or property.
 * @return string
 *   The transformed string
function views_natural_sort_transform(array $index_entry) {
  // Get copy the original string.
  $string = $index_entry['content'];
  module_load_include('inc', 'views_natural_sort', 'views_natural_sort');
  foreach (views_natural_sort_get_transformations($index_entry) as $transformation_method) {
    $string = $transformation_method($string);
  }
  return $string;
/**
 * Get the full list of transformations to run when saving a natural sort entry.
 * @param array $index_entry
 *   The original entry to be written to the views_natural_sort table.
 *     $eid - Entity Id of the item referenced
 *     $entity_type - The Entity Type. Ex. node
 *     $field - reference to the property or field name
 *     $delta - the item number in that field or property
 *     $content - The transformed data that a field will
 *                be sorted by.
 *
 * @return array
 *   The final list of transformations.
 */
function views_natural_sort_get_transformations(array $index_entry) {
  $transformations = array(
    'views_natural_sort_remove_beginning_words',
    'views_natural_sort_remove_words',
    'views_natural_sort_remove_symbols',
    'views_natural_sort_numbers',
  );

  if (variable_get('views_natural_sort_days_of_the_week_enabled', FALSE)) {
    $transformations[] = "views_natural_sort_days_of_the_week_sort_days";
  }
  // Allow other modules to modify the transformation that happens here if
  // needed.
  drupal_alter('views_natural_sort_transformations', $transformations, $index_entry);
  return $transformations;
}
/**
 * Retrieve the full list of entities and properties that can be supported.
 *
 * @return array
 *   An array of property information keyed by entity machine name. Example:
 *   array (
 *    'node' => array (
 *      'type' => array (
 *        'base_table' => 'node',
 *        'schema_field' => 'type',
 *      ),
 *      'title' => array (
 *        'base_table' => 'node',
 *        'schema_field' => 'title',
 *      ),
 *      'language' => array (
 *        'base_table' => 'node',
 *        'schema_field' => 'language',
 *      ),
 *    ),
 *    'user' => array (
 *      'name' => array (
 *        'base_table' => 'users',
 *        'schema_field' => 'name',
 *      ),
 *      'mail' => array (
 *        'base_table' => 'users',
 *        'schema_field' => 'mail',
 *      ),
 *      'theme' => array (
 *        'base_table' => 'users',
 *        'schema_field' => 'theme',
 *      ),
 *    ),
 *    'file' => array (
 *      'name' => array (
 *        'base_table' => 'file_managed',
 *        'schema_field' => 'filename',
 *      ),
 *      'mime' => array (
 *        'base_table' => 'file_managed',
 *        'schema_field' => 'filemime',
 *      ),
 *    ),
function views_natural_sort_get_supported_entity_properties() {
  $supported_properties = &drupal_static(__FUNCTION__, array());
  if (empty($supported_properties)) {
    $entity_property_info = entity_get_property_info();
    $entity_info = entity_get_info();
    foreach ($entity_property_info as $entity_type => $info) {
      if (empty($supported_properties[$entity_type])) {
        $supported_properties[$entity_type] = array();
      }
      $properties = $info['properties'];
      foreach ($properties as $property => $property_info) {
        $property_info += entity_property_info_defaults();
        if ($property_info['type'] != 'text' || empty($property_info['schema field'])) {
          continue;
        }
        $schema = drupal_get_schema($entity_info[$entity_type]['base table']);
        $schema_field = $property_info['schema field'];
        if (empty($schema['fields'][$schema_field]) || $schema['fields'][$schema_field]['type'] != 'varchar') {
          continue;
        }
        $supported_properties[$entity_type][$property] = array(
          'base_table' => $entity_info[$entity_type]['base table'],
          'schema_field' => $property_info['schema field'],
        );
      }
    }
  }
  return $supported_properties;
}

/**
 * Returns a list of properties that we know views will allow us to alter.
 *
 * This list of properties is more realistic than "supported properties" because
 * it factors in what views actually contains handlers for. This is used by
 * all the administration functions to determine what properties need to be
 * affected by VNS.
 *   Returns an array formatted as
 *   views_natural_sort_get_supported_entity_properties or FALSE when views
 *   hasn't initianalized yet.
 * @see views_natural_sort_get_supported_entity_properties
 */
function views_natural_sort_get_views_configurable_properties() {
  $views_configurable_properties = &drupal_static(__FUNCTION__, array());
  if (empty($supported_properties)) {
    $supported_entity_properties = views_natural_sort_get_supported_entity_properties();
    $views_data = views_fetch_data();
    if (empty($views_data)) {
      return FALSE;
    }
    foreach ($supported_entity_properties as $entity => $properties) {
      foreach ($properties as $property => $schema_info) {
        if (!empty($views_data[$schema_info['base_table']][$schema_info['schema_field']]) &&
          !empty($views_data[$schema_info['base_table']][$schema_info['schema_field']]['sort']) &&
          !empty($views_data[$schema_info['base_table']][$schema_info['schema_field']]['sort']['handler']) &&
          in_array($views_data[$schema_info['base_table']][$schema_info['schema_field']]['sort']['handler'], array('views_natural_sort_handler_sort', 'views_handler_sort'))) {
          $views_configurable_properties[$entity][$property] = $schema_info;
        }
/**
 * A helper function for creating a VNS record for storage.
 *
 *   An object representing an entity.
 * @param string $entity_type
 *   The machine name for an entity type.
 * @param string $field
 *   The machine name for the field the data belongs to.
 *
 * @return array
 *   An array that represents the VNS table row to be inserted.
 */
function views_natural_sort_entity_to_vns($entity, $entity_type, $field) {
  $supported_entity_properties = views_natural_sort_get_views_configurable_properties();
  if (empty($supported_entity_properties[$entity_type]) ||
    empty($supported_entity_properties[$entity_type][$field])) {
    throw new Exception("$entity_type -> $field doesn't exist. Cannot create Views Natural Sort record");
  }
  $entity_info = entity_get_info($entity_type);
  $id_field = $entity_info['entity keys']['id'];
  return array(
    'eid' => $entity->$id_field,
    'entity_type' => $entity_type,
    'field' => $field,
    'delta' => 0,
    'content' => $entity->$field,
  );
}
 * Get the queue used to store natural sort index jobs.
 */
function views_natural_sort_get_queue() {
  return DrupalQueue::get('views_natural_sort_index_queue');
}