diff --git a/Oqtane.Client/Modules/Admin/Logs/Detail.razor b/Oqtane.Client/Modules/Admin/Logs/Detail.razor
index 7df9838f..71f26397 100644
--- a/Oqtane.Client/Modules/Admin/Logs/Detail.razor
+++ b/Oqtane.Client/Modules/Admin/Logs/Detail.razor
@@ -130,13 +130,14 @@
private string _properties = string.Empty;
private string _server = string.Empty;
- public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
+ public override string RouteTemplate => "/{id}";
+ public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override async Task OnInitializedAsync()
{
try
{
- _logId = Int32.Parse(PageState.QueryString["id"]);
+ _logId = Int32.Parse(UrlParameters["id"]);
var log = await LogService.GetLogAsync(_logId);
if (log != null)
{
diff --git a/Oqtane.Client/Modules/Admin/Logs/Index.razor b/Oqtane.Client/Modules/Admin/Logs/Index.razor
index e8ed4717..49a8e0a9 100644
--- a/Oqtane.Client/Modules/Admin/Logs/Index.razor
+++ b/Oqtane.Client/Modules/Admin/Logs/Index.razor
@@ -63,7 +63,7 @@ else
@Localizer["Function"] |
- |
+ |
@context.LogDate |
@context.Level |
@context.Feature |
@@ -99,29 +99,32 @@ else
private List _logs;
private string _retention = "";
+ public override string RouteTemplate => "/{level}/{function}/{rows}/{page}";
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Host;
protected override async Task OnInitializedAsync()
{
try
{
+ // external link to log item will display Details component
if (PageState.QueryString.ContainsKey("id") && int.TryParse(PageState.QueryString["id"], out int id))
{
NavigationManager.NavigateTo(EditUrl(PageState.Page.Path, ModuleState.ModuleId, "Detail", $"id={id}"));
}
- if (PageState.QueryString.ContainsKey("level"))
+
+ if (UrlParameters.ContainsKey("level"))
{
- _level = PageState.QueryString["level"];
+ _level = UrlParameters["level"];
}
- if (PageState.QueryString.ContainsKey("function"))
+ if (UrlParameters.ContainsKey("function"))
{
- _function = PageState.QueryString["function"];
+ _function = UrlParameters["function"];
}
- if (PageState.QueryString.ContainsKey("rows"))
+ if (UrlParameters.ContainsKey("rows"))
{
- _rows = PageState.QueryString["rows"];
+ _rows = UrlParameters["rows"];
}
- if (PageState.QueryString.ContainsKey("page") && int.TryParse(PageState.QueryString["page"], out int page))
+ if (UrlParameters.ContainsKey("page") && int.TryParse(UrlParameters["page"], out int page))
{
_page = page;
}
diff --git a/Oqtane.Client/Modules/Controls/ActionLink.razor b/Oqtane.Client/Modules/Controls/ActionLink.razor
index a74c0d8a..6f3a29bf 100644
--- a/Oqtane.Client/Modules/Controls/ActionLink.razor
+++ b/Oqtane.Client/Modules/Controls/ActionLink.razor
@@ -121,12 +121,12 @@
_permissions = (string.IsNullOrEmpty(Permissions)) ? ModuleState.Permissions : Permissions;
_text = Localize(nameof(Text), _text);
+ _url = (ModuleId == -1) ? EditUrl(Action, _parameters) : EditUrl(ModuleId, Action, _parameters);
if (!string.IsNullOrEmpty(ReturnUrl))
{
- _parameters += ((!string.IsNullOrEmpty(_parameters)) ? "&" : "") + $"returnurl={WebUtility.UrlEncode(ReturnUrl)}";
+ _url += ((_url.Contains("?")) ? "&" : "?") + $"returnurl={WebUtility.UrlEncode(ReturnUrl)}";
}
- _url = (ModuleId == -1) ? EditUrl(Action, _parameters) : EditUrl(ModuleId, Action, _parameters);
- _authorized = IsAuthorized();
+ _authorized = IsAuthorized();
}
private bool IsAuthorized()
diff --git a/Oqtane.Client/Modules/ModuleBase.cs b/Oqtane.Client/Modules/ModuleBase.cs
index ab1cb528..3005dbe9 100644
--- a/Oqtane.Client/Modules/ModuleBase.cs
+++ b/Oqtane.Client/Modules/ModuleBase.cs
@@ -15,6 +15,7 @@ namespace Oqtane.Modules
public abstract class ModuleBase : ComponentBase, IModuleControl
{
private Logger _logger;
+ private Dictionary _urlparameters;
protected Logger logger => _logger ?? (_logger = new Logger(this));
@@ -47,6 +48,20 @@ namespace Oqtane.Modules
public virtual List Resources { get; set; }
+ // url parameters
+ public virtual string RouteTemplate { get; set; }
+
+ public Dictionary UrlParameters {
+ get
+ {
+ if (_urlparameters == null)
+ {
+ _urlparameters = GetUrlParameters(RouteTemplate);
+ }
+ return _urlparameters;
+ }
+ }
+
// base lifecycle method for handling JSInterop script registration
protected override async Task OnAfterRenderAsync(bool firstRender)
@@ -153,31 +168,26 @@ namespace Oqtane.Modules
return Utilities.ImageUrl(PageState.Alias, fileid, width, height, mode, position, background, rotate, recreate);
}
- public string AddUrlParameters(params string[] parameters)
+ public string AddUrlParameters(params object[] parameters)
{
- return AddUrlParameters(PageState.Page.Path, parameters);
- }
-
- public string AddUrlParameters(string path, params string[] parameters)
- {
- var url = path + "/" + Constants.UrlParametersDelimiter;
+ var url = "";
for (var i = 0; i < parameters.Length; i++)
{
- url += "/" + parameters[i];
+ url += "/" + parameters[i].ToString();
}
return url;
}
- // parameters template is in the form of a standard route template ie. "{id}/{name}"
- public virtual Dictionary GetUrlParameters(string parametersTemplate = "")
+ // template is in the form of a standard route template ie. "/{id}/{name}" and produces dictionary of key/value pairs
+ // if url parameters belong to a specific module you should embed a unique key into the route (ie. /!/blog/1) and validate the url parameter key in the module
+ public virtual Dictionary GetUrlParameters(string template = "")
{
var urlParameters = new Dictionary();
- string[] templateSegments;
var parameters = PageState.UrlParameters.Split('/', StringSplitOptions.RemoveEmptyEntries);
- var parameterId = 0;
- if (string.IsNullOrEmpty(parametersTemplate))
+ if (string.IsNullOrEmpty(template))
{
+ // no template will populate dictionary with generic "parameter#" keys
for (int i = 0; i < parameters.Length; i++)
{
urlParameters.TryAdd("parameter" + i, parameters[i]);
@@ -185,32 +195,30 @@ namespace Oqtane.Modules
}
else
{
- templateSegments = parametersTemplate.Split('/', StringSplitOptions.RemoveEmptyEntries);
+ var segments = template.Split('/', StringSplitOptions.RemoveEmptyEntries);
+ string key;
- if (parameters.Length == templateSegments.Length)
+ for (int i = 0; i < parameters.Length; i++)
{
- for (int i = 0; i < parameters.Length; i++)
+ if (i < segments.Length)
{
- if (parameters.Length > i)
+ key = segments[i];
+ if (key.StartsWith("{") && key.EndsWith("}"))
{
- if (templateSegments[i] == parameters[i])
- {
- urlParameters.TryAdd("parameter" + parameterId, parameters[i]);
- parameterId++;
- }
- 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;
- urlParameters.Clear();
- }
+ // dynamic segment
+ key = key.Substring(1, key.Length - 2);
+ }
+ else
+ {
+ // static segments use generic "parameter#" keys
+ key = "parameter" + i.ToString();
}
}
+ else // unspecified segments use generic "parameter#" keys
+ {
+ key = "parameter" + i.ToString();
+ }
+ urlParameters.TryAdd(key, parameters[i]);
}
}
diff --git a/Oqtane.Shared/Models/Route.cs b/Oqtane.Shared/Models/Route.cs
index bff8f17f..9b4cc683 100644
--- a/Oqtane.Shared/Models/Route.cs
+++ b/Oqtane.Shared/Models/Route.cs
@@ -38,13 +38,13 @@ namespace Oqtane.Models
if (pos != -1)
{
UrlParameters = PagePath.Substring(pos + 3);
- PagePath = PagePath.Substring(1, pos);
+ PagePath = PagePath.Substring(0, pos);
}
pos = PagePath.IndexOf("/" + Constants.ModuleDelimiter + "/");
if (pos != -1)
{
ModuleId = PagePath.Substring(pos + 3);
- PagePath = PagePath.Substring(1, pos);
+ PagePath = PagePath.Substring(0, pos);
}
if (ModuleId.Length != 0)
{