...
 
Commits (60)
<?php
/**
* @file
* Constains the views natural sort sorting handler.
*/
/**
* Sort handler for sorting records naturally.
*/
class views_natural_sort_handler_sort extends views_handler_sort {
/**
* {@inheritdoc}
*/
public function init(&$view, &$options) {
parent::init($view, $options);
$this->natural_sort = substr($this->options['order'], 0, 1) == 'N';
}
/**
* {@inheritdoc}
*/
public function sort_options() {
return array(
'ASC' => t('Sort ascending'),
'DESC' => t('Sort descending'),
'NASC' => t('Sort ascending naturally'),
'NDESC' => t('Sort descending naturally'),
);
}
/**
* {@inheritdoc}
*/
public function query() {
// If this field isn't being used as a Natural Sort Field, move along
// nothing to see here.
if (!$this->natural_sort) {
parent::query();
return;
}
// If someone has submitted the exposed form, lets grab it here.
if ($this->options['exposed'] && $this->view->exposed_data['sort_order']) {
$temporder = $this->view->exposed_data['sort_order'];
}
// If we are using this like a normal sort, our info will be here.
else {
$temporder = &$this->options['order'];
}
// Add the Views Natural Sort table for this field.
$vns_alias = 'vns_' . $this->table_alias;
if (empty($this->query->relationships[$vns_alias])) {
$this->ensure_my_table();
$vns_alias = $this->query->add_relationship('vns_' . $this->table_alias, $this->natural_sort_join(), $this->table, $this->relationship);
}
// Sometimes we get the appended N from the sort options. Filter it out
// here.
$order = substr($temporder, 0, 1) == 'N' ? substr($temporder, 1) : $temporder;
$this->query->add_orderby($vns_alias, 'content', $order);
}
/**
* Helper function that creates a join to the views natural sort table.
*/
public function natural_sort_join() {
$join = new views_join();
$table_data = views_fetch_data($this->table);
$join->definition = array(
'table' => 'views_natural_sort',
'field' => 'eid',
'left_field' => $table_data['table']['base']['field'],
'left_table' => $this->table,
'extra' => array(
array(
'field' => 'entity_type',
'value' => $table_data['table']['entity type'],
),
array(
'field' => 'field',
'value' => $this->real_field,
),
),
);
$join->construct();
$join->adjusted = TRUE;
return $join;
}
/**
* {@inheritdoc}
*/
public function admin_summary() {
if (!empty($this->options['exposed'])) {
return t('Exposed');
}
switch ($this->options['order']) {
case 'ASC':
case 'asc':
default:
return t('asc');
case 'DESC':
case 'desc':
return t('desc');
case 'NASC':
return t('natural asc');
case 'NDESC':
return t('natural asc');
}
}
}
<?php
/**
* @file
* Constains the views natural sort text field sorting handler.
*/
/**
* The text field sort handler for views.
*/
class views_natural_sort_handler_sort_text_field extends views_natural_sort_handler_sort {
/**
* {@inheritdoc}
*/
public function natural_sort_join() {
$join = new views_join();
$other_join = $this->get_join();
$table_data = views_fetch_data($other_join->definition['left_table']);
$join->definition = array(
'table' => 'views_natural_sort',
'field' => 'eid',
'left_field' => 'entity_id',
'left_table' => $this->table_alias,
'extra' => array(
array(
'field' => 'delta',
'value' => $this->table_alias . '.delta',
),
array(
'field' => 'entity_type',
'value' => $table_data['table']['entity type'],
),
array(
'field' => 'field',
'value' => preg_replace('/_value$/', '', $this->field),
),
),
);
$join->construct();
$join->adjusted = TRUE;
return $join;
}
}
name = Views Natural Sort Test
description = Set up module for Views Natural Sort testing.
package = Testing
core = 7.x
dependencies[] = views_natural_sort
hidden = TRUE
<?php
/**
* @file
* Install file for the testing module for Views Natural Sort.
*/
/**
* Implements hook_install().
*/
function views_natural_sort_test_install() {
node_types_rebuild();
$types = node_type_get_types();
node_add_body_field($types['views_natural_sort_test_content']);
}
<?php
/**
* @file
* Testing module for Views Natural Sort.
*/
/**
* Implements hook_form().
*/
function views_natural_sort_test_form($node, $form_state) {
return node_content_form($node, $form_state);
}
/**
* Implements hook_node_info().
*/
function views_natural_sort_test_node_info() {
return array(
'views_natural_sort_test_content' => array(
'name' => t('Views Natural Sort Test Content'),
'base' => 'node_content',
'description' => t('Content type used for testing the basic functionality of the Views Natural Sort module.'),
),
);
}
/**
* Implements hook_views_api().
*/
function views_natural_sort_test_views_api() {
return array(
'api' => 3.0,
'path' => drupal_get_path('module', 'views_natural_sort_test'),
);
}
/**
* Implements hook_views_default_views().
*/
function views_natural_sort_test_views_default_views() {
$view = new view();
$view->name = 'views_natural_sort_test';
$view->description = '';
$view->tag = 'default';
$view->base_table = 'node';
$view->human_name = 'Views Natural Sort Test';
$view->core = 7;
$view->api_version = '3.0';
$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
/* Display: Master */
$handler = $view->new_display('default', 'Master', 'default');
$handler->display->display_options['use_more_always'] = FALSE;
$handler->display->display_options['access']['type'] = 'perm';
$handler->display->display_options['cache']['type'] = 'none';
$handler->display->display_options['query']['type'] = 'views_query';
$handler->display->display_options['exposed_form']['type'] = 'basic';
$handler->display->display_options['pager']['type'] = 'none';
$handler->display->display_options['pager']['options']['offset'] = '0';
$handler->display->display_options['style_plugin'] = 'default';
$handler->display->display_options['row_plugin'] = 'fields';
/* Field: Content: Title */
$handler->display->display_options['fields']['title']['id'] = 'title';
$handler->display->display_options['fields']['title']['table'] = 'node';
$handler->display->display_options['fields']['title']['field'] = 'title';
$handler->display->display_options['fields']['title']['label'] = '';
$handler->display->display_options['fields']['title']['alter']['word_boundary'] = FALSE;
$handler->display->display_options['fields']['title']['alter']['ellipsis'] = FALSE;
/* Sort criterion: Content: Title */
$handler->display->display_options['sorts']['title']['id'] = 'title';
$handler->display->display_options['sorts']['title']['table'] = 'node';
$handler->display->display_options['sorts']['title']['field'] = 'title';
$handler->display->display_options['sorts']['title']['order'] = 'NASC';
/* Filter criterion: Content: Type */
$handler->display->display_options['filters']['type']['id'] = 'type';
$handler->display->display_options['filters']['type']['table'] = 'node';
$handler->display->display_options['filters']['type']['field'] = 'type';
$handler->display->display_options['filters']['type']['value'] = array(
'views_natural_sort_test_content' => 'views_natural_sort_test_content',
);
$views[$view->name] = $view;
return $views;
}
/**
* A convieance function that will create content used for testing.
*/
function views_natural_sort_test_views_create_test_content() {
$titles = array(
'1 apple',
'2 apples',
'10 apples',
'-1 apples',
'-10 apples',
'-2 apples',
'-3.550 apples',
'-3.5501 apples',
'3.5501 apples',
'3.550 apples',
'A(Z',
'A[B',
'A\\C',
'A Stripped Zebra',
'Oklahoma',
'The King And I',
);
foreach ($titles as $title) {
$node = new stdClass();
$node->type = 'views_natural_sort_test_content';
$node->title = $title;
node_object_prepare($node);
$node = node_submit($node);
node_save($node);
}
}
name = Views Natural Sort Text Field Test
description = Set up module for Views Natural Sort Text Field testing.
package = Testing
core = 7.x
dependencies[] = views_natural_sort_text_field
hidden = TRUE
<?php
/**
* @file
* Install file for the testing module for Views Natural Sort Text Field.
*/
/**
* Implements hook_install().
*/
function views_natural_sort_text_field_test_install() {
node_types_rebuild();
$field_name = 'field_test_vns_sort';
// Make sure the field doesn't already exist.
if (!field_info_field($field_name)) {
// Create the field.
$field = array(
'field_name' => $field_name,
'type' => 'text',
'views_natural_sort_enable_sort' => TRUE,
'settings' => array(
'max_length' => 64,
),
);
field_create_field($field);
// Create the instance.
$instance = array( 'field_name' => $field_name,
'entity_type' => 'node',
'bundle' => 'vns_text_field_test_content',
'label' => 'Test VNS Sort',
'description' => 'Test text to test Views Natural Sort functionality.',
'required' => TRUE,
);
field_create_instance($instance);
}
}
<?php
/**
* @file
* Testing module for Views Natural Sort Text Field.
*/
/**
* Implements hook_form().
*/
function views_natural_sort_text_field_test_form($node, $form_state) {
return node_content_form($node, $form_state);
}
/**
* Implements hook_node_info().
*/
function views_natural_sort_text_field_test_node_info() {
return array(
'vns_text_field_test_content' => array(
'name' => t('Views Natural Sort Text Field Test Content'),
'base' => 'node_content',
'description' => t('Content type used for testing the basic functionality of the Views Natural Sort Text Field module.'),
),
);
}
/**
* Implements hook_views_api().
*/
function views_natural_sort_text_field_test_views_api() {
return array(
'api' => 3.0,
'path' => drupal_get_path('module', 'views_natural_sort_text_field_test'),
);
}
/**
* Implements hook_views_default_views().
*/
function views_natural_sort_text_field_test_views_default_views() {
$view = new view();
$view->name = 'views_natural_sort_text_field_test';
$view->description = '';
$view->tag = 'default';
$view->base_table = 'node';
$view->human_name = 'Views Natural Sort Text Field Test';
$view->core = 7;
$view->api_version = '3.0';
$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
/* Display: Master */
$handler = $view->new_display('default', 'Master', 'default');
$handler->display->display_options['use_more_always'] = FALSE;
$handler->display->display_options['access']['type'] = 'perm';
$handler->display->display_options['cache']['type'] = 'none';
$handler->display->display_options['query']['type'] = 'views_query';
$handler->display->display_options['exposed_form']['type'] = 'basic';
$handler->display->display_options['pager']['type'] = 'none';
$handler->display->display_options['pager']['options']['offset'] = '0';
$handler->display->display_options['style_plugin'] = 'default';
$handler->display->display_options['row_plugin'] = 'fields';
/* Field: Content: Test VNS Sort */
$handler->display->display_options['fields']['field_test_vns_sort']['id'] = 'field_test_vns_sort';
$handler->display->display_options['fields']['field_test_vns_sort']['table'] = 'field_data_field_test_vns_sort';
$handler->display->display_options['fields']['field_test_vns_sort']['field'] = 'field_test_vns_sort';
$handler->display->display_options['fields']['field_test_vns_sort']['label'] = '';
$handler->display->display_options['fields']['field_test_vns_sort']['element_label_colon'] = FALSE;
/* Sort criterion: Content: Test VNS Sort (field_test_vns_sort) */
$handler->display->display_options['sorts']['field_test_vns_sort_value']['id'] = 'field_test_vns_sort_value';
$handler->display->display_options['sorts']['field_test_vns_sort_value']['table'] = 'field_data_field_test_vns_sort';
$handler->display->display_options['sorts']['field_test_vns_sort_value']['field'] = 'field_test_vns_sort_value';
$handler->display->display_options['sorts']['field_test_vns_sort_value']['order'] = 'NASC';
/* Filter criterion: Content: Type */
$handler->display->display_options['filters']['type']['id'] = 'type';
$handler->display->display_options['filters']['type']['table'] = 'node';
$handler->display->display_options['filters']['type']['field'] = 'type';
$handler->display->display_options['filters']['type']['value'] = array(
'vns_text_field_test_content' => 'vns_text_field_test_content',
);
$views[$view->name] = $view;
return $views;
}
/**
* A convieance function that will create content used for testing.
*/
function views_natural_sort_text_field_test_views_create_test_content() {
$titles = array(
'1 apple',
'2 apples',
'10 apples',
'-1 apples',
'-10 apples',
'-2 apples',
'-3.550 apples',
'-3.5501 apples',
'3.5501 apples',
'3.550 apples',
'A(Z',
'A[B',
'A\\C',
'A Stripped Zebra',
'Oklahoma',
'The King And I',
);
foreach ($titles as $title) {
$node = new stdClass();
$node->type = 'vns_text_field_test_content';
$node->title = $title;
$node->field_test_vns_sort[LANGUAGE_NONE][0]['value'] = $title;
node_object_prepare($node);
$node = node_submit($node);
node_save($node);
}
}
<?php
/**
* @file
* Callbacks for managing Views Natural Sort.
*/
/**
* Views Natural Sort Admin Settings Page callback.
*/
function views_natural_sort_settings_page() {
$content = array(
'settings' => drupal_get_form('views_natural_sort_settings_form'),
'rebuild' => drupal_get_form('views_natural_sort_rebuild_index_form'),
);
return $content;
}
/**
* Form callback for Views Natural Sort Rebuild Index page.
*
......@@ -14,16 +26,24 @@ function views_natural_sort_rebuild_index_form() {
$form = array();
$form['rebuild'] = array(
'#type' => 'submit',
'#value' => t('Rebuild title index'),
'#submit' => array('views_natural_sort_rebuild_index_submit'),
'#type' => 'fieldset',
'#title' => t('Incase of Emergency'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'button' => array(
'#type' => 'submit',
'#description' => 'Incase of an emergency.',
'#value' => t('Rebuild Index'),
'#submit' => array('views_natural_sort_rebuild_index_submit'),
),
);
return $form;
}
/**
* Form callback for the Views Natural Sort settings page
* Form callback for the Views Natural Sort settings page.
*
* Allows the removal of specific words and symbols from all your titles.
*/
......@@ -50,7 +70,19 @@ function views_natural_sort_settings_form() {
'#default_value' => variable_get('views_natural_sort_symbols_remove', ''),
'#description' => t('Most symbols are ignored when performing a sort naturally. Those symbols you want ignored go here. Do not use a separator. EX: &$".'),
);
$form['days_of_the_week_enabled'] = array(
'#type' => 'checkbox',
'#title' => 'Sort days of the week and their abbreviations',
'#description' => "Checking this setting will allow sorting of days of the week in their proper order starting with the day of the week that is configurable by you and for each language.",
'#efault_value' => variable_get('views_natural_sort_days_of_the_week_enabled', FALSE),
);
$form['batch_items'] = array(
'#type' => 'textfield',
'#title' => 'Items per Batch',
'#default_value' => variable_get('views_natural_sort_rebuild_items_per_batch', '500'),
'#description' => t('The number of items a batch process will work through at a given time. Raising this number will make the batch go quicker, however, raising it too high can cause timeouts and/or memory limit errors.'),
'#element_validate' => array('element_validate_integer_positive'),
);
$form['save'] = array(
'#type' => 'submit',
'#value' => t('Save Settings'),
......@@ -63,16 +95,17 @@ function views_natural_sort_settings_form() {
* Submit handler that saves custom word handlers and other settings.
*/
function views_natural_sort_settings_form_submit($form, &$form_state) {
$beginning_words = explode(',',$form_state['values']['beginning_words']);
$beginning_words = explode(',', $form_state['values']['beginning_words']);
array_walk($beginning_words, create_function('&$val', '$val = trim($val);'));
$words = explode(',',$form_state['values']['words']);
$words = explode(',', $form_state['values']['words']);
array_walk($words, create_function('&$val', '$val = trim($val);'));
$symbols = trim($form_state['values']['symbols']);
variable_set('views_natural_sort_beginning_words_remove', $beginning_words);
variable_set('views_natural_sort_words_remove', $words);
variable_set('views_natural_sort_symbols_remove', $symbols);
variable_set('views_natural_sort_days_of_the_week_enabled', $form_state['values']['days_of_the_week_enabled']);
variable_set('views_natural_sort_rebuild_items_per_batch', $form_state['values']['batch_items']);
views_natural_sort_rebuild_index_submit();
}
......@@ -80,10 +113,25 @@ function views_natural_sort_settings_form_submit($form, &$form_state) {
* Submit handler that triggers the rebuild_index batch.
*/
function views_natural_sort_rebuild_index_submit() {
views_natural_sort_rebuild_index_batch_set();
}
/**
* Sets up the batch job for reindexing all or specified VNS entry types.
*/
function views_natural_sort_rebuild_index_batch_set(array $entry_types = array()) {
if (empty($entry_types)) {
$entry_types = module_invoke_all('views_natural_sort_get_entry_types');
}
foreach ($entry_types as $entry_type) {
// Queue up all the data that needs to be rebuilt.
module_invoke_all('views_natural_sort_queue_rebuild_data', $entry_type);
}
// Run the queue.
$batch = array(
'operations' => array(
array('views_natural_sort_rebuild_index', array()),
),
'operations' => array(array('views_natural_sort_rebuild_index', array())),
'title' => t('Rebuilding Views Natural Sort Indexing Entries'),
'finished' => 'views_natural_sort_rebuild_index_finished',
'file' => drupal_get_path('module', 'views_natural_sort') . '/views_natural_sort.admin.inc',
);
......@@ -95,46 +143,28 @@ function views_natural_sort_rebuild_index_submit() {
* Batch API callback for rebuild_index.
*/
function views_natural_sort_rebuild_index(&$context) {
$queue = views_natural_sort_get_queue();
// Alias sandbox for easier referencing.
$sandbox = &$context['sandbox'];
// Initialize our context.
if (!isset($sandbox['max'])) {
$sandbox['progress'] = 0;
$sandbox['max'] = db_query('SELECT MAX(nid) FROM {node}')->fetchField();
$sandbox['total'] = db_query(
'SELECT COUNT(nid) FROM {node} WHERE nid <= :max',
array('max' => $sandbox['max'])
)->fetchField();
// Alias results for easier referencing.
$results = &$context['results'];
if (empty($sandbox)) {
$sandbox['current'] = 0;
$context['results']['nodes'] = 0;
if ($sandbox['total'] == 0) {
$context['finished'] = 1;
return;
$sandbox['max'] = $queue->numberOfItems();
$sandbox['items_per_batch'] = variable_get('views_natural_sort_rebuild_items_per_batch', '500');
}
for ($i = 0; $i < $sandbox['items_per_batch'] && $sandbox['current'] < $sandbox['max']; $i++) {
$item = $queue->claimItem(10);
if ($item) {
views_natural_sort_process_index_queue($item->data);
$queue->deleteItem($item);
}
$sandbox['current']++;
}
$results = db_query_range(
'SELECT nid, title FROM {node} WHERE nid > :current AND nid <= :max',
0,
10,
array('current' => $sandbox['current'], 'max' => $sandbox['max'])
);
$title = '';
foreach ($results as $row) {
_views_natural_sort_store_node($row);
++$sandbox['progress'];
$sandbox['current'] = $row->nid;
$title = $row->title;
++$context['results']['nodes'];
$results['entries'] = $sandbox['current'];
if ($sandbox['current'] != $sandbox['max']) {
$context['finished'] = $sandbox['current'] / $sandbox['max'];
}
$context['message'] = t('Processing node %title', array('%title' => $title));
$context['finished'] = $sandbox['progress'] / $sandbox['total'];
}
/**
......@@ -142,9 +172,9 @@ function views_natural_sort_rebuild_index(&$context) {
*/
function views_natural_sort_rebuild_index_finished($success, $results, $operations) {
if ($success) {
drupal_set_message(t('Index update has completed.'));
drupal_set_message(t('Index rebuild has completed.'));
drupal_set_message(t('Indexed %count.', array(
'%count' => format_plural($results['nodes'], '1 node', '@count nodes'),
'%count' => format_plural($results['entries'], '1 entry', '@count entries'),
)));
}
}
<?php
/**
* @file
* Hook Definition file for Views Natural Sort.
*/
/**
* Information used by the index rebuilding engine.
*
* This information is passed to each module during re-index so that modules can
* determine whether it needs to return items or not.
*
* @return array
* Array of arrays defining fields and entities to reindex
* array(
* array(
* 'entity_type' - string Ex. node
* 'field ' - string Field name to indicate during re-index
* ),
* )
*/
function hook_views_natural_sort_get_entry_types() {
return array(
array(
'entity_type' => 'user',
'field' => 'book_favorites',
),
);
}
/**
* Used for a custom module to queue data that needs to be re-indexed.
*
* This is typicall used when the module is installed or settings are changed.
*
* @param array $entry_type
* Array representing an entry type with an entity_type field pair.
* $entity_type - The type of the entity we are getting
* data that needs to be re-indexed from
* $field - The field that needs to be re-indexed.
*/
function hook_views_natural_sort_queue_rebuild_data(array $entry_type) {
if ($entry_type['entity_type'] != 'user' || $entry_type['field'] != 'book_favorites') {
return array();
}
$result = db_select('user', 'u')
->fields('u', array('uid', 'book_favorites'))
->execute();
$queue = views_natural_sort_get_queue();
foreach ($result as $row) {
// Grab the data returned and queue it up for transformation.
$queue->createItem = array(
'eid' => $row->uid,
'entity_type' => 'user',
'field' => 'book_favorites',
'delta' => 0,
'content' => $row->book_favorites,
);
}
}
/**
* Used to define custom transformations or reorder transformations.
*
* @param array &$transformations
* An array of transformations already defined.
* @param array $index_entry
* A representation of the original entry that is would have been put in the
* database before the transformation
* $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 original string before
* transformations.
*/
function hook_views_natural_sort_transformations_alter(array &$transformations, array $index_entry) {
// This function will receive a single argument that is the string that needs
// to be transformed. The transformation helps the database sort the entry
// to be more like a human would expect it to.
//
// This function will return a single string as well. Note these
// transformations happen serially, and the transformed string is passed on to
// the next function in the list. In the example below,
// `hook_my_special_transformation_function` will receive a string after all
// other transformations have happened.
$transformations[] = "_my_special_transformation_function";
// It is worth noting that the $index_entry does have the original string in
// it if you need to do some kind of magic. It is best to not clobber other
// people's transformations if you can help it though.
}
/**
* This is NOT A HOOK. Example transformation function.
*
* @param string $string
* The string to be transformed.
*
* @return string
* A transformed string used for sorting "Naturally".
*/
function _my_special_transformation_function($string) {
return str_replace('a', '', $string);
}
/**
* This hook has been deprecated and is no longer called.
*
* @deprecated
*
* @see hook_views_natural_sort_queue_rebuild_data
*/
function hook_views_natural_sort_queue_rebuild_data($entry_type) {
}
<?php
/**
* @file
* The Views Natural Sort module include file.
*/
/**
* Remove all the configured words from the beginning of the string only.
*
* @param string $string
* The string we wish to transform.
*
* @return string
* The transformed string.
*/
function views_natural_sort_remove_beginning_words($string) {
$beginning_words = variable_get('views_natural_sort_beginning_words_remove', array());
if (empty($beginning_words)) {
return $string;
}
array_walk($beginning_words, 'preg_quote');
return preg_replace(
'/^(' . implode('|', $beginning_words) . ')\s+/iu',
'',
$string
);
}
/**
* Remove all the configured words from the string.
*
* @param string $string
* The string we wish to transform.
*
* @return string
* The transformed string.
*/
function views_natural_sort_remove_words($string) {
$words = variable_get('views_natural_sort_words_remove', array());
if (empty($words)) {
return $string;
}
array_walk($words, 'preg_quote');
return preg_replace(
array(
'/\s(' . implode('|', $words) . ')\s+/iu',
'/^(' . implode('|', $words) . ')\s+/iu',
),
array(
' ',
'',
),
$string
);
}
/**
* Remove all the configured symbols from the string.
*
* @param string $string
* The string we wish to transform.
*
* @return string
* The transformed string.
*/
function views_natural_sort_remove_symbols($string) {
$symbols = variable_get('views_natural_sort_symbols_remove', '');
if (strlen($symbols) == 0) {
return $string;
}
return preg_replace(
'/[' . preg_quote($symbols) . ']/u',
'',
$string
);
}
/**
* Transform numbers in a string into a natural sortable string.
*
* Rules are as follows:
* - Embeded numbers will sort in numerical order. The following possibilities
* are supported
* - A leading dash indicates a negative number, unless it is preceded by a
* non-whitespace character, which case it is considered just a dash.
* - Leading zeros are properly ignored so as to not influence sort order
* - Decimal numbers are supported using a period as the decimal character
* - Thousands separates are ignored, using the comma as the thous. character
* - Numbers may be up to 99 digits before the decimal, up to the precision
* of the processor.
*
* @param string $string
* The string we wish to transform.
*/
function views_natural_sort_numbers($string) {
// Find an optional leading dash (either preceded by whitespace or the first
// character) followed by either:
// - an optional series of digits (with optional embedded commas), then a
// period, then an optional series of digits
// - a series of digits (with optional embedded commas)
return preg_replace_callback(
'/(\s-|^-)?(?:(\d[\d,]*)?\.(\d+)|(\d[\d,]*))/',
'_views_natural_sort_number_transform_match_callback',
$string
);
}
/**
* Transforms a string representing numbers into a special format.
*
* This special format can be sorted as if it was a number but in reality is
* being sorted alphanumerically.
*
* @param array $match
* Array of matches passed from preg_replace_callback
* $match[0] is the entire matching string
* $match[1] if present, is the optional dash, preceded by optional whitespace
* $match[2] if present, is whole number portion of the decimal number
* $match[3] if present, is the fractional portion of the decimal number
* $match[4] if present, is the integer (when no fraction is matched).
*
* @return string
* String representing a numerical value that will sort numerically in an
* alphanumeric search.
*/
function _views_natural_sort_number_transform_match_callback(array $match) {
// Remove commas and leading zeros from whole number.
$whole = (string) (int) str_replace(',', '', (isset($match[4]) && strlen($match[4]) > 0) ? $match[4] : $match[2]);
// Remove traililng 0's from fraction, then add the decimal and one trailing
// 0 and a space. The space serves as a way to always sort shorter decimal
// numbers that match exactly as less than longer ones.
// Ex: 3.05 and 3.05011.
$fraction = trim('.' . $match[3], '0') . '0 ';
$encode = sprintf('%02u', strlen($whole)) . $whole . $fraction;
if (strlen($match[1])) {
// Negative number. Make 10's complement. Put back any leading white space
// and the dash requires intermediate to avoid double-replacing the same
// digit. str_replace() seems to work by copying the source to the result,
// then successively replacing within it, rather than replacing from the
// source to the result.
// In this case since rules are reverced we also have to use a character
// that would be sorted higher than a space when a number is being compared
// against a longer one that is identical in negative numbers. This is so
// that longer numbers are always LESS than sorter numbers that have
// identical beginnings. Ex: -3.05 and -3.05011
$digits = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' ');
$intermediate = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k');
$rev_digits = array('9', '8', '7', '6', '5', '4', '3', '2', '1', '0', ':');
$encode = $match[1] . str_replace($intermediate, $rev_digits, str_replace($digits, $intermediate, $encode));
}
return $encode;
}
/**
* Simply convert days of the week over to numbers.
*
* This "should" be multilingual safe.
*
* @param string $string
* The string we are transforming days to numbers in.
*
* @return string
* The transformed string.
*/
function views_natural_sort_days_of_the_week_sort_days($string) {
global $language;
// Adding a configuration so that in the future an admin page can be made
// to switch which day is the first day of the week based on local.
$used_language = isset($language->language) ? $language->language : 'en';
$first_day = variable_get('views_natural_sort_days_of_the_week_first_day_' . $used_language, "Sunday");
$day_list = views_natural_sort_days_of_the_week_get_default_days();
$sorted_days = $day_list;
// Go through list and resort it and Translate it.
$start = array_search($first_day, $day_list);
for ($i = 0; $i < 7; $i++) {
$current_day = ($i + $start) % 7;
$abbreviations = views_natural_sort_days_of_the_week_get_acceptable_day_abbreviations($day_list[$current_day], $used_language);
$translated_day = t('@day', array('@day' => $day_list[$current_day]));
$string = preg_replace(
array(
'/\b' . $translated_day . '\b/i',
'/\b(' . implode(views_natural_sort_days_of_the_week_get_acceptable_day_abbreviations($day_list[$current_day]), '\.?|') . ')\b/i',
),
' ' . $i . ' ',
$string
);
}
return $string;
}
/**
* The default days of the week.
*/
function views_natural_sort_days_of_the_week_get_default_days() {
return array(
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
);
}
/**
* Gets all the configured abbreviations for a certain day of the week.
*
* @param string $day
* The day of the week we want to get abbreviations for.
* @param string $lang
* Determines which configuration to search for as they are keyed by language.
*
* @return array
* The abbreviations for the day of the week and language passed in.
*/
function views_natural_sort_days_of_the_week_get_acceptable_day_abbreviations($day, $lang = 'en') {
$default_days = views_natural_sort_days_of_the_week_get_default_days();
$index = array_search($day, $default_days);
$default_abbrev = views_natural_sort_days_of_the_week_get_default_day_abbreviations();
return variable_get(
'views_natural_sort_days_of_the_week_' . $lang . '_' . $day,
$default_abbrev[$index]
);
}
/**
* Default mappings of days of the week abbreviationas.
*/
function views_natural_sort_days_of_the_week_get_default_day_abbreviations() {
return array(
array("Sun"),
array("Mon"),
array("Tu", "Tue", "Tues"),
array("Wed"),
array("Th", "Thu", "Thur", "Thurs"),
array("Fri"),
array("Sat"),
);
}
name = Views Natural Sort
description = Sort results naturaly on a text field skipping articles like "the" and "a."
description = Sort results naturally on a node's title skipping articles like "the" and "a."
dependencies[] = views
dependencies[] = entity
package = Views
core = 7.x
; Views handlers
files[] = handlers/views_natural_sort_handler_sort.inc
files[] = views_natural_sort.test
<?php
/**
* @file
*
* The Views Natural Sort install file.
*/
/**
* Implementation of hook_schema().
* Implements hook_schema().
*/
function views_natural_sort_schema() {
// Contains relations between two users.
$schema['views_natural_sort'] = array(
'description' => t('Compressed titles for natural sorting.'),
'description' => 'Compressed titles for natural sorting.',
'fields' => array(
'nid' => array(
'description' => t('Node id'),
'eid' => array(
'description' => 'Entity id',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
),
'entity_type' => array(
'description' => 'Entity Type',
'type' => 'varchar',
'length' => 128,
'not null' => TRUE,
'default' => 'node',
),
'field' => array(
'description' => t('The field name. This will be title or some cck text field, etc.'),
'description' => 'The field name. This will be title or some cck text field, etc.',
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'default' => '',
),
'delta' => array(
'description' => 'The sequence number for this data item, used for multi-value fields',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'content' => array(
'description' => t('Filtered content used for sorting.'),
'description' => 'Filtered content used for sorting.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
),
'key' => array('nid', 'field', 'content'),
'primary key' => array('nid', 'field'),
'primary key' => array('eid', 'entity_type', 'field', 'delta'),
);
return $schema;
}
/**
* Implementation of hook_install().
* Implements hook_install().
*/
function views_natural_sort_install() {
variable_set(
......@@ -54,7 +67,7 @@ function views_natural_sort_install() {
t('An'),
t('La'),
t('Le'),
t('Il')
t('Il'),
)
);
variable_set(
......@@ -66,10 +79,12 @@ function views_natural_sort_install() {
)
);
variable_set('views_natural_sort_symbols_remove', "#\"'\\()[]");
variable_set('views_natural_sort_days_of_the_week_enabled', FALSE);
variable_set('views_natural_sort_rebuild_items_per_batch', 500);
}
/**
* Implementation of hook_enable().
* Implements hook_enable().
*/
function views_natural_sort_enable() {
module_load_include('inc', 'views_natural_sort', 'views_natural_sort.admin');
......@@ -77,20 +92,88 @@ function views_natural_sort_enable() {
}
/**
* Implementation of hook_uninstall().
* Implements hook_uninstall().
*/
function views_natural_sort_uninstall() {
variable_del('views_natural_sort_beginning_words_remove');
variable_del('views_natural_sort_words_remove');
variable_del('views_natural_sort_symbols_remove');
variable_del('views_natural_sort_days_of_the_week_enabled');
variable_del('views_natural_sort_rebuild_items_per_batch');
}
/**
* Impliments hook_update_N().
*
* Rebuild the sorting index after changes made for numerical data.
*/
function views_natural_sort_update_7001() {
module_load_include('inc', 'views_natural_sort', 'views_natural_sort.admin');
views_natural_sort_rebuild_index_submit();
}
/**
* Upgrade the 7.x-1.x table structure to the 7.x-2.x table structure.
*/
function views_natural_sort_update_7200() {
db_drop_primary_key('views_natural_sort');
db_add_field(
'views_natural_sort',
'entity_type',
array(
'description' => t('Entity Type'),
'type' => 'varchar',
'length' => 128,
'not null' => TRUE,
'default' => 'node',
)
);
db_add_field(
'views_natural_sort',
'delta',
array(
'description' => t('The sequence number for this data item, used for multi-value fields'),
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
)
);
db_change_field(
'views_natural_sort',
'nid',
'eid',
array(
'description' => t('Entity id'),
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
),
array(
'primary key' => array('eid', 'entity_type', 'field', 'delta'),
)
);
module_load_include('inc', 'views_natural_sort', 'views_natural_sort.admin');
views_natural_sort_rebuild_index_submit();
}
/**
* Upgrading 1.x VNS views to 2.x VNS views.
*/
function views_natural_sort_update_7201() {
foreach ($views as $view_name => $view) {
foreach ($view->display as &$display) {
if (!empty($display->display_options['sorts'])) {
foreach ($display->display_options['sorts'] as &$sort) {
if ($sort['table'] == 'views_natural_sort') {
$sort['table'] = 'node';
$sort['field'] = 'title';
$sort['order'] = 'N' . $sort['order'];
$view->save();
drupal_set_message(t('Views Natural Sort Upgraded the view !name.', array('!name' => $view_name)), 'status');
}
}
}
}
}
cache_clear_all();
return t('If you implement your views using Features, be sure to update the Featues that contain View Natural Sort Views immediatly after this upgrade and before the next revert.');
}
This diff is collapsed.
<?php
/**
* @file
* Tests for the Views Natural Sort module.
*/
module_load_include('test', 'views', 'tests/views_query');
/**
* Basic tests for the Views Natural Sort Module.
*/
class ViewsNaturalSortBasicTest extends ViewsTestCase {
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'Views Natural Sort Basic Test',
'description' => 'Tests basic functionality of View Natural Sort',
'group' => 'Views Natural Sort',
);
}
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp('views', 'views_natural_sort', 'views_natural_sort_test');
}
/**
* Test the default settings for stripping out the words A, An, and The.
*/
public function testNaturalSortDefaultBeginningWords() {
$titles = array(
'A Stripped Zebra',
'Oklahoma',
'The King And I',
);
$nodes = array();
foreach ($titles as $title) {
$nodes[] = $this->drupalCreateNode(array(
'type' => 'views_natural_sort_test_content',
'title' => $title,
));
}
$view = views_get_view('views_natural_sort_test');
$this->executeView($view);
$this->assertIdenticalResultset(
$view,
array(
array('title' => 'The King And I'),
array('title' => 'Oklahoma'),
array('title' => 'A Stripped Zebra'),
),
array('node_title' => 'title')
);
}
/**
* Test the default settings for symbol removal in sorting.
*/
public function testNaturalSortDefaultSymbols() {
$titles = array(
'A(Z',
'A[B',
'A\\C',
);
foreach ($titles as $title) {
$nodes[] = $this->drupalCreateNode(array(
'type' => 'views_natural_sort_test_content',
'title' => $title,
));
}
$view = views_get_view('views_natural_sort_test');
$this->executeView($view);
$this->assertIdenticalResultset(
$view,
array(
array('title' => 'A[B'),
array('title' => 'A\\C'),
array('title' => 'A(Z'),
),
array('node_title' => 'title')
);
}
/**
* Test Unicode symbol removal in sorting.
*/
public function testNaturalSortUnicodeSymbols() {
$symbols = variable_set('views_natural_sort_symbols_remove', "#…\",'\\()[]«?!»¡¿");
$titles = array(
'Cuando… se abre, ¿dará algún tipo de señal?',
);
$expected = array(
'Cuando se abre dará algún tipo de señal',
);
foreach ($titles as $key => $title) {
$this->assertEqual(views_natural_sort_remove_symbols($title), $expected[$key]);
}
}
/**
* Test sorting strings that contain numbers in them.
*/
public function testNaturalSortNumbers() {
$titles = array(
'1 apple',
'2 apples',
'10 apples',
'-1 apples',
'-10 apples',
'-2 apples',
'-3.550 apples',
'-3.5501 apples',
'3.5501 apples',
'3.550 apples',
);
foreach ($titles as $title) {
$nodes[] = $this->drupalCreateNode(array(
'type' => 'views_natural_sort_test_content',
'title' => $title,
));
}
$view = views_get_view('views_natural_sort_test');
$this->executeView($view);
$this->assertIdenticalResultset(
$view,
array(
array('title' => '-10 apples'),
array('title' => '-3.5501 apples'),
array('title' => '-3.550 apples'),
array('title' => '-2 apples'),
array('title' => '-1 apples'),
array('title' => '1 apple'),
array('title' => '2 apples'),
array('title' => '3.550 apples'),
array('title' => '3.5501 apples'),
array('title' => '10 apples'),
),
array('node_title' => 'title')
);
}
}
/**
* Tests all the "Days of the week" natural sort functionality.
*/
class ViewsNaturalSortDaysOfTheWeekTest extends DrupalWebTestCase {
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'Views Natural Sort Days of the Week',
'description' => 'Tests Days of the Week Sorting',
'group' => 'Views Natural Sort',
);
}
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp('views', 'views_natural_sort');
variable_set('views_natural_sort_days_of_the_week_enabled', TRUE);
}
/**
* Test tranformation functionality of the days of the week transformation.
*/
public function testDefaultDayReplace() {
foreach (views_natural_sort_days_of_the_week_get_default_days() as $replaced => $day) {
$this->assertEqual(views_natural_sort_days_of_the_week_sort_days($day), $replaced);
}
}