...
 
Commits (27)
===== 7.x-1.4 ======
* [#1832526] Fix for empty word replacement fields making regex compilation fail.
* [#1833388] Making the Help Text make more sense.
* [#1853554] Delete node title index on node delete to keep the table smaller.
===== 7.x-1.3 ======
* [#1042242] Adding ability to Define words and letters to ignore.
===== 7.x-1.2 ======
* [#546794] Implemented code provided by DanChadwick that sets the base standard for numerical sort.
* Fixed a divide-by-zero error when enabling module with no nodes.
===== 7.x-1.1 ======
* [#558668] Applied patch contributed by heatherann.
<?php
// $Id$
/**
* @file
* Callbacks for managing Views Natural Sort.
*/
/**
* Form callback for Views Natural Sort settings page..
* Form callback for Views Natural Sort Rebuild Index page.
*
* Allows rebuilding index but should also allow things like limiting what node
* types are indexed and cck text field index options.
*/
function views_natural_sort_settings_form() {
function views_natural_sort_rebuild_index_form() {
$form = array();
$form['rebuild'] = array(
......@@ -23,6 +22,60 @@ function views_natural_sort_settings_form() {
return $form;
}
/**
* Form callback for the Views Natural Sort settings page
*
* Allows the removal of specific words and symbols from all your titles.
*/
function views_natural_sort_settings_form() {
$form = array();
$form['beginning_words'] = array(
'#type' => 'textfield',
'#title' => 'Words to filter from the beginning of a phrase',
'#default_value' => implode(',', variable_get('views_natural_sort_beginning_words_remove', array())),
'#description' => t('Commonly, the words "A", "The", and "An" are removed when sorting book titles if they appear at the beginning of the title. Those would be great candidates for this field. Separate words with a comma.'),
);
$form['words'] = array(
'#type' => 'textfield',
'#title' => 'Words to filter from anywhere in a phrase',
'#default_value' => implode(',', variable_get('views_natural_sort_words_remove', array())),
'#description' => t('Commonly used words like "of", "and", and "or" are removed when sorting book titles. Words you would like filtered go here. Separate words with a comma.'),
);
$form['symbols'] = array(
'#type' => 'textfield',
'#title' => 'Symbols to filter from anywhere in a phrase',
'#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['save'] = array(
'#type' => 'submit',
'#value' => t('Save Settings'),
);
return $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']);
array_walk($beginning_words, create_function('&$val', '$val = trim($val);'));
$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);
views_natural_sort_rebuild_index_submit();
}
/**
* Submit handler that triggers the rebuild_index batch.
*/
......@@ -48,16 +101,30 @@ function views_natural_sort_rebuild_index(&$context) {
// Initialize our context.
if (!isset($sandbox['max'])) {
$sandbox['progress'] = 0;
$sandbox['max'] = db_result(db_query('SELECT MAX(nid) FROM {node}'));
$sandbox['total'] = db_result(db_query('SELECT COUNT(nid) FROM {node} WHERE nid < %d', $sandbox['max']));
$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();
$sandbox['current'] = 0;
$context['results']['nodes'] = 0;
if ($sandbox['total'] == 0) {
$context['finished'] = 1;
return;
}
}
$results = db_query_range('SELECT nid, title FROM {node} WHERE nid > %d AND nid < %d', $sandbox['current'], $sandbox['max'], 0, 10);
$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 = '';
while($row = db_fetch_object($results)) {
foreach ($results as $row) {
_views_natural_sort_store_node($row);
++$sandbox['progress'];
$sandbox['current'] = $row->nid;
......
; $Id$
name = Views Natural Sort
description = Sort results naturaly on a text field skipping articles like "the" and "a."
dependencies[] = views
package = Views
core = 6.x
core = 7.x
<?php
// $Id$
/**
* @file
*
......@@ -47,12 +46,51 @@ function views_natural_sort_schema() {
* Implementation of hook_install().
*/
function views_natural_sort_install() {
drupal_install_schema('views_natural_sort');
variable_set(
'views_natural_sort_beginning_words_remove',
array(
t('The'),
t('A'),
t('An'),
t('La'),
t('Le'),
t('Il')
)
);
variable_set(
'views_natural_sort_words_remove',
array(
t('and'),
t('or'),
t('of'),
)
);
variable_set('views_natural_sort_symbols_remove', "#\"'\\()[]");
}
/**
* Implementation of hook_enable().
*/
function views_natural_sort_enable() {
module_load_include('inc', 'views_natural_sort', 'views_natural_sort.admin');
views_natural_sort_rebuild_index_submit();
}
/**
* Implementation of hook_uninstall().
*/
function views_natural_sort_uninstall() {
drupal_uninstall_schema('views_natural_sort');
variable_del('views_natural_sort_beginning_words_remove');
variable_del('views_natural_sort_words_remove');
variable_del('views_natural_sort_symbols_remove');
}
/**
* 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();
}
<?php
// $Id$
/**
* @file
* Provides a views filter that sorts titles by a more natural manner by
......@@ -24,18 +23,35 @@
* Implementation of hook_menu().
*/
function views_natural_sort_menu() {
//
$items = array();
$items['admin/settings/views_natural_sort'] = array(
$items['admin/config/views_natural_sort'] = array(
'title' => 'Views Natural Sort',
'description' => 'Provides options for what should be indexed for sorting.',
'description' => 'Configuration and settings for natural sorting.',
'page callback' => 'system_admin_menu_block_page',
'access arguments' => array('administer site configuration'),
'file' => 'system.admin.inc',
'file path' => drupal_get_path('module', 'system'),
);
$items['admin/config/views_natural_sort/rebuild_index'] = array(
'title' => 'Rebuild Search Index',
'description' => 'Rebuild Views Natural Sort\'s search index',
'page callback' => 'drupal_get_form',
'page arguments' => array('views_natural_sort_rebuild_index_form'),
'access callback' => 'user_access',
'access arguments' => array('administer views'),
'file' => 'views_natural_sort.admin.inc',
'type' => MENU_NORMAL_ITEM,
);
$items['admin/config/views_natural_sort/settings'] = array(
'title' => 'Configure Word Removal Lists',
'description' => 'Set what words should be ignored when performing a natural sort.',
'page callback' => 'drupal_get_form',
'page arguments' => array('views_natural_sort_settings_form'),
'access callback' => 'user_access',
'access arguments' => array('administer views'),
'file' => 'views_natural_sort.admin.inc',
'type' => MENU_NORMAL_ITEM
'type' => MENU_NORMAL_ITEM,
);
return $items;
......@@ -51,18 +67,30 @@ function views_natural_sort_views_api() {
}
/**
* Implementation of hook_nodeapi().
* Implements hook_node_insert().
*
* This keeps our natural sort index up to date.
*/
function views_natural_sort_nodeapi(&$node, $op) {
//
switch ($op) {
case 'update':
case 'insert':
_views_natural_sort_store_node($node);
break;
}
function views_natural_sort_node_insert($node) {
_views_natural_sort_store_node($node);
}
/**
* Implementation of hook_node_update().
*
* This keeps our natural sort index up to date.
*/
function views_natural_sort_node_update($node) {
_views_natural_sort_store_node($node);
}
/**
* Implementation of hook_node_delete().
*
* This keep sour natural sort index clean.
*/
function views_natural_sort_node_delete($node) {
_views_natural_sort_remove_node($node);
}
/**
......@@ -70,28 +98,150 @@ function views_natural_sort_nodeapi(&$node, $op) {
*
* @param $node
* A drupal node object containing at least a nid and title.
*
* @return int
* MergeQuery::STATUS_UPDATE or MergeQuery::STATUS_INSERT
*/
function _views_natural_sort_store_node($node) {
$record = new stdClass();
$record->nid = $node->nid;
$record->field = 'title';
$record->content = _views_natural_sort_filter_content($node->title);
// Try to update. On fail, try inserting.
$return = drupal_write_record('views_natural_sort', $record, array('nid', 'field'));
if (!db_affected_rows()) {
$return = drupal_write_record('views_natural_sort', $record);
return db_merge('views_natural_sort')
->key(array(
'nid' => $node->nid,
'field' => 'title'
))
->fields(array(
'nid' => $node->nid,
'field' => 'title',
'content' => views_natural_sort_encode($node->title)
))
->execute();
}
/**
* Helper function for removing node data from our sort index.
*
* @param $node
* A drupal node object containing at least a nid.
*
* @return int
* The number of rows deleted.
*/
function _views_natural_sort_remove_node($node) {
return db_delete('views_natural_sort')
->condition('nid', $node->nid)
->execute();
}
/**
* Encodes a string into an ascii-sortable such:
* - 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
* - Embeded numbers will sort in numerical order. The following possiblities
* 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 to be encoded
*
* @return string
* The encoded string
*/
function views_natural_sort_encode($string) {
$words = variable_get('views_natural_sort_words_remove', array());
$beginning_words = variable_get('views_natural_sort_beginning_words_remove', array());
$symbols = variable_get('views_natural_sort_symbols_remove', '');
// Get the words ready for being put in a regex.
array_walk($beginning_words, 'preg_quote');
array_walk($words, 'preg_quote');
$regex = array();
$replace = array();
// Remove words from the beginning only!
if (!empty($beginning_words)) {
$regex[] = '/^(' . implode('|', $beginning_words) . ')\s+/i';
$replace[]='';
}
// Remove words reguardless where they are as long as they are a word.
if (!empty($words)) {
$regex[] = '/\s(' . implode('|', $words) . ')\s+/i';
$replace[] = ' ';
$regex[] = '/^(' . implode('|', $words) . ')\s+/i';
$replace[] = '';
}
// Remove symbols.
if (strlen($symbols) != 0) {
$regex[] = '/[' . preg_quote($symbols) . ']/';
$replace[] = '';
}
if (!empty($regex) && !empty($replace)) {
$string = preg_replace($regex, $replace, $string);
}
// Find an optional leading dash (either preceded by whitespace or the first character) followed
// by either:
// - an optional series of digits (with optional imbedded commas), then a period, then an optional series of digits OR
// - a series of digits (with optional imbedded commas)
$string = preg_replace_callback(
'/(\s-|^-)?(?:(\d[\d,]*)?\.(\d+)|(\d[\d,]*))/',
'_views_natural_sort_number_encode_match_callback',
$string
);
// Not exactly sure why sometimes data that has been preg replaced comes back
// without utf8_encoding. This has been known to make Mysql vomit, so encoding
// here. This isn't seen anywhere else in drupal though.
// @see http://drupal.org/node/1914098
$string = utf8_encode($string);
return $return;
// 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.
return substr($string, 0, 255);
}
/**
* Helper function for filtering out what the value we should sort on is.
* @param $content
* Original content.
* @return
* Filtered value.
* Encodes a string representing numbers into a special format that can be 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_filter_content($content) {
return preg_replace('/^(The|A)\s/i', '', $content);
function _views_natural_sort_number_encode_match_callback($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
$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.
$digits = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
$intermediate = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j');
$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;
}
<?php
// $Id$
/**
* @file
* Views related hooks.
......@@ -31,7 +30,15 @@ function views_natural_sort_views_data() {
$data['views_natural_sort']['content'] = array(
'title' => 'Title - Natural',
'group' => t('Node'),
'help' => t('Sort title using a natural sort.'),
'help' => t('The title of a node filtered for natural sorting.'),
// Normally you wouldn't want a field or argument on this but its available for grouping.
'field' => array(
'handler' => 'views_handler_field_node',
'click sortable' => TRUE,
),
'argument' => array(
'handler' => 'views_handler_argument_string',
),
'sort' => array(
'handler' => 'views_handler_sort',
),
......