public void Appending_string_sets_builder_non_empty()
        {
            var b = new DekiResourceBuilder();

            b.Append("bar");
            Assert.IsFalse(b.IsEmpty);
        }
        public void Appending_resource_sets_builder_non_empty()
        {
            var b = new DekiResourceBuilder();

            b.Append(new DekiResource("x"));
            Assert.IsFalse(b.IsEmpty);
        }
        public void Adding_null_string_keeps_builder_empty()
        {
            var b = new DekiResourceBuilder();

            b.Append((string)null);
            Assert.IsTrue(b.IsEmpty);
        }
        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();
        }
Пример #5
0
 public void Appending_resource_sets_builder_non_empty() {
     var b = new DekiResourceBuilder();
     b.Append(new DekiResource("x"));
     Assert.IsFalse(b.IsEmpty);
 }
        public void Creating_builder_with_null_string_keeps_builder_empty()
        {
            var b = new DekiResourceBuilder((string)null);

            Assert.IsTrue(b.IsEmpty);
        }
Пример #7
0
        private DekiResourceBuilder CompareTagSets(List <TagBE> current, List <TagBE> proposed, out List <TagBE> added, out List <uint> removed)
        {
            // perform a subtraction of tags (in both directions) to determine which tags were added and removed from one set to another
            added   = new List <TagBE>();
            removed = new List <uint>();
            TagComparer tagComparer = new TagComparer();

            current.Sort(tagComparer);
            proposed.Sort(tagComparer);

            // determine which pages have been added
            StringBuilder addedSummary = new StringBuilder();
            List <TagBE>  addedList    = new List <TagBE>();

            foreach (TagBE proposedTag in proposed)
            {
                if ((current.BinarySearch(proposedTag, tagComparer) < 0))
                {
                    // only add the tag if is not already being added
                    bool includeTag = (added.BinarySearch(proposedTag, tagComparer) < 0);
                    if (includeTag && (TagType.TEXT == proposedTag.Type))
                    {
                        TagBE proposedTagDefine = new TagBE();
                        proposedTagDefine.Type = TagType.DEFINE;
                        proposedTagDefine.Name = proposedTag.Name;
                        if (0 <= proposed.BinarySearch(proposedTagDefine, tagComparer))
                        {
                            includeTag = false;
                        }
                    }
                    if (includeTag)
                    {
                        added.Add(proposedTag);
                        if (1 < added.Count)
                        {
                            addedSummary.Append(", ");
                        }
                        addedSummary.Append(proposedTag.PrefixedName);
                    }
                }
            }

            // determine which pages have been removed
            StringBuilder removedSummary = new StringBuilder();

            foreach (TagBE currentTag in current)
            {
                if (proposed.BinarySearch(currentTag, tagComparer) < 0)
                {
                    removed.Add(currentTag.Id);
                    if (1 < removed.Count)
                    {
                        removedSummary.Append(", ");
                    }
                    removedSummary.Append(currentTag.PrefixedName);
                }
            }

            // create a diff summary string
            var diffSummary = new DekiResourceBuilder();

            if (0 < addedSummary.Length)
            {
                diffSummary.Append(DekiResources.TAG_ADDED(addedSummary.ToString()));
            }
            if (0 < removedSummary.Length)
            {
                if (!diffSummary.IsEmpty)
                {
                    diffSummary.Append(" ");
                }
                diffSummary.Append(DekiResources.TAG_REMOVED(removedSummary.ToString()));
            }
            return(diffSummary);
        }
Пример #8
0
        /// <summary>
        /// Main workflow of applying permissions to pages
        /// </summary>
        /// <param name="targetPage">Page to apply permissions to</param>
        /// <param name="cascade">Whether or not to apply permissions to child pages</param>
        /// <param name="userEffectiveRights">permission mask of user (user + group operations) independant of a page</param>
        /// <param name="proposedGrantSet">Used only for PUT cascade=absolute</param>
        /// <param name="proposedAddedGrants">Grants to add (if they dont already exist)</param>
        /// <param name="proposedRemovedGrants">Grants to remove (if they exist)</param>
        /// <param name="currentGrants">Optional current set of grants for targetPage. This is provided as an optimization to eliminate a db hit</param>
        /// <param name="proposedRestriction">Optional restriction to be applied for page (and child pages)</param>
        /// <param name="atStartPage">Always true when calling from outside this method. Used for recursion to not throw exceptions for errors on child pages</param>
        private static void ApplyPermissionChange(PageBE targetPage, bool cascade, ulong userEffectiveRights, IList<GrantBE> proposedGrantSet, IList<GrantBE> proposedAddedGrants, IList<GrantBE> proposedRemovedGrants, IList<GrantBE> currentGrants, RoleBE proposedRestriction, bool atStartPage) {

            // TODO (steveb): validate which parameters are null and which are not (i.e. proposedAddedGrants, proposedRemovedGrants, proposedGrantSet)

            if(!targetPage.Title.IsEditable) {
                throw new PermissionsNotAllowedForbiddenException(targetPage.Title.Path, targetPage.Title.Namespace);
            }

            UserBE currentUser = DekiContext.Current.User;
            bool arePermissionsOkForPage = true;

            // Make sure granter has access to what is being granted.
            // remove already existing grants from new grant list.
            if(currentGrants == null) {
                currentGrants = DbUtils.CurrentSession.Grants_GetByPage((uint)targetPage.ID);
            }

            //proposedGrantSet will be null for descendant pages as well as for setting permissions via POST in a relative fashion
            if(proposedGrantSet != null) {
                CompareGrantSets(currentGrants, proposedGrantSet, out proposedAddedGrants, out proposedRemovedGrants);
            }

            AddRequesterToAddedGrantList(currentGrants, proposedAddedGrants, targetPage);

            try {

                //Determine the grant perm mask for the current user by using either the proposedGrantSet for the initial page
                //Or by computing it for descendant pages that dont have a proposedGrantSet passed in
                ulong proposedPageGrantMaskForUser;
                if(proposedGrantSet != null) {
                    proposedPageGrantMaskForUser = SumGrantPermissions(proposedGrantSet, currentUser);
                } else {
                    List<GrantBE> proposedGrantSetForCurrentPage = ApplyGrantMerge(currentGrants, proposedAddedGrants, proposedRemovedGrants);
                    proposedPageGrantMaskForUser = SumGrantPermissions(proposedGrantSetForCurrentPage, currentUser);
                }

                //Ensure user has the combined permissions granted to the page
                ulong addedGrantMask = SumGrantPermissions(proposedAddedGrants, null);
                CheckUserAllowed(currentUser, targetPage, (Permissions)addedGrantMask);

                //Determine the restriction mask to use. 
                ulong restrictionFlag = ulong.MaxValue;
                RoleBE targetPageRestriction = GetRestrictionById(targetPage.RestrictionID);
                if(proposedRestriction == null && targetPageRestriction != null) {
                    restrictionFlag = targetPageRestriction.PermissionFlags;
                } else if(proposedRestriction != null) {
                    restrictionFlag = proposedRestriction.PermissionFlags;
                }

                //Ensure the granter is not locking himself out.
                //Calculate the rights the granter would have after the grants are saved
                ulong newEffectivePageRights = CalculateEffectivePageRights(new PermissionStruct(userEffectiveRights, restrictionFlag, proposedPageGrantMaskForUser));

                //If granter no longer has "CHANGEPERMISSIONS" access after grants, the user is locking self out.
                if(!IsActionAllowed(newEffectivePageRights, false, false, false, Permissions.CHANGEPERMISSIONS)) {
                    throw new PermissionsUserWouldBeLockedOutOfPageInvalidOperationException();
                }

            } catch(MindTouchException) { // Validation/permission related exceptions
                arePermissionsOkForPage = false;
                if(atStartPage) //Swallow validation exceptions on 
                    throw;
            } catch(DreamAbortException) { // Validation/permission related exceptions

                // TODO (arnec): remove this once all usage of Dream exceptions is purged from Deki logic
                arePermissionsOkForPage = false;
                if(atStartPage) //Swallow validation exceptions on 
                    throw;
            }

            if(arePermissionsOkForPage) {
                //All validation steps succeeded: Apply grants/restriction to page based on delta of current grants and proposed additions/removals

                List<GrantBE> grantsToRemove = IntersectGrantSets(currentGrants, proposedRemovedGrants);
                List<GrantBE> grantsToAdd = GetExtraGrantsInEndingSet(currentGrants, proposedAddedGrants);

                if(grantsToRemove.Count > 0) {

                    // Contstruct recent change description
                    var deleteGrantsDescription = new DekiResourceBuilder();
                    List<uint> userGrantIds = new List<uint>();
                    List<uint> groupGrantIds = new List<uint>();
                    foreach(GrantBE g in grantsToRemove) {
                        if(!deleteGrantsDescription.IsEmpty) {
                            deleteGrantsDescription.Append(", ");
                        }
                        if(g.Type == GrantType.USER) {
                            userGrantIds.Add(g.Id);
                            UserBE user = UserBL.GetUserById(g.UserId);
                            if(user != null) {
                                deleteGrantsDescription.Append(DekiResources.GRANT_REMOVED(user.Name, g.Role.Name.ToLowerInvariant()));
                            }
                        } else if(g.Type == GrantType.GROUP) {
                            groupGrantIds.Add(g.Id);
                            GroupBE group = GroupBL.GetGroupById(g.GroupId);
                            if(group != null) {
                                deleteGrantsDescription.Append(DekiResources.GRANT_REMOVED(group.Name, g.Role.Name.ToLowerInvariant()));
                            }
                        }
                    }
                    DbUtils.CurrentSession.Grants_Delete(userGrantIds, groupGrantIds);
                    RecentChangeBL.AddGrantsRemovedRecentChange(DateTime.UtcNow, targetPage, DekiContext.Current.User, deleteGrantsDescription);
                }

                if(grantsToAdd.Count > 0) {

                    // Contstruct recent change description
                    Dictionary<uint, PageBE> uniquePages = new Dictionary<uint, PageBE>();
                    var addGrantsDescription = new DekiResourceBuilder();
                    foreach(GrantBE grant in grantsToAdd) {
                        grant.CreatorUserId = DekiContext.Current.User.ID;
                        if(!addGrantsDescription.IsEmpty) {
                            addGrantsDescription.Append(", ");
                        }
                        if(grant.Type == GrantType.USER) {
                            UserBE user = UserBL.GetUserById(grant.UserId);
                            if(user != null) {
                                addGrantsDescription.Append(DekiResources.GRANT_ADDED(user.Name, grant.Role.Name));
                            }
                        } else if(grant.Type == GrantType.GROUP) {
                            GroupBE group = GroupBL.GetGroupById(grant.GroupId);
                            if(group != null) {
                                addGrantsDescription.Append(DekiResources.GRANT_ADDED(group.Name, grant.Role.Name));
                            }
                        }
                        uniquePages[grant.PageId] = PageBL.GetPageById(grant.PageId);
                    }
                    foreach(GrantBE grantToAdd in grantsToAdd) {
                        DbUtils.CurrentSession.Grants_Insert(grantToAdd);
                    }
                    foreach(PageBE p in uniquePages.Values) {
                        RecentChangeBL.AddGrantsAddedRecentChange(DateTime.UtcNow, p, DekiContext.Current.User, addGrantsDescription);
                    }
                }

                targetPage.Touched = DateTime.UtcNow;

                if(proposedRestriction != null && targetPage.RestrictionID != proposedRestriction.ID) {
                    targetPage.RestrictionID = proposedRestriction.ID;
                    DbUtils.CurrentSession.Pages_Update(targetPage);

                    // NOTE (maxm): Restriction change without grant changes will not perform cache invalidation on permissions. Force the invalidation (if any) here.
                    if(ArrayUtil.IsNullOrEmpty(grantsToAdd) && ArrayUtil.IsNullOrEmpty(grantsToRemove)) {
                        DbUtils.CurrentSession.Grants_Delete(new uint[] { }, new uint[] { });
                    }

                    RecentChangeBL.AddRestrictionUpdatedChange(targetPage.Touched, targetPage, currentUser, DekiResources.RESTRICTION_CHANGED(proposedRestriction.Name));
                } else {
                    PageBL.Touch(targetPage, DateTime.UtcNow);
                }
            }

            //Cascade into child pages only if current page permissions applied
            if(cascade) {
                if(proposedGrantSet != null) {
                    proposedAddedGrants = proposedRemovedGrants = null;
                }

                ICollection<PageBE> childPages = PageBL.GetChildren(targetPage, true);
                foreach(PageBE p in childPages) {
                    ResetGrants(proposedGrantSet, p);
                    ResetGrants(proposedAddedGrants, p);
                    ResetGrants(proposedRemovedGrants, p);

                    ApplyPermissionChange(p, true, userEffectiveRights, proposedGrantSet, proposedAddedGrants, proposedRemovedGrants, null, proposedRestriction, false);
                }
            }
        }
Пример #9
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);
        }
Пример #10
0
 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();
 }
Пример #11
0
 public void Appending_string_sets_builder_non_empty() {
     var b = new DekiResourceBuilder();
     b.Append("bar");
     Assert.IsFalse(b.IsEmpty);
 }
        public void Creating_builder_with_string_sets_builder_non_empty()
        {
            var b = new DekiResourceBuilder("x");

            Assert.IsFalse(b.IsEmpty);
        }
Пример #13
0
 public void Creating_builder_with_null_string_keeps_builder_empty() {
     var b = new DekiResourceBuilder((string)null);
     Assert.IsTrue(b.IsEmpty);
 }
Пример #14
0
 public void Adding_null_string_keeps_builder_empty() {
     var b = new DekiResourceBuilder();
     b.Append((string)null);
     Assert.IsTrue(b.IsEmpty);
 }
Пример #15
0
 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);
 }
Пример #16
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);
 }
Пример #17
0
        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);
        }
Пример #18
0
 public void Creating_builder_with_resource_sets_builder_non_empty() {
     var b = new DekiResourceBuilder(new DekiResource("x"));
     Assert.IsFalse(b.IsEmpty);
 }
Пример #19
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;
        }