From dd75fe985cdef75a78ef7606586993b09a60ee8e Mon Sep 17 00:00:00 2001 From: Michael Foster Date: Sat, 20 Jul 2013 18:50:40 -0400 Subject: [PATCH 1/9] Public search form --- search.php | 156 +++++++++++++++++++++++++++++++++++++ templates/search_form.html | 19 +++++ 2 files changed, 175 insertions(+) create mode 100644 search.php create mode 100644 templates/search_form.html diff --git a/search.php b/search.php new file mode 100644 index 00000000..d2dbdf24 --- /dev/null +++ b/search.php @@ -0,0 +1,156 @@ + $boards, 'board' => isset($_POST['board']) ? $_POST['board'] : false, 'search' => isset($_POST['search']) ? str_replace('"', '"', utf8tohtml($_POST['search'])) : false)); + + if(isset($_POST['search']) && !empty($_POST['search']) && isset($_POST['board']) && in_array($_POST['board'], $boards)) { + $phrase = $_POST['search']; + $_body = ''; + + $query = prepare("SELECT COUNT(*) FROM `search_queries` WHERE `ip` = :ip AND `time` > :time"); + $query->bindValue(':ip', $_SERVER['REMOTE_ADDR']); + $query->bindValue(':time', time() - ($queries_per_minutes[1] * 60)); + $query->execute() or error(db_error($query)); + if($query->fetchColumn() > $queries_per_minutes[0]) + error('Wait a while before searching again, please.'); + + $query = prepare("SELECT COUNT(*) FROM `search_queries` WHERE `time` > :time"); + $query->bindValue(':time', time() - ($queries_per_minutes_all[1] * 60)); + $query->execute() or error(db_error($query)); + if($query->fetchColumn() > $queries_per_minutes_all[0]) + error('Wait a while before searching again, please.'); + + + $query = prepare("INSERT INTO `search_queries` VALUES (:ip, :time, :query)"); + $query->bindValue(':ip', $_SERVER['REMOTE_ADDR']); + $query->bindValue(':time', time()); + $query->bindValue(':query', $phrase); + $query->execute() or error(db_error($query)); + + _syslog(LOG_NOTICE, 'Searched /' . $_POST['board'] . '/ for "' . $phrase . '"'); + + openBoard($_POST['board']); + + $filters = Array(); + + function search_filters($m) { + global $filters; + $name = $m[2]; + $value = isset($m[4]) ? $m[4] : $m[3]; + + if(!in_array($name, Array('id', 'thread', 'subject', 'name'))) { + // unknown filter + return $m[0]; + } + + $filters[$name] = $value; + + return $m[1]; + } + + $phrase = trim(preg_replace_callback('/(^|\s)(\w+):("(.*)?"|[^\s]*)/', 'search_filters', $phrase)); + + if(!preg_match('/[^*^\s]/', $phrase) && empty($filters)) { + _syslog(LOG_WARNING, 'Query too broad.'); + $body .= '

(Query too broad.)

'; + echo Element('page.html', Array( + 'config'=>$config, + 'title'=>'Search', + 'body'=>$body, + )); + exit; + } + + // Escape escape character + $phrase = str_replace('!', '!!', $phrase); + + // Remove SQL wildcard + $phrase = str_replace('%', '!%', $phrase); + + // Use asterisk as wildcard to suit convention + $phrase = str_replace('*', '%', $phrase); + + $like = ''; + $match = Array(); + + // Find exact phrases + if(preg_match_all('/"(.+?)"/', $phrase, $m)) { + foreach($m[1] as &$quote) { + $phrase = str_replace("\"{$quote}\"", '', $phrase); + $match[] = $pdo->quote($quote); + } + } + + $words = explode(' ', $phrase); + foreach($words as &$word) { + if(empty($word)) + continue; + $match[] = $pdo->quote($word); + } + + $like = ''; + foreach($match as &$phrase) { + if(!empty($like)) + $like .= ' AND '; + $phrase = preg_replace('/^\'(.+)\'$/', '\'%$1%\'', $phrase); + $like .= '`body` LIKE ' . $phrase . ' ESCAPE \'!\''; + } + + foreach($filters as $name => $value) { + if(!empty($like)) + $like .= ' AND '; + $like .= '`' . $name . '` = '. $pdo->quote($value); + } + + $like = str_replace('%', '%%', $like); + + $query = prepare(sprintf("SELECT * FROM `posts_%s` WHERE " . $like . " ORDER BY `time` DESC LIMIT :limit", $board['uri'])); + $query->bindValue(':limit', $search_limit, PDO::PARAM_INT); + $query->execute() or error(db_error($query)); + + if($query->rowCount() == $search_limit) { + _syslog(LOG_WARNING, 'Query too broad.'); + $body .= '

(Query too broad.)

'; + echo Element('page.html', Array( + 'config'=>$config, + 'title'=>'Search', + 'body'=>$body, + )); + exit; + } + + $temp = ''; + while($post = $query->fetch()) { + if(!$post['thread']) { + $po = new Thread($post['id'], $post['subject'], $post['email'], $post['name'], $post['trip'], $post['capcode'], $post['body'], $post['time'], $post['thumb'], $post['thumbwidth'], $post['thumbheight'], $post['file'], $post['filewidth'], $post['fileheight'], $post['filesize'], $post['filename'], $post['ip'], $post['sticky'], $post['locked'], $post['sage'], $post['embed']); + } else { + $po = new Post($post['id'], $post['thread'], $post['subject'], $post['email'], $post['name'], $post['trip'], $post['capcode'], $post['body'], $post['time'], $post['thumb'], $post['thumbwidth'], $post['thumbheight'], $post['file'], $post['filewidth'], $post['fileheight'], $post['filesize'], $post['filename'], $post['ip'], $post['embed']); + } + $temp .= $po->build(true) . '
'; + } + + if(!empty($temp)) + $_body .= '
' . $query->rowCount() . ' result' . ($query->rowCount() != 1 ? 's' : '') . ' in ' . + sprintf($config['board_abbreviation'], $board['uri']) . ' - ' . $board['title'] . + '' . $temp . '
'; + + $body .= '
'; + if(!empty($_body)) + $body .= $_body; + else + $body .= '

(No results.)

'; + } + + echo Element('page.html', Array( + 'config'=>$config, + 'title'=>'Search', + 'body'=>'' . $body + )); diff --git a/templates/search_form.html b/templates/search_form.html new file mode 100644 index 00000000..16b5d4af --- /dev/null +++ b/templates/search_form.html @@ -0,0 +1,19 @@ +
+

Search

+
+

+ + + + +

+
+

+ Search is case-insensitive and based on keywords. To match exact phrases, use "quotes". Use an asterisk (*) for wildcard.

You may apply the following filters to your searches: id, thread, subject, and name. To apply a filter, simply add to your query, for example, name:Anonymous or subject:"Some Thread". Wildcards cannot be used in filters. +

+
From 472b5181652e5012588d3eafce740cde487788cf Mon Sep 17 00:00:00 2001 From: czaks Date: Sat, 20 Jul 2013 19:48:14 -0400 Subject: [PATCH 2/9] post search: configs and database --- install.php | 5 ++++- install.sql | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/install.php b/install.php index 42fbb15d..542762ae 100644 --- a/install.php +++ b/install.php @@ -1,7 +1,7 @@ vichan-devel-4.0.1'); +define('VERSION', 'v0.9.6-dev-8 + vichan-devel-4.0.2'); require 'inc/functions.php'; @@ -230,6 +230,9 @@ if (file_exists($config['has_installed'])) { case 'v0.9.6-dev-7': case 'v0.9.6-dev-7 + vichan-devel-4.0-gold': query("ALTER TABLE `bans` ADD `seen` BOOLEAN NOT NULL") or error(db_error()); + case 'v0.9.6-dev-8': + case 'v0.9.6-dev-8 + vichan-devel-4.0.1': + query("CREATE TABLE IF NOT EXISTS `search_queries` ( `ip` varchar(39) NOT NULL, `time` int(11) NOT NULL, `query` text NOT NULL) ENGINE=MyISAM DEFAULT CHARSET=utf8;") or error(db_error()); case false: // Update version number file_write($config['has_installed'], VERSION); diff --git a/install.sql b/install.sql index 04896681..9c6a0acd 100644 --- a/install.sql +++ b/install.sql @@ -233,6 +233,18 @@ CREATE TABLE IF NOT EXISTS `robot` ( -- -------------------------------------------------------- +-- +-- Table structure for table `search_queries` +-- + +CREATE TABLE IF NOT EXISTS `search_queries` ( + `ip` varchar(39) NOT NULL, + `time` int(11) NOT NULL, + `query` text NOT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +-- -------------------------------------------------------- + -- -- Table structure for table `theme_settings` -- From 629d616de5275164349608827ad83e8013b4b7ed Mon Sep 17 00:00:00 2001 From: czaks Date: Sat, 20 Jul 2013 20:01:22 -0400 Subject: [PATCH 3/9] post search: internationalize and use config values --- search.php | 31 +++++++++++++++++++++---------- templates/search_form.html | 8 ++++---- 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/search.php b/search.php index d2dbdf24..30c05df1 100644 --- a/search.php +++ b/search.php @@ -1,11 +1,15 @@ $boards, 'board' => isset($_POST['board']) ? $_POST['board'] : false, 'search' => isset($_POST['search']) ? str_replace('"', '"', utf8tohtml($_POST['search'])) : false)); @@ -18,13 +22,13 @@ $query->bindValue(':time', time() - ($queries_per_minutes[1] * 60)); $query->execute() or error(db_error($query)); if($query->fetchColumn() > $queries_per_minutes[0]) - error('Wait a while before searching again, please.'); + error(_('Wait a while before searching again, please.')); $query = prepare("SELECT COUNT(*) FROM `search_queries` WHERE `time` > :time"); $query->bindValue(':time', time() - ($queries_per_minutes_all[1] * 60)); $query->execute() or error(db_error($query)); if($query->fetchColumn() > $queries_per_minutes_all[0]) - error('Wait a while before searching again, please.'); + error(_('Wait a while before searching again, please.')); $query = prepare("INSERT INTO `search_queries` VALUES (:ip, :time, :query)"); @@ -34,6 +38,11 @@ $query->execute() or error(db_error($query)); _syslog(LOG_NOTICE, 'Searched /' . $_POST['board'] . '/ for "' . $phrase . '"'); + + // Cleanup search queries table + $query = prepare("DELETE FROM `search_queries` WHERE `time` <= :time"); + $query->bindValue(':time', time() - ($queries_per_minutes_all[1] * 60)); + $query->execute() or error(db_error($query)); openBoard($_POST['board']); @@ -44,7 +53,7 @@ $name = $m[2]; $value = isset($m[4]) ? $m[4] : $m[3]; - if(!in_array($name, Array('id', 'thread', 'subject', 'name'))) { + if(!in_array($name, array('id', 'thread', 'subject', 'name'))) { // unknown filter return $m[0]; } @@ -116,7 +125,7 @@ if($query->rowCount() == $search_limit) { _syslog(LOG_WARNING, 'Query too broad.'); - $body .= '

(Query too broad.)

'; + $body .= '

('._('Query too broad.').')

'; echo Element('page.html', Array( 'config'=>$config, 'title'=>'Search', @@ -136,7 +145,9 @@ } if(!empty($temp)) - $_body .= '
' . $query->rowCount() . ' result' . ($query->rowCount() != 1 ? 's' : '') . ' in ' . sprintf($config['board_abbreviation'], $board['uri']) . ' - ' . $board['title'] . @@ -146,7 +157,7 @@ if(!empty($_body)) $body .= $_body; else - $body .= '

(No results.)

'; + $body .= '

('._('No results.').')

'; } echo Element('page.html', Array( diff --git a/templates/search_form.html b/templates/search_form.html index 16b5d4af..a391ac53 100644 --- a/templates/search_form.html +++ b/templates/search_form.html @@ -1,11 +1,11 @@
-

Search

+

{% trans %}Search{% endtrans %}

- + + - +