Location: PHPKode > projects > Glossword > plugins/search/gw_search.php
<?php
/**
 * @version		$Id$
 * @package		Joomla
 * @subpackage	Glossword Search
 * @copyright	© Dmitry N. Shilnikov, 2002-2010
 * @license		GNU/GPL, see http://code.google.com/p/glossword/
 */
defined( '_JEXEC' ) or die( 'Restricted access' );

$mainframe->registerEvent( 'onSearch', 'plgSearchGlossword' );
$mainframe->registerEvent( 'onSearchAreas', 'plgSearchGlosswordAreas' );

JPlugin::loadLanguage( 'plg_search_glossword' );

define( 'GW_AREAOFSEARCH', JText::_( 'AREAOFSEARCH' ) );

/**
 * @return array An array of search areas
 */
function &plgSearchGlosswordAreas()
{
	static $areas = array( 
		'gw_search' => GW_AREAOFSEARCH
	);
	return $areas;
}


/* */
function search_index__filter_si( &$o, $s )
{
	#$s = $this->rm_specials( $s );
	$s = search_index__str_normalize( $o, $s );
	$s = search_index__text_si( $o, trim( $s ) );
	return $s;
}
function search_index__text_si( &$o, $s )
{
	/* Do lowercase */
#	$s = $o->oCase->lc( $s );
	
	/* Mask non-ASCII characters */
	$s = preg_replace( "/([\\xc0-\\xff][\\x80-\\xbf]*)/e", "'U8' . bin2hex( \"$1\" )", $s );
	
	/* Mask MySQL stopwords */
	$s = search_index__mask_stopwords( $s );
	
	return $s;
}	
/* */
function search_index__str_normalize( &$o, $s  )
{
	$jo_db =& JFactory::getDBO();
	
	/* Do lowercase */
	$s = $o->oCase->lc( $s );

	/* Use PECL extension */
	if ( class_exists( 'Normalizer' ) )
	{
		return Normalizer::normalize( $s, Normalizer::FORM_C );
	}
	/* */
	preg_match_all( "/./u", $s, $ar );
	$ar = $ar[0];
	$ar_c_crc = array();
	/* For each character */
	foreach ($ar AS $k => &$v )
	{
		/* Use values as key */
		/* PHP-bug: sometimes a string keys becomes interger */
		$ar_c_crc[$v] = sprintf( "%u", crc32( $v ) );
	}

	unset( $v );
	if ( empty( $ar_c_crc ) ){ return $s; }

	/* */
	$query = 'SELECT `str_from`, `str_to`'
		. ' FROM '.$o->V->db_name.'.'.$o->V->table_prefix.'unicode_normalization '
		. ' WHERE `crc32u` IN ('. implode(',', array_values( $ar_c_crc ) ).')';
	$jo_db->setQuery( $query );
	$ar_sql = $jo_db->loadAssocList();
	if ( is_null( $ar_sql ) ){ $ar_sql = array(); }

	/* Normalize text */
	foreach ($ar_sql AS $k => &$v )
	{
		$s = str_replace( urldecode( $v['str_from'] ), urldecode( $v['str_to'] ), $s );
		unset( $ar_sql[$k] );
	}
	unset( $v );

	return $s;
}
function search_index__mask_stopwords( $s )
{
	return preg_replace( '/\b([a-z]+)/', '_\\1', $s );
}
function search_index__get_crc_u( $s )
{
	return sprintf( "%u", crc32( $s ) );
}


/* replacement for urlencode */
function ohtml_urlencode( $s )
{
	/* Encode special characters first */
	$s = str_replace( array( ',', '/', '+' ), array( '%2C', '%2F', '%2B' ), $s );
	$s = urlencode( $s );
	/* Restore separators for #area */
	$s = str_replace( array( '%01%01', '%02%02' ), array( ',', '.' ), $s );
	return $s;
}


/* */
function &plgSearchGlossword( $q, $phrase='', $ordering='', $areas=null )
{
	global $mainframe;

	$jo_db =& JFactory::getDBO();
	#$jo_user =& JFactory::getUser();
	#$jo_cfg =& JFactory::getConfig();
	$jo_component = &JComponentHelper::getComponent('com_glossword');
	$jo_menu = &JSite::getMenu();
	$jo_items = $jo_menu->getItems('componentid', $jo_component->id, true);

	#require_once( JPATH_SITE.DS.'components'.DS.'com_content'.DS.'helpers'.DS.'route.php' );
	
	if ( is_array( $areas ) ) 
	{
		if ( !array_intersect( $areas, array_keys( plgSearchGlosswordAreas() ) ) ) 
		{
			return array();
		}
	}
	
	/* Load plugin parameters */
	$jo_plugin =& JPluginHelper::getPlugin( 'search', 'gw_search' );
	$pluginParams = new JParameter( $jo_plugin->params );
	
	$offset = JRequest::getVar( 'start', 0, '', 'int' );
	$per_page = JRequest::getVar( 'limit', $pluginParams->get( 'int_per_page' ), '', 'int' );
	
	/* Load component configuration */
	$query = 'SELECT * FROM #__gw_config';
	$jo_db->setQuery( $query );
	$ar_sql = $jo_db->loadAssocList();
	if ( is_null( $ar_sql ) ){ $ar_sql = array(); }
	
	$ar_cfg = array();
	foreach ( $ar_sql as $k => $v )
	{
		$ar_cfg[$v['setting_key']] = $v['setting_value'];
	}
	
	require_once( $ar_cfg['path_core_abs'].'/gw_config.php' );
	
	/* */
	$_SERVER['REQUEST_TIME'] = isset( $_SERVER['REQUEST_TIME'] ) ? $_SERVER['REQUEST_TIME'] : mktime();
	$o = crc32( $_SERVER['REQUEST_TIME'] );
	
	${$o} = new site_db_config();
	
	${$o}->a( 'path_includes', $ar_cfg['path_core_abs'].'/'.${$o}->V->path_includes );

	foreach ( $ar_cfg as $setting_key => $setting_value )
	{
		${$o}->a( $setting_key, $setting_value );
	}
	
	/* */
	include_once( ${$o}->V->path_includes.'/class.case.php' );
	${$o}->oCase = new gwv_casemap;
	${$o}->oCase->is_use_mbstring = 1;

	$q = preg_replace( "/ {2,}/", ' ', $q );
	$q = trim( $q );
	
	$q_si = search_index__filter_si( ${$o}, $q );

	/* Need to test. Could not be equal. */
	$ar_words_sql = explode( ' ', $q_si );
	$ar_words_q = explode( ' ', $q );
	
	/* 11 Apr 2008: Enable search with asterisk for Chinese, Japanese and Korean characters */
	foreach ( $ar_words_q as $k => $word )
	{
		if ( preg_match( '/[\x{3040}-\x{312F}|\x{3400}-\x{9FFF}|\x{AC00}-\x{D7AF}]/u', $word, $ar_matches ) )
		{
			$ar_words_sql[$k] .= '*';
		}
	}
	
	/* Switch search modes */
	switch ( $phrase )
	{
		case 'any':
			$sql_against = implode( ' ', $ar_words_sql );
		break;
		case 'exact':
			$sql_against = '"'.implode( ' ', $ar_words_sql ).'"';
		break;
		default:
			$sql_against = '+'.implode( ' +', $ar_words_sql );
		break;
	}
	
	/**
	 * ----------------------------------------------
	 * Count Item IDs
	 * ----------------------------------------------
	 */
	$query = 'SELECT csi.id_item ';
	#$query .= "\n".', MATCH(csi.contents_si) AGAINST(\''. $jo_db->getEscaped( $sql_against ).'\' IN BOOLEAN MODE) score ';
	$query .= "\n".' FROM '.${$o}->V->db_name.'.'.${$o}->V->table_prefix.'contents_si csi';
	$query .= "\n".' WHERE ';
	$query .= "\n".' MATCH(csi.contents_si) AGAINST(\''.$jo_db->getEscaped( $sql_against ).'\' IN BOOLEAN MODE)';
	$query .= "\n".' GROUP BY csi.id_item ';

	$jo_db->setQuery( $query, 0, $pluginParams->get( 'int_search_max' ) );
	$jo_db->query();
	$cnt_records = $jo_db->getNumRows();
	
	/**
	 * ----------------------------------------------
	 * Select Item IDs
	 * ----------------------------------------------
	 */
	$jo_db->setQuery( 'SET SQL_BIG_SELECTS=1' );
	$jo_db->query();
	
	$query = 'SELECT csi.id_item';

	/* 1.9.3: Custom alphabetic order */
	$ar_join = array();
	for ( $i = 1; $i <= 8; $i++ )
	{
		$query .= ', az'.$i.'.int_sort';
		$ar_join[$i] = "\n".'LEFT JOIN '.${$o}->V->db_name.'.'.${$o}->V->table_prefix.'az_letters az'.$i.' ON ';
		$ar_join[$i] .= 'az'.$i.'.uc_crc32u = c.contents_'.$i.' AND c.id_lang = az'.$i.'.id_lang';
	}
	$query .= "\n".' FROM '.${$o}->V->db_name.'.'.${$o}->V->table_prefix.'contents_si csi, ';
	$query .= ${$o}->V->db_name.'.'.${$o}->V->table_prefix.'items i, '.${$o}->V->db_name.'.'.${$o}->V->table_prefix.'contents c';
	$query .= implode( ' ', $ar_join );

	$query .= "\n".' WHERE ';
	$query .= "\n".' MATCH(csi.contents_si) AGAINST(\''.$jo_db->getEscaped( $sql_against ).'\' IN BOOLEAN MODE)';
	$query .= ' AND i.id_item = c.id_item ';
	$query .= ' AND i.id_item = csi.id_item ';
	$query .= "\n".' GROUP BY csi.id_item ';
	
	$ar_order = array( 'c.id_lang' );

	/* Switch sorting modes */
	switch ( $ordering )
	{
		case 'newest':
			$ar_order[] = 'i.item_cdate DESC';
		break;
		case 'oldest': 
			$ar_order[] = 'i.item_cdate ASC';
		break;
		case 'popular': 
			$ar_order[] = 'i.cnt_hits DESC';
		break;
		default: 
		break;
	}

	/* 1.9.3: Custom alphabetic order */
	for ( $i = 1; $i <= 8; $i++ )
	{
		$ar_order[] = 'az'.$i.'.int_sort, c.contents_so';
	}

	$query .= "\n".'ORDER BY '.implode( ', ', $ar_order );
	
	#$query .= ' HAVING score > 0 ';
	#$query .= ' ORDER BY score DESC ';
	
	/* Can't use pagination for requests because of Joomla */
	/* Using `int_search_max` instead */
	/* @todo: workaround */
	#$jo_db->setQuery( $query, $offset, $per_page );
	$jo_db->setQuery( $query, 0, $pluginParams->get( 'int_search_max' ) );
	$ar_sql = $jo_db->loadAssocList();
	if ( is_null( $ar_sql ) ){ $ar_sql = array(); }

	$ar_item_ids = array();
	foreach ( $ar_sql as $ar_v )
	{
		$ar_item_ids[] = $ar_v['id_item'];
	}
	
	if ( empty( $ar_item_ids ) )
	{
		return array();
	}
	/**
	 * ----------------------------------------------
	 * Select Items
	 * ----------------------------------------------
	 */
	$ar_sql_items = array();
	if ( !empty( $ar_item_ids ) )
	{
		$query = 'SELECT uri.item_uri, i.id_item, i.item_id_user_created, i.item_cdate, c.contents_value_cached, c.id_field ';
		$query .= "\n".' FROM '.${$o}->V->db_name.'.'.${$o}->V->table_prefix.'items i, ';
		$query .= ${$o}->V->db_name.'.'.${$o}->V->table_prefix.'contents c, ';
		$query .= ${$o}->V->db_name.'.'.${$o}->V->table_prefix.'items_uri uri, ';
		$query .= ${$o}->V->db_name.'.'.${$o}->V->table_prefix.'map_field_to_fieldset mftf ';
		$query .= "\n".' WHERE i.id_item = c.id_item ';
		$query .= ' AND i.id_item = uri.id_item ';
		$query .= ' AND mftf.id_field = c.id_field ';
		$query .= ' AND mftf.id_fieldset = \'1\' ';
		$query .= ' AND i.id_item IN ('. implode( ', ', $ar_item_ids ).') ';
		$ar_order_by = array();
		foreach ( $ar_item_ids as $id_item_in )
		{
			$ar_order_by[] = 'i.id_item = "'.$id_item_in.'" DESC';
		}
		$ar_order_by[] = 'mftf.int_sort ASC';
		$query .= "\n".' ORDER BY '. implode( ', ', $ar_order_by );
		
		$jo_db->setQuery( $query );
		$ar_sql_items = $jo_db->loadAssocList();
		if ( is_null( $ar_sql_items ) ){ $ar_sql_items = array(); }
	}
	/* Re-arrange */
	$ar_items = array();
	foreach ( $ar_sql_items as $k => $ar_v)
	{
		$ar_items[$ar_v['id_item']][$ar_v['id_field']] = $ar_v;
		unset( $ar_sql_items[$k] );
	}

	/* */
	$cnt = 0;
	$oResults[0] = (object) 'results';
	foreach ( $ar_items as $id_item => $ar_fields_content)
	{
		$ar_str_item_title = array();
		$ar_str_item_descr = array();
		foreach ( $ar_fields_content as $id_field => $ar_v)
		{
			switch ( $id_field )
			{
				case 1:
					$ar_str_item_title[] = $ar_v['contents_value_cached'];
				break;
				/* More to come */
				default:
					$ar_str_item_descr[] = $ar_v['contents_value_cached'];
				break;
			}
		}
		/* Item title */
		$str_item = implode( ' ', $ar_str_item_title );
		
		/* Hyperlink to item */
		$href_area = 'a.search,q.'. ohtml_urlencode( $str_item ). ',t.items';
		$href_area = urlencode( $href_area );
		
		$href = JRoute::_( 'index.php?option='.$jo_component->option.'&Itemid='.$jo_items->id.'&arg[area]='.$href_area.'&view=default' );

		/* Item description */
		$str_descr = strip_tags( implode( ' ', $ar_str_item_descr ) );

		/* */
		$oResults[$cnt]->browsernav = 0;
		$oResults[$cnt]->section = GW_AREAOFSEARCH;
		
		$oResults[$cnt]->href = $href;
		$oResults[$cnt]->text = $str_descr;
		$oResults[$cnt]->title = strip_tags( $str_item );
		
		#$oResults[$cnt]->title .= ' [href='.htmlspecialchars( $oResults[$cnt]->href ).']';
		#show_date
		$oResults[$cnt]->created = $ar_v['item_cdate'];
		++$cnt;
	}
#print '<pre>'.__FILE__.' '.__LINE__.'<br />';
#print_r( $oResults );
#print '</pre>';
#print '<div>$cnt_records='.$cnt_records.'</div>';
#print '<div>$per_page='.$per_page.'</div>';

	return $oResults;
}


?>
Return current item: Glossword