diff --git a/Oqtane.Client/Modules/Controls/RichTextEditor.razor b/Oqtane.Client/Modules/Controls/RichTextEditor.razor index a67eb421..2231b02f 100644 --- a/Oqtane.Client/Modules/Controls/RichTextEditor.razor +++ b/Oqtane.Client/Modules/Controls/RichTextEditor.razor @@ -262,7 +262,7 @@ { var interop = new Interop(JSRuntime); int pos = await interop.GetCaretPosition("rawhtmleditor"); - var image = "\"""; + var image = "\"""; _rawhtml = _rawhtml.Substring(0, pos) + image + _rawhtml.Substring(pos); _rawfilemanager = false; } diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index be2c9d12..b0810bdb 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -75,7 +75,7 @@ namespace Oqtane.Modules var scripts = new List(); foreach (Resource resource in Resources.Where(item => item.ResourceType == ResourceType.Script)) { - var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + "/" + resource.Url; + var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + (!resource.Url.StartsWith("/") ? "/" : "") + resource.Url; scripts.Add(new { href = url, bundle = resource.Bundle ?? "", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", es6module = resource.ES6Module }); } if (scripts.Any()) @@ -91,7 +91,7 @@ namespace Oqtane.Modules public string ModulePath() { - return "Modules/" + GetType().Namespace + "/"; + return PageState?.Alias.BaseUrl + "/Modules/" + GetType().Namespace + "/"; } // url methods @@ -145,14 +145,23 @@ namespace Oqtane.Modules return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters); } - public string ContentUrl(int fileid) + public string FileUrl(string folderpath, string filename) { - return ContentUrl(fileid, false); + return FileUrl(folderpath, filename, false); } - public string ContentUrl(int fileid, bool asAttachment) + public string FileUrl(string folderpath, string filename, bool download) { - return Utilities.ContentUrl(PageState.Alias, fileid, asAttachment); + return Utilities.FileUrl(PageState.Alias, folderpath, filename, download); + } + public string FileUrl(int fileid) + { + return FileUrl(fileid, false); + } + + public string FileUrl(int fileid, bool download) + { + return Utilities.FileUrl(PageState.Alias, fileid, download); } public string ImageUrl(int fileid, int width, int height) @@ -407,5 +416,17 @@ namespace Oqtane.Modules await _moduleBase.Log(null, LogLevel.Critical, "", exception, message, args); } } + + [Obsolete("ContentUrl(int fileId) is deprecated. Use FileUrl(int fileId) instead.", false)] + public string ContentUrl(int fileid) + { + return ContentUrl(fileid, false); + } + + [Obsolete("ContentUrl(int fileId, bool asAttachment) is deprecated. Use FileUrl(int fileId, bool download) instead.", false)] + public string ContentUrl(int fileid, bool asAttachment) + { + return Utilities.FileUrl(PageState.Alias, fileid, asAttachment); + } } } diff --git a/Oqtane.Client/Themes/ThemeBase.cs b/Oqtane.Client/Themes/ThemeBase.cs index 6f011b91..232f1996 100644 --- a/Oqtane.Client/Themes/ThemeBase.cs +++ b/Oqtane.Client/Themes/ThemeBase.cs @@ -3,6 +3,7 @@ using Microsoft.JSInterop; using Oqtane.Models; using Oqtane.Shared; using Oqtane.UI; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -49,7 +50,7 @@ namespace Oqtane.Themes public string ThemePath() { - return "Themes/" + GetType().Namespace + "/"; + return PageState?.Alias.BaseUrl + "/Themes/" + GetType().Namespace + "/"; } // url methods @@ -94,14 +95,23 @@ namespace Oqtane.Themes return Utilities.EditUrl(PageState.Alias.Path, path, moduleid, action, parameters); } - public string ContentUrl(int fileid) + public string FileUrl(string folderpath, string filename) { - return Utilities.ContentUrl(PageState.Alias, fileid); + return FileUrl(folderpath, filename, false); } - public string ContentUrl(int fileid, bool asAttachment) + public string FileUrl(string folderpath, string filename, bool download) { - return Utilities.ContentUrl(PageState.Alias, fileid, asAttachment); + return Utilities.FileUrl(PageState.Alias, folderpath, filename, download); + } + public string FileUrl(int fileid) + { + return FileUrl(fileid, false); + } + + public string FileUrl(int fileid, bool download) + { + return Utilities.FileUrl(PageState.Alias, fileid, download); } public string ImageUrl(int fileid, int width, int height) @@ -118,5 +128,17 @@ namespace Oqtane.Themes { return Utilities.ImageUrl(PageState.Alias, fileid, width, height, mode, position, background, rotate, recreate); } + + [Obsolete("ContentUrl(int fileId) is deprecated. Use FileUrl(int fileId) instead.", false)] + public string ContentUrl(int fileid) + { + return ContentUrl(fileid, false); + } + + [Obsolete("ContentUrl(int fileId, bool asAttachment) is deprecated. Use FileUrl(int fileId, bool download) instead.", false)] + public string ContentUrl(int fileid, bool asAttachment) + { + return Utilities.FileUrl(PageState.Alias, fileid, asAttachment); + } } } diff --git a/Oqtane.Client/UI/ThemeBuilder.razor b/Oqtane.Client/UI/ThemeBuilder.razor index 1db0d301..d5137a02 100644 --- a/Oqtane.Client/UI/ThemeBuilder.razor +++ b/Oqtane.Client/UI/ThemeBuilder.razor @@ -36,7 +36,7 @@ foreach (Resource resource in PageState.Page.Resources.Where(item => item.ResourceType == ResourceType.Stylesheet)) { var prefix = "app-stylesheet-" + resource.Level.ToString().ToLower(); - var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + "/" + resource.Url; + var url = (resource.Url.Contains("://")) ? resource.Url : PageState.Alias.BaseUrl + (!resource.Url.StartsWith("/") ? "/" : "") + resource.Url; links.Add(new { id = prefix + "-" + batch + "-" + (links.Count + 1).ToString("00"), rel = "stylesheet", href = url, type = "text/css", integrity = resource.Integrity ?? "", crossorigin = resource.CrossOrigin ?? "", insertbefore = prefix }); } if (links.Any()) diff --git a/Oqtane.Server/Pages/Files.cshtml.cs b/Oqtane.Server/Pages/Files.cshtml.cs index b446c4d9..6714ce3f 100644 --- a/Oqtane.Server/Pages/Files.cshtml.cs +++ b/Oqtane.Server/Pages/Files.cshtml.cs @@ -42,6 +42,13 @@ namespace Oqtane.Pages var folderpath = ""; var filename = ""; + bool download = false; + if (path.Contains("?download")) + { + download = true; + path = path.Substring(0, path.IndexOf("?download")); + } + var segments = path.Split('/'); if (segments.Length > 0) { @@ -52,15 +59,31 @@ namespace Oqtane.Pages } } - var file = _files.GetFile(_alias.SiteId, folderpath, filename); + Models.File file; + if (folderpath == "id" && int.TryParse(filename, out int fileid)) + { + file = _files.GetFile(fileid, false); + } + else + { + file = _files.GetFile(_alias.SiteId, folderpath, filename); + } + if (file != null) { - if (_userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.Permissions)) + if (file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.Permissions)) { var filepath = _files.GetFilePath(file); if (System.IO.File.Exists(filepath)) { - return PhysicalFile(filepath, file.GetMimeType()); + if (download) + { + return PhysicalFile(filepath, file.GetMimeType(), file.Name); + } + else + { + return PhysicalFile(filepath, file.GetMimeType()); + } } else { @@ -91,7 +114,7 @@ namespace Oqtane.Pages } // broken link - string errorPath = Path.Combine(Utilities.PathCombine(_environment.ContentRootPath, "wwwroot\\images"), "error.png"); + string errorPath = Path.Combine(Utilities.PathCombine(_environment.ContentRootPath, "wwwroot/images"), "error.png"); return PhysicalFile(errorPath, MimeUtilities.GetMimeType(errorPath)); } } diff --git a/Oqtane.Server/Pages/_Host.cshtml.cs b/Oqtane.Server/Pages/_Host.cshtml.cs index 77e215b5..c0b64609 100644 --- a/Oqtane.Server/Pages/_Host.cshtml.cs +++ b/Oqtane.Server/Pages/_Host.cshtml.cs @@ -126,7 +126,7 @@ namespace Oqtane.Pages } if (site.FaviconFileId != null) { - FavIcon = Utilities.ContentUrl(alias, site.FaviconFileId.Value); + FavIcon = Utilities.FileUrl(alias, site.FaviconFileId.Value); } if (site.PwaIsEnabled && site.PwaAppIconFileId != null && site.PwaSplashIconFileId != null) { @@ -384,11 +384,11 @@ namespace Oqtane.Pages "\"background_color\": \"#fff\", " + "\"description\": \"" + site.Name + "\", " + "\"icons\": [{ " + - "\"src\": \"" + route.RootUrl + Utilities.ContentUrl(alias, site.PwaAppIconFileId.Value) + "\", " + + "\"src\": \"" + route.RootUrl + Utilities.FileUrl(alias, site.PwaAppIconFileId.Value) + "\", " + "\"sizes\": \"192x192\", " + "\"type\": \"image/png\" " + "}, { " + - "\"src\": \"" + route.RootUrl + Utilities.ContentUrl(alias, site.PwaSplashIconFileId.Value) + "\", " + + "\"src\": \"" + route.RootUrl + Utilities.FileUrl(alias, site.PwaSplashIconFileId.Value) + "\", " + "\"sizes\": \"512x512\", " + "\"type\": \"image/png\" " + "}] " + diff --git a/Oqtane.Shared/Shared/Constants.cs b/Oqtane.Shared/Shared/Constants.cs index aabe2ef6..1fd1a591 100644 --- a/Oqtane.Shared/Shared/Constants.cs +++ b/Oqtane.Shared/Shared/Constants.cs @@ -16,17 +16,12 @@ namespace Oqtane.Shared public const string ContainerComponent = "Oqtane.UI.ContainerBuilder, Oqtane.Client"; public const string DefaultTheme = "Oqtane.Themes.OqtaneTheme.Default, Oqtane.Client"; - [Obsolete("DefaultLayout is deprecated")] - public const string DefaultLayout = ""; public const string DefaultContainer = "Oqtane.Themes.OqtaneTheme.Container, Oqtane.Client"; public const string DefaultAdminContainer = "Oqtane.Themes.AdminContainer, Oqtane.Client"; public const string ActionToken = "{Action}"; public const string DefaultAction = "Index"; - [Obsolete("Use PaneNames.Admin")] - public const string AdminPane = PaneNames.Admin; - public static readonly string[] ReservedRoutes = { "api", "pages", "files" }; public const string ModuleDelimiter = "*"; public const string UrlParametersDelimiter = "!"; @@ -42,29 +37,13 @@ namespace Oqtane.Shared public const string DefaultSiteTemplate = "Oqtane.SiteTemplates.DefaultSiteTemplate, Oqtane.Server"; - public const string ContentUrl = "/api/file/download/"; + public const string FileUrl = "/files/"; public const string ImageUrl = "/api/file/image/"; public const int UserFolderCapacity = 20; // megabytes public const string PackagesFolder = "Packages"; - [Obsolete("Use UserNames.Host instead.")] - public const string HostUser = UserNames.Host; - - [Obsolete("Use TenantNames.Master instead")] - public const string MasterTenant = TenantNames.Master; public const string DefaultSite = "Default Site"; - const string RoleObsoleteMessage = "Use the corresponding member from Oqtane.Shared.RoleNames"; - - [Obsolete(RoleObsoleteMessage)] - public const string AllUsersRole = RoleNames.Everyone; - [Obsolete(RoleObsoleteMessage)] - public const string HostRole = RoleNames.Host; - [Obsolete(RoleObsoleteMessage)] - public const string AdminRole = RoleNames.Admin; - [Obsolete(RoleObsoleteMessage)] - public const string RegisteredRole = RoleNames.Registered; - public const string ImageFiles = "jpg,jpeg,jpe,gif,bmp,png,ico,webp"; public const string UploadableFiles = ImageFiles + ",mov,wmv,avi,mp4,mp3,doc,docx,xls,xlsx,ppt,pptx,pdf,txt,zip,nupkg,csv,json,xml,xslt,rss,html,htm,css"; public const string ReservedDevices = "CON,NUL,PRN,COM0,COM1,COM2,COM3,COM4,COM5,COM6,COM7,COM8,COM9,LPT0,LPT1,LPT2,LPT3,LPT4,LPT5,LPT6,LPT7,LPT8,LPT9,CONIN$,CONOUT$"; @@ -93,5 +72,33 @@ namespace Oqtane.Shared public static readonly string HttpContextSiteSettingsKey = "SiteSettings"; public static readonly string MauiUserAgent = "MAUI"; + + // Obsolete constants + + const string RoleObsoleteMessage = "Use the corresponding member from Oqtane.Shared.RoleNames"; + + [Obsolete(RoleObsoleteMessage)] + public const string AllUsersRole = RoleNames.Everyone; + [Obsolete(RoleObsoleteMessage)] + public const string HostRole = RoleNames.Host; + [Obsolete(RoleObsoleteMessage)] + public const string AdminRole = RoleNames.Admin; + [Obsolete(RoleObsoleteMessage)] + public const string RegisteredRole = RoleNames.Registered; + + [Obsolete("DefaultLayout is deprecated")] + public const string DefaultLayout = ""; + + [Obsolete("Use PaneNames.Admin")] + public const string AdminPane = PaneNames.Admin; + + [Obsolete("Use UserNames.Host instead.")] + public const string HostUser = UserNames.Host; + + [Obsolete("Use TenantNames.Master instead")] + public const string MasterTenant = TenantNames.Master; + + // [Obsolete("Use FileUrl instead")] + public const string ContentUrl = "/api/file/download/"; } } diff --git a/Oqtane.Shared/Shared/Utilities.cs b/Oqtane.Shared/Shared/Utilities.cs index 03a6cabc..34b8e88b 100644 --- a/Oqtane.Shared/Shared/Utilities.cs +++ b/Oqtane.Shared/Shared/Utilities.cs @@ -98,23 +98,28 @@ namespace Oqtane.Shared return NavigateUrl(alias, path, parameters); } - public static string ContentUrl(Alias alias, int fileId) - { - return ContentUrl(alias, fileId, false); - } - - public static string ContentUrl(Alias alias, int fileId, bool asAttachment) - { - var aliasUrl = (alias != null && !string.IsNullOrEmpty(alias.Path)) ? "/" + alias.Path : ""; - var method = asAttachment ? "/attach" : ""; - - return $"{alias.BaseUrl}{aliasUrl}{Constants.ContentUrl}{fileId}{method}"; - } - public static string FileUrl(Alias alias, string folderpath, string filename) + { + return FileUrl(alias, folderpath, filename, false); + } + + public static string FileUrl(Alias alias, string folderpath, string filename, bool download) { var aliasUrl = (alias != null && !string.IsNullOrEmpty(alias.Path)) ? "/" + alias.Path : ""; - return $"{alias.BaseUrl}{aliasUrl}/files/{folderpath.Replace("\\", "/")}{filename}"; + var querystring = (download) ? "?download" : ""; + return $"{alias?.BaseUrl}{aliasUrl}{Constants.FileUrl}{folderpath.Replace("\\", "/")}{filename}{querystring}"; + } + + public static string FileUrl(Alias alias, int fileid) + { + return FileUrl(alias, fileid, false); + } + + public static string FileUrl(Alias alias, int fileid, bool download) + { + var aliasUrl = (alias != null && !string.IsNullOrEmpty(alias.Path)) ? "/" + alias.Path : ""; + var querystring = (download) ? "?download" : ""; + return $"{alias?.BaseUrl}{aliasUrl}{Constants.FileUrl}id/{fileid}{querystring}"; } public static string ImageUrl(Alias alias, int fileId, int width, int height, string mode) @@ -128,25 +133,30 @@ namespace Oqtane.Shared mode = string.IsNullOrEmpty(mode) ? "crop" : mode; position = string.IsNullOrEmpty(position) ? "center" : position; background = string.IsNullOrEmpty(background) ? "000000" : background; - return $"{alias.BaseUrl}{url}{Constants.ImageUrl}{fileId}/{width}/{height}/{mode}/{position}/{background}/{rotate}/{recreate}"; + return $"{alias?.BaseUrl}{url}{Constants.ImageUrl}{fileId}/{width}/{height}/{mode}/{position}/{background}/{rotate}/{recreate}"; } public static string TenantUrl(Alias alias, string url) { url = (!url.StartsWith("/")) ? "/" + url : url; url = (alias != null && !string.IsNullOrEmpty(alias.Path)) ? "/" + alias.Path + url : url; - return $"{alias.BaseUrl}{url}"; + return $"{alias?.BaseUrl}{url}"; } public static string FormatContent(string content, Alias alias, string operation) { + var aliasUrl = (alias != null && !string.IsNullOrEmpty(alias.Path)) ? "/" + alias.Path : ""; switch (operation) { case "save": + content = content.Replace(alias?.BaseUrl + aliasUrl + Constants.FileUrl, Constants.FileUrl); + // legacy content = content.Replace(UrlCombine("Content", "Tenants", alias.TenantId.ToString(), "Sites", alias.SiteId.ToString()), "[siteroot]"); content = content.Replace(alias.Path + Constants.ContentUrl, Constants.ContentUrl); break; case "render": + content = content.Replace(Constants.FileUrl, alias?.BaseUrl + aliasUrl + Constants.FileUrl); + // legacy content = content.Replace("[siteroot]", UrlCombine("Content", "Tenants", alias.TenantId.ToString(), "Sites", alias.SiteId.ToString())); content = content.Replace(Constants.ContentUrl, alias.Path + Constants.ContentUrl); break; @@ -491,5 +501,19 @@ namespace Oqtane.Shared return (localDateTime?.Date, localTime); } + [Obsolete("ContentUrl(Alias alias, int fileId) is deprecated. Use FileUrl(Alias alias, int fileId) instead.", false)] + public static string ContentUrl(Alias alias, int fileId) + { + return ContentUrl(alias, fileId, false); + } + + [Obsolete("ContentUrl(Alias alias, int fileId, bool asAttachment) is deprecated. Use FileUrl(Alias alias, int fileId, bool download) instead.", false)] + public static string ContentUrl(Alias alias, int fileId, bool asAttachment) + { + var aliasUrl = (alias != null && !string.IsNullOrEmpty(alias.Path)) ? "/" + alias.Path : ""; + var method = asAttachment ? "/attach" : ""; + + return $"{alias?.BaseUrl}{aliasUrl}{Constants.ContentUrl}{fileId}{method}"; + } } }