diff --git a/www/mysql_adapter.php b/www/mysql_adapter.php index 6fc3e58..3d845b6 100644 --- a/www/mysql_adapter.php +++ b/www/mysql_adapter.php @@ -69,6 +69,14 @@ class MySQLAdapter { return $post_data; } + function _normalize_post_array($post_data) { + $post_data ??= []; + + return array_map(function($post) { + return $this->_normalize_post_data($post); + }, $post_data); + } + function bump_post($post_path, $post_metadata = [], $create_dirs = true) { $post_path = $this->_sanitize_path($post_path); $path_depth = substr_count($post_path, "/"); @@ -212,6 +220,86 @@ class MySQLAdapter { ", "ss", $this->SITE_CONFIG['HTTP_HOST'], $post_path . "%"); } + function escape_tag($tag) { + return preg_replace_callback('/[\WZ]/', function($match) { + return "Z" . ord($match[0]); + }, strtolower($tag)); + } + + function escape_search_tag($tag) { + preg_match("/^([\+\-]?)(.*?)(\*?)$/", $tag, $matches); + + if(!isset($matches[1])) { + echo "Problem with tag!"; + var_dump($tag); + } + + return $matches[1] . $this->escape_tag($matches[2]) . $matches[3]; + } + + function update_post_search_data($post_path, $post_tags) { + $post_tags []= "path:" . $post_path; + $post_tags []= "host:" . $this->SITE_CONFIG['HTTP_HOST']; + + $post_tags = array_unique($post_tags); + $post_tags = array_map(function($val) { + return $this->escape_tag($val); + }, $post_tags); + + asort($post_tags); + $post_tags = join(' ', $post_tags); + + $qry = " + INSERT INTO posts + ( host, post_path, post_tags ) + VALUES + ( ?, ?, ? ) AS new + ON DUPLICATE KEY + UPDATE post_tags=new.post_tags; + "; + + $this->_exec($qry, "sss", + $this->SITE_CONFIG['HTTP_HOST'], $post_path, $post_tags); + } + + function perform_post_search($taglist, $order = null, $limit = 20, $page = 0) { + $allowed_ordering = [ + "post_create_time" + ]; + + $qry = " + SELECT post_path, post_metadata + FROM posts + WHERE MATCH(post_tags) AGAINST (? IN BOOLEAN MODE) + "; + + if(!is_array($taglist)) { + $taglist = explode(' ', $taglist); + } + + $taglist []= '+host:' . $this->SITE_CONFIG['HTTP_HOST']; + $taglist = array_unique($taglist); + $taglist = array_map(function($key) { + return $this->escape_search_tag($key); + }, $taglist); + + $taglist = implode(' ', $taglist); + + if(isset($order) and in_array($order, $allowed_ordering)) { + $qry = $qry . " ORDER BY " . $order; + } + + $qry = $qry . " LIMIT ? OFFSET ?"; + + $search_results = $this->_exec($qry, "sii", $taglist, $limit, $limit * $page)->fetch_all(MYSQLI_ASSOC); + $search_results = [ + "query_string" => $taglist, + "results" => $this->_normalize_post_array($search_results) + ]; + + return $search_results; + } + function update_or_create_post($post_path, $post_metadata, $post_content) { $post_path = $this->_sanitize_path($post_path); $path_depth = substr_count($post_path, "/"); @@ -236,6 +324,8 @@ class MySQLAdapter { $path_depth, json_encode($post_metadata), $post_content); + + $this->update_post_search_data($post_path, $post_metadata['tags'] ?? []); } function get_settings_for_path($post_path) { @@ -294,9 +384,7 @@ class MySQLAdapter { $post_data = $this->_exec($qry, "ss", $post_path, $this->SITE_CONFIG['HTTP_HOST'])->fetch_assoc(); - if(!isset($post_data)) { - $post_data = ['found' => false]; - } + $post_data ??= ['found' => false]; $post_data['post_path'] = $post_path; $post_data = $this->_normalize_post_data($post_data); @@ -334,11 +422,7 @@ class MySQLAdapter { $post_data = $this->_exec($qry, "ssi", $this->SITE_CONFIG['HTTP_HOST'], $path, $path_depth+1)->fetch_all(MYSQLI_ASSOC); - $fn = function($data) { - return $this->_normalize_post_data($data); - }; - - $post_data = array_map($fn, $post_data); + $post_data = $this->_normalize_post_array($post_data); return $post_data; } diff --git a/www/router.php b/www/router.php index 91b9779..18c4641 100644 --- a/www/router.php +++ b/www/router.php @@ -25,7 +25,10 @@ $adapter = new PostHandler($SITE_CONFIG); $loader = new \Twig\Loader\FilesystemLoader(['./templates', './user_content']); -$twig = new \Twig\Environment($loader,['cache' => 'twig_cache']); +$twig = new \Twig\Environment($loader,[ + 'debug' => true, + 'cache' => 'twig_cache' +]); $twig->addExtension(new Twig\Extra\Markdown\MarkdownExtension()); use Twig\Extra\Markdown\DefaultMarkdown; @@ -239,6 +242,10 @@ function generate_website($SURI) { } elseif($SURI == '/api/upload') { echo $twig->render('upload.html'); + } elseif($SURI == '/api/search') { + + header('Content-Type: application/json'); + echo json_encode($adapter->perform_post_search($_GET['search_query'])); } } elseif(preg_match('/^\/feed(?:\/(rss|atom)(.*))?$/', $SURI, $match)) { $feed = $adapter->get_laminas_feed($match[2] ?? '/', $match[1] ?? 'rss');