123123
This commit is contained in:
@@ -47,7 +47,8 @@ namespace DummyPageGenerator
|
||||
{
|
||||
for (int i = 0; i < 1124 - _users.Count; i++)
|
||||
{
|
||||
string emailAddress = WordsRepository.GetRandomWords(1).First() + "@" + WordsRepository.GetRandomWords(1).First() + ".com";
|
||||
string emailAddress = WordsRepository.GetRandomWords(1).First() + "@" +
|
||||
WordsRepository.GetRandomWords(1).First() + ".com";
|
||||
CreateUserAndProfile(emailAddress);
|
||||
}
|
||||
|
||||
@@ -68,7 +69,7 @@ namespace DummyPageGenerator
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a user and the associated profile with claims and such.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="emailAddress"></param>
|
||||
/// <exception cref="Exception"></exception>
|
||||
@@ -80,7 +81,8 @@ namespace DummyPageGenerator
|
||||
Email = emailAddress
|
||||
};
|
||||
|
||||
var result = _userManager.CreateAsync(user, WordsRepository.GetRandomWords(1).First() + Guid.NewGuid().ToString()).Result;
|
||||
var result = _userManager
|
||||
.CreateAsync(user, WordsRepository.GetRandomWords(1).First() + Guid.NewGuid().ToString()).Result;
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
throw new Exception(string.Join("\r\n", result.Errors.Select(o => o.Description)));
|
||||
@@ -93,17 +95,17 @@ namespace DummyPageGenerator
|
||||
|
||||
var claimsToAdd = new List<Claim>
|
||||
{
|
||||
new (ClaimTypes.Role, membershipConfig.Value<string>("Default Signup Role").EnsureNotNull()),
|
||||
new ("timezone", membershipConfig.Value<string>("Default TimeZone").EnsureNotNull()),
|
||||
new (ClaimTypes.Country, membershipConfig.Value<string>("Default Country").EnsureNotNull()),
|
||||
new ("language", membershipConfig.Value<string>("Default Language").EnsureNotNull()),
|
||||
new(ClaimTypes.Role, membershipConfig.Value<string>("Default Signup Role").EnsureNotNull()),
|
||||
new("timezone", membershipConfig.Value<string>("Default TimeZone").EnsureNotNull()),
|
||||
new(ClaimTypes.Country, membershipConfig.Value<string>("Default Country").EnsureNotNull()),
|
||||
new("language", membershipConfig.Value<string>("Default Language").EnsureNotNull()),
|
||||
};
|
||||
|
||||
SecurityRepository.UpsertUserClaims(_userManager, user, claimsToAdd);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a paragraph/sentence structure.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="words"></param>
|
||||
/// <returns></returns>
|
||||
@@ -118,7 +120,7 @@ namespace DummyPageGenerator
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a paragraph/sentence structure with links and wiki markup.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="wordCount"></param>
|
||||
/// <returns></returns>
|
||||
@@ -135,18 +137,19 @@ namespace DummyPageGenerator
|
||||
|
||||
switch (_random.Next(0, 7))
|
||||
{
|
||||
case 2: //Dead link.
|
||||
case 2:
|
||||
paragraph = paragraph.Replace(token, $"[[{token}]]");
|
||||
break;
|
||||
case 4: //Wiki markup.
|
||||
case 4:
|
||||
paragraph = paragraph.Replace(token, AddWikiMarkup(token));
|
||||
break;
|
||||
case 6: //Good link.
|
||||
case 6:
|
||||
var recentPage = GetRandomRecentPageName();
|
||||
if (recentPage != null)
|
||||
{
|
||||
paragraph = paragraph.Replace(token, $"[[{recentPage}]]");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -163,7 +166,7 @@ namespace DummyPageGenerator
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_recentPageNames.Count > 200) //Shuffle and limit the recent page names.
|
||||
if (_recentPageNames.Count > 200)
|
||||
{
|
||||
_recentPageNames = ShuffleList(_recentPageNames).Take(100).ToList();
|
||||
}
|
||||
@@ -176,7 +179,7 @@ namespace DummyPageGenerator
|
||||
{
|
||||
lock (_pagePool)
|
||||
{
|
||||
if (_recentPageNames.Count > 200) //Shuffle and limit the recent page names.
|
||||
if (_recentPageNames.Count > 200)
|
||||
{
|
||||
_recentPageNames = ShuffleList(_recentPageNames).Take(100).ToList();
|
||||
}
|
||||
@@ -186,6 +189,7 @@ namespace DummyPageGenerator
|
||||
{
|
||||
pageNames.Add(_recentPageNames[_random.Next(0, _recentPageNames.Count)]);
|
||||
}
|
||||
|
||||
return pageNames;
|
||||
}
|
||||
}
|
||||
@@ -199,8 +203,9 @@ namespace DummyPageGenerator
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a random page on the wiki.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="engine"></param>
|
||||
/// <param name="userId"></param>
|
||||
public void GeneratePage(IZelEngine engine, Guid userId)
|
||||
{
|
||||
@@ -216,7 +221,8 @@ namespace DummyPageGenerator
|
||||
|
||||
var body = new StringBuilder();
|
||||
|
||||
body.AppendLine($"##title ##Tag(" + string.Join(' ', ShuffleList(_tags).Take(_random.Next(1, 4))) + ")");
|
||||
body.AppendLine($"##title ##Tag(" + string.Join(' ', ShuffleList(_tags).Take(_random.Next(1, 4))) +
|
||||
")");
|
||||
body.AppendLine($"##toc");
|
||||
|
||||
body.AppendLine($"==Overview");
|
||||
@@ -232,7 +238,6 @@ namespace DummyPageGenerator
|
||||
|
||||
if (_random.Next(100) >= 95)
|
||||
{
|
||||
//Add dead links (missing pages).
|
||||
textWithLinks.AddRange(WordsRepository.GetRandomWords(_random.Next(1, 2)).Select(o => $"[[{o}]]"));
|
||||
}
|
||||
|
||||
@@ -258,7 +263,8 @@ namespace DummyPageGenerator
|
||||
|
||||
if (_random.Next(100) >= 70)
|
||||
{
|
||||
var fileName = _fileNames[_random.Next(_fileNames.Count)] + ".txt"; ;
|
||||
var fileName = _fileNames[_random.Next(_fileNames.Count)] + ".txt";
|
||||
;
|
||||
var fileData = Encoding.UTF8.GetBytes(page.Body);
|
||||
AttachFile(newPageId, userId, fileName, fileData);
|
||||
}
|
||||
@@ -288,7 +294,7 @@ namespace DummyPageGenerator
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies a random page on the wiki.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
public void ModifyRandomPages(IZelEngine engine, Guid userId)
|
||||
@@ -328,12 +334,13 @@ namespace DummyPageGenerator
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attaches a file to a wiki page.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="pageId"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="fileName"></param>
|
||||
/// <param name="fileData"></param>
|
||||
/// <exception cref="Exception"></exception>
|
||||
private void AttachFile(int pageId, Guid userId, string fileName, byte[] fileData)
|
||||
{
|
||||
if (fileData.Length > GlobalConfiguration.MaxAttachmentFileSize)
|
||||
@@ -372,6 +379,7 @@ namespace DummyPageGenerator
|
||||
newList[k] = newList[n];
|
||||
newList[n] = value;
|
||||
}
|
||||
|
||||
return newList;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@ using System.Runtime.Caching;
|
||||
|
||||
namespace ZelWiki.Caching
|
||||
{
|
||||
/// <summary>
|
||||
/// 缓存
|
||||
/// </summary>
|
||||
public class WikiCache
|
||||
{
|
||||
public enum Category
|
||||
@@ -24,7 +27,7 @@ namespace ZelWiki.Caching
|
||||
public static int CacheItemCount => MemCache.Count();
|
||||
public static double CacheMemoryLimit => MemCache.CacheMemoryLimit;
|
||||
|
||||
public static MemoryCache MemCache => _memCache ?? throw new Exception("Cache has not been initialized.");
|
||||
public static MemoryCache MemCache => _memCache ?? throw new Exception("缓存尚未初始化");
|
||||
|
||||
public static void Initialize(int cacheMemoryLimitMB, int defaultCacheSeconds)
|
||||
{
|
||||
@@ -40,7 +43,7 @@ namespace ZelWiki.Caching
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an item from the cache.
|
||||
/// 获取缓存
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="cacheKey"></param>
|
||||
@@ -60,9 +63,8 @@ namespace ZelWiki.Caching
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the cache contains a given key.
|
||||
/// 确定缓存是否包含给定的key
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <returns></returns>
|
||||
public static bool Contains(IWikiCacheKey cacheKey)
|
||||
@@ -79,7 +81,7 @@ namespace ZelWiki.Caching
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an item from the cache.
|
||||
/// 获取缓存
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="cacheKey"></param>
|
||||
@@ -98,7 +100,7 @@ namespace ZelWiki.Caching
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an item to the cache. If the item is already in the cache, this will reset its expiration.
|
||||
///添加缓存
|
||||
/// </summary>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="value"></param>
|
||||
@@ -127,7 +129,7 @@ namespace ZelWiki.Caching
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all entries from the cache.
|
||||
/// 清理
|
||||
/// </summary>
|
||||
public static void Clear()
|
||||
{
|
||||
@@ -144,9 +146,9 @@ namespace ZelWiki.Caching
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes cache entries that begin with the given cache key.
|
||||
/// 删除某个
|
||||
/// </summary>
|
||||
/// <param name="category"></param>
|
||||
/// <param name="cacheKey"></param>
|
||||
public static void ClearCategory(WikiCacheKey cacheKey)
|
||||
{
|
||||
var keys = new List<string>();
|
||||
@@ -163,7 +165,7 @@ namespace ZelWiki.Caching
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes cache entries in a given category.
|
||||
/// 删除给定类别中的缓存条目
|
||||
/// </summary>
|
||||
/// <param name="category"></param>
|
||||
public static void ClearCategory(Category category)
|
||||
|
||||
@@ -3,17 +3,17 @@
|
||||
namespace ZelWiki.Caching
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains a verbatim cache key.
|
||||
/// 包含逐字缓存键
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
public class WikiCacheKey(string key) : IWikiCacheKey
|
||||
{
|
||||
public string Key { get; set; } = key;
|
||||
|
||||
public static WikiCacheKey Build(WikiCache.Category category, object?[] segments)
|
||||
public static WikiCacheKey Build(Category category, object?[] segments)
|
||||
=> new($"[{category}]:[{string.Join("]:[", segments)}]");
|
||||
|
||||
public static WikiCacheKey Build(WikiCache.Category category)
|
||||
public static WikiCacheKey Build(Category category)
|
||||
=> new($"[{category}]");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,22 +3,18 @@ using static ZelWiki.Caching.WikiCache;
|
||||
|
||||
namespace ZelWiki.Caching
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains a verbatim cache key which also includes the calling function name.
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
public class WikiCacheKeyFunction(string key) : IWikiCacheKey
|
||||
{
|
||||
public string Key { get; set; } = key;
|
||||
|
||||
/// <summary>
|
||||
/// Builds a cache key which includes the calling function name.
|
||||
/// 生成一个包含调用函数名称的缓存键。
|
||||
/// </summary>
|
||||
public static WikiCacheKeyFunction Build(WikiCache.Category category, object?[] segments, [CallerMemberName] string callingFunction = "")
|
||||
=> new($"[{category}]:[{string.Join("]:[", segments)}]:[{callingFunction}]");
|
||||
|
||||
/// <summary>
|
||||
/// Builds a cache key which includes the calling function name.
|
||||
/// 生成一个包含调用函数名称的缓存键。
|
||||
/// </summary>
|
||||
public static WikiCacheKeyFunction Build(WikiCache.Category category, [CallerMemberName] string callingFunction = "")
|
||||
=> new($"[{category}]:[{callingFunction}]");
|
||||
|
||||
@@ -11,6 +11,10 @@ namespace ZelWiki.Email
|
||||
{
|
||||
private readonly ILogger<WikiEmailSender> _logger;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
public WikiEmailSender(ILogger<WikiEmailSender> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
namespace ZelWiki.Engine.Function
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information about an actual function call, its supplied parameters, and is matched with a defined function.
|
||||
/// 包含有关实际函数调用及其提供的参数的信息,并与定义的函数相匹配。
|
||||
/// </summary>
|
||||
public class FunctionCall
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the function being called.
|
||||
///
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
|
||||
public FunctionPrototype Prototype { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The arguments supplied by the caller.
|
||||
/// T
|
||||
/// </summary>
|
||||
public FunctionParameters Parameters { get; private set; }
|
||||
|
||||
@@ -25,7 +27,7 @@
|
||||
{
|
||||
if (arg.StartsWith(':') && arg.Contains('='))
|
||||
{
|
||||
var parsed = arg.Substring(1); //Skip the colon.
|
||||
var parsed = arg.Substring(1);
|
||||
int index = parsed.IndexOf('=');
|
||||
var name = parsed.Substring(0, index).Trim().ToLower();
|
||||
var value = parsed.Substring(index + 1).Trim();
|
||||
@@ -42,9 +44,9 @@
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks the passed value against the function prototype to ensure that the variable is the correct type, value, etc.
|
||||
/// 对照函数原型检查传递的值,以确保变量的类型,值等正确
|
||||
/// </summary>
|
||||
/// <param name="segment"></param>
|
||||
/// <param name="param"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <exception cref="Exception"></exception>
|
||||
private void EnforcePrototypeParamValue(PrototypeParameter param, string value)
|
||||
@@ -53,21 +55,25 @@
|
||||
{
|
||||
if (bool.TryParse(value, out bool _) == false)
|
||||
{
|
||||
throw new Exception($"Function [{Name}], the value [{value}] passed to parameter [{param.Name}] could not be converted to boolean.");
|
||||
throw new Exception(
|
||||
$"函数 [{Name}] 传递给 [{param.Name}] 的值 [{value}] 无法转化成布尔");
|
||||
}
|
||||
}
|
||||
|
||||
if (param.Type == "integer")
|
||||
{
|
||||
if (int.TryParse(value, out int _) == false)
|
||||
{
|
||||
throw new Exception($"Function [{Name}], the value [{value}] passed to parameter [{param.Name}] could not be converted to integer.");
|
||||
throw new Exception(
|
||||
$"函数 [{Name}] 传递给 [{param.Name}] 的值 [{value}] 无法转化成整数.");
|
||||
}
|
||||
}
|
||||
else if (param.Type == "float")
|
||||
{
|
||||
if (double.TryParse(value, out double _) == false)
|
||||
{
|
||||
throw new Exception($"Function [{Name}], the value [{value}] passed to parameter [{param.Name}] could not be converted to float.");
|
||||
throw new Exception(
|
||||
$"函数 [{Name}] 传递给 [{param.Name}] 的值 [{value}] 无法转化成小数.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,24 +81,22 @@
|
||||
{
|
||||
if (param.AllowedValues.Contains(value.ToLower()) == false)
|
||||
{
|
||||
throw new Exception($"Function [{Name}], the value [{value}] passed to parameter [{param.Name}] is not allowed. Allowed values are [{string.Join(",", param.AllowedValues)}].");
|
||||
throw new Exception(
|
||||
$"函数 [{Name}] 传递给 [{param.Name}] 的值 [{value}] 为非法数据. 合法值为 [{string.Join(",", param.AllowedValues)}].");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rolls through the supplied arguments and applies them to the prototype. Also identifies which supplied arguments are associated with each
|
||||
/// prototype argument and adds the ordinal based arguments to the name based collection. Ensures that each argument conforms with the prototype.
|
||||
///
|
||||
/// </summary>
|
||||
/// <exception cref="Exception"></exception>
|
||||
private void ApplyPrototype()
|
||||
{
|
||||
int index = 0;
|
||||
var index = 0;
|
||||
|
||||
//Keep a list of the arguments as they are associated with the prototype so that we can later reference them by name.
|
||||
var namedToAddLater = new List<NamedParameter>();
|
||||
|
||||
//Handle non-infinite ordinal based required parameters:
|
||||
for (; index < Prototype.Parameters.Count; index++)
|
||||
{
|
||||
var param = Prototype.Parameters[index];
|
||||
@@ -101,15 +105,15 @@
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (param.IsInfinite == true)
|
||||
|
||||
if (param.IsInfinite)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (Parameters.Ordinals.Count > index)
|
||||
{
|
||||
//Good, we have a value.
|
||||
string value = Parameters.Ordinals[index].Value;
|
||||
var value = Parameters.Ordinals[index].Value;
|
||||
Parameters.Ordinals[index].AssociateWithPrototypeParam(param.Name);
|
||||
EnforcePrototypeParamValue(param, value.ToLower());
|
||||
|
||||
@@ -123,33 +127,30 @@
|
||||
|
||||
bool hasEncounteredOptionalParameter = false;
|
||||
|
||||
//Handle remaining optional parameters:
|
||||
for (; index < Prototype.Parameters.Count; index++)
|
||||
{
|
||||
var param = Prototype.Parameters[index];
|
||||
|
||||
if (param.IsInfinite == true)
|
||||
if (param.IsInfinite)
|
||||
{
|
||||
if (param.IsRequired == true)
|
||||
if (param.IsRequired)
|
||||
{
|
||||
//Make sure we have at least one of these required infinite parameters passed.
|
||||
if (Parameters.Ordinals.Count > index)
|
||||
{
|
||||
//Good, we have a value.
|
||||
string value = Parameters.Ordinals[index].Value;
|
||||
var value = Parameters.Ordinals[index].Value;
|
||||
Parameters.Ordinals[index].AssociateWithPrototypeParam(param.Name);
|
||||
EnforcePrototypeParamValue(param, value.ToLower());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Function [{Name}], the required infinite parameter [{param.Name}] was not passed.");
|
||||
throw new Exception(
|
||||
$"函数 [{Name}], 参数 [{param.Name}] 未通过.");
|
||||
}
|
||||
}
|
||||
|
||||
//Now that we have encountered an infinite parameter, it will swallow up all other ordinal based arguments. Might as well check the types and exit the loop.
|
||||
for (; index < Parameters.Ordinals.Count; index++)
|
||||
{
|
||||
string value = Parameters.Ordinals[index].Value;
|
||||
var value = Parameters.Ordinals[index].Value;
|
||||
Parameters.Ordinals[index].AssociateWithPrototypeParam(param.Name);
|
||||
EnforcePrototypeParamValue(param, value.ToLower());
|
||||
namedToAddLater.Add(new NamedParameter(param.Name, value));
|
||||
@@ -163,18 +164,20 @@
|
||||
hasEncounteredOptionalParameter = true;
|
||||
}
|
||||
|
||||
if (param.IsRequired == true && hasEncounteredOptionalParameter)
|
||||
if (param.IsRequired && hasEncounteredOptionalParameter)
|
||||
{
|
||||
throw new Exception($"Function [{Name}], the required parameter [{param.Name}] was found after other optional parameters.");
|
||||
throw new Exception(
|
||||
$"函数 [{Name}], 所必参数 [{param.Name}] 在其他可选参数之后找到.");
|
||||
}
|
||||
else if (param.IsInfinite == true)
|
||||
else if (param.IsInfinite)
|
||||
{
|
||||
throw new Exception($"Function [{Name}], encountered an unexpected number of infinite parameters in prototype for [{param.Name}].");
|
||||
throw new Exception(
|
||||
$"函数 [{Name}], 参数溢出 [{param.Name}].");
|
||||
}
|
||||
|
||||
if (Parameters.Ordinals.Count > index)
|
||||
{
|
||||
string value = Parameters.Ordinals[index].Value;
|
||||
var value = Parameters.Ordinals[index].Value;
|
||||
Parameters.Ordinals[index].AssociateWithPrototypeParam(param.Name);
|
||||
EnforcePrototypeParamValue(param, value.ToLower());
|
||||
namedToAddLater.Add(new NamedParameter(param.Name, value));
|
||||
@@ -183,8 +186,10 @@
|
||||
|
||||
foreach (var named in Parameters.Named)
|
||||
{
|
||||
var param = Prototype.Parameters.Where(o => o.Name.Equals(named.Name, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault()
|
||||
?? throw new Exception($"Function [{Name}], the named parameter [{named.Name}] is not defined in the function prototype.");
|
||||
var param = Prototype.Parameters
|
||||
.FirstOrDefault(o => o.Name.Equals(named.Name, StringComparison.InvariantCultureIgnoreCase))
|
||||
?? throw new Exception(
|
||||
$"函数 [{Name}], 命名参数 [{named.Name}] 未在函数原型中定义.");
|
||||
|
||||
EnforcePrototypeParamValue(param, named.Value);
|
||||
}
|
||||
@@ -194,16 +199,19 @@
|
||||
var unmatchedParams = Parameters.Ordinals.Where(o => o.IsMatched == false).ToList();
|
||||
if (unmatchedParams.Count != 0)
|
||||
{
|
||||
throw new Exception($"Function [{Name}], unmatched parameter value [{unmatchedParams.First().Value}].");
|
||||
throw new Exception($"函数 [{Name}], 不匹配的参数值 [{unmatchedParams.First().Value}].");
|
||||
}
|
||||
|
||||
var nonInfiniteParams = Prototype.Parameters.Where(o => o.IsInfinite == false).Select(o => o.Name.ToLower());
|
||||
var groups = Parameters.Named.Where(o => nonInfiniteParams.Contains(o.Name.ToLower())).GroupBy(o => o.Name.ToLower()).Where(o => o.Count() > 1);
|
||||
var nonInfiniteParams =
|
||||
Prototype.Parameters.Where(o => o.IsInfinite == false).Select(o => o.Name.ToLower());
|
||||
var groups = Parameters.Named.Where(o => nonInfiniteParams.Contains(o.Name.ToLower()))
|
||||
.GroupBy(o => o.Name.ToLower()).Where(o => o.Count() > 1);
|
||||
|
||||
if (groups.Any())
|
||||
{
|
||||
var group = groups.First();
|
||||
throw new Exception($"Function [{Name}], non-infinite parameter specified more than once: [{group.Key}].");
|
||||
throw new Exception(
|
||||
$"函数 [{Name}], 多次指定参数: [{group.Key}].");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,38 +4,42 @@ namespace ZelWiki.Engine.Function
|
||||
{
|
||||
public class FunctionParameters
|
||||
{
|
||||
/// <summary>
|
||||
/// Variables set by ordinal.
|
||||
/// </summary>
|
||||
public List<OrdinalParameter> Ordinals { get; set; } = new();
|
||||
/// <summary>
|
||||
/// Variables set by name.
|
||||
/// </summary>
|
||||
public List<NamedParameter> Named { get; private set; } = new();
|
||||
|
||||
private readonly FunctionCall _owner;
|
||||
|
||||
public FunctionParameters(FunctionCall owner)
|
||||
{
|
||||
_owner = owner;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public List<OrdinalParameter> Ordinals { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public List<NamedParameter> Named { get; private set; } = new();
|
||||
|
||||
|
||||
|
||||
public T Get<T>(string name)
|
||||
{
|
||||
try
|
||||
{
|
||||
var value = Named.Where(o => o.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault()?.Value;
|
||||
var value = Named
|
||||
.FirstOrDefault(o => o.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))?.Value;
|
||||
if (value == null)
|
||||
{
|
||||
var prototype = _owner.Prototype.Parameters.Where(o => o.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)).First();
|
||||
return Converters.ConvertTo<T>(prototype.DefaultValue) ?? throw new Exception("Value cannot be null");
|
||||
var prototype = _owner.Prototype.Parameters.First(o => o.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase));
|
||||
return Converters.ConvertTo<T>(prototype.DefaultValue) ??
|
||||
throw new Exception("值不能为空");
|
||||
}
|
||||
|
||||
return Converters.ConvertTo<T>(value) ?? throw new Exception("Value cannot be null");
|
||||
return Converters.ConvertTo<T>(value) ?? throw new Exception("值不能为空");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Function [{_owner.Name}], {ex.Message}");
|
||||
throw new Exception($"函数 [{_owner.Name}], {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,17 +47,18 @@ namespace ZelWiki.Engine.Function
|
||||
{
|
||||
try
|
||||
{
|
||||
var value = Named.Where(o => o.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault()?.Value;
|
||||
var value = Named
|
||||
.FirstOrDefault(o => o.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))?.Value;
|
||||
if (value == null)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return Converters.ConvertTo<T>(value) ?? throw new Exception("Value cannot be null");
|
||||
return Converters.ConvertTo<T>(value) ?? throw new Exception("值不能为空");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Function [{_owner.Name}], {ex.Message}");
|
||||
throw new Exception($"函数 [{_owner.Name}], {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,13 +67,14 @@ namespace ZelWiki.Engine.Function
|
||||
try
|
||||
{
|
||||
var values = Named.Where(o => o.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))?
|
||||
.Select(o => Converters.ConvertTo<T>(o.Value) ?? throw new Exception("Value cannot be null"))?.ToList();
|
||||
.Select(o => Converters.ConvertTo<T>(o.Value) ?? throw new Exception("值不能为空"))
|
||||
?.ToList();
|
||||
|
||||
return values ?? new List<T>();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Function [{_owner.Name}], {ex.Message}");
|
||||
throw new Exception($"函数 [{_owner.Name}], {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,13 +10,15 @@ namespace ZelWiki.Engine.Function
|
||||
private static partial Regex FunctionCallParser();
|
||||
|
||||
/// <summary>
|
||||
/// Parsed a function call, its parameters and matches it to a defined function and its prototype.
|
||||
/// 解析函数调用及其参数,并将其与已定义的函数及其原型进行匹配
|
||||
/// </summary>
|
||||
/// <param name="prototypes"></param>
|
||||
/// <param name="functionCall"></param>
|
||||
/// <param name="parseEndIndex"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public static FunctionCall ParseAndGetFunctionCall(FunctionPrototypeCollection prototypes, string functionCall, out int parseEndIndex)
|
||||
/// <exception cref="WikiFunctionPrototypeNotDefinedException"></exception>
|
||||
public static FunctionCall ParseAndGetFunctionCall(FunctionPrototypeCollection prototypes, string functionCall,
|
||||
out int parseEndIndex)
|
||||
{
|
||||
var rawArguments = new List<string>();
|
||||
|
||||
@@ -25,7 +27,8 @@ namespace ZelWiki.Engine.Function
|
||||
var prototype = prototypes.Get(parsed.Prefix, parsed.Name);
|
||||
if (prototype == null)
|
||||
{
|
||||
throw new WikiFunctionPrototypeNotDefinedException($"Function ({parsed.Name}) does not have a defined prototype.");
|
||||
throw new WikiFunctionPrototypeNotDefinedException(
|
||||
$"函数 ({parsed.Name}) 没有定义的原型.");
|
||||
}
|
||||
|
||||
parseEndIndex = parsed.EndIndex;
|
||||
@@ -35,18 +38,18 @@ namespace ZelWiki.Engine.Function
|
||||
|
||||
public static ParsedFunctionCall ParseFunctionCall(FunctionPrototypeCollection prototypes, string functionCall)
|
||||
{
|
||||
string functionName = string.Empty;
|
||||
int parseEndIndex = 0;
|
||||
var functionName = string.Empty;
|
||||
var parseEndIndex = 0;
|
||||
var rawArguments = new List<string>();
|
||||
|
||||
var firstLine = functionCall.Split('\n')?.FirstOrDefault();
|
||||
|
||||
if (firstLine == null || firstLine.Where(x => x == '(').Count() != firstLine.Where(x => x == ')').Count())
|
||||
if (firstLine == null || firstLine.Count(x => x == '(') != firstLine.Count(x => x == ')'))
|
||||
{
|
||||
throw new WikiFunctionPrototypeSyntaxError($"Function parentheses mismatch.");
|
||||
throw new WikiFunctionPrototypeSyntaxError($"函数括号不匹配.");
|
||||
}
|
||||
|
||||
string functionPrefix = functionCall.Substring(0, 2);
|
||||
var functionPrefix = functionCall.Substring(0, 2);
|
||||
|
||||
var parameterMatches = FunctionCallParser().Matches(firstLine);
|
||||
if (parameterMatches.Count > 0)
|
||||
@@ -57,12 +60,15 @@ namespace ZelWiki.Engine.Function
|
||||
functionName = match.Value[..paramStartIndex].ToLower().TrimStart(['{', '#', '@']).Trim();
|
||||
parseEndIndex = match.Index + match.Length;
|
||||
|
||||
string rawArgTrimmed = match.ToString().Substring(paramStartIndex, (match.ToString().Length - paramStartIndex));
|
||||
string rawArgTrimmed = match.ToString()
|
||||
.Substring(paramStartIndex, (match.ToString().Length - paramStartIndex));
|
||||
rawArguments = ParseRawArguments(rawArgTrimmed);
|
||||
}
|
||||
else //The function call has no parameters.
|
||||
else //函数调用没有参数.
|
||||
{
|
||||
int endOfLine = functionCall.Substring(2).TakeWhile(c => char.IsLetterOrDigit(c)).Count(); //Find the first non-alphanumeric after the function identifier (##, @@, etc).
|
||||
var endOfLine =
|
||||
functionCall.Substring(2).TakeWhile(c => char.IsLetterOrDigit(c))
|
||||
.Count();
|
||||
functionName = functionCall.Substring(2, endOfLine).ToLower().TrimStart(['{', '#', '@']).Trim();
|
||||
parseEndIndex = endOfLine + 2;
|
||||
}
|
||||
@@ -71,12 +77,11 @@ namespace ZelWiki.Engine.Function
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses function parameters into a list of arguments based on comma separation.
|
||||
/// String do not need to be enclosed in double-quotes unless they contain commas.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="paramString"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
/// <exception cref="WikiFunctionPrototypeSyntaxError"></exception>
|
||||
public static List<string> ParseRawArgumentsAddParenthesis(string paramString)
|
||||
{
|
||||
if (paramString.StartsWith('(') || paramString.EndsWith(')'))
|
||||
@@ -88,17 +93,16 @@ namespace ZelWiki.Engine.Function
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses function parameters into a list of arguments based on comma separation.
|
||||
/// String do not need to be enclosed in double-quotes unless they contain commas.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="paramString"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
/// <exception cref="WikiFunctionPrototypeSyntaxError"></exception>
|
||||
public static List<string> ParseRawArguments(string paramString)
|
||||
{
|
||||
List<string> ps = new();
|
||||
|
||||
int readPos = 0;
|
||||
var readPos = 0;
|
||||
|
||||
var singleParam = new StringBuilder();
|
||||
|
||||
@@ -109,7 +113,7 @@ namespace ZelWiki.Engine.Function
|
||||
|
||||
int parenNest = 1;
|
||||
|
||||
readPos++; //Skip the (
|
||||
readPos++;
|
||||
|
||||
while (readPos < paramString.Length && char.IsWhiteSpace(paramString[readPos])) readPos++;
|
||||
|
||||
@@ -130,7 +134,7 @@ namespace ZelWiki.Engine.Function
|
||||
}
|
||||
else if (paramString[readPos] == ')' && parenNest == 0)
|
||||
{
|
||||
readPos++; //Skip the )
|
||||
readPos++;
|
||||
|
||||
if (parenNest == 0 && readPos != paramString.Length)
|
||||
{
|
||||
@@ -141,6 +145,7 @@ namespace ZelWiki.Engine.Function
|
||||
{
|
||||
ps.Add(singleParam.ToString());
|
||||
}
|
||||
|
||||
singleParam.Clear();
|
||||
|
||||
if (parenNest == 0)
|
||||
@@ -150,10 +155,10 @@ namespace ZelWiki.Engine.Function
|
||||
}
|
||||
else if (paramString[readPos] == '\"')
|
||||
{
|
||||
readPos++; //Skip the ".
|
||||
readPos++;
|
||||
|
||||
bool escapeChar = false;
|
||||
for (; ; readPos++)
|
||||
var escapeChar = false;
|
||||
for (;; readPos++)
|
||||
{
|
||||
if (readPos == paramString.Length)
|
||||
{
|
||||
@@ -166,14 +171,14 @@ namespace ZelWiki.Engine.Function
|
||||
}
|
||||
else if (paramString[readPos] == '\"' && escapeChar == false)
|
||||
{
|
||||
//Found the end of the string:
|
||||
readPos++; //Skip the ".
|
||||
readPos++;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
singleParam.Append(paramString[readPos]);
|
||||
}
|
||||
|
||||
escapeChar = false;
|
||||
}
|
||||
|
||||
@@ -181,7 +186,7 @@ namespace ZelWiki.Engine.Function
|
||||
}
|
||||
else if (paramString[readPos] == ',')
|
||||
{
|
||||
readPos++; //Skip the ,
|
||||
readPos++;
|
||||
while (readPos < paramString.Length && char.IsWhiteSpace(paramString[readPos])) readPos++;
|
||||
|
||||
ps.Add(singleParam.ToString());
|
||||
@@ -209,7 +214,7 @@ namespace ZelWiki.Engine.Function
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < ps.Count; i++)
|
||||
for (var i = 0; i < ps.Count; i++)
|
||||
{
|
||||
ps[i] = ps[i].Trim();
|
||||
}
|
||||
|
||||
@@ -2,14 +2,19 @@
|
||||
{
|
||||
public class FunctionPrototype
|
||||
{
|
||||
public string FunctionPrefix { get; set; } = string.Empty;
|
||||
public string ProperName { get; set; } = string.Empty;
|
||||
public string FunctionName { get; set; } = string.Empty;
|
||||
public List<PrototypeParameter> Parameters { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public FunctionPrototype()
|
||||
{
|
||||
Parameters = new List<PrototypeParameter>();
|
||||
FunctionPrefix = string.Empty;
|
||||
ProperName = string.Empty;
|
||||
FunctionName = string.Empty;
|
||||
Parameters = new();
|
||||
}
|
||||
public string FunctionPrefix { get; set; }
|
||||
public string ProperName { get; set; }
|
||||
public string FunctionName { get; set; }
|
||||
public List<PrototypeParameter> Parameters { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,10 @@ namespace ZelWiki.Engine.Function
|
||||
public WikiFunctionType FunctionTypes { get; private set; }
|
||||
public List<PrototypeSet> Items { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="functionTypes"></param>
|
||||
public FunctionPrototypeCollection(WikiFunctionType functionTypes)
|
||||
{
|
||||
FunctionTypes = functionTypes;
|
||||
@@ -36,40 +40,40 @@ namespace ZelWiki.Engine.Function
|
||||
{
|
||||
functionName = functionName.ToLower();
|
||||
|
||||
//$$ are scope functions and are not called by prefix, we only have prefixes to make it easier to parse
|
||||
// the functions in the wikiText and scope functions are easy enough since they start with curly braces.
|
||||
return Items.Any(o => (o.FunctionPrefix == functionPrefix || o.FunctionPrefix == "$$") && o.FunctionName == functionName);
|
||||
return Items.Any(o =>
|
||||
(o.FunctionPrefix == functionPrefix || o.FunctionPrefix == "$$") && o.FunctionName == functionName);
|
||||
}
|
||||
|
||||
public FunctionPrototype Get(string functionPrefix, string functionName)
|
||||
{
|
||||
functionName = functionName.ToLower();
|
||||
|
||||
//$$ are scope functions and are not called by prefix, we only have prefixes to make it easier to parse
|
||||
// the functions in the wikiText and scope functions are easy enough since they start with curly braces.
|
||||
var functionPrototype = Items.Where(o => (o.FunctionPrefix == functionPrefix || o.FunctionPrefix == "$$") && o.FunctionName == functionName).FirstOrDefault()?.Value;
|
||||
var functionPrototype = Items.FirstOrDefault(o =>
|
||||
(o.FunctionPrefix == functionPrefix || o.FunctionPrefix == "$$") && o.FunctionName == functionName)
|
||||
?.Value;
|
||||
|
||||
return functionPrototype
|
||||
?? throw new WikiFunctionPrototypeNotDefinedException($"Function ({functionName}) does not have a defined prototype.");
|
||||
?? throw new WikiFunctionPrototypeNotDefinedException(
|
||||
$"函数 ({functionName}) 没有定义的原型.");
|
||||
}
|
||||
|
||||
#region Private
|
||||
|
||||
private FunctionPrototype ParsePrototype(string prototypeString)
|
||||
{
|
||||
int nameStartIndex = prototypeString.TakeWhile(c => char.IsLetterOrDigit(c) == false).Count();
|
||||
int nameEndIndex = prototypeString.IndexOf(':');
|
||||
string properName = prototypeString.Substring(nameStartIndex, nameEndIndex - nameStartIndex).Trim();
|
||||
string functionName = properName.ToLower();
|
||||
string functionPrefix = prototypeString.Substring(0, nameStartIndex).Trim();
|
||||
var nameStartIndex = prototypeString.TakeWhile(c => char.IsLetterOrDigit(c) == false).Count();
|
||||
var nameEndIndex = prototypeString.IndexOf(':');
|
||||
var properName = prototypeString.Substring(nameStartIndex, nameEndIndex - nameStartIndex).Trim();
|
||||
var functionName = properName.ToLower();
|
||||
var functionPrefix = prototypeString.Substring(0, nameStartIndex).Trim();
|
||||
|
||||
prototypeString = prototypeString.Substring(nameEndIndex + 1).Trim();
|
||||
|
||||
var prototype = new FunctionPrototype() { FunctionPrefix = functionPrefix, ProperName = properName, FunctionName = functionName };
|
||||
var prototype = new FunctionPrototype()
|
||||
{ FunctionPrefix = functionPrefix, ProperName = properName, FunctionName = functionName };
|
||||
|
||||
if (prototypeString.Length == 0)
|
||||
{
|
||||
//No parameters.
|
||||
return prototype;
|
||||
}
|
||||
|
||||
var segments = prototypeString.Trim().Split('|').Select(o => o.Trim());
|
||||
|
||||
@@ -77,13 +81,13 @@ namespace ZelWiki.Engine.Function
|
||||
{
|
||||
var prototypeSegment = new PrototypeParameter();
|
||||
|
||||
int index = 0;
|
||||
var index = 0;
|
||||
|
||||
if (segment[index] == '<')
|
||||
{
|
||||
index++; //Skip the '<'
|
||||
index++;
|
||||
prototypeSegment.Type = Tok(segment, ref index);
|
||||
index++; //Skip the '>'
|
||||
index++;
|
||||
|
||||
if (prototypeSegment.Type.Contains(':'))
|
||||
{
|
||||
@@ -94,12 +98,14 @@ namespace ZelWiki.Engine.Function
|
||||
prototypeSegment.IsInfinite = true;
|
||||
if (prototype.Parameters.Any(o => o.IsInfinite))
|
||||
{
|
||||
throw new Exception($"Function [{functionName}], prototype error: cannot contain more than one [infinite] parameter.");
|
||||
throw new Exception(
|
||||
$"函数 [{functionName}], 原型错误: cannot contain more than one [infinite] parameter.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Function [{functionName}], prototype error: expected [infinite] got [{splitSeg[1]}].");
|
||||
throw new Exception(
|
||||
$"函数 [{functionName}], 原型错误: expected [infinite] got [{splitSeg[1]}].");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,41 +118,42 @@ namespace ZelWiki.Engine.Function
|
||||
prototypeSegment.IsRequired = true;
|
||||
}
|
||||
|
||||
index++; //Skip the '[' or '{'
|
||||
index++;
|
||||
|
||||
prototypeSegment.Name = Tok(segment, ref index);
|
||||
|
||||
if (index < segment.Length && segment[index] == '(') //Parse allowed values.
|
||||
if (index < segment.Length && segment[index] == '(')
|
||||
{
|
||||
int allowedValueEndIndex = segment.IndexOf(')', index);
|
||||
string roteRequiredValues = segment.Substring(index + 1, allowedValueEndIndex - index - 1);
|
||||
prototypeSegment.AllowedValues = roteRequiredValues.Trim().Split(',').Select(o => o.Trim().ToLower()).ToList();
|
||||
prototypeSegment.AllowedValues = roteRequiredValues.Trim().Split(',')
|
||||
.Select(o => o.Trim().ToLower()).ToList();
|
||||
|
||||
index = allowedValueEndIndex;
|
||||
index++; //Skip the ')'
|
||||
index++;
|
||||
SkipWhiteSpace(segment, ref index);
|
||||
}
|
||||
|
||||
index++; //Skip the ']' or '}'
|
||||
index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Function [{functionName}], prototype error: expected [{{] or [[].");
|
||||
throw new Exception($"函数 [{functionName}], 原型错误: expected [{{] or [[].");
|
||||
}
|
||||
|
||||
SkipWhiteSpace(segment, ref index);
|
||||
|
||||
if (index < segment.Length && segment[index] == '=')
|
||||
{
|
||||
index++; //Skip the '='
|
||||
index++;
|
||||
SkipWhiteSpace(segment, ref index);
|
||||
|
||||
if (segment[index] != '\'')
|
||||
{
|
||||
throw new Exception($"Function [{functionName}], prototype error: expected [\'].");
|
||||
throw new Exception($"函数 [{functionName}], 原型错误: expected [\'].");
|
||||
}
|
||||
|
||||
index++; //Skip the '''
|
||||
index++;
|
||||
|
||||
prototypeSegment.DefaultValue = segment.Substring(index, (segment.Length - index) - 1);
|
||||
|
||||
@@ -154,13 +161,13 @@ namespace ZelWiki.Engine.Function
|
||||
|
||||
if (index < segment.Length && segment[index] != '\'')
|
||||
{
|
||||
throw new Exception($"Function [{functionName}], prototype error: expected [\'].");
|
||||
throw new Exception($"函数 [{functionName}], 原型错误: expected [\'].");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Function [{functionName}], prototype error: expected [<].");
|
||||
throw new Exception($"函数 [{functionName}], 原型错误: expected [<].");
|
||||
}
|
||||
|
||||
prototype.Parameters.Add(prototypeSegment);
|
||||
@@ -168,9 +175,8 @@ namespace ZelWiki.Engine.Function
|
||||
|
||||
return prototype;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next token in a string.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <param name="index"></param>
|
||||
@@ -203,5 +209,7 @@ namespace ZelWiki.Engine.Function
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,11 @@
|
||||
public string Name { get; set; }
|
||||
public string Value { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="value"></param>
|
||||
public NamedParameter(string name, string value)
|
||||
{
|
||||
Name = name;
|
||||
|
||||
@@ -2,22 +2,27 @@
|
||||
{
|
||||
public class OrdinalParameter
|
||||
{
|
||||
public string Value { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Has been matched to a prototype parameter?
|
||||
///
|
||||
/// </summary>
|
||||
public bool IsMatched { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// If matched to a prototype parameter, this is the name of the parameter.
|
||||
/// </summary>
|
||||
public string ParameterName { get; set; } = string.Empty;
|
||||
|
||||
/// <param name="value"></param>
|
||||
public OrdinalParameter(string value)
|
||||
{
|
||||
Value = value;
|
||||
IsMatched = false;
|
||||
ParameterName = string.Empty;
|
||||
}
|
||||
public string Value { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool IsMatched { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string ParameterName { get; set; }
|
||||
|
||||
public void AssociateWithPrototypeParam(string paramName)
|
||||
{
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
{
|
||||
public class ParsedFunctionCall
|
||||
{
|
||||
public string Prefix { get; set; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public int EndIndex { get; set; }
|
||||
public List<string> RawArguments { get; set; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="prefix"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="endIndex"></param>
|
||||
/// <param name="rawArguments"></param>
|
||||
public ParsedFunctionCall(string prefix, string name, int endIndex, List<string> rawArguments)
|
||||
{
|
||||
Prefix = prefix;
|
||||
@@ -14,5 +16,9 @@
|
||||
EndIndex = endIndex;
|
||||
RawArguments = rawArguments;
|
||||
}
|
||||
public string Prefix { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int EndIndex { get; set; }
|
||||
public List<string> RawArguments { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,24 @@
|
||||
{
|
||||
public class PrototypeParameter
|
||||
{
|
||||
public string Type { get; set; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string DefaultValue { get; set; } = string.Empty;
|
||||
public bool IsRequired { get; set; } = false;
|
||||
public bool IsInfinite { get; set; } = false;
|
||||
public List<string> AllowedValues { get; set; } = new();
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public PrototypeParameter()
|
||||
{
|
||||
Type = string.Empty;
|
||||
Name = string.Empty;
|
||||
DefaultValue = string.Empty;
|
||||
IsRequired = false;
|
||||
IsInfinite = false;
|
||||
AllowedValues = new();
|
||||
}
|
||||
|
||||
public string Type { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string DefaultValue { get; set; }
|
||||
public bool IsRequired { get; set; }
|
||||
public bool IsInfinite { get; set; }
|
||||
public List<string> AllowedValues { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,19 @@
|
||||
{
|
||||
public class PrototypeSet
|
||||
{
|
||||
public string FunctionPrefix { get; set; } = string.Empty;
|
||||
public string ProperName { get; set; } = string.Empty;
|
||||
public string FunctionName { get; set; } = string.Empty;
|
||||
public FunctionPrototype Value { get; set; } = new();
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public PrototypeSet()
|
||||
{
|
||||
FunctionPrefix = string.Empty;
|
||||
ProperName = string.Empty;
|
||||
FunctionName = string.Empty;
|
||||
Value = new();
|
||||
}
|
||||
public string FunctionPrefix { get; set; }
|
||||
public string ProperName { get; set; }
|
||||
public string FunctionName { get; set; }
|
||||
public FunctionPrototype Value { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
namespace ZelWiki.Engine.Function
|
||||
{
|
||||
[Obsolete("弃用了")]
|
||||
public static class SelfDocument
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't ever look at this. :(
|
||||
///
|
||||
/// </summary>
|
||||
[Obsolete("弃用了")]
|
||||
public static void CreateNotExisting()
|
||||
{
|
||||
/*
|
||||
|
||||
@@ -2,8 +2,13 @@
|
||||
{
|
||||
public class AggregatedSearchToken
|
||||
{
|
||||
public string Token { get; set; } = string.Empty;
|
||||
public AggregatedSearchToken()
|
||||
{
|
||||
Token = string.Empty;
|
||||
DoubleMetaphone = string.Empty;
|
||||
}
|
||||
public string Token { get; set; }
|
||||
public double Weight { get; set; }
|
||||
public string DoubleMetaphone { get; set; } = string.Empty;
|
||||
public string DoubleMetaphone { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,16 @@ using ZelWiki.Engine.Library.Interfaces;
|
||||
namespace ZelWiki.Engine.Implementation
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles wiki comments. These are generally removed from the result.
|
||||
///
|
||||
/// </summary>
|
||||
public class CommentHandler : ICommentHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles a wiki comment.
|
||||
/// 处理评论
|
||||
/// </summary>
|
||||
/// <param name="state">Reference to the wiki state object</param>
|
||||
/// <param name="text">The comment text</param>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
public HandlerResult Handle(IZelEngineState state, string text)
|
||||
{
|
||||
return new HandlerResult() { Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] };
|
||||
|
||||
@@ -5,14 +5,14 @@ using ZelWiki.Repository;
|
||||
namespace ZelWiki.Engine.Implementation
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles wiki completion events.
|
||||
///
|
||||
/// </summary>
|
||||
public class CompletionHandler : ICompletionHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles wiki completion events. Is called when the wiki processing competes for a given page.
|
||||
/// 完成事件
|
||||
/// </summary>
|
||||
/// <param name="state">Reference to the wiki state object</param>
|
||||
/// <param name="state"></param>
|
||||
public void Complete(IZelEngineState state)
|
||||
{
|
||||
if (GlobalConfiguration.RecordCompilationMetrics)
|
||||
|
||||
@@ -5,16 +5,17 @@ using ZelWiki.Models;
|
||||
namespace ZelWiki.Engine.Implementation
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles wiki emojis.
|
||||
///
|
||||
/// </summary>
|
||||
public class EmojiHandler : IEmojiHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles an emoji instruction.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="state">Reference to the wiki state object</param>
|
||||
/// <param name="key">The lookup key for the given emoji.</param>
|
||||
/// <param name="scale">The desired 1-100 scale factor for the emoji.</param>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="scale"></param>
|
||||
/// <returns></returns>
|
||||
public HandlerResult Handle(IZelEngineState state, string key, int scale)
|
||||
{
|
||||
var emoji = GlobalConfiguration.Emojis.FirstOrDefault(o => o.Shortcut == key);
|
||||
@@ -23,20 +24,23 @@ namespace ZelWiki.Engine.Implementation
|
||||
{
|
||||
if (scale != 100 && scale > 0 && scale <= 500)
|
||||
{
|
||||
var emojiImage = $"<img src=\"{GlobalConfiguration.BasePath}/file/Emoji/{key.Trim('%')}?Scale={scale}\" alt=\"{emoji?.Name}\" />";
|
||||
var emojiImage =
|
||||
$"<img src=\"{GlobalConfiguration.BasePath}/file/Emoji/{key.Trim('%')}?Scale={scale}\" alt=\"{emoji?.Name}\" />";
|
||||
|
||||
return new HandlerResult(emojiImage);
|
||||
}
|
||||
else
|
||||
{
|
||||
var emojiImage = $"<img src=\"{GlobalConfiguration.BasePath}/file/Emoji/{key.Trim('%')}\" alt=\"{emoji?.Name}\" />";
|
||||
var emojiImage =
|
||||
$"<img src=\"{GlobalConfiguration.BasePath}/file/Emoji/{key.Trim('%')}\" alt=\"{emoji?.Name}\" />";
|
||||
|
||||
return new HandlerResult(emojiImage);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return new HandlerResult(key) { Instructions = [Constants.HandlerResultInstruction.DisallowNestedProcessing] };
|
||||
return new HandlerResult(key)
|
||||
{ Instructions = [Constants.HandlerResultInstruction.DisallowNestedProcessing] };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,16 +4,16 @@ using ZelWiki.Repository;
|
||||
namespace ZelWiki.Engine.Implementation
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles exceptions thrown by the wiki engine.
|
||||
/// 异常处理.
|
||||
/// </summary>
|
||||
public class ExceptionHandler : IExceptionHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when an exception is thrown by the wiki engine.
|
||||
/// 日志处理
|
||||
/// </summary>
|
||||
/// <param name="state">Reference to the wiki state object</param>
|
||||
/// <param name="ex">Optional exception, in the case that this was an actual exception.</param>
|
||||
/// <param name="customText">Text that accompanies the exception.</param>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="ex"></param>
|
||||
/// <param name="customText"></param>
|
||||
public void Log(IZelEngineState state, Exception? ex, string customText)
|
||||
{
|
||||
if (ex != null)
|
||||
|
||||
@@ -4,18 +4,18 @@ using ZelWiki.Engine.Library.Interfaces;
|
||||
namespace ZelWiki.Engine.Implementation
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles links the wiki to another site.
|
||||
/// 处理链接
|
||||
/// </summary>
|
||||
public class ExternalLinkHandler : IExternalLinkHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles an internal wiki link.
|
||||
/// 处理内链
|
||||
/// </summary>
|
||||
/// <param name="state">Reference to the wiki state object</param>
|
||||
/// <param name="link">The address of the external site being linked to.</param>
|
||||
/// <param name="text">The text which should be show in the absence of an image.</param>
|
||||
/// <param name="image">The image that should be shown.</param>
|
||||
/// <param name="imageScale">The 0-100 image scale factor for the given image.</param>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="link"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="image"></param>
|
||||
/// <returns></returns>
|
||||
public HandlerResult Handle(IZelEngineState state, string link, string? text, string? image)
|
||||
{
|
||||
if (string.IsNullOrEmpty(image))
|
||||
@@ -25,13 +25,11 @@ namespace ZelWiki.Engine.Implementation
|
||||
Instructions = [Constants.HandlerResultInstruction.DisallowNestedProcessing]
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
return new HandlerResult($"<a href=\"{link}\"><img src=\"{image}\" border =\"0\"></a>")
|
||||
{
|
||||
Instructions = [Constants.HandlerResultInstruction.DisallowNestedProcessing]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,18 +3,16 @@ using ZelWiki.Engine.Library.Interfaces;
|
||||
|
||||
namespace ZelWiki.Engine.Implementation
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles wiki headings. These are automatically added to the table of contents.
|
||||
/// </summary>
|
||||
public class HeadingHandler : IHeadingHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles wiki headings. These are automatically added to the table of contents.
|
||||
/// 处理白哦提
|
||||
/// </summary>
|
||||
/// <param name="state">Reference to the wiki state object</param>
|
||||
/// <param name="depth">The size of the header, also used for table of table of contents indentation.</param>
|
||||
/// <param name="link">The self link reference.</param>
|
||||
/// <param name="text">The text for the self link.</param>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="depth"></param>
|
||||
/// <param name="link"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
public HandlerResult Handle(IZelEngineState state, int depth, string link, string text)
|
||||
{
|
||||
if (depth >= 2 && depth <= 6)
|
||||
@@ -22,7 +20,8 @@ namespace ZelWiki.Engine.Implementation
|
||||
int fontSize = 8 - depth;
|
||||
if (fontSize < 5) fontSize = 5;
|
||||
|
||||
string html = "<font size=\"" + fontSize + "\"><a name=\"" + link + "\"><span class=\"WikiH" + (depth - 1).ToString() + "\">" + text + "</span></a></font>\r\n";
|
||||
string html = "<font size=\"" + fontSize + "\"><a name=\"" + link + "\"><span class=\"WikiH" +
|
||||
(depth - 1).ToString() + "\">" + text + "</span></a></font>\r\n";
|
||||
return new HandlerResult(html);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,41 +9,41 @@ using ZelWiki.Repository;
|
||||
|
||||
namespace ZelWiki.Engine.Implementation
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Inserts a new page if Page.Id == 0, other wise updates the page. All metadata is written to the database.
|
||||
/// 更新页面 如果Id为0则新增页面
|
||||
/// </summary>
|
||||
/// <param name="sessionState"></param>
|
||||
/// <param name="query"></param>
|
||||
/// <param name="wikifier"></param>
|
||||
/// <param name="page"></param>
|
||||
/// <param name="sessionState"></param>
|
||||
/// <returns></returns>
|
||||
public static int UpsertPage(IZelEngine wikifier, Page page, ISessionState? sessionState = null)
|
||||
{
|
||||
bool isNewlyCreated = page.Id == 0;
|
||||
var isNewlyCreated = page.Id == 0;
|
||||
|
||||
page.Id = PageRepository.SavePage(page);
|
||||
|
||||
RefreshPageMetadata(wikifier, page, sessionState);
|
||||
|
||||
if (isNewlyCreated)
|
||||
{
|
||||
//This will update the PageId of references that have been saved to the navigation link.
|
||||
PageRepository.UpdateSinglePageReference(page.Navigation, page.Id);
|
||||
}
|
||||
|
||||
|
||||
return page.Id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rebuilds the page and writes all aspects to the database.
|
||||
/// 重建页面并将所有方面写入数据库
|
||||
/// </summary>
|
||||
/// <param name="sessionState"></param>
|
||||
/// <param name="query"></param>
|
||||
/// <param name="wikifier"></param>
|
||||
/// <param name="page"></param>
|
||||
/// <param name="sessionState"></param>
|
||||
public static void RefreshPageMetadata(IZelEngine wikifier, Page page, ISessionState? sessionState = null)
|
||||
{
|
||||
//We omit function calls from the tokenization process because they are too dynamic for static searching.
|
||||
var state = wikifier.Transform(sessionState, page, null,
|
||||
[Constants.WikiMatchType.StandardFunction]);
|
||||
|
||||
@@ -67,7 +67,13 @@ namespace ZelWiki.Engine.Implementation
|
||||
WikiCache.ClearCategory(WikiCacheKey.Build(WikiCache.Category.Page, [page.Navigation]));
|
||||
}
|
||||
|
||||
public static List<AggregatedSearchToken> ParsePageTokens(IZelEngineState state)
|
||||
#region Private
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="state"></param>
|
||||
/// <returns></returns>
|
||||
private static List<AggregatedSearchToken> ParsePageTokens(IZelEngineState state)
|
||||
{
|
||||
var parsedTokens = new List<WeightedSearchToken>();
|
||||
|
||||
@@ -86,7 +92,7 @@ namespace ZelWiki.Engine.Implementation
|
||||
return aggregatedTokens;
|
||||
}
|
||||
|
||||
internal static List<WeightedSearchToken> ComputeParsedPageTokens(string content, double weightMultiplier)
|
||||
private static List<WeightedSearchToken> ComputeParsedPageTokens(string content, double weightMultiplier)
|
||||
{
|
||||
var searchConfig = ConfigurationRepository.GetConfigurationEntryValuesByGroupName("Search");
|
||||
|
||||
@@ -117,7 +123,8 @@ namespace ZelWiki.Engine.Implementation
|
||||
tokens.RemoveAll(o => exclusionWords.Contains(o));
|
||||
|
||||
var searchTokens = (from w in tokens
|
||||
group w by w into g
|
||||
group w by w
|
||||
into g
|
||||
select new WeightedSearchToken
|
||||
{
|
||||
Token = g.Key,
|
||||
@@ -126,5 +133,7 @@ namespace ZelWiki.Engine.Implementation
|
||||
|
||||
return searchTokens.Where(o => string.IsNullOrWhiteSpace(o.Token) == false).ToList();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -8,19 +8,21 @@ using Constants = ZelWiki.Engine.Library.Constants;
|
||||
namespace ZelWiki.Engine.Implementation
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles links from one wiki page to another.
|
||||
/// 内链处理.
|
||||
/// </summary>
|
||||
public class InternalLinkHandler : IInternalLinkHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles an internal wiki link.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="state">Reference to the wiki state object</param>
|
||||
/// <param name="pageNavigation">The navigation for the linked page.</param>
|
||||
/// <param name="pageName">The name of the page being linked to.</param>
|
||||
/// <param name="linkText">The text which should be show in the absence of an image.</param>
|
||||
/// <param name="image">The image that should be shown.</param>
|
||||
/// <param name="imageScale">The 0-100 image scale factor for the given image.</param>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="pageNavigation"></param>
|
||||
/// <param name="pageName"></param>
|
||||
/// <param name="linkText"></param>
|
||||
/// <param name="image"></param>
|
||||
/// <param name="imageScale"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public HandlerResult Handle(IZelEngineState state, NamespaceNavigation pageNavigation,
|
||||
string pageName, string linkText, string? image, int imageScale)
|
||||
{
|
||||
@@ -37,18 +39,21 @@ namespace ZelWiki.Engine.Implementation
|
||||
if (image.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase)
|
||||
|| image.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
//The image is external.
|
||||
href = $"<a href=\"{GlobalConfiguration.BasePath}/Page/Create?Name={pageName}\"><img src=\"{GlobalConfiguration.BasePath}{image}?Scale={imageScale}\" /></a>";
|
||||
//外部图片.
|
||||
href =
|
||||
$"<a href=\"{GlobalConfiguration.BasePath}/Page/Create?Name={pageName}\"><img src=\"{GlobalConfiguration.BasePath}{image}?Scale={imageScale}\" /></a>";
|
||||
}
|
||||
else if (image.Contains('/'))
|
||||
{
|
||||
//The image is located on another page.
|
||||
href = $"<a href=\"{GlobalConfiguration.BasePath}/Page/Create?Name={pageName}\"><img src=\"{GlobalConfiguration.BasePath}/Page/Image/{image}?Scale={imageScale}\" /></a>";
|
||||
//图像位于另一页面.
|
||||
href =
|
||||
$"<a href=\"{GlobalConfiguration.BasePath}/Page/Create?Name={pageName}\"><img src=\"{GlobalConfiguration.BasePath}/Page/Image/{image}?Scale={imageScale}\" /></a>";
|
||||
}
|
||||
else
|
||||
{
|
||||
//The image is located on this page, but this page does not exist.
|
||||
href = $"<a href=\"{GlobalConfiguration.BasePath}/Page/Create?Name={pageName}\">{linkText}</a>";
|
||||
//图像位于此页面上,但此页面不存在.
|
||||
href =
|
||||
$"<a href=\"{GlobalConfiguration.BasePath}/Page/Create?Name={pageName}\">{linkText}</a>";
|
||||
}
|
||||
|
||||
return new HandlerResult(href)
|
||||
@@ -58,7 +63,8 @@ namespace ZelWiki.Engine.Implementation
|
||||
}
|
||||
else if (linkText != null)
|
||||
{
|
||||
var href = $"<a href=\"{GlobalConfiguration.BasePath}/Page/Create?Name={pageName}\">{linkText}</a>"
|
||||
var href =
|
||||
$"<a href=\"{GlobalConfiguration.BasePath}/Page/Create?Name={pageName}\">{linkText}</a>"
|
||||
+ "<font color=\"#cc0000\" size=\"2\">?</font>";
|
||||
|
||||
return new HandlerResult(href)
|
||||
@@ -73,7 +79,7 @@ namespace ZelWiki.Engine.Implementation
|
||||
}
|
||||
else
|
||||
{
|
||||
//The page does not exist and the user does not have permission to create it.
|
||||
//该页面不存在,用户没有创建该页面的权限.
|
||||
|
||||
if (image != null)
|
||||
{
|
||||
@@ -82,17 +88,18 @@ namespace ZelWiki.Engine.Implementation
|
||||
if (image.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase)
|
||||
|| image.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
//The image is external.
|
||||
//外部图像.
|
||||
mockHref = $"<img src=\"{GlobalConfiguration.BasePath}{image}?Scale={imageScale}\" />";
|
||||
}
|
||||
else if (image.Contains('/'))
|
||||
{
|
||||
//The image is located on another page.
|
||||
mockHref = $"<img src=\"{GlobalConfiguration.BasePath}/Page/Image/{image}?Scale={imageScale}\" />";
|
||||
//图像位于另一页.
|
||||
mockHref =
|
||||
$"<img src=\"{GlobalConfiguration.BasePath}/Page/Image/{image}?Scale={imageScale}\" />";
|
||||
}
|
||||
else
|
||||
{
|
||||
//The image is located on this page, but this page does not exist.
|
||||
//图像位于此页面上,但此页面不存在.
|
||||
mockHref = $"linkText";
|
||||
}
|
||||
|
||||
@@ -110,7 +117,7 @@ namespace ZelWiki.Engine.Implementation
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("No link or image was specified.");
|
||||
throw new Exception("未指定链接或图像.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,23 +130,26 @@ namespace ZelWiki.Engine.Implementation
|
||||
if (image.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase)
|
||||
|| image.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
//The image is external.
|
||||
href = $"<a href=\"{GlobalConfiguration.BasePath}/{page.Navigation}\"><img src=\"{GlobalConfiguration.BasePath}{image}\" /></a>";
|
||||
//外部图像.
|
||||
href =
|
||||
$"<a href=\"{GlobalConfiguration.BasePath}/{page.Navigation}\"><img src=\"{GlobalConfiguration.BasePath}{image}\" /></a>";
|
||||
}
|
||||
else if (image.Contains('/'))
|
||||
{
|
||||
//The image is located on another page.
|
||||
href = $"<a href=\"{GlobalConfiguration.BasePath}/{page.Navigation}\"><img src=\"{GlobalConfiguration.BasePath}/Page/Image/{image}?Scale={imageScale}\" /></a>";
|
||||
//图像在另一页面.
|
||||
href =
|
||||
$"<a href=\"{GlobalConfiguration.BasePath}/{page.Navigation}\"><img src=\"{GlobalConfiguration.BasePath}/Page/Image/{image}?Scale={imageScale}\" /></a>";
|
||||
}
|
||||
else
|
||||
{
|
||||
//The image is located on this page.
|
||||
href = $"<a href=\"{GlobalConfiguration.BasePath}/{page.Navigation}\"><img src=\"{GlobalConfiguration.BasePath}/Page/Image/{state.Page.Navigation}/{image}?Scale={imageScale}\" /></a>";
|
||||
//图像在此页面
|
||||
href =
|
||||
$"<a href=\"{GlobalConfiguration.BasePath}/{page.Navigation}\"><img src=\"{GlobalConfiguration.BasePath}/Page/Image/{state.Page.Navigation}/{image}?Scale={imageScale}\" /></a>";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Just a plain ol' internal page link.
|
||||
//内链
|
||||
href = $"<a href=\"{GlobalConfiguration.BasePath}/{page.Navigation}\">{linkText}</a>";
|
||||
}
|
||||
|
||||
|
||||
@@ -4,16 +4,17 @@ using ZelWiki.Engine.Library.Interfaces;
|
||||
namespace ZelWiki.Engine.Implementation
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles basic markup/style instructions like bole, italic, underline, etc.
|
||||
/// 处理基本的标记/样式指令,如粗体、斜体、下划线等.
|
||||
/// </summary>
|
||||
public class MarkupHandler : IMarkupHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles basic markup instructions like bole, italic, underline, etc.
|
||||
/// 处理基本的标记指令,如粗体、斜体、下划线等
|
||||
/// </summary>
|
||||
/// <param name="state">Reference to the wiki state object</param>
|
||||
/// <param name="sequence">The sequence of symbols that were found to denotate this markup instruction,</param>
|
||||
/// <param name="scopeBody">The body of text to apply the style to.</param>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="sequence"></param>
|
||||
/// <param name="scopeBody"></param>
|
||||
/// <returns></returns>
|
||||
public HandlerResult Handle(IZelEngineState state, char sequence, string scopeBody)
|
||||
{
|
||||
switch (sequence)
|
||||
@@ -26,7 +27,6 @@ namespace ZelWiki.Engine.Implementation
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return new HandlerResult() { Instructions = [Constants.HandlerResultInstruction.Skip] };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ using ZelWiki.Models;
|
||||
namespace ZelWiki.Engine.Implementation
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles post-processing function calls.
|
||||
/// 处理后处理函数调用.
|
||||
/// </summary>
|
||||
public class PostProcessingFunctionHandler : IPostProcessingFunctionHandler
|
||||
{
|
||||
@@ -20,9 +20,10 @@ namespace ZelWiki.Engine.Implementation
|
||||
{
|
||||
if (_collection == null)
|
||||
{
|
||||
_collection = new FunctionPrototypeCollection(FunctionPrototypeCollection.WikiFunctionType.Standard);
|
||||
_collection =
|
||||
new FunctionPrototypeCollection(FunctionPrototypeCollection.WikiFunctionType.Standard);
|
||||
|
||||
#region Prototypes.
|
||||
#region
|
||||
|
||||
_collection.Add("##Tags: <string>{styleName(Flat,List)}='List'");
|
||||
_collection.Add("##TagCloud: <string>[pageTag] | <integer>{Top}='1000'");
|
||||
@@ -37,20 +38,19 @@ namespace ZelWiki.Engine.Implementation
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called to handle function calls when proper prototypes are matched.
|
||||
/// 当匹配到合适的原型时,调用它来处理函数调用。
|
||||
/// </summary>
|
||||
/// <param name="state">Reference to the wiki state object</param>
|
||||
/// <param name="function">The parsed function call and all its parameters and their values.</param>
|
||||
/// <param name="scopeBody">This is not a scope function, this should always be null</param>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="function"></param>
|
||||
/// <param name="scopeBody"></param>
|
||||
/// <returns></returns>
|
||||
public HandlerResult Handle(IZelEngineState state, FunctionCall function, string? scopeBody = null)
|
||||
{
|
||||
switch (function.Name.ToLower())
|
||||
{
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
//Displays a tag link list.
|
||||
case "tags": //##tags
|
||||
{
|
||||
string styleName = function.Parameters.Get<string>("styleName").ToLower();
|
||||
var styleName = function.Parameters.Get<string>("styleName").ToLower();
|
||||
var html = new StringBuilder();
|
||||
|
||||
if (styleName == "list")
|
||||
@@ -60,6 +60,7 @@ namespace ZelWiki.Engine.Implementation
|
||||
{
|
||||
html.Append($"<li><a href=\"{GlobalConfiguration.BasePath}/Tag/Browse/{tag}\">{tag}</a>");
|
||||
}
|
||||
|
||||
html.Append("</ul>");
|
||||
}
|
||||
else if (styleName == "flat")
|
||||
@@ -74,7 +75,6 @@ namespace ZelWiki.Engine.Implementation
|
||||
return new HandlerResult(html.ToString());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
case "tagcloud":
|
||||
{
|
||||
var top = function.Parameters.Get<int>("Top");
|
||||
@@ -84,21 +84,19 @@ namespace ZelWiki.Engine.Implementation
|
||||
return new HandlerResult(html);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
case "searchcloud":
|
||||
{
|
||||
var top = function.Parameters.Get<int>("Top");
|
||||
var tokens = function.Parameters.Get<string>("searchPhrase").Split(" ", StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||
var tokens = function.Parameters.Get<string>("searchPhrase")
|
||||
.Split(" ", StringSplitOptions.RemoveEmptyEntries).ToList();
|
||||
|
||||
string html = SearchCloud.Build(tokens, top);
|
||||
return new HandlerResult(html);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
//Displays a table of contents for the page based on the header tags.
|
||||
case "toc":
|
||||
{
|
||||
bool alphabetized = function.Parameters.Get<bool>("alphabetized");
|
||||
var alphabetized = function.Parameters.Get<bool>("alphabetized");
|
||||
|
||||
var html = new StringBuilder();
|
||||
|
||||
@@ -111,7 +109,7 @@ namespace ZelWiki.Engine.Implementation
|
||||
|
||||
if (alphabetized)
|
||||
{
|
||||
int level = tags.FirstOrDefault()?.Level ?? 0;
|
||||
var level = tags.FirstOrDefault()?.Level ?? 0;
|
||||
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
@@ -131,7 +129,7 @@ namespace ZelWiki.Engine.Implementation
|
||||
tags = ordered.ToList();
|
||||
}
|
||||
|
||||
int currentLevel = 0;
|
||||
var currentLevel = 0;
|
||||
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
@@ -147,7 +145,6 @@ namespace ZelWiki.Engine.Implementation
|
||||
{
|
||||
while (currentLevel > tag.Level)
|
||||
{
|
||||
|
||||
html.Append("</ul>");
|
||||
currentLevel--;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ using ZelWiki.Engine.Library.Interfaces;
|
||||
namespace ZelWiki.Engine.Implementation
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles processing-instruction function calls, these functions affect the way the page is processed, but are not directly replaced with text.
|
||||
/// 处理处理指令函数调用,这些函数会影响页面的处理方式,但不会直接替换为文本.
|
||||
/// </summary>
|
||||
public class ProcessingInstructionFunctionHandler : IProcessingInstructionFunctionHandler
|
||||
{
|
||||
@@ -17,11 +17,11 @@ namespace ZelWiki.Engine.Implementation
|
||||
{
|
||||
if (_collection == null)
|
||||
{
|
||||
_collection = new FunctionPrototypeCollection(FunctionPrototypeCollection.WikiFunctionType.Instruction);
|
||||
_collection =
|
||||
new FunctionPrototypeCollection(FunctionPrototypeCollection.WikiFunctionType.Instruction);
|
||||
|
||||
#region Prototypes.
|
||||
#region
|
||||
|
||||
//Processing instructions:
|
||||
_collection.Add("@@Deprecate:");
|
||||
_collection.Add("@@Protect:<bool>{isSilent}='false'");
|
||||
_collection.Add("@@Tags: <string:infinite>[pageTags]");
|
||||
@@ -42,19 +42,16 @@ namespace ZelWiki.Engine.Implementation
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called to handle function calls when proper prototypes are matched.
|
||||
/// 处理各种页面
|
||||
/// </summary>
|
||||
/// <param name="state">Reference to the wiki state object</param>
|
||||
/// <param name="function">The parsed function call and all its parameters and their values.</param>
|
||||
/// <param name="scopeBody">This is not a scope function, this should always be null</param>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="function"></param>
|
||||
/// <param name="scopeBody"></param>
|
||||
/// <returns></returns>
|
||||
public HandlerResult Handle(IZelEngineState state, FunctionCall function, string? scopeBody = null)
|
||||
{
|
||||
switch (function.Name.ToLower())
|
||||
{
|
||||
//We check wikifierSession.Factory.CurrentNestLevel here because we don't want to include the processing instructions on any parent pages that are injecting this one.
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
//Associates tags with a page. These are saved with the page and can also be displayed.
|
||||
case "tags": //##tag(pipe|separated|list|of|tags)
|
||||
{
|
||||
var tags = function.Parameters.GetList<string>("pageTags");
|
||||
@@ -66,8 +63,6 @@ namespace ZelWiki.Engine.Implementation
|
||||
Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine]
|
||||
};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
case "title":
|
||||
{
|
||||
state.PageTitle = function.Parameters.Get<string>("pageTitle");
|
||||
@@ -77,8 +72,6 @@ namespace ZelWiki.Engine.Implementation
|
||||
Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine]
|
||||
};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
case "hidefooterlastmodified":
|
||||
{
|
||||
state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.HideFooterLastModified);
|
||||
@@ -88,8 +81,6 @@ namespace ZelWiki.Engine.Implementation
|
||||
Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine]
|
||||
};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
case "hidefootercomments":
|
||||
{
|
||||
state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.HideFooterComments);
|
||||
@@ -98,8 +89,6 @@ namespace ZelWiki.Engine.Implementation
|
||||
Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine]
|
||||
};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
case "nocache":
|
||||
{
|
||||
state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.NoCache);
|
||||
@@ -108,22 +97,20 @@ namespace ZelWiki.Engine.Implementation
|
||||
Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine]
|
||||
};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
case "deprecate":
|
||||
{
|
||||
if (state.NestDepth == 0)
|
||||
{
|
||||
state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.Deprecate);
|
||||
state.Headers.Add("<div class=\"alert alert-danger\">This page has been deprecated and will eventually be deleted.</div>");
|
||||
state.Headers.Add(
|
||||
"<div class=\"alert alert-danger\">此页面已被弃用,最终将被删除.</div>");
|
||||
}
|
||||
|
||||
return new HandlerResult(string.Empty)
|
||||
{
|
||||
Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine]
|
||||
};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
case "protect":
|
||||
{
|
||||
if (state.NestDepth == 0)
|
||||
@@ -132,65 +119,67 @@ namespace ZelWiki.Engine.Implementation
|
||||
state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.Protect);
|
||||
if (isSilent == false)
|
||||
{
|
||||
state.Headers.Add("<div class=\"alert alert-info\">This page has been protected and can not be changed by non-moderators.</div>");
|
||||
state.Headers.Add(
|
||||
"<div class=\"alert alert-info\">此页面已受到保护,非版主无法更改.</div>");
|
||||
}
|
||||
}
|
||||
|
||||
return new HandlerResult(string.Empty)
|
||||
{
|
||||
Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine]
|
||||
};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
case "template":
|
||||
{
|
||||
if (state.NestDepth == 0)
|
||||
{
|
||||
state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.Template);
|
||||
state.Headers.Add("<div class=\"alert alert-secondary\">This page is a template and will not appear in indexes or glossaries.</div>");
|
||||
state.Headers.Add(
|
||||
"<div class=\"alert alert-secondary\">此页面是一个模板,不会出现在索引或术语表中.</div>");
|
||||
}
|
||||
|
||||
return new HandlerResult(string.Empty)
|
||||
{
|
||||
Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine]
|
||||
};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
case "review":
|
||||
{
|
||||
if (state.NestDepth == 0)
|
||||
{
|
||||
state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.Review);
|
||||
state.Headers.Add("<div class=\"alert alert-warning\">This page has been flagged for review, its content may be inaccurate.</div>");
|
||||
state.Headers.Add(
|
||||
"<div class=\"alert alert-warning\">此页面已被标记为待审核,其内容可能不准确.</div>");
|
||||
}
|
||||
|
||||
return new HandlerResult(string.Empty)
|
||||
{
|
||||
Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine]
|
||||
};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
case "include":
|
||||
{
|
||||
if (state.NestDepth == 0)
|
||||
{
|
||||
state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.Include);
|
||||
state.Headers.Add("<div class=\"alert alert-secondary\">This page is an include and will not appear in indexes or glossaries.</div>");
|
||||
state.Headers.Add(
|
||||
"<div class=\"alert alert-secondary\">此页为包含页,不会出现在索引或术语表中.</div>");
|
||||
}
|
||||
|
||||
return new HandlerResult(string.Empty)
|
||||
{
|
||||
Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine]
|
||||
};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
case "draft":
|
||||
{
|
||||
if (state.NestDepth == 0)
|
||||
{
|
||||
state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.Draft);
|
||||
state.Headers.Add("<div class=\"alert alert-warning\">This page is a draft and may contain incorrect information and/or experimental styling.</div>");
|
||||
state.Headers.Add(
|
||||
"<div class=\"alert alert-warning\">本页为草稿,可能包含不正确的信息包括但不仅限于实验性样式.</div>");
|
||||
}
|
||||
|
||||
return new HandlerResult(string.Empty)
|
||||
{
|
||||
Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine]
|
||||
|
||||
@@ -9,7 +9,7 @@ using static ZelWiki.Engine.Library.Constants;
|
||||
namespace ZelWiki.Engine.Implementation
|
||||
{
|
||||
/// <summary>
|
||||
/// Handled scope function calls.
|
||||
///处理作用域函数调用.
|
||||
/// </summary>
|
||||
public class ScopeFunctionHandler : IScopeFunctionHandler
|
||||
{
|
||||
@@ -23,17 +23,23 @@ namespace ZelWiki.Engine.Implementation
|
||||
{
|
||||
_collection = new FunctionPrototypeCollection(FunctionPrototypeCollection.WikiFunctionType.Scoped);
|
||||
|
||||
#region Prototypes.
|
||||
#region
|
||||
|
||||
_collection.Add("$$Code: <string>{language(auto,wiki,cpp,lua,graphql,swift,r,yaml,kotlin,scss,shell,vbnet,json,objectivec,perl,diff,wasm,php,xml,bash,csharp,css,go,ini,javascript,less,makefile,markdown,plaintext,python,python-repl,ruby,rust,sql,typescript)}='auto'");
|
||||
_collection.Add(
|
||||
"$$Code: <string>{language(auto,wiki,cpp,lua,graphql,swift,r,yaml,kotlin,scss,shell,vbnet,json,objectivec,perl,diff,wasm,php,xml,bash,csharp,css,go,ini,javascript,less,makefile,markdown,plaintext,python,python-repl,ruby,rust,sql,typescript)}='auto'");
|
||||
_collection.Add("$$Bullets: <string>{type(unordered,ordered)}='unordered'");
|
||||
_collection.Add("$$Order: <string>{direction(ascending,descending)}='ascending'");
|
||||
_collection.Add("$$Jumbotron:");
|
||||
_collection.Add("$$Callout: <string>{styleName(default,primary,secondary,success,info,warning,danger)}='default' | <string>{titleText}=''");
|
||||
_collection.Add("$$Background: <string>{styleName(default,primary,secondary,light,dark,success,info,warning,danger,muted)}='default'");
|
||||
_collection.Add("$$Foreground: <string>{styleName(default,primary,secondary,light,dark,success,info,warning,danger,muted)}='default'");
|
||||
_collection.Add("$$Alert: <string>{styleName(default,primary,secondary,light,dark,success,info,warning,danger)}='default' | <string>{titleText}=''");
|
||||
_collection.Add("$$Card: <string>{styleName(default,primary,secondary,light,dark,success,info,warning,danger)}='default' | <string>{titleText}=''");
|
||||
_collection.Add(
|
||||
"$$Callout: <string>{styleName(default,primary,secondary,success,info,warning,danger)}='default' | <string>{titleText}=''");
|
||||
_collection.Add(
|
||||
"$$Background: <string>{styleName(default,primary,secondary,light,dark,success,info,warning,danger,muted)}='default'");
|
||||
_collection.Add(
|
||||
"$$Foreground: <string>{styleName(default,primary,secondary,light,dark,success,info,warning,danger,muted)}='default'");
|
||||
_collection.Add(
|
||||
"$$Alert: <string>{styleName(default,primary,secondary,light,dark,success,info,warning,danger)}='default' | <string>{titleText}=''");
|
||||
_collection.Add(
|
||||
"$$Card: <string>{styleName(default,primary,secondary,light,dark,success,info,warning,danger)}='default' | <string>{titleText}=''");
|
||||
_collection.Add("$$Collapse: <string>{linkText}='Show'");
|
||||
_collection.Add("$$Table: <boolean>{hasBorder}='true' | <boolean>{isFirstRowHeader}='true'");
|
||||
_collection.Add("$$StripedTable: <boolean>{hasBorder}='true' | <boolean>{isFirstRowHeader}='true'");
|
||||
@@ -47,23 +53,23 @@ namespace ZelWiki.Engine.Implementation
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called to handle function calls when proper prototypes are matched.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="state">Reference to the wiki state object</param>
|
||||
/// <param name="function">The parsed function call and all its parameters and their values.</param>
|
||||
/// <param name="scopeBody">The the text that the function is designed to affect.</param>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="function"></param>
|
||||
/// <param name="scopeBody"></param>
|
||||
/// <returns></returns>
|
||||
public HandlerResult Handle(IZelEngineState state, FunctionCall function, string? scopeBody = null)
|
||||
{
|
||||
scopeBody.EnsureNotNull($"The function '{function.Name}' scope body can not be null");
|
||||
scopeBody.EnsureNotNull($"函数'{function.Name}'作用域主题为空");
|
||||
|
||||
switch (function.Name.ToLower())
|
||||
{
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
case "code":
|
||||
{
|
||||
var html = new StringBuilder();
|
||||
|
||||
string language = function.Parameters.Get<string>("language");
|
||||
var language = function.Parameters.Get<string>("language");
|
||||
if (string.IsNullOrEmpty(language) || language?.ToLower() == "auto")
|
||||
{
|
||||
html.Append($"<pre>");
|
||||
@@ -77,11 +83,9 @@ namespace ZelWiki.Engine.Implementation
|
||||
|
||||
return new HandlerResult(html.ToString())
|
||||
{
|
||||
Instructions = [Constants.HandlerResultInstruction.DisallowNestedProcessing]
|
||||
Instructions = [HandlerResultInstruction.DisallowNestedProcessing]
|
||||
};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
case "stripedtable":
|
||||
case "table":
|
||||
{
|
||||
@@ -96,6 +100,7 @@ namespace ZelWiki.Engine.Implementation
|
||||
{
|
||||
html.Append(" table-striped");
|
||||
}
|
||||
|
||||
if (hasBorder)
|
||||
{
|
||||
html.Append(" table-bordered");
|
||||
@@ -103,7 +108,8 @@ namespace ZelWiki.Engine.Implementation
|
||||
|
||||
html.Append($"\">");
|
||||
|
||||
var lines = scopeBody.Split(['\n'], StringSplitOptions.RemoveEmptyEntries).Select(o => o.Trim()).Where(o => o.Length > 0);
|
||||
var lines = scopeBody.Split(['\n'], StringSplitOptions.RemoveEmptyEntries).Select(o => o.Trim())
|
||||
.Where(o => o.Length > 0);
|
||||
|
||||
int rowNumber = 0;
|
||||
|
||||
@@ -137,6 +143,7 @@ namespace ZelWiki.Engine.Implementation
|
||||
{
|
||||
html.Append($"</thead>");
|
||||
}
|
||||
|
||||
html.Append($"</tr>");
|
||||
|
||||
rowNumber++;
|
||||
@@ -147,27 +154,27 @@ namespace ZelWiki.Engine.Implementation
|
||||
|
||||
return new HandlerResult(html.ToString());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
case "bullets":
|
||||
{
|
||||
var html = new StringBuilder();
|
||||
|
||||
string type = function.Parameters.Get<string>("type");
|
||||
var type = function.Parameters.Get<string>("type");
|
||||
|
||||
if (type == "unordered")
|
||||
{
|
||||
var lines = scopeBody.Split(['\n'], StringSplitOptions.RemoveEmptyEntries).Select(o => o.Trim()).Where(o => o.Length > 0);
|
||||
var lines = scopeBody.Split(['\n'], StringSplitOptions.RemoveEmptyEntries).Select(o => o.Trim())
|
||||
.Where(o => o.Length > 0);
|
||||
|
||||
int currentLevel = 0;
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
int newIndent = 0;
|
||||
var newIndent = 0;
|
||||
for (; newIndent < line.Length && line[newIndent] == '>'; newIndent++)
|
||||
{
|
||||
//Count how many '>' are at the start of the line.
|
||||
//计算行开头有多少个“>”.
|
||||
}
|
||||
|
||||
newIndent++;
|
||||
|
||||
if (newIndent < currentLevel)
|
||||
@@ -195,17 +202,19 @@ namespace ZelWiki.Engine.Implementation
|
||||
}
|
||||
else if (type == "ordered")
|
||||
{
|
||||
var lines = scopeBody.Split(['\n'], StringSplitOptions.RemoveEmptyEntries).Select(o => o.Trim()).Where(o => o.Length > 0);
|
||||
var lines = scopeBody.Split(['\n'], StringSplitOptions.RemoveEmptyEntries).Select(o => o.Trim())
|
||||
.Where(o => o.Length > 0);
|
||||
|
||||
int currentLevel = 0;
|
||||
var currentLevel = 0;
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
int newIndent = 0;
|
||||
var newIndent = 0;
|
||||
for (; newIndent < line.Length && line[newIndent] == '>'; newIndent++)
|
||||
{
|
||||
//Count how many '>' are at the start of the line.
|
||||
//计算行开头有多少个“>”.
|
||||
}
|
||||
|
||||
newIndent++;
|
||||
|
||||
if (newIndent < currentLevel)
|
||||
@@ -231,15 +240,14 @@ namespace ZelWiki.Engine.Implementation
|
||||
html.Append($"</ol>");
|
||||
}
|
||||
}
|
||||
|
||||
return new HandlerResult(html.ToString());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
case "definesnippet":
|
||||
{
|
||||
var html = new StringBuilder();
|
||||
|
||||
string name = function.Parameters.Get<string>("name");
|
||||
var name = function.Parameters.Get<string>("name");
|
||||
|
||||
if (!state.Snippets.TryAdd(name, scopeBody))
|
||||
{
|
||||
@@ -248,14 +256,12 @@ namespace ZelWiki.Engine.Implementation
|
||||
|
||||
return new HandlerResult(html.ToString());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
case "alert":
|
||||
{
|
||||
var html = new StringBuilder();
|
||||
|
||||
string titleText = function.Parameters.Get<string>("titleText");
|
||||
string style = function.Parameters.Get<string>("styleName").ToLower();
|
||||
var titleText = function.Parameters.Get<string>("titleText");
|
||||
var style = function.Parameters.Get<string>("styleName").ToLower();
|
||||
style = style == "default" ? "" : $"alert-{style}";
|
||||
|
||||
if (!string.IsNullOrEmpty(titleText)) scopeBody = $"<h1>{titleText}</h1>{scopeBody}";
|
||||
@@ -267,34 +273,25 @@ namespace ZelWiki.Engine.Implementation
|
||||
{
|
||||
var html = new StringBuilder();
|
||||
|
||||
string direction = function.Parameters.Get<string>("direction");
|
||||
var direction = function.Parameters.Get<string>("direction");
|
||||
var lines = scopeBody.Split("\n").Select(o => o.Trim()).ToList();
|
||||
|
||||
if (direction == "ascending")
|
||||
{
|
||||
html.Append(string.Join("\r\n", lines.OrderBy(o => o)));
|
||||
}
|
||||
else
|
||||
{
|
||||
html.Append(string.Join("\r\n", lines.OrderByDescending(o => o)));
|
||||
}
|
||||
html.Append(direction == "ascending"
|
||||
? string.Join("\r\n", lines.OrderBy(o => o))
|
||||
: string.Join("\r\n", lines.OrderByDescending(o => o)));
|
||||
return new HandlerResult(html.ToString());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
case "jumbotron":
|
||||
{
|
||||
var html = new StringBuilder();
|
||||
|
||||
string titleText = function.Parameters.Get("titleText", "");
|
||||
var titleText = function.Parameters.Get("titleText", "");
|
||||
html.Append($"<div class=\"mt-4 p-5 bg-secondary text-white rounded\">");
|
||||
if (!string.IsNullOrEmpty(titleText)) html.Append($"<h1>{titleText}</h1>");
|
||||
html.Append($"<p>{scopeBody}</p>");
|
||||
html.Append($"</div>");
|
||||
return new HandlerResult(html.ToString());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
case "foreground":
|
||||
{
|
||||
var html = new StringBuilder();
|
||||
@@ -303,31 +300,27 @@ namespace ZelWiki.Engine.Implementation
|
||||
html.Append($"<p class=\"{style.ForegroundStyle} {style.BackgroundStyle}\">{scopeBody}</p>");
|
||||
return new HandlerResult(html.ToString());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
case "background":
|
||||
{
|
||||
var html = new StringBuilder();
|
||||
|
||||
var style = BGFGStyle.GetBackgroundStyle(function.Parameters.Get("styleName", "default"));
|
||||
html.Append($"<div class=\"p-3 mb-2 {style.ForegroundStyle} {style.BackgroundStyle}\">{scopeBody}</div>");
|
||||
html.Append(
|
||||
$"<div class=\"p-3 mb-2 {style.ForegroundStyle} {style.BackgroundStyle}\">{scopeBody}</div>");
|
||||
return new HandlerResult(html.ToString());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
case "collapse":
|
||||
{
|
||||
var html = new StringBuilder();
|
||||
|
||||
string linkText = function.Parameters.Get<string>("linktext");
|
||||
string uid = "A" + Guid.NewGuid().ToString().Replace("-", "");
|
||||
html.Append($"<a data-bs-toggle=\"collapse\" href=\"#{uid}\" role=\"button\" aria-expanded=\"false\" aria-controls=\"{uid}\">{linkText}</a>");
|
||||
var linkText = function.Parameters.Get<string>("linktext");
|
||||
var uid = "A" + Guid.NewGuid().ToString().Replace("-", "");
|
||||
html.Append(
|
||||
$"<a data-bs-toggle=\"collapse\" href=\"#{uid}\" role=\"button\" aria-expanded=\"false\" aria-controls=\"{uid}\">{linkText}</a>");
|
||||
html.Append($"<div class=\"collapse\" id=\"{uid}\">");
|
||||
html.Append($"<div class=\"card card-body\"><p class=\"card-text\">{scopeBody}</p></div></div>");
|
||||
return new HandlerResult(html.ToString());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
case "callout":
|
||||
{
|
||||
var html = new StringBuilder();
|
||||
@@ -342,31 +335,26 @@ namespace ZelWiki.Engine.Implementation
|
||||
html.Append($"</div>");
|
||||
return new HandlerResult(html.ToString());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
case "card":
|
||||
{
|
||||
var html = new StringBuilder();
|
||||
|
||||
string titleText = function.Parameters.Get<string>("titleText");
|
||||
var titleText = function.Parameters.Get<string>("titleText");
|
||||
var style = BGFGStyle.GetBackgroundStyle(function.Parameters.Get("styleName", "default"));
|
||||
|
||||
html.Append($"<div class=\"card {style.ForegroundStyle} {style.BackgroundStyle} mb-3\">");
|
||||
if (string.IsNullOrEmpty(titleText) == false)
|
||||
{
|
||||
html.Append($"<div class=\"card-header\">{titleText}</div>");
|
||||
}
|
||||
|
||||
html.Append("<div class=\"card-body\">");
|
||||
html.Append($"<p class=\"card-text\">{scopeBody}</p>");
|
||||
html.Append("</div>");
|
||||
html.Append("</div>");
|
||||
return new HandlerResult(html.ToString());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new HandlerResult() { Instructions = [Constants.HandlerResultInstruction.Skip] };
|
||||
return new HandlerResult() { Instructions = [HandlerResultInstruction.Skip] };
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,8 +2,14 @@
|
||||
{
|
||||
public class BGFGStyle
|
||||
{
|
||||
public string ForegroundStyle { get; set; } = String.Empty;
|
||||
public string BackgroundStyle { get; set; } = String.Empty;
|
||||
public BGFGStyle()
|
||||
{
|
||||
ForegroundStyle = string.Empty;
|
||||
BackgroundStyle = string.Empty;
|
||||
}
|
||||
|
||||
public string ForegroundStyle { get; set; }
|
||||
public string BackgroundStyle { get; set; }
|
||||
|
||||
public BGFGStyle(string foregroundStyle, string backgroundStyle)
|
||||
{
|
||||
@@ -16,9 +22,6 @@
|
||||
return new BGFGStyle(BackgroundStyle, ForegroundStyle);
|
||||
}
|
||||
|
||||
public BGFGStyle()
|
||||
{
|
||||
}
|
||||
|
||||
public static readonly Dictionary<string, BGFGStyle> ForegroundStyles = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace ZelWiki.Engine.Implementation.Utility
|
||||
public static class Differentiator
|
||||
{
|
||||
/// <summary>
|
||||
/// This leaves a lot to be desired.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="thisRev"></param>
|
||||
/// <param name="prevRev"></param>
|
||||
@@ -16,32 +16,32 @@ namespace ZelWiki.Engine.Implementation.Utility
|
||||
|
||||
var thisRevLines = thisRev.Split('\n');
|
||||
var prevRevLines = prevRev.Split('\n');
|
||||
int thisRevLineCount = thisRevLines.Length;
|
||||
int prevRevLinesCount = prevRevLines.Length;
|
||||
var thisRevLineCount = thisRevLines.Length;
|
||||
var prevRevLinesCount = prevRevLines.Length;
|
||||
|
||||
int linesAdded = prevRevLines.Except(thisRevLines).Count();
|
||||
int linesDeleted = thisRevLines.Except(prevRevLines).Count();
|
||||
var linesAdded = prevRevLines.Except(thisRevLines).Count();
|
||||
var linesDeleted = thisRevLines.Except(prevRevLines).Count();
|
||||
|
||||
if (thisRevLineCount != prevRevLinesCount)
|
||||
{
|
||||
summary.Append($"{Math.Abs(thisRevLineCount - prevRevLinesCount):N0} lines changed.");
|
||||
summary.Append($"{Math.Abs(thisRevLineCount - prevRevLinesCount):N0} 行修改.");
|
||||
}
|
||||
|
||||
if (linesAdded > 0)
|
||||
{
|
||||
if (summary.Length > 0) summary.Append(' ');
|
||||
summary.Append($"{linesAdded:N0} lines added.");
|
||||
summary.Append($"{linesAdded:N0} 行新增.");
|
||||
}
|
||||
|
||||
if (linesDeleted > 0)
|
||||
{
|
||||
if (summary.Length > 0) summary.Append(' ');
|
||||
summary.Append($"{linesDeleted:N0} lines deleted.");
|
||||
summary.Append($"{linesDeleted:N0} 行删除.");
|
||||
}
|
||||
|
||||
if (summary.Length == 0)
|
||||
{
|
||||
summary.Append($"No changes detected.");
|
||||
summary.Append($"未修改.");
|
||||
}
|
||||
|
||||
return summary.ToString();
|
||||
|
||||
@@ -16,10 +16,10 @@ namespace ZelWiki.Engine.Implementation.Utility
|
||||
pages = pages.Take((int)maxCount).ToList();
|
||||
}
|
||||
|
||||
int pageCount = pages.Count;
|
||||
int fontSize = 7;
|
||||
int sizeStep = (pageCount > fontSize ? pageCount : (fontSize * 2)) / fontSize;
|
||||
int pageIndex = 0;
|
||||
var pageCount = pages.Count;
|
||||
var fontSize = 7;
|
||||
var sizeStep = (pageCount > fontSize ? pageCount : (fontSize * 2)) / fontSize;
|
||||
var pageIndex = 0;
|
||||
|
||||
var pageList = new List<TagCloudItem>();
|
||||
|
||||
|
||||
@@ -17,10 +17,10 @@ namespace ZelWiki.Engine.Implementation.Utility
|
||||
tags = tags.Take((int)maxCount).ToList();
|
||||
}
|
||||
|
||||
int tagCount = tags.Count;
|
||||
int fontSize = 7;
|
||||
int sizeStep = (tagCount > fontSize ? tagCount : (fontSize * 2)) / fontSize;
|
||||
int tagIndex = 0;
|
||||
var tagCount = tags.Count;
|
||||
var fontSize = 7;
|
||||
var sizeStep = (tagCount > fontSize ? tagCount : (fontSize * 2)) / fontSize;
|
||||
var tagIndex = 0;
|
||||
|
||||
var tagList = new List<TagCloudItem>();
|
||||
|
||||
|
||||
@@ -2,7 +2,14 @@
|
||||
{
|
||||
public class WeightedSearchToken
|
||||
{
|
||||
public string Token { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public WeightedSearchToken()
|
||||
{
|
||||
Token = string.Empty;
|
||||
}
|
||||
public string Token { get; set; }
|
||||
public double Weight { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
namespace ZelWiki.Engine.Library
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class Constants
|
||||
{
|
||||
public const string SoftBreak = "<!--SoftBreak-->"; //These will remain as \r\n in the final HTML.
|
||||
public const string HardBreak = "<!--HardBreak-->"; //These will remain as <br /> in the final HTML.
|
||||
public const string SoftBreak = "<!--SoftBreak-->";
|
||||
public const string HardBreak = "<!--HardBreak-->";
|
||||
|
||||
public enum WikiMatchType
|
||||
{
|
||||
@@ -23,20 +26,22 @@
|
||||
public enum HandlerResultInstruction
|
||||
{
|
||||
/// <summary>
|
||||
/// Does not process the match, allowing it to be processed by another handler.
|
||||
/// 不处理匹配,允许它由另一个处理程序处理
|
||||
/// </summary>
|
||||
Skip,
|
||||
|
||||
/// <summary>
|
||||
/// Removes any single trailing newline after match.
|
||||
/// 删除匹配后的任何单个尾随换行符
|
||||
/// </summary>
|
||||
TruncateTrailingLine,
|
||||
|
||||
/// <summary>
|
||||
/// Will not continue to process content in this block.
|
||||
/// 将不会继续处理此块中的内容
|
||||
/// </summary>
|
||||
DisallowNestedProcessing,
|
||||
|
||||
/// <summary>
|
||||
/// As opposed to the default functionality of replacing all matches, this will cause ony the first match to be replaced.
|
||||
/// This also means that each match will be processed individually, which can impact performance.
|
||||
///
|
||||
/// </summary>
|
||||
OnlyReplaceFirstMatch
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
namespace ZelWiki.Engine.Library.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles wiki comments. These are generally removed from the result.
|
||||
/// 处理评论.
|
||||
/// </summary>
|
||||
public interface ICommentHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles a wiki comment.
|
||||
/// 处理评论
|
||||
/// </summary>
|
||||
/// <param name="state">Reference to the wiki state object</param>
|
||||
/// <param name="text">The comment text</param>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
public HandlerResult Handle(IZelEngineState state, string text);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
namespace ZelWiki.Engine.Library.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles wiki completion events.
|
||||
/// 处理完成事件
|
||||
/// </summary>
|
||||
public interface ICompletionHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles wiki completion events. Is called when the wiki processing competes for a given page.
|
||||
/// 处理完成事件
|
||||
/// </summary>
|
||||
/// <param name="state">Reference to the wiki state object</param>
|
||||
/// <param name="state"></param>
|
||||
public void Complete(IZelEngineState state);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,17 @@
|
||||
namespace ZelWiki.Engine.Library.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles wiki emojis.
|
||||
/// emoji
|
||||
/// </summary>
|
||||
public interface IEmojiHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles an emoji instruction.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="state">Reference to the wiki state object</param>
|
||||
/// <param name="key">The lookup key for the given emoji.</param>
|
||||
/// <param name="scale">The desired 1-100 scale factor for the emoji.</param>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="scale"></param>
|
||||
/// <returns></returns>
|
||||
public HandlerResult Handle(IZelEngineState state, string key, int scale);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
namespace ZelWiki.Engine.Library.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles exceptions thrown by the wiki engine.
|
||||
/// 错误日志
|
||||
/// </summary>
|
||||
public interface IExceptionHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when an exception is thrown by the wiki engine.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="state">Reference to the wiki state object</param>
|
||||
/// <param name="ex">Optional exception, in the case that this was an actual exception.</param>
|
||||
/// <param name="customText">Text that accompanies the exception.</param>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="ex"></param>
|
||||
/// <param name="customText"></param>
|
||||
public void Log(IZelEngineState state, Exception? ex, string customText);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
namespace ZelWiki.Engine.Library.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles links the wiki to another site.
|
||||
///
|
||||
/// </summary>
|
||||
public interface IExternalLinkHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles an internal wiki link.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="state">Reference to the wiki state object</param>
|
||||
/// <param name="link">The address of the external site being linked to.</param>
|
||||
/// <param name="text">The text which should be show in the absence of an image.</param>
|
||||
/// <param name="image">The image that should be shown.</param>
|
||||
/// <param name="imageScale">The 0-100 image scale factor for the given image.</param>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="link"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="image"></param>
|
||||
/// <returns></returns>
|
||||
public HandlerResult Handle(IZelEngineState state, string link, string? text, string? image);
|
||||
}
|
||||
}
|
||||
@@ -3,22 +3,23 @@
|
||||
namespace ZelWiki.Engine.Library.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Base function handler for standard, post-processing, scoped and processing-instruction functions.
|
||||
/// 处理函数.
|
||||
/// </summary>
|
||||
public interface IFunctionHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a collection of function prototypes.
|
||||
/// 回调.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public FunctionPrototypeCollection Prototypes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Called to handle function calls when proper prototypes are matched.
|
||||
/// 当匹配到合适的原型时,调用以处理函数调用
|
||||
/// </summary>
|
||||
/// <param name="state">Reference to the wiki state object</param>
|
||||
/// <param name="function">The parsed function call and all its parameters and their values.</param>
|
||||
/// <param name="scopeBody">For scope functions, this is the text that the function is designed to affect.</param>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="function"></param>
|
||||
/// <param name="scopeBody"></param>
|
||||
/// <returns></returns>
|
||||
public HandlerResult Handle(IZelEngineState state, FunctionCall function, string? scopeBody = null);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,18 @@
|
||||
namespace ZelWiki.Engine.Library.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles wiki headings. These are automatically added to the table of contents.
|
||||
/// 处理标题
|
||||
/// </summary>
|
||||
public interface IHeadingHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles wiki headings. These are automatically added to the table of contents.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="state">Reference to the wiki state object</param>
|
||||
/// <param name="depth">The size of the header, also used for table of table of contents indentation.</param>
|
||||
/// <param name="link">The self link reference.</param>
|
||||
/// <param name="text">The text for the self link.</param>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="depth"></param>
|
||||
/// <param name="link"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <returns></returns>
|
||||
public HandlerResult Handle(IZelEngineState state, int depth, string link, string text);
|
||||
}
|
||||
}
|
||||
@@ -3,19 +3,21 @@
|
||||
namespace ZelWiki.Engine.Library.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles links from one wiki page to another.
|
||||
/// 处理内链
|
||||
/// </summary>
|
||||
public interface IInternalLinkHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles an internal wiki link.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="state">Reference to the wiki state object</param>
|
||||
/// <param name="pageNavigation">The navigation for the linked page.</param>
|
||||
/// <param name="pageName">The name of the page being linked to.</param>
|
||||
/// <param name="linkText">The text which should be show in the absence of an image.</param>
|
||||
/// <param name="image">The image that should be shown.</param>
|
||||
/// <param name="imageScale">The 0-100 image scale factor for the given image.</param>
|
||||
public HandlerResult Handle(IZelEngineState state, NamespaceNavigation pageNavigation, string pageName, string linkText, string? image, int imageScale);
|
||||
/// <param name="state"></param>
|
||||
/// <param name="pageNavigation"></param>
|
||||
/// <param name="pageName"></param>
|
||||
/// <param name="linkText"></param>
|
||||
/// <param name="image"></param>
|
||||
/// <param name="imageScale"></param>
|
||||
/// <returns></returns>
|
||||
public HandlerResult Handle(IZelEngineState state, NamespaceNavigation pageNavigation, string pageName,
|
||||
string linkText, string? image, int imageScale);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,17 @@
|
||||
namespace ZelWiki.Engine.Library.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles basic markup/style instructions like bole, italic, underline, etc.
|
||||
/// 处理基本的标记/样式指令,如粗体、斜体、下划线等.
|
||||
/// </summary>
|
||||
public interface IMarkupHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles basic markup instructions like bole, italic, underline, etc.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="state">Reference to the wiki state object</param>
|
||||
/// <param name="sequence">The sequence of symbols that were found to denotate this markup instruction,</param>
|
||||
/// <param name="scopeBody">The body of text to apply the style to.</param>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="sequence"></param>
|
||||
/// <param name="scopeBody"></param>
|
||||
/// <returns></returns>
|
||||
public HandlerResult Handle(IZelEngineState state, char sequence, string scopeBody);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace ZelWiki.Engine.Library.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles post-processing function calls.
|
||||
///
|
||||
/// </summary>
|
||||
public interface IPostProcessingFunctionHandler : IFunctionHandler
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace ZelWiki.Engine.Library.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles processing-instruction function calls.
|
||||
///
|
||||
/// </summary>
|
||||
public interface IProcessingInstructionFunctionHandler : IFunctionHandler
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace ZelWiki.Engine.Library.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles scope function calls.
|
||||
///
|
||||
/// </summary>
|
||||
public interface IScopeFunctionHandler : IFunctionHandler
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace ZelWiki.Engine.Library.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles standard function calls.
|
||||
///
|
||||
/// </summary>
|
||||
public interface IStandardFunctionHandler : IFunctionHandler
|
||||
{
|
||||
|
||||
@@ -6,68 +6,63 @@ namespace ZelWiki.Engine.Library.Interfaces
|
||||
{
|
||||
public interface IZelEngineState
|
||||
{
|
||||
#region Parameters.
|
||||
#region 参数
|
||||
|
||||
ISessionState? Session { get; }
|
||||
IQueryCollection QueryString { get; }
|
||||
|
||||
IZelEngine Engine { get; }
|
||||
IPage Page { get; }
|
||||
int? Revision { get; }
|
||||
public ISessionState? Session { get; }
|
||||
public IQueryCollection QueryString { get; }
|
||||
public IZelEngine Engine { get; }
|
||||
public IPage Page { get; }
|
||||
public int? Revision { get; }
|
||||
public HashSet<Constants.WikiMatchType> OmitMatches { get; }
|
||||
public int NestDepth { get; } //Used for recursion.
|
||||
public int NestDepth { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region State.
|
||||
#region 状态
|
||||
|
||||
/// <summary>
|
||||
/// Custom page title set by a call to @@Title("...")
|
||||
/// </summary>
|
||||
public string? PageTitle { get; set; }
|
||||
|
||||
Dictionary<string, string> Variables { get; }
|
||||
Dictionary<string, string> Snippets { get; }
|
||||
List<string> Tags { get; set; }
|
||||
List<string> ProcessingInstructions { get; }
|
||||
List<PageReference> OutgoingLinks { get; }
|
||||
List<TableOfContentsTag> TableOfContents { get; }
|
||||
List<string> Headers { get; }
|
||||
public Dictionary<string, string> Variables { get; }
|
||||
public Dictionary<string, string> Snippets { get; }
|
||||
public List<string> Tags { get; set; }
|
||||
public List<string> ProcessingInstructions { get; }
|
||||
public List<PageReference> OutgoingLinks { get; }
|
||||
public List<TableOfContentsTag> TableOfContents { get; }
|
||||
public List<string> Headers { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Results.
|
||||
#region 结果
|
||||
|
||||
string HtmlResult { get; }
|
||||
TimeSpan ProcessingTime { get; }
|
||||
int ErrorCount { get; }
|
||||
int MatchCount { get; }
|
||||
public string HtmlResult { get; }
|
||||
public TimeSpan ProcessingTime { get; }
|
||||
public int ErrorCount { get; }
|
||||
public int MatchCount { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Used to store values for handlers that needs to survive only a single wiki processing session.
|
||||
///
|
||||
/// </summary>
|
||||
public void SetStateValue<T>(string key, T value);
|
||||
|
||||
/// <summary>
|
||||
/// Used to get values for handlers that needs to survive only a single wiki processing session.
|
||||
///
|
||||
/// </summary>
|
||||
public bool TryGetStateValue<T>(string key, [MaybeNullWhen(false)] out T? outValue);
|
||||
|
||||
/// <summary>
|
||||
/// Used to get values for handlers that needs to survive only a single wiki processing session.
|
||||
///
|
||||
/// </summary>
|
||||
public T GetStateValue<T>(string key, T defaultValue);
|
||||
|
||||
string GetNextQueryToken();
|
||||
|
||||
/// <summary>
|
||||
/// Transforms "included" wiki pages, for example if a wiki function
|
||||
/// injected additional wiki markup, this 'could' be processed separately.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="page">The child page to process</param>
|
||||
/// <param name="revision">The optional revision of the child page to process</param>
|
||||
/// <param name="page"></param>
|
||||
/// <param name="revision"></param>
|
||||
/// <returns></returns>
|
||||
IZelEngineState TransformChild(IPage page, int? revision = null);
|
||||
}
|
||||
|
||||
@@ -2,23 +2,29 @@
|
||||
{
|
||||
public class PageReference
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the page. Such as "Sand Box" or "Some Namespace : SandBox".
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The namespace part of the Name.
|
||||
/// </summary>
|
||||
public string Namespace { get; set; } = string.Empty;
|
||||
|
||||
/// The cleaned up version of the name, safe for passing in URLs.
|
||||
public string Navigation { get; set; } = string.Empty;
|
||||
|
||||
public PageReference()
|
||||
{
|
||||
Name = string.Empty;
|
||||
Namespace = string.Empty;
|
||||
Navigation = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string Namespace { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string Navigation { get; set; }
|
||||
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is PageReference other
|
||||
@@ -30,6 +36,12 @@
|
||||
return Navigation.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="navigation"></param>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public PageReference(string name, string navigation)
|
||||
{
|
||||
var parts = name.Split("::");
|
||||
@@ -45,7 +57,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Invalid page name {name}");
|
||||
throw new Exception($"页面名称无效: {name}");
|
||||
}
|
||||
|
||||
Navigation = navigation;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace ZelWiki.Engine.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// Table of contents tag.
|
||||
///
|
||||
/// </summary>
|
||||
public class TableOfContentsTag
|
||||
{
|
||||
@@ -10,6 +10,13 @@
|
||||
public string Text;
|
||||
public int StartingPosition;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="level"></param>
|
||||
/// <param name="startingPosition"></param>
|
||||
/// <param name="hrefTag"></param>
|
||||
/// <param name="text"></param>
|
||||
public TableOfContentsTag(int level, int startingPosition, string hrefTag, string text)
|
||||
{
|
||||
Level = level;
|
||||
|
||||
@@ -5,18 +5,17 @@ namespace ZelWiki.Engine
|
||||
public class WikiMatchSet
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of match that was found.
|
||||
/// 找到的匹配类型.
|
||||
/// </summary>
|
||||
public Constants.WikiMatchType MatchType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The resulting content of the wiki processing.
|
||||
/// 内容.
|
||||
/// </summary>
|
||||
public string Content { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The content in this segment will be wikified. This is useful to disable on things like error messages
|
||||
/// and literal blocks where the content may contain valid wiki markup but we want it to display verbatim.
|
||||
///
|
||||
/// </summary>
|
||||
public bool AllowNestedDecode { get; set; }
|
||||
}
|
||||
|
||||
@@ -2,7 +2,17 @@
|
||||
{
|
||||
public class WikiOrderedMatch
|
||||
{
|
||||
public string Value { get; set; } = string.Empty;
|
||||
public WikiOrderedMatch()
|
||||
{
|
||||
Value = string.Empty;
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string Value { get; set; }
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int Index { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
namespace ZelWiki.Engine
|
||||
{
|
||||
/// <summary>
|
||||
/// 一些正则
|
||||
/// </summary>
|
||||
internal static partial class PrecompiledRegex
|
||||
{
|
||||
[GeneratedRegex("\\#\\{([\\S\\s]*?)\\}\\#", RegexOptions.IgnoreCase)]
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace ZelWiki.Engine
|
||||
internal static class WikiUtility
|
||||
{
|
||||
/// <summary>
|
||||
/// Skips the namespace and returns just the page name part of the navigation.
|
||||
/// 跳过命名空间,只返回导航的页面名称部分.
|
||||
/// </summary>
|
||||
/// <param name="navigation"></param>
|
||||
/// <returns></returns>
|
||||
@@ -50,7 +50,7 @@ namespace ZelWiki.Engine
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of symbols where the symbol occurs consecutively, more than once. (e.g. "##This##")
|
||||
/// 获取符号连续出现多次的符号列表. ("##This##")
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
|
||||
@@ -5,7 +5,7 @@ using ZelWiki.Models;
|
||||
namespace ZelWiki.Engine
|
||||
{
|
||||
/// <summary>
|
||||
/// Tiny wikifier (reduced feature-set) for things like comments and profile bios.
|
||||
///
|
||||
/// </summary>
|
||||
public class WikifierLite
|
||||
{
|
||||
@@ -17,9 +17,9 @@ namespace ZelWiki.Engine
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 100; i++) //We don't want to process nested wiki forever.
|
||||
for (var i = 0; i < 100; i++)
|
||||
{
|
||||
bool processedMatches = false;
|
||||
var processedMatches = false;
|
||||
var matchStore = new Dictionary<string, string>();
|
||||
|
||||
var content = new WikiString(unprocessedText);
|
||||
@@ -75,7 +75,8 @@ namespace ZelWiki.Engine
|
||||
{
|
||||
if (scale != 100 && scale > 0 && scale <= 500)
|
||||
{
|
||||
var emojiImage = $"<img src=\"/file/Emoji/{key.Trim('%')}?Scale={scale}\" alt=\"{emoji?.Name}\" />";
|
||||
var emojiImage =
|
||||
$"<img src=\"/file/Emoji/{key.Trim('%')}?Scale={scale}\" alt=\"{emoji?.Name}\" />";
|
||||
pageContent.Replace(match.Value, StoreMatch(matchStore, emojiImage));
|
||||
}
|
||||
else
|
||||
@@ -92,7 +93,7 @@ namespace ZelWiki.Engine
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transform basic markup such as bold, italics, underline, etc. for single and multi-line.
|
||||
/// 转换单行和多行的基本标记,如粗体、斜体、下划线等.
|
||||
/// </summary>
|
||||
/// <param name="pageContent"></param>
|
||||
private static void TransformMarkup(WikiString pageContent, Dictionary<string, string> matchStore)
|
||||
@@ -126,12 +127,12 @@ namespace ZelWiki.Engine
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transform links, these can be internal Wiki links or external links.
|
||||
/// 转换链接,这些链接可以是内部Wiki链接或外部链接.
|
||||
/// </summary>
|
||||
/// <param name="pageContent"></param>
|
||||
private static void TransformLinks(WikiString pageContent, Dictionary<string, string> matchStore)
|
||||
{
|
||||
//Parse external explicit links. eg. [[http://test.net]].
|
||||
//解析外部链接 [[http://test.net]].
|
||||
var rgx = new Regex(@"(\[\[http\:\/\/.+?\]\])", RegexOptions.IgnoreCase);
|
||||
var matches = WikiUtility.OrderMatchesByLengthDescending(rgx.Matches(pageContent.ToString()));
|
||||
foreach (var match in matches)
|
||||
@@ -149,7 +150,7 @@ namespace ZelWiki.Engine
|
||||
}
|
||||
}
|
||||
|
||||
//Parse external explicit links. eg. [[https://test.net]].
|
||||
//解析外部链接. [[https://test.net]].
|
||||
rgx = new Regex(@"(\[\[https\:\/\/.+?\]\])", RegexOptions.IgnoreCase);
|
||||
matches = WikiUtility.OrderMatchesByLengthDescending(rgx.Matches(pageContent.ToString()));
|
||||
foreach (var match in matches)
|
||||
@@ -167,7 +168,7 @@ namespace ZelWiki.Engine
|
||||
}
|
||||
}
|
||||
|
||||
//Parse internal dynamic links. eg [[AboutUs|About Us]].
|
||||
//解析内部链接 [[AboutUs|About Us]].
|
||||
rgx = new Regex(@"(\[\[.+?\]\])", RegexOptions.IgnoreCase);
|
||||
matches = WikiUtility.OrderMatchesByLengthDescending(rgx.Matches(pageContent.ToString()));
|
||||
foreach (var match in matches)
|
||||
@@ -177,11 +178,13 @@ namespace ZelWiki.Engine
|
||||
|
||||
if (args.Count == 1)
|
||||
{
|
||||
pageContent.Replace(match.Value, StoreMatch(matchStore, $"<a href=\"{GlobalConfiguration.BasePath}/{args[0]}\">{args[0]}</a>"));
|
||||
pageContent.Replace(match.Value,
|
||||
StoreMatch(matchStore, $"<a href=\"{GlobalConfiguration.BasePath}/{args[0]}\">{args[0]}</a>"));
|
||||
}
|
||||
else if (args.Count > 1)
|
||||
{
|
||||
pageContent.Replace(match.Value, StoreMatch(matchStore, $"<a href=\"{GlobalConfiguration.BasePath}/{args[0]}\">{args[1]}</a>"));
|
||||
pageContent.Replace(match.Value,
|
||||
StoreMatch(matchStore, $"<a href=\"{GlobalConfiguration.BasePath}/{args[0]}\">{args[1]}</a>"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace ZelWiki.Engine
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the content for the given page.
|
||||
/// 转换给定页面的内容.
|
||||
/// </summary>
|
||||
/// <param name="session">The users current state, used for localization.</param>
|
||||
/// <param name="page">The page that is being processed.</param>
|
||||
|
||||
@@ -23,12 +23,13 @@ namespace ZelWiki.Engine
|
||||
private readonly string _tocName = "TOC_" + new Random().Next(0, 1000000).ToString();
|
||||
private readonly Dictionary<string, object> _handlerState = new();
|
||||
|
||||
#region Public properties.
|
||||
#region 公共属性.
|
||||
|
||||
/// <summary>
|
||||
/// Custom page title set by a call to @@Title("...")
|
||||
/// 通过调用设置自定义页面标题 @@Title("...")
|
||||
/// </summary>
|
||||
public string? PageTitle { get; set; }
|
||||
|
||||
public int ErrorCount { get; private set; }
|
||||
public int MatchCount { get; private set; }
|
||||
public int TransformIterations { get; private set; }
|
||||
@@ -45,19 +46,19 @@ namespace ZelWiki.Engine
|
||||
|
||||
#endregion
|
||||
|
||||
#region Input parameters.
|
||||
#region 入参.
|
||||
|
||||
public IPage Page { get; }
|
||||
public int? Revision { get; }
|
||||
public IQueryCollection QueryString { get; }
|
||||
public ISessionState? Session { get; }
|
||||
public HashSet<Constants.WikiMatchType> OmitMatches { get; private set; } = new();
|
||||
public int NestDepth { get; private set; } //Used for recursion.
|
||||
public int NestDepth { get; private set; } //用于递归
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Used to store values for handlers that needs to survive only a single wiki processing session.
|
||||
/// 存储只需要在单个wiki处理会话中生存的处理程序的值.
|
||||
/// </summary>
|
||||
public void SetStateValue<T>(string key, T value)
|
||||
{
|
||||
@@ -65,11 +66,12 @@ namespace ZelWiki.Engine
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_handlerState[key] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to get values for handlers that needs to survive only a single wiki processing session.
|
||||
/// 获取只需要在单个处理会话中生存的处理程序的值.
|
||||
/// </summary>
|
||||
public T GetStateValue<T>(string key, T defaultValue)
|
||||
{
|
||||
@@ -77,12 +79,13 @@ namespace ZelWiki.Engine
|
||||
{
|
||||
return (T)value;
|
||||
}
|
||||
|
||||
SetStateValue(key, defaultValue);
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to get values for handlers that needs to survive only a single wiki processing session.
|
||||
/// 尝试获取只需要在单个处理会话中生存的处理程序的值..
|
||||
/// </summary>
|
||||
public bool TryGetStateValue<T>(string key, [MaybeNullWhen(false)] out T? outValue)
|
||||
{
|
||||
@@ -97,13 +100,14 @@ namespace ZelWiki.Engine
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the ZelEngineState class. Typically created by a call to ZelEngine.Transform().
|
||||
/// 创建一个实例
|
||||
/// </summary>
|
||||
/// <param name="session">The users current state, used for localization.</param>
|
||||
/// <param name="page">The page that is being processed.</param>
|
||||
/// <param name="revision">The revision of the page that is being processed.</param>
|
||||
/// <param name="omitMatches">The type of matches that we want to omit from processing.</param>
|
||||
/// <param name="nestDepth">The current depth of recursion.</param>
|
||||
/// <param name="engine"></param>
|
||||
/// <param name="session"></param>
|
||||
/// <param name="page"></param>
|
||||
/// <param name="revision"></param>
|
||||
/// <param name="omitMatches"></param>
|
||||
/// <param name="nestDepth"></param>
|
||||
internal ZelEngineState(IZelEngine engine, ISessionState? session,
|
||||
IPage page, int? revision = null, Constants.WikiMatchType[]? omitMatches = null, int nestDepth = 0)
|
||||
{
|
||||
@@ -123,15 +127,15 @@ namespace ZelWiki.Engine
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transforms "included" wiki pages, for example if a wiki function
|
||||
/// injected additional wiki markup, this 'could' be processed separately.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="page">The child page to process</param>
|
||||
/// <param name="revision">The optional revision of the child page to process</param>
|
||||
/// <param name="page"></param>
|
||||
/// <param name="revision"></param>
|
||||
/// <returns></returns>
|
||||
public IZelEngineState TransformChild(IPage page, int? revision = null)
|
||||
{
|
||||
return new ZelEngineState(Engine, Session, page, revision, OmitMatches.ToArray(), NestDepth + 1).Transform();
|
||||
return new ZelEngineState(Engine, Session, page, revision, OmitMatches.ToArray(), NestDepth + 1)
|
||||
.Transform();
|
||||
}
|
||||
|
||||
internal IZelEngineState Transform()
|
||||
@@ -169,21 +173,15 @@ namespace ZelWiki.Engine
|
||||
foreach (var v in Matches)
|
||||
{
|
||||
if (OmitMatches.Contains(v.Value.MatchType))
|
||||
{
|
||||
/// When matches are omitted, the entire match will be removed from the resulting wiki text.
|
||||
pageContent.Replace(v.Key, string.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
pageContent.Replace(v.Key, v.Value.Content);
|
||||
}
|
||||
}
|
||||
} while (length != pageContent.Length);
|
||||
|
||||
pageContent.Replace(SoftBreak, "\r\n");
|
||||
pageContent.Replace(HardBreak, "<br />");
|
||||
|
||||
//Prepend any headers that were added by wiki handlers.
|
||||
foreach (var header in Headers)
|
||||
{
|
||||
pageContent.Insert(0, header);
|
||||
@@ -223,7 +221,6 @@ namespace ZelWiki.Engine
|
||||
TransformStandardFunctions(pageContent, false);
|
||||
TransformProcessingInstructionFunctions(pageContent);
|
||||
|
||||
//We have to replace a few times because we could have replace tags (guids) nested inside others.
|
||||
int length;
|
||||
do
|
||||
{
|
||||
@@ -233,15 +230,10 @@ namespace ZelWiki.Engine
|
||||
if (v.Value.AllowNestedDecode)
|
||||
{
|
||||
if (OmitMatches.Contains(v.Value.MatchType))
|
||||
{
|
||||
/// When matches are omitted, the entire match will be removed from the resulting wiki text.
|
||||
pageContent.Replace(v.Key, string.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
pageContent.Replace(v.Key, v.Value.Content);
|
||||
}
|
||||
|
||||
else
|
||||
pageContent.Replace(v.Key, v.Value.Content);
|
||||
}
|
||||
}
|
||||
} while (length != pageContent.Length);
|
||||
@@ -250,7 +242,7 @@ namespace ZelWiki.Engine
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transform basic markup such as bold, italics, underline, etc. for single and multi-line.
|
||||
/// 转换单行和多行的基本标记,如粗体、斜体、下划线等.
|
||||
/// </summary>
|
||||
/// <param name="pageContent"></param>
|
||||
private void TransformMarkup(WikiString pageContent)
|
||||
@@ -270,7 +262,8 @@ namespace ZelWiki.Engine
|
||||
|
||||
var result = Engine.MarkupHandler.Handle(this, symbol, body);
|
||||
|
||||
StoreHandlerResult(result, Constants.WikiMatchType.Markup, pageContent, match.Value, result.Content);
|
||||
StoreHandlerResult(result, WikiMatchType.Markup, pageContent, match.Value,
|
||||
result.Content);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,15 +272,17 @@ namespace ZelWiki.Engine
|
||||
|
||||
foreach (var match in sizeUpOrderedMatches)
|
||||
{
|
||||
int headingMarkers = 0;
|
||||
var headingMarkers = 0;
|
||||
foreach (char c in match.Value)
|
||||
{
|
||||
if (c != '^')
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
headingMarkers++;
|
||||
}
|
||||
|
||||
if (headingMarkers >= 2 && headingMarkers <= 6)
|
||||
{
|
||||
string value = match.Value.Substring(headingMarkers, match.Value.Length - headingMarkers).Trim();
|
||||
@@ -295,44 +290,43 @@ namespace ZelWiki.Engine
|
||||
int fontSize = 1 + headingMarkers;
|
||||
if (fontSize < 1) fontSize = 1;
|
||||
|
||||
string markup = "<font size=\"" + fontSize + "\">" + value + "</font>\r\n";
|
||||
StoreMatch(Constants.WikiMatchType.Markup, pageContent, match.Value, markup);
|
||||
var markup = "<font size=\"" + fontSize + "\">" + value + "</font>\r\n";
|
||||
StoreMatch(WikiMatchType.Markup, pageContent, match.Value, markup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Transform inline and multi-line literal blocks. These are blocks where the content will not be wikified and contain code that is encoded to display verbatim on the page.
|
||||
/// 转换内联和多行文字块
|
||||
/// </summary>
|
||||
/// <param name="pageContent"></param>
|
||||
private void TransformLiterals(WikiString pageContent)
|
||||
{
|
||||
//TODO: May need to do the same thing we did with TransformBlocks() to match all these if they need to be nested.
|
||||
|
||||
//Transform literal strings, even encodes HTML so that it displays verbatim.
|
||||
var orderedMatches = WikiUtility.OrderMatchesByLengthDescending(
|
||||
PrecompiledRegex.TransformLiterals().Matches(pageContent.ToString()));
|
||||
|
||||
foreach (var match in orderedMatches)
|
||||
{
|
||||
string value = match.Value.Substring(2, match.Value.Length - 4);
|
||||
var value = match.Value.Substring(2, match.Value.Length - 4);
|
||||
value = HttpUtility.HtmlEncode(value);
|
||||
StoreMatch(Constants.WikiMatchType.Literal, pageContent, match.Value, value.Replace("\r", "").Trim().Replace("\n", "<br />\r\n"), false);
|
||||
StoreMatch(WikiMatchType.Literal, pageContent, match.Value,
|
||||
value.Replace("\r", "").Trim().Replace("\n", "<br />\r\n"), false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Matching nested blocks with regex was hell, I escaped with a loop. ¯\_(ツ)_/¯
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="pageContent"></param>
|
||||
private void TransformScopeFunctions(WikiString pageContent)
|
||||
{
|
||||
var content = pageContent.ToString();
|
||||
|
||||
string rawBlock = string.Empty;
|
||||
var rawBlock = string.Empty;
|
||||
|
||||
int startPos = content.Length - 1;
|
||||
var startPos = content.Length - 1;
|
||||
|
||||
while (true)
|
||||
{
|
||||
@@ -341,18 +335,24 @@ namespace ZelWiki.Engine
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
int endPos = content.IndexOf("}}", startPos);
|
||||
|
||||
if (endPos < 0 || endPos < startPos)
|
||||
{
|
||||
var exception = new StringBuilder();
|
||||
exception.AppendLine($"<strong>A parsing error occurred after position {startPos}:<br /></strong> Unable to locate closing tag.<br /><br />");
|
||||
exception.AppendLine(
|
||||
$"<strong>定位后发生解析错误 {startPos}:<br /></strong> 无法找到结束标记.<br /><br />");
|
||||
if (rawBlock?.Length > 0)
|
||||
{
|
||||
exception.AppendLine($"<strong>The last successfully parsed block was:</strong><br /> {rawBlock}");
|
||||
exception.AppendLine(
|
||||
$"<strong>最后一个成功解析的块是:</strong><br /> {rawBlock}");
|
||||
}
|
||||
exception.AppendLine($"<strong>The problem occurred after:</strong><br /> {pageContent.ToString().Substring(startPos)}<br /><br />");
|
||||
exception.AppendLine($"<strong>The content the parser was working on is:</strong><br /> {pageContent}<br /><br />");
|
||||
|
||||
exception.AppendLine(
|
||||
$"<strong>问题发生在:</strong><br /> {pageContent.ToString().Substring(startPos)}<br /><br />");
|
||||
exception.AppendLine(
|
||||
$"<strong>解析器正在处理的内容是:</strong><br /> {pageContent}<br /><br />");
|
||||
|
||||
throw new Exception(exception.ToString());
|
||||
}
|
||||
@@ -369,10 +369,10 @@ namespace ZelWiki.Engine
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transform blocks or sections of code, these are thinks like panels and alerts.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="pageContent"></param>
|
||||
/// <param name="isFirstChance">Only process early functions (like code blocks)</param>
|
||||
/// <param name="isFirstChance"></param>
|
||||
private void TransformScopeFunctions(WikiString pageContent, bool isFirstChance)
|
||||
{
|
||||
// {{([\\S\\s]*)}}
|
||||
@@ -387,14 +387,14 @@ namespace ZelWiki.Engine
|
||||
|
||||
FunctionCall function;
|
||||
|
||||
//We are going to mock up a function call:
|
||||
string mockFunctionCall = "##" + match.Value.Trim([' ', '\t', '{', '}']);
|
||||
|
||||
try
|
||||
{
|
||||
function = FunctionParser.ParseAndGetFunctionCall(functionHandler.Prototypes, mockFunctionCall, out paramEndIndex);
|
||||
function = FunctionParser.ParseAndGetFunctionCall(functionHandler.Prototypes, mockFunctionCall,
|
||||
out paramEndIndex);
|
||||
|
||||
var firstChanceFunctions = new string[] { "code" }; //Process these the first time through.
|
||||
var firstChanceFunctions = new [] { "code" };
|
||||
if (isFirstChance && firstChanceFunctions.Contains(function.Name.ToLower()) == false)
|
||||
{
|
||||
continue;
|
||||
@@ -411,7 +411,8 @@ namespace ZelWiki.Engine
|
||||
try
|
||||
{
|
||||
var result = functionHandler.Handle(this, function, scopeBody);
|
||||
StoreHandlerResult(result, Constants.WikiMatchType.ScopeFunction, pageContent, match.Value, scopeBody);
|
||||
StoreHandlerResult(result, WikiMatchType.ScopeFunction, pageContent, match.Value,
|
||||
scopeBody);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -421,7 +422,7 @@ namespace ZelWiki.Engine
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transform headings. These are the basic HTML H1-H6 headings but they are saved for the building of the table of contents.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="pageContent"></param>
|
||||
private void TransformHeadings(WikiString pageContent)
|
||||
@@ -431,28 +432,32 @@ namespace ZelWiki.Engine
|
||||
|
||||
foreach (var match in orderedMatches)
|
||||
{
|
||||
int headingMarkers = 0;
|
||||
foreach (char c in match.Value)
|
||||
var headingMarkers = 0;
|
||||
foreach (var c in match.Value)
|
||||
{
|
||||
if (c != '=')
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
headingMarkers++;
|
||||
}
|
||||
|
||||
if (headingMarkers >= 2)
|
||||
{
|
||||
string link = _tocName + "_" + TableOfContents.Count.ToString();
|
||||
string text = match.Value.Substring(headingMarkers, match.Value.Length - headingMarkers).Trim().Trim(['=']).Trim();
|
||||
var link = _tocName + "_" + TableOfContents.Count.ToString();
|
||||
var text = match.Value.Substring(headingMarkers, match.Value.Length - headingMarkers).Trim()
|
||||
.Trim(['=']).Trim();
|
||||
|
||||
var result = Engine.HeadingHandler.Handle(this, headingMarkers, link, text);
|
||||
|
||||
if (!result.Instructions.Contains(Constants.HandlerResultInstruction.Skip))
|
||||
if (!result.Instructions.Contains(HandlerResultInstruction.Skip))
|
||||
{
|
||||
TableOfContents.Add(new TableOfContentsTag(headingMarkers - 1, match.Index, link, text));
|
||||
}
|
||||
|
||||
StoreHandlerResult(result, Constants.WikiMatchType.Heading, pageContent, match.Value, result.Content);
|
||||
StoreHandlerResult(result, WikiMatchType.Heading, pageContent, match.Value,
|
||||
result.Content);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -465,7 +470,7 @@ namespace ZelWiki.Engine
|
||||
foreach (var match in orderedMatches)
|
||||
{
|
||||
var result = Engine.CommentHandler.Handle(this, match.Value);
|
||||
StoreHandlerResult(result, Constants.WikiMatchType.Comment, pageContent, match.Value, result.Content);
|
||||
StoreHandlerResult(result, WikiMatchType.Comment, pageContent, match.Value, result.Content);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -476,23 +481,23 @@ namespace ZelWiki.Engine
|
||||
|
||||
foreach (var match in orderedMatches)
|
||||
{
|
||||
string key = match.Value.Trim().ToLower().Trim('%');
|
||||
int scale = 100;
|
||||
var key = match.Value.Trim().ToLower().Trim('%');
|
||||
var scale = 100;
|
||||
|
||||
var parts = key.Split(',');
|
||||
if (parts.Length > 1)
|
||||
{
|
||||
key = parts[0]; //Image key;
|
||||
scale = int.Parse(parts[1]); //Image scale.
|
||||
key = parts[0];
|
||||
scale = int.Parse(parts[1]);
|
||||
}
|
||||
|
||||
var result = Engine.EmojiHandler.Handle(this, $"%%{key}%%", scale);
|
||||
StoreHandlerResult(result, Constants.WikiMatchType.Emoji, pageContent, match.Value, result.Content);
|
||||
StoreHandlerResult(result, WikiMatchType.Emoji, pageContent, match.Value, result.Content);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transform variables.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="pageContent"></param>
|
||||
private void TransformVariables(WikiString pageContent)
|
||||
@@ -502,7 +507,7 @@ namespace ZelWiki.Engine
|
||||
|
||||
foreach (var match in orderedMatches)
|
||||
{
|
||||
string key = match.Value.Trim(['{', '}', ' ', '\t', '$']);
|
||||
var key = match.Value.Trim(['{', '}', ' ', '\t', '$']);
|
||||
if (key.Contains("="))
|
||||
{
|
||||
var sections = key.Split('=');
|
||||
@@ -514,27 +519,27 @@ namespace ZelWiki.Engine
|
||||
Variables[key] = value;
|
||||
}
|
||||
|
||||
var identifier = StoreMatch(Constants.WikiMatchType.Variable, pageContent, match.Value, "");
|
||||
var identifier = StoreMatch(WikiMatchType.Variable, pageContent, match.Value, "");
|
||||
pageContent.Replace($"{identifier}\n", $"{identifier}"); //Kill trailing newline.
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Variables.TryGetValue(key, out string? value))
|
||||
{
|
||||
var identifier = StoreMatch(Constants.WikiMatchType.Variable, pageContent, match.Value, value);
|
||||
var identifier = StoreMatch(WikiMatchType.Variable, pageContent, match.Value, value);
|
||||
pageContent.Replace($"{identifier}\n", $"{identifier}"); //Kill trailing newline.
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"The wiki variable {key} is not defined. It should be set with ##Set() before calling Get().");
|
||||
throw new Exception(
|
||||
$"未定义的变量 {key} . 在再用Get()应该先使用##Set().");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transform links, these can be internal Wiki links or external links.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="pageContent"></param>
|
||||
private void TransformLinks(WikiString pageContent)
|
||||
@@ -572,7 +577,6 @@ namespace ZelWiki.Engine
|
||||
}
|
||||
}
|
||||
|
||||
//Parse external explicit links. eg. [[https://test.net]].
|
||||
orderedMatches = WikiUtility.OrderMatchesByLengthDescending(
|
||||
PrecompiledRegex.TransformExplicitHTTPsLinks().Matches(pageContent.ToString()));
|
||||
|
||||
@@ -605,7 +609,6 @@ namespace ZelWiki.Engine
|
||||
}
|
||||
}
|
||||
|
||||
//Parse internal links. eg [[About Us]], [[About Us, Learn about us]], etc..
|
||||
orderedMatches = WikiUtility.OrderMatchesByLengthDescending(
|
||||
PrecompiledRegex.TransformInternalDynamicLinks().Matches(pageContent.ToString()));
|
||||
|
||||
@@ -622,29 +625,26 @@ namespace ZelWiki.Engine
|
||||
|
||||
if (args.Count == 1)
|
||||
{
|
||||
//Page navigation only.
|
||||
text = WikiUtility.GetPageNamePart(args[0]); //Text will be page name since we have an image.
|
||||
text = WikiUtility.GetPageNamePart(args[0]);
|
||||
pageName = args[0];
|
||||
}
|
||||
else if (args.Count >= 2)
|
||||
{
|
||||
//Page navigation and explicit text (possibly image).
|
||||
pageName = args[0];
|
||||
|
||||
string imageTag = "image:";
|
||||
if (args[1].StartsWith(imageTag, StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
image = args[1].Substring(imageTag.Length).Trim();
|
||||
text = WikiUtility.GetPageNamePart(args[0]); //Text will be page name since we have an image.
|
||||
text = WikiUtility.GetPageNamePart(args[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
text = args[1]; //Explicit text.
|
||||
text = args[1];
|
||||
}
|
||||
|
||||
if (args.Count >= 3)
|
||||
{
|
||||
//Get the specified image scale.
|
||||
if (int.TryParse(args[2], out imageScale) == false)
|
||||
{
|
||||
imageScale = 100;
|
||||
@@ -659,32 +659,24 @@ namespace ZelWiki.Engine
|
||||
|
||||
var pageNavigation = new NamespaceNavigation(pageName);
|
||||
|
||||
if (pageName.Trim().StartsWith("::"))
|
||||
{
|
||||
//The user explicitly specified the root (unnamed) namespace.
|
||||
}
|
||||
else if (string.IsNullOrEmpty(pageNavigation.Namespace))
|
||||
{
|
||||
//No namespace was specified, use the current page namespace.
|
||||
pageNavigation.Namespace = Page.Namespace;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Use the namespace that the user explicitly specified.
|
||||
}
|
||||
|
||||
var result = Engine.InternalLinkHandler.Handle(this, pageNavigation, pageName.Trim(':'), text, image, imageScale);
|
||||
if (!result.Instructions.Contains(Constants.HandlerResultInstruction.Skip))
|
||||
if (string.IsNullOrEmpty(pageNavigation.Namespace) || pageName.Trim().StartsWith("::"))
|
||||
pageNavigation.Namespace = Page.Namespace;
|
||||
|
||||
|
||||
var result = Engine.InternalLinkHandler.Handle(this, pageNavigation, pageName.Trim(':'), text, image,
|
||||
imageScale);
|
||||
if (!result.Instructions.Contains(HandlerResultInstruction.Skip))
|
||||
{
|
||||
OutgoingLinks.Add(new PageReference(pageName, pageNavigation.Canonical));
|
||||
}
|
||||
|
||||
StoreHandlerResult(result, Constants.WikiMatchType.Link, pageContent, match.Value, string.Empty);
|
||||
StoreHandlerResult(result, WikiMatchType.Link, pageContent, match.Value, string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transform processing instructions are used to flag pages for specific needs such as deletion, review, draft, etc.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="pageContent"></param>
|
||||
private void TransformProcessingInstructionFunctions(WikiString pageContent)
|
||||
@@ -701,7 +693,8 @@ namespace ZelWiki.Engine
|
||||
|
||||
try
|
||||
{
|
||||
function = FunctionParser.ParseAndGetFunctionCall(functionHandler.Prototypes, match.Value, out int matchEndIndex);
|
||||
function = FunctionParser.ParseAndGetFunctionCall(functionHandler.Prototypes, match.Value,
|
||||
out int matchEndIndex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -712,7 +705,8 @@ namespace ZelWiki.Engine
|
||||
try
|
||||
{
|
||||
var result = functionHandler.Handle(this, function, string.Empty);
|
||||
StoreHandlerResult(result, Constants.WikiMatchType.Instruction, pageContent, match.Value, string.Empty);
|
||||
StoreHandlerResult(result, Constants.WikiMatchType.Instruction, pageContent, match.Value,
|
||||
string.Empty);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -722,9 +716,10 @@ namespace ZelWiki.Engine
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transform functions is used to call wiki functions such as including template pages, setting tags and displaying images.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="pageContent"></param>
|
||||
/// <param name="isFirstChance"></param>
|
||||
private void TransformStandardFunctions(WikiString pageContent, bool isFirstChance)
|
||||
{
|
||||
//Remove the last "(\#\#[\w-]+)" if you start to have matching problems:
|
||||
@@ -739,7 +734,8 @@ namespace ZelWiki.Engine
|
||||
|
||||
try
|
||||
{
|
||||
function = FunctionParser.ParseAndGetFunctionCall(functionHandler.Prototypes, match.Value, out int matchEndIndex);
|
||||
function = FunctionParser.ParseAndGetFunctionCall(functionHandler.Prototypes, match.Value,
|
||||
out int matchEndIndex);
|
||||
}
|
||||
catch (WikiFunctionPrototypeNotDefinedException ex)
|
||||
{
|
||||
@@ -753,6 +749,7 @@ namespace ZelWiki.Engine
|
||||
continue; //This IS a function, but it is meant to be parsed at the end of processing.
|
||||
}
|
||||
}
|
||||
|
||||
StoreError(pageContent, match.Value, ex.Message);
|
||||
continue;
|
||||
}
|
||||
@@ -771,7 +768,8 @@ namespace ZelWiki.Engine
|
||||
try
|
||||
{
|
||||
var result = functionHandler.Handle(this, function, string.Empty);
|
||||
StoreHandlerResult(result, Constants.WikiMatchType.StandardFunction, pageContent, match.Value, string.Empty);
|
||||
StoreHandlerResult(result, Constants.WikiMatchType.StandardFunction, pageContent, match.Value,
|
||||
string.Empty);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -781,8 +779,9 @@ namespace ZelWiki.Engine
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transform post-process are functions that must be called after all other transformations. For example, we can't build a table-of-contents until we have parsed the entire page.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="pageContent"></param>
|
||||
private void TransformPostProcessingFunctions(WikiString pageContent)
|
||||
{
|
||||
//Remove the last "(\#\#[\w-]+)" if you start to have matching problems:
|
||||
@@ -797,7 +796,8 @@ namespace ZelWiki.Engine
|
||||
|
||||
try
|
||||
{
|
||||
function = FunctionParser.ParseAndGetFunctionCall(functionHandler.Prototypes, match.Value, out int matchEndIndex);
|
||||
function = FunctionParser.ParseAndGetFunctionCall(functionHandler.Prototypes, match.Value,
|
||||
out int matchEndIndex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -808,7 +808,8 @@ namespace ZelWiki.Engine
|
||||
try
|
||||
{
|
||||
var result = functionHandler.Handle(this, function, string.Empty);
|
||||
StoreHandlerResult(result, Constants.WikiMatchType.StandardFunction, pageContent, match.Value, string.Empty);
|
||||
StoreHandlerResult(result, Constants.WikiMatchType.StandardFunction, pageContent, match.Value,
|
||||
string.Empty);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -821,33 +822,31 @@ namespace ZelWiki.Engine
|
||||
{
|
||||
string identifier = $"<!--{Guid.NewGuid()}-->";
|
||||
|
||||
//Replace new-lines with single character new line:
|
||||
pageContent.Replace("\r\n", "\n");
|
||||
|
||||
//Replace new-lines with an identifier so we can identify the places we are going to introduce line-breaks:
|
||||
pageContent.Replace("\n", identifier);
|
||||
|
||||
//Replace any consecutive to-be-line-breaks that we are introducing with single line-break identifiers.
|
||||
pageContent.Replace($"{identifier}{identifier}", identifier);
|
||||
|
||||
//Swap in the real line-breaks.
|
||||
pageContent.Replace(identifier, "<br />");
|
||||
}
|
||||
|
||||
#region Utility.
|
||||
|
||||
private void StoreHandlerResult(HandlerResult result, Constants.WikiMatchType matchType, WikiString pageContent, string matchValue, string scopeBody)
|
||||
private void StoreHandlerResult(HandlerResult result, WikiMatchType matchType, WikiString pageContent,
|
||||
string matchValue, string scopeBody)
|
||||
{
|
||||
if (result.Instructions.Contains(Constants.HandlerResultInstruction.Skip))
|
||||
if (result.Instructions.Contains(HandlerResultInstruction.Skip))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool allowNestedDecode = !result.Instructions.Contains(Constants.HandlerResultInstruction.DisallowNestedProcessing);
|
||||
bool allowNestedDecode =
|
||||
!result.Instructions.Contains(HandlerResultInstruction.DisallowNestedProcessing);
|
||||
|
||||
string identifier;
|
||||
|
||||
if (result.Instructions.Contains(Constants.HandlerResultInstruction.OnlyReplaceFirstMatch))
|
||||
if (result.Instructions.Contains(HandlerResultInstruction.OnlyReplaceFirstMatch))
|
||||
{
|
||||
identifier = StoreFirstMatch(matchType, pageContent, matchValue, result.Content, allowNestedDecode);
|
||||
}
|
||||
@@ -860,7 +859,7 @@ namespace ZelWiki.Engine
|
||||
{
|
||||
switch (instruction)
|
||||
{
|
||||
case Constants.HandlerResultInstruction.TruncateTrailingLine:
|
||||
case HandlerResultInstruction.TruncateTrailingLine:
|
||||
pageContent.Replace($"{identifier}\n", $"{identifier}"); //Kill trailing newline.
|
||||
break;
|
||||
}
|
||||
@@ -888,7 +887,7 @@ namespace ZelWiki.Engine
|
||||
{
|
||||
Content = $"<i><font size=\"3\" color=\"#BB0000\">{{{value}}}</font></a>",
|
||||
AllowNestedDecode = false,
|
||||
MatchType = Constants.WikiMatchType.Error
|
||||
MatchType = WikiMatchType.Error
|
||||
};
|
||||
|
||||
Matches.Add(identifier, matchSet);
|
||||
@@ -897,7 +896,8 @@ namespace ZelWiki.Engine
|
||||
return identifier;
|
||||
}
|
||||
|
||||
private string StoreMatch(Constants.WikiMatchType matchType, WikiString pageContent, string match, string value, bool allowNestedDecode = true)
|
||||
private string StoreMatch(WikiMatchType matchType, WikiString pageContent, string match, string value,
|
||||
bool allowNestedDecode = true)
|
||||
{
|
||||
MatchCount++;
|
||||
_matchesStoredPerIteration++;
|
||||
@@ -917,7 +917,8 @@ namespace ZelWiki.Engine
|
||||
return identifier;
|
||||
}
|
||||
|
||||
private string StoreFirstMatch(Constants.WikiMatchType matchType, WikiString pageContent, string match, string value, bool allowNestedDecode = true)
|
||||
private string StoreFirstMatch(WikiMatchType matchType, WikiString pageContent, string match,
|
||||
string value, bool allowNestedDecode = true)
|
||||
{
|
||||
MatchCount++;
|
||||
_matchesStoredPerIteration++;
|
||||
@@ -940,15 +941,14 @@ namespace ZelWiki.Engine
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to generate unique and regenerable tokens so different wikification process can identify
|
||||
/// their own query strings. For instance, we can have more than one pager on a wiki page, this
|
||||
/// allows each pager to track its own current page in the query string.
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string GetNextQueryToken()
|
||||
{
|
||||
_queryTokenHash = ZelWiki.Security.Helpers.Sha256(ZelWiki.Security.Helpers.EncryptString(ZelWiki.Security.Helpers.MachineKey, _queryTokenHash));
|
||||
return $"H{ZelWiki.Security.Helpers.Crc32(_queryTokenHash)}";
|
||||
_queryTokenHash = Security.Helpers.Sha256(
|
||||
Security.Helpers.EncryptString(Security.Helpers.MachineKey, _queryTokenHash));
|
||||
return $"H{Security.Helpers.Crc32(_queryTokenHash)}";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -6,14 +6,14 @@ namespace ZelWiki.Library
|
||||
public static class ConfirmActionHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Generates a link that navigates via GET to a "confirm action" page where the yes link is RED, but the NO button is still GREEN.
|
||||
/// 生成一个链接,通过GET导航到“确认操作”页面,其中“是”链接为红色,但“否”按钮仍为绿色。
|
||||
/// </summary>
|
||||
/// <param name="message">The message to be displayed.</param>
|
||||
/// <param name="linkLabel">the label for the link that will redirect to this confirm action page.</param>
|
||||
/// <param name="controllerURL">The URL which will handle the click of the "yes" or "no" for the confirm action page.</param>
|
||||
/// <param name="parameter">An optional parameter to pass to the page and controller function.</param>
|
||||
/// <param name="yesOrDefaultRedirectURL">The URL to redirect to AFTER the controller has been called if the user selected YES (or NO, if the NO link is not specified.</param>
|
||||
/// <param name="noRedirectURL">The URL to redirect to AFTER the controller has been called if the user selected NO, if not specified, the same link that is provided to yesOrDefaultRedirectURL is used.</param>
|
||||
/// <param name="basePath"></param>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="linkLabel"></param>
|
||||
/// <param name="controllerURL"></param>
|
||||
/// <param name="yesOrDefaultRedirectURL"></param>
|
||||
/// <param name="noRedirectURL"></param>
|
||||
/// <returns></returns>
|
||||
public static string GenerateDangerLink(string basePath, string message, string linkLabel, string controllerURL,
|
||||
string? yesOrDefaultRedirectURL, string? noRedirectURL = null)
|
||||
@@ -30,18 +30,19 @@ namespace ZelWiki.Library
|
||||
param.Append($"&Message={Uri.EscapeDataString(message)}");
|
||||
param.Append($"&Style=Danger");
|
||||
|
||||
return $"<a class=\"btn btn-danger btn-thin\" href=\"{basePath}/Utility/ConfirmAction?{param}\">{linkLabel}</a>";
|
||||
return
|
||||
$"<a class=\"btn btn-danger btn-thin\" href=\"{basePath}/Utility/ConfirmAction?{param}\">{linkLabel}</a>";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a link that navigates via GET to a "confirm action" page where the yes link is GREEN.
|
||||
/// 生成一个链接,通过GET导航到“确认操作”页面,其中“是”链接为绿色。
|
||||
/// </summary>
|
||||
/// <param name="message">The message to be displayed.</param>
|
||||
/// <param name="linkLabel">the label for the link that will redirect to this confirm action page.</param>
|
||||
/// <param name="controllerURL">The URL which will handle the click of the "yes" or "no" for the confirm action page.</param>
|
||||
/// <param name="parameter">An optional parameter to pass to the page and controller function.</param>
|
||||
/// <param name="yesOrDefaultRedirectURL">The URL to redirect to AFTER the controller has been called if the user selected YES (or NO, if the NO link is not specified.</param>
|
||||
/// <param name="noRedirectURL">The URL to redirect to AFTER the controller has been called if the user selected NO, if not specified, the same link that is provided to yesOrDefaultRedirectURL is used.</param>
|
||||
/// <param name="basePath"></param>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="linkLabel"></param>
|
||||
/// <param name="controllerURL"></param>
|
||||
/// <param name="yesOrDefaultRedirectURL"></param>
|
||||
/// <param name="noRedirectURL"></param>
|
||||
/// <returns></returns>
|
||||
public static string GenerateSafeLink(string basePath, string message, string linkLabel, string controllerURL,
|
||||
string? yesOrDefaultRedirectURL, string? noRedirectURL = null)
|
||||
@@ -58,18 +59,19 @@ namespace ZelWiki.Library
|
||||
param.Append($"&Message={Uri.EscapeDataString(message)}");
|
||||
param.Append($"&Style=Safe");
|
||||
|
||||
return $"<a class=\"btn btn-success btn-thin\" href=\"{basePath}/Utility/ConfirmAction?{param}\">{linkLabel}</a>";
|
||||
return
|
||||
$"<a class=\"btn btn-success btn-thin\" href=\"{basePath}/Utility/ConfirmAction?{param}\">{linkLabel}</a>";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a link that navigates via GET to a "confirm action" page where the yes link is YELLOW, but the NO button is still GREEN.
|
||||
/// 生成一个链接,通过GET导航到“确认操作”页面,其中“是”链接为黄色,但“否”按钮仍为绿色。
|
||||
/// </summary>
|
||||
/// <param name="message">The message to be displayed.</param>
|
||||
/// <param name="linkLabel">the label for the link that will redirect to this confirm action page.</param>
|
||||
/// <param name="controllerURL">The URL which will handle the click of the "yes" or "no" for the confirm action page.</param>
|
||||
/// <param name="parameter">An optional parameter to pass to the page and controller function.</param>
|
||||
/// <param name="yesOrDefaultRedirectURL">The URL to redirect to AFTER the controller has been called if the user selected YES (or NO, if the NO link is not specified.</param>
|
||||
/// <param name="noRedirectURL">The URL to redirect to AFTER the controller has been called if the user selected NO, if not specified, the same link that is provided to yesOrDefaultRedirectURL is used.</param>
|
||||
/// <param name="basePath"></param>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="linkLabel"></param>
|
||||
/// <param name="controllerURL"></param>
|
||||
/// <param name="yesOrDefaultRedirectURL"></param>
|
||||
/// <param name="noRedirectURL"></param>
|
||||
/// <returns></returns>
|
||||
public static string GenerateWarnLink(string basePath, string message, string linkLabel, string controllerURL,
|
||||
string? yesOrDefaultRedirectURL, string? noRedirectURL = null)
|
||||
@@ -86,19 +88,19 @@ namespace ZelWiki.Library
|
||||
param.Append($"&Message={Uri.EscapeDataString(message)}");
|
||||
param.Append($"&Style=Warn");
|
||||
|
||||
return $"<a class=\"btn btn-warning btn-thin\" href=\"{basePath}/Utility/ConfirmAction?{param}\">{linkLabel}</a>";
|
||||
return
|
||||
$"<a class=\"btn btn-warning btn-thin\" href=\"{basePath}/Utility/ConfirmAction?{param}\">{linkLabel}</a>";
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
/// <summary>
|
||||
/// Generates a link that navigates via POST to a "confirm action" page where the yes button is RED, but the NO button is still GREEN.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="message">The message to be displayed.</param>
|
||||
/// <param name="buttonLabel">the label for the button that will redirect to this confirm action page.</param>
|
||||
/// <param name="controllerURL">The URL which will handle the click of the "yes" or "no" for the confirm action page.</param>
|
||||
/// <param name="parameter">An optional parameter to pass to the page and controller function.</param>
|
||||
/// <param name="yesOrDefaultRedirectURL">The URL to redirect to AFTER the controller has been called if the user selected YES (or NO, if the NO link is not specified.</param>
|
||||
/// <param name="noRedirectURL">The URL to redirect to AFTER the controller has been called if the user selected NO, if not specified, the same link that is provided to yesOrDefaultRedirectURL is used.</param>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="buttonLabel"></param>
|
||||
/// <param name="controllerURL"></param>
|
||||
/// <param name="yesOrDefaultRedirectURL"></param>
|
||||
/// <param name="noRedirectURL"></param>
|
||||
/// <returns></returns>
|
||||
public static string GenerateDangerButton(string message, string buttonLabel, string controllerURL,
|
||||
string? yesOrDefaultRedirectURL, string? noRedirectURL = null)
|
||||
@@ -115,21 +117,22 @@ namespace ZelWiki.Library
|
||||
html.Append($"<input type='hidden' name='NoRedirectURL' value='{noRedirectURL}' />");
|
||||
html.Append($"<input type='hidden' name='Message' value='{message}' />");
|
||||
html.Append($"<input type='hidden' name='Style' value='Danger' />");
|
||||
html.Append($"<button type='submit' class='btn btn-danger rounded-0' name='ActionToConfirm' value='PurgeDeletedPages'>{buttonLabel}</button>");
|
||||
html.Append(
|
||||
$"<button type='submit' class='btn btn-danger rounded-0' name='ActionToConfirm' value='PurgeDeletedPages'>{buttonLabel}</button>");
|
||||
html.Append("</form>");
|
||||
|
||||
return html.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a link that navigates via POST to a "confirm action" page where the yes and no buttons are GREEN.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="message">The message to be displayed.</param>
|
||||
/// <param name="buttonLabel">the label for the button that will redirect to this confirm action page.</param>
|
||||
/// <param name="controllerURL">The URL which will handle the click of the "yes" or "no" for the confirm action page.</param>
|
||||
/// <param name="parameter">An optional parameter to pass to the page and controller function.</param>
|
||||
/// <param name="yesOrDefaultRedirectURL">The URL to redirect to AFTER the controller has been called if the user selected YES (or NO, if the NO link is not specified.</param>
|
||||
/// <param name="noRedirectURL">The URL to redirect to AFTER the controller has been called if the user selected NO, if not specified, the same link that is provided to yesOrDefaultRedirectURL is used.</param>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="buttonLabel"></param>
|
||||
/// <param name="controllerURL"></param>
|
||||
/// <param name="yesOrDefaultRedirectURL"></param>
|
||||
/// <param name="noRedirectURL"></param>
|
||||
/// <returns></returns>
|
||||
public static string GenerateSafeButton(string message, string buttonLabel, string controllerURL,
|
||||
string? yesOrDefaultRedirectURL, string? noRedirectURL = null)
|
||||
{
|
||||
@@ -146,21 +149,22 @@ namespace ZelWiki.Library
|
||||
html.Append($"<input type='hidden' name='NoRedirectURL' value='{noRedirectURL}' />");
|
||||
html.Append($"<input type='hidden' name='Message' value='{message}' />");
|
||||
html.Append($"<input type='hidden' name='Style' value='Safe' />");
|
||||
html.Append($"<button type='submit' class='btn btn-success rounded-0' name='ActionToConfirm' value='PurgeDeletedPages'>{buttonLabel}</button>");
|
||||
html.Append(
|
||||
$"<button type='submit' class='btn btn-success rounded-0' name='ActionToConfirm' value='PurgeDeletedPages'>{buttonLabel}</button>");
|
||||
html.Append("</form>");
|
||||
|
||||
return html.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a link that navigates via POST to a "confirm action" page where the yes button is YELLOW, but the NO button is still GREEN.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="message">The message to be displayed.</param>
|
||||
/// <param name="buttonLabel">the label for the button that will redirect to this confirm action page.</param>
|
||||
/// <param name="controllerURL">The URL which will handle the click of the "yes" or "no" for the confirm action page.</param>
|
||||
/// <param name="parameter">An optional parameter to pass to the page and controller function.</param>
|
||||
/// <param name="yesOrDefaultRedirectURL">The URL to redirect to AFTER the controller has been called if the user selected YES (or NO, if the NO link is not specified.</param>
|
||||
/// <param name="noRedirectURL">The URL to redirect to AFTER the controller has been called if the user selected NO, if not specified, the same link that is provided to yesOrDefaultRedirectURL is used.</param>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="buttonLabel"></param>
|
||||
/// <param name="controllerURL"></param>
|
||||
/// <param name="yesOrDefaultRedirectURL"></param>
|
||||
/// <param name="noRedirectURL"></param>
|
||||
/// <returns></returns>
|
||||
public static string GenerateWarnButton(string message, string buttonLabel, string controllerURL,
|
||||
string? yesOrDefaultRedirectURL, string? noRedirectURL = null)
|
||||
{
|
||||
@@ -177,11 +181,11 @@ namespace ZelWiki.Library
|
||||
html.Append($"<input type='hidden' name='NoRedirectURL' value='{noRedirectURL}' />");
|
||||
html.Append($"<input type='hidden' name='Message' value='{message}' />");
|
||||
html.Append($"<input type='hidden' name='Style' value='Warn' />");
|
||||
html.Append($"<button type='submit' class='btn btn-warning rounded-0' name='ActionToConfirm' value='PurgeDeletedPages'>{buttonLabel}</button>");
|
||||
html.Append(
|
||||
$"<button type='submit' class='btn btn-warning rounded-0' name='ActionToConfirm' value='PurgeDeletedPages'>{buttonLabel}</button>");
|
||||
html.Append("</form>");
|
||||
|
||||
return html.ToString();
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,9 @@
|
||||
public static class Constants
|
||||
{
|
||||
public const string CRYPTOCHECK = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
public const string DEFAULTUSERNAME = "admin@tightwiki.com";
|
||||
public const string DEFAULTUSERNAME = "admin@ipangci.top";
|
||||
public const string DEFAULTACCOUNT = "admin";
|
||||
public const string DEFAULTPASSWORD = "2Tight2Wiki@";
|
||||
public const string DEFAULTPASSWORD = "Zhu0906.";
|
||||
|
||||
public enum WikiTheme
|
||||
{
|
||||
@@ -16,15 +16,15 @@
|
||||
public enum AdminPasswordChangeState
|
||||
{
|
||||
/// <summary>
|
||||
/// The password has not been changed, display a big warning.
|
||||
/// 密码为默认
|
||||
/// </summary>
|
||||
IsDefault,
|
||||
/// <summary>
|
||||
/// All is well!
|
||||
/// 已修改密码
|
||||
/// </summary>
|
||||
HasBeenChanged,
|
||||
/// <summary>
|
||||
/// The default password status does not exist and the password needs to be set to default.
|
||||
/// 默认密码状态不存在,需要将密码设置为默认值
|
||||
/// </summary>
|
||||
NeedsToBeSet
|
||||
}
|
||||
@@ -45,19 +45,19 @@
|
||||
public static class Roles
|
||||
{
|
||||
/// <summary>
|
||||
/// Administrators can do anything. Add, edit, delete, pages, users, etc.
|
||||
///管理员可以做任何事情。添加、编辑、删除、页面、用户等。
|
||||
/// </summary>
|
||||
public const string Administrator = "Administrator";
|
||||
/// <summary>
|
||||
/// Read-only user with a profile.
|
||||
/// 成员
|
||||
/// </summary>
|
||||
public const string Member = "Member";
|
||||
/// <summary>
|
||||
/// Contributor can add and edit pages.
|
||||
/// 版主
|
||||
/// </summary>
|
||||
public const string Contributor = "Contributor";
|
||||
/// <summary>
|
||||
/// Moderators can add, edit and delete pages.
|
||||
/// 主持
|
||||
/// </summary>
|
||||
public const string Moderator = "Moderator";
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace ZelWiki.Library
|
||||
foreach (var ci in CultureInfo.GetCultures(CultureTypes.SpecificCultures))
|
||||
{
|
||||
var regionInfo = new RegionInfo(ci.Name);
|
||||
if (list.Where(o => o.Value == regionInfo.Name).Any() == false)
|
||||
if (list.Any(o => o.Value == regionInfo.Name) == false)
|
||||
{
|
||||
list.Add(new CountryItem
|
||||
{
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace ZelWiki.Library
|
||||
image.SaveAsBmp(ms);
|
||||
return preferredContentType;
|
||||
case "image/gif":
|
||||
throw new NotImplementedException("Use [ResizeGifImage] for saving animated images.");
|
||||
throw new NotImplementedException("使用 [ResizeGifImage] 保存动图");
|
||||
//image.SaveAsGif(ms);
|
||||
//return preferredContentType;
|
||||
case "image/tiff":
|
||||
|
||||
@@ -9,32 +9,32 @@ namespace ZelWiki.Library.Interfaces
|
||||
IQueryCollection? QueryString { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is the current user (or anonymous) allowed to view?
|
||||
///
|
||||
/// </summary>
|
||||
public bool CanView => true;
|
||||
|
||||
/// <summary>
|
||||
/// Is the current user allowed to edit?
|
||||
///
|
||||
/// </summary>
|
||||
public bool CanEdit { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Is the current user allowed to perform administrative functions?
|
||||
/// 是否允许当前用户执行管理功能?
|
||||
/// </summary>
|
||||
public bool CanAdmin { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Is the current user allowed to moderate content (such as delete comments, and view moderation tools)?
|
||||
/// 是否允许当前用户审核内容(如删除评论和查看审核工具)?
|
||||
/// </summary>
|
||||
public bool CanModerate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Is the current user allowed to create pages?
|
||||
///是否允许当前用户创建页面?
|
||||
/// </summary>
|
||||
public bool CanCreate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Is the current user allowed to delete unprotected pages?
|
||||
/// 是否允许当前用户删除未受保护的页面?
|
||||
/// </summary>
|
||||
public bool CanDelete { get; }
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace ZelWiki.Library
|
||||
name = name.Substring(0, name.IndexOf('(')).Trim();
|
||||
}
|
||||
|
||||
if (list.Where(o => o.Value == name).Any() == false)
|
||||
if (list.Any(o => o.Value == name) == false)
|
||||
{
|
||||
list.Add(new LanguageItem
|
||||
{
|
||||
|
||||
@@ -5,14 +5,14 @@ namespace ZelWiki.Library
|
||||
internal static class MimeTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// Given a file path, determine the MIME type
|
||||
/// 给定文件路径,确定MIME类型
|
||||
/// </summary>
|
||||
/// <param name="subpath">A file path</param>
|
||||
/// <param name="filePath">A file path</param>
|
||||
/// <param name="contentType">The resulting MIME type</param>
|
||||
/// <returns>True if MIME type could be determined</returns>
|
||||
public static bool TryGetContentType(string filePath, [MaybeNullWhen(false)] out string contentType)
|
||||
{
|
||||
string extension = Path.GetExtension(filePath);
|
||||
var extension = Path.GetExtension(filePath);
|
||||
if (extension == null)
|
||||
{
|
||||
contentType = null;
|
||||
@@ -21,7 +21,6 @@ namespace ZelWiki.Library
|
||||
return Collection.TryGetValue(extension, out contentType);
|
||||
}
|
||||
|
||||
//Borrowed from FileExtensionContentTypeProvider().TryGetContentType
|
||||
public static Dictionary<string, string> Collection = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{ ".323", "text/h323" },
|
||||
@@ -78,7 +77,7 @@ namespace ZelWiki.Library
|
||||
{ ".crt", "application/x-x509-ca-cert" },
|
||||
{ ".csh", "application/x-csh" },
|
||||
{ ".css", "text/css" },
|
||||
{ ".csv", "text/csv" }, // https://tools.ietf.org/html/rfc7111#section-5.1
|
||||
{ ".csv", "text/csv" },
|
||||
{ ".cur", "application/octet-stream" },
|
||||
{ ".dcr", "application/x-director" },
|
||||
{ ".deploy", "application/octet-stream" },
|
||||
@@ -106,7 +105,7 @@ namespace ZelWiki.Library
|
||||
{ ".eps", "application/postscript" },
|
||||
{ ".etx", "text/x-setext" },
|
||||
{ ".evy", "application/envoy" },
|
||||
{ ".exe", "application/vnd.microsoft.portable-executable" }, // https://www.iana.org/assignments/media-types/application/vnd.microsoft.portable-executable
|
||||
{ ".exe", "application/vnd.microsoft.portable-executable" },
|
||||
{ ".fdf", "application/vnd.fdf" },
|
||||
{ ".fif", "application/fractals" },
|
||||
{ ".fla", "application/octet-stream" },
|
||||
@@ -347,7 +346,7 @@ namespace ZelWiki.Library
|
||||
{ ".wcm", "application/vnd.ms-works" },
|
||||
{ ".wdb", "application/vnd.ms-works" },
|
||||
{ ".webm", "video/webm" },
|
||||
{ ".webmanifest", "application/manifest+json" }, // https://w3c.github.io/manifest/#media-type-registration
|
||||
{ ".webmanifest", "application/manifest+json" },
|
||||
{ ".webp", "image/webp" },
|
||||
{ ".wks", "application/vnd.ms-works" },
|
||||
{ ".wm", "video/x-ms-wm" },
|
||||
@@ -362,8 +361,8 @@ namespace ZelWiki.Library
|
||||
{ ".wmv", "video/x-ms-wmv" },
|
||||
{ ".wmx", "video/x-ms-wmx" },
|
||||
{ ".wmz", "application/x-ms-wmz" },
|
||||
{ ".woff", "application/font-woff" }, // https://www.w3.org/TR/WOFF/#appendix-b
|
||||
{ ".woff2", "font/woff2" }, // https://www.w3.org/TR/WOFF2/#IMT
|
||||
{ ".woff", "application/font-woff" },
|
||||
{ ".woff2", "font/woff2" },
|
||||
{ ".wps", "application/vnd.ms-works" },
|
||||
{ ".wri", "application/x-mswrite" },
|
||||
{ ".wrl", "x-world/x-vrml" },
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace ZelWiki.Library
|
||||
{
|
||||
return Page;
|
||||
}
|
||||
|
||||
return $"{Namespace}::{Page}";
|
||||
}
|
||||
set
|
||||
@@ -49,9 +50,9 @@ namespace ZelWiki.Library
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of NamespaceNavigation.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="givenCanonical">Page navigation with optional namespace.</param>
|
||||
/// <param name="givenCanonical"></param>
|
||||
public NamespaceNavigation(string givenCanonical)
|
||||
{
|
||||
_lowerCase = true;
|
||||
@@ -59,10 +60,10 @@ namespace ZelWiki.Library
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of NamespaceNavigation.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="givenCanonical">Page navigation with optional namespace.</param>
|
||||
/// <param name="lowerCase">If false, the namespace and page name will not be lowercased.</param>
|
||||
/// <param name="givenCanonical"></param>
|
||||
/// <param name="lowerCase"></param>
|
||||
public NamespaceNavigation(string givenCanonical, bool lowerCase)
|
||||
{
|
||||
_lowerCase = lowerCase;
|
||||
@@ -75,10 +76,10 @@ namespace ZelWiki.Library
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes a page name with optional namespace and returns the cleaned version that can be used for matching Navigations.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="givenCanonical">Page navigation with optional namespace.</param>
|
||||
/// <param name="lowerCase">If false, the namespace and page name will not be lowercased.</param>
|
||||
/// <param name="str"></param>
|
||||
/// <param name="lowerCase"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public static string CleanAndValidate(string? str, bool lowerCase = true)
|
||||
@@ -88,7 +89,6 @@ namespace ZelWiki.Library
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
//Fix names like "::Page" or "Namespace::".
|
||||
str = str.Trim().Trim([':']).Trim();
|
||||
|
||||
if (str.Contains("::"))
|
||||
@@ -96,23 +96,22 @@ namespace ZelWiki.Library
|
||||
var parts = str.Split("::");
|
||||
if (parts.Length != 2)
|
||||
{
|
||||
throw new Exception("Navigation can not contain more than one namespace.");
|
||||
throw new Exception("导航不能包含多个命名空间");
|
||||
}
|
||||
|
||||
return $"{CleanAndValidate(parts[0].Trim())}::{CleanAndValidate(parts[1].Trim(), lowerCase)}";
|
||||
}
|
||||
|
||||
// Decode common HTML entities
|
||||
str = str.Replace(""", "\"")
|
||||
.Replace("&", "&")
|
||||
.Replace("<", "<")
|
||||
.Replace(">", ">")
|
||||
.Replace(" ", " ");
|
||||
|
||||
// Normalize backslashes to forward slashes
|
||||
str = str.Replace('\\', '/');
|
||||
|
||||
var sb = new StringBuilder();
|
||||
foreach (char c in str)
|
||||
foreach (var c in str)
|
||||
{
|
||||
if (char.IsWhiteSpace(c) || c == '.')
|
||||
{
|
||||
@@ -124,21 +123,13 @@ namespace ZelWiki.Library
|
||||
}
|
||||
}
|
||||
|
||||
string result = sb.ToString();
|
||||
var result = sb.ToString();
|
||||
|
||||
// Remove multiple consecutive underscores or slashes
|
||||
result = Regex.Replace(result, @"[_]{2,}", "_");
|
||||
result = Regex.Replace(result, @"[/]{2,}", "/");
|
||||
|
||||
|
||||
if (lowerCase)
|
||||
{
|
||||
return result.TrimEnd(['/', '\\']).ToLowerInvariant();
|
||||
}
|
||||
else
|
||||
{
|
||||
return result.TrimEnd(['/', '\\']);
|
||||
}
|
||||
return lowerCase ? result.TrimEnd(['/', '\\']).ToLowerInvariant() : result.TrimEnd(['/', '\\']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,9 @@ using System.Text.RegularExpressions;
|
||||
|
||||
namespace ZelWiki.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// 导航
|
||||
/// </summary>
|
||||
public class Navigation
|
||||
{
|
||||
public static string Clean(string? str)
|
||||
@@ -12,29 +15,25 @@ namespace ZelWiki.Library
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
//Fix names like "::Page" or "Namespace::".
|
||||
str = str.Trim().Trim([':']).Trim();
|
||||
|
||||
if (str.Contains("::"))
|
||||
{
|
||||
throw new Exception("Navigation can not contain a namespace.");
|
||||
throw new Exception("导航不能包含命名空间");
|
||||
}
|
||||
|
||||
// Decode common HTML entities
|
||||
str = str.Replace(""", "\"")
|
||||
.Replace("&", "&")
|
||||
.Replace("<", "<")
|
||||
.Replace(">", ">")
|
||||
.Replace(" ", " ");
|
||||
|
||||
// Normalize backslashes to forward slashes
|
||||
str = str.Replace('\\', '/');
|
||||
|
||||
// Replace special sequences
|
||||
str = str.Replace("::", "_").Trim();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
foreach (char c in str)
|
||||
foreach (var c in str)
|
||||
{
|
||||
if (char.IsWhiteSpace(c) || c == '.')
|
||||
{
|
||||
@@ -46,9 +45,8 @@ namespace ZelWiki.Library
|
||||
}
|
||||
}
|
||||
|
||||
string result = sb.ToString();
|
||||
var result = sb.ToString();
|
||||
|
||||
// Remove multiple consecutive underscores or slashes
|
||||
result = Regex.Replace(result, @"[_]{2,}", "_");
|
||||
result = Regex.Replace(result, @"[/]{2,}", "/");
|
||||
|
||||
|
||||
@@ -42,25 +42,25 @@ namespace ZelWiki.Library
|
||||
sb.Append($"<center>");
|
||||
if (currentPage > 1)
|
||||
{
|
||||
sb.Append($"<a href=\"{url}?{QueryStringConverter.FromCollection(firstPage)}\"><< First</a>");
|
||||
sb.Append($"<a href=\"{url}?{QueryStringConverter.FromCollection(firstPage)}\"><< 首页</a>");
|
||||
sb.Append(" | ");
|
||||
sb.Append($"<a href=\"{url}?{QueryStringConverter.FromCollection(prevPage)}\">< Previous</a>");
|
||||
sb.Append($"<a href=\"{url}?{QueryStringConverter.FromCollection(prevPage)}\">< 上一页</a>");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append($"<< First | < Previous");
|
||||
sb.Append($"<< 首页 | < 上一页");
|
||||
}
|
||||
sb.Append(" | ");
|
||||
|
||||
if (currentPage < totalPageCount)
|
||||
{
|
||||
sb.Append($"<a href=\"{url}?{QueryStringConverter.FromCollection(nextPage)}\">Next ></a>");
|
||||
sb.Append($"<a href=\"{url}?{QueryStringConverter.FromCollection(nextPage)}\">下一页 ></a>");
|
||||
sb.Append(" | ");
|
||||
sb.Append($"<a href=\"{url}?{QueryStringConverter.FromCollection(lastPage)}\">Last >></a>");
|
||||
sb.Append($"<a href=\"{url}?{QueryStringConverter.FromCollection(lastPage)}\">尾页 >></a>");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append("Next > | Last >>");
|
||||
sb.Append("下一页 > | 尾页 >>");
|
||||
}
|
||||
sb.Append($"</center>");
|
||||
}
|
||||
|
||||
@@ -8,18 +8,19 @@ namespace ZelWiki.Library
|
||||
public static class QueryStringConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Takes the current page query string and upserts the given order-by field,
|
||||
/// if the string already sorts on the given field then the order is inverted (asc/desc).
|
||||
/// 排序
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static string OrderHelper(ISessionState context, string value)
|
||||
{
|
||||
string orderByKey = "OrderBy";
|
||||
string orderByDirectionKey = "OrderByDirection";
|
||||
string? currentDirection = "asc";
|
||||
var orderByKey = "OrderBy";
|
||||
var orderByDirectionKey = "OrderByDirection";
|
||||
var currentDirection = "asc";
|
||||
var collection = ToDictionary(context.QueryString);
|
||||
|
||||
//Check to see if we are sorting on the value that we are already sorted on, this would mean we need to invert the sort.
|
||||
|
||||
if (collection.TryGetValue(orderByKey, out var currentValue))
|
||||
{
|
||||
bool invertDirection = string.Equals(currentValue, value, StringComparison.InvariantCultureIgnoreCase);
|
||||
@@ -28,14 +29,7 @@ namespace ZelWiki.Library
|
||||
{
|
||||
if (collection.TryGetValue(orderByDirectionKey, out currentDirection))
|
||||
{
|
||||
if (currentDirection == "asc")
|
||||
{
|
||||
currentDirection = "desc";
|
||||
}
|
||||
else
|
||||
{
|
||||
currentDirection = "asc";
|
||||
}
|
||||
currentDirection = currentDirection == "asc" ? "desc" : "asc";
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -53,13 +47,13 @@ namespace ZelWiki.Library
|
||||
return FromCollection(collection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes the current page query string and upserts a query key/value, replacing any conflicting query string entry.
|
||||
/// </summary>
|
||||
/// <param name="queryString"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="queryString"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static string Upsert(IQueryCollection? queryString, string name, string value)
|
||||
{
|
||||
var collection = ToDictionary(queryString);
|
||||
@@ -79,8 +73,6 @@ namespace ZelWiki.Library
|
||||
|
||||
foreach (var item in queryString)
|
||||
{
|
||||
//Technically, keys can be duplicated in a IQueryCollection but we do not
|
||||
//support this. Use .Single() to throw exception if duplicates are found.
|
||||
dictionary.Add(item.Key, item.Value.Single() ?? string.Empty);
|
||||
}
|
||||
|
||||
@@ -98,13 +90,11 @@ namespace ZelWiki.Library
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
// If the query string starts with '?', remove it
|
||||
if (queryString.StartsWith('?'))
|
||||
{
|
||||
queryString = queryString.Substring(1);
|
||||
}
|
||||
|
||||
// Split the query string into key-value pairs
|
||||
var keyValuePairs = queryString.Split('&');
|
||||
|
||||
foreach (var kvp in keyValuePairs)
|
||||
@@ -158,6 +148,7 @@ namespace ZelWiki.Library
|
||||
{
|
||||
queryString.Append('&');
|
||||
}
|
||||
|
||||
queryString.Append($"{Uri.EscapeDataString(kvp.Key)}={Uri.EscapeDataString(kvp.Value)}");
|
||||
}
|
||||
|
||||
@@ -176,6 +167,7 @@ namespace ZelWiki.Library
|
||||
{
|
||||
clone.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,28 @@
|
||||
{
|
||||
public class Theme
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string DelimitedFiles { get; set; } = string.Empty;
|
||||
public string ClassNavBar { get; set; } = string.Empty;
|
||||
public string ClassNavLink { get; set; } = string.Empty;
|
||||
public string ClassDropdown { get; set; } = string.Empty;
|
||||
public string ClassBranding { get; set; } = string.Empty;
|
||||
public string EditorTheme { get; set; } = string.Empty;
|
||||
public List<string> Files { get; set; } = new();
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public Theme()
|
||||
{
|
||||
Name = string.Empty;
|
||||
DelimitedFiles = string.Empty;
|
||||
ClassNavBar = string.Empty;
|
||||
ClassNavLink = string.Empty;
|
||||
ClassDropdown = string.Empty;
|
||||
ClassBranding = string.Empty;
|
||||
EditorTheme = string.Empty;
|
||||
Files = new();
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
public string DelimitedFiles { get; set; }
|
||||
public string ClassNavBar { get; set; }
|
||||
public string ClassNavLink { get; set; }
|
||||
public string ClassDropdown { get; set; }
|
||||
public string ClassBranding { get; set; }
|
||||
public string EditorTheme { get; set; }
|
||||
public List<string> Files { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,17 @@
|
||||
{
|
||||
public class TimeZoneItem
|
||||
{
|
||||
public string Text { get; set; } = string.Empty;
|
||||
public string Value { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public TimeZoneItem()
|
||||
{
|
||||
Text = string.Empty;
|
||||
Value = string.Empty;
|
||||
}
|
||||
|
||||
public string Text { get; set; }
|
||||
public string Value { get; set; }
|
||||
|
||||
public static List<TimeZoneItem> GetAll()
|
||||
{
|
||||
|
||||
@@ -10,7 +10,6 @@ namespace ZelWiki.Library
|
||||
|
||||
public static string SanitizeAccountName(string fileName, char[]? extraInvalidCharacters = null)
|
||||
{
|
||||
// Get array of invalid characters for file names
|
||||
var invalidChars = Path.GetInvalidFileNameChars().ToList();
|
||||
|
||||
if (extraInvalidCharacters != null)
|
||||
@@ -27,7 +26,7 @@ namespace ZelWiki.Library
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Take a height and width and enforces a max on both dimensions while maintaining the ratio.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="originalWidth"></param>
|
||||
/// <param name="originalHeight"></param>
|
||||
@@ -35,20 +34,16 @@ namespace ZelWiki.Library
|
||||
/// <returns></returns>
|
||||
public static (int Width, int Height) ScaleToMaxOf(int originalWidth, int originalHeight, int maxSize)
|
||||
{
|
||||
// Calculate aspect ratio
|
||||
float aspectRatio = (float)originalWidth / originalHeight;
|
||||
var aspectRatio = (float)originalWidth / originalHeight;
|
||||
|
||||
// Determine new dimensions based on the larger dimension
|
||||
int newWidth, newHeight;
|
||||
if (originalWidth > originalHeight)
|
||||
{
|
||||
// Scale down the width to the maxSize and calculate the height
|
||||
newWidth = maxSize;
|
||||
newHeight = (int)(maxSize / aspectRatio);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Scale down the height to the maxSize and calculate the width
|
||||
newHeight = maxSize;
|
||||
newWidth = (int)(maxSize * aspectRatio);
|
||||
}
|
||||
@@ -72,7 +67,7 @@ namespace ZelWiki.Library
|
||||
public static byte[] ConvertHttpFileToBytes(IFormFile image)
|
||||
{
|
||||
using var stream = image.OpenReadStream();
|
||||
using BinaryReader reader = new BinaryReader(stream);
|
||||
using var reader = new BinaryReader(stream);
|
||||
return reader.ReadBytes((int)image.Length);
|
||||
}
|
||||
|
||||
@@ -80,7 +75,7 @@ namespace ZelWiki.Library
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
return Array.Empty<byte>();
|
||||
return [];
|
||||
}
|
||||
|
||||
using var compressedStream = new MemoryStream();
|
||||
@@ -88,15 +83,15 @@ namespace ZelWiki.Library
|
||||
{
|
||||
compressor.Write(data, 0, data.Length);
|
||||
}
|
||||
|
||||
return compressedStream.ToArray();
|
||||
}
|
||||
|
||||
public static byte[] Decompress(byte[] data)
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
return Array.Empty<byte>();
|
||||
}
|
||||
return [];
|
||||
|
||||
|
||||
using var compressedStream = new MemoryStream(data);
|
||||
using var decompressor = new GZipStream(compressedStream, CompressionMode.Decompress);
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
public T? Value<T>(string name)
|
||||
{
|
||||
var value = Collection.Where(o => o.Name == name).FirstOrDefault();
|
||||
var value = Collection.FirstOrDefault(o => o.Name == name);
|
||||
if (value == null)
|
||||
{
|
||||
return default;
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
public T Value<T>(string name, T defaultValue)
|
||||
{
|
||||
var value = Collection.Where(o => o.Name == name).FirstOrDefault();
|
||||
var value = Collection.FirstOrDefault(o => o.Name == name);
|
||||
if (value == null)
|
||||
{
|
||||
return defaultValue;
|
||||
|
||||
@@ -19,12 +19,7 @@ namespace ZelWiki.Models.DataModels
|
||||
|
||||
public T? As<T>(T defaultValue)
|
||||
{
|
||||
if (Value == null)
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return Converters.ConvertTo<T>(Value);
|
||||
return Value == null ? defaultValue : Converters.ConvertTo<T>(Value);
|
||||
}
|
||||
|
||||
public string DataType { get; set; } = string.Empty;
|
||||
|
||||
@@ -7,23 +7,23 @@ namespace ZelWiki.Models.DataModels
|
||||
public int Id { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The revision of this page that is being viewed. May not be the latest revision.
|
||||
/// 正在查看的此页面的修订版。可能不是最新版本
|
||||
/// </summary>
|
||||
public int Revision { get; set; }
|
||||
/// <summary>
|
||||
/// The most current revision of this page.
|
||||
/// 此页面的最新修订版
|
||||
/// </summary>
|
||||
public int MostCurrentRevision { get; set; }
|
||||
|
||||
public bool IsHistoricalVersion => Revision != MostCurrentRevision;
|
||||
|
||||
/// <summary>
|
||||
/// Lets us know whether this page exists and is loaded.
|
||||
/// 此页面是否存在并已加载
|
||||
/// </summary>
|
||||
public bool Exists => Id > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Count of revisions higher than Revision.
|
||||
/// 修订次数高于修订次数
|
||||
/// </summary>
|
||||
public int HigherRevisionCount { get; set; }
|
||||
public int DeletedRevisionCount { get; set; }
|
||||
@@ -36,8 +36,8 @@ namespace ZelWiki.Models.DataModels
|
||||
{
|
||||
get
|
||||
{
|
||||
int idealLength = 64;
|
||||
int maxLength = 100;
|
||||
var idealLength = 64;
|
||||
var maxLength = 100;
|
||||
|
||||
if (Description.Length > idealLength)
|
||||
{
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
namespace ZelWiki.Models.DataModels
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to cache pre-processed wiki results.
|
||||
/// 用于缓存预处理的结果
|
||||
/// </summary>
|
||||
public class PageCache
|
||||
{
|
||||
public PageCache(string body)
|
||||
{
|
||||
Body = body;
|
||||
}
|
||||
/// <summary>
|
||||
/// Custom page title set by a call to @@Title("...")
|
||||
/// 通过调用@@Title("...")设置自定义页面title
|
||||
/// </summary>
|
||||
public string? PageTitle { get; set; }
|
||||
public string Body { get; set; }
|
||||
|
||||
public PageCache(string body)
|
||||
{
|
||||
Body = body;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace ZelWiki.Models.DataModels
|
||||
{
|
||||
public int PageId { get; set; }
|
||||
/// <summary>
|
||||
/// TightWiki.Library.Constants.WikiInstruction
|
||||
///
|
||||
/// </summary>
|
||||
public string Instruction { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
public List<ProcessingInstruction> Collection { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the collection contains the given processing instruction.
|
||||
/// 如果集合包含给定的处理指令,则返回true
|
||||
/// </summary>
|
||||
/// <param name="wikiInstruction">WikiInstruction.Protect</param>
|
||||
/// <returns></returns>
|
||||
|
||||
@@ -13,11 +13,12 @@ namespace ZelWiki.Repository
|
||||
{
|
||||
public static class ConfigurationRepository
|
||||
{
|
||||
#region Upgrade Database.
|
||||
#region
|
||||
|
||||
public static string GetVersionStateVersion()
|
||||
{
|
||||
var entries = ManagedDataStorage.Config.ExecuteScalar<string>(@"Scripts\Initialization\GetVersionStateVersion.sql");
|
||||
var entries =
|
||||
ManagedDataStorage.Config.ExecuteScalar<string>(@"Scripts\Initialization\GetVersionStateVersion.sql");
|
||||
return entries ?? "0.0.0";
|
||||
}
|
||||
|
||||
@@ -25,11 +26,12 @@ namespace ZelWiki.Repository
|
||||
{
|
||||
var version = string.Join('.',
|
||||
(Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "0.0.0.0").Split('.').Take(3));
|
||||
ManagedDataStorage.Config.Execute(@"Scripts\Initialization\SetVersionStateVersion.sql", new { Version = version });
|
||||
ManagedDataStorage.Config.Execute(@"Scripts\Initialization\SetVersionStateVersion.sql",
|
||||
new { Version = version });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See @Initialization.Versions.md
|
||||
///
|
||||
/// </summary>
|
||||
public static void UpgradeDatabase()
|
||||
{
|
||||
@@ -45,43 +47,44 @@ namespace ZelWiki.Repository
|
||||
|
||||
if (currentPaddedVersion == storedPaddedVersion)
|
||||
{
|
||||
return; //The database version is already at the latest version.
|
||||
return;
|
||||
}
|
||||
|
||||
var updateScriptNames = Assembly.GetExecutingAssembly().GetManifestResourceNames()
|
||||
.Where(o => o.Contains("Repository.Scripts.Initialization.Versions", StringComparison.InvariantCultureIgnoreCase)).OrderBy(o => o);
|
||||
.Where(o => o.Contains("Repository.Scripts.Initialization.Versions",
|
||||
StringComparison.InvariantCultureIgnoreCase)).OrderBy(o => o);
|
||||
|
||||
string startVersionTag = ".Initialization.Versions.";
|
||||
string endVersionTag = ".^";
|
||||
var startVersionTag = ".Initialization.Versions.";
|
||||
var endVersionTag = ".^";
|
||||
|
||||
foreach (var updateScriptName in updateScriptNames)
|
||||
{
|
||||
int startIndex = updateScriptName.IndexOf(startVersionTag, StringComparison.InvariantCultureIgnoreCase);
|
||||
int startIndex =
|
||||
updateScriptName.IndexOf(startVersionTag, StringComparison.InvariantCultureIgnoreCase);
|
||||
if (startIndex >= 0)
|
||||
{
|
||||
startIndex += startVersionTag.Length;
|
||||
|
||||
int endIndex = updateScriptName.IndexOf(endVersionTag, startIndex, StringComparison.InvariantCultureIgnoreCase);
|
||||
var endIndex = updateScriptName.IndexOf(endVersionTag, startIndex,
|
||||
StringComparison.InvariantCultureIgnoreCase);
|
||||
if (endIndex > startIndex)
|
||||
{
|
||||
//The name of the script file without the namespaces, version numbers etc.
|
||||
var fullScriptName = updateScriptName.Substring(endIndex + endVersionTag.Length).Trim().Replace("_", "");
|
||||
var fullScriptName = updateScriptName.Substring(endIndex + endVersionTag.Length).Trim()
|
||||
.Replace("_", "");
|
||||
|
||||
int filesFolderVersion = Utility.PadVersionString(updateScriptName.Substring(startIndex, endIndex - startIndex).Trim().Replace("_", ""));
|
||||
int filesFolderVersion = Utility.PadVersionString(updateScriptName
|
||||
.Substring(startIndex, endIndex - startIndex).Trim().Replace("_", ""));
|
||||
if (filesFolderVersion > storedPaddedVersion)
|
||||
{
|
||||
//Get the script text.
|
||||
using var stream = assembly.GetManifestResourceStream(updateScriptName);
|
||||
using var reader = new StreamReader(stream.EnsureNotNull());
|
||||
var scriptText = reader.ReadToEnd();
|
||||
|
||||
//Get the script "metadata" from the file name.
|
||||
var scriptNameParts = fullScriptName.Split('^');
|
||||
//string executionOrder = scriptNameParts[0];
|
||||
string databaseName = scriptNameParts[1];
|
||||
//string scriptName = scriptNameParts[2];
|
||||
var databaseName = scriptNameParts[1];
|
||||
|
||||
var databaseFactory = ManagedDataStorage.Collection.Single(o => o.Name == databaseName).Factory;
|
||||
var databaseFactory = ManagedDataStorage.Collection.Single(o => o.Name == databaseName)
|
||||
.Factory;
|
||||
|
||||
databaseFactory.Execute(scriptText);
|
||||
}
|
||||
@@ -95,13 +98,14 @@ namespace ZelWiki.Repository
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExceptionRepository.InsertException(ex, "Database upgrade failed.");
|
||||
ExceptionRepository.InsertException(ex, "数据库升级失败");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static ConfigurationEntries GetConfigurationEntryValuesByGroupName(string groupName, bool allowCache = true)
|
||||
public static ConfigurationEntries GetConfigurationEntryValuesByGroupName(string groupName,
|
||||
bool allowCache = true)
|
||||
{
|
||||
if (allowCache)
|
||||
{
|
||||
@@ -163,27 +167,25 @@ namespace ZelWiki.Repository
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if this is the first time the wiki has run. Returns true if it is the first time.
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static bool IsFirstRun()
|
||||
{
|
||||
bool isEncryptionValid = GetCryptoCheck();
|
||||
var isEncryptionValid = GetCryptoCheck();
|
||||
if (isEncryptionValid == false)
|
||||
{
|
||||
SetCryptoCheck();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an encrypted value from the database so we can determine if encryption is setup.
|
||||
/// If the value is missing then we are NOT setup.
|
||||
/// If the value is present but we cant decrypt it, then we are NOT setup.
|
||||
/// /// If the value is present and we can decrypt it, then we are setup and good to go!
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static bool GetCryptoCheck()
|
||||
{
|
||||
var value = ManagedDataStorage.Config.QueryFirstOrDefault<string>("GetCryptoCheck.sql") ?? string.Empty;
|
||||
@@ -203,9 +205,9 @@ namespace ZelWiki.Repository
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes an encrypted value to the database so we can test at a later time to ensure that encryption is setup.
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static void SetCryptoCheck()
|
||||
{
|
||||
var param = new
|
||||
@@ -275,6 +277,7 @@ namespace ZelWiki.Repository
|
||||
ConfigurationGroupId = group.Key,
|
||||
});
|
||||
}
|
||||
|
||||
result.Add(nest);
|
||||
}
|
||||
|
||||
@@ -284,14 +287,16 @@ namespace ZelWiki.Repository
|
||||
public static List<ConfigurationFlat> GetFlatConfiguration()
|
||||
=> ManagedDataStorage.Config.Query<ConfigurationFlat>("GetFlatConfiguration.sql").ToList();
|
||||
|
||||
public static string? GetConfigurationEntryValuesByGroupNameAndEntryName(string groupName, string entryName, bool allowCache = true)
|
||||
public static string? GetConfigurationEntryValuesByGroupNameAndEntryName(string groupName, string entryName,
|
||||
bool allowCache = true)
|
||||
{
|
||||
if (allowCache)
|
||||
{
|
||||
var cacheKey = WikiCacheKeyFunction.Build(WikiCache.Category.Configuration, [groupName, entryName]);
|
||||
if (!WikiCache.TryGet<string>(cacheKey, out var result))
|
||||
{
|
||||
if ((result = GetConfigurationEntryValuesByGroupNameAndEntryName(groupName, entryName, false)) != null)
|
||||
if ((result = GetConfigurationEntryValuesByGroupNameAndEntryName(groupName, entryName, false)) !=
|
||||
null)
|
||||
{
|
||||
WikiCache.Put(cacheKey, result);
|
||||
}
|
||||
@@ -306,7 +311,9 @@ namespace ZelWiki.Repository
|
||||
EntryName = entryName
|
||||
};
|
||||
|
||||
var configEntry = ManagedDataStorage.Config.QuerySingle<ConfigurationEntry>("GetConfigurationEntryValuesByGroupNameAndEntryName.sql", param);
|
||||
var configEntry =
|
||||
ManagedDataStorage.Config.QuerySingle<ConfigurationEntry>(
|
||||
"GetConfigurationEntryValuesByGroupNameAndEntryName.sql", param);
|
||||
if (configEntry?.IsEncrypted == true)
|
||||
{
|
||||
try
|
||||
@@ -431,33 +438,31 @@ namespace ZelWiki.Repository
|
||||
|
||||
if (emoji.ImageData != null)
|
||||
{
|
||||
var scaledImageCacheKey = WikiCacheKey.Build(WikiCache.Category.Emoji, [emoji.Shortcut, "100"]);
|
||||
var scaledImageCacheKey =
|
||||
WikiCacheKey.Build(WikiCache.Category.Emoji, [emoji.Shortcut, "100"]);
|
||||
var decompressedImageBytes = Utility.Decompress(emoji.ImageData);
|
||||
var img = Image.Load(new MemoryStream(decompressedImageBytes));
|
||||
|
||||
int customScalePercent = 100;
|
||||
var customScalePercent = 100;
|
||||
|
||||
var (Width, Height) = Utility.ScaleToMaxOf(img.Width, img.Height, GlobalConfiguration.DefaultEmojiHeight);
|
||||
var (Width, Height) = Utility.ScaleToMaxOf(img.Width, img.Height,
|
||||
GlobalConfiguration.DefaultEmojiHeight);
|
||||
|
||||
//Adjust to any specified scaling.
|
||||
Height = (int)(Height * (customScalePercent / 100.0));
|
||||
Width = (int)(Width * (customScalePercent / 100.0));
|
||||
|
||||
//Adjusting by a ratio (and especially after applying additional scaling) may have caused one
|
||||
// dimension to become very small (or even negative). So here we will check the height and width
|
||||
// to ensure they are both at least n pixels and adjust both dimensions.
|
||||
if (Height < 16)
|
||||
{
|
||||
Height += 16 - Height;
|
||||
Width += 16 - Height;
|
||||
}
|
||||
|
||||
if (Width < 16)
|
||||
{
|
||||
Height += 16 - Width;
|
||||
Width += 16 - Width;
|
||||
}
|
||||
|
||||
//These are hard to generate, so just keep it forever.
|
||||
var resized = Images.ResizeGifImage(decompressedImageBytes, Width, Height);
|
||||
var itemCache = new ImageCacheItem(resized, "image/gif");
|
||||
WikiCache.Put(scaledImageCacheKey, itemCache, new CacheItemPolicy());
|
||||
@@ -497,27 +502,34 @@ namespace ZelWiki.Repository
|
||||
|
||||
GlobalConfiguration.FixedMenuPosition = customizationConfig.Value("Fixed Header Menu Position", false);
|
||||
GlobalConfiguration.AllowSignup = membershipConfig.Value("Allow Signup", false);
|
||||
GlobalConfiguration.DefaultProfileRecentlyModifiedCount = performanceConfig.Value<int>("Default Profile Recently Modified Count");
|
||||
GlobalConfiguration.DefaultProfileRecentlyModifiedCount =
|
||||
performanceConfig.Value<int>("Default Profile Recently Modified Count");
|
||||
GlobalConfiguration.PreLoadAnimatedEmojis = performanceConfig.Value<bool>("Pre-Load Animated Emojis");
|
||||
GlobalConfiguration.SystemTheme = GetAllThemes().Single(o => o.Name == themeName);
|
||||
GlobalConfiguration.DefaultEmojiHeight = customizationConfig.Value<int>("Default Emoji Height");
|
||||
GlobalConfiguration.AllowGoogleAuthentication = membershipConfig.Value<bool>("Allow Google Authentication");
|
||||
GlobalConfiguration.DefaultTimeZone = customizationConfig?.Value<string>("Default TimeZone") ?? string.Empty;
|
||||
GlobalConfiguration.IncludeWikiDescriptionInMeta = functionalityConfig.Value<bool>("Include wiki Description in Meta");
|
||||
GlobalConfiguration.DefaultTimeZone =
|
||||
customizationConfig?.Value<string>("Default TimeZone") ?? string.Empty;
|
||||
GlobalConfiguration.IncludeWikiDescriptionInMeta =
|
||||
functionalityConfig.Value<bool>("Include wiki Description in Meta");
|
||||
GlobalConfiguration.IncludeWikiTagsInMeta = functionalityConfig.Value<bool>("Include wiki Tags in Meta");
|
||||
GlobalConfiguration.EnablePageComments = functionalityConfig.Value<bool>("Enable Page Comments");
|
||||
GlobalConfiguration.EnablePublicProfiles = functionalityConfig.Value<bool>("Enable Public Profiles");
|
||||
GlobalConfiguration.ShowCommentsOnPageFooter = functionalityConfig.Value<bool>("Show Comments on Page Footer");
|
||||
GlobalConfiguration.ShowLastModifiedOnPageFooter = functionalityConfig.Value<bool>("Show Last Modified on Page Footer");
|
||||
GlobalConfiguration.ShowCommentsOnPageFooter =
|
||||
functionalityConfig.Value<bool>("Show Comments on Page Footer");
|
||||
GlobalConfiguration.ShowLastModifiedOnPageFooter =
|
||||
functionalityConfig.Value<bool>("Show Last Modified on Page Footer");
|
||||
GlobalConfiguration.IncludeSearchOnNavbar = searchConfig.Value<bool>("Include Search on Navbar");
|
||||
GlobalConfiguration.HTMLHeader = htmlConfig?.Value<string>("Header") ?? string.Empty;
|
||||
GlobalConfiguration.HTMLFooter = htmlConfig?.Value<string>("Footer") ?? string.Empty;
|
||||
GlobalConfiguration.HTMLPreBody = htmlConfig?.Value<string>("Pre-Body") ?? string.Empty;
|
||||
GlobalConfiguration.HTMLPostBody = htmlConfig?.Value<string>("Post-Body") ?? string.Empty;
|
||||
GlobalConfiguration.BrandImageSmall = customizationConfig?.Value<string>("Brand Image (Small)") ?? string.Empty;
|
||||
GlobalConfiguration.BrandImageSmall =
|
||||
customizationConfig?.Value<string>("Brand Image (Small)") ?? string.Empty;
|
||||
GlobalConfiguration.FooterBlurb = customizationConfig?.Value<string>("FooterBlurb") ?? string.Empty;
|
||||
GlobalConfiguration.MaxAvatarFileSize = filesAndAttachmentsConfig.Value<int>("Max Avatar File Size");
|
||||
GlobalConfiguration.MaxAttachmentFileSize = filesAndAttachmentsConfig.Value<int>("Max Attachment File Size");
|
||||
GlobalConfiguration.MaxAttachmentFileSize =
|
||||
filesAndAttachmentsConfig.Value<int>("Max Attachment File Size");
|
||||
GlobalConfiguration.MaxEmojiFileSize = filesAndAttachmentsConfig.Value<int>("Max Emoji File Size");
|
||||
|
||||
GlobalConfiguration.MenuItems = GetAllMenuItems();
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
namespace ZelWiki.Repository
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores instances of ManagedDataStorageFactories that are used to store various parts of the data for the site.
|
||||
/// </summary>
|
||||
public static class ManagedDataStorage
|
||||
{
|
||||
private static (string Name, ManagedDataStorageFactory Factory)[]? _collection = null;
|
||||
|
||||
@@ -179,8 +179,6 @@ namespace ZelWiki.Repository
|
||||
var pageFileInfo = GetPageFileInfoByFileNavigation(o, item.PageId, item.FileNavigation);
|
||||
if (pageFileInfo == null)
|
||||
{
|
||||
//If the page file does not exist, then insert it.
|
||||
|
||||
var InsertPageFileParam = new
|
||||
{
|
||||
PageId = item.PageId,
|
||||
@@ -194,7 +192,6 @@ namespace ZelWiki.Repository
|
||||
|
||||
o.Execute("InsertPageFile.sql", InsertPageFileParam);
|
||||
|
||||
//Get the id of the newly inserted page file.
|
||||
pageFileInfo = GetPageFileInfoByFileNavigation(o, item.PageId, item.FileNavigation)
|
||||
?? throw new Exception("Failed find newly inserted page attachment.");
|
||||
|
||||
@@ -208,18 +205,12 @@ namespace ZelWiki.Repository
|
||||
var currentlyAttachedFile = GetPageCurrentRevisionAttachmentByFileNavigation(o, item.PageId, item.FileNavigation);
|
||||
if (currentlyAttachedFile != null)
|
||||
{
|
||||
//The PageFile exists and a revision of it is attached to this page revision.
|
||||
//Keep track of the file revision, and determine if the file has changed (via the file hash).
|
||||
|
||||
currentFileRevision = currentlyAttachedFile.Revision;
|
||||
hasFileChanged = currentlyAttachedFile.DataHash != newDataHash;
|
||||
}
|
||||
else
|
||||
{
|
||||
//The file either does not exist or is not attached to the current page revision.
|
||||
hasFileChanged = true;
|
||||
|
||||
//We determined earlier that the PageFile does exist, so keep track of the file revision.
|
||||
currentFileRevision = pageFileInfo.Revision;
|
||||
}
|
||||
|
||||
@@ -227,7 +218,6 @@ namespace ZelWiki.Repository
|
||||
{
|
||||
currentFileRevision++;
|
||||
|
||||
//Get the current page revision so that we can associate the page file attachment with the current page revision.
|
||||
int currentPageRevision = PageRepository.GetCurrentPageRevision(o, item.PageId);
|
||||
|
||||
var updatePageFileRevisionParam = new
|
||||
@@ -235,7 +225,6 @@ namespace ZelWiki.Repository
|
||||
PageFileId = pageFileInfo.PageFileId,
|
||||
FileRevision = currentFileRevision
|
||||
};
|
||||
//The file has changed (or is newly inserted), bump the file revision.
|
||||
o.Execute("UpdatePageFileRevision.sql", updatePageFileRevisionParam);
|
||||
|
||||
var insertPageFileRevisionParam = new
|
||||
@@ -250,7 +239,6 @@ namespace ZelWiki.Repository
|
||||
DataHash = newDataHash,
|
||||
};
|
||||
|
||||
//Insert the actual file data.
|
||||
o.Execute("InsertPageFileRevision.sql", insertPageFileRevisionParam);
|
||||
|
||||
var associatePageFileAttachmentWithPageRevisionParam = new
|
||||
@@ -259,10 +247,9 @@ namespace ZelWiki.Repository
|
||||
PageFileId = pageFileInfo.PageFileId,
|
||||
PageRevision = currentPageRevision,
|
||||
FileRevision = currentFileRevision,
|
||||
PreviousFileRevision = currentlyAttachedFile?.Revision //This is so we can disassociate the previous file revision.
|
||||
PreviousFileRevision = currentlyAttachedFile?.Revision
|
||||
};
|
||||
|
||||
//Associate the latest version of the file with the latest version of the page.
|
||||
o.Execute("AssociatePageFileAttachmentWithPageRevision.sql", associatePageFileAttachmentWithPageRevisionParam);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,8 @@ namespace ZelWiki.Repository
|
||||
return ManagedDataStorage.Pages.QuerySingleOrDefault<Page>("GetPageRevisionInfoById.sql", param);
|
||||
}
|
||||
|
||||
public static ProcessingInstructionCollection GetPageProcessingInstructionsByPageId(int pageId, bool allowCache = true)
|
||||
public static ProcessingInstructionCollection GetPageProcessingInstructionsByPageId(int pageId,
|
||||
bool allowCache = true)
|
||||
{
|
||||
if (allowCache)
|
||||
{
|
||||
@@ -41,7 +42,8 @@ namespace ZelWiki.Repository
|
||||
|
||||
return new ProcessingInstructionCollection()
|
||||
{
|
||||
Collection = ManagedDataStorage.Pages.Query<ProcessingInstruction>("GetPageProcessingInstructionsByPageId.sql", param).ToList()
|
||||
Collection = ManagedDataStorage.Pages
|
||||
.Query<ProcessingInstruction>("GetPageProcessingInstructionsByPageId.sql", param).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -68,7 +70,8 @@ namespace ZelWiki.Repository
|
||||
}
|
||||
|
||||
public static List<PageRevision> GetPageRevisionsInfoByNavigationPaged(
|
||||
string navigation, int pageNumber, string? orderBy = null, string? orderByDirection = null, int? pageSize = null)
|
||||
string navigation, int pageNumber, string? orderBy = null, string? orderByDirection = null,
|
||||
int? pageSize = null)
|
||||
{
|
||||
pageSize ??= ConfigurationRepository.Get<int>("Customization", "Pagination Size");
|
||||
|
||||
@@ -83,7 +86,8 @@ namespace ZelWiki.Repository
|
||||
{
|
||||
using var users_db = o.Attach("users.db", "users_db");
|
||||
|
||||
var query = RepositoryHelper.TransposeOrderby("GetPageRevisionsInfoByNavigationPaged.sql", orderBy, orderByDirection);
|
||||
var query = RepositoryHelper.TransposeOrderby("GetPageRevisionsInfoByNavigationPaged.sql", orderBy,
|
||||
orderByDirection);
|
||||
return o.Query<PageRevision>(query, param).ToList();
|
||||
});
|
||||
}
|
||||
@@ -96,7 +100,8 @@ namespace ZelWiki.Repository
|
||||
TopCount = topCount
|
||||
};
|
||||
|
||||
return ManagedDataStorage.Pages.Query<PageRevision>("GetTopRecentlyModifiedPagesInfoByUserId.sql", param).ToList();
|
||||
return ManagedDataStorage.Pages.Query<PageRevision>("GetTopRecentlyModifiedPagesInfoByUserId.sql", param)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public static string? GetPageNavigationByPageId(int pageId)
|
||||
@@ -149,12 +154,13 @@ namespace ZelWiki.Repository
|
||||
});
|
||||
}
|
||||
|
||||
private static List<PageSearchToken> GetMeteredPageSearchTokens(List<string> searchTerms, bool allowFuzzyMatching, bool allowCache = true)
|
||||
private static List<PageSearchToken> GetMeteredPageSearchTokens(List<string> searchTerms,
|
||||
bool allowFuzzyMatching, bool allowCache = true)
|
||||
{
|
||||
if (allowCache)
|
||||
{
|
||||
//This caching is really just used for paging - so we don't have to do a token search for every click of next/previous.
|
||||
var cacheKey = WikiCacheKeyFunction.Build(WikiCache.Category.Search, [string.Join(',', searchTerms), allowFuzzyMatching]);
|
||||
var cacheKey = WikiCacheKeyFunction.Build(WikiCache.Category.Search,
|
||||
[string.Join(',', searchTerms), allowFuzzyMatching]);
|
||||
if (!WikiCache.TryGet<List<PageSearchToken>>(cacheKey, out var result))
|
||||
{
|
||||
result = GetMeteredPageSearchTokens(searchTerms, allowFuzzyMatching, false);
|
||||
@@ -182,7 +188,7 @@ namespace ZelWiki.Repository
|
||||
|
||||
return allTokens
|
||||
.GroupBy(token => token.PageId)
|
||||
.Where(group => group.Sum(g => g.Score) >= minimumMatchScore) // Filtering groups
|
||||
.Where(group => group.Sum(g => g.Score) >= minimumMatchScore)
|
||||
.Select(group => new PageSearchToken
|
||||
{
|
||||
PageId = group.Key,
|
||||
@@ -204,7 +210,7 @@ namespace ZelWiki.Repository
|
||||
return new List<Page>();
|
||||
}
|
||||
|
||||
bool allowFuzzyMatching = ConfigurationRepository.Get<bool>("Search", "Allow Fuzzy Matching");
|
||||
var allowFuzzyMatching = ConfigurationRepository.Get<bool>("Search", "Allow Fuzzy Matching");
|
||||
var meteredSearchTokens = GetMeteredPageSearchTokens(searchTerms, allowFuzzyMatching == true);
|
||||
if (meteredSearchTokens.Count == 0)
|
||||
{
|
||||
@@ -224,7 +230,8 @@ namespace ZelWiki.Repository
|
||||
});
|
||||
}
|
||||
|
||||
public static List<Page> PageSearchPaged(List<string> searchTerms, int pageNumber, int? pageSize = null, bool? allowFuzzyMatching = null)
|
||||
public static List<Page> PageSearchPaged(List<string> searchTerms, int pageNumber, int? pageSize = null,
|
||||
bool? allowFuzzyMatching = null)
|
||||
{
|
||||
if (searchTerms.Count == 0)
|
||||
{
|
||||
@@ -256,7 +263,8 @@ namespace ZelWiki.Repository
|
||||
});
|
||||
}
|
||||
|
||||
public static List<RelatedPage> GetSimilarPagesPaged(int pageId, int similarity, int pageNumber, int? pageSize = null)
|
||||
public static List<RelatedPage> GetSimilarPagesPaged(int pageId, int similarity, int pageNumber,
|
||||
int? pageSize = null)
|
||||
{
|
||||
pageSize ??= ConfigurationRepository.Get<int>("Customization", "Pagination Size");
|
||||
|
||||
@@ -364,7 +372,8 @@ namespace ZelWiki.Repository
|
||||
});
|
||||
}
|
||||
|
||||
public static List<NonexistentPage> GetMissingPagesPaged(int pageNumber, string? orderBy = null, string? orderByDirection = null)
|
||||
public static List<NonexistentPage> GetMissingPagesPaged(int pageNumber, string? orderBy = null,
|
||||
string? orderByDirection = null)
|
||||
{
|
||||
int pageSize = ConfigurationRepository.Get<int>("Customization", "Pagination Size");
|
||||
|
||||
@@ -478,17 +487,18 @@ namespace ZelWiki.Repository
|
||||
return ManagedDataStorage.Pages.Ephemeral(o =>
|
||||
{
|
||||
using var users_db = o.Attach("users.db", "users_db");
|
||||
var query = RepositoryHelper.TransposeOrderby("GetAllNamespacePagesPaged.sql", orderBy, orderByDirection);
|
||||
var query = RepositoryHelper.TransposeOrderby("GetAllNamespacePagesPaged.sql", orderBy,
|
||||
orderByDirection);
|
||||
return o.Query<Page>(query, param).ToList();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unlike the search, this method returns all pages and allows them to be paired down using the search terms.
|
||||
/// Whereas the search requires a search term to get results. The matching here is also exact, no score based matching.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="pageNumber"></param>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <param name="orderBy"></param>
|
||||
/// <param name="orderByDirection"></param>
|
||||
/// <param name="searchTerms"></param>
|
||||
/// <returns></returns>
|
||||
public static List<Page> GetAllPagesPaged(int pageNumber,
|
||||
@@ -512,7 +522,8 @@ namespace ZelWiki.Repository
|
||||
using var deletedpagerevisions_db = o.Attach("deletedpagerevisions.db", "deletedpagerevisions_db");
|
||||
using var tempTable = o.CreateTempTableFrom("TempPageIds", pageIds);
|
||||
|
||||
var query = RepositoryHelper.TransposeOrderby("GetAllPagesByPageIdPaged.sql", orderBy, orderByDirection);
|
||||
var query = RepositoryHelper.TransposeOrderby("GetAllPagesByPageIdPaged.sql", orderBy,
|
||||
orderByDirection);
|
||||
return o.Query<Page>(query, param).ToList();
|
||||
});
|
||||
}
|
||||
@@ -528,11 +539,11 @@ namespace ZelWiki.Repository
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unlike the search, this method returns all pages and allows them to be paired down using the search terms.
|
||||
/// Whereas the search requires a search term to get results. The matching here is also exact, no score based matching.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="pageNumber"></param>
|
||||
/// <param name="pageSize"></param>
|
||||
/// <param name="orderBy"></param>
|
||||
/// <param name="orderByDirection"></param>
|
||||
/// <param name="searchTerms"></param>
|
||||
/// <returns></returns>
|
||||
public static List<Page> GetAllDeletedPagesPaged(int pageNumber, string? orderBy = null,
|
||||
@@ -554,7 +565,8 @@ namespace ZelWiki.Repository
|
||||
using var users_db = o.Attach("users.db", "users_db");
|
||||
using var tempTable = o.CreateTempTableFrom("TempPageIds", pageIds);
|
||||
|
||||
var query = RepositoryHelper.TransposeOrderby("GetAllDeletedPagesByPageIdPaged.sql", orderBy, orderByDirection);
|
||||
var query = RepositoryHelper.TransposeOrderby("GetAllDeletedPagesByPageIdPaged.sql", orderBy,
|
||||
orderByDirection);
|
||||
return o.Query<Page>(query, param).ToList();
|
||||
});
|
||||
}
|
||||
@@ -567,7 +579,8 @@ namespace ZelWiki.Repository
|
||||
});
|
||||
}
|
||||
|
||||
public static List<NamespaceStat> GetAllNamespacesPaged(int pageNumber, string? orderBy = null, string? orderByDirection = null)
|
||||
public static List<NamespaceStat> GetAllNamespacesPaged(int pageNumber, string? orderBy = null,
|
||||
string? orderByDirection = null)
|
||||
{
|
||||
int pageSize = ConfigurationRepository.Get<int>("Customization", "Pagination Size");
|
||||
|
||||
@@ -683,6 +696,7 @@ namespace ZelWiki.Repository
|
||||
WikiCache.Put(cacheKey, result);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -719,27 +733,23 @@ namespace ZelWiki.Repository
|
||||
|
||||
try
|
||||
{
|
||||
int currentPageRevision = 0;
|
||||
bool hasPageChanged = false;
|
||||
var currentPageRevision = 0;
|
||||
var hasPageChanged = false;
|
||||
|
||||
if (page.Id == 0)
|
||||
{
|
||||
//This is a new page, just insert it.
|
||||
page.Id = o.ExecuteScalar<int>("CreatePage.sql", pageUpsertParam);
|
||||
hasPageChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Get current page so we can determine if anything has changed.
|
||||
var currentRevisionInfo = GetLimitedPageInfoByIdAndRevision(page.Id)
|
||||
?? throw new Exception("The page could not be found.");
|
||||
|
||||
currentPageRevision = currentRevisionInfo.Revision;
|
||||
|
||||
//Update the existing page.
|
||||
o.Execute("UpdatePage.sql", pageUpsertParam);
|
||||
|
||||
//Determine if anything has actually changed.
|
||||
hasPageChanged = currentRevisionInfo.Name != page.Name
|
||||
|| currentRevisionInfo.Namespace != page.Namespace
|
||||
|| currentRevisionInfo.Description != page.Description
|
||||
@@ -755,7 +765,6 @@ namespace ZelWiki.Repository
|
||||
PageId = page.Id,
|
||||
PageRevision = currentPageRevision
|
||||
};
|
||||
//The page content has actually changed (according to the checksum), so we will bump the page revision.
|
||||
o.Execute("UpdatePageRevisionNumber.sql", updatePageRevisionNumberParam);
|
||||
|
||||
var InsertPageRevisionParam = new
|
||||
@@ -770,7 +779,6 @@ namespace ZelWiki.Repository
|
||||
ModifiedByUserId = page.ModifiedByUserId,
|
||||
ModifiedDate = DateTime.UtcNow,
|
||||
};
|
||||
//Insert the new actual page revision entry (this is the data).
|
||||
o.Execute("InsertPageRevision.sql", InsertPageRevisionParam);
|
||||
|
||||
var reassociateAllPageAttachmentsParam = new
|
||||
@@ -778,7 +786,6 @@ namespace ZelWiki.Repository
|
||||
PageId = page.Id,
|
||||
PageRevision = currentPageRevision,
|
||||
};
|
||||
//Associate all page attachments with the latest revision.
|
||||
o.Execute("ReassociateAllPageAttachments.sql", reassociateAllPageAttachmentsParam);
|
||||
}
|
||||
|
||||
@@ -795,7 +802,7 @@ namespace ZelWiki.Repository
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the page info without the content.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="navigation"></param>
|
||||
/// <returns></returns>
|
||||
@@ -1001,7 +1008,8 @@ namespace ZelWiki.Repository
|
||||
{
|
||||
using var users_db = o.Attach("users.db", "users_db");
|
||||
|
||||
var query = RepositoryHelper.TransposeOrderby("GetDeletedPageRevisionsByIdPaged.sql", orderBy, orderByDirection);
|
||||
var query = RepositoryHelper.TransposeOrderby("GetDeletedPageRevisionsByIdPaged.sql", orderBy,
|
||||
orderByDirection);
|
||||
return o.Query<DeletedPageRevision>(query, param).ToList();
|
||||
});
|
||||
}
|
||||
@@ -1083,7 +1091,8 @@ namespace ZelWiki.Repository
|
||||
});
|
||||
}
|
||||
|
||||
public static Page? GetPageRevisionByNavigation(string givenNavigation, int? revision = null, bool allowCache = true)
|
||||
public static Page? GetPageRevisionByNavigation(string givenNavigation, int? revision = null,
|
||||
bool allowCache = true)
|
||||
{
|
||||
var navigation = new NamespaceNavigation(givenNavigation);
|
||||
|
||||
|
||||
@@ -5,11 +5,13 @@ namespace ZelWiki.Repository
|
||||
internal static class RepositoryHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Fills in a custom orderby on a given sql script.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="filename"></param>
|
||||
/// <param name="orderBy"></param>
|
||||
/// <param name="orderByDirection"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public static string TransposeOrderby(string filename, string? orderBy, string? orderByDirection)
|
||||
{
|
||||
var script = ManagedDataStorageInstance.TranslateSqlScript(filename);
|
||||
@@ -19,47 +21,53 @@ namespace ZelWiki.Repository
|
||||
return script;
|
||||
}
|
||||
|
||||
string beginParentTag = "--CUSTOM_ORDER_BEGIN::";
|
||||
string endParentTag = "--::CUSTOM_ORDER_BEGIN";
|
||||
var beginParentTag = "--CUSTOM_ORDER_BEGIN::";
|
||||
var endParentTag = "--::CUSTOM_ORDER_BEGIN";
|
||||
|
||||
string beginConfigTag = "--CONFIG::";
|
||||
string endConfigTag = "--::CONFIG";
|
||||
var beginConfigTag = "--CONFIG::";
|
||||
var endConfigTag = "--::CONFIG";
|
||||
|
||||
while (true)
|
||||
{
|
||||
int beginParentIndex = script.IndexOf(beginParentTag, StringComparison.OrdinalIgnoreCase);
|
||||
int endParentIndex = script.IndexOf(endParentTag, StringComparison.OrdinalIgnoreCase);
|
||||
var beginParentIndex = script.IndexOf(beginParentTag, StringComparison.OrdinalIgnoreCase);
|
||||
var endParentIndex = script.IndexOf(endParentTag, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (beginParentIndex > 0 && endParentIndex > beginParentIndex)
|
||||
{
|
||||
var sectionText = script.Substring(beginParentIndex + beginParentTag.Length, (endParentIndex - beginParentIndex) - endParentTag.Length).Trim();
|
||||
var sectionText = script.Substring(beginParentIndex + beginParentTag.Length,
|
||||
(endParentIndex - beginParentIndex) - endParentTag.Length).Trim();
|
||||
|
||||
int beginConfigIndex = sectionText.IndexOf(beginConfigTag, StringComparison.OrdinalIgnoreCase);
|
||||
int endConfigIndex = sectionText.IndexOf(endConfigTag, StringComparison.OrdinalIgnoreCase);
|
||||
var beginConfigIndex = sectionText.IndexOf(beginConfigTag, StringComparison.OrdinalIgnoreCase);
|
||||
var endConfigIndex = sectionText.IndexOf(endConfigTag, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (beginConfigIndex >= 0 && endConfigIndex > beginConfigIndex)
|
||||
{
|
||||
var configText = sectionText.Substring(beginConfigIndex + beginConfigTag.Length, (endConfigIndex - beginConfigIndex) - endConfigTag.Length).Trim();
|
||||
var configText = sectionText.Substring(beginConfigIndex + beginConfigTag.Length,
|
||||
(endConfigIndex - beginConfigIndex) - endConfigTag.Length).Trim();
|
||||
|
||||
var configs = configText.Split("\n").Select(o => o.Trim())
|
||||
.Where(o => o.Contains('='))
|
||||
.Select(o => (Name: o.Split("=")[0], Field: o.Split("=")[1]));
|
||||
|
||||
var selectedConfig = configs.SingleOrDefault(o => string.Equals(o.Name, orderBy, StringComparison.OrdinalIgnoreCase));
|
||||
var selectedConfig = configs.SingleOrDefault(o =>
|
||||
string.Equals(o.Name, orderBy, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (selectedConfig == default)
|
||||
{
|
||||
throw new Exception($"No order by mapping was found in '{filename}' for the field '{orderBy}'.");
|
||||
throw new Exception(
|
||||
$"在 '{filename}' 中找不到排序字段 '{orderBy}'的映射");
|
||||
}
|
||||
|
||||
script = script.Substring(0, beginParentIndex)
|
||||
+ $"ORDER BY\r\n\t{selectedConfig.Field} "
|
||||
+ (string.Equals(orderByDirection, "asc", StringComparison.InvariantCultureIgnoreCase) ? "asc" : "desc")
|
||||
+ (string.Equals(orderByDirection, "asc", StringComparison.InvariantCultureIgnoreCase)
|
||||
? "asc"
|
||||
: "desc")
|
||||
+ script.Substring(endParentIndex + endParentTag.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"No order configuration was found in '{filename}'.");
|
||||
throw new Exception($"在 '{filename}'中找不到配置");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
--Remove the previous page file revision attachment, if any.
|
||||
DELETE FROM PageRevisionAttachment
|
||||
WHERE
|
||||
PageId = @PageId
|
||||
@@ -6,7 +5,7 @@ WHERE
|
||||
AND FileRevision = @PreviousFileRevision
|
||||
AND PageRevision = @PageRevision;
|
||||
|
||||
--Associate the file revision record with the page revision.
|
||||
|
||||
INSERT INTO PageRevisionAttachment
|
||||
(
|
||||
PageId,
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
--This proc is exactly like GetAllUsersPaged except it has no filter on personal infomation so it can be used for public info.
|
||||
|
||||
SELECT
|
||||
U.UserId,
|
||||
U.AccountName,
|
||||
|
||||
@@ -9,7 +9,6 @@ FROM
|
||||
T.PageId,
|
||||
COUNT(DISTINCT T.Token) / (@TokenCount + 0.0) as [Match],
|
||||
SUM(T.[Weight] * 1.5) as [Weight],
|
||||
--Extra weight on score for exact matches:
|
||||
SUM(T.[Weight] * 1.5) * (COUNT(DISTINCT T.Token) / (@TokenCount + 0.0)) as [Score]
|
||||
FROM
|
||||
PageToken as T
|
||||
|
||||
@@ -9,7 +9,6 @@ FROM
|
||||
T.PageId,
|
||||
COUNT(DISTINCT T.DoubleMetaphone) / (@TokenCount + 0.0) as [Match],
|
||||
SUM(T.[Weight] * 1.0) as [Weight],
|
||||
--No weight benefits on score for fuzzy matching weight for exact matches:
|
||||
(COUNT(DISTINCT T.DoubleMetaphone) / (@TokenCount + 0.0)) as [Score]
|
||||
FROM
|
||||
PageToken as T
|
||||
|
||||
@@ -19,7 +19,7 @@ INNER JOIN PageRevisionAttachment as PRA
|
||||
ON PRA.PageId = P.Id
|
||||
AND PRA.PageFileId = PF.Id
|
||||
AND PRA.PageRevision = PR.Revision
|
||||
AND PRA.FileRevision = PF.Revision --Latest file revision.
|
||||
AND PRA.FileRevision = PF.Revision
|
||||
INNER JOIN PageFileRevision as PFR
|
||||
ON PFR.PageFileId = PF.Id
|
||||
AND PFR.Revision = PRA.FileRevision
|
||||
|
||||
@@ -23,7 +23,7 @@ SELECT
|
||||
ON PRA.PageId = P.Id
|
||||
AND PRA.PageFileId = PF.Id
|
||||
AND PRA.PageRevision = PR.Revision
|
||||
AND PRA.FileRevision = PF.Revision --Latest file revision.
|
||||
AND PRA.FileRevision = PF.Revision
|
||||
INNER JOIN PageFileRevision as PFR
|
||||
ON PFR.PageFileId = PF.Id
|
||||
AND PFR.Revision = PRA.FileRevision
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
# Database Upgrade Initialization
|
||||
|
||||
When TightWiki is run, any scripts in the folders contained in "TightWiki.Repository\Scripts\Initialization\Versions"
|
||||
are executed. The "previous version" of TightWiki is stored in the Config database VersionState table.
|
||||
|
||||
The scripts are executed in the order denoted by the name of the folders in "Version\*", these folders are
|
||||
expected to be named with a three-part version scheme. MM.mm.pp (major.minor.patch).
|
||||
|
||||
The scripts are only executed if the three-part folder version is
|
||||
greater than the "previous version" from the VersionState table.
|
||||
|
||||
Theses scripts are executed in the order of their name as well, their name consists of three parts:
|
||||
"\^EXECUTION_ORDER\^DATABASE_NAME\^SCRIPT_NAME" where the execution order should be a zero padded numeric string,
|
||||
database name is the key from ManagedDataStorage.Collection, and script name is whatever you want to call it.
|
||||
@@ -1,4 +1,3 @@
|
||||
--Insert the actual file data.
|
||||
INSERT INTO PageFileRevision
|
||||
(
|
||||
PageFileId,
|
||||
|
||||
@@ -5,7 +5,6 @@ INSERT INTO deletedpages_db.[PageFileRevision] SELECT * FROM PageFileRevision WH
|
||||
INSERT INTO deletedpages_db.[PageFile] SELECT * FROM [PageFile] WHERE PageId = @PageId;
|
||||
INSERT INTO deletedpages_db.[Page] SELECT * FROM [Page] WHERE Id = @PageId;
|
||||
|
||||
--We save these so we can search for deleted pages.
|
||||
INSERT INTO deletedpages_db.[PageTag] SELECT * FROM [PageTag] WHERE PageId = @PageId;
|
||||
INSERT INTO deletedpages_db.[PageToken] SELECT * FROM [PageToken] WHERE PageId = @PageId;
|
||||
INSERT INTO deletedpages_db.[PageProcessingInstruction] SELECT * FROM [PageProcessingInstruction] WHERE PageId = @PageId;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--Cleanup
|
||||
|
||||
DELETE FROM DeletionMeta WHERE PageId = @PageId;
|
||||
|
||||
DELETE FROM [PageTag] WHERE PageId = @PageId;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
--Delete orphaned PageFileRevision.
|
||||
DELETE FROM PageFileRevision WHERE PageFileId = @PageFileId AND Revision = @Revision;
|
||||
|
||||
--Delete orphaned PageFile.
|
||||
DELETE FROM PageFile
|
||||
WHERE Id = @PageFileId
|
||||
AND Id NOT IN (SELECT PFR.PageFileId FROM PageFileRevision as PFR WHERE PFR.PageFileId = @PageFileId);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
--Delete orphaned PageFileRevision.
|
||||
|
||||
DELETE FROM PageFileRevision
|
||||
WHERE (PageFileId, Revision) IN (
|
||||
SELECT
|
||||
@@ -19,7 +19,7 @@ WHERE (PageFileId, Revision) IN (
|
||||
PRA.PageFileId IS NULL
|
||||
);
|
||||
|
||||
--Delete orphaned PageFile.
|
||||
|
||||
DELETE FROM PageFile
|
||||
WHERE Id NOT IN (SELECT PageFileId FROM PageFileRevision);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--Restore:
|
||||
|
||||
INSERT INTO [Page] SELECT * FROM deletedpages_db.[Page] WHERE Id = @PageId;
|
||||
INSERT INTO [PageRevision] SELECT * FROM deletedpages_db.[PageRevision] WHERE PageId = @PageId;
|
||||
INSERT INTO [PageFile] SELECT * FROM deletedpages_db.[PageFile] WHERE PageId = @PageId;
|
||||
@@ -6,7 +6,7 @@ INSERT INTO [PageFileRevision] SELECT * FROM deletedpages_db.PageFileRevision WH
|
||||
INSERT INTO [PageRevisionAttachment] SELECT * FROM deletedpages_db.[PageRevisionAttachment] WHERE PageId = @PageId;
|
||||
INSERT INTO [PageComment] SELECT * FROM deletedpages_db.[PageComment] WHERE PageId = @PageId;
|
||||
|
||||
--Cleanup
|
||||
|
||||
DELETE FROM deletedpages_db.DeletionMeta WHERE PageId = @PageId;
|
||||
|
||||
DELETE FROM deletedpages_db.[PageTag] WHERE PageId = @PageId;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
-- Deleting non-current page revisions
|
||||
DELETE FROM PageRevision
|
||||
WHERE EXISTS (
|
||||
SELECT 1
|
||||
@@ -11,7 +10,6 @@ WHERE EXISTS (
|
||||
AND PageRevision.Revision < MostRecent.MaxRevision
|
||||
);
|
||||
|
||||
-- Deleting non-current attachments.
|
||||
DELETE FROM PageRevisionAttachment
|
||||
WHERE EXISTS (
|
||||
SELECT 1
|
||||
@@ -25,7 +23,6 @@ WHERE EXISTS (
|
||||
AND PageRevisionAttachment.FileRevision < MostRecent.MaxFileRevision
|
||||
);
|
||||
|
||||
-- Deleting non-current page revision attachments
|
||||
DELETE FROM PageRevisionAttachment
|
||||
WHERE EXISTS (
|
||||
SELECT 1
|
||||
@@ -39,7 +36,6 @@ WHERE EXISTS (
|
||||
AND PageRevisionAttachment.PageRevision < MostRecent.MaxPageRevision
|
||||
);
|
||||
|
||||
-- Deleting non-current page file revisions.
|
||||
DELETE FROM PageFileRevision
|
||||
WHERE EXISTS (
|
||||
SELECT 1
|
||||
@@ -52,19 +48,16 @@ WHERE EXISTS (
|
||||
AND PageFileRevision.Revision < MostRecent.MaxPageRevision
|
||||
);
|
||||
|
||||
-- Delete orphaned PageFileRevision
|
||||
DELETE FROM PageFileRevision
|
||||
WHERE PageFileId NOT IN (
|
||||
SELECT PageFileId FROM PageRevisionAttachment
|
||||
);
|
||||
|
||||
-- Delete orphaned PageFile
|
||||
DELETE FROM PageFile
|
||||
WHERE Id NOT IN (
|
||||
SELECT PageFileId FROM PageRevisionAttachment
|
||||
);
|
||||
|
||||
-- Assuming everything else worked, lets set all of the revisions back to 1.
|
||||
UPDATE [Page] SET Revision = 1;
|
||||
UPDATE PageRevision SET Revision = 1;
|
||||
UPDATE PageRevisionAttachment SET PageRevision = 1, FileRevision = 1;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
--The ReferencesPageId is NULL by default and needs to be filled in for pages that referece orphaned pages.
|
||||
UPDATE
|
||||
PageReference
|
||||
SET
|
||||
|
||||
@@ -11,7 +11,6 @@ DELETE FROM EmojiCategory WHERE Id IN
|
||||
AND TC.Value IS NULL
|
||||
);
|
||||
|
||||
--Insert previously non-existing categories.
|
||||
INSERT INTO EmojiCategory
|
||||
(
|
||||
EmojiId,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user