/// <summary> /// Отрисовывает переданный текстовой контент в виде Wiki /// </summary> /// <param name="usage"></param> /// <param name="page"></param> /// <param name="context"></param> /// <returns></returns> public string ToHTML(string usage, WikiPage page, IMvcContext context) { if (string.IsNullOrWhiteSpace(page.Text)) return ""; var lines = Preprocess(usage, page.Text, context); IList<string> processed = new List<string>(); ProcessHTML(lines, processed, usage, page, context); return string.Join("\n",processed.ToArray()).Replace("__BLOCK__", "[["); }
private void RenderStandalone(WikiPage page, string usage, bool onserver, IMvcContext context) { if (onserver) { RenderStandaloneServerProcessedPage(page,usage,context); } else { RenderStandaloneClientProcessedPage(page,usage,context); } }
/// <summary> /// Создать дескриптор на основе страницы /// </summary> /// <param name="page"></param> public WikiObjectDescriptor(WikiPage page) { Code = page.Code; Type = WikiObjectType.Page; ContentType = "text/wiki"; Size = -1; Name = page.Title; LastWriteTime = page.LastWriteTime; }
private void Render(WikiPage page, string usage, bool standalone, bool onserver,IMvcContext context) { context.ContentType = MimeHelper.HTML; if (standalone) { RenderStandalone(page, usage, onserver,context); } else { RenderEmbeded(page, usage, context); } }
public void CanSaveWikiPageWithCreateVersion() { _storage.Database.Drop(); var wikiPage = new WikiPage { Code = "test", Editor = "remalloc", Existed = true, LastWriteTime = DateTime.Now, Owner = "remalloc", Text = "some text", Title = "fgfgdfgd" }; _storage.SetApplication(_app); _storage.Save(wikiPage); // сохраним тестовую страницу var t = _storage.Get("test").FirstOrDefault(); // получим её Assert.NotNull(t); Assert.AreEqual("some text", t.Text); // и проверим, что текст нормальный t.Text = "Test2"; // слегка изменим текст _storage.Save(t); // и сохраним var r = _storage.Get("test").FirstOrDefault(); // снова получим эту страницу из базы Assert.NotNull(r); Assert.AreEqual("Test2", r.Text); // и убедимся, что всё сохранятеся нормально и идентификатор не перебивается /* на этом этапе мы убедились, что весь базовый старый функционал работает нормально, ничего не сломалось */ /* а теперь убедимся, что работает версионность */ var e = (MongoDbWikiVersionCreateResult)_storage.CreateVersion("test", "test commit"); // теперь создадим тестовую версию Assert.NotNull(e); var h = _storage.Get("test").FirstOrDefault(); // теперь получим страницу из базы Assert.NotNull(h); Assert.AreEqual("Test2", h.Text); // и проверим, что текст не изменился var f = _storage.GetWikiPageByVersion("test", e.Version); // и в нашей версии-бэкапе всё точно так же как и в текущей копии Assert.NotNull(f); Assert.AreEqual(h.Text, f.Text); h.Text = "Some new text"; // измением слегка текст _storage.Save(h); // и сохраним var y =_storage.Get(h.Code).FirstOrDefault(); // вытащим из базы Assert.NotNull(y); Assert.AreEqual("Some new text", y.Text); // и проверим, что всё хорошо.\ /* теперь проверим, что последнее изменение не слилось в бэкапы */ var c = _storage.GetWikiPageByVersion("test", e.Version); var u = _storage.Get("test").FirstOrDefault(); Assert.AreNotEqual(c.Text, u.Text); }
/// <summary> /// /// </summary> /// <param name="document"></param> /// <returns></returns> public WikiPage FromHistory(BsonDocument document) { var result = new WikiPage { Code = document["code"].AsString, Text = document["text"].AsString, Owner = document["owner"].AsString, Editor = document["editor"].AsString, Title = document["title"].AsString, Published = document["published"].ToUniversalTime(), Version = document["version"].AsString, Existed = true }; foreach (var e in document) { if (IsProperty(e.Name)) { result.Propeties[e.Name] = e.Value.AsString; } } return result; }
private void ProcessHTML(string[] lines, IList<string> processed, string usage, WikiPage page, IMvcContext context) { bool codeblock = false; bool nowikiblock = false; bool table = false; bool ishead = false; for (var idx = 0; idx < lines.Length; idx++) { var curline = lines[idx]; if (string.IsNullOrWhiteSpace(curline)) continue; if (CheckoutCodeBlock(processed, usage, page, context, curline, ref codeblock)) continue; //WIKI IGNORANCE SUPPORT WITH BLOCK AND INLINE if (CheckoutNoWikiBlock(processed, curline, ref nowikiblock)) continue; if (CheckoutTable(processed, usage, page, context, curline, ref table, ref ishead)) continue; if (CheckoutSampleBlock(processed, curline)) continue; if (CheckoutPageDelimiter(processed, curline)) continue; var defaultProcessed = ProcessDefault(curline, usage, page, context); if (!string.IsNullOrWhiteSpace(defaultProcessed)) { processed.Add(defaultProcessed); } } }
private string ProcessDefault(string curline, string usage, WikiPage page, IMvcContext context) { // BY LINE IGNORE if (curline.StartsWith("!")){ //must ignore any wiki syntax this row curline = curline.Substring(1); return curline; } ///////////////////////////////////////////////////// // LINE BREAK SUPPORT if (curline == "[BR]") { return "<br/>"; } curline = ProcessInline(curline,usage,page,context); // references curline = ProcessReferences(curline, usage, page, context); curline = ProcessLine(curline, usage, page, context); return curline; }
private WikiPage GetWikiPage(IMvcContext context) { WikiPage page = null; if (context.ActionResult is WikiPage) { page = context.ActionResult as WikiPage; } else if (context.ActionResult is WikiPage[]) { page = (context.ActionResult as WikiPage[])[0]; } else if (context.ActionResult is string) { page = new WikiPage {Text = context.ActionResult as string}; } else { var code = context.Get("code", ""); if (!string.IsNullOrWhiteSpace(code)) { page = WikiSource.Get(code).First(); } else { throw new Exception("cannot determine wiki to be rendered"); } } return page; }
/// <summary> /// /// </summary> /// <param name="document"></param> /// <returns></returns> public WikiPage FromMain(BsonDocument document) { BsonValue locker; document.TryGetValue("locker", out locker); var result = new WikiPage { Code = document["_id"].AsString, Text = document["text"].AsString, Owner = document["owner"].AsString, Editor = document["editor"].AsString, Title = document["title"].AsString, LastWriteTime = document["ver"].ToUniversalTime(), Locker = (locker != null) ? (locker.AsString) : (""), Existed = true }; foreach (var e in document) { if (IsProperty(e.Name)) { result.Propeties[e.Name] = e.Value is BsonDocument? e.Value.ToString() : e.Value.AsString; } } return result; }
private void RenderEmbeded(WikiPage page, string usage, IMvcContext context) { context.Output.Write(WikiSerializer.ToHTML(usage,page,context)); }
private void RenderStandaloneServerProcessedPage(WikiPage page, string usage, IMvcContext context) { context.Output.Write( string.Format(StandaloneServerBasedTemplate, ("/"+Application.ApplicationName+"/"+DefaultWikiCssReference).Replace("//","/"), page.Title, page.Editor, page.LastWriteTime.ToString("dd.MM.yyyy hh:mm:ss"), WikiSerializer.ToHTML(usage,page,context))); }
private string ProcessInline(string curline, string usage, WikiPage page, object context) { curline = Regex.Replace(curline, @"\*\*\*([\s\S]+?)\*\*\*", "<strong>$1</strong>", RegexOptions.Compiled); //italic curline = Regex.Replace(curline, @"\*\*([\s\S]+?)\*\*", "<em>$1</em>", RegexOptions.Compiled); //underline curline = Regex.Replace(curline, @"__([\s\S]+?)__", "<ins>$1</ins>", RegexOptions.Compiled); //strikeout curline = Regex.Replace(curline, @"--([\s\S]+?)--", "<del>$1</del>", RegexOptions.Compiled); //subtext new version curline = Regex.Replace(curline, @",,([\s\S]+?),,", "<sub>$1</sub>", RegexOptions.Compiled); //supertext curline = Regex.Replace(curline, @"::([\s\S]+?)::", "<sup>$1</sup>", RegexOptions.Compiled); //custom style curline = Regex.Replace(curline, @"\{style:([\s\S]+?)\}([\s\S]+?)\{style\}", "<span style=\"$1\">$2</span>", RegexOptions.Compiled); return curline; }
/// <summary> /// Обновление версии и даты отправки страницы в БД /// </summary> /// <param name="page">Страница</param> public void UpdateWikiPageVersion(WikiPage page) { page.Version = Guid.NewGuid().ToString(); page.Published = DateTime.Now; }
/// <summary> /// Конвертирует BSON в страницу /// </summary> /// <param name="page">Страница Wiki</param> /// <returns>Конвертированный BSON документ</returns> public BsonDocument NewFormPage(WikiPage page) { var document = new BsonDocument { {"_id", page.Code}, {"text", page.Text}, {"owner", Application.Current.Principal.CurrentUser.Identity.Name}, {"editor", Application.Current.Principal.CurrentUser.Identity.Name}, {"title", page.Title ?? ""}, {"ver", DateTime.Now} }; foreach (var propety in page.Propeties) { document[propety.Key] = propety.Value ?? ""; } return document; }
private string ProcessLine(string curline, string usage, WikiPage page, object context) { if (curline.StartsWith("======")){ curline = "<h6>" + curline.Substring(6) + "</h6>"; }else if (curline.StartsWith("=====")){ curline = "<h5>" + curline.Substring(5) + "</h5>"; }else if (curline.StartsWith("====")){ curline = "<h4>" + curline.Substring(4) + "</h4>"; }else if (curline.StartsWith("===")){ curline = "<h3>" + curline.Substring(3) + "</h3>"; }else if (curline.StartsWith("==")){ curline = "<h2>" + curline.Substring(2) + "</h2>"; }else if (curline.StartsWith("=")){ curline = "<h1>" + curline.Substring(1) + "</h1>"; } //LIST SUPPORT else if (curline.StartsWith("%%%%%%")){ curline = "<div class='wiki-list wiki-list-6'>" + curline.Substring(6) + "</div>"; }else if (curline.StartsWith("%%%%%")){ curline = "<div class='wiki-list wiki-list-5'>" + curline.Substring(5) + "</div>"; }else if (curline.StartsWith("%%%%")){ curline = "<div class='wiki-list wiki-list-4'>" + curline.Substring(4) + "</div>"; }else if (curline.StartsWith("%%%")){ curline = "<div class='wiki-list wiki-list-3'>" + curline.Substring(3) + "</div>"; }else if (curline.StartsWith("%%")){ curline = "<div class='wiki-list wiki-list-2'>" + curline.Substring(2) + "</div>"; }else if (curline.StartsWith("%")){ curline = "<div class='wiki-list wiki-list-1'>" + curline.Substring(1) + "</div>"; } else if (curline.StartsWith("№№№№№№")) { curline = "<div class='wiki-list wiki-list-6 number' >" + curline.Substring(6) + "</div>"; } else if (curline.StartsWith("№№№№№")) { curline = "<div class='wiki-list wiki-list-5 number'>" + curline.Substring(5) + "</div>"; } else if (curline.StartsWith("№№№№")) { curline = "<div class='wiki-list wiki-list-4 number'>" + curline.Substring(4) + "</div>"; } else if (curline.StartsWith("№№№")) { curline = "<div class='wiki-list wiki-list-3 number'>" + curline.Substring(3) + "</div>"; } else if (curline.StartsWith("№№")) { curline = "<div class='wiki-list wiki-list-2 number'>" + curline.Substring(2) + "</div>"; } else if (curline.StartsWith("№")) { curline = "<div class='wiki-list wiki-list-1 number'>" + curline.Substring(1) + "</div>"; } else{ curline = "<p>"+curline+"</p>"; } return curline; }
private string ProcessReferences(string curline, string usage, WikiPage page, IMvcContext context) { return Regex.Replace(curline,ReferenceRegex,m=> ReferenceReplacer(m,usage,page,context)); }
private string ProcessCode(string curline, string usage, WikiPage page, object context) { curline = Regex.Replace(curline,@"\ "," __BR__ "); curline = Regex.Replace(curline,@"\[BR\]",""); curline = Regex.Replace(curline,@"\s{4}"," __TAB__ "); curline = Regex.Replace(curline,@"\t"," __TAB__ "); //CODE BLOCKS curline = Regex.Replace(curline,@"([!=+\-*\.\\\/;<>%\&\^\:\|]+)","<span _CLASS_ATTR_'operator'>$1</span>"); curline = Regex.Replace(curline,@"\/\*","<span _CLASS_ATTR_'comment'>"); curline = Regex.Replace(curline,@"\*\/","</span>"); curline = Regex.Replace(curline,@"(\#[^""']+)$","<span _CLASS_ATTR_'comment'>$1</span>"); curline = Regex.Replace(curline,@"\b((var)|(for)|(return)|(foreach)|(while)|(case)|(switch)|(in)|(out)|(private)|(public)|(protected)|(void)|(function)|(class)|(namespace)|(using)|(select)|(where)|(group by)|(order by)|(null)|(true)|(false))\b","<span _CLASS_ATTR_'keyword'>$1</span>"); curline = Regex.Replace(curline,@"\b((int)|(string)|(DateTime)|(decimal)|(bool)|(nvarchar)|(datetime)|(bit)|(byte)|(float)|(long)|(bigint))\b","<span _CLASS_ATTR_'type'>$1</span>"); curline = Regex.Replace(curline,@"([\{\}\[\]\(\),]+)","<span _CLASS_ATTR_'delimiter'>$1</span>"); curline = Regex.Replace(curline,"\\\"","_EQ_"); curline = Regex.Replace(curline,@"""","_DQ_"); curline = Regex.Replace(curline,"\"([\\s\\S]+?)\"","<span _CLASS_ATTR_'string'>$1</span>"); curline = Regex.Replace(curline,@"_EQ_","\\\""); curline = Regex.Replace(curline,@"_DQ_","\"\""); curline = Regex.Replace(curline,@"(\b-?\d+(\.\d+)?)","<span _CLASS_ATTR_'number'>$1</span>"); curline+="<br/>"; curline = Regex.Replace(curline,@"_CLASS_ATTR_","class="); curline = Regex.Replace(curline,@"__TAB__"," "); curline = Regex.Replace(curline,@"__BR__","<br/>"); return curline; }
private bool CheckoutCodeBlock(IList<string> processed, string usage, WikiPage page, IMvcContext context, string curline, ref bool codeblock) { if (curline == "[[/code]]") { codeblock = false; processed.Add("</div>"); return true; } if (curline == "[[code]]") { codeblock = true; processed.Add("<div class='wiki-code'>"); return true; } if (codeblock) { processed.Add(ProcessCode(curline, usage, page, context)); return true; } return false; }
private bool CheckoutTable(IList<string> processed, string usage, WikiPage page, IMvcContext context, string curline, ref bool table, ref bool ishead) { if (Regex.IsMatch(curline, @"^\|", RegexOptions.Compiled)) { if (!table) { processed.Add("<table>"); processed.Add("<thead>"); table = true; ishead = true; } } else { if (table) { processed.Add("</tbody>"); processed.Add("</table>"); table = false; } } if (table) { var tde = ishead ? "th" : "td"; if (!ishead) { if (Regex.IsMatch(curline, @"^\|\{\+\}", RegexOptions.Compiled)) { curline = Regex.Replace(curline, @"^\|\{\+\}", "|", RegexOptions.Compiled); ishead = true; tde = "th"; } } var items = curline.Split('|'); var row = ""; if (!ishead) { row += "</thead></tbody>"; } ishead = false; row += "<tr>"; for (var i = 0; i < items.Length; i++) { if (i == 0 || i == items.Length - 1) { continue; //ignore left-right starters } var cell = items[i].Trim(); cell = ProcessInline(cell, usage, page, context); cell = ProcessReferences(cell,usage,page,context).Trim(); var spanmatch = Regex.Match(cell, @"^\{(\d+)?(,(\d+))?\}", RegexOptions.Compiled); if (spanmatch.Success) { cell = Regex.Replace(cell, @"^\{(\d+)?(,(\d+))?\}", "", RegexOptions.Compiled); var rowspan = "1"; var colspan = "1"; if (!string.IsNullOrWhiteSpace(spanmatch.Groups[1].Value)) { colspan = spanmatch.Groups[1].Value; } if (!string.IsNullOrWhiteSpace(spanmatch.Groups[3].Value)) { rowspan = spanmatch.Groups[3].Value; } row += "<" + tde + " rowspan='" + rowspan + "' colspan='" + colspan + "' >" + cell + "</" + tde + ">"; } else { row += "<" + tde + ">" + cell + "</" + tde + ">"; } } row += "</tr>"; processed.Add(row); return true; } return false; }
public void LastWriteTime() { _storage.Database.Drop(); var wikiPage = new WikiPage { Code = "test", Editor = "remalloc", Existed = true, LastWriteTime = DateTime.Now, Owner = "remalloc", Text = "some text", Title = "fgfgdfgd" }; _storage.SetApplication(_app); _storage.Save(wikiPage); // сохраним тестовую страницу var t = _storage.Get("test").FirstOrDefault(); // получим её Assert.NotNull(t); Assert.AreEqual("some text", t.Text); // и проверим, что текст нормальный _storage.Save(wikiPage); var y = _storage.Get("test").FirstOrDefault(); Assert.NotNull(y); Assert.AreNotEqual(wikiPage.LastWriteTime, y.LastWriteTime); }
public void CheckLocking() { _storage.Database.Drop(); var wikiPage = new WikiPage { Code = "test", Editor = "remalloc", Existed = true, LastWriteTime = DateTime.Now, Owner = "remalloc", Text = "some text", Title = "fgfgdfgd" }; ((StubClaimsPrincipal)_app.Principal.CurrentUser).WithAdmin = false; Assert.IsTrue(_storage.Save(wikiPage)); Assert.IsTrue(_storage.GetLock(wikiPage.Code)); _app.Principal.SetCurrentUser(new StubClaimsPrincipal { Identity = new StubIdentity { Name = Guid.NewGuid().ToString() } }); Assert.IsFalse(_storage.Save(wikiPage)); Assert.IsFalse(_storage.GetLock(wikiPage.Code)); Assert.IsFalse(_storage.ReleaseLock(wikiPage.Code)); ((StubClaimsPrincipal) _app.Principal.CurrentUser).WithAdmin = true; Assert.IsFalse(_storage.Save(wikiPage)); Assert.IsFalse(_storage.GetLock(wikiPage.Code)); Assert.IsTrue(_storage.ReleaseLock(wikiPage.Code)); Assert.IsTrue(_storage.Save(wikiPage)); }
public void CorrectReturnForNonExistsPageFromGet() { _storage.Database.Drop(); var wikiPage = new WikiPage { Code = "test", Editor = "remalloc", Existed = true, LastWriteTime = DateTime.Now, Owner = "remalloc", Text = "some text", Title = "fgfgdfgd" }; Assert.IsTrue(_storage.Save(wikiPage)); var t = _storage.Get("test", "notExists"); Assert.AreEqual(1, t.Count()); }
/// <summary> /// /// </summary> /// <param name="other"></param> /// <returns></returns> protected bool Equals(WikiPage other) { return string.Equals(Code, other.Code) && Existed.Equals(other.Existed) && string.Equals(Text, other.Text) && LastWriteTime.Equals(other.LastWriteTime) && string.Equals(Owner, other.Owner) && string.Equals(Editor, other.Editor) && string.Equals(Title, other.Title) && string.Equals(Version, other.Version) && Published.Equals(other.Published) && string.Equals(Locker, other.Locker); }
/// <summary> /// Конвертирует страницу в команду обновления /// </summary> /// <param name="page"></param> /// <returns></returns> public IMongoUpdate UpdateFromPage(WikiPage page) { var updateBuilder = new UpdateBuilder(); if (!string.IsNullOrEmpty(page.Text)) { updateBuilder.Set("text", page.Text); } if (!string.IsNullOrEmpty(page.Title)) { updateBuilder.Set("title", page.Title); } updateBuilder.Set("ver", (page.LastWriteTime != DateTime.MinValue) ? (page.LastWriteTime) : (DateTime.Now)); updateBuilder.Set("editor", Application.Current.Principal.CurrentUser.Identity.Name); foreach (var propety in page.Propeties) { updateBuilder.Set(propety.Key, propety.Value ?? ""); } return updateBuilder; }
/// <summary> /// Поиск страниц по маске /// </summary> /// <param name="search"></param> /// <returns></returns> public IEnumerable<WikiPage> FindPages(string search) { var queryJson = "{$or : [{_id : {$regex : '(?ix)_REGEX_'}}, {code : {$regex : '(?ix)_REGEX_'}},{title:{$regex: '(?ix)_REGEX_'}},{owner:{$regex: '(?ix)_REGEX_'}},{editor:{$regex: '(?ix)_REGEX_'}}]},".Replace("_REGEX_", search); var queryDoc = new QueryDocument(BsonDocument.Parse(queryJson)); foreach (var doc in Collection.Find(queryDoc).SetFields("code", "title", "ver", "editor", "owner")) { var page = new WikiPage { Code = doc["_id"].AsString, Title = doc["title"].AsString, LastWriteTime = doc["ver"].ToLocalTime() }; yield return page; } }
/// <summary> /// Копирует данные о версии и дате занесения в базу страницы Wiki /// </summary> /// <param name="page">Страница Wiki</param> /// <param name="document">Целевой документ</param> public void CopyWikiPageVersionToBson(WikiPage page, BsonDocument document) { document["version"] = page.Version; document["published"] = page.Published; }
/// <summary> /// Push a Wiki page to the history /// </summary> /// <param name="wikiPage"></param> /// <param name="comment"></param> private MongoDbWikiVersionCreateResult PushHistory(WikiPage wikiPage, string comment) { var version = Guid.NewGuid().ToString(); var published = DateTime.Now; var writeResult = VersionsStorage.Collection.Insert( Serializer.NewFormPage(wikiPage).Merge( new BsonDocument { {"published", published}, {"version", version}, {"creator", Application.Principal.CurrentUser.Identity.Name}, {"comment", comment ?? "Uncommented version"}, {"_id", ObjectId.GenerateNewId()}, {"owner", wikiPage.Owner}, {"editor", wikiPage.Editor}, {"code", wikiPage.Code} }, true ) ); if (!writeResult.Ok) { return new MongoDbWikiVersionCreateResult(false, version, published, writeResult.ErrorMessage); } return new MongoDbWikiVersionCreateResult(true, version, published, "Version created to with comment: '" + comment + "'"); }
public void CanLockWikiPage() { _storage.Database.Drop(); var wikiPage = new WikiPage { Code = "test", Editor = "remalloc", Existed = true, LastWriteTime = DateTime.Now, Owner = "remalloc", Text = "some text", Title = "fgfgdfgd" }; _storage.Save(wikiPage); var r = _storage.Get("test"); Assert.NotNull(r); var t01 = new Task(() => GetLockTask(true)); var t02 = new Task(() => GetLockTask(false)); var t03 = new Task(() => GetLockTask(false)); var t04 = new Task(() => GetLockTask(false)); var t05 = new Task(() => GetLockTask(false)); t01.Start(); t02.Start(); t03.Start(); t04.Start(); t05.Start(); }
private string ReferenceReplacer(Match match, string usage, WikiPage page, IMvcContext context) { var addr = match.Groups["addr"].Value; var name = match.Groups["name"].Value; var tail = match.Groups["tail"].Value; if (string.IsNullOrWhiteSpace(name)) name = addr; if (context != null) { if (addr.StartsWith("/")) { return PageOpenUrl(addr, name, tail, context); } else if(addr.StartsWith("img:")) { return GetWikiImageRef(addr, name, tail, context); } else if (addr.StartsWith("file:")) { return GetWikiBinaryRef(addr, name, tail, context); } else { return GetDefaultReference(addr, name, tail, context); } } else { return GetDefaultReference(addr, name, tail, context); } }