diff --git a/DummyPageGenerator/PageGenerator.cs b/DummyPageGenerator/PageGenerator.cs index 26ca2e0..715f0a9 100644 --- a/DummyPageGenerator/PageGenerator.cs +++ b/DummyPageGenerator/PageGenerator.cs @@ -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 } /// - /// Creates a user and the associated profile with claims and such. + /// /// /// /// @@ -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))); @@ -92,18 +94,18 @@ namespace DummyPageGenerator UsersRepository.CreateProfile(Guid.Parse(userId), GetRandomUnusedAccountName()); var claimsToAdd = new List - { - new (ClaimTypes.Role, membershipConfig.Value("Default Signup Role").EnsureNotNull()), - new ("timezone", membershipConfig.Value("Default TimeZone").EnsureNotNull()), - new (ClaimTypes.Country, membershipConfig.Value("Default Country").EnsureNotNull()), - new ("language", membershipConfig.Value("Default Language").EnsureNotNull()), - }; + { + new(ClaimTypes.Role, membershipConfig.Value("Default Signup Role").EnsureNotNull()), + new("timezone", membershipConfig.Value("Default TimeZone").EnsureNotNull()), + new(ClaimTypes.Country, membershipConfig.Value("Default Country").EnsureNotNull()), + new("language", membershipConfig.Value("Default Language").EnsureNotNull()), + }; SecurityRepository.UpsertUserClaims(_userManager, user, claimsToAdd); } /// - /// Creates a paragraph/sentence structure. + /// /// /// /// @@ -118,7 +120,7 @@ namespace DummyPageGenerator } /// - /// Creates a paragraph/sentence structure with links and wiki markup. + /// /// /// /// @@ -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 } /// - /// Creates a random page on the wiki. + /// /// + /// /// 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 } /// - /// Modifies a random page on the wiki. + /// /// /// public void ModifyRandomPages(IZelEngine engine, Guid userId) @@ -312,8 +318,8 @@ namespace DummyPageGenerator string bottomText = pageToModify.Body.Substring(endIndex); pageToModify.Body = topText.Trim() - + "\r\n" + GenerateWikiParagraph(_random.Next(10, 20)) - + "\r\n" + bottomText.Trim(); + + "\r\n" + GenerateWikiParagraph(_random.Next(10, 20)) + + "\r\n" + bottomText.Trim(); pageToModify.ModifiedByUserId = userId; pageToModify.ModifiedByUserId = userId; Helpers.UpsertPage(engine, pageToModify); @@ -328,12 +334,13 @@ namespace DummyPageGenerator } /// - /// Attaches a file to a wiki page. + /// /// /// /// /// /// + /// 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; } @@ -397,4 +405,4 @@ namespace DummyPageGenerator } } } -} +} \ No newline at end of file diff --git a/ZelWiki.Caching/WikiCache.cs b/ZelWiki.Caching/WikiCache.cs index f45f3d8..26748a4 100644 --- a/ZelWiki.Caching/WikiCache.cs +++ b/ZelWiki.Caching/WikiCache.cs @@ -4,6 +4,9 @@ using System.Runtime.Caching; namespace ZelWiki.Caching { + /// + /// 缓存 + /// 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 } /// - /// Gets an item from the cache. + /// 获取缓存 /// /// /// @@ -60,9 +63,8 @@ namespace ZelWiki.Caching } /// - /// Determines if the cache contains a given key. + /// 确定缓存是否包含给定的key /// - /// /// /// public static bool Contains(IWikiCacheKey cacheKey) @@ -79,7 +81,7 @@ namespace ZelWiki.Caching } /// - /// Gets an item from the cache. + /// 获取缓存 /// /// /// @@ -98,7 +100,7 @@ namespace ZelWiki.Caching } /// - /// Adds an item to the cache. If the item is already in the cache, this will reset its expiration. + ///添加缓存 /// /// /// @@ -127,7 +129,7 @@ namespace ZelWiki.Caching } /// - /// Removes all entries from the cache. + /// 清理 /// public static void Clear() { @@ -144,9 +146,9 @@ namespace ZelWiki.Caching } /// - /// Removes cache entries that begin with the given cache key. + /// 删除某个 /// - /// + /// public static void ClearCategory(WikiCacheKey cacheKey) { var keys = new List(); @@ -163,7 +165,7 @@ namespace ZelWiki.Caching } /// - /// Removes cache entries in a given category. + /// 删除给定类别中的缓存条目 /// /// public static void ClearCategory(Category category) @@ -183,4 +185,4 @@ namespace ZelWiki.Caching keys.ForEach(o => MemCache.Remove(o)); } } -} +} \ No newline at end of file diff --git a/ZelWiki.Caching/WikiCacheKey.cs b/ZelWiki.Caching/WikiCacheKey.cs index a4e360d..3bce531 100644 --- a/ZelWiki.Caching/WikiCacheKey.cs +++ b/ZelWiki.Caching/WikiCacheKey.cs @@ -3,17 +3,17 @@ namespace ZelWiki.Caching { /// - /// Contains a verbatim cache key. + /// 包含逐字缓存键 /// /// 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}]"); } } diff --git a/ZelWiki.Caching/WikiCacheKeyFunction.cs b/ZelWiki.Caching/WikiCacheKeyFunction.cs index 3bfa940..ed6fae8 100644 --- a/ZelWiki.Caching/WikiCacheKeyFunction.cs +++ b/ZelWiki.Caching/WikiCacheKeyFunction.cs @@ -3,22 +3,18 @@ using static ZelWiki.Caching.WikiCache; namespace ZelWiki.Caching { - /// - /// Contains a verbatim cache key which also includes the calling function name. - /// - /// public class WikiCacheKeyFunction(string key) : IWikiCacheKey { public string Key { get; set; } = key; /// - /// Builds a cache key which includes the calling function name. + /// 生成一个包含调用函数名称的缓存键。 /// public static WikiCacheKeyFunction Build(WikiCache.Category category, object?[] segments, [CallerMemberName] string callingFunction = "") => new($"[{category}]:[{string.Join("]:[", segments)}]:[{callingFunction}]"); /// - /// Builds a cache key which includes the calling function name. + /// 生成一个包含调用函数名称的缓存键。 /// public static WikiCacheKeyFunction Build(WikiCache.Category category, [CallerMemberName] string callingFunction = "") => new($"[{category}]:[{callingFunction}]"); diff --git a/ZelWiki.Email/WikiEmailSender.cs b/ZelWiki.Email/WikiEmailSender.cs index cbf1c91..64884f3 100644 --- a/ZelWiki.Email/WikiEmailSender.cs +++ b/ZelWiki.Email/WikiEmailSender.cs @@ -11,6 +11,10 @@ namespace ZelWiki.Email { private readonly ILogger _logger; + /// + /// + /// + /// public WikiEmailSender(ILogger logger) { _logger = logger; diff --git a/ZelWiki.Engine.Function/FunctionCall.cs b/ZelWiki.Engine.Function/FunctionCall.cs index aa31e40..704fcf0 100644 --- a/ZelWiki.Engine.Function/FunctionCall.cs +++ b/ZelWiki.Engine.Function/FunctionCall.cs @@ -1,17 +1,19 @@ namespace ZelWiki.Engine.Function { /// - /// Contains information about an actual function call, its supplied parameters, and is matched with a defined function. + /// 包含有关实际函数调用及其提供的参数的信息,并与定义的函数相匹配。 /// public class FunctionCall { /// - /// The name of the function being called. + /// /// public string Name { get; private set; } + public FunctionPrototype Prototype { get; set; } + /// - /// The arguments supplied by the caller. + /// T /// 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 @@ } /// - /// Checks the passed value against the function prototype to ensure that the variable is the correct type, value, etc. + /// 对照函数原型检查传递的值,以确保变量的类型,值等正确 /// - /// + /// /// /// 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)}]."); } } } /// - /// 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. + /// /// /// 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(); - //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,17 +199,20 @@ 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}]."); } } } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Function/FunctionParameters.cs b/ZelWiki.Engine.Function/FunctionParameters.cs index 06b4b92..760d81b 100644 --- a/ZelWiki.Engine.Function/FunctionParameters.cs +++ b/ZelWiki.Engine.Function/FunctionParameters.cs @@ -4,38 +4,42 @@ namespace ZelWiki.Engine.Function { public class FunctionParameters { - /// - /// Variables set by ordinal. - /// - public List Ordinals { get; set; } = new(); - /// - /// Variables set by name. - /// - public List Named { get; private set; } = new(); - private readonly FunctionCall _owner; - public FunctionParameters(FunctionCall owner) { _owner = owner; } + /// + /// + /// + public List Ordinals { get; set; } = new(); + + /// + /// + /// + public List Named { get; private set; } = new(); + + + public T Get(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(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(prototype.DefaultValue) ?? + throw new Exception("值不能为空"); } - return Converters.ConvertTo(value) ?? throw new Exception("Value cannot be null"); + return Converters.ConvertTo(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(value) ?? throw new Exception("Value cannot be null"); + return Converters.ConvertTo(value) ?? throw new Exception("值不能为空"); } catch (Exception ex) { - throw new Exception($"Function [{_owner.Name}], {ex.Message}"); + throw new Exception($"函数 [{_owner.Name}], {ex.Message}"); } } @@ -62,14 +67,15 @@ namespace ZelWiki.Engine.Function try { var values = Named.Where(o => o.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))? - .Select(o => Converters.ConvertTo(o.Value) ?? throw new Exception("Value cannot be null"))?.ToList(); + .Select(o => Converters.ConvertTo(o.Value) ?? throw new Exception("值不能为空")) + ?.ToList(); return values ?? new List(); } catch (Exception ex) { - throw new Exception($"Function [{_owner.Name}], {ex.Message}"); + throw new Exception($"函数 [{_owner.Name}], {ex.Message}"); } } } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Function/FunctionParser.cs b/ZelWiki.Engine.Function/FunctionParser.cs index ecf13b5..0671be1 100644 --- a/ZelWiki.Engine.Function/FunctionParser.cs +++ b/ZelWiki.Engine.Function/FunctionParser.cs @@ -10,13 +10,15 @@ namespace ZelWiki.Engine.Function private static partial Regex FunctionCallParser(); /// - /// Parsed a function call, its parameters and matches it to a defined function and its prototype. + /// 解析函数调用及其参数,并将其与已定义的函数及其原型进行匹配 /// + /// /// /// /// - /// - public static FunctionCall ParseAndGetFunctionCall(FunctionPrototypeCollection prototypes, string functionCall, out int parseEndIndex) + /// + public static FunctionCall ParseAndGetFunctionCall(FunctionPrototypeCollection prototypes, string functionCall, + out int parseEndIndex) { var rawArguments = new List(); @@ -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(); 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 } /// - /// 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. + /// /// /// /// - /// + /// public static List ParseRawArgumentsAddParenthesis(string paramString) { if (paramString.StartsWith('(') || paramString.EndsWith(')')) @@ -88,17 +93,16 @@ namespace ZelWiki.Engine.Function } /// - /// 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. + /// /// /// /// - /// + /// public static List ParseRawArguments(string paramString) { List 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(); } @@ -217,4 +222,4 @@ namespace ZelWiki.Engine.Function return ps; } } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Function/FunctionPrototype.cs b/ZelWiki.Engine.Function/FunctionPrototype.cs index 07d8b8e..06c1748 100644 --- a/ZelWiki.Engine.Function/FunctionPrototype.cs +++ b/ZelWiki.Engine.Function/FunctionPrototype.cs @@ -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 Parameters { get; set; } - + /// + /// + /// public FunctionPrototype() { - Parameters = new List(); + 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 Parameters { get; set; } } } diff --git a/ZelWiki.Engine.Function/FunctionPrototypeCollection.cs b/ZelWiki.Engine.Function/FunctionPrototypeCollection.cs index 1e17a50..b51fbaa 100644 --- a/ZelWiki.Engine.Function/FunctionPrototypeCollection.cs +++ b/ZelWiki.Engine.Function/FunctionPrototypeCollection.cs @@ -14,6 +14,10 @@ namespace ZelWiki.Engine.Function public WikiFunctionType FunctionTypes { get; private set; } public List Items { get; set; } = new(); + /// + /// + /// + /// 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; } - /// - /// Gets the next token in a string. + /// /// /// /// @@ -203,5 +209,7 @@ namespace ZelWiki.Engine.Function index++; } } + + #endregion } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Function/NamedParameter.cs b/ZelWiki.Engine.Function/NamedParameter.cs index a844d35..530888e 100644 --- a/ZelWiki.Engine.Function/NamedParameter.cs +++ b/ZelWiki.Engine.Function/NamedParameter.cs @@ -5,6 +5,11 @@ public string Name { get; set; } public string Value { get; set; } + /// + /// + /// + /// + /// public NamedParameter(string name, string value) { Name = name; diff --git a/ZelWiki.Engine.Function/OrdinalParameter.cs b/ZelWiki.Engine.Function/OrdinalParameter.cs index 7ef3810..7d1feb4 100644 --- a/ZelWiki.Engine.Function/OrdinalParameter.cs +++ b/ZelWiki.Engine.Function/OrdinalParameter.cs @@ -2,27 +2,32 @@ { public class OrdinalParameter { - public string Value { get; set; } - /// - /// Has been matched to a prototype parameter? + /// /// - public bool IsMatched { get; set; } = false; - - /// - /// If matched to a prototype parameter, this is the name of the parameter. - /// - public string ParameterName { get; set; } = string.Empty; - + /// public OrdinalParameter(string value) { Value = value; + IsMatched = false; + ParameterName = string.Empty; } + public string Value { get; set; } + /// + /// + /// + public bool IsMatched { get; set; } + + /// + /// + /// + public string ParameterName { get; set; } + public void AssociateWithPrototypeParam(string paramName) { IsMatched = true; ParameterName = paramName; } } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Function/ParsedFunctionCall.cs b/ZelWiki.Engine.Function/ParsedFunctionCall.cs index af760ea..5f9477b 100644 --- a/ZelWiki.Engine.Function/ParsedFunctionCall.cs +++ b/ZelWiki.Engine.Function/ParsedFunctionCall.cs @@ -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 RawArguments { get; set; } = new List(); - + /// + /// + /// + /// + /// + /// + /// public ParsedFunctionCall(string prefix, string name, int endIndex, List 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 RawArguments { get; set; } } } diff --git a/ZelWiki.Engine.Function/PrototypeParameter.cs b/ZelWiki.Engine.Function/PrototypeParameter.cs index f4a8f6e..e7c8de9 100644 --- a/ZelWiki.Engine.Function/PrototypeParameter.cs +++ b/ZelWiki.Engine.Function/PrototypeParameter.cs @@ -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 AllowedValues { get; set; } = new(); + /// + /// + /// + 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 AllowedValues { get; set; } } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Function/PrototypeSet.cs b/ZelWiki.Engine.Function/PrototypeSet.cs index bd51bed..1b370f1 100644 --- a/ZelWiki.Engine.Function/PrototypeSet.cs +++ b/ZelWiki.Engine.Function/PrototypeSet.cs @@ -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(); + /// + /// + /// + 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; } } } diff --git a/ZelWiki.Engine.Function/SelfDocument.cs b/ZelWiki.Engine.Function/SelfDocument.cs index 33cfd27..9f55917 100644 --- a/ZelWiki.Engine.Function/SelfDocument.cs +++ b/ZelWiki.Engine.Function/SelfDocument.cs @@ -1,10 +1,12 @@ namespace ZelWiki.Engine.Function { + [Obsolete("弃用了")] public static class SelfDocument { /// - /// Don't ever look at this. :( + /// /// + [Obsolete("弃用了")] public static void CreateNotExisting() { /* diff --git a/ZelWiki.Engine.Implementation/AggregatedSearchToken.cs b/ZelWiki.Engine.Implementation/AggregatedSearchToken.cs index 1ce5303..9916ba8 100644 --- a/ZelWiki.Engine.Implementation/AggregatedSearchToken.cs +++ b/ZelWiki.Engine.Implementation/AggregatedSearchToken.cs @@ -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; } } } diff --git a/ZelWiki.Engine.Implementation/CommentHandler.cs b/ZelWiki.Engine.Implementation/CommentHandler.cs index 7ec5a7e..32fbc29 100644 --- a/ZelWiki.Engine.Implementation/CommentHandler.cs +++ b/ZelWiki.Engine.Implementation/CommentHandler.cs @@ -4,18 +4,19 @@ using ZelWiki.Engine.Library.Interfaces; namespace ZelWiki.Engine.Implementation { /// - /// Handles wiki comments. These are generally removed from the result. + /// /// public class CommentHandler : ICommentHandler { /// - /// Handles a wiki comment. + /// 处理评论 /// - /// Reference to the wiki state object - /// The comment text + /// + /// + /// public HandlerResult Handle(IZelEngineState state, string text) { return new HandlerResult() { Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] }; } } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Implementation/CompletionHandler.cs b/ZelWiki.Engine.Implementation/CompletionHandler.cs index 715b98b..f358791 100644 --- a/ZelWiki.Engine.Implementation/CompletionHandler.cs +++ b/ZelWiki.Engine.Implementation/CompletionHandler.cs @@ -5,14 +5,14 @@ using ZelWiki.Repository; namespace ZelWiki.Engine.Implementation { /// - /// Handles wiki completion events. + /// /// public class CompletionHandler : ICompletionHandler { /// - /// Handles wiki completion events. Is called when the wiki processing competes for a given page. + /// 完成事件 /// - /// Reference to the wiki state object + /// public void Complete(IZelEngineState state) { if (GlobalConfiguration.RecordCompilationMetrics) @@ -28,4 +28,4 @@ namespace ZelWiki.Engine.Implementation } } } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Implementation/EmojiHandler.cs b/ZelWiki.Engine.Implementation/EmojiHandler.cs index 48b7263..20b6356 100644 --- a/ZelWiki.Engine.Implementation/EmojiHandler.cs +++ b/ZelWiki.Engine.Implementation/EmojiHandler.cs @@ -5,16 +5,17 @@ using ZelWiki.Models; namespace ZelWiki.Engine.Implementation { /// - /// Handles wiki emojis. + /// /// public class EmojiHandler : IEmojiHandler { /// - /// Handles an emoji instruction. + /// /// - /// Reference to the wiki state object - /// The lookup key for the given emoji. - /// The desired 1-100 scale factor for the emoji. + /// + /// + /// + /// public HandlerResult Handle(IZelEngineState state, string key, int scale) { var emoji = GlobalConfiguration.Emojis.FirstOrDefault(o => o.Shortcut == key); @@ -23,21 +24,24 @@ namespace ZelWiki.Engine.Implementation { if (scale != 100 && scale > 0 && scale <= 500) { - var emojiImage = $"\"{emoji?.Name}\""; + var emojiImage = + $"\"{emoji?.Name}\""; return new HandlerResult(emojiImage); } else { - var emojiImage = $"\"{emoji?.Name}\""; + var emojiImage = + $"\"{emoji?.Name}\""; return new HandlerResult(emojiImage); } } else { - return new HandlerResult(key) { Instructions = [Constants.HandlerResultInstruction.DisallowNestedProcessing] }; + return new HandlerResult(key) + { Instructions = [Constants.HandlerResultInstruction.DisallowNestedProcessing] }; } } } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Implementation/ExceptionHandler.cs b/ZelWiki.Engine.Implementation/ExceptionHandler.cs index 2775e4c..6d141ec 100644 --- a/ZelWiki.Engine.Implementation/ExceptionHandler.cs +++ b/ZelWiki.Engine.Implementation/ExceptionHandler.cs @@ -4,16 +4,16 @@ using ZelWiki.Repository; namespace ZelWiki.Engine.Implementation { /// - /// Handles exceptions thrown by the wiki engine. + /// 异常处理. /// public class ExceptionHandler : IExceptionHandler { /// - /// Called when an exception is thrown by the wiki engine. + /// 日志处理 /// - /// Reference to the wiki state object - /// Optional exception, in the case that this was an actual exception. - /// Text that accompanies the exception. + /// + /// + /// public void Log(IZelEngineState state, Exception? ex, string customText) { if (ex != null) @@ -24,4 +24,4 @@ namespace ZelWiki.Engine.Implementation ExceptionRepository.InsertException(customText); } } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Implementation/ExternalLinkHandler.cs b/ZelWiki.Engine.Implementation/ExternalLinkHandler.cs index 85bb58c..c2ea1b8 100644 --- a/ZelWiki.Engine.Implementation/ExternalLinkHandler.cs +++ b/ZelWiki.Engine.Implementation/ExternalLinkHandler.cs @@ -4,18 +4,18 @@ using ZelWiki.Engine.Library.Interfaces; namespace ZelWiki.Engine.Implementation { /// - /// Handles links the wiki to another site. + /// 处理链接 /// public class ExternalLinkHandler : IExternalLinkHandler { /// - /// Handles an internal wiki link. + /// 处理内链 /// - /// Reference to the wiki state object - /// The address of the external site being linked to. - /// The text which should be show in the absence of an image. - /// The image that should be shown. - /// The 0-100 image scale factor for the given image. + /// + /// + /// + /// + /// 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($"") { - return new HandlerResult($"") - { - Instructions = [Constants.HandlerResultInstruction.DisallowNestedProcessing] - }; - } + Instructions = [Constants.HandlerResultInstruction.DisallowNestedProcessing] + }; } } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Implementation/HeadingHandler.cs b/ZelWiki.Engine.Implementation/HeadingHandler.cs index 882d156..d00ea2d 100644 --- a/ZelWiki.Engine.Implementation/HeadingHandler.cs +++ b/ZelWiki.Engine.Implementation/HeadingHandler.cs @@ -3,18 +3,16 @@ using ZelWiki.Engine.Library.Interfaces; namespace ZelWiki.Engine.Implementation { - /// - /// Handles wiki headings. These are automatically added to the table of contents. - /// public class HeadingHandler : IHeadingHandler { /// - /// Handles wiki headings. These are automatically added to the table of contents. + /// 处理白哦提 /// - /// Reference to the wiki state object - /// The size of the header, also used for table of table of contents indentation. - /// The self link reference. - /// The text for the self link. + /// + /// + /// + /// + /// public HandlerResult Handle(IZelEngineState state, int depth, string link, string text) { if (depth >= 2 && depth <= 6) @@ -22,11 +20,12 @@ namespace ZelWiki.Engine.Implementation int fontSize = 8 - depth; if (fontSize < 5) fontSize = 5; - string html = "" + text + "\r\n"; + string html = "" + text + "\r\n"; return new HandlerResult(html); } return new HandlerResult() { Instructions = [Constants.HandlerResultInstruction.Skip] }; } } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Implementation/Helpers.cs b/ZelWiki.Engine.Implementation/Helpers.cs index 89ac077..dd4f659 100644 --- a/ZelWiki.Engine.Implementation/Helpers.cs +++ b/ZelWiki.Engine.Implementation/Helpers.cs @@ -9,41 +9,41 @@ using ZelWiki.Repository; namespace ZelWiki.Engine.Implementation { + /// + /// + /// public class Helpers { /// - /// Inserts a new page if Page.Id == 0, other wise updates the page. All metadata is written to the database. + /// 更新页面 如果Id为0则新增页面 /// - /// - /// + /// /// + /// /// 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; } /// - /// Rebuilds the page and writes all aspects to the database. + /// 重建页面并将所有方面写入数据库 /// - /// - /// + /// /// + /// 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]); @@ -51,13 +51,13 @@ namespace ZelWiki.Engine.Implementation PageRepository.UpdatePageProcessingInstructions(page.Id, state.ProcessingInstructions); var pageTokens = ParsePageTokens(state).Select(o => - new PageToken - { - PageId = page.Id, - Token = o.Token, - DoubleMetaphone = o.DoubleMetaphone, - Weight = o.Weight - }).ToList(); + new PageToken + { + PageId = page.Id, + Token = o.Token, + DoubleMetaphone = o.DoubleMetaphone, + Weight = o.Weight + }).ToList(); PageRepository.SavePageSearchTokens(pageTokens); @@ -67,7 +67,13 @@ namespace ZelWiki.Engine.Implementation WikiCache.ClearCategory(WikiCacheKey.Build(WikiCache.Category.Page, [page.Navigation])); } - public static List ParsePageTokens(IZelEngineState state) + #region Private + /// + /// + /// + /// + /// + private static List ParsePageTokens(IZelEngineState state) { var parsedTokens = new List(); @@ -86,7 +92,7 @@ namespace ZelWiki.Engine.Implementation return aggregatedTokens; } - internal static List ComputeParsedPageTokens(string content, double weightMultiplier) + private static List ComputeParsedPageTokens(string content, double weightMultiplier) { var searchConfig = ConfigurationRepository.GetConfigurationEntryValuesByGroupName("Search"); @@ -117,14 +123,17 @@ namespace ZelWiki.Engine.Implementation tokens.RemoveAll(o => exclusionWords.Contains(o)); var searchTokens = (from w in tokens - group w by w into g - select new WeightedSearchToken - { - Token = g.Key, - Weight = g.Count() * weightMultiplier - }).ToList(); + group w by w + into g + select new WeightedSearchToken + { + Token = g.Key, + Weight = g.Count() * weightMultiplier + }).ToList(); return searchTokens.Where(o => string.IsNullOrWhiteSpace(o.Token) == false).ToList(); } + + #endregion } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Implementation/InternalLinkHandler.cs b/ZelWiki.Engine.Implementation/InternalLinkHandler.cs index 631db1e..9eff78f 100644 --- a/ZelWiki.Engine.Implementation/InternalLinkHandler.cs +++ b/ZelWiki.Engine.Implementation/InternalLinkHandler.cs @@ -8,19 +8,21 @@ using Constants = ZelWiki.Engine.Library.Constants; namespace ZelWiki.Engine.Implementation { /// - /// Handles links from one wiki page to another. + /// 内链处理. /// public class InternalLinkHandler : IInternalLinkHandler { /// - /// Handles an internal wiki link. + /// /// - /// Reference to the wiki state object - /// The navigation for the linked page. - /// The name of the page being linked to. - /// The text which should be show in the absence of an image. - /// The image that should be shown. - /// The 0-100 image scale factor for the given image. + /// + /// + /// + /// + /// + /// + /// + /// 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 = $""; + //外部图片. + href = + $""; } else if (image.Contains('/')) { - //The image is located on another page. - href = $""; + //图像位于另一页面. + href = + $""; } else { - //The image is located on this page, but this page does not exist. - href = $"{linkText}"; + //图像位于此页面上,但此页面不存在. + href = + $"{linkText}"; } return new HandlerResult(href) @@ -58,7 +63,8 @@ namespace ZelWiki.Engine.Implementation } else if (linkText != null) { - var href = $"{linkText}" + var href = + $"{linkText}" + "?"; 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 = $""; } else if (image.Contains('/')) { - //The image is located on another page. - mockHref = $""; + //图像位于另一页. + mockHref = + $""; } 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 = $""; + //外部图像. + href = + $""; } else if (image.Contains('/')) { - //The image is located on another page. - href = $""; + //图像在另一页面. + href = + $""; } else { - //The image is located on this page. - href = $""; + //图像在此页面 + href = + $""; } } else { - //Just a plain ol' internal page link. + //内链 href = $"{linkText}"; } @@ -150,4 +160,4 @@ namespace ZelWiki.Engine.Implementation } } } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Implementation/MarkupHandler.cs b/ZelWiki.Engine.Implementation/MarkupHandler.cs index 534eafd..589b5ee 100644 --- a/ZelWiki.Engine.Implementation/MarkupHandler.cs +++ b/ZelWiki.Engine.Implementation/MarkupHandler.cs @@ -4,16 +4,17 @@ using ZelWiki.Engine.Library.Interfaces; namespace ZelWiki.Engine.Implementation { /// - /// Handles basic markup/style instructions like bole, italic, underline, etc. + /// 处理基本的标记/样式指令,如粗体、斜体、下划线等. /// public class MarkupHandler : IMarkupHandler { /// - /// Handles basic markup instructions like bole, italic, underline, etc. + /// 处理基本的标记指令,如粗体、斜体、下划线等 /// - /// Reference to the wiki state object - /// The sequence of symbols that were found to denotate this markup instruction, - /// The body of text to apply the style to. + /// + /// + /// + /// public HandlerResult Handle(IZelEngineState state, char sequence, string scopeBody) { switch (sequence) @@ -26,8 +27,7 @@ namespace ZelWiki.Engine.Implementation default: break; } - return new HandlerResult() { Instructions = [Constants.HandlerResultInstruction.Skip] }; } } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Implementation/PostProcessingFunctionHandler.cs b/ZelWiki.Engine.Implementation/PostProcessingFunctionHandler.cs index 3894d63..aa419a7 100644 --- a/ZelWiki.Engine.Implementation/PostProcessingFunctionHandler.cs +++ b/ZelWiki.Engine.Implementation/PostProcessingFunctionHandler.cs @@ -8,7 +8,7 @@ using ZelWiki.Models; namespace ZelWiki.Engine.Implementation { /// - /// Handles post-processing function calls. + /// 处理后处理函数调用. /// 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: {styleName(Flat,List)}='List'"); _collection.Add("##TagCloud: [pageTag] | {Top}='1000'"); @@ -37,133 +38,129 @@ namespace ZelWiki.Engine.Implementation } /// - /// Called to handle function calls when proper prototypes are matched. + /// 当匹配到合适的原型时,调用它来处理函数调用。 /// - /// Reference to the wiki state object - /// The parsed function call and all its parameters and their values. - /// This is not a scope function, this should always be null + /// + /// + /// + /// public HandlerResult Handle(IZelEngineState state, FunctionCall function, string? scopeBody = null) { switch (function.Name.ToLower()) { - //------------------------------------------------------------------------------------------------------------------------------ - //Displays a tag link list. case "tags": //##tags + { + var styleName = function.Parameters.Get("styleName").ToLower(); + var html = new StringBuilder(); + + if (styleName == "list") { - string styleName = function.Parameters.Get("styleName").ToLower(); - var html = new StringBuilder(); - - if (styleName == "list") + html.Append("
    "); + foreach (var tag in state.Tags) { - html.Append("
      "); - foreach (var tag in state.Tags) - { - html.Append($"
    • {tag}"); - } - html.Append("
    "); - } - else if (styleName == "flat") - { - foreach (var tag in state.Tags) - { - if (html.Length > 0) html.Append(" | "); - html.Append($"{tag}"); - } + html.Append($"
  • {tag}"); } - return new HandlerResult(html.ToString()); + html.Append("
"); + } + else if (styleName == "flat") + { + foreach (var tag in state.Tags) + { + if (html.Length > 0) html.Append(" | "); + html.Append($"{tag}"); + } } - //------------------------------------------------------------------------------------------------------------------------------ + return new HandlerResult(html.ToString()); + } + case "tagcloud": - { - var top = function.Parameters.Get("Top"); - string seedTag = function.Parameters.Get("pageTag"); + { + var top = function.Parameters.Get("Top"); + string seedTag = function.Parameters.Get("pageTag"); - string html = TagCloud.Build(seedTag, top); - return new HandlerResult(html); - } - - //------------------------------------------------------------------------------------------------------------------------------ + string html = TagCloud.Build(seedTag, top); + return new HandlerResult(html); + } + case "searchcloud": - { - var top = function.Parameters.Get("Top"); - var tokens = function.Parameters.Get("searchPhrase").Split(" ", StringSplitOptions.RemoveEmptyEntries).ToList(); + { + var top = function.Parameters.Get("Top"); + var tokens = function.Parameters.Get("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. + string html = SearchCloud.Build(tokens, top); + return new HandlerResult(html); + } + case "toc": + { + var alphabetized = function.Parameters.Get("alphabetized"); + + var html = new StringBuilder(); + + var tags = (from t in state.TableOfContents + orderby t.StartingPosition + select t).ToList(); + + var unordered = new List(); + var ordered = new List(); + + if (alphabetized) { - bool alphabetized = function.Parameters.Get("alphabetized"); - - var html = new StringBuilder(); - - var tags = (from t in state.TableOfContents - orderby t.StartingPosition - select t).ToList(); - - var unordered = new List(); - var ordered = new List(); - - if (alphabetized) - { - int level = tags.FirstOrDefault()?.Level ?? 0; - - foreach (var tag in tags) - { - if (level != tag.Level) - { - ordered.AddRange(unordered.OrderBy(o => o.Text)); - unordered.Clear(); - level = tag.Level; - } - - unordered.Add(tag); - } - - ordered.AddRange(unordered.OrderBy(o => o.Text)); - unordered.Clear(); - - tags = ordered.ToList(); - } - - int currentLevel = 0; + var level = tags.FirstOrDefault()?.Level ?? 0; foreach (var tag in tags) { - if (tag.Level > currentLevel) + if (level != tag.Level) { - while (currentLevel < tag.Level) - { - html.Append("
    "); - currentLevel++; - } - } - else if (tag.Level < currentLevel) - { - while (currentLevel > tag.Level) - { - - html.Append("
"); - currentLevel--; - } + ordered.AddRange(unordered.OrderBy(o => o.Text)); + unordered.Clear(); + level = tag.Level; } - html.Append("
  • " + tag.Text + "
  • "); + unordered.Add(tag); } - while (currentLevel > 0) - { - html.Append(""); - currentLevel--; - } + ordered.AddRange(unordered.OrderBy(o => o.Text)); + unordered.Clear(); - return new HandlerResult(html.ToString()); + tags = ordered.ToList(); } + + var currentLevel = 0; + + foreach (var tag in tags) + { + if (tag.Level > currentLevel) + { + while (currentLevel < tag.Level) + { + html.Append("
      "); + currentLevel++; + } + } + else if (tag.Level < currentLevel) + { + while (currentLevel > tag.Level) + { + html.Append("
    "); + currentLevel--; + } + } + + html.Append("
  • " + tag.Text + "
  • "); + } + + while (currentLevel > 0) + { + html.Append(""); + currentLevel--; + } + + return new HandlerResult(html.ToString()); + } } return new HandlerResult() { Instructions = [Constants.HandlerResultInstruction.Skip] }; diff --git a/ZelWiki.Engine.Implementation/ProcessingInstructionFunctionHandler.cs b/ZelWiki.Engine.Implementation/ProcessingInstructionFunctionHandler.cs index ab0db99..21ead69 100644 --- a/ZelWiki.Engine.Implementation/ProcessingInstructionFunctionHandler.cs +++ b/ZelWiki.Engine.Implementation/ProcessingInstructionFunctionHandler.cs @@ -5,7 +5,7 @@ using ZelWiki.Engine.Library.Interfaces; namespace ZelWiki.Engine.Implementation { /// - /// Handles processing-instruction function calls, these functions affect the way the page is processed, but are not directly replaced with text. + /// 处理处理指令函数调用,这些函数会影响页面的处理方式,但不会直接替换为文本. /// 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:{isSilent}='false'"); _collection.Add("@@Tags: [pageTags]"); @@ -42,163 +42,152 @@ namespace ZelWiki.Engine.Implementation } /// - /// Called to handle function calls when proper prototypes are matched. + /// 处理各种页面 /// - /// Reference to the wiki state object - /// The parsed function call and all its parameters and their values. - /// This is not a scope function, this should always be null + /// + /// + /// + /// 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("pageTags"); + state.Tags.AddRange(tags); + state.Tags = state.Tags.Distinct().ToList(); + + return new HandlerResult(string.Empty) { - var tags = function.Parameters.GetList("pageTags"); - state.Tags.AddRange(tags); - state.Tags = state.Tags.Distinct().ToList(); - - return new HandlerResult(string.Empty) - { - Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] - }; - } - - //------------------------------------------------------------------------------------------------------------------------------ + Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] + }; + } case "title": + { + state.PageTitle = function.Parameters.Get("pageTitle"); + + return new HandlerResult(string.Empty) { - state.PageTitle = function.Parameters.Get("pageTitle"); - - return new HandlerResult(string.Empty) - { - Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] - }; - } - - //------------------------------------------------------------------------------------------------------------------------------ + Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] + }; + } case "hidefooterlastmodified": + { + state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.HideFooterLastModified); + + return new HandlerResult(string.Empty) { - state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.HideFooterLastModified); - - return new HandlerResult(string.Empty) - { - Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] - }; - } - - //------------------------------------------------------------------------------------------------------------------------------ + Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] + }; + } case "hidefootercomments": + { + state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.HideFooterComments); + return new HandlerResult(string.Empty) { - state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.HideFooterComments); - return new HandlerResult(string.Empty) - { - Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] - }; - } - - //------------------------------------------------------------------------------------------------------------------------------ + Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] + }; + } case "nocache": + { + state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.NoCache); + return new HandlerResult(string.Empty) { - state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.NoCache); - return new HandlerResult(string.Empty) - { - Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] - }; - } - - //------------------------------------------------------------------------------------------------------------------------------ + Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] + }; + } case "deprecate": + { + if (state.NestDepth == 0) { - if (state.NestDepth == 0) - { - state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.Deprecate); - state.Headers.Add("
    This page has been deprecated and will eventually be deleted.
    "); - } - return new HandlerResult(string.Empty) - { - Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] - }; + state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.Deprecate); + state.Headers.Add( + "
    此页面已被弃用,最终将被删除.
    "); } - //------------------------------------------------------------------------------------------------------------------------------ + return new HandlerResult(string.Empty) + { + Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] + }; + } case "protect": + { + if (state.NestDepth == 0) { - if (state.NestDepth == 0) + bool isSilent = function.Parameters.Get("isSilent"); + state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.Protect); + if (isSilent == false) { - bool isSilent = function.Parameters.Get("isSilent"); - state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.Protect); - if (isSilent == false) - { - state.Headers.Add("
    This page has been protected and can not be changed by non-moderators.
    "); - } + state.Headers.Add( + "
    此页面已受到保护,非版主无法更改.
    "); } - return new HandlerResult(string.Empty) - { - Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] - }; } - //------------------------------------------------------------------------------------------------------------------------------ + return new HandlerResult(string.Empty) + { + Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] + }; + } case "template": + { + if (state.NestDepth == 0) { - if (state.NestDepth == 0) - { - state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.Template); - state.Headers.Add("
    This page is a template and will not appear in indexes or glossaries.
    "); - } - return new HandlerResult(string.Empty) - { - Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] - }; + state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.Template); + state.Headers.Add( + "
    此页面是一个模板,不会出现在索引或术语表中.
    "); } - //------------------------------------------------------------------------------------------------------------------------------ + return new HandlerResult(string.Empty) + { + Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] + }; + } case "review": + { + if (state.NestDepth == 0) { - if (state.NestDepth == 0) - { - state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.Review); - state.Headers.Add("
    This page has been flagged for review, its content may be inaccurate.
    "); - } - return new HandlerResult(string.Empty) - { - Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] - }; + state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.Review); + state.Headers.Add( + "
    此页面已被标记为待审核,其内容可能不准确.
    "); } - //------------------------------------------------------------------------------------------------------------------------------ + return new HandlerResult(string.Empty) + { + Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] + }; + } case "include": + { + if (state.NestDepth == 0) { - if (state.NestDepth == 0) - { - state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.Include); - state.Headers.Add("
    This page is an include and will not appear in indexes or glossaries.
    "); - } - return new HandlerResult(string.Empty) - { - Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] - }; + state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.Include); + state.Headers.Add( + "
    此页为包含页,不会出现在索引或术语表中.
    "); } - //------------------------------------------------------------------------------------------------------------------------------ - case "draft": + return new HandlerResult(string.Empty) { - if (state.NestDepth == 0) - { - state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.Draft); - state.Headers.Add("
    This page is a draft and may contain incorrect information and/or experimental styling.
    "); - } - return new HandlerResult(string.Empty) - { - Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] - }; + Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] + }; + } + case "draft": + { + if (state.NestDepth == 0) + { + state.ProcessingInstructions.Add(ZelWiki.Library.Constants.WikiInstruction.Draft); + state.Headers.Add( + "
    本页为草稿,可能包含不正确的信息包括但不仅限于实验性样式.
    "); } + + return new HandlerResult(string.Empty) + { + Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] + }; + } } return new HandlerResult() { Instructions = [Constants.HandlerResultInstruction.Skip] }; } } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Implementation/ScopeFunctionHandler.cs b/ZelWiki.Engine.Implementation/ScopeFunctionHandler.cs index 6bad5bb..10665ca 100644 --- a/ZelWiki.Engine.Implementation/ScopeFunctionHandler.cs +++ b/ZelWiki.Engine.Implementation/ScopeFunctionHandler.cs @@ -9,7 +9,7 @@ using static ZelWiki.Engine.Library.Constants; namespace ZelWiki.Engine.Implementation { /// - /// Handled scope function calls. + ///处理作用域函数调用. /// public class ScopeFunctionHandler : IScopeFunctionHandler { @@ -23,17 +23,23 @@ namespace ZelWiki.Engine.Implementation { _collection = new FunctionPrototypeCollection(FunctionPrototypeCollection.WikiFunctionType.Scoped); - #region Prototypes. + #region - _collection.Add("$$Code: {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: {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: {type(unordered,ordered)}='unordered'"); _collection.Add("$$Order: {direction(ascending,descending)}='ascending'"); _collection.Add("$$Jumbotron:"); - _collection.Add("$$Callout: {styleName(default,primary,secondary,success,info,warning,danger)}='default' | {titleText}=''"); - _collection.Add("$$Background: {styleName(default,primary,secondary,light,dark,success,info,warning,danger,muted)}='default'"); - _collection.Add("$$Foreground: {styleName(default,primary,secondary,light,dark,success,info,warning,danger,muted)}='default'"); - _collection.Add("$$Alert: {styleName(default,primary,secondary,light,dark,success,info,warning,danger)}='default' | {titleText}=''"); - _collection.Add("$$Card: {styleName(default,primary,secondary,light,dark,success,info,warning,danger)}='default' | {titleText}=''"); + _collection.Add( + "$$Callout: {styleName(default,primary,secondary,success,info,warning,danger)}='default' | {titleText}=''"); + _collection.Add( + "$$Background: {styleName(default,primary,secondary,light,dark,success,info,warning,danger,muted)}='default'"); + _collection.Add( + "$$Foreground: {styleName(default,primary,secondary,light,dark,success,info,warning,danger,muted)}='default'"); + _collection.Add( + "$$Alert: {styleName(default,primary,secondary,light,dark,success,info,warning,danger)}='default' | {titleText}=''"); + _collection.Add( + "$$Card: {styleName(default,primary,secondary,light,dark,success,info,warning,danger)}='default' | {titleText}=''"); _collection.Add("$$Collapse: {linkText}='Show'"); _collection.Add("$$Table: {hasBorder}='true' | {isFirstRowHeader}='true'"); _collection.Add("$$StripedTable: {hasBorder}='true' | {isFirstRowHeader}='true'"); @@ -47,326 +53,308 @@ namespace ZelWiki.Engine.Implementation } /// - /// Called to handle function calls when proper prototypes are matched. + /// /// - /// Reference to the wiki state object - /// The parsed function call and all its parameters and their values. - /// The the text that the function is designed to affect. + /// + /// + /// + /// 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(); + + var language = function.Parameters.Get("language"); + if (string.IsNullOrEmpty(language) || language?.ToLower() == "auto") { - var html = new StringBuilder(); - - string language = function.Parameters.Get("language"); - if (string.IsNullOrEmpty(language) || language?.ToLower() == "auto") - { - html.Append($"
    ");
    -                            html.Append($"{scopeBody.Replace("\r\n", "\n").Replace("\n", SoftBreak)}
    "); - } - else - { - html.Append($"
    ");
    -                            html.Append($"{scopeBody.Replace("\r\n", "\n").Replace("\n", SoftBreak)}
    "); - } - - return new HandlerResult(html.ToString()) - { - Instructions = [Constants.HandlerResultInstruction.DisallowNestedProcessing] - }; + html.Append($"
    ");
    +                        html.Append($"{scopeBody.Replace("\r\n", "\n").Replace("\n", SoftBreak)}
    "); + } + else + { + html.Append($"
    ");
    +                        html.Append($"{scopeBody.Replace("\r\n", "\n").Replace("\n", SoftBreak)}
    "); } - //------------------------------------------------------------------------------------------------------------------------------ + return new HandlerResult(html.ToString()) + { + Instructions = [HandlerResultInstruction.DisallowNestedProcessing] + }; + } case "stripedtable": case "table": + { + var html = new StringBuilder(); + + var hasBorder = function.Parameters.Get("hasBorder"); + var isFirstRowHeader = function.Parameters.Get("isFirstRowHeader"); + + html.Append($"("hasBorder"); - var isFirstRowHeader = function.Parameters.Get("isFirstRowHeader"); - - html.Append($"
    "); - - var lines = scopeBody.Split(['\n'], StringSplitOptions.RemoveEmptyEntries).Select(o => o.Trim()).Where(o => o.Length > 0); - - int rowNumber = 0; - - foreach (var lineText in lines) - { - var columns = lineText.Split("||"); - - if (rowNumber == 0 && isFirstRowHeader) - { - html.Append($""); - } - else if (rowNumber == 1 && isFirstRowHeader || rowNumber == 0 && isFirstRowHeader == false) - { - html.Append($""); - } - - html.Append($""); - foreach (var columnText in columns) - { - if (rowNumber == 0 && isFirstRowHeader) - { - html.Append($""); - } - else - { - html.Append($""); - } - } - - if (rowNumber == 0 && isFirstRowHeader) - { - html.Append($""); - } - html.Append($""); - - rowNumber++; - } - - html.Append($""); - html.Append($"
    {columnText}{columnText}
    "); - - return new HandlerResult(html.ToString()); + html.Append(" table-striped"); } - //------------------------------------------------------------------------------------------------------------------------------ + if (hasBorder) + { + html.Append(" table-bordered"); + } + + html.Append($"\">"); + + var lines = scopeBody.Split(['\n'], StringSplitOptions.RemoveEmptyEntries).Select(o => o.Trim()) + .Where(o => o.Length > 0); + + int rowNumber = 0; + + foreach (var lineText in lines) + { + var columns = lineText.Split("||"); + + if (rowNumber == 0 && isFirstRowHeader) + { + html.Append($""); + } + else if (rowNumber == 1 && isFirstRowHeader || rowNumber == 0 && isFirstRowHeader == false) + { + html.Append($""); + } + + html.Append($""); + foreach (var columnText in columns) + { + if (rowNumber == 0 && isFirstRowHeader) + { + html.Append($"{columnText}"); + } + else + { + html.Append($"{columnText}"); + } + } + + if (rowNumber == 0 && isFirstRowHeader) + { + html.Append($""); + } + + html.Append($""); + + rowNumber++; + } + + html.Append($""); + html.Append($""); + + return new HandlerResult(html.ToString()); + } case "bullets": + { + var html = new StringBuilder(); + + var type = function.Parameters.Get("type"); + + if (type == "unordered") { - var html = new StringBuilder(); + var lines = scopeBody.Split(['\n'], StringSplitOptions.RemoveEmptyEntries).Select(o => o.Trim()) + .Where(o => o.Length > 0); - string type = function.Parameters.Get("type"); + int currentLevel = 0; - if (type == "unordered") + foreach (var line in lines) { - var lines = scopeBody.Split(['\n'], StringSplitOptions.RemoveEmptyEntries).Select(o => o.Trim()).Where(o => o.Length > 0); - - int currentLevel = 0; - - foreach (var line in lines) + var newIndent = 0; + for (; newIndent < line.Length && line[newIndent] == '>'; newIndent++) { - int newIndent = 0; - for (; newIndent < line.Length && line[newIndent] == '>'; newIndent++) - { - //Count how many '>' are at the start of the line. - } - newIndent++; - - if (newIndent < currentLevel) - { - for (; currentLevel != newIndent; currentLevel--) - { - html.Append($""); - } - } - else if (newIndent > currentLevel) - { - for (; currentLevel != newIndent; currentLevel++) - { - html.Append($"
      "); - } - } - - html.Append($"
    • {line.Trim(['>'])}
    • "); + //计算行开头有多少个“>”. } - for (; currentLevel > 0; currentLevel--) + newIndent++; + + if (newIndent < currentLevel) { - html.Append($"
    "); + for (; currentLevel != newIndent; currentLevel--) + { + html.Append($""); + } } + else if (newIndent > currentLevel) + { + for (; currentLevel != newIndent; currentLevel++) + { + html.Append($"
      "); + } + } + + html.Append($"
    • {line.Trim(['>'])}
    • "); } - else if (type == "ordered") + + for (; currentLevel > 0; currentLevel--) { - 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; - for (; newIndent < line.Length && line[newIndent] == '>'; newIndent++) - { - //Count how many '>' are at the start of the line. - } - newIndent++; - - if (newIndent < currentLevel) - { - for (; currentLevel != newIndent; currentLevel--) - { - html.Append($""); - } - } - else if (newIndent > currentLevel) - { - for (; currentLevel != newIndent; currentLevel++) - { - html.Append($"
        "); - } - } - - html.Append($"
      1. {line.Trim(['>'])}
      2. "); - } - - for (; currentLevel > 0; currentLevel--) - { - html.Append($"
      "); - } + html.Append($"
    "); + } + } + else if (type == "ordered") + { + var lines = scopeBody.Split(['\n'], StringSplitOptions.RemoveEmptyEntries).Select(o => o.Trim()) + .Where(o => o.Length > 0); + + var currentLevel = 0; + + foreach (var line in lines) + { + var newIndent = 0; + for (; newIndent < line.Length && line[newIndent] == '>'; newIndent++) + { + //计算行开头有多少个“>”. + } + + newIndent++; + + if (newIndent < currentLevel) + { + for (; currentLevel != newIndent; currentLevel--) + { + html.Append($""); + } + } + else if (newIndent > currentLevel) + { + for (; currentLevel != newIndent; currentLevel++) + { + html.Append($"
      "); + } + } + + html.Append($"
    1. {line.Trim(['>'])}
    2. "); + } + + for (; currentLevel > 0; currentLevel--) + { + html.Append($"
    "); } - return new HandlerResult(html.ToString()); } - //------------------------------------------------------------------------------------------------------------------------------ + return new HandlerResult(html.ToString()); + } case "definesnippet": + { + var html = new StringBuilder(); + + var name = function.Parameters.Get("name"); + + if (!state.Snippets.TryAdd(name, scopeBody)) { - var html = new StringBuilder(); - - string name = function.Parameters.Get("name"); - - if (!state.Snippets.TryAdd(name, scopeBody)) - { - state.Snippets[name] = scopeBody; - } - - return new HandlerResult(html.ToString()); + state.Snippets[name] = scopeBody; } - //------------------------------------------------------------------------------------------------------------------------------ + return new HandlerResult(html.ToString()); + } case "alert": - { - var html = new StringBuilder(); + { + var html = new StringBuilder(); - string titleText = function.Parameters.Get("titleText"); - string style = function.Parameters.Get("styleName").ToLower(); - style = style == "default" ? "" : $"alert-{style}"; + var titleText = function.Parameters.Get("titleText"); + var style = function.Parameters.Get("styleName").ToLower(); + style = style == "default" ? "" : $"alert-{style}"; - if (!string.IsNullOrEmpty(titleText)) scopeBody = $"

    {titleText}

    {scopeBody}"; - html.Append($"
    {scopeBody}
    "); - return new HandlerResult(html.ToString()); - } + if (!string.IsNullOrEmpty(titleText)) scopeBody = $"

    {titleText}

    {scopeBody}"; + html.Append($"
    {scopeBody}
    "); + return new HandlerResult(html.ToString()); + } case "order": - { - var html = new StringBuilder(); + { + var html = new StringBuilder(); - string direction = function.Parameters.Get("direction"); - var lines = scopeBody.Split("\n").Select(o => o.Trim()).ToList(); + var direction = function.Parameters.Get("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))); - } - return new HandlerResult(html.ToString()); - } - - //------------------------------------------------------------------------------------------------------------------------------ + 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(); + { + var html = new StringBuilder(); - string titleText = function.Parameters.Get("titleText", ""); - html.Append($"
    "); - if (!string.IsNullOrEmpty(titleText)) html.Append($"

    {titleText}

    "); - html.Append($"

    {scopeBody}

    "); - html.Append($"
    "); - return new HandlerResult(html.ToString()); - } - - //------------------------------------------------------------------------------------------------------------------------------ + var titleText = function.Parameters.Get("titleText", ""); + html.Append($"
    "); + if (!string.IsNullOrEmpty(titleText)) html.Append($"

    {titleText}

    "); + html.Append($"

    {scopeBody}

    "); + html.Append($"
    "); + return new HandlerResult(html.ToString()); + } case "foreground": - { - var html = new StringBuilder(); + { + var html = new StringBuilder(); - var style = BGFGStyle.GetForegroundStyle(function.Parameters.Get("styleName", "default")).Swap(); - html.Append($"

    {scopeBody}

    "); - return new HandlerResult(html.ToString()); - } - - //------------------------------------------------------------------------------------------------------------------------------ + var style = BGFGStyle.GetForegroundStyle(function.Parameters.Get("styleName", "default")).Swap(); + html.Append($"

    {scopeBody}

    "); + return new HandlerResult(html.ToString()); + } case "background": - { - var html = new StringBuilder(); + { + var html = new StringBuilder(); - var style = BGFGStyle.GetBackgroundStyle(function.Parameters.Get("styleName", "default")); - html.Append($"
    {scopeBody}
    "); - return new HandlerResult(html.ToString()); - } - - //------------------------------------------------------------------------------------------------------------------------------ + var style = BGFGStyle.GetBackgroundStyle(function.Parameters.Get("styleName", "default")); + html.Append( + $"
    {scopeBody}
    "); + return new HandlerResult(html.ToString()); + } case "collapse": - { - var html = new StringBuilder(); + { + var html = new StringBuilder(); - string linkText = function.Parameters.Get("linktext"); - string uid = "A" + Guid.NewGuid().ToString().Replace("-", ""); - html.Append($"{linkText}"); - html.Append($"
    "); - html.Append($"

    {scopeBody}

    "); - return new HandlerResult(html.ToString()); - } - - //------------------------------------------------------------------------------------------------------------------------------ + var linkText = function.Parameters.Get("linktext"); + var uid = "A" + Guid.NewGuid().ToString().Replace("-", ""); + html.Append( + $"{linkText}"); + html.Append($"
    "); + html.Append($"

    {scopeBody}

    "); + return new HandlerResult(html.ToString()); + } case "callout": - { - var html = new StringBuilder(); + { + var html = new StringBuilder(); - string titleText = function.Parameters.Get("titleText"); - string style = function.Parameters.Get("styleName").ToLower(); - style = style == "default" ? "" : style; + string titleText = function.Parameters.Get("titleText"); + string style = function.Parameters.Get("styleName").ToLower(); + style = style == "default" ? "" : style; - html.Append($"
    "); - if (string.IsNullOrWhiteSpace(titleText) == false) html.Append($"

    {titleText}

    "); - html.Append($"{scopeBody}"); - html.Append($"
    "); - return new HandlerResult(html.ToString()); - } - - //------------------------------------------------------------------------------------------------------------------------------ + html.Append($"
    "); + if (string.IsNullOrWhiteSpace(titleText) == false) html.Append($"

    {titleText}

    "); + html.Append($"{scopeBody}"); + html.Append($"
    "); + return new HandlerResult(html.ToString()); + } case "card": - { - var html = new StringBuilder(); + { + var html = new StringBuilder(); - string titleText = function.Parameters.Get("titleText"); - var style = BGFGStyle.GetBackgroundStyle(function.Parameters.Get("styleName", "default")); + var titleText = function.Parameters.Get("titleText"); + var style = BGFGStyle.GetBackgroundStyle(function.Parameters.Get("styleName", "default")); - html.Append($"
    "); - if (string.IsNullOrEmpty(titleText) == false) - { - html.Append($"
    {titleText}
    "); - } - html.Append("
    "); - html.Append($"

    {scopeBody}

    "); - html.Append("
    "); - html.Append("
    "); - return new HandlerResult(html.ToString()); - - } + html.Append($"
    "); + if (string.IsNullOrEmpty(titleText) == false) + html.Append($"
    {titleText}
    "); + html.Append("
    "); + html.Append($"

    {scopeBody}

    "); + html.Append("
    "); + html.Append("
    "); + return new HandlerResult(html.ToString()); + } } - return new HandlerResult() { Instructions = [Constants.HandlerResultInstruction.Skip] }; + return new HandlerResult() { Instructions = [HandlerResultInstruction.Skip] }; } } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Implementation/StandardFunctionHandler.cs b/ZelWiki.Engine.Implementation/StandardFunctionHandler.cs index 6c0d3dc..364edce 100644 --- a/ZelWiki.Engine.Implementation/StandardFunctionHandler.cs +++ b/ZelWiki.Engine.Implementation/StandardFunctionHandler.cs @@ -14,7 +14,7 @@ using Constants = ZelWiki.Engine.Library.Constants; namespace ZelWiki.Engine.Implementation { /// - /// Handled standard function calls. + /// 处理函数调用. /// public class StandardFunctionHandler : IStandardFunctionHandler { @@ -26,34 +26,46 @@ namespace ZelWiki.Engine.Implementation { if (_collection == null) { - _collection = new FunctionPrototypeCollection(FunctionPrototypeCollection.WikiFunctionType.Standard); + _collection = + new FunctionPrototypeCollection(FunctionPrototypeCollection.WikiFunctionType.Standard); - #region Prototypes. + #region _collection.Add("##Snippet: [name]"); _collection.Add("##Seq: {key}='Default'"); _collection.Add("##Set: [key] | [value]"); _collection.Add("##Get: [key]"); _collection.Add("##Color: [color] | [text]"); - _collection.Add("##Tag: [pageTags]"); //This is left here for backwards compatibility, Tag does not change the output so it should be a processing instruction. - _collection.Add("##SearchList: [searchPhrase] | {styleName(List,Full)}='Full' | {pageSize}='5' | {pageSelector}='true' | {allowFuzzyMatching}='false' | {showNamespace}='false'"); - _collection.Add("##TagList: [pageTags] | {Top}='1000' | {styleName(List,Full)}='Full' | {showNamespace}='false'"); - _collection.Add("##NamespaceGlossary: [namespaces] | {Top}='1000' | {styleName(List,Full)}='Full' | {showNamespace}='false'"); - _collection.Add("##NamespaceList: [namespaces] | {Top}='1000' | {styleName(List,Full)}='Full' | {showNamespace}='false'"); - _collection.Add("##TagGlossary: [pageTags] | {Top}='1000' | {styleName(List,Full)}='Full' | {showNamespace}='false'"); - _collection.Add("##RecentlyModified: {Top}='1000' | {styleName(List,Full)}='Full' | {showNamespace}='false'"); - _collection.Add("##TextGlossary: [searchPhrase] | {Top}='1000' | {styleName(List,Full)}='Full' | {showNamespace}='false'"); + _collection.Add("##Tag: [pageTags]"); + _collection.Add( + "##SearchList: [searchPhrase] | {styleName(List,Full)}='Full' | {pageSize}='5' | {pageSelector}='true' | {allowFuzzyMatching}='false' | {showNamespace}='false'"); + _collection.Add( + "##TagList: [pageTags] | {Top}='1000' | {styleName(List,Full)}='Full' | {showNamespace}='false'"); + _collection.Add( + "##NamespaceGlossary: [namespaces] | {Top}='1000' | {styleName(List,Full)}='Full' | {showNamespace}='false'"); + _collection.Add( + "##NamespaceList: [namespaces] | {Top}='1000' | {styleName(List,Full)}='Full' | {showNamespace}='false'"); + _collection.Add( + "##TagGlossary: [pageTags] | {Top}='1000' | {styleName(List,Full)}='Full' | {showNamespace}='false'"); + _collection.Add( + "##RecentlyModified: {Top}='1000' | {styleName(List,Full)}='Full' | {showNamespace}='false'"); + _collection.Add( + "##TextGlossary: [searchPhrase] | {Top}='1000' | {styleName(List,Full)}='Full' | {showNamespace}='false'"); _collection.Add("##Image: [name] | {scale}='100' | {altText}=''"); _collection.Add("##File: [name] | {linkText} | {showSize}='false'"); - _collection.Add("##Related: {styleName(List,Flat,Full)}='Full' | {pageSize}='10' | {pageSelector}='true'"); - _collection.Add("##Similar: {similarity}='80' | {styleName(List,Flat,Full)}='Full' | {pageSize}='10' | {pageSelector}='true'"); + _collection.Add( + "##Related: {styleName(List,Flat,Full)}='Full' | {pageSize}='10' | {pageSelector}='true'"); + _collection.Add( + "##Similar: {similarity}='80' | {styleName(List,Flat,Full)}='Full' | {pageSize}='10' | {pageSelector}='true'"); _collection.Add("##EditLink: {linkText}='edit'"); _collection.Add("##Inject: [pageName]"); _collection.Add("##Include: [pageName]"); _collection.Add("##BR: {Count}='1'"); _collection.Add("##HR: {Height}='1'"); - _collection.Add("##Revisions:{styleName(Full,List)}='Full' | {pageSize}='5' | {pageSelector}='true' | {pageName}=''"); - _collection.Add("##Attachments:{styleName(Full,List)}='Full' | {pageSize}='5' | {pageSelector}='true' | {pageName}=''"); + _collection.Add( + "##Revisions:{styleName(Full,List)}='Full' | {pageSize}='5' | {pageSelector}='true' | {pageName}=''"); + _collection.Add( + "##Attachments:{styleName(Full,List)}='Full' | {pageSize}='5' | {pageSelector}='true' | {pageName}=''"); _collection.Add("##Title:"); _collection.Add("##Navigation:"); _collection.Add("##Name:"); @@ -62,10 +74,11 @@ namespace ZelWiki.Engine.Implementation _collection.Add("##Created:"); _collection.Add("##LastModified:"); _collection.Add("##AppVersion:"); - _collection.Add("##ProfileGlossary: {Top}='1000' | {pageSize}='100' | {searchToken}=''"); - _collection.Add("##ProfileList: {Top}='1000' | {pageSize}='100' | {searchToken}=''"); + _collection.Add( + "##ProfileGlossary: {Top}='1000' | {pageSize}='100' | {searchToken}=''"); + _collection.Add( + "##ProfileList: {Top}='1000' | {pageSize}='100' | {searchToken}=''"); - //System functions (we don't advertize these, but they aren't unsafe): _collection.Add("##SystemEmojiCategoryList:"); _collection.Add("##SystemEmojiList:"); @@ -100,1087 +113,1074 @@ namespace ZelWiki.Engine.Implementation } /// - /// Called to handle function calls when proper prototypes are matched. + /// 当匹配到合适的原型时,调用它来处理函数调用。 /// - /// Reference to the wiki state object - /// The parsed function call and all its parameters and their values. - /// This is not a scope function, this should always be null + /// + /// + /// + /// + /// public HandlerResult Handle(IZelEngineState state, FunctionCall function, string? scopeBody = null) { switch (function.Name.ToLower()) { - //------------------------------------------------------------------------------------------------------------------------------ - //Creates a glossary all user profiles. case "profileglossary": + { + if (!GlobalConfiguration.EnablePublicProfiles) { - if (!ZelWiki.Models.GlobalConfiguration.EnablePublicProfiles) + return new HandlerResult("公共配置文件已禁用."); + } + + var html = new StringBuilder(); + var refTag = state.GetNextQueryToken(); + var pageNumber = int.Parse(state.QueryString[refTag].ToString().DefaultWhenNullOrEmpty("1")); + var pageSize = function.Parameters.Get("pageSize"); + var searchToken = function.Parameters.Get("searchToken"); + var topCount = function.Parameters.Get("top"); + var profiles = UsersRepository.GetAllPublicProfilesPaged(pageNumber, pageSize, searchToken); + + var glossaryName = "glossary_" + new Random().Next(0, 1000000).ToString(); + var alphabet = profiles.Select(p => p.AccountName.Substring(0, 1).ToUpper()).Distinct(); + + if (profiles.Any()) + { + html.Append("
    "); + foreach (var alpha in alphabet) { - return new HandlerResult("Public profiles are disabled."); + html.Append("" + alpha + " "); } - var html = new StringBuilder(); - string refTag = state.GetNextQueryToken(); - int pageNumber = int.Parse(state.QueryString[refTag].ToString().DefaultWhenNullOrEmpty("1")); - var pageSize = function.Parameters.Get("pageSize"); - var searchToken = function.Parameters.Get("searchToken"); - var topCount = function.Parameters.Get("top"); - var profiles = UsersRepository.GetAllPublicProfilesPaged(pageNumber, pageSize, searchToken); + html.Append("
    "); - string glossaryName = "glossary_" + new Random().Next(0, 1000000).ToString(); - var alphabet = profiles.Select(p => p.AccountName.Substring(0, 1).ToUpper()).Distinct(); - - if (profiles.Count() > 0) + html.Append("
      "); + foreach (var alpha in alphabet) { - html.Append("
      "); - foreach (var alpha in alphabet) - { - html.Append("" + alpha + " "); - } - html.Append("
      "); + html.Append("
    • " + alpha + "
    • "); html.Append("
        "); - foreach (var alpha in alphabet) + foreach (var profile in profiles.Where(p => + p.AccountName.ToLower().StartsWith(alpha.ToLower()))) { - html.Append("
      • " + alpha + "
      • "); - - html.Append("
          "); - foreach (var profile in profiles.Where(p => p.AccountName.ToLower().StartsWith(alpha.ToLower()))) - { - html.Append($"
        • {profile.AccountName}"); - html.Append("
        • "); - } - html.Append("
        "); + html.Append( + $"
      • {profile.AccountName}"); + html.Append("
      • "); } html.Append("
      "); } - return new HandlerResult(html.ToString()); + + html.Append("
    "); } - //------------------------------------------------------------------------------------------------------------------------------ - //Creates a list of all user profiles. + return new HandlerResult(html.ToString()); + } + case "profilelist": + { + if (!GlobalConfiguration.EnablePublicProfiles) { - if (!ZelWiki.Models.GlobalConfiguration.EnablePublicProfiles) - { - return new HandlerResult("Public profiles are disabled."); - } - - var html = new StringBuilder(); - string refTag = state.GetNextQueryToken(); - int pageNumber = int.Parse(state.QueryString[refTag].ToString().DefaultWhenNullOrEmpty("1")); - var pageSize = function.Parameters.Get("pageSize"); - var searchToken = function.Parameters.Get("searchToken"); - var profiles = UsersRepository.GetAllPublicProfilesPaged(pageNumber, pageSize, searchToken); - - if (profiles.Count() > 0) - { - html.Append("
      "); - - foreach (var profile in profiles) - { - html.Append($"
    • {profile.AccountName}"); - html.Append("
    • "); - } - - html.Append("
    "); - } - - if (profiles.Count > 0 && profiles.First().PaginationPageCount > 1) - { - html.Append(PageSelectorGenerator.Generate(refTag, state.QueryString, profiles.First().PaginationPageCount)); - } - - return new HandlerResult(html.ToString()); + return new HandlerResult("公共配置文件已禁用."); } - //------------------------------------------------------------------------------------------------------------------------------ + var html = new StringBuilder(); + var refTag = state.GetNextQueryToken(); + var pageNumber = int.Parse(state.QueryString[refTag].ToString().DefaultWhenNullOrEmpty("1")); + var pageSize = function.Parameters.Get("pageSize"); + var searchToken = function.Parameters.Get("searchToken"); + var profiles = UsersRepository.GetAllPublicProfilesPaged(pageNumber, pageSize, searchToken); + + if (profiles.Any()) + { + html.Append("
      "); + + foreach (var profile in profiles) + { + html.Append( + $"
    • {profile.AccountName}"); + html.Append("
    • "); + } + + html.Append("
    "); + } + + if (profiles.Count > 0 && profiles.First().PaginationPageCount > 1) + { + html.Append(PageSelectorGenerator.Generate(refTag, state.QueryString, + profiles.First().PaginationPageCount)); + } + + return new HandlerResult(html.ToString()); + } case "attachments": + { + var refTag = state.GetNextQueryToken(); + + var pageNumber = int.Parse(state.QueryString[refTag].ToString().DefaultWhenNullOrEmpty("1")); + + var navigation = + NamespaceNavigation.CleanAndValidate(function.Parameters.Get("pageName", + state.Page.Navigation)); + var styleName = function.Parameters.Get("styleName").ToLower(); + var pageSize = function.Parameters.Get("pageSize"); + var pageSelector = function.Parameters.Get("pageSelector"); + var attachments = + PageFileRepository.GetPageFilesInfoByPageNavigationAndPageRevisionPaged(navigation, pageNumber, + pageSize, state.Revision); + var html = new StringBuilder(); + + if (attachments.Count > 0) { - string refTag = state.GetNextQueryToken(); - - int pageNumber = int.Parse(state.QueryString[refTag].ToString().DefaultWhenNullOrEmpty("1")); - - var navigation = NamespaceNavigation.CleanAndValidate(function.Parameters.Get("pageName", state.Page.Navigation)); - string styleName = function.Parameters.Get("styleName").ToLower(); - var pageSize = function.Parameters.Get("pageSize"); - var pageSelector = function.Parameters.Get("pageSelector"); - var attachments = PageFileRepository.GetPageFilesInfoByPageNavigationAndPageRevisionPaged(navigation, pageNumber, pageSize, state.Revision); - var html = new StringBuilder(); - - if (attachments.Count() > 0) + html.Append("
      "); + foreach (var file in attachments) { - html.Append("
        "); - foreach (var file in attachments) + if (state.Revision != null) { - if (state.Revision != null) - { - html.Append($"
      • {file.Name}"); - } - else - { - html.Append($"
      • {file.Name} "); - } - - if (styleName == "full") - { - html.Append($" - ({file.FriendlySize})"); - } - - html.Append("
      • "); - } - html.Append("
      "); - - if (pageSelector && attachments.Count > 0 && attachments.First().PaginationPageCount > 1) - { - html.Append(PageSelectorGenerator.Generate(refTag, state.QueryString, attachments.First().PaginationPageCount)); - } - } - - return new HandlerResult(html.ToString()); - } - - //------------------------------------------------------------------------------------------------------------------------------ - case "revisions": - { - if (state.Session == null) - { - throw new Exception($"Localization is not supported without SessionState."); - } - - string refTag = state.GetNextQueryToken(); - - int pageNumber = int.Parse(state.QueryString[refTag].ToString().DefaultWhenNullOrEmpty("1")); - - var navigation = NamespaceNavigation.CleanAndValidate(function.Parameters.Get("pageName", state.Page.Navigation)); - string styleName = function.Parameters.Get("styleName").ToLower(); - var pageSize = function.Parameters.Get("pageSize"); - var pageSelector = function.Parameters.Get("pageSelector"); - var revisions = PageRepository.GetPageRevisionsInfoByNavigationPaged(navigation, pageNumber, null, null, pageSize); - var html = new StringBuilder(); - - if (revisions.Count() > 0) - { - html.Append("
        "); - foreach (var item in revisions) - { - html.Append($"
      • {item.Revision} by {item.ModifiedByUserName} on {state.Session.LocalizeDateTime(item.ModifiedDate)}"); - - if (styleName == "full") - { - var thisRev = PageRepository.GetPageRevisionByNavigation(state.Page.Navigation, item.Revision); - var prevRev = PageRepository.GetPageRevisionByNavigation(state.Page.Navigation, item.Revision - 1); - - var summaryText = Differentiator.GetComparisonSummary(thisRev?.Body ?? string.Empty, prevRev?.Body ?? string.Empty); - - if (summaryText.Length > 0) - { - html.Append(" - " + summaryText); - } - } - html.Append("
      • "); - } - html.Append("
      "); - - if (pageSelector && revisions.Count > 0 && revisions.First().PaginationPageCount > 1) - { - html.Append(PageSelectorGenerator.Generate(refTag, state.QueryString, revisions.First().PaginationPageCount)); - } - } - - return new HandlerResult(html.ToString()); - } - - //------------------------------------------------------------------------------------------------------------------------------ - case "seq": //##Seq({Key}) Inserts a sequence into the document. - { - var key = function.Parameters.Get("Key"); - - var sequences = state.GetStateValue("_sequences", new Dictionary()); - - if (sequences.ContainsKey(key) == false) - { - sequences.Add(key, 0); - } - - sequences[key]++; - - return new HandlerResult(sequences[key].ToString()) - { - Instructions = [Constants.HandlerResultInstruction.OnlyReplaceFirstMatch] - }; - } - - //------------------------------------------------------------------------------------------------------------------------------ - case "editlink": //(##EditLink(link text)) - { - var linkText = function.Parameters.Get("linkText"); - return new HandlerResult($"{linkText}"); - } - - //------------------------------------------------------------------------------------------------------------------------------ - //injects an un-processed wiki body into the calling page. - case "inject": //(PageName) - { - var navigation = function.Parameters.Get("pageName"); - - var page = GetPageFromPathInfo(navigation); - if (page != null) - { - return new HandlerResult(page.Body) - { - Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] - }; - } - throw new Exception($"The include page was not found: [{navigation}]"); - - } - - //------------------------------------------------------------------------------------------------------------------------------ - //Includes a processed wiki body into the calling page. - case "include": //(PageName) - { - var navigation = function.Parameters.Get("pageName"); - - var page = GetPageFromPathInfo(navigation); - if (page != null) - { - var childState = state.TransformChild(page); - - MergeUserVariables(ref state, childState.Variables); - MergeSnippets(ref state, childState.Snippets); - - return new HandlerResult(childState.HtmlResult) - { - Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] - }; - } - throw new Exception($"The include page was not found: [{navigation}]"); - } - //------------------------------------------------------------------------------------------------------------------------------ - - case "set": - { - var key = function.Parameters.Get("key"); - var value = function.Parameters.Get("value"); - - if (state.Variables.ContainsKey(key)) - { - state.Variables[key] = value; - } - else - { - state.Variables.Add(key, value); - } - - return new HandlerResult(string.Empty) - { - Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] - }; - } - //------------------------------------------------------------------------------------------------------------------------------ - - case "get": - { - var key = function.Parameters.Get("key"); - - if (state.Variables.TryGetValue(key, out var variable)) - { - return new HandlerResult(variable); - } - - throw new Exception($"The wiki variable {key} is not defined. It should be set with ##Set() before calling Get()."); - } - - //------------------------------------------------------------------------------------------------------------------------------ - case "color": - { - var color = function.Parameters.Get("color"); - var text = function.Parameters.Get("text"); - - return new HandlerResult($"{text}"); - } - - //------------------------------------------------------------------------------------------------------------------------------ - //Associates tags with a page. These are saved with the page and can also be displayed. - case "tag": //##tag(pipe|separated|list|of|tags) - { - var tags = function.Parameters.GetList("pageTags"); - state.Tags.AddRange(tags); - state.Tags = state.Tags.Distinct().ToList(); - - return new HandlerResult(string.Empty) - { - Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] - }; - } - - //------------------------------------------------------------------------------------------------------------------------------ - //Displays an image that is attached to the page. - case "image": //##Image(Name, [optional:default=100]Scale, [optional:default=""]Alt-Text) - { - string imageName = function.Parameters.Get("name"); - string alt = function.Parameters.Get("alttext", imageName); - int scale = function.Parameters.Get("scale"); - - bool explicitNamespace = imageName.Contains("::"); - bool isPageForeignImage = false; - - string navigation = state.Page.Navigation; - if (imageName.StartsWith("http", StringComparison.InvariantCultureIgnoreCase)) - { - string image = $"\"{alt}\""; - return new HandlerResult(image); - } - else if (imageName.Contains('/')) - { - //Allow loading attached images from other pages. - int slashIndex = imageName.IndexOf("/"); - navigation = NamespaceNavigation.CleanAndValidate(imageName.Substring(0, slashIndex)); - imageName = imageName.Substring(slashIndex + 1); - isPageForeignImage = true; - } - - if (explicitNamespace == false && state.Page.Namespace != null) - { - if (PageFileRepository.GetPageFileAttachmentInfoByPageNavigationPageRevisionAndFileNavigation(navigation, NamespaceNavigation.CleanAndValidate(imageName), state.Revision) == null) - { - //If the image does not exist, and no namespace was specified, but the page has a namespace - then default to the pages namespace. - navigation = NamespaceNavigation.CleanAndValidate($"{state.Page.Namespace}::{imageName}"); - } - } - - if (state.Revision != null && isPageForeignImage == false) - { - //Check for isPageForeignImage because we don't version foreign page files. - string link = $"/Page/Image/{navigation}/{NamespaceNavigation.CleanAndValidate(imageName)}/{state.Revision}"; - string image = $"\"{alt}\""; - return new HandlerResult(image); - } - else - { - string link = $"/Page/Image/{navigation}/{NamespaceNavigation.CleanAndValidate(imageName)}"; - string image = $"\"{alt}\""; - return new HandlerResult(image); - } - } - - //------------------------------------------------------------------------------------------------------------------------------ - //Displays an file download link - case "file": //##file(Name | Alt-Text | [optional display file size] true/false) - { - string fileName = function.Parameters.Get("name"); - - bool explicitNamespace = fileName.Contains("::"); - bool isPageForeignFile = false; - - string navigation = state.Page.Navigation; - if (fileName.Contains('/')) - { - //Allow loading attached images from other pages. - int slashIndex = fileName.IndexOf("/"); - navigation = NamespaceNavigation.CleanAndValidate(fileName.Substring(0, slashIndex)); - fileName = fileName.Substring(slashIndex + 1); - isPageForeignFile = true; - } - - if (explicitNamespace == false && state.Page.Namespace != null) - { - if (PageFileRepository.GetPageFileAttachmentInfoByPageNavigationPageRevisionAndFileNavigation(navigation, NamespaceNavigation.CleanAndValidate(fileName), state.Revision) == null) - { - //If the image does not exist, and no namespace was specified, but the page has a namespace - then default to the pages namespace. - navigation = NamespaceNavigation.CleanAndValidate($"{state.Page.Namespace}::{fileName}"); - } - } - - var attachment = PageFileRepository.GetPageFileAttachmentInfoByPageNavigationPageRevisionAndFileNavigation(navigation, NamespaceNavigation.CleanAndValidate(fileName), state.Revision); - if (attachment != null) - { - string alt = function.Parameters.Get("linkText", fileName); - - if (function.Parameters.Get("showSize")) - { - alt += $" ({attachment.FriendlySize})"; - } - - if (state.Revision != null && isPageForeignFile == false) - { - //Check for isPageForeignImage because we don't version foreign page files. - string link = $"/Page/Binary/{navigation}/{NamespaceNavigation.CleanAndValidate(fileName)}/{state.Revision}"; - string image = $"{alt}"; - return new HandlerResult(image); + html.Append( + $"
    • {file.Name}"); } else { - string link = $"/Page/Binary/{navigation}/{NamespaceNavigation.CleanAndValidate(fileName)}"; - string image = $"{alt}"; - return new HandlerResult(image); + html.Append( + $"
    • {file.Name} "); } + + if (styleName == "full") + { + html.Append($" - ({file.FriendlySize})"); + } + + html.Append("
    • "); + } + + html.Append("
    "); + + if (pageSelector && attachments.Count > 0 && attachments.First().PaginationPageCount > 1) + { + html.Append(PageSelectorGenerator.Generate(refTag, state.QueryString, + attachments.First().PaginationPageCount)); } - throw new Exception($"File not found [{fileName}]"); } - //------------------------------------------------------------------------------------------------------------------------------ - //Creates a list of pages that have been recently modified. - case "recentlymodified": //##RecentlyModified(TopCount) + return new HandlerResult(html.ToString()); + } + case "revisions": + { + if (state.Session == null) { - string styleName = function.Parameters.Get("styleName").ToLower(); - var takeCount = function.Parameters.Get("top"); - var showNamespace = function.Parameters.Get("showNamespace"); + throw new Exception($"session异常."); + } - var pages = PageRepository.GetTopRecentlyModifiedPagesInfo(takeCount) - .OrderByDescending(o => o.ModifiedDate).ThenBy(o => o.Title).ToList(); + var refTag = state.GetNextQueryToken(); - var html = new StringBuilder(); + var pageNumber = int.Parse(state.QueryString[refTag].ToString().DefaultWhenNullOrEmpty("1")); - if (pages.Count() > 0) + var navigation = + NamespaceNavigation.CleanAndValidate(function.Parameters.Get("pageName", + state.Page.Navigation)); + var styleName = function.Parameters.Get("styleName").ToLower(); + var pageSize = function.Parameters.Get("pageSize"); + var pageSelector = function.Parameters.Get("pageSelector"); + var revisions = + PageRepository.GetPageRevisionsInfoByNavigationPaged(navigation, pageNumber, null, null, + pageSize); + var html = new StringBuilder(); + + if (revisions.Any()) + { + html.Append("
      "); + foreach (var item in revisions) { - html.Append("
        "); - foreach (var page in pages) + html.Append( + $"
      • {item.Revision} by {item.ModifiedByUserName} on {state.Session.LocalizeDateTime(item.ModifiedDate)}"); + + if (styleName == "full") { - if (showNamespace) + var thisRev = + PageRepository.GetPageRevisionByNavigation(state.Page.Navigation, item.Revision); + var prevRev = + PageRepository.GetPageRevisionByNavigation(state.Page.Navigation, + item.Revision - 1); + + var summaryText = Differentiator.GetComparisonSummary(thisRev?.Body ?? string.Empty, + prevRev?.Body ?? string.Empty); + + if (summaryText.Length > 0) { - html.Append($"
      • {page.Name}"); + html.Append(" - " + summaryText); } - else - { - html.Append($"
      • {page.Title}"); - } - - if (styleName == "full") - { - if (page?.Description?.Length > 0) - { - html.Append(" - " + page.Description); - } - } - html.Append("
      • "); - } - html.Append("
      "); - } - - return new HandlerResult(html.ToString()); - } - - //------------------------------------------------------------------------------------------------------------------------------ - //Creates a glossary of pages in the specified namespace. - case "namespaceglossary": - { - string glossaryName = "glossary_" + new Random().Next(0, 1000000).ToString(); - var namespaces = function.Parameters.GetList("namespaces"); - - string styleName = function.Parameters.Get("styleName").ToLower(); - var topCount = function.Parameters.Get("top"); - var pages = PageRepository.GetPageInfoByNamespaces(namespaces).Take(topCount).OrderBy(o => o.Name).ToList(); - var html = new StringBuilder(); - var alphabet = pages.Select(p => p.Title.Substring(0, 1).ToUpper()).Distinct(); - var showNamespace = function.Parameters.Get("showNamespace"); - - if (pages.Count() > 0) - { - html.Append("
      "); - foreach (var alpha in alphabet) - { - html.Append("" + alpha + " "); - } - html.Append("
      "); - - html.Append("
        "); - foreach (var alpha in alphabet) - { - html.Append("
      • " + alpha + "
      • "); - - html.Append("
          "); - foreach (var page in pages.Where(p => p.Title.ToLower().StartsWith(alpha.ToLower()))) - { - if (showNamespace) - { - html.Append($"
        • {page.Name}"); - } - else - { - html.Append($"
        • {page.Title}"); - } - - if (styleName == "full") - { - if (page?.Description?.Length > 0) - { - html.Append(" - " + page.Description); - } - } - html.Append("
        • "); - } - html.Append("
        "); } - html.Append("
      "); + html.Append(""); } - return new HandlerResult(html.ToString()); + html.Append("
    "); + + if (pageSelector && revisions.Count > 0 && revisions.First().PaginationPageCount > 1) + { + html.Append(PageSelectorGenerator.Generate(refTag, state.QueryString, + revisions.First().PaginationPageCount)); + } } - //------------------------------------------------------------------------------------------------------------------------------ - //Creates a list of pages by searching the page tags. - case "namespacelist": + return new HandlerResult(html.ToString()); + } + case "seq": //##Seq({Key}) 在文档中插入序列. + { + var key = function.Parameters.Get("Key"); + + var sequences = state.GetStateValue("_sequences", new Dictionary()); + + if (sequences.ContainsKey(key) == false) { - string styleName = function.Parameters.Get("styleName").ToLower(); - var topCount = function.Parameters.Get("top"); - var namespaces = function.Parameters.GetList("namespaces"); - var showNamespace = function.Parameters.Get("showNamespace"); - - var pages = PageRepository.GetPageInfoByNamespaces(namespaces).Take(topCount).OrderBy(o => o.Name).ToList(); - var html = new StringBuilder(); - - if (pages.Count() > 0) - { - html.Append("
      "); - - foreach (var page in pages) - { - if (showNamespace) - { - html.Append($"
    • {page.Name}"); - } - else - { - html.Append($"
    • {page.Title}"); - } - - if (styleName == "full") - { - if (page?.Description?.Length > 0) - { - html.Append(" - " + page.Description); - } - } - - html.Append("
    • "); - } - - html.Append("
    "); - } - - return new HandlerResult(html.ToString()); + sequences.Add(key, 0); } - //------------------------------------------------------------------------------------------------------------------------------ - //Creates a glossary of pages with the specified comma separated tags. - case "tagglossary": + sequences[key]++; + + return new HandlerResult(sequences[key].ToString()) { - string glossaryName = "glossary_" + new Random().Next(0, 1000000).ToString(); - var tags = function.Parameters.GetList("pageTags"); + Instructions = [Constants.HandlerResultInstruction.OnlyReplaceFirstMatch] + }; + } + case "editlink": //(##EditLink(link text)) + { + var linkText = function.Parameters.Get("linkText"); + return new HandlerResult($"{linkText}"); + } + case "inject": //(PageName) + { + var navigation = function.Parameters.Get("pageName"); - string styleName = function.Parameters.Get("styleName").ToLower(); - var topCount = function.Parameters.Get("top"); - var pages = PageRepository.GetPageInfoByTags(tags).Take(topCount).OrderBy(o => o.Name).ToList(); - var html = new StringBuilder(); - var alphabet = pages.Select(p => p.Title.Substring(0, 1).ToUpper()).Distinct(); - var showNamespace = function.Parameters.Get("showNamespace"); - - if (pages.Count() > 0) + var page = GetPageFromPathInfo(navigation); + if (page != null) + { + return new HandlerResult(page.Body) { - html.Append("
    "); - foreach (var alpha in alphabet) - { - html.Append("" + alpha + " "); - } - html.Append("
    "); - - html.Append("
      "); - foreach (var alpha in alphabet) - { - html.Append("
    • " + alpha + "
    • "); - - html.Append("
        "); - foreach (var page in pages.Where(p => p.Title.ToLower().StartsWith(alpha.ToLower()))) - { - if (showNamespace) - { - html.Append($"
      • {page.Name}"); - } - else - { - html.Append($"
      • {page.Title}"); - } - - if (styleName == "full") - { - if (page?.Description?.Length > 0) - { - html.Append(" - " + page.Description); - } - } - html.Append("
      • "); - } - html.Append("
      "); - } - - html.Append("
    "); - } - - return new HandlerResult(html.ToString()); + Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] + }; } - //------------------------------------------------------------------------------------------------------------------------------ - //Creates a glossary by searching page's body text for the specified comma separated list of words. - case "textglossary": + throw new Exception($"未找到包含页面: [{navigation}]"); + } + + case "include": //(PageName) + { + var navigation = function.Parameters.Get("pageName"); + + var page = GetPageFromPathInfo(navigation); + if (page != null) { - string glossaryName = "glossary_" + new Random().Next(0, 1000000).ToString(); - var searchTokens = function.Parameters.Get("searchPhrase").Split(" ", StringSplitOptions.RemoveEmptyEntries).ToList(); - var topCount = function.Parameters.Get("top"); - var showNamespace = function.Parameters.Get("showNamespace"); + var childState = state.TransformChild(page); - var pages = PageRepository.PageSearch(searchTokens).Take(topCount).OrderBy(o => o.Name).ToList(); - var html = new StringBuilder(); - var alphabet = pages.Select(p => p.Title.Substring(0, 1).ToUpper()).Distinct(); - string styleName = function.Parameters.Get("styleName").ToLower(); + MergeUserVariables(ref state, childState.Variables); + MergeSnippets(ref state, childState.Snippets); - if (pages.Count() > 0) + return new HandlerResult(childState.HtmlResult) { - html.Append("
    "); - foreach (var alpha in alphabet) - { - html.Append("" + alpha + " "); - } - html.Append("
    "); - - html.Append("
      "); - foreach (var alpha in alphabet) - { - html.Append("
    • " + alpha + "
    • "); - - html.Append("
        "); - foreach (var page in pages.Where(p => p.Title.ToLower().StartsWith(alpha.ToLower()))) - { - if (showNamespace) - { - html.Append($"
      • {page.Name}"); - } - else - { - html.Append($"
      • {page.Title}"); - } - - if (styleName == "full") - { - if (page?.Description?.Length > 0) - { - html.Append(" - " + page.Description); - } - } - html.Append("
      • "); - } - html.Append("
      "); - } - - html.Append("
    "); - } - - return new HandlerResult(html.ToString()); + Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] + }; } - //------------------------------------------------------------------------------------------------------------------------------ - //Creates a list of pages by searching the page body for the specified text. - case "searchlist": + throw new Exception($"T未找到包含页面: [{navigation}]"); + } + case "set": + { + var key = function.Parameters.Get("key"); + var value = function.Parameters.Get("value"); + + if (state.Variables.ContainsKey(key)) { - string styleName = function.Parameters.Get("styleName").ToLower(); - string refTag = state.GetNextQueryToken(); - int pageNumber = int.Parse(state.QueryString[refTag].ToString().DefaultWhenNullOrEmpty("1")); - var pageSize = function.Parameters.Get("pageSize"); - var pageSelector = function.Parameters.Get("pageSelector"); - var allowFuzzyMatching = function.Parameters.Get("allowFuzzyMatching"); - var searchTokens = function.Parameters.Get("searchPhrase").Split(" ", StringSplitOptions.RemoveEmptyEntries).ToList(); - var showNamespace = function.Parameters.Get("showNamespace"); - - var pages = PageRepository.PageSearchPaged(searchTokens, pageNumber, pageSize, allowFuzzyMatching); - var html = new StringBuilder(); - - if (pages.Count() > 0) - { - html.Append("
      "); - - foreach (var page in pages) - { - if (showNamespace) - { - html.Append($"
    • {page.Name}"); - } - else - { - html.Append($"
    • {page.Title}"); - } - - if (styleName == "full") - { - if (page?.Description?.Length > 0) - { - html.Append(" - " + page.Description); - } - } - - html.Append("
    • "); - } - - html.Append("
    "); - } - - if (pageSelector && (pageNumber > 1 || (pages.Count > 0 && pages.First().PaginationPageCount > 1))) - { - html.Append(PageSelectorGenerator.Generate(refTag, state.QueryString, pages.FirstOrDefault()?.PaginationPageCount ?? 1)); - } - - return new HandlerResult(html.ToString()); + state.Variables[key] = value; + } + else + { + state.Variables.Add(key, value); } - //------------------------------------------------------------------------------------------------------------------------------ - //Creates a list of pages by searching the page tags. - case "taglist": + return new HandlerResult(string.Empty) { - string styleName = function.Parameters.Get("styleName").ToLower(); - var topCount = function.Parameters.Get("top"); - var tags = function.Parameters.GetList("pageTags"); - var showNamespace = function.Parameters.Get("showNamespace"); + Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] + }; + } + case "get": + { + var key = function.Parameters.Get("key"); - var pages = PageRepository.GetPageInfoByTags(tags).Take(topCount).OrderBy(o => o.Name).ToList(); - var html = new StringBuilder(); - - if (pages.Count() > 0) - { - html.Append("
      "); - - foreach (var page in pages) - { - if (showNamespace) - { - html.Append($"
    • {page.Name}"); - } - else - { - html.Append($"
    • {page.Title}"); - } - - if (styleName == "full") - { - if (page?.Description?.Length > 0) - { - html.Append(" - " + page.Description); - } - } - - html.Append("
    • "); - } - - html.Append("
    "); - } - - return new HandlerResult(html.ToString()); + if (state.Variables.TryGetValue(key, out var variable)) + { + return new HandlerResult(variable); } - //------------------------------------------------------------------------------------------------------------------------------ - //Displays a list of other related pages based on tags. - case "similar": //##Similar() + throw new Exception( + $"未定义变量 {key} .需要在调用Get()前设置##Set()."); + } + case "color": + { + var color = function.Parameters.Get("color"); + var text = function.Parameters.Get("text"); + + return new HandlerResult($"{text}"); + } + case "tag": //##tag(pipe|separated|list|of|tags) + { + var tags = function.Parameters.GetList("pageTags"); + state.Tags.AddRange(tags); + state.Tags = state.Tags.Distinct().ToList(); + + return new HandlerResult(string.Empty) { - string refTag = state.GetNextQueryToken(); + Instructions = [Constants.HandlerResultInstruction.TruncateTrailingLine] + }; + } + case "image": //##Image(Name, [optional:default=100]Scale, [optional:default=""]Alt-Text) + { + var imageName = function.Parameters.Get("name"); + var alt = function.Parameters.Get("alttext", imageName); + var scale = function.Parameters.Get("scale"); - var similarity = function.Parameters.Get("similarity"); - int pageNumber = int.Parse(state.QueryString[refTag].ToString().DefaultWhenNullOrEmpty("1")); - var pageSize = function.Parameters.Get("pageSize"); - var pageSelector = function.Parameters.Get("pageSelector"); - string styleName = function.Parameters.Get("styleName").ToLower(); - var html = new StringBuilder(); + var explicitNamespace = imageName.Contains("::"); + var isPageForeignImage = false; - var pages = PageRepository.GetSimilarPagesPaged(state.Page.Id, similarity, pageNumber, pageSize); - - if (styleName == "list") - { - html.Append("
      "); - foreach (var page in pages) - { - html.Append($"
    • {page.Title}"); - } - html.Append("
    "); - } - else if (styleName == "flat") - { - foreach (var page in pages) - { - if (html.Length > 0) html.Append(" | "); - html.Append($"{page.Title}"); - } - } - else if (styleName == "full") - { - html.Append("
      "); - foreach (var page in pages) - { - html.Append($"
    • {page.Title} - {page.Description}"); - } - html.Append("
    "); - } - - if (pageSelector && pages.Count > 0 && pages.First().PaginationPageCount > 1) - { - html.Append(PageSelectorGenerator.Generate(refTag, state.QueryString, pages.First().PaginationPageCount)); - } - - return new HandlerResult(html.ToString()); + var navigation = state.Page.Navigation; + if (imageName.StartsWith("http", StringComparison.InvariantCultureIgnoreCase)) + { + string image = + $"\"{alt}\""; + return new HandlerResult(image); + } + else if (imageName.Contains('/')) + { + //允许从其他页面加载附加图像. + var slashIndex = imageName.IndexOf("/"); + navigation = NamespaceNavigation.CleanAndValidate(imageName.Substring(0, slashIndex)); + imageName = imageName.Substring(slashIndex + 1); + isPageForeignImage = true; } - //------------------------------------------------------------------------------------------------------------------------------ - //Displays a list of other related pages based incoming links. - case "related": //##related + if (explicitNamespace == false && state.Page.Namespace != null) { - string refTag = state.GetNextQueryToken(); - - int pageNumber = int.Parse(state.QueryString[refTag].ToString().DefaultWhenNullOrEmpty("1")); - var pageSize = function.Parameters.Get("pageSize"); - var pageSelector = function.Parameters.Get("pageSelector"); - string styleName = function.Parameters.Get("styleName").ToLower(); - var html = new StringBuilder(); - - var pages = PageRepository.GetRelatedPagesPaged(state.Page.Id, pageNumber, pageSize); - - if (styleName == "list") + if (PageFileRepository.GetPageFileAttachmentInfoByPageNavigationPageRevisionAndFileNavigation( + navigation, NamespaceNavigation.CleanAndValidate(imageName), state.Revision) == null) { - html.Append("
      "); - foreach (var page in pages) - { - html.Append($"
    • {page.Title}"); - } - html.Append("
    "); + //如果图像不存在,也没有指定命名空间,但页面有命名空间,则默认为页面命名空间. + navigation = NamespaceNavigation.CleanAndValidate($"{state.Page.Namespace}::{imageName}"); } - else if (styleName == "flat") - { - foreach (var page in pages) - { - if (html.Length > 0) html.Append(" | "); - html.Append($"{page.Title}"); - } - } - else if (styleName == "full") - { - html.Append("
      "); - foreach (var page in pages) - { - html.Append($"
    • {page.Title} - {page.Description}"); - } - html.Append("
    "); - } - - if (pageSelector && pages.Count > 0 && pages.First().PaginationPageCount > 1) - { - html.Append(PageSelectorGenerator.Generate(refTag, state.QueryString, pages.First().PaginationPageCount)); - } - - return new HandlerResult(html.ToString()); } - //------------------------------------------------------------------------------------------------------------------------------ - //Displays the date and time that the current page was last modified. - case "lastmodified": + if (state.Revision != null && isPageForeignImage == false) { - if (state.Session == null) + var link = + $"/Page/Image/{navigation}/{NamespaceNavigation.CleanAndValidate(imageName)}/{state.Revision}"; + var image = + $"\"{alt}\""; + return new HandlerResult(image); + } + else + { + var link = $"/Page/Image/{navigation}/{NamespaceNavigation.CleanAndValidate(imageName)}"; + var image = + $"\"{alt}\""; + return new HandlerResult(image); + } + } + + //显示文件下载链接 + case "file": //##file(Name | Alt-Text | [optional display file size] true/false) + { + var fileName = function.Parameters.Get("name"); + + var explicitNamespace = fileName.Contains("::"); + var isPageForeignFile = false; + + var navigation = state.Page.Navigation; + if (fileName.Contains('/')) + { + //允许从其他页面加载附加图像。 + var slashIndex = fileName.IndexOf("/"); + navigation = NamespaceNavigation.CleanAndValidate(fileName.Substring(0, slashIndex)); + fileName = fileName.Substring(slashIndex + 1); + isPageForeignFile = true; + } + + if (explicitNamespace == false && state.Page.Namespace != null) + { + if (PageFileRepository.GetPageFileAttachmentInfoByPageNavigationPageRevisionAndFileNavigation( + navigation, NamespaceNavigation.CleanAndValidate(fileName), state.Revision) == null) { - throw new Exception($"Localization is not supported without SessionState."); + navigation = NamespaceNavigation.CleanAndValidate($"{state.Page.Namespace}::{fileName}"); + } + } + + var attachment = + PageFileRepository.GetPageFileAttachmentInfoByPageNavigationPageRevisionAndFileNavigation( + navigation, NamespaceNavigation.CleanAndValidate(fileName), state.Revision); + if (attachment != null) + { + var alt = function.Parameters.Get("linkText", fileName); + + if (function.Parameters.Get("showSize")) + { + alt += $" ({attachment.FriendlySize})"; } - if (state.Page.ModifiedDate != DateTime.MinValue) + if (state.Revision != null && isPageForeignFile == false) { - var localized = state.Session.LocalizeDateTime(state.Page.ModifiedDate); - return new HandlerResult($"{localized.ToShortDateString()} {localized.ToShortTimeString()}"); - } - - return new HandlerResult(string.Empty); - } - - //------------------------------------------------------------------------------------------------------------------------------ - //Displays the date and time that the current page was created. - case "created": - { - if (state.Session == null) - { - throw new Exception($"Localization is not supported without SessionState."); - } - - if (state.Page.CreatedDate != DateTime.MinValue) - { - var localized = state.Session.LocalizeDateTime(state.Page.CreatedDate); - return new HandlerResult($"{localized.ToShortDateString()} {localized.ToShortTimeString()}"); - } - - return new HandlerResult(string.Empty); - } - - //------------------------------------------------------------------------------------------------------------------------------ - //Displays the version of the wiki. - case "appversion": - { - var version = string.Join('.', (Assembly.GetExecutingAssembly() - .GetName().Version?.ToString() ?? "0.0.0.0").Split('.').Take(3)); //Major.Minor.Patch - - return new HandlerResult(version); - } - - //------------------------------------------------------------------------------------------------------------------------------ - //Displays the title of the current page. - case "name": - { - return new HandlerResult(state.Page.Title); - } - - //------------------------------------------------------------------------------------------------------------------------------ - //Displays the title of the site. - case "sitename": - { - return new HandlerResult(GlobalConfiguration.Name); - } - - //------------------------------------------------------------------------------------------------------------------------------ - //Displays the title of the current page in title form. - case "title": - { - return new HandlerResult($"

    {state.Page.Title}

    "); - } - - //------------------------------------------------------------------------------------------------------------------------------ - //Displays the namespace of the current page. - case "namespace": - { - return new HandlerResult(state.Page.Namespace ?? string.Empty); - } - - //------------------------------------------------------------------------------------------------------------------------------ - //Displays the namespace of the current page. - case "snippet": - { - string name = function.Parameters.Get("name"); - - if (state.Snippets.ContainsKey(name)) - { - return new HandlerResult(state.Snippets[name]); + var link = + $"/Page/Binary/{navigation}/{NamespaceNavigation.CleanAndValidate(fileName)}/{state.Revision}"; + var image = $"{alt}"; + return new HandlerResult(image); } else { - return new HandlerResult(string.Empty); + var link = $"/Page/Binary/{navigation}/{NamespaceNavigation.CleanAndValidate(fileName)}"; + var image = $"{alt}"; + return new HandlerResult(image); } } - //------------------------------------------------------------------------------------------------------------------------------ - //Inserts empty lines into the page. + throw new Exception($"File not found [{fileName}]"); + } + case "recentlymodified": //##RecentlyModified(TopCount) + { + var styleName = function.Parameters.Get("styleName").ToLower(); + var takeCount = function.Parameters.Get("top"); + var showNamespace = function.Parameters.Get("showNamespace"); + + var pages = PageRepository.GetTopRecentlyModifiedPagesInfo(takeCount) + .OrderByDescending(o => o.ModifiedDate).ThenBy(o => o.Title).ToList(); + + var html = new StringBuilder(); + + if (pages.Any()) + { + html.Append("
      "); + foreach (var page in pages) + { + if (showNamespace) + { + html.Append( + $"
    • {page.Name}"); + } + else + { + html.Append( + $"
    • {page.Title}"); + } + + if (styleName == "full") + { + if (page?.Description?.Length > 0) + { + html.Append(" - " + page.Description); + } + } + + html.Append("
    • "); + } + + html.Append("
    "); + } + + return new HandlerResult(html.ToString()); + } + case "namespaceglossary": + { + var glossaryName = "glossary_" + new Random().Next(0, 1000000).ToString(); + var namespaces = function.Parameters.GetList("namespaces"); + + var styleName = function.Parameters.Get("styleName").ToLower(); + var topCount = function.Parameters.Get("top"); + var pages = PageRepository.GetPageInfoByNamespaces(namespaces).Take(topCount).OrderBy(o => o.Name) + .ToList(); + var html = new StringBuilder(); + var alphabet = pages.Select(p => p.Title.Substring(0, 1).ToUpper()).Distinct(); + var showNamespace = function.Parameters.Get("showNamespace"); + + if (pages.Any()) + { + html.Append("
    "); + foreach (var alpha in alphabet) + { + html.Append("" + alpha + " "); + } + + html.Append("
    "); + + html.Append("
      "); + foreach (var alpha in alphabet) + { + html.Append("
    • " + alpha + "
    • "); + + html.Append("
        "); + foreach (var page in pages.Where(p => p.Title.ToLower().StartsWith(alpha.ToLower()))) + { + if (showNamespace) + { + html.Append( + $"
      • {page.Name}"); + } + else + { + html.Append( + $"
      • {page.Title}"); + } + + if (styleName == "full") + { + if (page?.Description?.Length > 0) + { + html.Append(" - " + page.Description); + } + } + + html.Append("
      • "); + } + + html.Append("
      "); + } + + html.Append("
    "); + } + + return new HandlerResult(html.ToString()); + } + case "namespacelist": + { + var styleName = function.Parameters.Get("styleName").ToLower(); + var topCount = function.Parameters.Get("top"); + var namespaces = function.Parameters.GetList("namespaces"); + var showNamespace = function.Parameters.Get("showNamespace"); + + var pages = PageRepository.GetPageInfoByNamespaces(namespaces).Take(topCount).OrderBy(o => o.Name) + .ToList(); + var html = new StringBuilder(); + + if (pages.Any()) + { + html.Append("
      "); + + foreach (var page in pages) + { + if (showNamespace) + { + html.Append( + $"
    • {page.Name}"); + } + else + { + html.Append( + $"
    • {page.Title}"); + } + + if (styleName == "full") + { + if (page?.Description?.Length > 0) + { + html.Append(" - " + page.Description); + } + } + + html.Append("
    • "); + } + + html.Append("
    "); + } + + return new HandlerResult(html.ToString()); + } + case "tagglossary": + { + var glossaryName = "glossary_" + new Random().Next(0, 1000000).ToString(); + var tags = function.Parameters.GetList("pageTags"); + + var styleName = function.Parameters.Get("styleName").ToLower(); + var topCount = function.Parameters.Get("top"); + var pages = PageRepository.GetPageInfoByTags(tags).Take(topCount).OrderBy(o => o.Name).ToList(); + var html = new StringBuilder(); + var alphabet = pages.Select(p => p.Title.Substring(0, 1).ToUpper()).Distinct(); + var showNamespace = function.Parameters.Get("showNamespace"); + + if (pages.Any()) + { + html.Append("
    "); + foreach (var alpha in alphabet) + { + html.Append("" + alpha + " "); + } + + html.Append("
    "); + + html.Append("
      "); + foreach (var alpha in alphabet) + { + html.Append("
    • " + alpha + "
    • "); + + html.Append("
        "); + foreach (var page in pages.Where(p => p.Title.ToLower().StartsWith(alpha.ToLower()))) + { + if (showNamespace) + { + html.Append( + $"
      • {page.Name}"); + } + else + { + html.Append( + $"
      • {page.Title}"); + } + + if (styleName == "full") + { + if (page?.Description?.Length > 0) + { + html.Append(" - " + page.Description); + } + } + + html.Append("
      • "); + } + + html.Append("
      "); + } + + html.Append("
    "); + } + + return new HandlerResult(html.ToString()); + } + case "textglossary": + { + var glossaryName = "glossary_" + new Random().Next(0, 1000000).ToString(); + var searchTokens = function.Parameters.Get("searchPhrase") + .Split(" ", StringSplitOptions.RemoveEmptyEntries).ToList(); + var topCount = function.Parameters.Get("top"); + var showNamespace = function.Parameters.Get("showNamespace"); + + var pages = PageRepository.PageSearch(searchTokens).Take(topCount).OrderBy(o => o.Name).ToList(); + var html = new StringBuilder(); + var alphabet = pages.Select(p => p.Title.Substring(0, 1).ToUpper()).Distinct(); + var styleName = function.Parameters.Get("styleName").ToLower(); + + if (pages.Count() > 0) + { + html.Append("
    "); + foreach (var alpha in alphabet) + { + html.Append("" + alpha + " "); + } + + html.Append("
    "); + + html.Append("
      "); + foreach (var alpha in alphabet) + { + html.Append("
    • " + alpha + "
    • "); + + html.Append("
        "); + foreach (var page in pages.Where(p => p.Title.ToLower().StartsWith(alpha.ToLower()))) + { + if (showNamespace) + { + html.Append( + $"
      • {page.Name}"); + } + else + { + html.Append( + $"
      • {page.Title}"); + } + + if (styleName == "full") + { + if (page?.Description?.Length > 0) + { + html.Append(" - " + page.Description); + } + } + + html.Append("
      • "); + } + + html.Append("
      "); + } + + html.Append("
    "); + } + + return new HandlerResult(html.ToString()); + } + case "searchlist": + { + var styleName = function.Parameters.Get("styleName").ToLower(); + var refTag = state.GetNextQueryToken(); + var pageNumber = int.Parse(state.QueryString[refTag].ToString().DefaultWhenNullOrEmpty("1")); + var pageSize = function.Parameters.Get("pageSize"); + var pageSelector = function.Parameters.Get("pageSelector"); + var allowFuzzyMatching = function.Parameters.Get("allowFuzzyMatching"); + var searchTokens = function.Parameters.Get("searchPhrase") + .Split(" ", StringSplitOptions.RemoveEmptyEntries).ToList(); + var showNamespace = function.Parameters.Get("showNamespace"); + + var pages = PageRepository.PageSearchPaged(searchTokens, pageNumber, pageSize, allowFuzzyMatching); + var html = new StringBuilder(); + + if (pages.Any()) + { + html.Append("
      "); + + foreach (var page in pages) + { + if (showNamespace) + { + html.Append( + $"
    • {page.Name}"); + } + else + { + html.Append( + $"
    • {page.Title}"); + } + + if (styleName == "full") + { + if (page?.Description?.Length > 0) + { + html.Append(" - " + page.Description); + } + } + + html.Append("
    • "); + } + + html.Append("
    "); + } + + if (pageSelector && (pageNumber > 1 || (pages.Count > 0 && pages.First().PaginationPageCount > 1))) + { + html.Append(PageSelectorGenerator.Generate(refTag, state.QueryString, + pages.FirstOrDefault()?.PaginationPageCount ?? 1)); + } + + return new HandlerResult(html.ToString()); + } + case "taglist": + { + var styleName = function.Parameters.Get("styleName").ToLower(); + var topCount = function.Parameters.Get("top"); + var tags = function.Parameters.GetList("pageTags"); + var showNamespace = function.Parameters.Get("showNamespace"); + + var pages = PageRepository.GetPageInfoByTags(tags).Take(topCount).OrderBy(o => o.Name).ToList(); + var html = new StringBuilder(); + + if (pages.Any()) + { + html.Append("
      "); + + foreach (var page in pages) + { + if (showNamespace) + { + html.Append( + $"
    • {page.Name}"); + } + else + { + html.Append( + $"
    • {page.Title}"); + } + + if (styleName == "full") + { + if (page?.Description?.Length > 0) + { + html.Append(" - " + page.Description); + } + } + + html.Append("
    • "); + } + + html.Append("
    "); + } + + return new HandlerResult(html.ToString()); + } + case "similar": //##Similar() + { + var refTag = state.GetNextQueryToken(); + + var similarity = function.Parameters.Get("similarity"); + var pageNumber = int.Parse(state.QueryString[refTag].ToString().DefaultWhenNullOrEmpty("1")); + var pageSize = function.Parameters.Get("pageSize"); + var pageSelector = function.Parameters.Get("pageSelector"); + var styleName = function.Parameters.Get("styleName").ToLower(); + var html = new StringBuilder(); + + var pages = PageRepository.GetSimilarPagesPaged(state.Page.Id, similarity, pageNumber, pageSize); + + if (styleName == "list") + { + html.Append("
      "); + foreach (var page in pages) + { + html.Append( + $"
    • {page.Title}"); + } + + html.Append("
    "); + } + else if (styleName == "flat") + { + foreach (var page in pages) + { + if (html.Length > 0) html.Append(" | "); + html.Append( + $"{page.Title}"); + } + } + else if (styleName == "full") + { + html.Append("
      "); + foreach (var page in pages) + { + html.Append( + $"
    • {page.Title} - {page.Description}"); + } + + html.Append("
    "); + } + + if (pageSelector && pages.Count > 0 && pages.First().PaginationPageCount > 1) + { + html.Append(PageSelectorGenerator.Generate(refTag, state.QueryString, + pages.First().PaginationPageCount)); + } + + return new HandlerResult(html.ToString()); + } + case "related": //##related + { + var refTag = state.GetNextQueryToken(); + + var pageNumber = int.Parse(state.QueryString[refTag].ToString().DefaultWhenNullOrEmpty("1")); + var pageSize = function.Parameters.Get("pageSize"); + var pageSelector = function.Parameters.Get("pageSelector"); + var styleName = function.Parameters.Get("styleName").ToLower(); + var html = new StringBuilder(); + + var pages = PageRepository.GetRelatedPagesPaged(state.Page.Id, pageNumber, pageSize); + + if (styleName == "list") + { + html.Append("
      "); + foreach (var page in pages) + { + html.Append( + $"
    • {page.Title}"); + } + + html.Append("
    "); + } + else if (styleName == "flat") + { + foreach (var page in pages) + { + if (html.Length > 0) html.Append(" | "); + html.Append( + $"{page.Title}"); + } + } + else if (styleName == "full") + { + html.Append("
      "); + foreach (var page in pages) + { + html.Append( + $"
    • {page.Title} - {page.Description}"); + } + + html.Append("
    "); + } + + if (pageSelector && pages.Count > 0 && pages.First().PaginationPageCount > 1) + { + html.Append(PageSelectorGenerator.Generate(refTag, state.QueryString, + pages.First().PaginationPageCount)); + } + + return new HandlerResult(html.ToString()); + } + case "lastmodified": + { + if (state.Session == null) + { + throw new Exception($"Session异常."); + } + + if (state.Page.ModifiedDate != DateTime.MinValue) + { + var localized = state.Session.LocalizeDateTime(state.Page.ModifiedDate); + return new HandlerResult($"{localized.ToShortDateString()} {localized.ToShortTimeString()}"); + } + + return new HandlerResult(string.Empty); + } + case "created": + { + if (state.Session == null) + { + throw new Exception($"Session异常."); + } + + if (state.Page.CreatedDate != DateTime.MinValue) + { + var localized = state.Session.LocalizeDateTime(state.Page.CreatedDate); + return new HandlerResult($"{localized.ToShortDateString()} {localized.ToShortTimeString()}"); + } + + return new HandlerResult(string.Empty); + } + case "appversion": + { + var version = string.Join('.', (Assembly.GetExecutingAssembly() + .GetName().Version?.ToString() ?? "0.0.0.0").Split('.').Take(3)); //Major.Minor.Patch + + return new HandlerResult(version); + } + case "name": + { + return new HandlerResult(state.Page.Title); + } + case "sitename": + { + return new HandlerResult(GlobalConfiguration.Name); + } + case "title": + { + return new HandlerResult($"

    {state.Page.Title}

    "); + } + case "namespace": + { + return new HandlerResult(state.Page.Namespace ?? string.Empty); + } + case "snippet": + { + var name = function.Parameters.Get("name"); + + return state.Snippets.ContainsKey(name) ? new HandlerResult(state.Snippets[name]) : new HandlerResult(string.Empty); + } + case "br": case "nl": case "newline": //##NewLine([optional:default=1]count) + { + var lineBreaks = new StringBuilder(); + var count = function.Parameters.Get("Count"); + for (var i = 0; i < count; i++) { - var lineBreaks = new StringBuilder(); - int count = function.Parameters.Get("Count"); - for (int i = 0; i < count; i++) - { - lineBreaks.Append("
    "); - } - return new HandlerResult(lineBreaks.ToString()); + lineBreaks.Append("
    "); } - //Inserts a horizontal rule + return new HandlerResult(lineBreaks.ToString()); + } case "hr": - { - int size = function.Parameters.Get("height"); - return new HandlerResult($"
    "); - } - - //------------------------------------------------------------------------------------------------------------------------------ - //Displays the navigation text for the current page. + { + var size = function.Parameters.Get("height"); + return new HandlerResult($"
    "); + } + case "navigation": - { - return new HandlerResult(state.Page.Navigation); - } - - //------------------------------------------------------------------------------------------------------------------------------ + { + return new HandlerResult(state.Page.Navigation); + } case "systememojilist": + { + StringBuilder html = new(); + + html.Append($""); + + html.Append($""); + html.Append($""); + html.Append($""); + html.Append($""); + html.Append($""); + html.Append($""); + html.Append($""); + + var category = state.QueryString["Category"].ToString(); + + html.Append($""); + + if (string.IsNullOrWhiteSpace(category) == false) { - StringBuilder html = new(); + var emojis = EmojiRepository.GetEmojisByCategory(category); - html.Append($"
    NameImageShortcut
    "); - - html.Append($""); - html.Append($""); - html.Append($""); - html.Append($""); - html.Append($""); - html.Append($""); - html.Append($""); - - string category = state.QueryString["Category"].ToString(); - - html.Append($""); - - if (string.IsNullOrWhiteSpace(category) == false) + foreach (var emoji in emojis) { - var emojis = EmojiRepository.GetEmojisByCategory(category); - - foreach (var emoji in emojis) - { - html.Append($""); - html.Append($""); - //html.Append($""); - html.Append($""); - html.Append($""); - html.Append($""); - } - } - - html.Append($""); - html.Append($"
    NameImageShortcut
    {emoji.Name}{emoji.Shortcut}
    "); - - return new HandlerResult(html.ToString()) - { - Instructions = [Constants.HandlerResultInstruction.DisallowNestedProcessing] - }; - } - - //------------------------------------------------------------------------------------------------------------------------------ - case "systememojicategorylist": - { - var categories = EmojiRepository.GetEmojiCategoriesGrouped(); - - StringBuilder html = new(); - - html.Append($""); - - int rowNumber = 0; - - html.Append($""); - html.Append($""); - html.Append($""); - html.Append($""); - html.Append($""); - html.Append($""); - - foreach (var category in categories) - { - if (rowNumber == 1) - { - html.Append($""); - } - html.Append($""); - html.Append($""); - html.Append($""); + html.Append($""); + //html.Append($""); + html.Append( + $""); + html.Append($""); html.Append($""); - rowNumber++; + } + } + + html.Append($""); + html.Append($"
    NameCount of Emojis
    {category.Category}{category.EmojiCount:N0}{emoji.Name}{emoji.Shortcut}
    "); + + return new HandlerResult(html.ToString()) + { + Instructions = [Constants.HandlerResultInstruction.DisallowNestedProcessing] + }; + } + case "systememojicategorylist": + { + var categories = EmojiRepository.GetEmojiCategoriesGrouped(); + + StringBuilder html = new(); + + html.Append($""); + + var rowNumber = 0; + + html.Append($""); + html.Append($""); + html.Append($""); + html.Append($""); + html.Append($""); + html.Append($""); + + foreach (var category in categories) + { + if (rowNumber == 1) + { + html.Append($""); } - html.Append($""); - html.Append($"
    NameCount of Emojis
    "); - - return new HandlerResult(html.ToString()) - { - Instructions = [Constants.HandlerResultInstruction.DisallowNestedProcessing] - }; + html.Append($""); + html.Append( + $"{category.Category}"); + html.Append($"{category.EmojiCount:N0}"); + html.Append($""); + rowNumber++; } + + html.Append($""); + html.Append($""); + + return new HandlerResult(html.ToString()) + { + Instructions = [Constants.HandlerResultInstruction.DisallowNestedProcessing] + }; + } } return new HandlerResult() { Instructions = [Constants.HandlerResultInstruction.Skip] }; } } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Implementation/Utility/BGFGStyle.cs b/ZelWiki.Engine.Implementation/Utility/BGFGStyle.cs index ef6e76a..c3d1c14 100644 --- a/ZelWiki.Engine.Implementation/Utility/BGFGStyle.cs +++ b/ZelWiki.Engine.Implementation/Utility/BGFGStyle.cs @@ -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 ForegroundStyles = new(StringComparer.OrdinalIgnoreCase) { @@ -67,4 +70,4 @@ return new BGFGStyle(); } } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Implementation/Utility/Differentiator.cs b/ZelWiki.Engine.Implementation/Utility/Differentiator.cs index 315aeb2..df096be 100644 --- a/ZelWiki.Engine.Implementation/Utility/Differentiator.cs +++ b/ZelWiki.Engine.Implementation/Utility/Differentiator.cs @@ -5,7 +5,7 @@ namespace ZelWiki.Engine.Implementation.Utility public static class Differentiator { /// - /// This leaves a lot to be desired. + /// /// /// /// @@ -16,35 +16,35 @@ 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(); } } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Implementation/Utility/SearchCloud.cs b/ZelWiki.Engine.Implementation/Utility/SearchCloud.cs index 937d973..768e803 100644 --- a/ZelWiki.Engine.Implementation/Utility/SearchCloud.cs +++ b/ZelWiki.Engine.Implementation/Utility/SearchCloud.cs @@ -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(); diff --git a/ZelWiki.Engine.Implementation/Utility/TagCloud.cs b/ZelWiki.Engine.Implementation/Utility/TagCloud.cs index 813a6b5..984674c 100644 --- a/ZelWiki.Engine.Implementation/Utility/TagCloud.cs +++ b/ZelWiki.Engine.Implementation/Utility/TagCloud.cs @@ -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(); diff --git a/ZelWiki.Engine.Implementation/WeightedSearchToken.cs b/ZelWiki.Engine.Implementation/WeightedSearchToken.cs index fb10858..06eeda9 100644 --- a/ZelWiki.Engine.Implementation/WeightedSearchToken.cs +++ b/ZelWiki.Engine.Implementation/WeightedSearchToken.cs @@ -2,7 +2,14 @@ { public class WeightedSearchToken { - public string Token { get; set; } = string.Empty; + /// + /// + /// + public WeightedSearchToken() + { + Token = string.Empty; + } + public string Token { get; set; } public double Weight { get; set; } } } diff --git a/ZelWiki.Engine.Library/Constants.cs b/ZelWiki.Engine.Library/Constants.cs index 48d7e0f..6e38f09 100644 --- a/ZelWiki.Engine.Library/Constants.cs +++ b/ZelWiki.Engine.Library/Constants.cs @@ -1,9 +1,12 @@ namespace ZelWiki.Engine.Library { + /// + /// + /// public class Constants { - public const string SoftBreak = ""; //These will remain as \r\n in the final HTML. - public const string HardBreak = ""; //These will remain as
    in the final HTML. + public const string SoftBreak = ""; + public const string HardBreak = ""; public enum WikiMatchType { @@ -23,22 +26,24 @@ public enum HandlerResultInstruction { /// - /// Does not process the match, allowing it to be processed by another handler. + /// 不处理匹配,允许它由另一个处理程序处理 /// Skip, + /// - /// Removes any single trailing newline after match. + /// 删除匹配后的任何单个尾随换行符 /// TruncateTrailingLine, + /// - /// Will not continue to process content in this block. + /// 将不会继续处理此块中的内容 /// DisallowNestedProcessing, + /// - /// 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. + /// /// OnlyReplaceFirstMatch } } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Library/Interfaces/ICommentHandler.cs b/ZelWiki.Engine.Library/Interfaces/ICommentHandler.cs index 8216ad2..8488f19 100644 --- a/ZelWiki.Engine.Library/Interfaces/ICommentHandler.cs +++ b/ZelWiki.Engine.Library/Interfaces/ICommentHandler.cs @@ -1,15 +1,16 @@ namespace ZelWiki.Engine.Library.Interfaces { /// - /// Handles wiki comments. These are generally removed from the result. + /// 处理评论. /// public interface ICommentHandler { /// - /// Handles a wiki comment. + /// 处理评论 /// - /// Reference to the wiki state object - /// The comment text + /// + /// + /// public HandlerResult Handle(IZelEngineState state, string text); } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Library/Interfaces/ICompletionHandler.cs b/ZelWiki.Engine.Library/Interfaces/ICompletionHandler.cs index 1f0aaf5..3aa4579 100644 --- a/ZelWiki.Engine.Library/Interfaces/ICompletionHandler.cs +++ b/ZelWiki.Engine.Library/Interfaces/ICompletionHandler.cs @@ -1,14 +1,14 @@ namespace ZelWiki.Engine.Library.Interfaces { /// - /// Handles wiki completion events. + /// 处理完成事件 /// public interface ICompletionHandler { /// - /// Handles wiki completion events. Is called when the wiki processing competes for a given page. + /// 处理完成事件 /// - /// Reference to the wiki state object + /// public void Complete(IZelEngineState state); } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Library/Interfaces/IEmojiHandler.cs b/ZelWiki.Engine.Library/Interfaces/IEmojiHandler.cs index 504d928..7a7999e 100644 --- a/ZelWiki.Engine.Library/Interfaces/IEmojiHandler.cs +++ b/ZelWiki.Engine.Library/Interfaces/IEmojiHandler.cs @@ -1,16 +1,17 @@ namespace ZelWiki.Engine.Library.Interfaces { /// - /// Handles wiki emojis. + /// emoji /// public interface IEmojiHandler { /// - /// Handles an emoji instruction. + /// /// - /// Reference to the wiki state object - /// The lookup key for the given emoji. - /// The desired 1-100 scale factor for the emoji. + /// + /// + /// + /// public HandlerResult Handle(IZelEngineState state, string key, int scale); } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Library/Interfaces/IExceptionHandler.cs b/ZelWiki.Engine.Library/Interfaces/IExceptionHandler.cs index fc815ed..48f1fde 100644 --- a/ZelWiki.Engine.Library/Interfaces/IExceptionHandler.cs +++ b/ZelWiki.Engine.Library/Interfaces/IExceptionHandler.cs @@ -1,16 +1,16 @@ namespace ZelWiki.Engine.Library.Interfaces { /// - /// Handles exceptions thrown by the wiki engine. + /// 错误日志 /// public interface IExceptionHandler { /// - /// Called when an exception is thrown by the wiki engine. + /// /// - /// Reference to the wiki state object - /// Optional exception, in the case that this was an actual exception. - /// Text that accompanies the exception. + /// + /// + /// public void Log(IZelEngineState state, Exception? ex, string customText); } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Library/Interfaces/IExternalLinkHandler.cs b/ZelWiki.Engine.Library/Interfaces/IExternalLinkHandler.cs index 65ebaf1..60dfc04 100644 --- a/ZelWiki.Engine.Library/Interfaces/IExternalLinkHandler.cs +++ b/ZelWiki.Engine.Library/Interfaces/IExternalLinkHandler.cs @@ -1,18 +1,18 @@ namespace ZelWiki.Engine.Library.Interfaces { /// - /// Handles links the wiki to another site. + /// /// public interface IExternalLinkHandler { /// - /// Handles an internal wiki link. + /// /// - /// Reference to the wiki state object - /// The address of the external site being linked to. - /// The text which should be show in the absence of an image. - /// The image that should be shown. - /// The 0-100 image scale factor for the given image. + /// + /// + /// + /// + /// public HandlerResult Handle(IZelEngineState state, string link, string? text, string? image); } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Library/Interfaces/IFunctionHandler.cs b/ZelWiki.Engine.Library/Interfaces/IFunctionHandler.cs index a94ae52..1180cf6 100644 --- a/ZelWiki.Engine.Library/Interfaces/IFunctionHandler.cs +++ b/ZelWiki.Engine.Library/Interfaces/IFunctionHandler.cs @@ -3,22 +3,23 @@ namespace ZelWiki.Engine.Library.Interfaces { /// - /// Base function handler for standard, post-processing, scoped and processing-instruction functions. + /// 处理函数. /// public interface IFunctionHandler { /// - /// Returns a collection of function prototypes. + /// 回调. /// /// public FunctionPrototypeCollection Prototypes { get; } /// - /// Called to handle function calls when proper prototypes are matched. + /// 当匹配到合适的原型时,调用以处理函数调用 /// - /// Reference to the wiki state object - /// The parsed function call and all its parameters and their values. - /// For scope functions, this is the text that the function is designed to affect. + /// + /// + /// + /// public HandlerResult Handle(IZelEngineState state, FunctionCall function, string? scopeBody = null); } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Library/Interfaces/IHeadingHandler.cs b/ZelWiki.Engine.Library/Interfaces/IHeadingHandler.cs index 207d32c..0f31dab 100644 --- a/ZelWiki.Engine.Library/Interfaces/IHeadingHandler.cs +++ b/ZelWiki.Engine.Library/Interfaces/IHeadingHandler.cs @@ -1,17 +1,18 @@ namespace ZelWiki.Engine.Library.Interfaces { /// - /// Handles wiki headings. These are automatically added to the table of contents. + /// 处理标题 /// public interface IHeadingHandler { /// - /// Handles wiki headings. These are automatically added to the table of contents. + /// /// - /// Reference to the wiki state object - /// The size of the header, also used for table of table of contents indentation. - /// The self link reference. - /// The text for the self link. + /// + /// + /// + /// + /// public HandlerResult Handle(IZelEngineState state, int depth, string link, string text); } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Library/Interfaces/IInternalLinkHandler.cs b/ZelWiki.Engine.Library/Interfaces/IInternalLinkHandler.cs index a42132c..24d16d4 100644 --- a/ZelWiki.Engine.Library/Interfaces/IInternalLinkHandler.cs +++ b/ZelWiki.Engine.Library/Interfaces/IInternalLinkHandler.cs @@ -3,19 +3,21 @@ namespace ZelWiki.Engine.Library.Interfaces { /// - /// Handles links from one wiki page to another. + /// 处理内链 /// public interface IInternalLinkHandler { /// - /// Handles an internal wiki link. + /// /// - /// Reference to the wiki state object - /// The navigation for the linked page. - /// The name of the page being linked to. - /// The text which should be show in the absence of an image. - /// The image that should be shown. - /// The 0-100 image scale factor for the given image. - public HandlerResult Handle(IZelEngineState state, NamespaceNavigation pageNavigation, string pageName, string linkText, string? image, int imageScale); + /// + /// + /// + /// + /// + /// + /// + public HandlerResult Handle(IZelEngineState state, NamespaceNavigation pageNavigation, string pageName, + string linkText, string? image, int imageScale); } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Library/Interfaces/IMarkupHandler.cs b/ZelWiki.Engine.Library/Interfaces/IMarkupHandler.cs index d7cb194..62df737 100644 --- a/ZelWiki.Engine.Library/Interfaces/IMarkupHandler.cs +++ b/ZelWiki.Engine.Library/Interfaces/IMarkupHandler.cs @@ -1,16 +1,17 @@ namespace ZelWiki.Engine.Library.Interfaces { /// - /// Handles basic markup/style instructions like bole, italic, underline, etc. + /// 处理基本的标记/样式指令,如粗体、斜体、下划线等. /// public interface IMarkupHandler { /// - /// Handles basic markup instructions like bole, italic, underline, etc. + /// /// - /// Reference to the wiki state object - /// The sequence of symbols that were found to denotate this markup instruction, - /// The body of text to apply the style to. + /// + /// + /// + /// public HandlerResult Handle(IZelEngineState state, char sequence, string scopeBody); } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Library/Interfaces/IPostProcessingFunctionHandler.cs b/ZelWiki.Engine.Library/Interfaces/IPostProcessingFunctionHandler.cs index e07eccd..d9fc098 100644 --- a/ZelWiki.Engine.Library/Interfaces/IPostProcessingFunctionHandler.cs +++ b/ZelWiki.Engine.Library/Interfaces/IPostProcessingFunctionHandler.cs @@ -1,7 +1,7 @@ namespace ZelWiki.Engine.Library.Interfaces { /// - /// Handles post-processing function calls. + /// /// public interface IPostProcessingFunctionHandler : IFunctionHandler { diff --git a/ZelWiki.Engine.Library/Interfaces/IProcessingInstructionFunctionHandler.cs b/ZelWiki.Engine.Library/Interfaces/IProcessingInstructionFunctionHandler.cs index 0f5af15..87087f1 100644 --- a/ZelWiki.Engine.Library/Interfaces/IProcessingInstructionFunctionHandler.cs +++ b/ZelWiki.Engine.Library/Interfaces/IProcessingInstructionFunctionHandler.cs @@ -1,7 +1,7 @@ namespace ZelWiki.Engine.Library.Interfaces { /// - /// Handles processing-instruction function calls. + /// /// public interface IProcessingInstructionFunctionHandler : IFunctionHandler { diff --git a/ZelWiki.Engine.Library/Interfaces/IScopeFunctionHandler.cs b/ZelWiki.Engine.Library/Interfaces/IScopeFunctionHandler.cs index 8a45ecb..06c17c7 100644 --- a/ZelWiki.Engine.Library/Interfaces/IScopeFunctionHandler.cs +++ b/ZelWiki.Engine.Library/Interfaces/IScopeFunctionHandler.cs @@ -1,7 +1,7 @@ namespace ZelWiki.Engine.Library.Interfaces { /// - /// Handles scope function calls. + /// /// public interface IScopeFunctionHandler : IFunctionHandler { diff --git a/ZelWiki.Engine.Library/Interfaces/IStandardFunctionHandler.cs b/ZelWiki.Engine.Library/Interfaces/IStandardFunctionHandler.cs index f38fe64..4f22c61 100644 --- a/ZelWiki.Engine.Library/Interfaces/IStandardFunctionHandler.cs +++ b/ZelWiki.Engine.Library/Interfaces/IStandardFunctionHandler.cs @@ -1,7 +1,7 @@ namespace ZelWiki.Engine.Library.Interfaces { /// - /// Handles standard function calls. + /// /// public interface IStandardFunctionHandler : IFunctionHandler { diff --git a/ZelWiki.Engine.Library/Interfaces/IZelEngineState.cs b/ZelWiki.Engine.Library/Interfaces/IZelEngineState.cs index b87076c..6923dbe 100644 --- a/ZelWiki.Engine.Library/Interfaces/IZelEngineState.cs +++ b/ZelWiki.Engine.Library/Interfaces/IZelEngineState.cs @@ -6,69 +6,64 @@ 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 OmitMatches { get; } - public int NestDepth { get; } //Used for recursion. + public int NestDepth { get; } #endregion - #region State. + #region 状态 - /// - /// Custom page title set by a call to @@Title("...") - /// public string? PageTitle { get; set; } - Dictionary Variables { get; } - Dictionary Snippets { get; } - List Tags { get; set; } - List ProcessingInstructions { get; } - List OutgoingLinks { get; } - List TableOfContents { get; } - List Headers { get; } + public Dictionary Variables { get; } + public Dictionary Snippets { get; } + public List Tags { get; set; } + public List ProcessingInstructions { get; } + public List OutgoingLinks { get; } + public List TableOfContents { get; } + public List 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 /// - /// Used to store values for handlers that needs to survive only a single wiki processing session. + /// /// public void SetStateValue(string key, T value); /// - /// Used to get values for handlers that needs to survive only a single wiki processing session. + /// /// public bool TryGetStateValue(string key, [MaybeNullWhen(false)] out T? outValue); /// - /// Used to get values for handlers that needs to survive only a single wiki processing session. + /// /// public T GetStateValue(string key, T defaultValue); string GetNextQueryToken(); /// - /// Transforms "included" wiki pages, for example if a wiki function - /// injected additional wiki markup, this 'could' be processed separately. + /// /// - /// The child page to process - /// The optional revision of the child page to process + /// + /// /// IZelEngineState TransformChild(IPage page, int? revision = null); } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Library/PageReference.cs b/ZelWiki.Engine.Library/PageReference.cs index 518e8e4..60ba30e 100644 --- a/ZelWiki.Engine.Library/PageReference.cs +++ b/ZelWiki.Engine.Library/PageReference.cs @@ -2,27 +2,33 @@ { public class PageReference { - /// - /// The name of the page. Such as "Sand Box" or "Some Namespace : SandBox". - /// - public string Name { get; set; } = string.Empty; - - /// - /// The namespace part of the Name. - /// - 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; } + /// + /// + /// + public string Name { get; set; } + + /// + /// + /// + public string Namespace { get; set; } + + /// + /// + /// + public string Navigation { get; set; } + + public override bool Equals(object? obj) { return obj is PageReference other - && string.Equals(Navigation, other.Navigation, StringComparison.OrdinalIgnoreCase); + && string.Equals(Navigation, other.Navigation, StringComparison.OrdinalIgnoreCase); } public override int GetHashCode() @@ -30,6 +36,12 @@ return Navigation.GetHashCode(); } + /// + /// + /// + /// + /// + /// public PageReference(string name, string navigation) { var parts = name.Split("::"); @@ -45,10 +57,10 @@ } else { - throw new Exception($"Invalid page name {name}"); + throw new Exception($"页面名称无效: {name}"); } Navigation = navigation; } } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine.Library/TableOfContentsTag.cs b/ZelWiki.Engine.Library/TableOfContentsTag.cs index 2f75e28..acea97a 100644 --- a/ZelWiki.Engine.Library/TableOfContentsTag.cs +++ b/ZelWiki.Engine.Library/TableOfContentsTag.cs @@ -1,7 +1,7 @@ namespace ZelWiki.Engine.Library { /// - /// Table of contents tag. + /// /// public class TableOfContentsTag { @@ -10,6 +10,13 @@ public string Text; public int StartingPosition; + /// + /// + /// + /// + /// + /// + /// public TableOfContentsTag(int level, int startingPosition, string hrefTag, string text) { Level = level; diff --git a/ZelWiki.Engine/WikiMatchSet.cs b/ZelWiki.Engine/WikiMatchSet.cs index 6526c6e..d46f613 100644 --- a/ZelWiki.Engine/WikiMatchSet.cs +++ b/ZelWiki.Engine/WikiMatchSet.cs @@ -5,18 +5,17 @@ namespace ZelWiki.Engine public class WikiMatchSet { /// - /// The type of match that was found. + /// 找到的匹配类型. /// public Constants.WikiMatchType MatchType { get; set; } /// - /// The resulting content of the wiki processing. + /// 内容. /// public string Content { get; set; } = string.Empty; /// - /// 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. + /// /// public bool AllowNestedDecode { get; set; } } diff --git a/ZelWiki.Engine/WikiOrderedMatch.cs b/ZelWiki.Engine/WikiOrderedMatch.cs index 363d717..79088d8 100644 --- a/ZelWiki.Engine/WikiOrderedMatch.cs +++ b/ZelWiki.Engine/WikiOrderedMatch.cs @@ -2,7 +2,17 @@ { public class WikiOrderedMatch { - public string Value { get; set; } = string.Empty; + public WikiOrderedMatch() + { + Value = string.Empty; + } + /// + /// + /// + public string Value { get; set; } + /// + /// + /// public int Index { get; set; } } } diff --git a/ZelWiki.Engine/WikiPrecompiledRegex.cs b/ZelWiki.Engine/WikiPrecompiledRegex.cs index 169ed52..25c9cee 100644 --- a/ZelWiki.Engine/WikiPrecompiledRegex.cs +++ b/ZelWiki.Engine/WikiPrecompiledRegex.cs @@ -2,6 +2,9 @@ namespace ZelWiki.Engine { + /// + /// 一些正则 + /// internal static partial class PrecompiledRegex { [GeneratedRegex("\\#\\{([\\S\\s]*?)\\}\\#", RegexOptions.IgnoreCase)] diff --git a/ZelWiki.Engine/WikiUtility.cs b/ZelWiki.Engine/WikiUtility.cs index ba5f4b1..9452f04 100644 --- a/ZelWiki.Engine/WikiUtility.cs +++ b/ZelWiki.Engine/WikiUtility.cs @@ -6,7 +6,7 @@ namespace ZelWiki.Engine internal static class WikiUtility { /// - /// Skips the namespace and returns just the page name part of the navigation. + /// 跳过命名空间,只返回导航的页面名称部分. /// /// /// @@ -50,7 +50,7 @@ namespace ZelWiki.Engine } /// - /// Gets a list of symbols where the symbol occurs consecutively, more than once. (e.g. "##This##") + /// 获取符号连续出现多次的符号列表. ("##This##") /// /// /// diff --git a/ZelWiki.Engine/WikifierLite.cs b/ZelWiki.Engine/WikifierLite.cs index 2ad47c5..82ff157 100644 --- a/ZelWiki.Engine/WikifierLite.cs +++ b/ZelWiki.Engine/WikifierLite.cs @@ -5,7 +5,7 @@ using ZelWiki.Models; namespace ZelWiki.Engine { /// - /// Tiny wikifier (reduced feature-set) for things like comments and profile bios. + /// /// 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(); var content = new WikiString(unprocessedText); @@ -75,7 +75,8 @@ namespace ZelWiki.Engine { if (scale != 100 && scale > 0 && scale <= 500) { - var emojiImage = $"\"{emoji?.Name}\""; + var emojiImage = + $"\"{emoji?.Name}\""; pageContent.Replace(match.Value, StoreMatch(matchStore, emojiImage)); } else @@ -92,7 +93,7 @@ namespace ZelWiki.Engine } /// - /// Transform basic markup such as bold, italics, underline, etc. for single and multi-line. + /// 转换单行和多行的基本标记,如粗体、斜体、下划线等. /// /// private static void TransformMarkup(WikiString pageContent, Dictionary matchStore) @@ -126,12 +127,12 @@ namespace ZelWiki.Engine } /// - /// Transform links, these can be internal Wiki links or external links. + /// 转换链接,这些链接可以是内部Wiki链接或外部链接. /// /// private static void TransformLinks(WikiString pageContent, Dictionary 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,13 +178,15 @@ namespace ZelWiki.Engine if (args.Count == 1) { - pageContent.Replace(match.Value, StoreMatch(matchStore, $"{args[0]}")); + pageContent.Replace(match.Value, + StoreMatch(matchStore, $"{args[0]}")); } else if (args.Count > 1) { - pageContent.Replace(match.Value, StoreMatch(matchStore, $"{args[1]}")); + pageContent.Replace(match.Value, + StoreMatch(matchStore, $"{args[1]}")); } } } } -} +} \ No newline at end of file diff --git a/ZelWiki.Engine/ZelEngine.cs b/ZelWiki.Engine/ZelEngine.cs index 687707e..44e47fc 100644 --- a/ZelWiki.Engine/ZelEngine.cs +++ b/ZelWiki.Engine/ZelEngine.cs @@ -50,7 +50,7 @@ namespace ZelWiki.Engine } /// - /// Transforms the content for the given page. + /// 转换给定页面的内容. /// /// The users current state, used for localization. /// The page that is being processed. diff --git a/ZelWiki.Engine/ZelEngineState.cs b/ZelWiki.Engine/ZelEngineState.cs index 972737d..21cba4d 100644 --- a/ZelWiki.Engine/ZelEngineState.cs +++ b/ZelWiki.Engine/ZelEngineState.cs @@ -23,12 +23,13 @@ namespace ZelWiki.Engine private readonly string _tocName = "TOC_" + new Random().Next(0, 1000000).ToString(); private readonly Dictionary _handlerState = new(); - #region Public properties. + #region 公共属性. /// - /// Custom page title set by a call to @@Title("...") + /// 通过调用设置自定义页面标题 @@Title("...") /// 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 OmitMatches { get; private set; } = new(); - public int NestDepth { get; private set; } //Used for recursion. + public int NestDepth { get; private set; } //用于递归 #endregion /// - /// Used to store values for handlers that needs to survive only a single wiki processing session. + /// 存储只需要在单个wiki处理会话中生存的处理程序的值. /// public void SetStateValue(string key, T value) { @@ -65,11 +66,12 @@ namespace ZelWiki.Engine { return; } + _handlerState[key] = value; } /// - /// Used to get values for handlers that needs to survive only a single wiki processing session. + /// 获取只需要在单个处理会话中生存的处理程序的值. /// public T GetStateValue(string key, T defaultValue) { @@ -77,12 +79,13 @@ namespace ZelWiki.Engine { return (T)value; } + SetStateValue(key, defaultValue); return defaultValue; } /// - /// Used to get values for handlers that needs to survive only a single wiki processing session. + /// 尝试获取只需要在单个处理会话中生存的处理程序的值.. /// public bool TryGetStateValue(string key, [MaybeNullWhen(false)] out T? outValue) { @@ -97,13 +100,14 @@ namespace ZelWiki.Engine } /// - /// Creates a new instance of the ZelEngineState class. Typically created by a call to ZelEngine.Transform(). + /// 创建一个实例 /// - /// The users current state, used for localization. - /// The page that is being processed. - /// The revision of the page that is being processed. - /// The type of matches that we want to omit from processing. - /// The current depth of recursion. + /// + /// + /// + /// + /// + /// 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 } /// - /// Transforms "included" wiki pages, for example if a wiki function - /// injected additional wiki markup, this 'could' be processed separately. + /// /// - /// The child page to process - /// The optional revision of the child page to process + /// + /// /// 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, "
    "); - //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 } /// - /// Transform basic markup such as bold, italics, underline, etc. for single and multi-line. + /// 转换单行和多行的基本标记,如粗体、斜体、下划线等. /// /// 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 = "" + value + "\r\n"; - StoreMatch(Constants.WikiMatchType.Markup, pageContent, match.Value, markup); + var markup = "" + value + "\r\n"; + StoreMatch(WikiMatchType.Markup, pageContent, match.Value, markup); } } } /// - /// 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. + /// 转换内联和多行文字块 /// /// 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", "
    \r\n"), false); + StoreMatch(WikiMatchType.Literal, pageContent, match.Value, + value.Replace("\r", "").Trim().Replace("\n", "
    \r\n"), false); } } /// - /// Matching nested blocks with regex was hell, I escaped with a loop. ¯\_(ツ)_/¯ + /// /// /// 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($"A parsing error occurred after position {startPos}:
    Unable to locate closing tag.

    "); + exception.AppendLine( + $"定位后发生解析错误 {startPos}:
    无法找到结束标记.

    "); if (rawBlock?.Length > 0) { - exception.AppendLine($"The last successfully parsed block was:
    {rawBlock}"); + exception.AppendLine( + $"最后一个成功解析的块是:
    {rawBlock}"); } - exception.AppendLine($"The problem occurred after:
    {pageContent.ToString().Substring(startPos)}

    "); - exception.AppendLine($"The content the parser was working on is:
    {pageContent}

    "); + + exception.AppendLine( + $"问题发生在:
    {pageContent.ToString().Substring(startPos)}

    "); + exception.AppendLine( + $"解析器正在处理的内容是:
    {pageContent}

    "); throw new Exception(exception.ToString()); } @@ -369,10 +369,10 @@ namespace ZelWiki.Engine } /// - /// Transform blocks or sections of code, these are thinks like panels and alerts. + /// /// /// - /// Only process early functions (like code blocks) + /// 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 } /// - /// Transform headings. These are the basic HTML H1-H6 headings but they are saved for the building of the table of contents. + /// /// /// 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); } } /// - /// Transform variables. + /// /// /// 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()."); } } } } /// - /// Transform links, these can be internal Wiki links or external links. + /// /// /// 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); } } /// - /// Transform processing instructions are used to flag pages for specific needs such as deletion, review, draft, etc. + /// /// /// 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 } /// - /// Transform functions is used to call wiki functions such as including template pages, setting tags and displaying images. + /// /// /// + /// 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 } /// - /// 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. + /// /// + /// 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 = $""; - //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, "
    "); } #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 = $"{{{value}}}", 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,17 +941,16 @@ namespace ZelWiki.Engine } /// - /// 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. + /// /// /// 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 } -} +} \ No newline at end of file diff --git a/ZelWiki.Library/ConfirmActionHelper.cs b/ZelWiki.Library/ConfirmActionHelper.cs index 64d9d24..bf6efc2 100644 --- a/ZelWiki.Library/ConfirmActionHelper.cs +++ b/ZelWiki.Library/ConfirmActionHelper.cs @@ -6,14 +6,14 @@ namespace ZelWiki.Library public static class ConfirmActionHelper { /// - /// 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导航到“确认操作”页面,其中“是”链接为红色,但“否”按钮仍为绿色。 /// - /// The message to be displayed. - /// the label for the link that will redirect to this confirm action page. - /// The URL which will handle the click of the "yes" or "no" for the confirm action page. - /// An optional parameter to pass to the page and controller function. - /// 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. - /// 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. + /// + /// + /// + /// + /// + /// /// 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 $"{linkLabel}"; + return + $"{linkLabel}"; } /// - /// Generates a link that navigates via GET to a "confirm action" page where the yes link is GREEN. + /// 生成一个链接,通过GET导航到“确认操作”页面,其中“是”链接为绿色。 /// - /// The message to be displayed. - /// the label for the link that will redirect to this confirm action page. - /// The URL which will handle the click of the "yes" or "no" for the confirm action page. - /// An optional parameter to pass to the page and controller function. - /// 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. - /// 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. + /// + /// + /// + /// + /// + /// /// 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 $"{linkLabel}"; + return + $"{linkLabel}"; } /// - /// 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导航到“确认操作”页面,其中“是”链接为黄色,但“否”按钮仍为绿色。 /// - /// The message to be displayed. - /// the label for the link that will redirect to this confirm action page. - /// The URL which will handle the click of the "yes" or "no" for the confirm action page. - /// An optional parameter to pass to the page and controller function. - /// 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. - /// 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. + /// + /// + /// + /// + /// + /// /// 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 $"{linkLabel}"; + return + $"{linkLabel}"; } - /* + /// - /// 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. + /// /// - /// The message to be displayed. - /// the label for the button that will redirect to this confirm action page. - /// The URL which will handle the click of the "yes" or "no" for the confirm action page. - /// An optional parameter to pass to the page and controller function. - /// 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. - /// 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. + /// + /// + /// + /// + /// /// public static string GenerateDangerButton(string message, string buttonLabel, string controllerURL, string? yesOrDefaultRedirectURL, string? noRedirectURL = null) @@ -115,21 +117,22 @@ namespace ZelWiki.Library html.Append($""); html.Append($""); html.Append($""); - html.Append($""); + html.Append( + $""); html.Append(""); return html.ToString(); } /// - /// Generates a link that navigates via POST to a "confirm action" page where the yes and no buttons are GREEN. + /// /// - /// The message to be displayed. - /// the label for the button that will redirect to this confirm action page. - /// The URL which will handle the click of the "yes" or "no" for the confirm action page. - /// An optional parameter to pass to the page and controller function. - /// 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. - /// 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. + /// + /// + /// + /// + /// + /// public static string GenerateSafeButton(string message, string buttonLabel, string controllerURL, string? yesOrDefaultRedirectURL, string? noRedirectURL = null) { @@ -146,21 +149,22 @@ namespace ZelWiki.Library html.Append($""); html.Append($""); html.Append($""); - html.Append($""); + html.Append( + $""); html.Append(""); return html.ToString(); } /// - /// 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. + /// /// - /// The message to be displayed. - /// the label for the button that will redirect to this confirm action page. - /// The URL which will handle the click of the "yes" or "no" for the confirm action page. - /// An optional parameter to pass to the page and controller function. - /// 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. - /// 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. + /// + /// + /// + /// + /// + /// public static string GenerateWarnButton(string message, string buttonLabel, string controllerURL, string? yesOrDefaultRedirectURL, string? noRedirectURL = null) { @@ -177,11 +181,11 @@ namespace ZelWiki.Library html.Append($""); html.Append($""); html.Append($""); - html.Append($""); + html.Append( + $""); html.Append(""); return html.ToString(); } - */ } -} +} \ No newline at end of file diff --git a/ZelWiki.Library/Constants.cs b/ZelWiki.Library/Constants.cs index 0976759..ad95f7d 100644 --- a/ZelWiki.Library/Constants.cs +++ b/ZelWiki.Library/Constants.cs @@ -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 { /// - /// The password has not been changed, display a big warning. + /// 密码为默认 /// IsDefault, /// - /// All is well! + /// 已修改密码 /// HasBeenChanged, /// - /// The default password status does not exist and the password needs to be set to default. + /// 默认密码状态不存在,需要将密码设置为默认值 /// NeedsToBeSet } @@ -45,19 +45,19 @@ public static class Roles { /// - /// Administrators can do anything. Add, edit, delete, pages, users, etc. + ///管理员可以做任何事情。添加、编辑、删除、页面、用户等。 /// public const string Administrator = "Administrator"; /// - /// Read-only user with a profile. + /// 成员 /// public const string Member = "Member"; /// - /// Contributor can add and edit pages. + /// 版主 /// public const string Contributor = "Contributor"; /// - /// Moderators can add, edit and delete pages. + /// 主持 /// public const string Moderator = "Moderator"; } diff --git a/ZelWiki.Library/CountryItem.cs b/ZelWiki.Library/CountryItem.cs index fa52ec9..7ec76b2 100644 --- a/ZelWiki.Library/CountryItem.cs +++ b/ZelWiki.Library/CountryItem.cs @@ -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 { diff --git a/ZelWiki.Library/Images.cs b/ZelWiki.Library/Images.cs index b4f22be..e92c707 100644 --- a/ZelWiki.Library/Images.cs +++ b/ZelWiki.Library/Images.cs @@ -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": diff --git a/ZelWiki.Library/Interfaces/ISessionState.cs b/ZelWiki.Library/Interfaces/ISessionState.cs index 2e13c38..5bbc6e6 100644 --- a/ZelWiki.Library/Interfaces/ISessionState.cs +++ b/ZelWiki.Library/Interfaces/ISessionState.cs @@ -9,36 +9,36 @@ namespace ZelWiki.Library.Interfaces IQueryCollection? QueryString { get; set; } /// - /// Is the current user (or anonymous) allowed to view? + /// /// public bool CanView => true; /// - /// Is the current user allowed to edit? + /// /// public bool CanEdit { get; } /// - /// Is the current user allowed to perform administrative functions? + /// 是否允许当前用户执行管理功能? /// public bool CanAdmin { get; } /// - /// Is the current user allowed to moderate content (such as delete comments, and view moderation tools)? + /// 是否允许当前用户审核内容(如删除评论和查看审核工具)? /// public bool CanModerate { get; } /// - /// Is the current user allowed to create pages? + ///是否允许当前用户创建页面? /// public bool CanCreate { get; } /// - /// Is the current user allowed to delete unprotected pages? + /// 是否允许当前用户删除未受保护的页面? /// public bool CanDelete { get; } public DateTime LocalizeDateTime(DateTime datetime); public TimeZoneInfo GetPreferredTimeZone(); } -} +} \ No newline at end of file diff --git a/ZelWiki.Library/LanguageItem.cs b/ZelWiki.Library/LanguageItem.cs index 3aacec7..f1656c3 100644 --- a/ZelWiki.Library/LanguageItem.cs +++ b/ZelWiki.Library/LanguageItem.cs @@ -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 { diff --git a/ZelWiki.Library/MimeTypes.cs b/ZelWiki.Library/MimeTypes.cs index b5d4098..d6bd48e 100644 --- a/ZelWiki.Library/MimeTypes.cs +++ b/ZelWiki.Library/MimeTypes.cs @@ -5,14 +5,14 @@ namespace ZelWiki.Library internal static class MimeTypes { /// - /// Given a file path, determine the MIME type + /// 给定文件路径,确定MIME类型 /// - /// A file path + /// A file path /// The resulting MIME type /// True if MIME type could be determined 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; @@ -20,8 +20,7 @@ namespace ZelWiki.Library } return Collection.TryGetValue(extension, out contentType); } - - //Borrowed from FileExtensionContentTypeProvider().TryGetContentType + public static Dictionary 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" }, diff --git a/ZelWiki.Library/NamespaceNavigation.cs b/ZelWiki.Library/NamespaceNavigation.cs index 4bee124..44a9efc 100644 --- a/ZelWiki.Library/NamespaceNavigation.cs +++ b/ZelWiki.Library/NamespaceNavigation.cs @@ -29,6 +29,7 @@ namespace ZelWiki.Library { return Page; } + return $"{Namespace}::{Page}"; } set @@ -49,9 +50,9 @@ namespace ZelWiki.Library } /// - /// Creates a new instance of NamespaceNavigation. + /// /// - /// Page navigation with optional namespace. + /// public NamespaceNavigation(string givenCanonical) { _lowerCase = true; @@ -59,10 +60,10 @@ namespace ZelWiki.Library } /// - /// Creates a new instance of NamespaceNavigation. + /// /// - /// Page navigation with optional namespace. - /// If false, the namespace and page name will not be lowercased. + /// + /// public NamespaceNavigation(string givenCanonical, bool lowerCase) { _lowerCase = lowerCase; @@ -75,10 +76,10 @@ namespace ZelWiki.Library } /// - /// Takes a page name with optional namespace and returns the cleaned version that can be used for matching Navigations. + /// /// - /// Page navigation with optional namespace. - /// If false, the namespace and page name will not be lowercased. + /// + /// /// /// 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(" ", " "); + .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(['/', '\\']); } } -} +} \ No newline at end of file diff --git a/ZelWiki.Library/Navigation.cs b/ZelWiki.Library/Navigation.cs index 542dd9e..754f87d 100644 --- a/ZelWiki.Library/Navigation.cs +++ b/ZelWiki.Library/Navigation.cs @@ -3,6 +3,9 @@ using System.Text.RegularExpressions; namespace ZelWiki.Library { + /// + /// 导航 + /// public class Navigation { public static string Clean(string? str) @@ -11,30 +14,26 @@ 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,}", "/"); diff --git a/ZelWiki.Library/PageSelectorGenerator.cs b/ZelWiki.Library/PageSelectorGenerator.cs index 1eee3ad..329d84f 100644 --- a/ZelWiki.Library/PageSelectorGenerator.cs +++ b/ZelWiki.Library/PageSelectorGenerator.cs @@ -42,25 +42,25 @@ namespace ZelWiki.Library sb.Append($"
    "); if (currentPage > 1) { - sb.Append($"<< First"); + sb.Append($"<< 首页"); sb.Append("  |  "); - sb.Append($"< Previous"); + sb.Append($"< 上一页"); } else { - sb.Append($"<< First   |   < Previous"); + sb.Append($"<< 首页   |   < 上一页"); } sb.Append("  |  "); if (currentPage < totalPageCount) { - sb.Append($"Next >"); + sb.Append($"下一页 >"); sb.Append("  |  "); - sb.Append($"Last >>"); + sb.Append($"尾页 >>"); } else { - sb.Append("Next >   |   Last >>"); + sb.Append("下一页 >   |   尾页 >>"); } sb.Append($"
    "); } diff --git a/ZelWiki.Library/QueryStringConverter.cs b/ZelWiki.Library/QueryStringConverter.cs index 949753f..5ee3010 100644 --- a/ZelWiki.Library/QueryStringConverter.cs +++ b/ZelWiki.Library/QueryStringConverter.cs @@ -8,18 +8,19 @@ namespace ZelWiki.Library public static class QueryStringConverter { /// - /// 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). + /// 排序 /// + /// + /// /// 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); } - /// - /// Takes the current page query string and upserts a query key/value, replacing any conflicting query string entry. - /// - /// - /// - /// - /// +/// +/// +/// +/// +/// +/// +/// 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,7 +167,8 @@ namespace ZelWiki.Library { clone.Add(kvp.Key, kvp.Value); } + return clone; } } -} +} \ No newline at end of file diff --git a/ZelWiki.Library/Theme.cs b/ZelWiki.Library/Theme.cs index 9b39c67..973e268 100644 --- a/ZelWiki.Library/Theme.cs +++ b/ZelWiki.Library/Theme.cs @@ -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 Files { get; set; } = new(); + /// + /// + /// + 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 Files { get; set; } } -} +} \ No newline at end of file diff --git a/ZelWiki.Library/TimeZoneItem.cs b/ZelWiki.Library/TimeZoneItem.cs index 618aa81..2766175 100644 --- a/ZelWiki.Library/TimeZoneItem.cs +++ b/ZelWiki.Library/TimeZoneItem.cs @@ -2,8 +2,17 @@ { public class TimeZoneItem { - public string Text { get; set; } = string.Empty; - public string Value { get; set; } = string.Empty; + /// + /// + /// + public TimeZoneItem() + { + Text = string.Empty; + Value = string.Empty; + } + + public string Text { get; set; } + public string Value { get; set; } public static List GetAll() { @@ -17,4 +26,4 @@ return list.OrderBy(o => o.Text).ToList(); } } -} +} \ No newline at end of file diff --git a/ZelWiki.Library/Utility.cs b/ZelWiki.Library/Utility.cs index a42d8fe..8940fcd 100644 --- a/ZelWiki.Library/Utility.cs +++ b/ZelWiki.Library/Utility.cs @@ -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 } /// - /// Take a height and width and enforces a max on both dimensions while maintaining the ratio. + /// /// /// /// @@ -35,20 +34,16 @@ namespace ZelWiki.Library /// public static (int Width, int Height) ScaleToMaxOf(int originalWidth, int originalHeight, int maxSize) { - // Calculate aspect ratio - float aspectRatio = (float)originalWidth / originalHeight; - - // Determine new dimensions based on the larger dimension + var aspectRatio = (float)originalWidth / originalHeight; + 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(); + 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(); - } + return []; + using var compressedStream = new MemoryStream(data); using var decompressor = new GZipStream(compressedStream, CompressionMode.Decompress); @@ -105,4 +100,4 @@ namespace ZelWiki.Library return decompressedStream.ToArray(); } } -} +} \ No newline at end of file diff --git a/ZelWiki.Models/DataModels/ConfigurationEntries.cs b/ZelWiki.Models/DataModels/ConfigurationEntries.cs index 0e18c91..5ce166b 100644 --- a/ZelWiki.Models/DataModels/ConfigurationEntries.cs +++ b/ZelWiki.Models/DataModels/ConfigurationEntries.cs @@ -16,7 +16,7 @@ public T? Value(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(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; diff --git a/ZelWiki.Models/DataModels/ConfigurationEntry.cs b/ZelWiki.Models/DataModels/ConfigurationEntry.cs index 678460b..87d8987 100644 --- a/ZelWiki.Models/DataModels/ConfigurationEntry.cs +++ b/ZelWiki.Models/DataModels/ConfigurationEntry.cs @@ -19,12 +19,7 @@ namespace ZelWiki.Models.DataModels public T? As(T defaultValue) { - if (Value == null) - { - return defaultValue; - } - - return Converters.ConvertTo(Value); + return Value == null ? defaultValue : Converters.ConvertTo(Value); } public string DataType { get; set; } = string.Empty; diff --git a/ZelWiki.Models/DataModels/Page.cs b/ZelWiki.Models/DataModels/Page.cs index 510e615..26791d6 100644 --- a/ZelWiki.Models/DataModels/Page.cs +++ b/ZelWiki.Models/DataModels/Page.cs @@ -7,23 +7,23 @@ namespace ZelWiki.Models.DataModels public int Id { get; set; } = 0; /// - /// The revision of this page that is being viewed. May not be the latest revision. + /// 正在查看的此页面的修订版。可能不是最新版本 /// public int Revision { get; set; } /// - /// The most current revision of this page. + /// 此页面的最新修订版 /// public int MostCurrentRevision { get; set; } public bool IsHistoricalVersion => Revision != MostCurrentRevision; /// - /// Lets us know whether this page exists and is loaded. + /// 此页面是否存在并已加载 /// public bool Exists => Id > 0; /// - /// Count of revisions higher than Revision. + /// 修订次数高于修订次数 /// 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) { diff --git a/ZelWiki.Models/DataModels/PageCache.cs b/ZelWiki.Models/DataModels/PageCache.cs index d510d3d..56d0c3c 100644 --- a/ZelWiki.Models/DataModels/PageCache.cs +++ b/ZelWiki.Models/DataModels/PageCache.cs @@ -1,20 +1,20 @@ namespace ZelWiki.Models.DataModels { /// - /// Used to cache pre-processed wiki results. + /// 用于缓存预处理的结果 /// public class PageCache { + public PageCache(string body) + { + Body = body; + } /// - /// Custom page title set by a call to @@Title("...") + /// 通过调用@@Title("...")设置自定义页面title /// public string? PageTitle { get; set; } public string Body { get; set; } - public PageCache(string body) - { - Body = body; - } } } diff --git a/ZelWiki.Models/DataModels/ProcessingInstruction.cs b/ZelWiki.Models/DataModels/ProcessingInstruction.cs index 9b78400..34b96b2 100644 --- a/ZelWiki.Models/DataModels/ProcessingInstruction.cs +++ b/ZelWiki.Models/DataModels/ProcessingInstruction.cs @@ -4,7 +4,7 @@ namespace ZelWiki.Models.DataModels { public int PageId { get; set; } /// - /// TightWiki.Library.Constants.WikiInstruction + /// /// public string Instruction { get; set; } = string.Empty; } diff --git a/ZelWiki.Models/DataModels/ProcessingInstructionCollection.cs b/ZelWiki.Models/DataModels/ProcessingInstructionCollection.cs index a363186..6f501d7 100644 --- a/ZelWiki.Models/DataModels/ProcessingInstructionCollection.cs +++ b/ZelWiki.Models/DataModels/ProcessingInstructionCollection.cs @@ -5,7 +5,7 @@ public List Collection { get; set; } = new(); /// - /// Returns true if the collection contains the given processing instruction. + /// 如果集合包含给定的处理指令,则返回true /// /// WikiInstruction.Protect /// diff --git a/ZelWiki.Repository/ConfigurationRepository.cs b/ZelWiki.Repository/ConfigurationRepository.cs index e70b522..aa3c390 100644 --- a/ZelWiki.Repository/ConfigurationRepository.cs +++ b/ZelWiki.Repository/ConfigurationRepository.cs @@ -13,11 +13,12 @@ namespace ZelWiki.Repository { public static class ConfigurationRepository { - #region Upgrade Database. + #region public static string GetVersionStateVersion() { - var entries = ManagedDataStorage.Config.ExecuteScalar(@"Scripts\Initialization\GetVersionStateVersion.sql"); + var entries = + ManagedDataStorage.Config.ExecuteScalar(@"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 }); } /// - /// See @Initialization.Versions.md + /// /// 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 } /// - /// Determines if this is the first time the wiki has run. Returns true if it is the first time. + /// /// /// public static bool IsFirstRun() { - bool isEncryptionValid = GetCryptoCheck(); + var isEncryptionValid = GetCryptoCheck(); if (isEncryptionValid == false) { SetCryptoCheck(); return true; } + return false; } - /// - /// 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! - /// - /// +/// +/// +/// +/// public static bool GetCryptoCheck() { var value = ManagedDataStorage.Config.QueryFirstOrDefault("GetCryptoCheck.sql") ?? string.Empty; @@ -203,9 +205,9 @@ namespace ZelWiki.Repository return false; } - /// - /// Writes an encrypted value to the database so we can test at a later time to ensure that encryption is setup. - /// +/// +/// +/// 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 GetFlatConfiguration() => ManagedDataStorage.Config.Query("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(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("GetConfigurationEntryValuesByGroupNameAndEntryName.sql", param); + var configEntry = + ManagedDataStorage.Config.QuerySingle( + "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); - - //Adjust to any specified scaling. + var (Width, Height) = Utility.ScaleToMaxOf(img.Width, img.Height, + GlobalConfiguration.DefaultEmojiHeight); + 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("Default Profile Recently Modified Count"); + GlobalConfiguration.DefaultProfileRecentlyModifiedCount = + performanceConfig.Value("Default Profile Recently Modified Count"); GlobalConfiguration.PreLoadAnimatedEmojis = performanceConfig.Value("Pre-Load Animated Emojis"); GlobalConfiguration.SystemTheme = GetAllThemes().Single(o => o.Name == themeName); GlobalConfiguration.DefaultEmojiHeight = customizationConfig.Value("Default Emoji Height"); GlobalConfiguration.AllowGoogleAuthentication = membershipConfig.Value("Allow Google Authentication"); - GlobalConfiguration.DefaultTimeZone = customizationConfig?.Value("Default TimeZone") ?? string.Empty; - GlobalConfiguration.IncludeWikiDescriptionInMeta = functionalityConfig.Value("Include wiki Description in Meta"); + GlobalConfiguration.DefaultTimeZone = + customizationConfig?.Value("Default TimeZone") ?? string.Empty; + GlobalConfiguration.IncludeWikiDescriptionInMeta = + functionalityConfig.Value("Include wiki Description in Meta"); GlobalConfiguration.IncludeWikiTagsInMeta = functionalityConfig.Value("Include wiki Tags in Meta"); GlobalConfiguration.EnablePageComments = functionalityConfig.Value("Enable Page Comments"); GlobalConfiguration.EnablePublicProfiles = functionalityConfig.Value("Enable Public Profiles"); - GlobalConfiguration.ShowCommentsOnPageFooter = functionalityConfig.Value("Show Comments on Page Footer"); - GlobalConfiguration.ShowLastModifiedOnPageFooter = functionalityConfig.Value("Show Last Modified on Page Footer"); + GlobalConfiguration.ShowCommentsOnPageFooter = + functionalityConfig.Value("Show Comments on Page Footer"); + GlobalConfiguration.ShowLastModifiedOnPageFooter = + functionalityConfig.Value("Show Last Modified on Page Footer"); GlobalConfiguration.IncludeSearchOnNavbar = searchConfig.Value("Include Search on Navbar"); GlobalConfiguration.HTMLHeader = htmlConfig?.Value("Header") ?? string.Empty; GlobalConfiguration.HTMLFooter = htmlConfig?.Value("Footer") ?? string.Empty; GlobalConfiguration.HTMLPreBody = htmlConfig?.Value("Pre-Body") ?? string.Empty; GlobalConfiguration.HTMLPostBody = htmlConfig?.Value("Post-Body") ?? string.Empty; - GlobalConfiguration.BrandImageSmall = customizationConfig?.Value("Brand Image (Small)") ?? string.Empty; + GlobalConfiguration.BrandImageSmall = + customizationConfig?.Value("Brand Image (Small)") ?? string.Empty; GlobalConfiguration.FooterBlurb = customizationConfig?.Value("FooterBlurb") ?? string.Empty; GlobalConfiguration.MaxAvatarFileSize = filesAndAttachmentsConfig.Value("Max Avatar File Size"); - GlobalConfiguration.MaxAttachmentFileSize = filesAndAttachmentsConfig.Value("Max Attachment File Size"); + GlobalConfiguration.MaxAttachmentFileSize = + filesAndAttachmentsConfig.Value("Max Attachment File Size"); GlobalConfiguration.MaxEmojiFileSize = filesAndAttachmentsConfig.Value("Max Emoji File Size"); GlobalConfiguration.MenuItems = GetAllMenuItems(); @@ -525,4 +537,4 @@ namespace ZelWiki.Repository ReloadEmojis(); } } -} +} \ No newline at end of file diff --git a/ZelWiki.Repository/ManagedDataStorage.cs b/ZelWiki.Repository/ManagedDataStorage.cs index a505211..7528d22 100644 --- a/ZelWiki.Repository/ManagedDataStorage.cs +++ b/ZelWiki.Repository/ManagedDataStorage.cs @@ -2,9 +2,6 @@ namespace ZelWiki.Repository { - /// - /// Stores instances of ManagedDataStorageFactories that are used to store various parts of the data for the site. - /// public static class ManagedDataStorage { private static (string Name, ManagedDataStorageFactory Factory)[]? _collection = null; diff --git a/ZelWiki.Repository/PageFileRepository.cs b/ZelWiki.Repository/PageFileRepository.cs index 11c3cf0..be4853c 100644 --- a/ZelWiki.Repository/PageFileRepository.cs +++ b/ZelWiki.Repository/PageFileRepository.cs @@ -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, @@ -193,8 +191,7 @@ 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,26 +205,19 @@ 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; } if (hasFileChanged) { 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); } diff --git a/ZelWiki.Repository/PageRepository.cs b/ZelWiki.Repository/PageRepository.cs index 39305bc..ee1d6f4 100644 --- a/ZelWiki.Repository/PageRepository.cs +++ b/ZelWiki.Repository/PageRepository.cs @@ -20,7 +20,8 @@ namespace ZelWiki.Repository return ManagedDataStorage.Pages.QuerySingleOrDefault("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("GetPageProcessingInstructionsByPageId.sql", param).ToList() + Collection = ManagedDataStorage.Pages + .Query("GetPageProcessingInstructionsByPageId.sql", param).ToList() }; } @@ -68,7 +70,8 @@ namespace ZelWiki.Repository } public static List 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("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(query, param).ToList(); }); } @@ -96,7 +100,8 @@ namespace ZelWiki.Repository TopCount = topCount }; - return ManagedDataStorage.Pages.Query("GetTopRecentlyModifiedPagesInfoByUserId.sql", param).ToList(); + return ManagedDataStorage.Pages.Query("GetTopRecentlyModifiedPagesInfoByUserId.sql", param) + .ToList(); } public static string? GetPageNavigationByPageId(int pageId) @@ -149,12 +154,13 @@ namespace ZelWiki.Repository }); } - private static List GetMeteredPageSearchTokens(List searchTerms, bool allowFuzzyMatching, bool allowCache = true) + private static List GetMeteredPageSearchTokens(List 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>(cacheKey, out var result)) { result = GetMeteredPageSearchTokens(searchTerms, allowFuzzyMatching, false); @@ -167,11 +173,11 @@ namespace ZelWiki.Repository var minimumMatchScore = ConfigurationRepository.Get("Search", "Minimum Match Score"); var searchTokens = (from o in searchTerms - select new PageToken - { - Token = o, - DoubleMetaphone = o.ToDoubleMetaphone() - }).ToList(); + select new PageToken + { + Token = o, + DoubleMetaphone = o.ToDoubleMetaphone() + }).ToList(); if (allowFuzzyMatching == true) { @@ -181,15 +187,15 @@ namespace ZelWiki.Repository allTokens.AddRange(fuzzyTokens); return allTokens - .GroupBy(token => token.PageId) - .Where(group => group.Sum(g => g.Score) >= minimumMatchScore) // Filtering groups - .Select(group => new PageSearchToken - { - PageId = group.Key, - Match = group.Max(g => g.Match), - Weight = group.Max(g => g.Weight), - Score = group.Max(g => g.Score) - }).ToList(); + .GroupBy(token => token.PageId) + .Where(group => group.Sum(g => g.Score) >= minimumMatchScore) + .Select(group => new PageSearchToken + { + PageId = group.Key, + Match = group.Max(g => g.Match), + Weight = group.Max(g => g.Weight), + Score = group.Max(g => g.Score) + }).ToList(); } else { @@ -204,7 +210,7 @@ namespace ZelWiki.Repository return new List(); } - bool allowFuzzyMatching = ConfigurationRepository.Get("Search", "Allow Fuzzy Matching"); + var allowFuzzyMatching = ConfigurationRepository.Get("Search", "Allow Fuzzy Matching"); var meteredSearchTokens = GetMeteredPageSearchTokens(searchTerms, allowFuzzyMatching == true); if (meteredSearchTokens.Count == 0) { @@ -224,7 +230,8 @@ namespace ZelWiki.Repository }); } - public static List PageSearchPaged(List searchTerms, int pageNumber, int? pageSize = null, bool? allowFuzzyMatching = null) + public static List PageSearchPaged(List searchTerms, int pageNumber, int? pageSize = null, + bool? allowFuzzyMatching = null) { if (searchTerms.Count == 0) { @@ -256,7 +263,8 @@ namespace ZelWiki.Repository }); } - public static List GetSimilarPagesPaged(int pageId, int similarity, int pageNumber, int? pageSize = null) + public static List GetSimilarPagesPaged(int pageId, int similarity, int pageNumber, + int? pageSize = null) { pageSize ??= ConfigurationRepository.Get("Customization", "Pagination Size"); @@ -364,7 +372,8 @@ namespace ZelWiki.Repository }); } - public static List GetMissingPagesPaged(int pageNumber, string? orderBy = null, string? orderByDirection = null) + public static List GetMissingPagesPaged(int pageNumber, string? orderBy = null, + string? orderByDirection = null) { int pageSize = ConfigurationRepository.Get("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(query, param).ToList(); }); } /// - /// 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. + /// /// /// - /// + /// + /// /// /// public static List 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(query, param).ToList(); }); } @@ -528,11 +539,11 @@ namespace ZelWiki.Repository } /// - /// 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. + /// /// /// - /// + /// + /// /// /// public static List 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(query, param).ToList(); }); } @@ -567,7 +579,8 @@ namespace ZelWiki.Repository }); } - public static List GetAllNamespacesPaged(int pageNumber, string? orderBy = null, string? orderByDirection = null) + public static List GetAllNamespacesPaged(int pageNumber, string? orderBy = null, + string? orderByDirection = null) { int pageSize = ConfigurationRepository.Get("Customization", "Pagination Size"); @@ -683,6 +696,7 @@ namespace ZelWiki.Repository WikiCache.Put(cacheKey, result); } } + return result; } @@ -719,31 +733,27 @@ 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("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."); + ?? 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 - || currentRevisionInfo.DataHash != newDataHash; + || currentRevisionInfo.Namespace != page.Namespace + || currentRevisionInfo.Description != page.Description + || currentRevisionInfo.DataHash != newDataHash; } if (hasPageChanged) @@ -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 } /// - /// Gets the page info without the content. + /// /// /// /// @@ -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(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); @@ -1172,4 +1181,4 @@ namespace ZelWiki.Repository #endregion } -} +} \ No newline at end of file diff --git a/ZelWiki.Repository/RepositoryHelper.cs b/ZelWiki.Repository/RepositoryHelper.cs index c4e3803..2608a81 100644 --- a/ZelWiki.Repository/RepositoryHelper.cs +++ b/ZelWiki.Repository/RepositoryHelper.cs @@ -5,11 +5,13 @@ namespace ZelWiki.Repository internal static class RepositoryHelper { /// - /// Fills in a custom orderby on a given sql script. + /// /// /// /// + /// /// + /// 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") - + script.Substring(endParentIndex + endParentTag.Length); + + $"ORDER BY\r\n\t{selectedConfig.Field} " + + (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 @@ -71,4 +79,4 @@ namespace ZelWiki.Repository return script; } } -} +} \ No newline at end of file diff --git a/ZelWiki.Repository/Scripts/AssociatePageFileAttachmentWithPageRevision.sql b/ZelWiki.Repository/Scripts/AssociatePageFileAttachmentWithPageRevision.sql index c8fd818..5b74250 100644 --- a/ZelWiki.Repository/Scripts/AssociatePageFileAttachmentWithPageRevision.sql +++ b/ZelWiki.Repository/Scripts/AssociatePageFileAttachmentWithPageRevision.sql @@ -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, diff --git a/ZelWiki.Repository/Scripts/GetAllPublicProfilesPaged.sql b/ZelWiki.Repository/Scripts/GetAllPublicProfilesPaged.sql index 44f954a..ed72618 100644 --- a/ZelWiki.Repository/Scripts/GetAllPublicProfilesPaged.sql +++ b/ZelWiki.Repository/Scripts/GetAllPublicProfilesPaged.sql @@ -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, diff --git a/ZelWiki.Repository/Scripts/GetExactPageSearchTokens.sql b/ZelWiki.Repository/Scripts/GetExactPageSearchTokens.sql index 858d91d..5ada219 100644 --- a/ZelWiki.Repository/Scripts/GetExactPageSearchTokens.sql +++ b/ZelWiki.Repository/Scripts/GetExactPageSearchTokens.sql @@ -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 diff --git a/ZelWiki.Repository/Scripts/GetFuzzyPageSearchTokens.sql b/ZelWiki.Repository/Scripts/GetFuzzyPageSearchTokens.sql index e04aa45..da5da77 100644 --- a/ZelWiki.Repository/Scripts/GetFuzzyPageSearchTokens.sql +++ b/ZelWiki.Repository/Scripts/GetFuzzyPageSearchTokens.sql @@ -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 diff --git a/ZelWiki.Repository/Scripts/GetPageFilesInfoByPageId.sql b/ZelWiki.Repository/Scripts/GetPageFilesInfoByPageId.sql index 8ec3ed6..1c00096 100644 --- a/ZelWiki.Repository/Scripts/GetPageFilesInfoByPageId.sql +++ b/ZelWiki.Repository/Scripts/GetPageFilesInfoByPageId.sql @@ -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 diff --git a/ZelWiki.Repository/Scripts/GetPageFilesInfoByPageNavigationAndPageRevisionPaged.sql b/ZelWiki.Repository/Scripts/GetPageFilesInfoByPageNavigationAndPageRevisionPaged.sql index 45b9642..9667445 100644 --- a/ZelWiki.Repository/Scripts/GetPageFilesInfoByPageNavigationAndPageRevisionPaged.sql +++ b/ZelWiki.Repository/Scripts/GetPageFilesInfoByPageNavigationAndPageRevisionPaged.sql @@ -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 diff --git a/ZelWiki.Repository/Scripts/Initialization/@Initialization.Versions.md b/ZelWiki.Repository/Scripts/Initialization/@Initialization.Versions.md deleted file mode 100644 index bbf8669..0000000 --- a/ZelWiki.Repository/Scripts/Initialization/@Initialization.Versions.md +++ /dev/null @@ -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. diff --git a/ZelWiki.Repository/Scripts/InsertPageFileRevision.sql b/ZelWiki.Repository/Scripts/InsertPageFileRevision.sql index cba2921..9b69086 100644 --- a/ZelWiki.Repository/Scripts/InsertPageFileRevision.sql +++ b/ZelWiki.Repository/Scripts/InsertPageFileRevision.sql @@ -1,4 +1,3 @@ ---Insert the actual file data. INSERT INTO PageFileRevision ( PageFileId, diff --git a/ZelWiki.Repository/Scripts/MovePageToDeletedById.sql b/ZelWiki.Repository/Scripts/MovePageToDeletedById.sql index ce92f95..a504709 100644 --- a/ZelWiki.Repository/Scripts/MovePageToDeletedById.sql +++ b/ZelWiki.Repository/Scripts/MovePageToDeletedById.sql @@ -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; diff --git a/ZelWiki.Repository/Scripts/PurgeDeletedPageByPageId.sql b/ZelWiki.Repository/Scripts/PurgeDeletedPageByPageId.sql index f49126d..291dab8 100644 --- a/ZelWiki.Repository/Scripts/PurgeDeletedPageByPageId.sql +++ b/ZelWiki.Repository/Scripts/PurgeDeletedPageByPageId.sql @@ -1,4 +1,4 @@ ---Cleanup + DELETE FROM DeletionMeta WHERE PageId = @PageId; DELETE FROM [PageTag] WHERE PageId = @PageId; diff --git a/ZelWiki.Repository/Scripts/PurgeOrphanedPageAttachment.sql b/ZelWiki.Repository/Scripts/PurgeOrphanedPageAttachment.sql index f9df04a..d77c702 100644 --- a/ZelWiki.Repository/Scripts/PurgeOrphanedPageAttachment.sql +++ b/ZelWiki.Repository/Scripts/PurgeOrphanedPageAttachment.sql @@ -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); diff --git a/ZelWiki.Repository/Scripts/PurgeOrphanedPageAttachments.sql b/ZelWiki.Repository/Scripts/PurgeOrphanedPageAttachments.sql index cffef73..4cb2704 100644 --- a/ZelWiki.Repository/Scripts/PurgeOrphanedPageAttachments.sql +++ b/ZelWiki.Repository/Scripts/PurgeOrphanedPageAttachments.sql @@ -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); diff --git a/ZelWiki.Repository/Scripts/RestoreDeletedPageByPageId.sql b/ZelWiki.Repository/Scripts/RestoreDeletedPageByPageId.sql index a9f5a0c..47f5f12 100644 --- a/ZelWiki.Repository/Scripts/RestoreDeletedPageByPageId.sql +++ b/ZelWiki.Repository/Scripts/RestoreDeletedPageByPageId.sql @@ -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; diff --git a/ZelWiki.Repository/Scripts/TruncateAllPageRevisions.sql b/ZelWiki.Repository/Scripts/TruncateAllPageRevisions.sql index 7db8a0c..8ad7895 100644 --- a/ZelWiki.Repository/Scripts/TruncateAllPageRevisions.sql +++ b/ZelWiki.Repository/Scripts/TruncateAllPageRevisions.sql @@ -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; diff --git a/ZelWiki.Repository/Scripts/UpdateSinglePageReference.sql b/ZelWiki.Repository/Scripts/UpdateSinglePageReference.sql index af37d5f..ec9743e 100644 --- a/ZelWiki.Repository/Scripts/UpdateSinglePageReference.sql +++ b/ZelWiki.Repository/Scripts/UpdateSinglePageReference.sql @@ -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 diff --git a/ZelWiki.Repository/Scripts/UpsertEmojiCategories.sql b/ZelWiki.Repository/Scripts/UpsertEmojiCategories.sql index 55b676a..8ecd815 100644 --- a/ZelWiki.Repository/Scripts/UpsertEmojiCategories.sql +++ b/ZelWiki.Repository/Scripts/UpsertEmojiCategories.sql @@ -11,7 +11,6 @@ DELETE FROM EmojiCategory WHERE Id IN AND TC.Value IS NULL ); ---Insert previously non-existing categories. INSERT INTO EmojiCategory ( EmojiId, diff --git a/ZelWiki.Repository/SecurityRepository.cs b/ZelWiki.Repository/SecurityRepository.cs index e8710e2..66d7072 100644 --- a/ZelWiki.Repository/SecurityRepository.cs +++ b/ZelWiki.Repository/SecurityRepository.cs @@ -7,16 +7,10 @@ namespace ZelWiki.Repository { public static class SecurityRepository { - /// - /// Detect whether this is the first time the WIKI has ever been run and do some initialization. - /// Adds the first user with the email and password contained in Constants.DEFAULTUSERNAME and Constants.DEFAULTPASSWORD - /// public static async void ValidateEncryptionAndCreateAdminUser(UserManager userManager) { if (ConfigurationRepository.IsFirstRun()) { - //If this is the first time the app has run on this machine (based on an encryption key) then clear the admin password status. - //This will cause the application to set the admin password to the default password and display a warning until it is changed. UsersRepository.SetAdminPasswordClear(); } @@ -36,7 +30,7 @@ namespace ZelWiki.Repository user.EnsureNotNull(); - user.Email = Constants.DEFAULTUSERNAME; // Ensure email is set or updated + user.Email = Constants.DEFAULTUSERNAME; user.EmailConfirmed = true; var emailUpdateResult = await userManager.UpdateAsync(user); if (!emailUpdateResult.Succeeded) @@ -79,19 +73,15 @@ namespace ZelWiki.Repository public static async void UpsertUserClaims(UserManager userManager, IdentityUser user, List givenClaims) { - // Get existing claims for the user var existingClaims = await userManager.GetClaimsAsync(user); foreach (var givenClaim in givenClaims) { - // Remove existing claims if they exist var firstNameClaim = existingClaims.FirstOrDefault(c => c.Type == givenClaim.Type); if (firstNameClaim != null) { await userManager.RemoveClaimAsync(user, firstNameClaim); } - - // Add new claim. await userManager.AddClaimAsync(user, givenClaim); } diff --git a/ZelWiki.Repository/UsersRepository.cs b/ZelWiki.Repository/UsersRepository.cs index e39a076..68d57f8 100644 --- a/ZelWiki.Repository/UsersRepository.cs +++ b/ZelWiki.Repository/UsersRepository.cs @@ -93,7 +93,7 @@ namespace ZelWiki.Repository { if (DoesProfileAccountExist(Navigation.Clean(accountName))) { - throw new Exception("An account with that name already exists"); + throw new Exception("帐户名已存在"); } var param = new diff --git a/ZelWiki.Security/CRC32.cs b/ZelWiki.Security/CRC32.cs index 9d5433c..ef41fa9 100644 --- a/ZelWiki.Security/CRC32.cs +++ b/ZelWiki.Security/CRC32.cs @@ -1,24 +1,26 @@ namespace ZelWiki.Security { /// - /// Performs 32-bit reversed cyclic redundancy checks. + /// /// public class Crc32 { - #region Constants + #region + /// - /// Generator polynomial (modulo 2) for the reversed CRC32 algorithm. + /// 逆CRC32算法的生成多项式(模2) /// private const UInt32 s_generator = 0xEDB88320; + #endregion - #region Constructors + #region + /// - /// Creates a new instance of the Crc32 class. + /// 创建Crc32类的新实例 /// public Crc32() { - // Constructs the checksum lookup table. Used to optimize the checksum. m_checksumTable = Enumerable.Range(0, 256).Select(i => { var tableEntry = (uint)i; @@ -28,46 +30,53 @@ ? (s_generator ^ (tableEntry >> 1)) : (tableEntry >> 1); } + return tableEntry; }).ToArray(); } + #endregion - #region Methods + #region + /// - /// Calculates the checksum of the byte stream. + /// 计算字节流的校验和 /// - /// The byte stream to calculate the checksum for. - /// A 32-bit reversed checksum. + /// + /// + /// + /// public UInt32 Get(IEnumerable byteStream) { try { - // Initialize checksumRegister to 0xFFFFFFFF and calculate the checksum. return ~byteStream.Aggregate(0xFFFFFFFF, (checksumRegister, currentByte) => - (m_checksumTable[(checksumRegister & 0xFF) ^ Convert.ToByte(currentByte)] ^ (checksumRegister >> 8))); + (m_checksumTable[(checksumRegister & 0xFF) ^ Convert.ToByte(currentByte)] ^ + (checksumRegister >> 8))); } catch (FormatException e) { - throw new Exception("Could not read the stream out as bytes.", e); + throw new Exception("无法以字节形式读取流", e); } catch (InvalidCastException e) { - throw new Exception("Could not read the stream out as bytes.", e); + throw new Exception("无法以字节形式读取流", e); } catch (OverflowException e) { - throw new Exception("Could not read the stream out as bytes.", e); + throw new Exception("无法以字节形式读取流", e); } } + #endregion - #region Fields + #region + /// - /// Contains a cache of calculated checksum chunks. + /// 包含计算校验和块的缓存 /// private readonly UInt32[] m_checksumTable; #endregion } -} +} \ No newline at end of file diff --git a/ZelWiki.Security/Helpers.cs b/ZelWiki.Security/Helpers.cs index 79b158f..1e05cb2 100644 --- a/ZelWiki.Security/Helpers.cs +++ b/ZelWiki.Security/Helpers.cs @@ -6,6 +6,7 @@ namespace ZelWiki.Security public static class Helpers { private static string? _machineKey; + public static string MachineKey => _machineKey ??= Sha1(Environment.MachineName); @@ -35,22 +36,23 @@ namespace ZelWiki.Security public static string Sha256(string value) { var hash = new StringBuilder(); - byte[] crypto = SHA256.HashData(Encoding.UTF8.GetBytes(value)); + var crypto = SHA256.HashData(Encoding.UTF8.GetBytes(value)); foreach (byte theByte in crypto) { hash.Append(theByte.ToString("x2")); } + return hash.ToString(); } public static string EncryptString(string key, string plainText) { using var aes = Aes.Create(); - byte[] iv = new byte[16]; - byte[] keyBytes = SHA256.HashData(Encoding.Unicode.GetBytes(key)); - byte[] vector = (byte[])keyBytes.Clone(); + var iv = new byte[16]; + var keyBytes = SHA256.HashData(Encoding.Unicode.GetBytes(key)); + var vector = (byte[])keyBytes.Clone(); - for (int i = 0; i < 16; i++) + for (var i = 0; i < 16; i++) { iv[i] = vector[i]; } @@ -72,14 +74,14 @@ namespace ZelWiki.Security public static string DecryptString(string key, string cipherText) { - byte[] buffer = Convert.FromBase64String(cipherText); + var buffer = Convert.FromBase64String(cipherText); using var aes = Aes.Create(); - byte[] iv = new byte[16]; - byte[] keyBytes = SHA256.HashData(Encoding.Unicode.GetBytes(key)); - byte[] vector = (byte[])keyBytes.Clone(); + var iv = new byte[16]; + var keyBytes = SHA256.HashData(Encoding.Unicode.GetBytes(key)); + var vector = (byte[])keyBytes.Clone(); - for (int i = 0; i < 16; i++) + for (var i = 0; i < 16; i++) { iv[i] = vector[i]; } @@ -95,4 +97,4 @@ namespace ZelWiki.Security return streamReader.ReadToEnd(); } } -} +} \ No newline at end of file