WebOrbiton
v1.0.0.6

Publisium

159 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. $showBanner = false;
  102. ​
  103. require __DIR__ . '/includes/front-header.php';
  104. ​
  105. ?>
  106. <h1 class="category-title"><?= htmlspecialchars(Language::get('nav_search', 'Search')) ?></h1>
  107. ​
  108. <form action="search.php" method="get" class="site-search-form site-search-form-page" role="search">
  109. <span class="site-search-form-icon">
  110. <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">
  111. <circle cx="11" cy="11" r="7"></circle>
  112. <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
  113. </svg>
  114. </span>
  115. <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>
  116. <button type="submit" class="site-search-submit"><?= htmlspecialchars(Language::get('search_button', 'Search')) ?></button>
  117. </form>
  118. <script>
  119. (function() {
  120. var input = document.querySelector('.site-search-form-page .site-search-input');
  121. if (!input) {
  122. return;
  123. }
  124. var end = input.value.length;
  125. input.focus();
  126. input.setSelectionRange(end, end);
  127. })();
  128. </script>
  129. ​
  130. <?= SiteFront::renderAds('search-top') ?>
  131. ​
  132. <?php if ($query === ''): ?>
  133. <p class="search-hint"><?= htmlspecialchars(Language::get('search_hint_empty', 'Type something above to search articles.')) ?></p>
  134. <?php else: ?>
  135. <p class="search-hint">
  136. <?php
  137. $resultsTemplate = $totalArticles === 1
  138. ? Language::get('search_results_count_one', '%d result for "%s"')
  139. : Language::get('search_results_count_other', '%d results for "%s"');
  140. echo htmlspecialchars(sprintf($resultsTemplate, $totalArticles, $query));
  141. ?>
  142. </p>
  143. ​
  144. <div class="article-grid" id="article-list" data-pagination-source="search" data-search-query="<?= htmlspecialchars($query, ENT_QUOTES) ?>">
  145. <?php foreach ($articles as $article): ?>
  146. <?= renderArticleCard($article) ?>
  147. <?php endforeach; ?>
  148. <?php if (empty($articles)): ?>
  149. <p><?= htmlspecialchars(Language::get('search_no_results', 'No articles matched your search.')) ?></p>
  150. <?php endif; ?>
  151. </div>
  152. ​
  153. <div id="pagination-container">
  154. <?= ($settings['article_loading_mode'] ?? 'scroll') === 'static' ? '' : Pagination::render($settings['pagination_style'], 'search.php', $currentPage, $totalPages, ['q' => $query], 'article-list') ?>
  155. </div>
  156. <?php endif; ?>
  157. ​
  158. <?php require __DIR__ . '/includes/front-footer.php'; ?>
  159. ​