diff --git a/Oqtane.Server/Controllers/ModuleDefinitionController.cs b/Oqtane.Server/Controllers/ModuleDefinitionController.cs
index 4fbea7eb..39ae9f48 100644
--- a/Oqtane.Server/Controllers/ModuleDefinitionController.cs
+++ b/Oqtane.Server/Controllers/ModuleDefinitionController.cs
@@ -16,6 +16,7 @@ using Microsoft.Extensions.DependencyInjection;
using System.Text.Json;
using System.Net;
using Oqtane.Modules;
+using Oqtane.Infrastructure.Interfaces;
namespace Oqtane.Controllers
{
@@ -319,52 +320,33 @@ namespace Oqtane.Controllers
private void ProcessTemplatesRecursively(DirectoryInfo current, string rootPath, string rootFolder, string templatePath, ModuleDefinition moduleDefinition)
{
+ var tokenReplace = InitializeTokenReplace(rootPath, rootFolder, moduleDefinition);
+
// process folder
- string folderPath = Utilities.PathCombine(rootPath, current.FullName.Replace(templatePath, ""));
- folderPath = folderPath.Replace("[Owner]", moduleDefinition.Owner);
- folderPath = folderPath.Replace("[Module]", moduleDefinition.Name);
+ var folderPath = Utilities.PathCombine(rootPath, current.FullName.Replace(templatePath, ""));
+ folderPath = tokenReplace.ReplaceTokens(folderPath);
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
- FileInfo[] files = current.GetFiles("*.*");
+ tokenReplace.AddSource("Folder", folderPath);
+ var files = current.GetFiles("*.*");
if (files != null)
{
foreach (FileInfo file in files)
{
// process file
- string filePath = Path.Combine(folderPath, file.Name);
- filePath = filePath.Replace("[Owner]", moduleDefinition.Owner);
- filePath = filePath.Replace("[Module]", moduleDefinition.Name);
+ var filePath = Path.Combine(folderPath, file.Name);
+ filePath = tokenReplace.ReplaceTokens(filePath);
+ tokenReplace.AddSource("File", Path.GetFileName(filePath));
- string text = System.IO.File.ReadAllText(file.FullName);
- text = text.Replace("[Owner]", moduleDefinition.Owner);
- text = text.Replace("[Module]", moduleDefinition.Name);
- text = text.Replace("[Description]", moduleDefinition.Description);
- text = text.Replace("[RootPath]", rootPath);
- text = text.Replace("[RootFolder]", rootFolder);
- text = text.Replace("[ServerManagerType]", moduleDefinition.ServerManagerType);
- text = text.Replace("[Folder]", folderPath);
- text = text.Replace("[File]", Path.GetFileName(filePath));
- if (moduleDefinition.Version == "local")
- {
- text = text.Replace("[FrameworkVersion]", Constants.Version);
- text = text.Replace("[ClientReference]", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Client.dll");
- text = text.Replace("[ServerReference]", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Server.dll");
- text = text.Replace("[SharedReference]", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Shared.dll");
- }
- else
- {
- text = text.Replace("[FrameworkVersion]", moduleDefinition.Version);
- text = text.Replace("[ClientReference]", "");
- text = text.Replace("[ServerReference]", "");
- text = text.Replace("[SharedReference]", "");
- }
+ var text = System.IO.File.ReadAllText(file.FullName);
+ text = tokenReplace.ReplaceTokens(text);
System.IO.File.WriteAllText(filePath, text);
}
- DirectoryInfo[] folders = current.GetDirectories();
+ var folders = current.GetDirectories();
foreach (DirectoryInfo folder in folders.Reverse())
{
@@ -372,5 +354,51 @@ namespace Oqtane.Controllers
}
}
}
+
+ private ITokenReplace InitializeTokenReplace(string rootPath, string rootFolder, ModuleDefinition moduleDefinition)
+ {
+ var tokenReplace = _serviceProvider.GetService();
+ tokenReplace.AddSource(() =>
+ {
+ return new Dictionary
+ {
+ { "RootPath", rootPath },
+ { "RootFolder", rootFolder },
+ { "Owner", moduleDefinition.Owner },
+ { "Module", moduleDefinition.Name },
+ { "Description", moduleDefinition.Description },
+ { "ServerManagerType", moduleDefinition.ServerManagerType }
+ };
+ });
+
+ if (moduleDefinition.Version == "local")
+ {
+ tokenReplace.AddSource(() =>
+ {
+ return new Dictionary()
+ {
+ { "FrameworkVersion", Constants.Version },
+ { "ClientReference", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Client.dll" },
+ { "ServerReference", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Server.dll" },
+ { "SharedReference", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Shared.dll" },
+ };
+ });
+ }
+ else
+ {
+ tokenReplace.AddSource(() =>
+ {
+ return new Dictionary()
+ {
+ { "FrameworkVersion", moduleDefinition.Version },
+ { "ClientReference", $"" },
+ { "ServerReference", $"" },
+ { "SharedReference", $"" },
+ };
+ });
+ }
+
+ return tokenReplace;
+ }
}
}
diff --git a/Oqtane.Server/Controllers/ThemeController.cs b/Oqtane.Server/Controllers/ThemeController.cs
index 40067fa3..f871d1d7 100644
--- a/Oqtane.Server/Controllers/ThemeController.cs
+++ b/Oqtane.Server/Controllers/ThemeController.cs
@@ -14,6 +14,8 @@ using System.Text.Json;
using System.Net;
using System.Reflection.Metadata;
using System;
+using Microsoft.Extensions.DependencyInjection;
+using Oqtane.Infrastructure.Interfaces;
// ReSharper disable StringIndexOfIsCultureSpecific.1
@@ -29,8 +31,9 @@ namespace Oqtane.Controllers
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
private readonly Alias _alias;
+ private readonly IServiceProvider _serviceProvider;
- public ThemeController(IThemeRepository themes, IInstallationManager installationManager, IWebHostEnvironment environment, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
+ public ThemeController(IThemeRepository themes, IInstallationManager installationManager, IWebHostEnvironment environment, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger, IServiceProvider serviceProvider)
{
_themes = themes;
_installationManager = installationManager;
@@ -39,6 +42,7 @@ namespace Oqtane.Controllers
_syncManager = syncManager;
_logger = logger;
_alias = tenantManager.GetAlias();
+ _serviceProvider = serviceProvider;
}
// GET: api/
@@ -208,54 +212,80 @@ namespace Oqtane.Controllers
private void ProcessTemplatesRecursively(DirectoryInfo current, string rootPath, string rootFolder, string templatePath, Theme theme)
{
+ var tokenReplace = InitializeTokenReplace(rootPath, rootFolder, theme);
+
// process folder
- string folderPath = Utilities.PathCombine(rootPath, current.FullName.Replace(templatePath, ""));
- folderPath = folderPath.Replace("[Owner]", theme.Owner);
- folderPath = folderPath.Replace("[Theme]", theme.Name);
+ var folderPath = Utilities.PathCombine(rootPath, current.FullName.Replace(templatePath, ""));
+ folderPath = tokenReplace.ReplaceTokens(folderPath);
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
- FileInfo[] files = current.GetFiles("*.*");
+ tokenReplace.AddSource("Folder", folderPath);
+ var files = current.GetFiles("*.*");
if (files != null)
{
foreach (FileInfo file in files)
{
// process file
- string filePath = Path.Combine(folderPath, file.Name);
- filePath = filePath.Replace("[Owner]", theme.Owner);
- filePath = filePath.Replace("[Theme]", theme.Name);
+ var filePath = Path.Combine(folderPath, file.Name);
+ filePath = tokenReplace.ReplaceTokens(filePath);
+ tokenReplace.AddSource("File", Path.GetFileName(filePath));
- string text = System.IO.File.ReadAllText(file.FullName);
- text = text.Replace("[Owner]", theme.Owner);
- text = text.Replace("[Theme]", theme.Name);
- text = text.Replace("[RootPath]", rootPath);
- text = text.Replace("[RootFolder]", rootFolder);
- text = text.Replace("[Folder]", folderPath);
- text = text.Replace("[File]", Path.GetFileName(filePath));
- if (theme.Version == "local")
- {
- text = text.Replace("[FrameworkVersion]", Constants.Version);
- text = text.Replace("[ClientReference]", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Client.dll");
- text = text.Replace("[SharedReference]", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Shared.dll");
- }
- else
- {
- text = text.Replace("[FrameworkVersion]", theme.Version);
- text = text.Replace("[ClientReference]", "");
- text = text.Replace("[SharedReference]", "");
- }
+ var text = System.IO.File.ReadAllText(file.FullName);
+ text = tokenReplace.ReplaceTokens(text);
System.IO.File.WriteAllText(filePath, text);
}
- DirectoryInfo[] folders = current.GetDirectories();
-
+ var folders = current.GetDirectories();
foreach (DirectoryInfo folder in folders.Reverse())
{
ProcessTemplatesRecursively(folder, rootPath, rootFolder, templatePath, theme);
}
}
}
+
+ private ITokenReplace InitializeTokenReplace(string rootPath, string rootFolder, Theme theme)
+ {
+ var tokenReplace = _serviceProvider.GetService();
+ tokenReplace.AddSource(() =>
+ {
+ return new Dictionary
+ {
+ { "RootPath", rootPath },
+ { "RootFolder", rootFolder },
+ { "Owner", theme.Owner },
+ { "Theme", theme.Name }
+ };
+ });
+
+ if (theme.Version == "local")
+ {
+ tokenReplace.AddSource(() =>
+ {
+ return new Dictionary()
+ {
+ { "FrameworkVersion", Constants.Version },
+ { "ClientReference", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Client.dll" },
+ { "SharedReference", $"..\\..\\{rootFolder}\\Oqtane.Server\\bin\\Debug\\net8.0\\Oqtane.Shared.dll" },
+ };
+ });
+ }
+ else
+ {
+ tokenReplace.AddSource(() =>
+ {
+ return new Dictionary()
+ {
+ { "FrameworkVersion", theme.Version },
+ { "ClientReference", $"" },
+ { "SharedReference", $"" },
+ };
+ });
+ }
+
+ return tokenReplace;
+ }
}
}
diff --git a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs
index dc742cbf..a3324e49 100644
--- a/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs
+++ b/Oqtane.Server/Extensions/OqtaneServiceCollectionExtensions.cs
@@ -19,6 +19,7 @@ using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using Oqtane.Infrastructure;
+using Oqtane.Infrastructure.Interfaces;
using Oqtane.Managers;
using Oqtane.Models;
using Oqtane.Modules;
@@ -150,6 +151,8 @@ namespace Microsoft.Extensions.DependencyInjection
// obsolete - replaced by ITenantManager
services.AddTransient();
+ services.AddTransient();
+
return services;
}
diff --git a/Oqtane.Server/Infrastructure/Interfaces/ITokenReplace.cs b/Oqtane.Server/Infrastructure/Interfaces/ITokenReplace.cs
new file mode 100644
index 00000000..ff934a17
--- /dev/null
+++ b/Oqtane.Server/Infrastructure/Interfaces/ITokenReplace.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using Oqtane.Interfaces;
+
+namespace Oqtane.Infrastructure.Interfaces
+{
+ public interface ITokenReplace
+ {
+ void AddSource(ITokenSource source);
+
+ void AddSource(Func> sourceFunc);
+
+ void AddSource(IDictionary source);
+
+ void AddSource(string key, object value);
+
+ void AddSource(string name, ITokenSource source);
+
+ void AddSource(string name, Func> sourceFunc);
+
+ void AddSource(string name, IDictionary source);
+
+ void AddSource(string name, string key, object value);
+
+ string ReplaceTokens(string source);
+ }
+}
diff --git a/Oqtane.Server/Infrastructure/TokenReplace.cs b/Oqtane.Server/Infrastructure/TokenReplace.cs
new file mode 100644
index 00000000..3c7e8a8c
--- /dev/null
+++ b/Oqtane.Server/Infrastructure/TokenReplace.cs
@@ -0,0 +1,158 @@
+using System;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using System.Text;
+using Oqtane.Infrastructure.Interfaces;
+using Oqtane.Interfaces;
+using Oqtane.Models;
+
+namespace Oqtane.Infrastructure
+{
+ public class TokenReplace : ITokenReplace
+ {
+ public const string GenericName = "generic";
+
+ private const string TokenExpression = "(?:(?\\[\\])|\\[(?:(?[^{}\\]\\[:]+):(?[^\\]\\[\\|]+)|(?[^\\]\\[\\|]+))(?:\\|(?:(?[^\\]\\[]+)\\|(?[^\\]\\\\[]+))|\\|(?:(?[^\\|\\]\\[]+)))?\\])|(?\\[[^\\]\\[]+\\])|(?\\[{0,1}[^\\]\\[]+\\]{0,1})";
+
+ private Regex TokenizerRegex = new Regex(TokenExpression, RegexOptions.Compiled | RegexOptions.Singleline);
+ private IDictionary> _tokens;
+
+ private readonly ILogManager _logger;
+
+ public TokenReplace(ILogManager logger)
+ {
+ _tokens = new Dictionary>();
+ _logger = logger;
+ }
+
+ public void AddSource(ITokenSource source)
+ {
+ this.AddSource(GenericName, source);
+ }
+
+ public void AddSource(Func> sourceFunc)
+ {
+ this.AddSource(GenericName, sourceFunc);
+ }
+
+ public void AddSource(IDictionary source)
+ {
+ this.AddSource(GenericName, source);
+ }
+
+ public void AddSource(string key, object value)
+ {
+ this.AddSource(GenericName, key, value);
+ }
+
+ public void AddSource(string name, ITokenSource source)
+ {
+ var tokens = source.GetTokens();
+ this.AddSource(name, tokens);
+ }
+
+ public void AddSource(string name, Func> sourceFunc)
+ {
+ var tokens = sourceFunc();
+ this.AddSource(name, tokens);
+ }
+
+ public void AddSource(string name, IDictionary source)
+ {
+ if(source != null)
+ {
+ foreach (var key in source.Keys)
+ {
+ this.AddSource(name, key, source[key]);
+ }
+ }
+ }
+
+ public void AddSource(string name, string key, object value)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ name = GenericName;
+ }
+
+ var source = _tokens.ContainsKey(name.ToLower()) ? _tokens[name.ToLower()] : null;
+ if(source == null)
+ {
+ source = new Dictionary();
+ }
+ source[key] = value;
+
+ _tokens[name.ToLower()] = source;
+ }
+
+ public string ReplaceTokens(string source)
+ {
+ if (string.IsNullOrWhiteSpace(source))
+ {
+ return source;
+ }
+
+ var result = new StringBuilder();
+ foreach (Match match in this.TokenizerRegex.Matches(source))
+ {
+ var key = match.Result("${key}");
+ if (!string.IsNullOrWhiteSpace(key))
+ {
+ var sourceName = match.Result("${source}");
+ if (string.IsNullOrWhiteSpace(sourceName) || sourceName == "[")
+ {
+ sourceName = GenericName;
+ }
+
+ var format = match.Result("${format}");
+ var emptyReplacment = match.Result("${empty}");
+ var value = ReplaceTokenValue(sourceName, key, format);
+ if (string.IsNullOrWhiteSpace(value))
+ {
+ if(!string.IsNullOrWhiteSpace(emptyReplacment))
+ {
+ value = emptyReplacment;
+ }
+ else //keep the original content
+ {
+ value = match.Value;
+ }
+ }
+
+ result.Append(value);
+ }
+ else
+ {
+ result.Append(match.Result("${text}"));
+ }
+ }
+
+ return result.ToString();
+ }
+
+ private string ReplaceTokenValue(string sourceName, string key, string format)
+ {
+ if(!_tokens.ContainsKey(sourceName.ToLower()))
+ {
+ _logger.Log(Shared.LogLevel.Debug, this, Enums.LogFunction.Other, $"MissingSource:{sourceName}");
+ return string.Empty;
+ }
+
+ var tokens = _tokens[sourceName.ToLower()];
+ if(!tokens.ContainsKey(key))
+ {
+ _logger.Log(Shared.LogLevel.Debug, this, Enums.LogFunction.Other, $"MissingKey:{key}");
+ return string.Empty;
+ }
+
+ var value = tokens[key];
+ if(value == null)
+ {
+ return string.Empty;
+ }
+
+ //TODO: need to implement the format.
+ return value.ToString();
+ }
+ }
+}
diff --git a/Oqtane.Shared/Interfaces/ITokenSource.cs b/Oqtane.Shared/Interfaces/ITokenSource.cs
new file mode 100644
index 00000000..46fe7f42
--- /dev/null
+++ b/Oqtane.Shared/Interfaces/ITokenSource.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Oqtane.Interfaces
+{
+ public interface ITokenSource
+ {
+ IDictionary GetTokens();
+ }
+}