From 8c6c66fb118b8ea8c91b09d49b56048b85339c6a Mon Sep 17 00:00:00 2001 From: Shaun Walker Date: Mon, 20 Feb 2023 08:35:46 -0500 Subject: [PATCH] add sitemap generator which outputs all public pages and also includes an ISitemap interface for modules --- Oqtane.Client/Modules/ModuleBase.cs | 7 +- Oqtane.Server/Modules/IPortable.cs | 4 +- Oqtane.Server/Modules/ISitemap.cs | 12 +++ Oqtane.Server/Pages/Sitemap.cshtml | 3 + Oqtane.Server/Pages/Sitemap.cshtml.cs | 112 ++++++++++++++++++++++++++ Oqtane.Shared/Models/Sitemap.cs | 20 +++++ Oqtane.Shared/Shared/Utilities.cs | 10 +++ 7 files changed, 160 insertions(+), 8 deletions(-) create mode 100644 Oqtane.Server/Modules/ISitemap.cs create mode 100644 Oqtane.Server/Pages/Sitemap.cshtml create mode 100644 Oqtane.Server/Pages/Sitemap.cshtml.cs create mode 100644 Oqtane.Shared/Models/Sitemap.cs diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index 429634611..9ba0d2648 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -190,12 +190,7 @@ public string ImageUrl(int fileid, int width, int height, string mode, string po public string AddUrlParameters(params object[] parameters) { - var url = ""; - for (var i = 0; i < parameters.Length; i++) - { - url += "/" + parameters[i].ToString(); - } - return url; + return Utilities.AddUrlParameters(parameters); } // template is in the form of a standard route template ie. "/{id}/{name}" and produces dictionary of key/value pairs diff --git a/Oqtane.Server/Modules/IPortable.cs b/Oqtane.Server/Modules/IPortable.cs index b4c89735f..bdc0c2d27 100644 --- a/Oqtane.Server/Modules/IPortable.cs +++ b/Oqtane.Server/Modules/IPortable.cs @@ -1,10 +1,10 @@ -using Oqtane.Models; +using Oqtane.Models; namespace Oqtane.Modules { public interface IPortable { - // You Must Set The "ServerAssemblyName" In Your IModule Interface + // You Must Set The "ServerManagerType" In Your IModule Interface string ExportModule(Module module); diff --git a/Oqtane.Server/Modules/ISitemap.cs b/Oqtane.Server/Modules/ISitemap.cs new file mode 100644 index 000000000..558e42e45 --- /dev/null +++ b/Oqtane.Server/Modules/ISitemap.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using Oqtane.Models; + +namespace Oqtane.Modules +{ + public interface ISitemap + { + // You Must Set The "ServerManagerType" In Your IModule Interface + + List GetUrls(string alias, string path, Module module); + } +} diff --git a/Oqtane.Server/Pages/Sitemap.cshtml b/Oqtane.Server/Pages/Sitemap.cshtml new file mode 100644 index 000000000..9b0bdca5e --- /dev/null +++ b/Oqtane.Server/Pages/Sitemap.cshtml @@ -0,0 +1,3 @@ +@page "/pages/sitemap.xml" +@namespace Oqtane.Pages +@model Oqtane.Pages.SitemapModel diff --git a/Oqtane.Server/Pages/Sitemap.cshtml.cs b/Oqtane.Server/Pages/Sitemap.cshtml.cs new file mode 100644 index 000000000..55f08d409 --- /dev/null +++ b/Oqtane.Server/Pages/Sitemap.cshtml.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.DependencyInjection; +using Oqtane.Enums; +using Oqtane.Infrastructure; +using Oqtane.Models; +using Oqtane.Modules; +using Oqtane.Repository; +using Oqtane.Security; +using Oqtane.Shared; + +namespace Oqtane.Pages +{ + [AllowAnonymous] + public class SitemapModel : PageModel + { + private readonly IServiceProvider _serviceProvider; + private readonly IPageRepository _pages; + private readonly IPageModuleRepository _pageModules; + private readonly IModuleDefinitionRepository _moduleDefinitions; + private readonly IUserPermissions _userPermissions; + private readonly ILogManager _logger; + private readonly Alias _alias; + + public SitemapModel(IServiceProvider serviceProvider, IPageRepository pages, IPageModuleRepository pageModules, IModuleDefinitionRepository moduleDefinitions, IUserPermissions userPermissions, IUrlMappingRepository urlMappings, ISyncManager syncManager, ILogManager logger, ITenantManager tenantManager) + { + _serviceProvider = serviceProvider; + _pages = pages; + _pageModules = pageModules; + _moduleDefinitions = moduleDefinitions; + _userPermissions = userPermissions; + _logger = logger; + _alias = tenantManager.GetAlias(); + } + + public IActionResult OnGet() + { + var sitemap = new List(); + + // build site map + var moduleDefinitions = _moduleDefinitions.GetModuleDefinitions(_alias.SiteId).ToList(); + var pageModules = _pageModules.GetPageModules(_alias.SiteId); + foreach (var page in _pages.GetPages(_alias.SiteId)) + { + if (_userPermissions.IsAuthorized(null, PermissionNames.View, page.Permissions)) + { + sitemap.Add(new Sitemap { Url = _alias.Protocol + _alias.Name + Utilities.NavigateUrl(_alias.Path, page.Path, ""), ModifiedOn = page.ModifiedOn }); + + foreach (var pageModule in pageModules.Where(item => item.PageId == page.PageId)) + { + if (_userPermissions.IsAuthorized(null, PermissionNames.View, pageModule.Module.Permissions)) + { + var moduleDefinition = moduleDefinitions.Where(item => item.ModuleDefinitionName == pageModule.Module.ModuleDefinitionName).FirstOrDefault(); + if (moduleDefinition != null && moduleDefinition.ServerManagerType != "") + { + Type moduletype = Type.GetType(moduleDefinition.ServerManagerType); + if (moduletype != null && moduletype.GetInterface("ISitemap") != null) + { + try + { + var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype); + var urls = ((ISitemap)moduleobject).GetUrls(_alias.Path, page.Path, pageModule.Module); + foreach (var url in urls) + { + sitemap.Add(new Sitemap { Url = _alias.Protocol + _alias.Name + url.Url, ModifiedOn = url.ModifiedOn }); + } + } + catch (Exception ex) + { + _logger.Log(LogLevel.Error, this, LogFunction.Other, ex, "Error Retrieving SiteMap For {Name} Module", moduleDefinition.Name); + } + } + } + } + } + } + } + + // write XML + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Indent = true; + settings.IndentChars = (" "); + settings.CloseOutput = true; + settings.OmitXmlDeclaration = true; + settings.WriteEndDocumentOnClose = true; + + StringBuilder builder = new StringBuilder(); + using (XmlWriter writer = XmlWriter.Create(builder, settings)) + { + writer.WriteStartDocument(); + writer.WriteStartElement("urlset", "http://www.sitemaps.org/schemas/sitemap/0.9"); + + foreach (var url in sitemap) + { + writer.WriteStartElement("url"); + writer.WriteElementString("loc", url.Url); + writer.WriteElementString("lastmod", url.ModifiedOn.ToString("yyyy-MM-dd")); + writer.WriteEndElement(); + } + writer.Close(); + } + + return Content(builder.ToString()); + } + } +} diff --git a/Oqtane.Shared/Models/Sitemap.cs b/Oqtane.Shared/Models/Sitemap.cs new file mode 100644 index 000000000..15d0cedca --- /dev/null +++ b/Oqtane.Shared/Models/Sitemap.cs @@ -0,0 +1,20 @@ +using System; + +namespace Oqtane.Models +{ + /// + /// Describes a Sitemap + /// + public class Sitemap + { + /// + /// Url + /// + public string Url { get; set; } + + /// + /// Url + /// + public DateTime ModifiedOn { get; set; } + } +} diff --git a/Oqtane.Shared/Shared/Utilities.cs b/Oqtane.Shared/Shared/Utilities.cs index 34b8e88bb..7235af3d4 100644 --- a/Oqtane.Shared/Shared/Utilities.cs +++ b/Oqtane.Shared/Shared/Utilities.cs @@ -143,6 +143,16 @@ public static string TenantUrl(Alias alias, string url) return $"{alias?.BaseUrl}{url}"; } + public static string AddUrlParameters(params object[] parameters) + { + var url = ""; + for (var i = 0; i < parameters.Length; i++) + { + url += "/" + parameters[i].ToString(); + } + return url; + } + public static string FormatContent(string content, Alias alias, string operation) { var aliasUrl = (alias != null && !string.IsNullOrEmpty(alias.Path)) ? "/" + alias.Path : "";