~azzar1/unity/add-show-desktop-key

443 by dcoles
Added Forum application along with unmodifed version of phpBB3 "Olympus" 3.0.0
1
<?php
2
/**
3
*
4
* @package search
5
* @version $Id: fulltext_mysql.php,v 1.51 2007/11/29 18:26:20 davidmj Exp $
6
* @copyright (c) 2005 phpBB Group
7
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
8
*
9
*/
10
11
/**
12
* @ignore
13
*/
14
if (!defined('IN_PHPBB'))
15
{
16
	exit;
17
}
18
19
/**
20
* @ignore
21
*/
22
include_once($phpbb_root_path . 'includes/search/search.' . $phpEx);
23
24
/**
25
* fulltext_mysql
26
* Fulltext search for MySQL
27
* @package search
28
*/
29
class fulltext_mysql extends search_backend
30
{
31
	var $stats = array();
32
	var $word_length = array();
33
	var $split_words = array();
34
	var $search_query;
35
	var $common_words = array();
36
	var $pcre_properties = false;
37
	var $mbstring_regex = false;
38
39
	function fulltext_mysql(&$error)
40
	{
41
		global $config;
42
43
		$this->word_length = array('min' => $config['fulltext_mysql_min_word_len'], 'max' => $config['fulltext_mysql_max_word_len']);
44
45
		if (version_compare(PHP_VERSION, '5.1.0', '>=') || (version_compare(PHP_VERSION, '5.0.0-dev', '<=') && version_compare(PHP_VERSION, '4.4.0', '>=')))
46
		{
47
			// While this is the proper range of PHP versions, PHP may not be linked with the bundled PCRE lib and instead with an older version
48
			if (@preg_match('/\p{L}/u', 'a') !== false)
49
			{
50
				$this->pcre_properties = true;
51
			}
52
		}
53
54
		if (function_exists('mb_ereg'))
55
		{
56
			$this->mbstring_regex = true;
57
			mb_regex_encoding('UTF-8');
58
		}
59
60
		$error = false;
61
	}
62
63
	/**
64
	* Checks for correct MySQL version and stores min/max word length in the config
65
	*/
66
	function init()
67
	{
68
		global $db, $user;
69
70
		if ($db->sql_layer != 'mysql4' && $db->sql_layer != 'mysqli')
71
		{
72
			return $user->lang['FULLTEXT_MYSQL_INCOMPATIBLE_VERSION'];
73
		}
74
75
		$result = $db->sql_query('SHOW TABLE STATUS LIKE \'' . POSTS_TABLE . '\'');
76
		$info = $db->sql_fetchrow($result);
77
		$db->sql_freeresult($result);
78
79
		$engine = '';
80
		if (isset($info['Engine']))
81
		{
82
			$engine = $info['Engine'];
83
		}
84
		else if (isset($info['Type']))
85
		{
86
			$engine = $info['Type'];
87
		}
88
89
		if ($engine != 'MyISAM')
90
		{
91
			return $user->lang['FULLTEXT_MYSQL_NOT_MYISAM'];
92
		}
93
94
		$sql = 'SHOW VARIABLES
95
			LIKE \'ft\_%\'';
96
		$result = $db->sql_query($sql);
97
98
		$mysql_info = array();
99
		while ($row = $db->sql_fetchrow($result))
100
		{
101
			$mysql_info[$row['Variable_name']] = $row['Value'];
102
		}
103
		$db->sql_freeresult($result);
104
105
		set_config('fulltext_mysql_max_word_len', $mysql_info['ft_max_word_len']);
106
		set_config('fulltext_mysql_min_word_len', $mysql_info['ft_min_word_len']);
107
108
		return false;
109
	}
110
111
	/**
112
	* Splits keywords entered by a user into an array of words stored in $this->split_words
113
	* Stores the tidied search query in $this->search_query
114
	*
115
	* @param string &$keywords Contains the keyword as entered by the user
116
	* @param string $terms is either 'all' or 'any'
117
	* @return bool false if no valid keywords were found and otherwise true
118
	*/
119
	function split_keywords(&$keywords, $terms)
120
	{
121
		global $config;
122
123
		if ($terms == 'all')
124
		{
125
			$match		= array('#\sand\s#iu', '#\sor\s#iu', '#\snot\s#iu', '#\+#', '#-#', '#\|#');
126
			$replace	= array(' +', ' |', ' -', ' +', ' -', ' |');
127
128
			$keywords = preg_replace($match, $replace, $keywords);
129
		}
130
131
		// Filter out as above
132
		$split_keywords = preg_replace("#[\n\r\t]+#", ' ', trim(htmlspecialchars_decode($keywords)));
133
134
		// Split words
135
		if ($this->pcre_properties)
136
		{
137
			$split_keywords = preg_replace('#([^\p{L}\p{N}\'*"()])#u', '$1$1', str_replace('\'\'', '\' \'', trim($split_keywords)));
138
		}
139
		else if ($this->mbstring_regex)
140
		{
141
			$split_keywords = mb_ereg_replace('([^\w\'*"()])', '\\1\\1', str_replace('\'\'', '\' \'', trim($split_keywords)));
142
		}
143
		else
144
		{
145
			$split_keywords = preg_replace('#([^\w\'*"()])#u', '$1$1', str_replace('\'\'', '\' \'', trim($split_keywords)));
146
		}
147
148
		if ($this->pcre_properties)
149
		{
150
			$matches = array();
151
			preg_match_all('#(?:[^\p{L}\p{N}*"()]|^)([+\-|]?(?:[\p{L}\p{N}*"()]+\'?)*[\p{L}\p{N}*"()])(?:[^\p{L}\p{N}*"()]|$)#u', $split_keywords, $matches);
152
			$this->split_words = $matches[1];
153
		}
154
		else if ($this->mbstring_regex)
155
		{
156
			mb_ereg_search_init($split_keywords, '(?:[^\w*"()]|^)([+\-|]?(?:[\w*"()]+\'?)*[\w*"()])(?:[^\w*"()]|$)');
157
158
			while (($word = mb_ereg_search_regs()))
159
			{
160
				$this->split_words[] = $word[1];
161
			}
162
		}
163
		else
164
		{
165
			$matches = array();
166
			preg_match_all('#(?:[^\w*"()]|^)([+\-|]?(?:[\w*"()]+\'?)*[\w*"()])(?:[^\w*"()]|$)#u', $split_keywords, $matches);
167
			$this->split_words = $matches[1];
168
		}
169
170
		// to allow phrase search, we need to concatenate quoted words
171
		$tmp_split_words = array();
172
		$phrase = '';
173
		foreach ($this->split_words as $word)
174
		{
175
			if ($phrase)
176
			{
177
				$phrase .= ' ' . $word;
178
				if (strpos($word, '"') !== false && substr_count($word, '"') % 2 == 1)
179
				{
180
					$tmp_split_words[] = $phrase;
181
					$phrase = '';
182
				}
183
			}
184
			else if (strpos($word, '"') !== false && substr_count($word, '"') % 2 == 1)
185
			{
186
				$phrase = $word;
187
			}
188
			else
189
			{
190
				$tmp_split_words[] = $word . ' ';
191
			}
192
		}
193
		if ($phrase)
194
		{
195
			$tmp_split_words[] = $phrase;
196
		}
197
198
		$this->split_words = $tmp_split_words;
199
200
		unset($tmp_split_words);
201
		unset($phrase);
202
203
		foreach ($this->split_words as $i => $word)
204
		{
205
			$clean_word = preg_replace('#^[+\-|"]#', '', $word);
206
207
			// check word length
208
			$clean_len = utf8_strlen(str_replace('*', '', $clean_word));
209
			if (($clean_len < $config['fulltext_mysql_min_word_len']) || ($clean_len > $config['fulltext_mysql_max_word_len']))
210
			{
211
				$this->common_words[] = $word;
212
				unset($this->split_words[$i]);
213
			}
214
		}
215
216
		if ($terms == 'any')
217
		{
218
			$this->search_query = '';
219
			foreach ($this->split_words as $word)
220
			{
221
				if ((strpos($word, '+') === 0) || (strpos($word, '-') === 0) || (strpos($word, '|') === 0))
222
				{
223
					$word = substr($word, 1);
224
				}
225
				$this->search_query .= $word . ' ';
226
			}
227
		}
228
		else
229
		{
230
			$this->search_query = '';
231
			foreach ($this->split_words as $word)
232
			{
233
				if ((strpos($word, '+') === 0) || (strpos($word, '-') === 0))
234
				{
235
					$this->search_query .= $word . ' ';
236
				}
237
				else if (strpos($word, '|') === 0)
238
				{
239
					$this->search_query .= substr($word, 1) . ' ';
240
				}
241
				else
242
				{
243
					$this->search_query .= '+' . $word . ' ';
244
				}
245
			}
246
		}
247
248
		$this->search_query = utf8_htmlspecialchars($this->search_query);
249
250
		if ($this->search_query)
251
		{
252
			$this->split_words = array_values($this->split_words);
253
			sort($this->split_words);
254
			return true;
255
		}
256
		return false;
257
	}
258
259
	/**
260
	* Turns text into an array of words
261
	*/
262
	function split_message($text)
263
	{
264
		global $config;
265
266
		// Split words
267
		if ($this->pcre_properties)
268
		{
269
			$text = preg_replace('#([^\p{L}\p{N}\'*])#u', '$1$1', str_replace('\'\'', '\' \'', trim($text)));
270
		}
271
		else if ($this->mbstring_regex)
272
		{
273
			$text = mb_ereg_replace('([^\w\'*])', '\\1\\1', str_replace('\'\'', '\' \'', trim($text)));
274
		}
275
		else
276
		{
277
			$text = preg_replace('#([^\w\'*])#u', '$1$1', str_replace('\'\'', '\' \'', trim($text)));
278
		}
279
280
		if ($this->pcre_properties)
281
		{
282
			$matches = array();
283
			preg_match_all('#(?:[^\p{L}\p{N}*]|^)([+\-|]?(?:[\p{L}\p{N}*]+\'?)*[\p{L}\p{N}*])(?:[^\p{L}\p{N}*]|$)#u', $text, $matches);
284
			$text = $matches[1];
285
		}
286
		else if ($this->mbstring_regex)
287
		{
288
			mb_ereg_search_init($text, '(?:[^\w*]|^)([+\-|]?(?:[\w*]+\'?)*[\w*])(?:[^\w*]|$)');
289
290
			$text = array();
291
			while (($word = mb_ereg_search_regs()))
292
			{
293
				$text[] = $word[1];
294
			}
295
		}
296
		else
297
		{
298
			$matches = array();
299
			preg_match_all('#(?:[^\w*]|^)([+\-|]?(?:[\w*]+\'?)*[\w*])(?:[^\w*]|$)#u', $text, $matches);
300
			$text = $matches[1];
301
		}
302
303
		// remove too short or too long words
304
		$text = array_values($text);
305
		for ($i = 0, $n = sizeof($text); $i < $n; $i++)
306
		{
307
			$text[$i] = trim($text[$i]);
308
			if (utf8_strlen($text[$i]) < $config['fulltext_mysql_min_word_len'] || utf8_strlen($text[$i]) > $config['fulltext_mysql_max_word_len'])
309
			{
310
				unset($text[$i]);
311
			}
312
		}
313
314
		return array_values($text);
315
	}
316
317
	/**
318
	* Performs a search on keywords depending on display specific params. You have to run split_keywords() first.
319
	*
320
	* @param	string		$type				contains either posts or topics depending on what should be searched for
321
	* @param	string		&$fields			contains either titleonly (topic titles should be searched), msgonly (only message bodies should be searched), firstpost (only subject and body of the first post should be searched) or all (all post bodies and subjects should be searched)
322
	* @param	string		&$terms				is either 'all' (use query as entered, words without prefix should default to "have to be in field") or 'any' (ignore search query parts and just return all posts that contain any of the specified words)
323
	* @param	array		&$sort_by_sql		contains SQL code for the ORDER BY part of a query
324
	* @param	string		&$sort_key			is the key of $sort_by_sql for the selected sorting
325
	* @param	string		&$sort_dir			is either a or d representing ASC and DESC
326
	* @param	string		&$sort_days			specifies the maximum amount of days a post may be old
327
	* @param	array		&$ex_fid_ary		specifies an array of forum ids which should not be searched
328
	* @param	array		&$m_approve_fid_ary	specifies an array of forum ids in which the searcher is allowed to view unapproved posts
329
	* @param	int			&$topic_id			is set to 0 or a topic id, if it is not 0 then only posts in this topic should be searched
330
	* @param	array		&$author_ary		an array of author ids if the author should be ignored during the search the array is empty
331
	* @param	array		&$id_ary			passed by reference, to be filled with ids for the page specified by $start and $per_page, should be ordered
332
	* @param	int			$start				indicates the first index of the page
333
	* @param	int			$per_page			number of ids each page is supposed to contain
334
	* @return	boolean|int						total number of results
335
	*
336
	* @access	public
337
	*/
338
	function keyword_search($type, &$fields, &$terms, &$sort_by_sql, &$sort_key, &$sort_dir, &$sort_days, &$ex_fid_ary, &$m_approve_fid_ary, &$topic_id, &$author_ary, &$id_ary, $start, $per_page)
339
	{
340
		global $config, $db;
341
342
		// No keywords? No posts.
343
		if (!$this->search_query)
344
		{
345
			return false;
346
		}
347
348
		// generate a search_key from all the options to identify the results
349
		$search_key = md5(implode('#', array(
350
			implode(', ', $this->split_words),
351
			$type,
352
			$fields,
353
			$terms,
354
			$sort_days,
355
			$sort_key,
356
			$topic_id,
357
			implode(',', $ex_fid_ary),
358
			implode(',', $m_approve_fid_ary),
359
			implode(',', $author_ary)
360
		)));
361
362
		// try reading the results from cache
363
		$result_count = 0;
364
		if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE)
365
		{
366
			return $result_count;
367
		}
368
369
		$id_ary = array();
370
371
		$join_topic = ($type == 'posts') ? false : true;
372
373
		// Build sql strings for sorting
374
		$sql_sort = $sort_by_sql[$sort_key] . (($sort_dir == 'a') ? ' ASC' : ' DESC');
375
		$sql_sort_table = $sql_sort_join = '';
376
377
		switch ($sql_sort[0])
378
		{
379
			case 'u':
380
				$sql_sort_table	= USERS_TABLE . ' u, ';
381
				$sql_sort_join	= ($type == 'posts') ? ' AND u.user_id = p.poster_id ' : ' AND u.user_id = t.topic_poster ';
382
			break;
383
384
			case 't':
385
				$join_topic = true;
386
			break;
387
388
			case 'f':
389
				$sql_sort_table	= FORUMS_TABLE . ' f, ';
390
				$sql_sort_join	= ' AND f.forum_id = p.forum_id ';
391
			break;
392
		}
393
394
		// Build some display specific sql strings
395
		switch ($fields)
396
		{
397
			case 'titleonly':
398
				$sql_match = 'p.post_subject';
399
				$sql_match_where = ' AND p.post_id = t.topic_first_post_id';
400
				$join_topic = true;
401
			break;
402
403
			case 'msgonly':
404
				$sql_match = 'p.post_text';
405
				$sql_match_where = '';
406
			break;
407
408
			case 'firstpost':
409
				$sql_match = 'p.post_subject, p.post_text';
410
				$sql_match_where = ' AND p.post_id = t.topic_first_post_id';
411
				$join_topic = true;
412
			break;
413
414
			default:
415
				$sql_match = 'p.post_subject, p.post_text';
416
				$sql_match_where = '';
417
			break;
418
		}
419
420
		if (!sizeof($m_approve_fid_ary))
421
		{
422
			$m_approve_fid_sql = ' AND p.post_approved = 1';
423
		}
424
		else if ($m_approve_fid_ary === array(-1))
425
		{
426
			$m_approve_fid_sql = '';
427
		}
428
		else
429
		{
430
			$m_approve_fid_sql = ' AND (p.post_approved = 1 OR ' . $db->sql_in_set('p.forum_id', $m_approve_fid_ary, true) . ')';
431
		}
432
433
		$sql_select			= (!$result_count) ? 'SQL_CALC_FOUND_ROWS ' : '';
434
		$sql_select			= ($type == 'posts') ? $sql_select . 'p.post_id' : 'DISTINCT ' . $sql_select . 't.topic_id';
435
		$sql_from			= ($join_topic) ? TOPICS_TABLE . ' t, ' : '';
436
		$field				= ($type == 'posts') ? 'post_id' : 'topic_id';
437
		$sql_author			= (sizeof($author_ary) == 1) ? ' = ' . $author_ary[0] : 'IN (' . implode(', ', $author_ary) . ')';
438
439
		$sql_where_options = $sql_sort_join;
440
		$sql_where_options .= ($topic_id) ? ' AND p.topic_id = ' . $topic_id : '';
441
		$sql_where_options .= ($join_topic) ? ' AND t.topic_id = p.topic_id' : '';
442
		$sql_where_options .= (sizeof($ex_fid_ary)) ? ' AND ' . $db->sql_in_set('p.forum_id', $ex_fid_ary, true) : '';
443
		$sql_where_options .= $m_approve_fid_sql;
444
		$sql_where_options .= (sizeof($author_ary)) ? ' AND p.poster_id ' . $sql_author : '';
445
		$sql_where_options .= ($sort_days) ? ' AND p.post_time >= ' . (time() - ($sort_days * 86400)) : '';
446
		$sql_where_options .= $sql_match_where;
447
448
		$sql = "SELECT $sql_select
449
			FROM $sql_from$sql_sort_table" . POSTS_TABLE . " p
450
			WHERE MATCH ($sql_match) AGAINST ('" . $db->sql_escape(htmlspecialchars_decode($this->search_query)) . "' IN BOOLEAN MODE)
451
				$sql_where_options
452
			ORDER BY $sql_sort";
453
		$result = $db->sql_query_limit($sql, $config['search_block_size'], $start);
454
455
		while ($row = $db->sql_fetchrow($result))
456
		{
457
			$id_ary[] = $row[$field];
458
		}
459
		$db->sql_freeresult($result);
460
461
		$id_ary = array_unique($id_ary);
462
463
		if (!sizeof($id_ary))
464
		{
465
			return false;
466
		}
467
468
		// if the total result count is not cached yet, retrieve it from the db
469
		if (!$result_count)
470
		{
471
			$sql = 'SELECT FOUND_ROWS() as result_count';
472
			$result = $db->sql_query($sql);
473
			$result_count = (int) $db->sql_fetchfield('result_count');
474
			$db->sql_freeresult($result);
475
476
			if (!$result_count)
477
			{
478
				return false;
479
			}
480
		}
481
482
		// store the ids, from start on then delete anything that isn't on the current page because we only need ids for one page
483
		$this->save_ids($search_key, implode(' ', $this->split_words), $author_ary, $result_count, $id_ary, $start, $sort_dir);
484
		$id_ary = array_slice($id_ary, 0, (int) $per_page);
485
486
		return $result_count;
487
	}
488
489
	/**
490
	* Performs a search on an author's posts without caring about message contents. Depends on display specific params
491
	*
492
	* @param array &$id_ary passed by reference, to be filled with ids for the page specified by $start and $per_page, should be ordered
493
	* @param int $start indicates the first index of the page
494
	* @param int $per_page number of ids each page is supposed to contain
495
	* @return total number of results
496
	*/
497
	function author_search($type, $firstpost_only, &$sort_by_sql, &$sort_key, &$sort_dir, &$sort_days, &$ex_fid_ary, &$m_approve_fid_ary, &$topic_id, &$author_ary, &$id_ary, $start, $per_page)
498
	{
499
		global $config, $db;
500
501
		// No author? No posts.
502
		if (!sizeof($author_ary))
503
		{
504
			return 0;
505
		}
506
507
		// generate a search_key from all the options to identify the results
508
		$search_key = md5(implode('#', array(
509
			'',
510
			$type,
511
			($firstpost_only) ? 'firstpost' : '',
512
			'',
513
			'',
514
			$sort_days,
515
			$sort_key,
516
			$topic_id,
517
			implode(',', $ex_fid_ary),
518
			implode(',', $m_approve_fid_ary),
519
			implode(',', $author_ary)
520
		)));
521
522
		// try reading the results from cache
523
		$result_count = 0;
524
		if ($this->obtain_ids($search_key, $result_count, $id_ary, $start, $per_page, $sort_dir) == SEARCH_RESULT_IN_CACHE)
525
		{
526
			return $result_count;
527
		}
528
529
		$id_ary = array();
530
531
		// Create some display specific sql strings
532
		$sql_author		= $db->sql_in_set('p.poster_id', $author_ary);
533
		$sql_fora		= (sizeof($ex_fid_ary)) ? ' AND ' . $db->sql_in_set('p.forum_id', $ex_fid_ary, true) : '';
534
		$sql_topic_id	= ($topic_id) ? ' AND p.topic_id = ' . (int) $topic_id : '';
535
		$sql_time		= ($sort_days) ? ' AND p.post_time >= ' . (time() - ($sort_days * 86400)) : '';
536
		$sql_firstpost = ($firstpost_only) ? ' AND p.post_id = t.topic_first_post_id' : '';
537
538
		// Build sql strings for sorting
539
		$sql_sort = $sort_by_sql[$sort_key] . (($sort_dir == 'a') ? ' ASC' : ' DESC');
540
		$sql_sort_table = $sql_sort_join = '';
541
		switch ($sql_sort[0])
542
		{
543
			case 'u':
544
				$sql_sort_table	= USERS_TABLE . ' u, ';
545
				$sql_sort_join	= ($type == 'posts') ? ' AND u.user_id = p.poster_id ' : ' AND u.user_id = t.topic_poster ';
546
			break;
547
548
			case 't':
549
				$sql_sort_table	= ($type == 'posts') ? TOPICS_TABLE . ' t, ' : '';
550
				$sql_sort_join	= ($type == 'posts') ? ' AND t.topic_id = p.topic_id ' : '';
551
			break;
552
553
			case 'f':
554
				$sql_sort_table	= FORUMS_TABLE . ' f, ';
555
				$sql_sort_join	= ' AND f.forum_id = p.forum_id ';
556
			break;
557
		}
558
559
		if (!sizeof($m_approve_fid_ary))
560
		{
561
			$m_approve_fid_sql = ' AND p.post_approved = 1';
562
		}
563
		else if ($m_approve_fid_ary == array(-1))
564
		{
565
			$m_approve_fid_sql = '';
566
		}
567
		else
568
		{
569
			$m_approve_fid_sql = ' AND (p.post_approved = 1 OR ' . $db->sql_in_set('p.forum_id', $m_approve_fid_ary, true) . ')';
570
		}
571
572
		// If the cache was completely empty count the results
573
		$calc_results = ($result_count) ? '' : 'SQL_CALC_FOUND_ROWS ';
574
575
		// Build the query for really selecting the post_ids
576
		if ($type == 'posts')
577
		{
578
			$sql = "SELECT {$calc_results}p.post_id
579
				FROM " . $sql_sort_table . POSTS_TABLE . ' p' . (($firstpost_only) ? ', ' . TOPICS_TABLE . ' t ' : ' ') . "
580
				WHERE $sql_author
581
					$sql_topic_id
582
					$sql_firstpost
583
					$m_approve_fid_sql
584
					$sql_fora
585
					$sql_sort_join
586
					$sql_time
587
				ORDER BY $sql_sort";
588
			$field = 'post_id';
589
		}
590
		else
591
		{
592
			$sql = "SELECT {$calc_results}t.topic_id
593
				FROM " . $sql_sort_table . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p
594
				WHERE $sql_author
595
					$sql_topic_id
596
					$sql_firstpost
597
					$m_approve_fid_sql
598
					$sql_fora
599
					AND t.topic_id = p.topic_id
600
					$sql_sort_join
601
					$sql_time
602
				GROUP BY t.topic_id
603
				ORDER BY $sql_sort";
604
			$field = 'topic_id';
605
		}
606
607
		// Only read one block of posts from the db and then cache it
608
		$result = $db->sql_query_limit($sql, $config['search_block_size'], $start);
609
610
		while ($row = $db->sql_fetchrow($result))
611
		{
612
			$id_ary[] = $row[$field];
613
		}
614
		$db->sql_freeresult($result);
615
616
		// retrieve the total result count if needed
617
		if (!$result_count)
618
		{
619
			$sql = 'SELECT FOUND_ROWS() as result_count';
620
			$result = $db->sql_query($sql);
621
			$result_count = (int) $db->sql_fetchfield('result_count');
622
			$db->sql_freeresult($result);
623
624
			if (!$result_count)
625
			{
626
				return false;
627
			}
628
		}
629
630
		if (sizeof($id_ary))
631
		{
632
			$this->save_ids($search_key, '', $author_ary, $result_count, $id_ary, $start, $sort_dir);
633
			$id_ary = array_slice($id_ary, 0, $per_page);
634
635
			return $result_count;
636
		}
637
		return false;
638
	}
639
640
	/**
641
	* Destroys cached search results, that contained one of the new words in a post so the results won't be outdated.
642
	*
643
	* @param string $mode contains the post mode: edit, post, reply, quote ...
644
	*/
645
	function index($mode, $post_id, &$message, &$subject, $poster_id, $forum_id)
646
	{
647
		global $db;
648
649
		// Split old and new post/subject to obtain array of words
650
		$split_text = $this->split_message($message);
651
		$split_title = ($subject) ? $this->split_message($subject) : array();
652
653
		$words = array_unique(array_merge($split_text, $split_title));
654
655
		unset($split_text);
656
		unset($split_title);
657
658
		// destroy cached search results containing any of the words removed or added
659
		$this->destroy_cache($words, array($poster_id));
660
661
		unset($words);
662
	}
663
664
	/**
665
	* Destroy cached results, that might be outdated after deleting a post
666
	*/
667
	function index_remove($post_ids, $author_ids, $forum_ids)
668
	{
669
		$this->destroy_cache(array(), $author_ids);
670
	}
671
672
	/**
673
	* Destroy old cache entries
674
	*/
675
	function tidy()
676
	{
677
		global $db, $config;
678
679
		// destroy too old cached search results
680
		$this->destroy_cache(array());
681
682
		set_config('search_last_gc', time(), true);
683
	}
684
685
	/**
686
	* Create fulltext index
687
	*/
688
	function create_index($acp_module, $u_action)
689
	{
690
		global $db;
691
692
		// Make sure we can actually use MySQL with fulltext indexes
693
		if ($error = $this->init())
694
		{
695
			return $error;
696
		}
697
698
		if (empty($this->stats))
699
		{
700
			$this->get_stats();
701
		}
702
703
		$alter = array();
704
705
		if (!isset($this->stats['post_subject']))
706
		{
707
			if ($db->sql_layer == 'mysqli' || version_compare($db->mysql_version, '4.1.3', '>='))
708
			{
709
				//$alter[] = 'MODIFY post_subject varchar(100) COLLATE utf8_unicode_ci DEFAULT \'\' NOT NULL';
710
			}
711
			else
712
			{
713
				$alter[] = 'MODIFY post_subject text NOT NULL';
714
			}
715
			$alter[] = 'ADD FULLTEXT (post_subject)';
716
		}
717
718
		if (!isset($this->stats['post_text']))
719
		{
720
			if ($db->sql_layer == 'mysqli' || version_compare($db->mysql_version, '4.1.3', '>='))
721
			{
722
				$alter[] = 'MODIFY post_text mediumtext COLLATE utf8_unicode_ci NOT NULL';
723
			}
724
			else
725
			{
726
				$alter[] = 'MODIFY post_text mediumtext NOT NULL';
727
			}
728
			$alter[] = 'ADD FULLTEXT (post_text)';
729
		}
730
731
		if (!isset($this->stats['post_content']))
732
		{
733
			$alter[] = 'ADD FULLTEXT post_content (post_subject, post_text)';
734
		}
735
736
		if (sizeof($alter))
737
		{
738
			$db->sql_query('ALTER TABLE ' . POSTS_TABLE . ' ' . implode(', ', $alter));
739
		}
740
741
		$db->sql_query('TRUNCATE TABLE ' . SEARCH_RESULTS_TABLE);
742
743
		return false;
744
	}
745
746
	/**
747
	* Drop fulltext index
748
	*/
749
	function delete_index($acp_module, $u_action)
750
	{
751
		global $db;
752
753
		// Make sure we can actually use MySQL with fulltext indexes
754
		if ($error = $this->init())
755
		{
756
			return $error;
757
		}
758
759
		if (empty($this->stats))
760
		{
761
			$this->get_stats();
762
		}
763
764
		$alter = array();
765
766
		if (isset($this->stats['post_subject']))
767
		{
768
			$alter[] = 'DROP INDEX post_subject';
769
		}
770
771
		if (isset($this->stats['post_text']))
772
		{
773
			$alter[] = 'DROP INDEX post_text';
774
		}
775
776
		if (isset($this->stats['post_content']))
777
		{
778
			$alter[] = 'DROP INDEX post_content';
779
		}
780
781
		if (sizeof($alter))
782
		{
783
			$db->sql_query('ALTER TABLE ' . POSTS_TABLE . ' ' . implode(', ', $alter));
784
		}
785
786
		$db->sql_query('TRUNCATE TABLE ' . SEARCH_RESULTS_TABLE);
787
788
		return false;
789
	}
790
791
	/**
792
	* Returns true if both FULLTEXT indexes exist
793
	*/
794
	function index_created()
795
	{
796
		if (empty($this->stats))
797
		{
798
			$this->get_stats();
799
		}
800
801
		return (isset($this->stats['post_text']) && isset($this->stats['post_subject']) && isset($this->stats['post_content'])) ? true : false;
802
	}
803
804
	/**
805
	* Returns an associative array containing information about the indexes
806
	*/
807
	function index_stats()
808
	{
809
		global $user;
810
811
		if (empty($this->stats))
812
		{
813
			$this->get_stats();
814
		}
815
816
		return array(
817
			$user->lang['FULLTEXT_MYSQL_TOTAL_POSTS']			=> ($this->index_created()) ? $this->stats['total_posts'] : 0,
818
		);
819
	}
820
821
	function get_stats()
822
	{
823
		global $db;
824
825
		if (strpos($db->sql_layer, 'mysql') === false)
826
		{
827
			$this->stats = array();
828
			return;
829
		}
830
831
		$sql = 'SHOW INDEX
832
			FROM ' . POSTS_TABLE;
833
		$result = $db->sql_query($sql);
834
835
		while ($row = $db->sql_fetchrow($result))
836
		{
837
			// deal with older MySQL versions which didn't use Index_type
838
			$index_type = (isset($row['Index_type'])) ? $row['Index_type'] : $row['Comment'];
839
840
			if ($index_type == 'FULLTEXT')
841
			{
842
				if ($row['Key_name'] == 'post_text')
843
				{
844
					$this->stats['post_text'] = $row;
845
				}
846
				else if ($row['Key_name'] == 'post_subject')
847
				{
848
					$this->stats['post_subject'] = $row;
849
				}
850
				else if ($row['Key_name'] == 'post_content')
851
				{
852
					$this->stats['post_content'] = $row;
853
				}
854
			}
855
		}
856
		$db->sql_freeresult($result);
857
858
		$sql = 'SELECT COUNT(post_id) as total_posts
859
			FROM ' . POSTS_TABLE;
860
		$result = $db->sql_query($sql);
861
		$this->stats['total_posts'] = (int) $db->sql_fetchfield('total_posts');
862
		$db->sql_freeresult($result);
863
	}
864
865
	/**
866
	* Display a note, that UTF-8 support is not available with certain versions of PHP
867
	*/
868
	function acp()
869
	{
870
		global $user, $config;
871
872
		$tpl = '
873
		<dl>
874
			<dt><label>' . $user->lang['FULLTEXT_MYSQL_PCRE'] . '</label><br /><span>' . $user->lang['FULLTEXT_MYSQL_PCRE_EXPLAIN'] . '</span></dt>
875
			<dd>' . (($this->pcre_properties) ? $user->lang['YES'] : $user->lang['NO']) . ' (PHP ' . PHP_VERSION . ')</dd>
876
		</dl>
877
		<dl>
878
			<dt><label>' . $user->lang['FULLTEXT_MYSQL_MBSTRING'] . '</label><br /><span>' . $user->lang['FULLTEXT_MYSQL_MBSTRING_EXPLAIN'] . '</span></dt>
879
			<dd>' . (($this->mbstring_regex) ? $user->lang['YES'] : $user->lang['NO']). '</dd>
880
		</dl>
881
		';
882
883
		// These are fields required in the config table
884
		return array(
885
			'tpl'		=> $tpl,
886
			'config'	=> array()
887
		);
888
	}
889
}
890
891
?>