public void Can_localize_string_resource_mixed_builder() { var resourceManagerMock = new Mock <IPlainTextResourceManager>(); var resources = new DekiResources(resourceManagerMock.Object, CultureInfo.InvariantCulture); resourceManagerMock.Setup(x => x.GetString("x", CultureInfo.InvariantCulture, null)) .Returns("abc").AtMostOnce().Verifiable(); resourceManagerMock.Setup(x => x.GetString("y", CultureInfo.InvariantCulture, null)) .Returns("xyz").AtMostOnce().Verifiable(); var b = new DekiResourceBuilder(); b.Append(new DekiResource("x")); b.Append("+"); b.Append(new DekiResource("y")); b.Append("-"); Assert.AreEqual("abc+xyz-", b.Localize(resources)); resourceManagerMock.VerifyAll(); }
public void Can_localize_string_resource_mixed_builder() { var resourceManagerMock = new Mock<IPlainTextResourceManager>(); var resources = new DekiResources(resourceManagerMock.Object, CultureInfo.InvariantCulture); resourceManagerMock.Setup(x => x.GetString("x", CultureInfo.InvariantCulture, null)) .Returns("abc").AtMostOnce().Verifiable(); resourceManagerMock.Setup(x => x.GetString("y", CultureInfo.InvariantCulture, null)) .Returns("xyz").AtMostOnce().Verifiable(); var b = new DekiResourceBuilder(); b.Append(new DekiResource("x")); b.Append("+"); b.Append(new DekiResource("y")); b.Append("-"); Assert.AreEqual("abc+xyz-",b.Localize(resources)); resourceManagerMock.VerifyAll(); }
public static void AddEditPageRecentChange(DateTime timestamp, PageBE title, UserBE user, DekiResourceBuilder comment, OldBE old) { var resources = DekiContext.Current.Resources; DbUtils.CurrentSession.RecentChanges_Insert(timestamp, title, user, comment.Localize(resources), old.ID, RC.EDIT, 0, String.Empty, false, 0); }
public static void AddGrantsRemovedRecentChange(DateTime timestamp, PageBE title, UserBE user, DekiResourceBuilder comment) { var resources = DekiContext.Current.Resources; DbUtils.CurrentSession.RecentChanges_Insert(timestamp, title, user, comment.Localize(resources), 0, RC.GRANTS_REMOVED, 0, String.Empty, false, 0); }
public static OldBE Save(PageBE page, OldBE previous, string userComment, string text, string contentType, string displayName, string language, int section, string xpath, DateTime timeStamp, ulong restoredPageId, bool loggingEnabled, bool removeIllegalElements, Title relToTitle, bool overwrite, uint authorId, out bool conflict) { // NOTE (steveb): // page: most recent page about to be overwritten // previous: (optional) possible earlier page on which the current edit is based upon conflict = false; bool isNewForEventContext = page.ID == 0 || page.IsRedirect; // check save permissions IsAccessAllowed(page, 0 == page.ID ? Permissions.CREATE : Permissions.UPDATE, false); // validate the save if((0 == page.ID) && ((-1 != section) || (null != xpath))) { throw new PageEditExistingSectionInvalidOperationException(); } // displaynames entered by user are trimmed if(displayName != null) { displayName = displayName.Trim(); } if(!Title.FromDbPath(page.Title.Namespace, page.Title.AsUnprefixedDbPath(), displayName).IsValid) { throw new PageInvalidTitleConflictException(); } // load old contents into current page when a section is edited ParserResult alternate = new ParserResult(); ParserResult original = new ParserResult(); if(previous != null) { // parse most recent version as alternate alternate = DekiXmlParser.Parse(page, ParserMode.RAW); // parse base version for three way diff string pageContentType = page.ContentType; string pageText = page.GetText(DbUtils.CurrentSession); page.ContentType = previous.ContentType; page.SetText(previous.Text); original = DekiXmlParser.Parse(page, ParserMode.RAW); page.ContentType = pageContentType; page.SetText(pageText); } // ensure the parent exists PageBE parent = EnsureParent(DekiXmlParser.REDIRECT_REGEX.IsMatch(text), page.Title); if(null != parent) { page.ParentID = parent.Title.IsRoot ? 0 : parent.ID; } // Explicitly setting the language of a talk page is not valid if(page.Title.IsTalk && !string.IsNullOrEmpty(language)) { throw new TalkPageLanguageCannotBeSetConflictException(); } // Language is set in this order: explicitly given, already set, language of parent language = language ?? page.Language ?? (null != parent ? parent.Language : String.Empty); // talk pages always get their language from their corresponding front page if(page.Title.IsTalk) { PageBE frontPage = PageBL.GetPageByTitle(page.Title.AsFront()); if(frontPage != null && frontPage.ID != 0) { language = frontPage.Language; } } string nativeName = ValidatePageLanguage(language); // parse the content ParserResult parserResult = DekiXmlParser.ParseSave(page, contentType, language, text, section, xpath, removeIllegalElements, relToTitle); OldBE old = null; var comment = new DekiResourceBuilder(userComment ?? string.Empty); // check if this is a new page if(0 == page.ID) { _log.DebugFormat("saving new page {0}", page.Title); AuthorizePage(DekiContext.Current.User, Permissions.CREATE, parent, false); if(0 == restoredPageId) { if(!comment.IsEmpty) { comment.Append("; "); } comment.Append(DekiResources.PAGE_CREATED()); if((null == parserResult.RedirectsToTitle) && (null == parserResult.RedirectsToUri)) { comment.Append(", "); comment.Append(DekiResources.PAGE_DIFF_SUMMARY_ADDED(Utils.GetPageWordCount(parserResult.MainBody))); } page.MinorEdit = false; page.IsNew = true; } } // if this is an existing page, ensure the content has changed and save the current page information else { _log.DebugFormat("saving existing page {0}", page.ID); // prevent creating a redirect on a page that has non-redirect children if((null != parserResult.RedirectsToTitle) || (null != parserResult.RedirectsToUri)) { IList<PageBE> children = DbUtils.CurrentSession.Pages_GetChildren((uint)page.ID, page.Title.Namespace, true); if(0 < children.Count) { throw new PageInvalidRedirectConflictException(); } } bool displayNameChanged = page.Title.DisplayName != displayName && displayName != null; bool languageChanged = !page.Language.EqualsInvariant(language); if(parserResult.ContentType == page.ContentType) { if(parserResult.BodyText.EqualsInvariant(page.GetText(DbUtils.CurrentSession))) { if(!displayNameChanged && !languageChanged && !overwrite) { return null; } } else { int maxDelta = DekiContext.Current.Instance.MaxDiffSize; // merge changes if(previous != null) { conflict = true; try { XDoc mergeBody = XDocDiff.Merge(original.MainBody, alternate.MainBody, parserResult.MainBody, maxDelta, ArrayMergeDiffPriority.Right, out conflict); if(mergeBody == null) { // NOTE (steveb): for some reason, Merge() thinks that returning null is not a conflict. conflict = true; // merge failed, use the submitted body instead mergeBody = parserResult.MainBody; } parserResult = DekiXmlParser.ParseSave(page, page.ContentType, page.Language, mergeBody.ToInnerXHtml(), -1, null, removeIllegalElements, relToTitle); } catch(Exception e) { _log.Error("Save", e); } } if(!comment.IsEmpty) { comment.Append("; "); } if(page.IsRedirect) { comment.Append(DekiResources.PAGE_DIFF_SUMMARY_ADDED(Utils.GetPageWordCount(parserResult.MainBody))); } else { comment.Append(Utils.GetPageDiffSummary(page, page.GetText(DbUtils.CurrentSession), page.ContentType, parserResult.BodyText, parserResult.ContentType, maxDelta)); } } } else { if(!comment.IsEmpty) { comment.Append("; "); } comment.Append(DekiResources.PAGE_CONTENTTYPE_CHANGED(parserResult.ContentType)); } if(displayNameChanged) { if(!comment.IsEmpty) { comment.Append("; "); } comment.Append(DekiResources.PAGE_DISPLAYNAME_CHANGED(displayName)); } if(languageChanged) { if(!comment.IsEmpty) { comment.Append("; "); } comment.Append(DekiResources.PAGE_LANGUAGE_CHANGED(nativeName)); // set the language on the talk page as well. if(!page.Title.IsTalk) { PageBE talkPage = PageBL.GetPageByTitle(page.Title.AsTalk()); if(talkPage != null && talkPage.ID != 0) { SetPageLanguage(talkPage, language, true); } } } old = InsertOld(page, 0); page.MinorEdit = (page.UserID == DekiContext.Current.User.ID) && (DateTime.Now < page.TimeStamp.AddMinutes(15)); page.IsNew = false; } // update the page information to reflect the new content var resources = DekiContext.Current.Resources; page.Comment = comment.Localize(resources); page.ContentType = parserResult.ContentType; page.Language = language; page.UserID = authorId; var bodyText = string.Empty; if(parserResult.RedirectsToTitle != null) { page.IsRedirect = true; bodyText = "#REDIRECT [[" + parserResult.RedirectsToTitle.AsPrefixedDbPath() + "]]"; } else if(parserResult.RedirectsToUri != null) { page.IsRedirect = true; bodyText = "#REDIRECT [[" + parserResult.RedirectsToUri + "]]"; } else { page.IsRedirect = false; bodyText = parserResult.BodyText; } page.SetText(bodyText); page.UseCache = !parserResult.HasScriptContent; page.TIP = parserResult.Summary; page.Touched = page.TimeStamp = timeStamp; // nametype and displayname logic if(string.IsNullOrEmpty(displayName) && (page.ID == 0)) { // new page created without a title: title comes from the name page.Title.DisplayName = page.Title.AsUserFriendlyDisplayName(); } else if(!string.IsNullOrEmpty(displayName) || page.Title.IsHomepage) { // title is provided: title set from provided value page.Title.DisplayName = displayName; } else { // preserve the display name of the page } // Note (arnec): Using Encoding.UTF8 because the default is Encoding.Unicode which produces a different md5sum than expected from ascii page.Etag = StringUtil.ComputeHashString(bodyText, Encoding.UTF8); // insert or update the page if(0 == page.ID) { if(restoredPageId == 0) { // only set the revision if this isn't a restore. page.Revision = 1; } ulong pageId = DbUtils.CurrentSession.Pages_Insert(page, restoredPageId); if(pageId != 0) { // the ID is set on the passed in page object page.ID = pageId; if(loggingEnabled) { RecentChangeBL.AddNewPageRecentChange(page.TimeStamp, page, DekiContext.Current.User, comment); } // Copy permissions from parent if the page is a child of homepage or Special: ulong parentPageId = page.ParentID; if((parentPageId == 0) && (parent != null) && (page.Title.IsMain || page.Title.IsSpecial)) { parentPageId = parent.ID; } if(parentPageId > 0) { DbUtils.CurrentSession.Grants_CopyToPage(parentPageId, page.ID); // refresh the entity since the restriction may have changed and copy the state to passed in page object PageBE temp = GetPageById(page.ID); temp.Copy(page); } // never log creation of userhomepages to recentchanges if(loggingEnabled && page.Title.IsUser && page.Title.GetParent().IsHomepage) { loggingEnabled = false; } } } else { page.Revision++; DbUtils.CurrentSession.Pages_Update(page); if(loggingEnabled) { RecentChangeBL.AddEditPageRecentChange(page.TimeStamp, page, DekiContext.Current.User, comment, old); } } try { if(null != parserResult.Templates) { ImportTemplatePages(page, parserResult.Templates.ToArray()); } if(null != parserResult.Tags) { ImportPageTags(page, parserResult.Tags.ToArray()); } if(null != parserResult.Links) { UpdateLinks(page, parserResult.Links.ToArray()); } } finally { if(isNewForEventContext) { DekiContext.Current.Instance.EventSink.PageCreate(DekiContext.Current.Now, page, DekiContext.Current.User); } else { DekiContext.Current.Instance.EventSink.PageUpdate(DekiContext.Current.Now, page, DekiContext.Current.User); } } return old; }