PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/joomla/plugins/search/gw_search.php

http://glossword.googlecode.com/
PHP | 396 lines | 256 code | 62 blank | 78 comment | 16 complexity | 2101b9d520870f26e476932c18b67cf8 MD5 | raw file
  1. <?php
  2. /**
  3. * @version $Id$
  4. * @package Joomla
  5. * @subpackage Glossword Search
  6. * @copyright Š Dmitry N. Shilnikov, 2002-2010
  7. * @license GNU/GPL, see http://code.google.com/p/glossword/
  8. */
  9. defined( '_JEXEC' ) or die( 'Restricted access' );
  10. $mainframe->registerEvent( 'onSearch', 'plgSearchGlossword' );
  11. $mainframe->registerEvent( 'onSearchAreas', 'plgSearchGlosswordAreas' );
  12. JPlugin::loadLanguage( 'plg_search_glossword' );
  13. define( 'GW_AREAOFSEARCH', JText::_( 'AREAOFSEARCH' ) );
  14. /**
  15. * @return array An array of search areas
  16. */
  17. function &plgSearchGlosswordAreas()
  18. {
  19. static $areas = array(
  20. 'gw_search' => GW_AREAOFSEARCH
  21. );
  22. return $areas;
  23. }
  24. /* */
  25. function search_index__filter_si( &$o, $s )
  26. {
  27. #$s = $this->rm_specials( $s );
  28. $s = search_index__str_normalize( $o, $s );
  29. $s = search_index__text_si( $o, trim( $s ) );
  30. return $s;
  31. }
  32. function search_index__text_si( &$o, $s )
  33. {
  34. /* Do lowercase */
  35. # $s = $o->oCase->lc( $s );
  36. /* Mask non-ASCII characters */
  37. $s = preg_replace( "/([\\xc0-\\xff][\\x80-\\xbf]*)/e", "'U8' . bin2hex( \"$1\" )", $s );
  38. /* Mask MySQL stopwords */
  39. $s = search_index__mask_stopwords( $s );
  40. return $s;
  41. }
  42. /* */
  43. function search_index__str_normalize( &$o, $s )
  44. {
  45. $jo_db =& JFactory::getDBO();
  46. /* Do lowercase */
  47. $s = $o->oCase->lc( $s );
  48. /* Use PECL extension */
  49. if ( class_exists( 'Normalizer' ) )
  50. {
  51. return Normalizer::normalize( $s, Normalizer::FORM_C );
  52. }
  53. /* */
  54. preg_match_all( "/./u", $s, $ar );
  55. $ar = $ar[0];
  56. $ar_c_crc = array();
  57. /* For each character */
  58. foreach ($ar AS $k => &$v )
  59. {
  60. /* Use values as key */
  61. /* PHP-bug: sometimes a string keys becomes interger */
  62. $ar_c_crc[$v] = sprintf( "%u", crc32( $v ) );
  63. }
  64. unset( $v );
  65. if ( empty( $ar_c_crc ) ){ return $s; }
  66. /* */
  67. $query = 'SELECT `str_from`, `str_to`'
  68. . ' FROM '.$o->V->db_name.'.'.$o->V->table_prefix.'unicode_normalization '
  69. . ' WHERE `crc32u` IN ('. implode(',', array_values( $ar_c_crc ) ).')';
  70. $jo_db->setQuery( $query );
  71. $ar_sql = $jo_db->loadAssocList();
  72. if ( is_null( $ar_sql ) ){ $ar_sql = array(); }
  73. /* Normalize text */
  74. foreach ($ar_sql AS $k => &$v )
  75. {
  76. $s = str_replace( urldecode( $v['str_from'] ), urldecode( $v['str_to'] ), $s );
  77. unset( $ar_sql[$k] );
  78. }
  79. unset( $v );
  80. return $s;
  81. }
  82. function search_index__mask_stopwords( $s )
  83. {
  84. return preg_replace( '/\b([a-z]+)/', '_\\1', $s );
  85. }
  86. function search_index__get_crc_u( $s )
  87. {
  88. return sprintf( "%u", crc32( $s ) );
  89. }
  90. /* replacement for urlencode */
  91. function ohtml_urlencode( $s )
  92. {
  93. /* Encode special characters first */
  94. $s = str_replace( array( ',', '/', '+' ), array( '%2C', '%2F', '%2B' ), $s );
  95. $s = urlencode( $s );
  96. /* Restore separators for #area */
  97. $s = str_replace( array( '%01%01', '%02%02' ), array( ',', '.' ), $s );
  98. return $s;
  99. }
  100. /* */
  101. function &plgSearchGlossword( $q, $phrase='', $ordering='', $areas=null )
  102. {
  103. global $mainframe;
  104. $jo_db =& JFactory::getDBO();
  105. #$jo_user =& JFactory::getUser();
  106. #$jo_cfg =& JFactory::getConfig();
  107. $jo_component = &JComponentHelper::getComponent('com_glossword');
  108. $jo_menu = &JSite::getMenu();
  109. $jo_items = $jo_menu->getItems('componentid', $jo_component->id, true);
  110. #require_once( JPATH_SITE.DS.'components'.DS.'com_content'.DS.'helpers'.DS.'route.php' );
  111. if ( is_array( $areas ) )
  112. {
  113. if ( !array_intersect( $areas, array_keys( plgSearchGlosswordAreas() ) ) )
  114. {
  115. return array();
  116. }
  117. }
  118. /* Load plugin parameters */
  119. $jo_plugin =& JPluginHelper::getPlugin( 'search', 'gw_search' );
  120. $pluginParams = new JParameter( $jo_plugin->params );
  121. $offset = JRequest::getVar( 'start', 0, '', 'int' );
  122. $per_page = JRequest::getVar( 'limit', $pluginParams->get( 'int_per_page' ), '', 'int' );
  123. /* Load component configuration */
  124. $query = 'SELECT * FROM #__gw_config';
  125. $jo_db->setQuery( $query );
  126. $ar_sql = $jo_db->loadAssocList();
  127. if ( is_null( $ar_sql ) ){ $ar_sql = array(); }
  128. $ar_cfg = array();
  129. foreach ( $ar_sql as $k => $v )
  130. {
  131. $ar_cfg[$v['setting_key']] = $v['setting_value'];
  132. }
  133. require_once( $ar_cfg['path_core_abs'].'/gw_config.php' );
  134. /* */
  135. $_SERVER['REQUEST_TIME'] = isset( $_SERVER['REQUEST_TIME'] ) ? $_SERVER['REQUEST_TIME'] : mktime();
  136. $o = crc32( $_SERVER['REQUEST_TIME'] );
  137. ${$o} = new site_db_config();
  138. ${$o}->a( 'path_includes', $ar_cfg['path_core_abs'].'/'.${$o}->V->path_includes );
  139. foreach ( $ar_cfg as $setting_key => $setting_value )
  140. {
  141. ${$o}->a( $setting_key, $setting_value );
  142. }
  143. /* */
  144. include_once( ${$o}->V->path_includes.'/class.case.php' );
  145. ${$o}->oCase = new gwv_casemap;
  146. ${$o}->oCase->is_use_mbstring = 1;
  147. $q = preg_replace( "/ {2,}/", ' ', $q );
  148. $q = trim( $q );
  149. $q_si = search_index__filter_si( ${$o}, $q );
  150. /* Need to test. Could not be equal. */
  151. $ar_words_sql = explode( ' ', $q_si );
  152. $ar_words_q = explode( ' ', $q );
  153. /* 11 Apr 2008: Enable search with asterisk for Chinese, Japanese and Korean characters */
  154. foreach ( $ar_words_q as $k => $word )
  155. {
  156. if ( preg_match( '/[\x{3040}-\x{312F}|\x{3400}-\x{9FFF}|\x{AC00}-\x{D7AF}]/u', $word, $ar_matches ) )
  157. {
  158. $ar_words_sql[$k] .= '*';
  159. }
  160. }
  161. /* Switch search modes */
  162. switch ( $phrase )
  163. {
  164. case 'any':
  165. $sql_against = implode( ' ', $ar_words_sql );
  166. break;
  167. case 'exact':
  168. $sql_against = '"'.implode( ' ', $ar_words_sql ).'"';
  169. break;
  170. default:
  171. $sql_against = '+'.implode( ' +', $ar_words_sql );
  172. break;
  173. }
  174. /**
  175. * ----------------------------------------------
  176. * Count Item IDs
  177. * ----------------------------------------------
  178. */
  179. $query = 'SELECT csi.id_item ';
  180. #$query .= "\n".', MATCH(csi.contents_si) AGAINST(\''. $jo_db->getEscaped( $sql_against ).'\' IN BOOLEAN MODE) score ';
  181. $query .= "\n".' FROM '.${$o}->V->db_name.'.'.${$o}->V->table_prefix.'contents_si csi';
  182. $query .= "\n".' WHERE ';
  183. $query .= "\n".' MATCH(csi.contents_si) AGAINST(\''.$jo_db->getEscaped( $sql_against ).'\' IN BOOLEAN MODE)';
  184. $query .= "\n".' GROUP BY csi.id_item ';
  185. $jo_db->setQuery( $query, 0, $pluginParams->get( 'int_search_max' ) );
  186. $jo_db->query();
  187. $cnt_records = $jo_db->getNumRows();
  188. /**
  189. * ----------------------------------------------
  190. * Select Item IDs
  191. * ----------------------------------------------
  192. */
  193. $jo_db->setQuery( 'SET SQL_BIG_SELECTS=1' );
  194. $jo_db->query();
  195. $query = 'SELECT csi.id_item';
  196. /* 1.9.3: Custom alphabetic order */
  197. $ar_join = array();
  198. for ( $i = 1; $i <= 8; $i++ )
  199. {
  200. $query .= ', az'.$i.'.int_sort';
  201. $ar_join[$i] = "\n".'LEFT JOIN '.${$o}->V->db_name.'.'.${$o}->V->table_prefix.'az_letters az'.$i.' ON ';
  202. $ar_join[$i] .= 'az'.$i.'.uc_crc32u = c.contents_'.$i.' AND c.id_lang = az'.$i.'.id_lang';
  203. }
  204. $query .= "\n".' FROM '.${$o}->V->db_name.'.'.${$o}->V->table_prefix.'contents_si csi, ';
  205. $query .= ${$o}->V->db_name.'.'.${$o}->V->table_prefix.'items i, '.${$o}->V->db_name.'.'.${$o}->V->table_prefix.'contents c';
  206. $query .= implode( ' ', $ar_join );
  207. $query .= "\n".' WHERE ';
  208. $query .= "\n".' MATCH(csi.contents_si) AGAINST(\''.$jo_db->getEscaped( $sql_against ).'\' IN BOOLEAN MODE)';
  209. $query .= ' AND i.id_item = c.id_item ';
  210. $query .= ' AND i.id_item = csi.id_item ';
  211. $query .= "\n".' GROUP BY csi.id_item ';
  212. $ar_order = array( 'c.id_lang' );
  213. /* Switch sorting modes */
  214. switch ( $ordering )
  215. {
  216. case 'newest':
  217. $ar_order[] = 'i.item_cdate DESC';
  218. break;
  219. case 'oldest':
  220. $ar_order[] = 'i.item_cdate ASC';
  221. break;
  222. case 'popular':
  223. $ar_order[] = 'i.cnt_hits DESC';
  224. break;
  225. default:
  226. break;
  227. }
  228. /* 1.9.3: Custom alphabetic order */
  229. for ( $i = 1; $i <= 8; $i++ )
  230. {
  231. $ar_order[] = 'az'.$i.'.int_sort, c.contents_so';
  232. }
  233. $query .= "\n".'ORDER BY '.implode( ', ', $ar_order );
  234. #$query .= ' HAVING score > 0 ';
  235. #$query .= ' ORDER BY score DESC ';
  236. /* Can't use pagination for requests because of Joomla */
  237. /* Using `int_search_max` instead */
  238. /* @todo: workaround */
  239. #$jo_db->setQuery( $query, $offset, $per_page );
  240. $jo_db->setQuery( $query, 0, $pluginParams->get( 'int_search_max' ) );
  241. $ar_sql = $jo_db->loadAssocList();
  242. if ( is_null( $ar_sql ) ){ $ar_sql = array(); }
  243. $ar_item_ids = array();
  244. foreach ( $ar_sql as $ar_v )
  245. {
  246. $ar_item_ids[] = $ar_v['id_item'];
  247. }
  248. if ( empty( $ar_item_ids ) )
  249. {
  250. return array();
  251. }
  252. /**
  253. * ----------------------------------------------
  254. * Select Items
  255. * ----------------------------------------------
  256. */
  257. $ar_sql_items = array();
  258. if ( !empty( $ar_item_ids ) )
  259. {
  260. $query = 'SELECT uri.item_uri, i.id_item, i.item_id_user_created, i.item_cdate, c.contents_value_cached, c.id_field ';
  261. $query .= "\n".' FROM '.${$o}->V->db_name.'.'.${$o}->V->table_prefix.'items i, ';
  262. $query .= ${$o}->V->db_name.'.'.${$o}->V->table_prefix.'contents c, ';
  263. $query .= ${$o}->V->db_name.'.'.${$o}->V->table_prefix.'items_uri uri, ';
  264. $query .= ${$o}->V->db_name.'.'.${$o}->V->table_prefix.'map_field_to_fieldset mftf ';
  265. $query .= "\n".' WHERE i.id_item = c.id_item ';
  266. $query .= ' AND i.id_item = uri.id_item ';
  267. $query .= ' AND mftf.id_field = c.id_field ';
  268. $query .= ' AND mftf.id_fieldset = \'1\' ';
  269. $query .= ' AND i.id_item IN ('. implode( ', ', $ar_item_ids ).') ';
  270. $ar_order_by = array();
  271. foreach ( $ar_item_ids as $id_item_in )
  272. {
  273. $ar_order_by[] = 'i.id_item = "'.$id_item_in.'" DESC';
  274. }
  275. $ar_order_by[] = 'mftf.int_sort ASC';
  276. $query .= "\n".' ORDER BY '. implode( ', ', $ar_order_by );
  277. $jo_db->setQuery( $query );
  278. $ar_sql_items = $jo_db->loadAssocList();
  279. if ( is_null( $ar_sql_items ) ){ $ar_sql_items = array(); }
  280. }
  281. /* Re-arrange */
  282. $ar_items = array();
  283. foreach ( $ar_sql_items as $k => $ar_v)
  284. {
  285. $ar_items[$ar_v['id_item']][$ar_v['id_field']] = $ar_v;
  286. unset( $ar_sql_items[$k] );
  287. }
  288. /* */
  289. $cnt = 0;
  290. $oResults[0] = (object) 'results';
  291. foreach ( $ar_items as $id_item => $ar_fields_content)
  292. {
  293. $ar_str_item_title = array();
  294. $ar_str_item_descr = array();
  295. foreach ( $ar_fields_content as $id_field => $ar_v)
  296. {
  297. switch ( $id_field )
  298. {
  299. case 1:
  300. $ar_str_item_title[] = $ar_v['contents_value_cached'];
  301. break;
  302. /* More to come */
  303. default:
  304. $ar_str_item_descr[] = $ar_v['contents_value_cached'];
  305. break;
  306. }
  307. }
  308. /* Item title */
  309. $str_item = implode( ' ', $ar_str_item_title );
  310. /* Hyperlink to item */
  311. $href_area = 'a.search,q.'. ohtml_urlencode( $str_item ). ',t.items';
  312. $href_area = urlencode( $href_area );
  313. $href = JRoute::_( 'index.php?option='.$jo_component->option.'&Itemid='.$jo_items->id.'&arg[area]='.$href_area.'&view=default' );
  314. /* Item description */
  315. $str_descr = strip_tags( implode( ' ', $ar_str_item_descr ) );
  316. /* */
  317. $oResults[$cnt]->browsernav = 0;
  318. $oResults[$cnt]->section = GW_AREAOFSEARCH;
  319. $oResults[$cnt]->href = $href;
  320. $oResults[$cnt]->text = $str_descr;
  321. $oResults[$cnt]->title = strip_tags( $str_item );
  322. #$oResults[$cnt]->title .= ' [href='.htmlspecialchars( $oResults[$cnt]->href ).']';
  323. #show_date
  324. $oResults[$cnt]->created = $ar_v['item_cdate'];
  325. ++$cnt;
  326. }
  327. #print '<pre>'.__FILE__.' '.__LINE__.'<br />';
  328. #print_r( $oResults );
  329. #print '</pre>';
  330. #print '<div>$cnt_records='.$cnt_records.'</div>';
  331. #print '<div>$per_page='.$per_page.'</div>';
  332. return $oResults;
  333. }
  334. ?>