/** * Custom WonderCMS search * Searches visible menu LINK pages, including nested pages. */ function wonderSearchGetMenuLinks($items, $parentPath = '') { $links = []; foreach ($items as $item) { if (is_array($item)) { $item = (object)$item; } $slug = trim($item->slug ?? '', '/'); if ($slug === '') { continue; } $currentPath = $parentPath === '' ? $slug : $parentPath . '/' . $slug; $subpages = []; if (!empty($item->subpages)) { $subpages = is_object($item->subpages) ? get_object_vars($item->subpages) : (array)$item->subpages; } $visibleSubpages = array_filter( $subpages, function ($subpage) { $subpage = is_array($subpage) ? (object)$subpage : $subpage; return !isset($subpage->visibility) || $subpage->visibility !== 'hide'; } ); if (empty($visibleSubpages)) { $links[] = $currentPath; } if (!empty($subpages)) { $links = array_merge( $links, wonderSearchGetMenuLinks($subpages, $currentPath) ); } } return $links; } function wonderSearchGetPageByPath($pages, $path) { $parts = explode('/', $path); $current = $pages; foreach ($parts as $index => $part) { if ($index === 0) { if (!isset($current->{$part})) { return null; } $current = $current->{$part}; } else { if (!isset($current->subpages->{$part})) { return null; } $current = $current->subpages->{$part}; } } return $current; } function wonderSearchCleanTitle($title) { return html_entity_decode( $title, ENT_QUOTES | ENT_HTML5, 'UTF-8' ); } function wonderSearch() { global $Wcms; $query = trim($_GET['q'] ?? ''); $searchUrl = $Wcms->url('search'); $output = '
'; if ($query === '') { $output .= '
'; return $output; } $menuConfig = $Wcms->get('config', 'menuItems'); $menuItems = is_object($menuConfig) ? get_object_vars($menuConfig) : (array)$menuConfig; $menuLinks = wonderSearchGetMenuLinks($menuItems); $pages = $Wcms->get('pages'); // Read the list of page slugs excluded from search. $excludeFile = $Wcms->filesPath . '/searchexclude.txt'; $excludedSlugs = []; if (is_readable($excludeFile)) { $excludedSlugs = file( $excludeFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES ); $excludedSlugs = array_map('trim', $excludedSlugs); } foreach ($menuLinks as $path) { // Skip the search and 404 pages. if ($path === 'search' || $path === '404') { continue; } // Compare the final page slug against the exclusion list. $slug = substr( $path, strrpos($path, '/') === false ? 0 : strrpos($path, '/') + 1 ); if (in_array($slug, $excludedSlugs, true)) { continue; } $page = wonderSearchGetPageByPath($pages, $path); if (!$page) { continue; } $title = $page->title ?? ''; $content = $page->content ?? ''; $plainContent = html_entity_decode( strip_tags($content), ENT_QUOTES | ENT_HTML5, 'UTF-8' ); $titleMatch = stripos($title, $query) !== false; $contentMatch = stripos($plainContent, $query) !== false; if (!$titleMatch && !$contentMatch) { continue; } $matchPosition = stripos($plainContent, $query); if ($matchPosition !== false) { $start = max(0, $matchPosition - 20); $excerpt = substr($plainContent, $start, 100); if ($start > 0) { $excerpt = '...' . $excerpt; } } else { $excerpt = substr($plainContent, 0, 100); } $url = $Wcms->url($path); $cleanTitle = wonderSearchCleanTitle($title); $safeTitle = htmlspecialchars( $cleanTitle, ENT_QUOTES, 'UTF-8' ); $safeExcerpt = htmlspecialchars( $excerpt, ENT_QUOTES, 'UTF-8' ); $safeQuery = htmlspecialchars( $query, ENT_QUOTES, 'UTF-8' ); $highlightedExcerpt = preg_replace( '/(' . preg_quote($safeQuery, '/') . ')/i', '$1', $safeExcerpt ); $output .= '
' . $safeTitle . '

' . $highlightedExcerpt . '

'; } $output .= ''; return $output; }