#4303: add search function.

This commit is contained in:
Ben
2024-06-03 21:19:42 +08:00
parent d75e3acdf3
commit 9d85ca07f4
50 changed files with 2478 additions and 4 deletions

View File

@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Oqtane.Interfaces;
using Oqtane.Models;
using Oqtane.Repository;
using Oqtane.Shared;
namespace Oqtane.Managers.Search
{
public class ModuleSearchIndexManager : SearchIndexManagerBase
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<ModuleSearchIndexManager> _logger;
private readonly IPageModuleRepository _pageModuleRepostory;
private readonly IPageRepository _pageRepository;
public ModuleSearchIndexManager(
IServiceProvider serviceProvider,
IPageModuleRepository pageModuleRepostory,
ILogger<ModuleSearchIndexManager> logger,
IPageRepository pageRepository)
: base(serviceProvider)
{
_serviceProvider = serviceProvider;
_logger = logger;
_pageModuleRepostory = pageModuleRepostory;
_pageRepository = pageRepository;
}
public override string Name => Constants.ModuleSearchIndexManagerName;
public override int Priority => Constants.ModuleSearchIndexManagerPriority;
public override int IndexDocuments(int siteId, DateTime? startTime, Action<IList<SearchDocument>> processSearchDocuments, Action<string> handleError)
{
var pageModules = _pageModuleRepostory.GetPageModules(siteId).DistinctBy(i => i.ModuleId);
var searchDocuments = new List<SearchDocument>();
foreach(var pageModule in pageModules)
{
var module = pageModule.Module;
if (module.ModuleDefinition.ServerManagerType != "")
{
_logger.LogDebug($"Search: Begin index module {module.ModuleId}.");
var type = Type.GetType(module.ModuleDefinition.ServerManagerType);
if (type?.GetInterface("IModuleSearch") != null)
{
try
{
var moduleSearch = (IModuleSearch)ActivatorUtilities.CreateInstance(_serviceProvider, type);
var documents = moduleSearch.GetSearchDocuments(module, startTime.GetValueOrDefault(DateTime.MinValue));
if(documents != null)
{
foreach(var document in documents)
{
SaveModuleMetaData(document, pageModule);
searchDocuments.Add(document);
}
}
}
catch(Exception ex)
{
_logger.LogError(ex, $"Search: Index module {module.ModuleId} failed.");
handleError($"Search: Index module {module.ModuleId} failed: {ex.Message}");
}
}
_logger.LogDebug($"Search: End index module {module.ModuleId}.");
}
}
processSearchDocuments(searchDocuments);
return searchDocuments.Count;
}
private void SaveModuleMetaData(SearchDocument document, PageModule pageModule)
{
document.EntryId = pageModule.ModuleId;
document.IndexerName = Name;
document.SiteId = pageModule.Module.SiteId;
document.LanguageCode = string.Empty;
if(document.ModifiedTime == DateTime.MinValue)
{
document.ModifiedTime = pageModule.ModifiedOn;
}
if (string.IsNullOrEmpty(document.AdditionalContent))
{
document.AdditionalContent = string.Empty;
}
var page = _pageRepository.GetPage(pageModule.PageId);
if (string.IsNullOrEmpty(document.Url) && page != null)
{
document.Url = page.Url;
}
if (string.IsNullOrEmpty(document.Title) && page != null)
{
document.Title = !string.IsNullOrEmpty(page.Title) ? page.Title : page.Name;
}
if (document.Properties == null)
{
document.Properties = new List<SearchDocumentProperty>();
}
if(!document.Properties.Any(i => i.Name == Constants.SearchPageIdPropertyName))
{
document.Properties.Add(new SearchDocumentProperty { Name = Constants.SearchPageIdPropertyName, Value = pageModule.PageId.ToString() });
}
if (!document.Properties.Any(i => i.Name == Constants.SearchModuleIdPropertyName))
{
document.Properties.Add(new SearchDocumentProperty { Name = Constants.SearchModuleIdPropertyName, Value = pageModule.ModuleId.ToString() });
}
}
}
}

View File

@ -0,0 +1,69 @@
using System;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Oqtane.Models;
using Oqtane.Repository;
using Oqtane.Security;
using Oqtane.Services;
using Oqtane.Shared;
namespace Oqtane.Managers.Search
{
public class ModuleSearchResultManager : ISearchResultManager
{
public string Name => Constants.ModuleSearchIndexManagerName;
private readonly IServiceProvider _serviceProvider;
public ModuleSearchResultManager(
IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public string GetUrl(SearchResult searchResult, SearchQuery searchQuery)
{
var pageRepository = _serviceProvider.GetRequiredService<IPageRepository>();
var pageIdValue = searchResult.Properties?.FirstOrDefault(i => i.Name == Constants.SearchPageIdPropertyName)?.Value ?? string.Empty;
if(!string.IsNullOrEmpty(pageIdValue) && int.TryParse(pageIdValue, out int pageId))
{
var page = pageRepository.GetPage(pageId);
if (page != null)
{
return $"{searchQuery.Alias.Protocol}{searchQuery.Alias.Name}{(!string.IsNullOrEmpty(page.Path) && !page.Path.StartsWith("/") ? "/" : "")}{page.Path}";
}
}
return string.Empty;
}
public bool Visible(SearchDocument searchResult, SearchQuery searchQuery)
{
var pageIdValue = searchResult.Properties?.FirstOrDefault(i => i.Name == Constants.SearchPageIdPropertyName)?.Value ?? string.Empty;
var moduleIdValue = searchResult.Properties?.FirstOrDefault(i => i.Name == Constants.SearchModuleIdPropertyName)?.Value ?? string.Empty;
if (!string.IsNullOrEmpty(pageIdValue) && int.TryParse(pageIdValue, out int pageId)
&& !string.IsNullOrEmpty(moduleIdValue) && int.TryParse(moduleIdValue, out int moduleId))
{
return CanViewPage(pageId, searchQuery.User) && CanViewModule(moduleId, searchQuery.User);
}
return false;
}
private bool CanViewModule(int moduleId, User user)
{
var moduleRepository = _serviceProvider.GetRequiredService<IModuleRepository>();
var module = moduleRepository.GetModule(moduleId);
return module != null && !module.IsDeleted && UserSecurity.IsAuthorized(user, PermissionNames.View, module.PermissionList);
}
private bool CanViewPage(int pageId, User user)
{
var pageRepository = _serviceProvider.GetRequiredService<IPageRepository>();
var page = pageRepository.GetPage(pageId);
return page != null && !page.IsDeleted && UserSecurity.IsAuthorized(user, PermissionNames.View, page.PermissionList)
&& (Utilities.IsPageModuleVisible(page.EffectiveDate, page.ExpiryDate) || UserSecurity.IsAuthorized(user, PermissionNames.Edit, page.PermissionList));
}
}
}

View File

@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Reflection.Metadata;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Oqtane.Interfaces;
using Oqtane.Models;
using Oqtane.Repository;
using Oqtane.Shared;
namespace Oqtane.Managers.Search
{
public class PageSearchIndexManager : SearchIndexManagerBase
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<ModuleSearchIndexManager> _logger;
private readonly IPageRepository _pageRepository;
public PageSearchIndexManager(
IServiceProvider serviceProvider,
ILogger<ModuleSearchIndexManager> logger,
IPageRepository pageRepository)
: base(serviceProvider)
{
_serviceProvider = serviceProvider;
_logger = logger;
_pageRepository = pageRepository;
}
public override string Name => Constants.PageSearchIndexManagerName;
public override int Priority => Constants.PageSearchIndexManagerPriority;
public override int IndexDocuments(int siteId, DateTime? startTime, Action<IList<SearchDocument>> processSearchDocuments, Action<string> handleError)
{
var startTimeValue = startTime.GetValueOrDefault(DateTime.MinValue);
var pages = _pageRepository.GetPages(siteId).Where(i => i.ModifiedOn >= startTimeValue);
var searchDocuments = new List<SearchDocument>();
foreach(var page in pages)
{
try
{
if(IsSystemPage(page))
{
continue;
}
var document = new SearchDocument
{
EntryId = page.PageId,
IndexerName = Name,
SiteId = page.SiteId,
LanguageCode = string.Empty,
ModifiedTime = page.ModifiedOn,
AdditionalContent = string.Empty,
Url = page.Url ?? string.Empty,
Title = !string.IsNullOrEmpty(page.Title) ? page.Title : page.Name,
Description = string.Empty,
Body = $"{page.Name} {page.Title}"
};
if (document.Properties == null)
{
document.Properties = new List<SearchDocumentProperty>();
}
if (!document.Properties.Any(i => i.Name == Constants.SearchPageIdPropertyName))
{
document.Properties.Add(new SearchDocumentProperty { Name = Constants.SearchPageIdPropertyName, Value = page.PageId.ToString() });
}
searchDocuments.Add(document);
}
catch(Exception ex)
{
_logger.LogError(ex, $"Search: Index page {page.PageId} failed.");
handleError($"Search: Index page {page.PageId} failed: {ex.Message}");
}
}
processSearchDocuments(searchDocuments);
return searchDocuments.Count;
}
private bool IsSystemPage(Models.Page page)
{
return page.Path.Contains("admin") || page.Path == "login" || page.Path == "register" || page.Path == "profile";
}
}
}

View File

@ -0,0 +1,46 @@
using System;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Oqtane.Models;
using Oqtane.Repository;
using Oqtane.Security;
using Oqtane.Services;
using Oqtane.Shared;
namespace Oqtane.Managers.Search
{
public class PageSearchResultManager : ISearchResultManager
{
public string Name => Constants.PageSearchIndexManagerName;
private readonly IServiceProvider _serviceProvider;
public PageSearchResultManager(
IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public string GetUrl(SearchResult searchResult, SearchQuery searchQuery)
{
var pageRepository = _serviceProvider.GetRequiredService<IPageRepository>();
var page = pageRepository.GetPage(searchResult.EntryId);
if (page != null)
{
return $"{searchQuery.Alias.Protocol}{searchQuery.Alias.Name}{(!string.IsNullOrEmpty(page.Path) && !page.Path.StartsWith("/") ? "/" : "")}{page.Path}";
}
return string.Empty;
}
public bool Visible(SearchDocument searchResult, SearchQuery searchQuery)
{
var pageRepository = _serviceProvider.GetRequiredService<IPageRepository>();
var page = pageRepository.GetPage(searchResult.EntryId);
return page != null && !page.IsDeleted
&& UserSecurity.IsAuthorized(searchQuery.User, PermissionNames.View, page.PermissionList)
&& (Utilities.IsPageModuleVisible(page.EffectiveDate, page.ExpiryDate) || UserSecurity.IsAuthorized(searchQuery.User, PermissionNames.Edit, page.PermissionList));
}
}
}

View File

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Oqtane.Models;
using Oqtane.Repository;
using Oqtane.Services;
using Oqtane.Shared;
namespace Oqtane.Managers.Search
{
public abstract class SearchIndexManagerBase : ISearchIndexManager
{
private readonly IServiceProvider _serviceProvider;
public SearchIndexManagerBase(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public abstract int Priority { get; }
public abstract string Name { get; }
public abstract int IndexDocuments(int siteId, DateTime? startDate, Action<IList<SearchDocument>> processSearchDocuments, Action<string> handleError);
public virtual bool IsIndexEnabled(int siteId)
{
var settingName = string.Format(Constants.SearchIndexManagerEnabledSettingFormat, Name);
var settingRepository = _serviceProvider.GetRequiredService<ISettingRepository>();
var setting = settingRepository.GetSetting(EntityNames.Site, siteId, settingName);
return setting == null || setting.SettingValue == "true";
}
}
}