WebOrbiton
v2.0.0.1

Publisium

160 lines · 6.0 KB
  1. <?php
  2. ​
  3. declare(strict_types=1);
  4. ​
  5. require_once __DIR__ . '/includes/config.php';
  6. require_once __DIR__ . '/includes/database.php';
  7. require_once __DIR__ . '/includes/user-auth.php';
  8. require_once __DIR__ . '/includes/language.php';
  9. require_once __DIR__ . '/includes/site-front.php';
  10. require_once __DIR__ . '/includes/pagination.php';
  11. require_once __DIR__ . '/includes/article-card.php';
  12. ​
  13. UserAuth::boot();
  14. ​
  15. $db = Database::site();
  16. $settings = SiteFront::settings();
  17. ​
  18. if ($settings['search_enabled'] !== '1') {
  19. http_response_code(404);
  20. $pageTitle = Language::get('not_found_title', 'Not found') . ' - ' . $settings['site_name'];
  21. $showBanner = false;
  22. require __DIR__ . '/includes/front-header.php';
  23. echo '<p>' . htmlspecialchars(Language::get('search_not_available', 'Search is not available.')) . '</p>';
  24. require __DIR__ . '/includes/front-footer.php';
  25. exit;
  26. }
  27. ​
  28. $query = trim((string) ($_GET['q'] ?? ''));
  29. ​
  30. $matchTitle = $settings['search_match_title'] === '1';
  31. $matchExcerpt = $settings['search_match_excerpt'] === '1';
  32. $matchContent = $settings['search_match_content'] === '1';
  33. ​
  34. if (!$matchTitle && !$matchExcerpt && !$matchContent) {
  35. $matchTitle = true;
  36. }
  37. ​
  38. $articles = [];
  39. $totalArticles = 0;
  40. $totalPages = 0;
  41. $currentPage = Pagination::currentPage();
  42. $perPage = max(1, (int) $settings['articles_per_page_home']);
  43. $offset = Pagination::offset($currentPage, $perPage);
  44. ​
  45. if ($query !== '') {
  46. $conditions = [];
  47. if ($matchTitle) {
  48. $conditions[] = 'a.title LIKE :q_title';
  49. }
  50. if ($matchExcerpt) {
  51. $conditions[] = 'a.excerpt LIKE :q_excerpt';
  52. }
  53. if ($matchContent) {
  54. $conditions[] = 'a.content_blocks LIKE :q_content';
  55. }
  56. ​
  57. $whereClause = "a.status = 'published' AND (" . implode(' OR ', $conditions) . ')' . Languages::articleFilterSql('a');
  58. $likeValue = '%' . str_replace(['%', '_'], ['\\%', '\\_'], $query) . '%';
  59. ​
  60. $bindings = [];
  61. if ($matchTitle) {
  62. $bindings['q_title'] = $likeValue;
  63. }
  64. if ($matchExcerpt) {
  65. $bindings['q_excerpt'] = $likeValue;
  66. }
  67. if ($matchContent) {
  68. $bindings['q_content'] = $likeValue;
  69. }
  70. ​
  71. $countStatement = $db->prepare("SELECT COUNT(*) AS c FROM articles a WHERE {$whereClause}");
  72. $countStatement->execute($bindings);
  73. $totalArticles = (int) $countStatement->fetch()['c'];
  74. $isStaticArticleLoading = ($settings['article_loading_mode'] ?? 'scroll') === 'static';
  75. if ($isStaticArticleLoading) {
  76. $perPage = max(1, $totalArticles);
  77. $currentPage = 1;
  78. $offset = 0;
  79. }
  80. $totalPages = Pagination::totalPages($totalArticles, $perPage);
  81. ​
  82. $searchStatement = $db->prepare(
  83. "SELECT a.*, c.name AS category_name, c.slug AS category_slug
  84. FROM articles a
  85. LEFT JOIN categories c ON c.id = a.category_id
  86. WHERE {$whereClause}
  87. ORDER BY a.published_at DESC
  88. LIMIT :limit OFFSET :offset"
  89. );
  90. foreach ($bindings as $bindKey => $bindValue) {
  91. $searchStatement->bindValue($bindKey, $bindValue, PDO::PARAM_STR);
  92. }
  93. $searchStatement->bindValue('limit', $perPage, PDO::PARAM_INT);
  94. $searchStatement->bindValue('offset', $offset, PDO::PARAM_INT);
  95. $searchStatement->execute();
  96. $articles = Languages::articles($searchStatement->fetchAll());
  97. }
  98. ​
  99. $pageTitle = Language::get('nav_search', 'Search') . ' - ' . $settings['site_name'];
  100. $pageDescription = '';
  101. $pageNoindex = $settings['search_noindex_enabled'] === '1';
  102. $showBanner = false;
  103. ​
  104. require __DIR__ . '/includes/front-header.php';
  105. ​
  106. ?>
  107. <h1 class="category-title"><?= htmlspecialchars(Language::get('nav_search', 'Search')) ?></h1>
  108. ​
  109. <form action="search.php" method="get" class="site-search-form site-search-form-page" role="search">
  110. <span class="site-search-form-icon">
  111. <svg class="icon icon-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
  112. <circle cx="11" cy="11" r="7"></circle>
  113. <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
  114. </svg>
  115. </span>
  116. <input type="text" name="q" class="site-search-input" value="<?= htmlspecialchars($query) ?>" placeholder="<?= htmlspecialchars(Language::get('search_placeholder', 'Search')) ?>" aria-label="<?= htmlspecialchars(Language::get('search_placeholder', 'Search')) ?>" autofocus>
  117. <button type="submit" class="site-search-submit"><?= htmlspecialchars(Language::get('search_button', 'Search')) ?></button>
  118. </form>
  119. <script>
  120. (function() {
  121. var input = document.querySelector('.site-search-form-page .site-search-input');
  122. if (!input) {
  123. return;
  124. }
  125. var end = input.value.length;
  126. input.focus();
  127. input.setSelectionRange(end, end);
  128. })();
  129. </script>
  130. ​
  131. <?= SiteFront::renderAds('search-top') ?>
  132. ​
  133. <?php if ($query === ''): ?>
  134. <p class="search-hint"><?= htmlspecialchars(Language::get('search_hint_empty', 'Type something above to search articles.')) ?></p>
  135. <?php else: ?>
  136. <p class="search-hint">
  137. <?php
  138. $resultsTemplate = $totalArticles === 1
  139. ? Language::get('search_results_count_one', '%d result for "%s"')
  140. : Language::get('search_results_count_other', '%d results for "%s"');
  141. echo htmlspecialchars(sprintf($resultsTemplate, $totalArticles, $query));
  142. ?>
  143. </p>
  144. ​
  145. <div class="article-grid" id="article-list" data-pagination-source="search" data-search-query="<?= htmlspecialchars($query, ENT_QUOTES) ?>">
  146. <?php foreach ($articles as $article): ?>
  147. <?= renderArticleCard($article) ?>
  148. <?php endforeach; ?>
  149. <?php if (empty($articles)): ?>
  150. <p><?= htmlspecialchars(Language::get('search_no_results', 'No articles matched your search.')) ?></p>
  151. <?php endif; ?>
  152. </div>
  153. ​
  154. <div id="pagination-container">
  155. <?= ($settings['article_loading_mode'] ?? 'scroll') === 'static' ? '' : Pagination::render($settings['pagination_style'], 'search.php', $currentPage, $totalPages, ['q' => $query], 'article-list') ?>
  156. </div>
  157. <?php endif; ?>
  158. ​
  159. <?php require __DIR__ . '/includes/front-footer.php'; ?>
  160. ​