diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs index f36a04ae..1f6d38eb 100644 --- a/Oqtane.Client/Modules/ModuleBase.cs +++ b/Oqtane.Client/Modules/ModuleBase.cs @@ -17,7 +17,7 @@ namespace Oqtane.Modules private Logger _logger; protected Logger logger => _logger ?? (_logger = new Logger(this)); - + [Inject] protected ILogService LoggingService { get; set; } @@ -30,7 +30,7 @@ namespace Oqtane.Modules [CascadingParameter] protected Module ModuleState { get; set; } - [CascadingParameter] + [CascadingParameter] protected ModuleInstance ModuleInstance { get; set; } // optional interface properties @@ -62,7 +62,7 @@ namespace Oqtane.Modules } } } - + // path method public string ModulePath() @@ -116,6 +116,38 @@ namespace Oqtane.Modules return Utilities.ContentUrl(PageState.Alias, fileid); } + public Dictionary GetUrlParameters(string parameterTemplate) + { + var urlParameters = new Dictionary(); + + var templateSegments = parameterTemplate.Split('/', StringSplitOptions.RemoveEmptyEntries); + var parameters = PageState.UrlParameters.Split('/', StringSplitOptions.RemoveEmptyEntries); + + if (parameters.Length == templateSegments.Length) + { + for (int i = 0; i < parameters.Length; i++) + { + if (parameters.Length > i) + { + if (templateSegments[i] == parameters[i]) + { + } + else if (templateSegments[i].StartsWith("{") && templateSegments[i].EndsWith("}")) + { + var key = templateSegments[i].Replace("{", ""); + key = key.Replace("}", ""); + urlParameters.TryAdd(key, parameters[i]); + } + else + { + i = parameters.Length; + } + } + } + } + return urlParameters; + } + // user feedback methods public void AddModuleMessage(string message, MessageType type) { @@ -154,12 +186,15 @@ namespace Oqtane.Modules case "add": logFunction = LogFunction.Create; break; + case "edit": logFunction = LogFunction.Update; break; + case "delete": logFunction = LogFunction.Delete; break; + default: logFunction = LogFunction.Read; break; @@ -241,4 +276,4 @@ namespace Oqtane.Modules } } } -} +} \ No newline at end of file diff --git a/Oqtane.Client/UI/PageState.cs b/Oqtane.Client/UI/PageState.cs index a2678802..cde1779b 100644 --- a/Oqtane.Client/UI/PageState.cs +++ b/Oqtane.Client/UI/PageState.cs @@ -14,10 +14,11 @@ namespace Oqtane.UI public List Modules { get; set; } public Uri Uri { get; set; } public Dictionary QueryString { get; set; } + public string UrlParameters { get; set; } public int ModuleId { get; set; } public string Action { get; set; } public bool EditMode { get; set; } public DateTime LastSyncDate { get; set; } public Runtime Runtime { get; set; } } -} +} \ No newline at end of file diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor index c3142689..c7235c1f 100644 --- a/Oqtane.Client/UI/SiteRouter.razor +++ b/Oqtane.Client/UI/SiteRouter.razor @@ -76,7 +76,8 @@ User user = null; List modules; var moduleid = -1; - var action = ""; + var action = string.Empty; + var urlparameters = string.Empty; var editmode = false; var reload = Reload.None; var lastsyncdate = DateTime.UtcNow; @@ -179,21 +180,60 @@ // extract admin route elements from path var segments = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); int result; - // check if path has moduleid and action specification ie. pagename/moduleid/action/ - if (segments.Length >= 2 && int.TryParse(segments[segments.Length - 2], out result)) + + int modIdPos = 0; + int actionPos = 0; + int urlParametersPos = 0; + + for (int i = 0; i < segments.Length; i++) { - action = segments[segments.Length - 1]; - moduleid = result; - path = path.Replace(moduleid.ToString() + "/" + action + "/", ""); - } - else - { - // check if path has moduleid specification ie. pagename/moduleid/ - if (segments.Length >= 1 && int.TryParse(segments[segments.Length - 1], out result)) + + if (segments[i] == Constants.UrlParametersDelimiter) { - moduleid = result; - path = path.Replace(moduleid.ToString() + "/", ""); + urlParametersPos = i + 1; } + + if (i >= urlParametersPos && urlParametersPos != 0) + { + urlparameters += "/" + segments[i]; + } + + if (segments[i] == Constants.ModuleDelimiter) + { + modIdPos = i + 1; + actionPos = modIdPos + 1; + if (actionPos > segments.Length - 1) + { + action = Constants.DefaultAction; + } + else + { + action = segments[actionPos]; + + } + } + + } + + // check if path has moduleid and action specification ie. pagename/moduleid/action/ + if (modIdPos > 0) + { + int.TryParse(segments[modIdPos], out result); + moduleid = result; + if (actionPos > segments.Length - 1) + { + path = path.Replace("/" + segments[modIdPos - 1] + "/" + segments[modIdPos], ""); + } + else + { + path = path.Replace("/" + segments[modIdPos - 1] + "/" + segments[modIdPos] + "/" + segments[actionPos], ""); + } + + } + + if (urlParametersPos > 0) + { + path = path.Replace("/" + segments[urlParametersPos - 1] + urlparameters, ""); } // remove trailing slash so it can be used as a key for Pages @@ -263,6 +303,7 @@ Modules = modules, Uri = new Uri(_absoluteUri, UriKind.Absolute), QueryString = querystring, + UrlParameters = urlparameters, ModuleId = moduleid, Action = action, EditMode = editmode, @@ -512,4 +553,4 @@ => RuntimeInformation.IsOSPlatform(OSPlatform.Create("BROWSER")) ? Runtime.WebAssembly : Runtime.Server; -} +} \ No newline at end of file diff --git a/Oqtane.Shared/Shared/Constants.cs b/Oqtane.Shared/Shared/Constants.cs index 0ce9743b..77f7dc68 100644 --- a/Oqtane.Shared/Shared/Constants.cs +++ b/Oqtane.Shared/Shared/Constants.cs @@ -19,6 +19,8 @@ namespace Oqtane.Shared public const string ActionToken = "{Action}"; public const string DefaultAction = "Index"; public const string AdminPane = "Admin"; + public const string ModuleDelimiter = "*"; + public const string UrlParametersDelimiter = "!"; // Default Module Actions are reserved and should not be used by modules public static readonly string[] DefaultModuleActions = new[] { "Settings", "Import", "Export" }; @@ -46,6 +48,7 @@ namespace Oqtane.Shared public const string ImageFiles = "jpg,jpeg,jpe,gif,bmp,png"; public const string UploadableFiles = "jpg,jpeg,jpe,gif,bmp,png,mov,wmv,avi,mp4,mp3,doc,docx,xls,xlsx,ppt,pptx,pdf,txt,zip,nupkg"; 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$"; + public static readonly char[] InvalidFileNameChars = { '\"', '<', '>', '|', '\0', (Char) 1, (Char) 2, (Char) 3, (Char) 4, (Char) 5, (Char) 6, (Char) 7, (Char) 8, @@ -55,4 +58,4 @@ namespace Oqtane.Shared }; public static readonly string[] InvalidFileNameEndingChars = { ".", " " }; } -} +} \ No newline at end of file diff --git a/Oqtane.Shared/Shared/Utilities.cs b/Oqtane.Shared/Shared/Utilities.cs index 04f3ea1e..ea36861b 100644 --- a/Oqtane.Shared/Shared/Utilities.cs +++ b/Oqtane.Shared/Shared/Utilities.cs @@ -20,8 +20,53 @@ namespace Oqtane.Shared return $"{type.Namespace}, {assemblyName}"; } + public static (string UrlParameters, string Querystring, string Anchor) ParseParameters(string parameters) + { + // /urlparameters /urlparameters?Id=1 /urlparameters#5 /urlparameters?Id=1#5 /urlparameters?reload#5 + + // Id=1 Id=1#5 reload#5 reload + + // #5 + + var urlparameters = string.Empty; + var querystring = string.Empty; + var anchor = string.Empty; + + if (parameters.Contains('#')) + { + anchor = parameters.Split('#').Last(); + parameters = parameters.Replace("#" + anchor, ""); + } + + if (parameters.Contains('?')) + { + urlparameters = parameters.Split('?').First(); + querystring = parameters.Replace(urlparameters + "?", ""); + } + else if (parameters.Contains('/')) + { + urlparameters = parameters; + } + else + { + querystring = parameters; + } + + return (urlparameters, querystring, anchor); + } + public static string NavigateUrl(string alias, string path, string parameters) { + string urlparameters; + string querystring; + string anchor; + (urlparameters, querystring, anchor) = ParseParameters(parameters); + + if (!string.IsNullOrEmpty(urlparameters)) + { + if (urlparameters.StartsWith("/")) urlparameters = urlparameters.Remove(0, 1); + path += $"/{Constants.UrlParametersDelimiter}/{urlparameters}"; + } var uriBuilder = new UriBuilder { Path = !string.IsNullOrEmpty(alias) @@ -29,17 +74,18 @@ namespace Oqtane.Shared ? $"{alias}/{path}" : $"{alias}" : $"{path}", - Query = parameters + Query = querystring, }; - - return uriBuilder.Uri.PathAndQuery; + anchor = string.IsNullOrEmpty(anchor) ? "" : "#" + anchor; + var navigateUrl = uriBuilder.Uri.PathAndQuery + anchor; + return navigateUrl; } public static string EditUrl(string alias, string path, int moduleid, string action, string parameters) { if (moduleid != -1) { - path += $"/{moduleid}"; + path += $"/{Constants.ModuleDelimiter}/{moduleid}"; if (!string.IsNullOrEmpty(action)) { path += $"/{action}"; @@ -136,6 +182,7 @@ namespace Oqtane.Shared stringBuilder.Append(RemapInternationalCharToAscii(c)); prevdash = false; break; + case UnicodeCategory.SpaceSeparator: case UnicodeCategory.ConnectorPunctuation: case UnicodeCategory.DashPunctuation: @@ -250,7 +297,7 @@ namespace Oqtane.Shared public static string PathCombine(params string[] segments) { - var separators = new char[] {Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar}; + var separators = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; for (int i = 1; i < segments.Length; i++) { @@ -284,7 +331,6 @@ namespace Oqtane.Shared !Constants.ReservedDevices.Split(',').Contains(name.ToUpper().Split('.')[0])); } - public static bool TryGetQueryValue( this Uri uri, string key, @@ -304,7 +350,7 @@ namespace Oqtane.Shared { value = defaultValue; string s; - return uri.TryGetQueryValue(key, out s, (string) null) && int.TryParse(s, out value); + return uri.TryGetQueryValue(key, out s, (string)null) && int.TryParse(s, out value); } public static Dictionary ParseQueryString(string query) @@ -314,7 +360,7 @@ namespace Oqtane.Shared { query = query.Substring(1); string str = query; - char[] separator = new char[1] {'&'}; + char[] separator = new char[1] { '&' }; foreach (string key in str.Split(separator, StringSplitOptions.RemoveEmptyEntries)) { if (key != "") @@ -333,4 +379,4 @@ namespace Oqtane.Shared return dictionary; } } -} +} \ No newline at end of file diff --git a/README.md b/README.md index ec9fb2c7..180db82e 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ Control panel for adding, editing, and deleting pages as well as adding new modu ![Manage Page](https://github.com/oqtane/framework/blob/master/screenshots/screenshot5.png?raw=true "Manage Page") -Admin dashboard for accessing the variuous administrative features of the framework: +Admin dashboard for accessing the various administrative features of the framework: ![Admin Dashboard](https://github.com/oqtane/framework/blob/master/screenshots/screenshot6.png?raw=true "Admin Dashboard")