diff --git a/FLocal.Common/BBCodes/AbstractLocalLink.cs b/FLocal.Common/BBCodes/AbstractLocalLink.cs index fd2810c..4dee507 100644 --- a/FLocal.Common/BBCodes/AbstractLocalLink.cs +++ b/FLocal.Common/BBCodes/AbstractLocalLink.cs @@ -16,11 +16,11 @@ namespace FLocal.Common.BBCodes { get; } - public override string Format(ITextFormatter formatter) { + public override string Format(IPostParsingContext context, ITextFormatter formatter) { var url = this.url; var name = this.Safe(url.title); if(this.Default != null) { - name = this.GetInnerHTML(formatter); + name = this.GetInnerHTML(context, formatter); } return string.Format("{1}", url.canonical, url.title); } diff --git a/FLocal.Common/BBCodes/B.cs b/FLocal.Common/BBCodes/B.cs index 5197f1c..c161191 100644 --- a/FLocal.Common/BBCodes/B.cs +++ b/FLocal.Common/BBCodes/B.cs @@ -11,8 +11,8 @@ namespace FLocal.Common.BBCodes { : base("b") { } - public override string Format(ITextFormatter formatter) { - return "" + this.GetInnerHTML(formatter) + ""; + public override string Format(IPostParsingContext context, ITextFormatter formatter) { + return "" + this.GetInnerHTML(context, formatter) + ""; } } diff --git a/FLocal.Common/BBCodes/Code.cs b/FLocal.Common/BBCodes/Code.cs index 02115ec..0201128 100644 --- a/FLocal.Common/BBCodes/Code.cs +++ b/FLocal.Common/BBCodes/Code.cs @@ -11,7 +11,7 @@ namespace FLocal.Common.BBCodes { : base("code") { } - public override string Format(ITextFormatter formatter) { + public override string Format(IPostParsingContext context, ITextFormatter formatter) { return "
" + System.Web.HttpUtility.HtmlEncode(this.InnerBBCode) + "

"; } diff --git a/FLocal.Common/BBCodes/ECode.cs b/FLocal.Common/BBCodes/ECode.cs index db8de6c..f272f9c 100644 --- a/FLocal.Common/BBCodes/ECode.cs +++ b/FLocal.Common/BBCodes/ECode.cs @@ -11,8 +11,8 @@ namespace FLocal.Common.BBCodes { : base("ecode") { } - public override string Format(ITextFormatter formatter) { - return this.GetInnerHTML(new BBCodeHtmlFormatter()); + public override string Format(IPostParsingContext context, ITextFormatter formatter) { + return this.GetInnerHTML(context, new BBCodeHtmlFormatter()); } } diff --git a/FLocal.Common/BBCodes/FUrl.cs b/FLocal.Common/BBCodes/FUrl.cs index dd77367..6404204 100644 --- a/FLocal.Common/BBCodes/FUrl.cs +++ b/FLocal.Common/BBCodes/FUrl.cs @@ -11,11 +11,11 @@ namespace FLocal.Common.BBCodes { : base("furl") { } - public override string Format(ITextFormatter formatter) { + public override string Format(IPostParsingContext context, ITextFormatter formatter) { string rawUrl = this.DefaultOrValue; string title = null; if(rawUrl.ToLower() != this.InnerText.ToLower()) { - title = this.GetInnerHTML(formatter); + title = this.GetInnerHTML(context, formatter); } return UrlProcessor.ProcessLink(rawUrl, title, false); } diff --git a/FLocal.Common/BBCodes/Font.cs b/FLocal.Common/BBCodes/Font.cs index 4cf6cfc..6540a6b 100644 --- a/FLocal.Common/BBCodes/Font.cs +++ b/FLocal.Common/BBCodes/Font.cs @@ -11,8 +11,8 @@ namespace FLocal.Common.BBCodes { : base("font") { } - public override string Format(ITextFormatter formatter) { - return "" + this.GetInnerHTML(formatter) + ""; + public override string Format(IPostParsingContext context, ITextFormatter formatter) { + return "" + this.GetInnerHTML(context, formatter) + ""; } } diff --git a/FLocal.Common/BBCodes/FontColor.cs b/FLocal.Common/BBCodes/FontColor.cs index 8031296..013a69f 100644 --- a/FLocal.Common/BBCodes/FontColor.cs +++ b/FLocal.Common/BBCodes/FontColor.cs @@ -11,8 +11,8 @@ namespace FLocal.Common.BBCodes { : base("color") { } - public override string Format(ITextFormatter formatter) { - return "" + this.GetInnerHTML(formatter) + ""; + public override string Format(IPostParsingContext context, ITextFormatter formatter) { + return "" + this.GetInnerHTML(context, formatter) + ""; } } diff --git a/FLocal.Common/BBCodes/FontSize.cs b/FLocal.Common/BBCodes/FontSize.cs index dec1ecb..3a22f77 100644 --- a/FLocal.Common/BBCodes/FontSize.cs +++ b/FLocal.Common/BBCodes/FontSize.cs @@ -11,8 +11,8 @@ namespace FLocal.Common.BBCodes { : base("size") { } - public override string Format(ITextFormatter formatter) { - return "" + this.GetInnerHTML(formatter) + ""; + public override string Format(IPostParsingContext context, ITextFormatter formatter) { + return "" + this.GetInnerHTML(context, formatter) + ""; } } diff --git a/FLocal.Common/BBCodes/Google.cs b/FLocal.Common/BBCodes/Google.cs index 10867ec..4181145 100644 --- a/FLocal.Common/BBCodes/Google.cs +++ b/FLocal.Common/BBCodes/Google.cs @@ -12,8 +12,8 @@ namespace FLocal.Common.BBCodes { : base("google") { } - public override string Format(ITextFormatter formatter) { - return "g:" + this.GetInnerHTML(formatter) + ""; + public override string Format(IPostParsingContext context, ITextFormatter formatter) { + return "g:" + this.GetInnerHTML(context, formatter) + ""; } } diff --git a/FLocal.Common/BBCodes/I.cs b/FLocal.Common/BBCodes/I.cs index 2241b5b..3c01ec3 100644 --- a/FLocal.Common/BBCodes/I.cs +++ b/FLocal.Common/BBCodes/I.cs @@ -11,8 +11,8 @@ namespace FLocal.Common.BBCodes { : base("i") { } - public override string Format(ITextFormatter formatter) { - return "" + this.GetInnerHTML(formatter) + ""; + public override string Format(IPostParsingContext context, ITextFormatter formatter) { + return "" + this.GetInnerHTML(context, formatter) + ""; } } diff --git a/FLocal.Common/BBCodes/Image.cs b/FLocal.Common/BBCodes/Image.cs index fc0562c..84afe24 100644 --- a/FLocal.Common/BBCodes/Image.cs +++ b/FLocal.Common/BBCodes/Image.cs @@ -10,7 +10,7 @@ namespace FLocal.Common.BBCodes { public Image() : base("image") { } - public override string Format(ITextFormatter formatter) { + public override string Format(IPostParsingContext context, ITextFormatter formatter) { var urlInfo = UrlProcessor.Process(this.InnerText); if (urlInfo.isLocal && urlInfo.relativeUrl.StartsWith("/user/upload/")) { return "" + urlInfo.relativeUrl + "" + urlInfo.relativeUrl + ""; diff --git a/FLocal.Common/BBCodes/List.cs b/FLocal.Common/BBCodes/List.cs index c59ec03..3ecec22 100644 --- a/FLocal.Common/BBCodes/List.cs +++ b/FLocal.Common/BBCodes/List.cs @@ -11,8 +11,8 @@ namespace FLocal.Common.BBCodes { : base("list") { } - public override string Format(ITextFormatter formatter) { - return "
    " + this.GetInnerHTML(formatter) + "
"; + public override string Format(IPostParsingContext context, ITextFormatter formatter) { + return "
    " + this.GetInnerHTML(context, formatter) + "
"; } } diff --git a/FLocal.Common/BBCodes/ListElem.cs b/FLocal.Common/BBCodes/ListElem.cs index 6e6f670..2ca38c9 100644 --- a/FLocal.Common/BBCodes/ListElem.cs +++ b/FLocal.Common/BBCodes/ListElem.cs @@ -11,7 +11,7 @@ namespace FLocal.Common.BBCodes { : base("*") { } - public override string Format(ITextFormatter formatter) { + public override string Format(IPostParsingContext context, ITextFormatter formatter) { // return "
  • " + this.GetInnerHTML(formatter) + "
  • "; return "
  • "; } diff --git a/FLocal.Common/BBCodes/Lurk.cs b/FLocal.Common/BBCodes/Lurk.cs index 6a9b17e..0f5ad47 100644 --- a/FLocal.Common/BBCodes/Lurk.cs +++ b/FLocal.Common/BBCodes/Lurk.cs @@ -12,8 +12,8 @@ namespace FLocal.Common.BBCodes { : base("lurk") { } - public override string Format(ITextFormatter formatter) { - return "l:" + this.GetInnerHTML(formatter) + ""; + public override string Format(IPostParsingContext context, ITextFormatter formatter) { + return "l:" + this.GetInnerHTML(context, formatter) + ""; } } diff --git a/FLocal.Common/BBCodes/Math.cs b/FLocal.Common/BBCodes/Math.cs index 9eef691..b66ae81 100644 --- a/FLocal.Common/BBCodes/Math.cs +++ b/FLocal.Common/BBCodes/Math.cs @@ -11,7 +11,7 @@ namespace FLocal.Common.BBCodes { : base("math") { } - public override string Format(ITextFormatter formatter) { + public override string Format(IPostParsingContext context, ITextFormatter formatter) { string tex = "$$" + this.InnerBBCode + "$$"; var upload = helpers.TexProcessor.getCompiled(tex); return "/Upload/Item/" + upload.id.ToString() + "/" + this.Safe(tex) + ""; diff --git a/FLocal.Common/BBCodes/Quote.cs b/FLocal.Common/BBCodes/Quote.cs index ed0f2fe..2ec759b 100644 --- a/FLocal.Common/BBCodes/Quote.cs +++ b/FLocal.Common/BBCodes/Quote.cs @@ -12,8 +12,8 @@ namespace FLocal.Common.BBCodes { : base("quote") { } - public override string Format(ITextFormatter formatter) { - string inner = this.GetInnerHTML(formatter).TrimHtml(); + public override string Format(IPostParsingContext context, ITextFormatter formatter) { + string inner = this.GetInnerHTML(context, formatter).TrimHtml(); if(inner == "") return ""; string marker = this.Default; if(marker == null) marker = "Quote:"; diff --git a/FLocal.Common/BBCodes/QuoteSkipper.cs b/FLocal.Common/BBCodes/QuoteSkipper.cs index 84f9b4c..a44cd58 100644 --- a/FLocal.Common/BBCodes/QuoteSkipper.cs +++ b/FLocal.Common/BBCodes/QuoteSkipper.cs @@ -10,7 +10,7 @@ namespace FLocal.Common.BBCodes { public QuoteSkipper() : base("quoteskipper") { } - public override string Format(ITextFormatter formatter) { + public override string Format(IPostParsingContext context, ITextFormatter formatter) { if(this.Name.ToLower() == "q" || this.Name.ToLower() == "quote") { return ""; } else if(this.Name.ToLower() == "code") { @@ -36,7 +36,7 @@ namespace FLocal.Common.BBCodes { } sb.Append("]"); if(this.RequireClosingTag) { - sb.Append(this.GetInnerHTML(formatter)); + sb.Append(this.GetInnerHTML(context, formatter)); sb.Append("[/"); sb.Append(name); sb.Append("]"); diff --git a/FLocal.Common/BBCodes/RuWiki.cs b/FLocal.Common/BBCodes/RuWiki.cs index 2bcc644..ffa2206 100644 --- a/FLocal.Common/BBCodes/RuWiki.cs +++ b/FLocal.Common/BBCodes/RuWiki.cs @@ -12,8 +12,8 @@ namespace FLocal.Common.BBCodes { : base("ruwiki") { } - public override string Format(ITextFormatter formatter) { - return "в:" + this.GetInnerHTML(formatter) + ""; + public override string Format(IPostParsingContext context, ITextFormatter formatter) { + return "в:" + this.GetInnerHTML(context, formatter) + ""; } } diff --git a/FLocal.Common/BBCodes/S.cs b/FLocal.Common/BBCodes/S.cs index 76796cb..229c9cd 100644 --- a/FLocal.Common/BBCodes/S.cs +++ b/FLocal.Common/BBCodes/S.cs @@ -11,8 +11,8 @@ namespace FLocal.Common.BBCodes { : base("s") { } - public override string Format(ITextFormatter formatter) { - return "" + this.GetInnerHTML(formatter) + ""; + public override string Format(IPostParsingContext context, ITextFormatter formatter) { + return "" + this.GetInnerHTML(context, formatter) + ""; } } diff --git a/FLocal.Common/BBCodes/Spoiler.cs b/FLocal.Common/BBCodes/Spoiler.cs index 68519d7..ab4850b 100644 --- a/FLocal.Common/BBCodes/Spoiler.cs +++ b/FLocal.Common/BBCodes/Spoiler.cs @@ -11,10 +11,10 @@ namespace FLocal.Common.BBCodes { : base("spoiler") { } - public override string Format(ITextFormatter formatter) { + public override string Format(IPostParsingContext context, ITextFormatter formatter) { string marker = this.Default; if(marker == null) marker = "Spoiler"; - return "
    " + marker + "
    " + this.GetInnerHTML(formatter).Trim() + "


    "; + return "
    " + marker + "
    " + this.GetInnerHTML(context, formatter).Trim() + "


    "; } } diff --git a/FLocal.Common/BBCodes/Tex.cs b/FLocal.Common/BBCodes/Tex.cs index 2a94843..27f3bed 100644 --- a/FLocal.Common/BBCodes/Tex.cs +++ b/FLocal.Common/BBCodes/Tex.cs @@ -11,7 +11,7 @@ namespace FLocal.Common.BBCodes { : base("tex") { } - public override string Format(ITextFormatter formatter) { + public override string Format(IPostParsingContext context, ITextFormatter formatter) { string tex = this.InnerBBCode; var upload = helpers.TexProcessor.getCompiled(tex); return "/Upload/Item/" + upload.id.ToString() + "/" + this.Safe(tex) + ""; diff --git a/FLocal.Common/BBCodes/U.cs b/FLocal.Common/BBCodes/U.cs index 8548211..4fa7058 100644 --- a/FLocal.Common/BBCodes/U.cs +++ b/FLocal.Common/BBCodes/U.cs @@ -11,8 +11,8 @@ namespace FLocal.Common.BBCodes { : base("u") { } - public override string Format(ITextFormatter formatter) { - return "" + this.GetInnerHTML(formatter) + ""; + public override string Format(IPostParsingContext context, ITextFormatter formatter) { + return "" + this.GetInnerHTML(context, formatter) + ""; } } diff --git a/FLocal.Common/BBCodes/UploadImage.cs b/FLocal.Common/BBCodes/UploadImage.cs index 5688dcd..5ee0db3 100644 --- a/FLocal.Common/BBCodes/UploadImage.cs +++ b/FLocal.Common/BBCodes/UploadImage.cs @@ -11,7 +11,7 @@ namespace FLocal.Common.BBCodes { : base("uploadimage") { } - public override string Format(ITextFormatter formatter) { + public override string Format(IPostParsingContext context, ITextFormatter formatter) { var upload = dataobjects.Upload.LoadById(int.Parse(this.InnerText)); var name = upload.filename; return "/Upload/Item/" + upload.id.ToString() + "/" + this.Safe(upload.filename) + ""; diff --git a/FLocal.Common/BBCodes/UploadLink.cs b/FLocal.Common/BBCodes/UploadLink.cs index cb2feaf..00fe190 100644 --- a/FLocal.Common/BBCodes/UploadLink.cs +++ b/FLocal.Common/BBCodes/UploadLink.cs @@ -11,11 +11,11 @@ namespace FLocal.Common.BBCodes { : base("uploadlink") { } - public override string Format(ITextFormatter formatter) { + public override string Format(IPostParsingContext context, ITextFormatter formatter) { var upload = dataobjects.Upload.LoadById(int.Parse(this.DefaultOrValue)); var name = this.Safe(upload.filename); if(this.Default != null) { - name = this.GetInnerHTML(formatter); + name = this.GetInnerHTML(context, formatter); } return "" + name + ""; } diff --git a/FLocal.Common/BBCodes/Url.cs b/FLocal.Common/BBCodes/Url.cs index e879572..45dda97 100644 --- a/FLocal.Common/BBCodes/Url.cs +++ b/FLocal.Common/BBCodes/Url.cs @@ -11,11 +11,11 @@ namespace FLocal.Common.BBCodes { : base("url") { } - public override string Format(ITextFormatter formatter) { + public override string Format(IPostParsingContext context, ITextFormatter formatter) { string rawUrl = this.DefaultOrValue; string title = null; if(rawUrl.ToLower() != this.InnerText.ToLower()) { - title = this.GetInnerHTML(formatter); + title = this.GetInnerHTML(context, formatter); } return UrlProcessor.ProcessLink(rawUrl, title, true); } diff --git a/FLocal.Common/BBCodes/User.cs b/FLocal.Common/BBCodes/User.cs index cb1b72d..e34fed7 100644 --- a/FLocal.Common/BBCodes/User.cs +++ b/FLocal.Common/BBCodes/User.cs @@ -11,10 +11,8 @@ namespace FLocal.Common.BBCodes { : base("user") { } - public override string Format(ITextFormatter formatter) { - var user = dataobjects.User.LoadByName(this.DefaultOrValue); - var url = new URL.users.user.Info(user.id.ToString(), null); - return String.Format("{2}", this.Safe(user.userGroup.name), url.canonical, this.Safe(user.name)); + public override string Format(IPostParsingContext context, ITextFormatter formatter) { + return UserMentionProcessor.ProcessUserMention(context, this.DefaultOrValue); } } diff --git a/FLocal.Common/BBCodes/Wiki.cs b/FLocal.Common/BBCodes/Wiki.cs index 28be404..5143c47 100644 --- a/FLocal.Common/BBCodes/Wiki.cs +++ b/FLocal.Common/BBCodes/Wiki.cs @@ -12,8 +12,8 @@ namespace FLocal.Common.BBCodes { : base("wiki") { } - public override string Format(ITextFormatter formatter) { - return "w:" + this.GetInnerHTML(formatter) + ""; + public override string Format(IPostParsingContext context, ITextFormatter formatter) { + return "w:" + this.GetInnerHTML(context, formatter) + ""; } } diff --git a/FLocal.Common/BBCodes/helpers/BBCode.cs b/FLocal.Common/BBCodes/helpers/BBCode.cs index 82a0e15..4328aae 100644 --- a/FLocal.Common/BBCodes/helpers/BBCode.cs +++ b/FLocal.Common/BBCodes/helpers/BBCode.cs @@ -2,18 +2,19 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using FLocal.Common.helpers; namespace FLocal.Common.BBCodes { - abstract class BBCode : PJonDevelopment.BBCode.BBCodeElement { + abstract class BBCode : PJonDevelopment.BBCode.BBCodeElement { public BBCode(string name) : base(name) { } - protected string GetInnerHTML(PJonDevelopment.BBCode.ITextFormatter formatter) { + protected string GetInnerHTML(IPostParsingContext context, PJonDevelopment.BBCode.ITextFormatter formatter) { StringBuilder builder = new StringBuilder(); foreach (var node in this.Nodes) { - builder.Append(node.Format(formatter)); + builder.Append(node.Format(context, formatter)); } return builder.ToString(); } diff --git a/FLocal.Common/BBCodes/helpers/IPostParsingContext.cs b/FLocal.Common/BBCodes/helpers/IPostParsingContext.cs new file mode 100644 index 0000000..7a3bf3b --- /dev/null +++ b/FLocal.Common/BBCodes/helpers/IPostParsingContext.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace FLocal.Common.BBCodes { + public interface IPostParsingContext { + void OnUserMention(dataobjects.User user); + } +} diff --git a/FLocal.Common/BBCodes/helpers/UserMentionProcessor.cs b/FLocal.Common/BBCodes/helpers/UserMentionProcessor.cs new file mode 100644 index 0000000..1933365 --- /dev/null +++ b/FLocal.Common/BBCodes/helpers/UserMentionProcessor.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace FLocal.Common.BBCodes { + class UserMentionProcessor { + + private static string Safe(string str) { + return System.Web.HttpUtility.HtmlEncode(str); + } + + public static string ProcessUserMention(IPostParsingContext context, string username) { + var user = dataobjects.User.LoadByName(username); + context.OnUserMention(user); + var url = new URL.users.user.Info(user.id.ToString(), null); + return String.Format("{2}", Safe(user.userGroup.name), url.canonical, Safe(user.name)); + } + + } +} diff --git a/FLocal.Common/FLocal.Common.csproj b/FLocal.Common/FLocal.Common.csproj index ce6788c..b47cde2 100644 --- a/FLocal.Common/FLocal.Common.csproj +++ b/FLocal.Common/FLocal.Common.csproj @@ -68,6 +68,7 @@ + @@ -100,6 +101,7 @@ + @@ -122,6 +124,8 @@ + + @@ -179,7 +183,7 @@ - + diff --git a/FLocal.Common/UBBParser.cs b/FLocal.Common/UBBParser.cs index c465e28..2320bf3 100644 --- a/FLocal.Common/UBBParser.cs +++ b/FLocal.Common/UBBParser.cs @@ -7,26 +7,27 @@ using System.Text.RegularExpressions; using PJonDevelopment.BBCode; using System.IO; using Web.Core; +using FLocal.Common.helpers; namespace FLocal.Common { public static class UBBParser { private class BBParserGateway { - private class SimpleFormatter : ITextFormatter { + private class SimpleFormatter : ITextFormatter { public static readonly SimpleFormatter instance = new SimpleFormatter(); private SimpleFormatter() { } - public string Format(string source) { + public string Format(BBCodes.IPostParsingContext context, string source) { return source; } } - private class TextFormatter : ITextFormatter { + private class TextFormatter : ITextFormatter { public static readonly TextFormatter instance = new TextFormatter(); @@ -68,6 +69,14 @@ namespace FLocal.Common { return BBCodes.UrlProcessor.ProcessLink(url, null, true) + remainder; } + private static readonly Regex USERS_MATCHER = new Regex("(?^|\\W)@(?\\w+)(?\\W|$)", RegexOptions.Singleline | RegexOptions.Compiled); + private static string USERS_REPLACE(BBCodes.IPostParsingContext context, Match match) { + string start = match.Groups["start"].Value; + string username = match.Groups["username"].Value; + string end = match.Groups["end"].Value; + return start + BBCodes.UserMentionProcessor.ProcessUserMention(context, username) + end; + } + private static readonly Dictionary TYPOGRAPHICS = new Dictionary { { new Regex("(?<=\\s)--?(?=\\s)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline), match => "–" }, { new Regex("(?<=\\s)---(?=\\s)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline), match => "—" }, @@ -85,15 +94,16 @@ namespace FLocal.Common { { new Regex("<-", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline), match => "←" }, }; - private ITextFormatter inner; + private ITextFormatter inner; private TextFormatter() { - this.inner = new BBCodeHtmlFormatter(); + this.inner = new BBCodeHtmlFormatter(); } - public string Format(string source) { - string result = this.inner.Format(source).Replace(" ", " "); + public string Format(BBCodes.IPostParsingContext context, string source) { + string result = this.inner.Format(context, source).Replace(" ", " "); result = LINKS_MATCHER.Replace(result, LINKS_REPLACE); + result = USERS_MATCHER.Replace(result, match => USERS_REPLACE(context, match)); foreach(var smile in SMILEYS_DATA) { result = smile.Key.Replace(result, smile.Value); } @@ -108,14 +118,14 @@ namespace FLocal.Common { public static readonly BBParserGateway instance = new BBParserGateway(); - private BBCodeParser parser; - private ITextFormatter formatter; + private BBCodeParser parser; + private ITextFormatter formatter; - private BBCodeParser quotesParser; - private ITextFormatter simpleFormatter; + private BBCodeParser quotesParser; + private ITextFormatter simpleFormatter; private BBParserGateway() { - this.parser = new BBCodeParser(); + this.parser = new BBCodeParser(); this.parser.ElementTypes.Add("b", typeof(BBCodes.B), true); this.parser.ElementTypes.Add("code", typeof(BBCodes.Code), true); this.parser.ElementTypes.Add("ecode", typeof(BBCodes.ECode), true); @@ -145,30 +155,37 @@ namespace FLocal.Common { this.parser.ElementTypes.Add("wiki", typeof(BBCodes.Wiki), true); this.parser.ElementTypes.Add("ruwiki", typeof(BBCodes.RuWiki), true); this.formatter = TextFormatter.instance; - - this.quotesParser = new BBCodeParser(); + + this.quotesParser = new BBCodeParser(); foreach(var elementType in this.parser.ElementTypes) { this.quotesParser.ElementTypes.Add(elementType.Key, typeof(BBCodes.QuoteSkipper), elementType.Value.RequireClosingTag); } this.simpleFormatter = SimpleFormatter.instance; } - public string Parse(string input) { - string result = this.parser.Parse(input).Format(this.formatter); + public string Parse(BBCodes.IPostParsingContext context, string input) { + string result = this.parser.Parse(input).Format(context, this.formatter); if(result.EndsWith("
    ")) result = result.Substring(0, result.Length - 5); return result; } public string ParseQuote(string input) { - string result = this.quotesParser.Parse(input).Format(this.simpleFormatter); + string result = this.quotesParser.Parse(input).Format(CreateStubContext(), this.simpleFormatter); return result; } } + private static BBCodes.IPostParsingContext CreateStubContext() { + return new DelegatePostParsingContext(user => {}); + } + + public static string UBBToIntermediate(BBCodes.IPostParsingContext context, string UBB) { + return BBParserGateway.instance.Parse(context, UBB); + } + public static string UBBToIntermediate(string UBB) { - //return HttpUtility.HtmlEncode(UBB).Replace(Util.EOL, "
    " + Util.EOL); - return BBParserGateway.instance.Parse(UBB); + return UBBToIntermediate(CreateStubContext(), UBB); } public static string ShallerToUBB(string shaller) { diff --git a/FLocal.Common/URL/UrlManager.cs b/FLocal.Common/URL/UrlManager.cs index b01aa7d..f5c3b8f 100644 --- a/FLocal.Common/URL/UrlManager.cs +++ b/FLocal.Common/URL/UrlManager.cs @@ -305,7 +305,8 @@ namespace FLocal.Common.URL { case "posts": return new users.user.Posts(requestParts[2], GetRemainder(requestParts, 4)); case "replies": - return new users.user.Replies(requestParts[2], GetRemainder(requestParts, 4)); + case "mentions": + return new users.user.Mentions(requestParts[2], GetRemainder(requestParts, 4)); case "threads": return new users.user.Threads(requestParts[2], GetRemainder(requestParts, 4)); default: diff --git a/FLocal.Common/URL/users/user/Replies.cs b/FLocal.Common/URL/users/user/Mentions.cs similarity index 51% rename from FLocal.Common/URL/users/user/Replies.cs rename to FLocal.Common/URL/users/user/Mentions.cs index f4cf083..b7f74c3 100644 --- a/FLocal.Common/URL/users/user/Replies.cs +++ b/FLocal.Common/URL/users/user/Mentions.cs @@ -4,14 +4,14 @@ using System.Linq; using System.Text; namespace FLocal.Common.URL.users.user { - public class Replies : Abstract { + public class Mentions : Abstract { - public Replies(string userId, string remainder) : base(userId, remainder) { + public Mentions(string userId, string remainder) : base(userId, remainder) { } protected override string _canonical { get { - return "/Users/User/" + this.user.id + "/Replies/"; + return "/Users/User/" + this.user.id + "/Mentions/"; } } } diff --git a/FLocal.Common/actions/ChangeSet.cs b/FLocal.Common/actions/ChangeSet.cs index 2503344..f2ba2a1 100644 --- a/FLocal.Common/actions/ChangeSet.cs +++ b/FLocal.Common/actions/ChangeSet.cs @@ -41,6 +41,7 @@ namespace FLocal.Common.actions { dataobjects.PunishmentLayerChange.TableSpec.TABLE, dataobjects.Restriction.TableSpec.TABLE, dataobjects.TexImage.TableSpec.TABLE, + dataobjects.Mention.TableSpec.TABLE, dataobjects.Session.TableSpec.TABLE, } ); diff --git a/FLocal.Common/dataobjects/Mention.cs b/FLocal.Common/dataobjects/Mention.cs new file mode 100644 index 0000000..e278fa6 --- /dev/null +++ b/FLocal.Common/dataobjects/Mention.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; +using Web.Core; +using Web.Core.DB; +using Web.Core.DB.conditions; +using FLocal.Common.actions; + +namespace FLocal.Common.dataobjects { + public class Mention : SqlObject { + + public class TableSpec : ISqlObjectTableSpec { + public const string TABLE = "Mentions"; + public const string FIELD_ID = "Id"; + public const string FIELD_MENTIONEDUSERID = "MentionedUserId"; + public const string FIELD_POSTID = "PostId"; + public const string FIELD_DATE = "Date"; + public static readonly TableSpec instance = new TableSpec(); + public string name { get { return TABLE; } } + public string idName { get { return FIELD_ID; } } + public void refreshSqlObject(int id) { Refresh(id); } + } + + protected override ISqlObjectTableSpec table { get { return TableSpec.instance; } } + + private int _mentionedUserId; + public int mentionedUserId { + get { + this.LoadIfNotLoaded(); + return this._mentionedUserId; + } + } + public User mentionedUser { + get { + return User.LoadById(this.mentionedUserId); + } + } + + private int _postId; + public int postId { + get { + this.LoadIfNotLoaded(); + return this._postId; + } + } + public Post post { + get { + return Post.LoadById(this.postId); + } + } + + private DateTime _date; + public DateTime date { + get { + this.LoadIfNotLoaded(); + return this._date; + } + } + + protected override void doFromHash(Dictionary data) { + this._mentionedUserId = int.Parse(data[TableSpec.FIELD_MENTIONEDUSERID]); + this._postId = int.Parse(data[TableSpec.FIELD_POSTID]); + this._date = Util.ParseDateTimeFromTimestamp(data[TableSpec.FIELD_DATE]).Value; + } + + } +} diff --git a/FLocal.Common/dataobjects/Post.cs b/FLocal.Common/dataobjects/Post.cs index 3bbd102..dabc529 100644 --- a/FLocal.Common/dataobjects/Post.cs +++ b/FLocal.Common/dataobjects/Post.cs @@ -8,6 +8,7 @@ using Web.Core.DB; using Web.Core.DB.conditions; using FLocal.Common; using FLocal.Common.actions; +using FLocal.Common.helpers; namespace FLocal.Common.dataobjects { public class Post : SqlObject { @@ -330,12 +331,24 @@ namespace FLocal.Common.dataobjects { actualLayer = this.layer; } lock(this.Edit_locker) { + + DateTime date = DateTime.Now; + + HashSet newMentionedUsersIds = new HashSet(); + if(parentPost != null && parentPost.poster.id != poster.id) { + newMentionedUsersIds.Add(parentPost.poster.id); + } + string newBodyIntermediate = UBBParser.UBBToIntermediate( + new DelegatePostParsingContext(mentionedUser => newMentionedUsersIds.Add(mentionedUser.id)), + newBody + ); + List changes = new List { new InsertChange( Revision.TableSpec.instance, new Dictionary { { Revision.TableSpec.FIELD_POSTID, new ScalarFieldValue(this.id.ToString()) }, - { Revision.TableSpec.FIELD_CHANGEDATE, new ScalarFieldValue(DateTime.Now.ToUTCString()) }, + { Revision.TableSpec.FIELD_CHANGEDATE, new ScalarFieldValue(date.ToUTCString()) }, { Revision.TableSpec.FIELD_TITLE, new ScalarFieldValue(newTitle) }, { Revision.TableSpec.FIELD_BODY, new ScalarFieldValue(newBody) }, { Revision.TableSpec.FIELD_NUMBER, new ScalarFieldValue((this.revision + 1).ToString()) }, @@ -345,8 +358,8 @@ namespace FLocal.Common.dataobjects { TableSpec.instance, new Dictionary { { TableSpec.FIELD_TITLE, new ScalarFieldValue(newTitle) }, - { TableSpec.FIELD_BODY, new ScalarFieldValue(UBBParser.UBBToIntermediate(newBody)) }, - { TableSpec.FIELD_LASTCHANGEDATE, new ScalarFieldValue(DateTime.Now.ToUTCString()) }, + { TableSpec.FIELD_BODY, new ScalarFieldValue(newBodyIntermediate) }, + { TableSpec.FIELD_LASTCHANGEDATE, new ScalarFieldValue(date.ToUTCString()) }, { TableSpec.FIELD_REVISION, new IncrementFieldValue() }, { TableSpec.FIELD_LAYERID, new ScalarFieldValue(actualLayer.id.ToString()) }, }, @@ -364,10 +377,37 @@ namespace FLocal.Common.dataobjects { ) ); } + foreach(var mentionedUserId in newMentionedUsersIds.Except(this.mentionedUsersIds)) { + changes.Add( + new InsertChange( + Mention.TableSpec.instance, + new Dictionary { + { Mention.TableSpec.FIELD_MENTIONEDUSERID, new ScalarFieldValue(mentionedUserId.ToString()) }, + { Mention.TableSpec.FIELD_POSTID, new ScalarFieldValue(this.id.ToString()) }, + { Mention.TableSpec.FIELD_DATE, new ScalarFieldValue(date.ToUTCString()) }, + } + ) + ); + } ChangeSetUtil.ApplyChanges(changes.ToArray()); } } + private IEnumerable mentionedUsersIds { + get { + return from stringId in Config.instance.mainConnection.LoadIdsByConditions( + Mention.TableSpec.instance, + new ComparisonCondition( + Mention.TableSpec.instance.getColumnSpec(Mention.TableSpec.FIELD_POSTID), + ComparisonType.EQUAL, + this.id.ToString() + ), + Diapasone.unlimited, + Mention.TableSpec.instance.getColumnSpec(Mention.TableSpec.FIELD_MENTIONEDUSERID) + ) select int.Parse(stringId); + } + } + private IEnumerable subPosts { get { return Post.LoadByIds( diff --git a/FLocal.Common/dataobjects/Thread.cs b/FLocal.Common/dataobjects/Thread.cs index 23d9fc5..d07a26f 100644 --- a/FLocal.Common/dataobjects/Thread.cs +++ b/FLocal.Common/dataobjects/Thread.cs @@ -7,6 +7,7 @@ using Web.Core; using Web.Core.DB; using Web.Core.DB.conditions; using FLocal.Common.actions; +using FLocal.Common.helpers; namespace FLocal.Common.dataobjects { public class Thread : SqlObject { @@ -411,12 +412,19 @@ namespace FLocal.Common.dataobjects { string parentPostId = null; if(parentPost != null) parentPostId = parentPost.id.ToString(); bool isNewThread = (parentPost == null); + HashSet mentionedUsersIds = new HashSet(); + if(parentPost != null && parentPost.poster.id != poster.id) { + mentionedUsersIds.Add(parentPost.poster.id); + } string bodyIntermediate; if(forcedPostId.HasValue) { //dirty hack bodyIntermediate = body; } else { - bodyIntermediate = UBBParser.UBBToIntermediate(body); + bodyIntermediate = UBBParser.UBBToIntermediate( + new DelegatePostParsingContext(mentionedUser => mentionedUsersIds.Add(mentionedUser.id)), + body + ); } var postInsertData = new Dictionary { { Post.TableSpec.FIELD_THREADID, new ScalarFieldValue(threadId.ToString()) }, @@ -484,6 +492,18 @@ namespace FLocal.Common.dataobjects { changes.Add(threadUpdate); changes.Add(userUpdate); + foreach(var mentionedUserId in mentionedUsersIds) { + changes.Add( + new InsertChange( + Mention.TableSpec.instance, + new Dictionary { + { Mention.TableSpec.FIELD_MENTIONEDUSERID, new ScalarFieldValue(mentionedUserId.ToString()) }, + { Mention.TableSpec.FIELD_POSTID, new ReferenceFieldValue(postInsert) }, + { Mention.TableSpec.FIELD_DATE, new ScalarFieldValue(date.ToUTCString()) }, + } + ) + ); + } Dictionary boardData = new Dictionary { { Board.TableSpec.FIELD_TOTALPOSTS, new IncrementFieldValue() }, diff --git a/FLocal.Common/dataobjects/User.cs b/FLocal.Common/dataobjects/User.cs index 9c1e993..c8c41ed 100644 --- a/FLocal.Common/dataobjects/User.cs +++ b/FLocal.Common/dataobjects/User.cs @@ -253,38 +253,18 @@ namespace FLocal.Common.dataobjects { ); } - public IEnumerable getReplies(Diapasone diapasone, bool isAscending) { - JoinSpec parent = new JoinSpec( - Post.TableSpec.instance.getColumnSpec(Post.TableSpec.FIELD_PARENTPOSTID), - Post.TableSpec.instance, - "parent" - ); + public IEnumerable getMentions(Diapasone diapasone, bool isAscending) { return Post.LoadByIds( from stringId in Config.instance.mainConnection.LoadIdsByConditions( - Post.TableSpec.instance, - new ComplexCondition( - ConditionsJoinType.AND, - new ComparisonCondition( - parent.additionalTable.getColumnSpec(Post.TableSpec.FIELD_POSTERID), - ComparisonType.EQUAL, - this.id.ToString() - ), - new ComparisonCondition( - Post.TableSpec.instance.getColumnSpec(Post.TableSpec.FIELD_POSTERID), - ComparisonType.NOTEQUAL, - this.id.ToString() - ) + Mention.TableSpec.instance, + new ComparisonCondition( + Mention.TableSpec.instance.getColumnSpec(Mention.TableSpec.FIELD_MENTIONEDUSERID), + ComparisonType.EQUAL, + this.id.ToString() ), diapasone, - new JoinSpec[] { - parent - }, - new SortSpec[] { - new SortSpec( - Post.TableSpec.instance.getIdSpec(), - isAscending - ), - } + Mention.TableSpec.instance.getColumnSpec(Mention.TableSpec.FIELD_POSTID), + new SortSpec(Mention.TableSpec.instance.getIdSpec(), isAscending) ) select int.Parse(stringId) ); } diff --git a/FLocal.Common/helpers/DelegatePostParsingContext.cs b/FLocal.Common/helpers/DelegatePostParsingContext.cs new file mode 100644 index 0000000..4036e6f --- /dev/null +++ b/FLocal.Common/helpers/DelegatePostParsingContext.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using FLocal.Common.dataobjects; + +namespace FLocal.Common.helpers { + class DelegatePostParsingContext : BBCodes.IPostParsingContext { + + private readonly Action onUserMention; + + public DelegatePostParsingContext(Action onUserMention) { + this.onUserMention = onUserMention; + } + + #region IPostParsingContext Members + + public void OnUserMention(User user) { + this.onUserMention(user); + } + + #endregion + + } +} diff --git a/FLocal.IISHandler/HandlersFactory.cs b/FLocal.IISHandler/HandlersFactory.cs index e54b185..54096fe 100644 --- a/FLocal.IISHandler/HandlersFactory.cs +++ b/FLocal.IISHandler/HandlersFactory.cs @@ -51,7 +51,7 @@ namespace FLocal.IISHandler { { typeof(URL.users.user.Info), CreateHandler }, { typeof(URL.users.user.PollsParticipated), CreateHandler }, { typeof(URL.users.user.Posts), CreateHandler }, - { typeof(URL.users.user.Replies), CreateHandler }, + { typeof(URL.users.user.Mentions), CreateHandler }, { typeof(URL.users.user.Threads), CreateHandler }, }; diff --git a/FLocal.IISHandler/handlers/response/UserRepliesHandler.cs b/FLocal.IISHandler/handlers/response/UserRepliesHandler.cs index e353641..3a2f8a8 100644 --- a/FLocal.IISHandler/handlers/response/UserRepliesHandler.cs +++ b/FLocal.IISHandler/handlers/response/UserRepliesHandler.cs @@ -12,7 +12,7 @@ using Web.Core.DB.conditions; namespace FLocal.IISHandler.handlers.response { - class UserRepliesHandler : AbstractUserGetHandler { + class UserRepliesHandler : AbstractUserGetHandler { override protected string templateName { get { @@ -22,7 +22,7 @@ namespace FLocal.IISHandler.handlers.response { override protected IEnumerable getUserSpecificData(WebContext context, User user) { PageOuter pageOuter = PageOuter.createFromUrl(this.url, context.userSettings.postsPerPage); - IEnumerable posts = user.getReplies(pageOuter, pageOuter.descendingDirection); + IEnumerable posts = user.getMentions(pageOuter, pageOuter.descendingDirection); return new XElement[] { user.exportToXmlForViewing(context), diff --git a/FLocal.Patcher.Common/FLocal.Patcher.Common.csproj b/FLocal.Patcher.Common/FLocal.Patcher.Common.csproj index 3c429df..7693f53 100644 --- a/FLocal.Patcher.Common/FLocal.Patcher.Common.csproj +++ b/FLocal.Patcher.Common/FLocal.Patcher.Common.csproj @@ -69,6 +69,9 @@ + + +