/// <summary>
        /// Returns a paged result of media items known to be of a "Folder" type
        /// </summary>
        /// <param name="id"></param>
        /// <param name="pageNumber"></param>
        /// <param name="pageSize"></param>
        /// <returns></returns>
        public PagedResult <ContentItemBasic <ContentPropertyBasic> > GetChildFolders(int id, int pageNumber = 1, int pageSize = 1000)
        {
            //Suggested convention for folder mediatypes - we can make this more or less complicated as long as we document it...
            //if you create a media type, which has an alias that ends with ...Folder then its a folder: ex: "secureFolder", "bannerFolder", "Folder"
            var folderTypes = _mediaTypeService
                              .GetAll()
                              .Where(x => x.Alias.EndsWith("Folder"))
                              .Select(x => x.Id)
                              .ToArray();

            if (folderTypes.Length == 0)
            {
                return(new PagedResult <ContentItemBasic <ContentPropertyBasic> >(0, pageNumber, pageSize));
            }

            long total;
            var  children = _mediaService.GetPagedChildren(id, pageNumber - 1, pageSize, out total,
                                                           //lookup these content types
                                                           _sqlContext.Query <IMedia>().Where(x => folderTypes.Contains(x.ContentTypeId)),
                                                           Ordering.By("Name", Direction.Ascending));

            return(new PagedResult <ContentItemBasic <ContentPropertyBasic> >(total, pageNumber, pageSize)
            {
                Items = children.Select(_umbracoMapper.Map <IMedia, ContentItemBasic <ContentPropertyBasic> >)
            });
        }
示例#2
0
        public void Get_Paged_Children_With_Media_Type_Filter()
        {
            var mediaService = ServiceContext.MediaService;
            var mediaType1   = MockedContentTypes.CreateImageMediaType("Image2");

            ServiceContext.MediaTypeService.Save(mediaType1);
            var mediaType2 = MockedContentTypes.CreateImageMediaType("Image3");

            ServiceContext.MediaTypeService.Save(mediaType2);

            for (int i = 0; i < 10; i++)
            {
                var m1 = MockedMedia.CreateMediaImage(mediaType1, -1);
                mediaService.Save(m1);
                var m2 = MockedMedia.CreateMediaImage(mediaType2, -1);
                mediaService.Save(m2);
            }

            long total;
            var  result = ServiceContext.MediaService.GetPagedChildren(-1, 0, 11, out total,
                                                                       SqlContext.Query <IMedia>().Where(x => new[] { mediaType1.Id, mediaType2.Id }.Contains(x.ContentTypeId)),
                                                                       Ordering.By("SortOrder", Direction.Ascending));

            Assert.AreEqual(11, result.Count());
            Assert.AreEqual(20, total);

            result = ServiceContext.MediaService.GetPagedChildren(-1, 1, 11, out total,
                                                                  SqlContext.Query <IMedia>().Where(x => new[] { mediaType1.Id, mediaType2.Id }.Contains(x.ContentTypeId)),
                                                                  Ordering.By("SortOrder", Direction.Ascending));
            Assert.AreEqual(9, result.Count());
            Assert.AreEqual(20, total);
        }
示例#3
0
        public void GetPagedResultsByQuery_CustomPropertySort()
        {
            var provider = TestObjects.GetScopeProvider(Logger);

            using (var scope = provider.CreateScope())
            {
                var repository = CreateRepository((IScopeAccessor)provider, out _);

                var query = scope.SqlContext.Query <IContent>().Where(x => x.Name.Contains("Text"));

                try
                {
                    scope.Database.AsUmbracoDatabase().EnableSqlTrace = true;
                    scope.Database.AsUmbracoDatabase().EnableSqlCount = true;

                    var result = repository.GetPage(query, 0, 2, out var totalRecords, null, Ordering.By("title", isCustomField: true));

                    Assert.AreEqual(3, totalRecords);
                    Assert.AreEqual(2, result.Count());

                    result = repository.GetPage(query, 1, 2, out totalRecords, null, Ordering.By("title", isCustomField: true));

                    Assert.AreEqual(1, result.Count());
                }
                finally
                {
                    scope.Database.AsUmbracoDatabase().EnableSqlTrace = false;
                    scope.Database.AsUmbracoDatabase().EnableSqlCount = false;
                }
            }
        }
示例#4
0
        private void RefreshContentOfContentTypes(int[] contentTypeIds)
        {
            const int pageSize = 500;
            var       page     = 0;
            var       total    = long.MaxValue;

            while (page * pageSize < total)
            {
                var contentToRefresh = _services.ContentService.GetPagedOfTypes(
                    //Re-index all content of these types
                    contentTypeIds,
                    page++, pageSize, out total, null,
                    //order by shallowest to deepest, this allows us to check it's published state without checking every item
                    Ordering.By("Path", Direction.Ascending));

                //track which Ids have their paths are published
                var publishChecked = new Dictionary <int, bool>();

                foreach (var c in contentToRefresh)
                {
                    var isPublished = false;
                    if (c.Published)
                    {
                        if (!publishChecked.TryGetValue(c.ParentId, out isPublished))
                        {
                            //nothing by parent id, so query the service and cache the result for the next child to check against
                            isPublished          = _services.ContentService.IsPathPublished(c);
                            publishChecked[c.Id] = isPublished;
                        }
                    }

                    ReIndexForContent(c, isPublished);
                }
            }
        }
示例#5
0
        /// <summary>
        /// Get paged child entities by id
        /// </summary>
        /// <param name="id"></param>
        /// <param name="type"></param>
        /// <param name="pageNumber"></param>
        /// <param name="pageSize"></param>
        /// <param name="orderBy"></param>
        /// <param name="orderDirection"></param>
        /// <param name="filter"></param>
        /// <returns></returns>
        public PagedResult <EntityBasic> GetPagedChildren(
            int id,
            UmbracoEntityTypes type,
            int pageNumber,
            int pageSize,
            string orderBy           = "SortOrder",
            Direction orderDirection = Direction.Ascending,
            string filter            = "")
        {
            if (pageNumber <= 0)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
            if (pageSize <= 0)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            var objectType = ConvertToObjectType(type);

            if (objectType.HasValue)
            {
                var entities = Services.EntityService.GetPagedChildren(id, objectType.Value, pageNumber - 1, pageSize, out var totalRecords,
                                                                       filter.IsNullOrWhiteSpace()
                        ? null
                        : SqlContext.Query <IUmbracoEntity>().Where(x => x.Name.Contains(filter)),
                                                                       Ordering.By(orderBy, orderDirection));

                if (totalRecords == 0)
                {
                    return(new PagedResult <EntityBasic>(0, 0, 0));
                }

                var pagedResult = new PagedResult <EntityBasic>(totalRecords, pageNumber, pageSize)
                {
                    Items = entities.Select(entity => Mapper.Map <IEntitySlim, EntityBasic>(entity, options =>
                                                                                            options.AfterMap((src, dest) => { dest.AdditionalData["hasChildren"] = src.HasChildren; })
                                                                                            )
                                            )
                };

                return(pagedResult);
            }

            //now we need to convert the unknown ones
            switch (type)
            {
            case UmbracoEntityTypes.PropertyType:
            case UmbracoEntityTypes.PropertyGroup:
            case UmbracoEntityTypes.Domain:
            case UmbracoEntityTypes.Language:
            case UmbracoEntityTypes.User:
            case UmbracoEntityTypes.Macro:
            default:
                throw new NotSupportedException("The " + typeof(EntityController) + " does not currently support data for the type " + type);
            }
        }
        protected void IndexPublishedContent(int contentParentId, int pageIndex, int pageSize,
                                             IReadOnlyList <IIndex> indexes)
        {
            IContent[] content;

            var publishedPages = new HashSet <int>();

            do
            {
                //add the published filter
                //note: We will filter for published variants in the validator
                content = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out _, _publishedQuery,
                                                              Ordering.By("Path", Direction.Ascending)).ToArray();


                if (content.Length > 0)
                {
                    var indexableContent = new List <IContent>();

                    foreach (var item in content)
                    {
                        if (item.Level == 1)
                        {
                            // first level pages are always published so no need to filter them
                            indexableContent.Add(item);
                            publishedPages.Add(item.Id);
                        }
                        else
                        {
                            if (publishedPages.Contains(item.ParentId))
                            {
                                // only index when parent is published
                                publishedPages.Add(item.Id);
                                indexableContent.Add(item);
                            }
                        }
                    }

                    var valueSets = _contentValueSetBuilder.GetValueSets(indexableContent.ToArray()).ToList();

                    // ReSharper disable once PossibleMultipleEnumeration
                    foreach (var index in indexes)
                    {
                        index.IndexItems(valueSets);
                    }
                }

                pageIndex++;
            } while (content.Length == pageSize);
        }
示例#7
0
        public void EntityService_Can_Get_Paged_Descendants_Ordering_Path()
        {
            var contentType = ServiceContext.ContentTypeService.Get("umbTextpage");

            var root = MockedContent.CreateSimpleContent(contentType);

            ServiceContext.ContentService.Save(root);
            var rootId = root.Id;
            var ids    = new List <int>();

            for (int i = 0; i < 10; i++)
            {
                var c1 = MockedContent.CreateSimpleContent(contentType, Guid.NewGuid().ToString(), root);
                ServiceContext.ContentService.Save(c1);
                ids.Add(c1.Id);
                root = c1; // make a hierarchy
            }

            var service = ServiceContext.EntityService;

            long total;

            var entities = service.GetPagedDescendants(rootId, UmbracoObjectTypes.Document, 0, 6, out total).ToArray();

            Assert.That(entities.Length, Is.EqualTo(6));
            Assert.That(total, Is.EqualTo(10));
            Assert.AreEqual(ids[0], entities[0].Id);

            entities = service.GetPagedDescendants(rootId, UmbracoObjectTypes.Document, 1, 6, out total).ToArray();
            Assert.That(entities.Length, Is.EqualTo(4));
            Assert.That(total, Is.EqualTo(10));
            Assert.AreEqual(ids[6], entities[0].Id);

            //Test ordering direction

            entities = service.GetPagedDescendants(rootId, UmbracoObjectTypes.Document, 0, 6, out total,
                                                   ordering: Ordering.By("Path", Direction.Descending)).ToArray();
            Assert.That(entities.Length, Is.EqualTo(6));
            Assert.That(total, Is.EqualTo(10));
            Assert.AreEqual(ids[ids.Count - 1], entities[0].Id);

            entities = service.GetPagedDescendants(rootId, UmbracoObjectTypes.Document, 1, 6, out total,
                                                   ordering: Ordering.By("Path", Direction.Descending)).ToArray();
            Assert.That(entities.Length, Is.EqualTo(4));
            Assert.That(total, Is.EqualTo(10));
            Assert.AreEqual(ids[ids.Count - 1 - 6], entities[0].Id);
        }
示例#8
0
        protected override void PopulateIndexes(IReadOnlyList <IIndex> indexes)
        {
            if (indexes.Count == 0)
            {
                return;
            }

            const int pageSize  = 10000;
            var       pageIndex = 0;

            var contentParentId = -1;

            if (_parentId.HasValue && _parentId.Value > 0)
            {
                contentParentId = _parentId.Value;
            }

            IContent[] content;

            do
            {
                if (!_publishedValuesOnly)
                {
                    content = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out _)
                              .ToArray();
                }
                else
                {
                    //add the published filter
                    //note: We will filter for published variants in the validator
                    content = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out _,
                                                                  _publishedQuery, Ordering.By("Path", Direction.Ascending)).ToArray();
                }

                if (content.Length > 0)
                {
                    // ReSharper disable once PossibleMultipleEnumeration
                    foreach (var index in indexes)
                    {
                        index.IndexItems(_contentValueSetBuilder.GetValueSets(content));
                    }
                }

                pageIndex++;
            } while (content.Length == pageSize);
        }
示例#9
0
    public void Get_Paged_Children_With_Media_Type_Filter()
    {
        var mediaType1 = MediaTypeBuilder.CreateImageMediaType("Image2");

        MediaTypeService.Save(mediaType1);
        var mediaType2 = MediaTypeBuilder.CreateImageMediaType("Image3");

        MediaTypeService.Save(mediaType2);

        for (var i = 0; i < 10; i++)
        {
            var m1 = MediaBuilder.CreateMediaImage(mediaType1, -1);
            MediaService.Save(m1);
            var m2 = MediaBuilder.CreateMediaImage(mediaType2, -1);
            MediaService.Save(m2);
        }

        var provider = ScopeProvider;

        using (provider.CreateScope())
        {
            var result = MediaService.GetPagedChildren(
                -1,
                0,
                11,
                out var total,
                provider.CreateQuery <IMedia>()
                .Where(x => new[] { mediaType1.Id, mediaType2.Id }.Contains(x.ContentTypeId)),
                Ordering.By("SortOrder"));
            Assert.AreEqual(11, result.Count());
            Assert.AreEqual(20, total);

            result = MediaService.GetPagedChildren(
                -1,
                1,
                11,
                out total,
                provider.CreateQuery <IMedia>()
                .Where(x => new[] { mediaType1.Id, mediaType2.Id }.Contains(x.ContentTypeId)),
                Ordering.By("SortOrder"));
            Assert.AreEqual(9, result.Count());
            Assert.AreEqual(20, total);
        }
    }
示例#10
0
        public void Get_Paged_Children_With_Media_Type_Filter()
        {
            MediaType mediaType1 = MediaTypeBuilder.CreateImageMediaType("Image2");

            MediaTypeService.Save(mediaType1);
            MediaType mediaType2 = MediaTypeBuilder.CreateImageMediaType("Image3");

            MediaTypeService.Save(mediaType2);

            for (int i = 0; i < 10; i++)
            {
                Media m1 = MediaBuilder.CreateMediaImage(mediaType1, -1);
                MediaService.Save(m1);
                Media m2 = MediaBuilder.CreateMediaImage(mediaType2, -1);
                MediaService.Save(m2);
            }

            IScopeProvider provider = ScopeProvider;

            using (provider.CreateScope())
            {
                IEnumerable <IMedia> result = MediaService.GetPagedChildren(
                    -1,
                    0,
                    11,
                    out long total,
                    provider.SqlContext.Query <IMedia>()
                    .Where(x => new[] { mediaType1.Id, mediaType2.Id }.Contains(x.ContentTypeId)),
                    Ordering.By("SortOrder", Direction.Ascending));
                Assert.AreEqual(11, result.Count());
                Assert.AreEqual(20, total);

                result = MediaService.GetPagedChildren(
                    -1,
                    1,
                    11,
                    out total,
                    provider.SqlContext.Query <IMedia>()
                    .Where(x => new[] { mediaType1.Id, mediaType2.Id }.Contains(x.ContentTypeId)),
                    Ordering.By("SortOrder", Direction.Ascending));
                Assert.AreEqual(9, result.Count());
                Assert.AreEqual(20, total);
            }
        }
示例#11
0
    public void GetPagedResultsByQuery_AlternateOrder()
    {
        // Arrange
        var provider = ScopeProvider;

        using (var scope = provider.CreateScope())
        {
            var repository = CreateRepository(provider, out var mediaTypeRepository);

            // Act
            var query  = provider.CreateQuery <IMedia>().Where(x => x.Level == 2);
            var result = repository.GetPage(query, 0, 1, out var totalRecords, null, Ordering.By("Name")).ToArray();

            // Assert
            Assert.That(totalRecords, Is.GreaterThanOrEqualTo(2));
            Assert.That(result.Count(), Is.EqualTo(1));
            Assert.That(result.First().Name, Is.EqualTo("Test File"));
        }
    }
示例#12
0
        private void RefreshMediaOfMediaTypes(int[] mediaTypeIds)
        {
            const int pageSize = 500;
            var       page     = 0;
            var       total    = long.MaxValue;

            while (page * pageSize < total)
            {
                var mediaToRefresh = _services.MediaService.GetPagedOfTypes(
                    //Re-index all content of these types
                    mediaTypeIds,
                    page++, pageSize, out total, null,
                    Ordering.By("Path", Direction.Ascending));

                foreach (var c in mediaToRefresh)
                {
                    ReIndexForMedia(c, c.Trashed == false);
                }
            }
        }
    private void RefreshMediaOfMediaTypes(int[] mediaTypeIds)
    {
        const int pageSize = 500;
        var       page     = 0;
        var       total    = long.MaxValue;

        while (page * pageSize < total)
        {
            IEnumerable <IMedia> mediaToRefresh = _mediaService.GetPagedOfTypes(

                // Re-index all content of these types
                mediaTypeIds,
                page++, pageSize, out total, null,
                Ordering.By("Path"));

            foreach (IMedia c in mediaToRefresh)
            {
                _umbracoIndexingHandler.ReIndexForMedia(c, c.Trashed == false);
            }
        }
    }
示例#14
0
    /// <inheritdoc />
    protected override Task<bool> IsAuthorized(AuthorizationHandlerContext context,
        ContentPermissionsPublishBranchRequirement requirement, IContent resource)
    {
        IUser? currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser;

        var denied = new List<IUmbracoEntity>();
        var page = 0;
        const int pageSize = 500;
        var total = long.MaxValue;

        while (page * pageSize < total)
        {
            // Order descendents by shallowest to deepest, this allows us to check permissions from top to bottom so we can exit
            // early if a permission higher up fails.
            IEnumerable<IEntitySlim> descendants = _entityService.GetPagedDescendants(
                resource.Id,
                UmbracoObjectTypes.Document,
                page++,
                pageSize,
                out total,
                ordering: Ordering.By("path"));

            foreach (IEntitySlim c in descendants)
            {
                // If this item's path has already been denied or if the user doesn't have access to it, add to the deny list.
                if (denied.Any(x => c.Path.StartsWith($"{x.Path},")) ||
                    _contentPermissions.CheckPermissions(
                        c,
                        currentUser,
                        requirement.Permission) == ContentPermissions.ContentAccess.Denied)
                {
                    denied.Add(c);
                }
            }
        }

        return Task.FromResult(denied.Count == 0);
    }
示例#15
0
    public IEnumerable <IRelation> GetPagedRelationsByQuery(IQuery <IRelation>?query, long pageIndex, int pageSize, out long totalRecords, Ordering?ordering)
    {
        Sql <ISqlContext> sql = GetBaseQuery(false);

        if (ordering == null || ordering.IsEmpty)
        {
            ordering = Ordering.By(SqlSyntax.GetQuotedColumn(Constants.DatabaseSchema.Tables.Relation, "id"));
        }

        var translator = new SqlTranslator <IRelation>(sql, query);

        sql = translator.Translate();

        // apply ordering
        ApplyOrdering(ref sql, ordering);

        var pageIndexToFetch    = pageIndex + 1;
        Page <RelationDto>?page = Database.Page <RelationDto>(pageIndexToFetch, pageSize, sql);
        List <RelationDto>?dtos = page.Items;

        totalRecords = page.TotalItems;

        var relTypes = _relationTypeRepository.GetMany(dtos.Select(x => x.RelationType).Distinct().ToArray())?
                       .ToDictionary(x => x.Id, x => x);

        var result = dtos.Select(r =>
        {
            if (relTypes is null || !relTypes.TryGetValue(r.RelationType, out IRelationType? relType))
            {
                throw new InvalidOperationException(string.Format("RelationType with Id: {0} doesn't exist", r.RelationType));
            }

            return(DtoToEntity(r, relType));
        }).WhereNotNull().ToList();

        return(result);
    }
示例#16
0
        public void GetPagedResultsByQuery_DescendingOrder()
        {
            var provider = TestObjects.GetScopeProvider(Logger);

            using (var scope = provider.CreateScope())
            {
                var repository = CreateRepository((IScopeAccessor)provider, out _);

                var query  = scope.SqlContext.Query <IContent>().Where(x => x.Level == 2);
                var result = repository.GetPage(query, 0, 1, out var totalRecords, null, Ordering.By("Name", Direction.Descending));

                Assert.That(totalRecords, Is.GreaterThanOrEqualTo(2));
                Assert.That(result.Count(), Is.EqualTo(1));
                Assert.That(result.First().Name, Is.EqualTo("Text Page 2"));
            }
        }
示例#17
0
        public void GetPagedResultsByQuery_FilterMatchingAll()
        {
            var provider = TestObjects.GetScopeProvider(Logger);

            using (var scope = provider.CreateScope())
            {
                var repository = CreateRepository((IScopeAccessor)provider, out _);

                var query = scope.SqlContext.Query <IContent>().Where(x => x.Level == 2);

                var filterQuery = scope.SqlContext.Query <IContent>().Where(x => x.Name.Contains("text"));
                var result      = repository.GetPage(query, 0, 1, out var totalRecords, filterQuery, Ordering.By("Name"));

                Assert.That(totalRecords, Is.EqualTo(2));
                Assert.That(result.Count(), Is.EqualTo(1));
                Assert.That(result.First().Name, Is.EqualTo("Text Page 1"));
            }
        }
示例#18
0
        // assumes content tree lock
        private void RebuildContentDbCache(IContentCacheDataSerializer serializer, int groupSize, IReadOnlyCollection <int> contentTypeIds)
        {
            Guid contentObjectType = Constants.ObjectTypes.Document;

            // remove all - if anything fails the transaction will rollback
            if (contentTypeIds == null || contentTypeIds.Count == 0)
            {
                // must support SQL-CE
                Database.Execute(
                    @"DELETE FROM cmsContentNu
WHERE cmsContentNu.nodeId IN (
    SELECT id FROM umbracoNode WHERE umbracoNode.nodeObjectType=@objType
)",
                    new { objType = contentObjectType });
            }
            else
            {
                // assume number of ctypes won't blow IN(...)
                // must support SQL-CE
                Database.Execute(
                    $@"DELETE FROM cmsContentNu
WHERE cmsContentNu.nodeId IN (
    SELECT id FROM umbracoNode
    JOIN {Constants.DatabaseSchema.Tables.Content} ON {Constants.DatabaseSchema.Tables.Content}.nodeId=umbracoNode.id
    WHERE umbracoNode.nodeObjectType=@objType
    AND {Constants.DatabaseSchema.Tables.Content}.contentTypeId IN (@ctypes)
)",
                    new { objType = contentObjectType, ctypes = contentTypeIds });
            }

            // insert back - if anything fails the transaction will rollback
            IQuery <IContent> query = SqlContext.Query <IContent>();

            if (contentTypeIds != null && contentTypeIds.Count > 0)
            {
                query = query.WhereIn(x => x.ContentTypeId, contentTypeIds); // assume number of ctypes won't blow IN(...)
            }

            long pageIndex = 0;
            long processed = 0;
            long total;

            do
            {
                // the tree is locked, counting and comparing to total is safe
                IEnumerable <IContent> descendants = _documentRepository.GetPage(query, pageIndex++, groupSize, out total, null, Ordering.By("Path"));
                var items = new List <ContentNuDto>();
                var count = 0;
                foreach (IContent c in descendants)
                {
                    // always the edited version
                    items.Add(GetDto(c, false, serializer));

                    // and also the published version if it makes any sense
                    if (c.Published)
                    {
                        items.Add(GetDto(c, true, serializer));
                    }

                    count++;
                }

                Database.BulkInsertRecords(items);
                processed += count;
            } while (processed < total);
        }
示例#19
0
        public void GetPagedResultsByQuery_FirstPage()
        {
            var provider = TestObjects.GetScopeProvider(Logger);

            using (var scope = provider.CreateScope())
            {
                var repository = CreateRepository((IScopeAccessor)provider, out _);

                var query = scope.SqlContext.Query <IContent>().Where(x => x.Level == 2);

                try
                {
                    scope.Database.AsUmbracoDatabase().EnableSqlTrace = true;
                    scope.Database.AsUmbracoDatabase().EnableSqlCount = true;

                    var result = repository.GetPage(query, 0, 1, out var totalRecords, null, Ordering.By("Name"));

                    Assert.That(totalRecords, Is.GreaterThanOrEqualTo(2));
                    Assert.That(result.Count(), Is.EqualTo(1));
                    Assert.That(result.First().Name, Is.EqualTo("Text Page 1"));
                }
                finally
                {
                    scope.Database.AsUmbracoDatabase().EnableSqlTrace = false;
                    scope.Database.AsUmbracoDatabase().EnableSqlCount = false;
                }
            }
        }
示例#20
0
        // assumes member tree lock
        private void RebuildMemberDbCache(IContentCacheDataSerializer serializer, int groupSize, IReadOnlyCollection <int> contentTypeIds)
        {
            Guid memberObjectType = Constants.ObjectTypes.Member;

            // remove all - if anything fails the transaction will rollback
            if (contentTypeIds == null || contentTypeIds.Count == 0)
            {
                // must support SQL-CE
                Database.Execute(
                    @"DELETE FROM cmsContentNu
WHERE cmsContentNu.nodeId IN (
    SELECT id FROM umbracoNode WHERE umbracoNode.nodeObjectType=@objType
)",
                    new { objType = memberObjectType });
            }
            else
            {
                // assume number of ctypes won't blow IN(...)
                // must support SQL-CE
                Database.Execute(
                    $@"DELETE FROM cmsContentNu
WHERE cmsContentNu.nodeId IN (
    SELECT id FROM umbracoNode
    JOIN {Constants.DatabaseSchema.Tables.Content} ON {Constants.DatabaseSchema.Tables.Content}.nodeId=umbracoNode.id
    WHERE umbracoNode.nodeObjectType=@objType
    AND {Constants.DatabaseSchema.Tables.Content}.contentTypeId IN (@ctypes)
)",
                    new { objType = memberObjectType, ctypes = contentTypeIds });
            }

            // insert back - if anything fails the transaction will rollback
            IQuery <IMember> query = SqlContext.Query <IMember>();

            if (contentTypeIds != null && contentTypeIds.Count > 0)
            {
                query = query.WhereIn(x => x.ContentTypeId, contentTypeIds); // assume number of ctypes won't blow IN(...)
            }

            long pageIndex = 0;
            long processed = 0;
            long total;

            do
            {
                IEnumerable <IMember> descendants = _memberRepository.GetPage(query, pageIndex++, groupSize, out total, null, Ordering.By("Path"));
                ContentNuDto[]        items       = descendants.Select(m => GetDto(m, false, serializer)).ToArray();
                Database.BulkInsertRecords(items);
                processed += items.Length;
            } while (processed < total);
        }
示例#21
0
        public void GetPagedResultsByQuery_With_Variant_Names()
        {
            // one invariant content type named "umbInvariantTextPage"
            //
            var invariantCt = MockedContentTypes.CreateSimpleContentType("umbInvariantTextpage", "Invariant Textpage");

            invariantCt.Variations = ContentVariation.Nothing;
            foreach (var p in invariantCt.PropertyTypes)
            {
                p.Variations = ContentVariation.Nothing;
            }
            ServiceContext.FileService.SaveTemplate(invariantCt.DefaultTemplate); // else, FK violation on contentType!
            ServiceContext.ContentTypeService.Save(invariantCt);

            // one variant (by culture) content type named "umbVariantTextPage"
            // with properties, every 2nd one being variant (by culture), the other being invariant
            //
            var variantCt = MockedContentTypes.CreateSimpleContentType("umbVariantTextpage", "Variant Textpage");

            variantCt.Variations = ContentVariation.Culture;
            var propTypes = variantCt.PropertyTypes.ToList();

            for (var i = 0; i < propTypes.Count; i++)
            {
                var p = propTypes[i];
                p.Variations = i % 2 == 0 ? ContentVariation.Culture : ContentVariation.Nothing;
            }
            ServiceContext.FileService.SaveTemplate(variantCt.DefaultTemplate); // else, FK violation on contentType!
            ServiceContext.ContentTypeService.Save(variantCt);

            invariantCt.AllowedContentTypes = new[] { new ContentTypeSort(invariantCt.Id, 0), new ContentTypeSort(variantCt.Id, 1) };
            ServiceContext.ContentTypeService.Save(invariantCt);

            //create content

            var root = MockedContent.CreateSimpleContent(invariantCt);

            ServiceContext.ContentService.Save(root);

            var children = new List <IContent>();

            for (var i = 0; i < 25; i++)
            {
                var isInvariant = i % 2 == 0;
                var name        = (isInvariant ? "INV" : "VAR") + "_" + Guid.NewGuid();
                var culture     = isInvariant ? null : "en-US";

                var child = MockedContent.CreateSimpleContent(
                    isInvariant ? invariantCt : variantCt,
                    name, root,
                    culture,
                    setPropertyValues: isInvariant);

                if (!isInvariant)
                {
                    //manually set the property values since we have mixed variant/invariant property types
                    child.SetValue("title", name + " Subpage", culture: culture);
                    child.SetValue("bodyText", "This is a subpage", culture: null); //this one is invariant
                    child.SetValue("author", "John Doe", culture: culture);
                }

                ServiceContext.ContentService.Save(child);
                children.Add(child);
            }

            var child1 = children[1];

            Assert.IsTrue(child1.ContentType.VariesByCulture());
            Assert.IsTrue(child1.Name.StartsWith("VAR"));
            Assert.IsTrue(child1.GetCultureName("en-US").StartsWith("VAR"));

            var provider = TestObjects.GetScopeProvider(Logger);

            using (var scope = provider.CreateScope())
            {
                var repository = CreateRepository((IScopeAccessor)provider, out _);

                var child = repository.Get(children[1].Id); // 1 is variant
                Assert.IsTrue(child.ContentType.VariesByCulture());
                Assert.IsTrue(child.Name.StartsWith("VAR"));
                Assert.IsTrue(child.GetCultureName("en-US").StartsWith("VAR"));

                try
                {
                    scope.Database.AsUmbracoDatabase().EnableSqlTrace = true;
                    scope.Database.AsUmbracoDatabase().EnableSqlCount = true;

                    var query  = scope.SqlContext.Query <IContent>().Where(x => x.ParentId == root.Id);
                    var result = repository.GetPage(query, 0, 20, out var totalRecords, null, Ordering.By("UpdateDate"));

                    Assert.AreEqual(25, totalRecords);
                    foreach (var r in result)
                    {
                        var isInvariant = r.ContentType.Alias == "umbInvariantTextpage";
                        var name        = isInvariant ? r.Name : r.CultureInfos["en-US"].Name;
                        var namePrefix  = isInvariant ? "INV" : "VAR";

                        //ensure the correct name (invariant vs variant) is in the result
                        Assert.IsTrue(name.StartsWith(namePrefix));

                        foreach (var p in r.Properties)
                        {
                            //ensure there is a value for the correct variant/invariant property
                            var value = p.GetValue(p.PropertyType.Variations.VariesByNothing() ? null : "en-US");
                            Assert.IsNotNull(value);
                        }
                    }
                }
                finally
                {
                    scope.Database.AsUmbracoDatabase().EnableSqlTrace = false;
                    scope.Database.AsUmbracoDatabase().EnableSqlCount = false;
                }
            }
        }
示例#22
0
        /// <summary>
        /// Updates indexes based on content changes
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private void ContentCacheRefresherUpdated(ContentCacheRefresher sender, CacheRefresherEventArgs args)
        {
            if (Suspendable.ExamineEvents.CanIndex == false)
            {
                return;
            }

            if (args.MessageType != MessageType.RefreshByPayload)
            {
                throw new NotSupportedException();
            }

            var contentService = _services.ContentService;

            foreach (var payload in (ContentCacheRefresher.JsonPayload[])args.MessageObject)
            {
                if (payload.ChangeTypes.HasType(TreeChangeTypes.Remove))
                {
                    // delete content entirely (with descendants)
                    //  false: remove entirely from all indexes
                    DeleteIndexForEntity(payload.Id, false);
                }
                else if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll))
                {
                    // ExamineEvents does not support RefreshAll
                    // just ignore that payload
                    // so what?!

                    // TODO: Rebuild the index at this point?
                }
                else // RefreshNode or RefreshBranch (maybe trashed)
                {
                    // don't try to be too clever - refresh entirely
                    // there has to be race conditions in there ;-(

                    var content = contentService.GetById(payload.Id);
                    if (content == null)
                    {
                        // gone fishing, remove entirely from all indexes (with descendants)
                        DeleteIndexForEntity(payload.Id, false);
                        continue;
                    }

                    IContent published = null;
                    if (content.Published && contentService.IsPathPublished(content))
                    {
                        published = content;
                    }

                    if (published == null)
                    {
                        DeleteIndexForEntity(payload.Id, true);
                    }

                    // just that content
                    ReIndexForContent(content, published != null);

                    // branch
                    if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch))
                    {
                        var       masked   = published == null ? null : new List <int>();
                        const int pageSize = 500;
                        var       page     = 0;
                        var       total    = long.MaxValue;
                        while (page * pageSize < total)
                        {
                            var descendants = contentService.GetPagedDescendants(content.Id, page++, pageSize, out total,
                                                                                 //order by shallowest to deepest, this allows us to check it's published state without checking every item
                                                                                 ordering: Ordering.By("Path", Direction.Ascending));

                            foreach (var descendant in descendants)
                            {
                                published = null;
                                if (masked != null) // else everything is masked
                                {
                                    if (masked.Contains(descendant.ParentId) || !descendant.Published)
                                    {
                                        masked.Add(descendant.Id);
                                    }
                                    else
                                    {
                                        published = descendant;
                                    }
                                }

                                ReIndexForContent(descendant, published != null);
                            }
                        }
                    }
                }

                // NOTE
                //
                // DeleteIndexForEntity is handled by UmbracoContentIndexer.DeleteFromIndex() which takes
                //  care of also deleting the descendants
                //
                // ReIndexForContent is NOT taking care of descendants so we have to reload everything
                //  again in order to process the branch - we COULD improve that by just reloading the
                //  XML from database instead of reloading content & re-serializing!
                //
                // BUT ... pretty sure it is! see test "Index_Delete_Index_Item_Ensure_Heirarchy_Removed"
            }
        }
示例#23
0
        private void AddBlogPosts(IContent archiveNode, BlogMLDocument blogMlDoc, string categoryGroup, string tagGroup)
        {
            const int pageSize  = 1000;
            var       pageIndex = 0;

            IContent[] posts;
            do
            {
                posts = _contentService.GetPagedChildren(archiveNode.Id, pageIndex, pageSize, out long _, ordering: Ordering.By("createDate")).ToArray();

                foreach (var child in posts)
                {
                    string content = "";
                    if (child.ContentType.Alias.InvariantEquals("ArticulateRichText"))
                    {
                        //TODO: this would also need to export all macros
                        content = child.GetValue <string>("richText");
                    }
                    else if (child.ContentType.Alias.InvariantEquals("ArticulateMarkdown"))
                    {
                        var md = new Markdown();
                        content = md.Transform(child.GetValue <string>("markdown"));
                    }

                    var postUrl         = new Uri(_umbracoContextAccessor.UmbracoContext.UrlProvider.GetUrl(child.Id), UriKind.RelativeOrAbsolute);
                    var postAbsoluteUrl = new Uri(_umbracoContextAccessor.UmbracoContext.UrlProvider.GetUrl(child.Id, UrlProviderMode.Absolute), UriKind.Absolute);
                    var blogMlPost      = new BlogMLPost()
                    {
                        Id             = child.Key.ToString(),
                        Name           = new BlogMLTextConstruct(child.Name),
                        Title          = new BlogMLTextConstruct(child.Name),
                        ApprovalStatus = BlogMLApprovalStatus.Approved,
                        PostType       = BlogMLPostType.Normal,
                        CreatedOn      = child.CreateDate,
                        LastModifiedOn = child.UpdateDate,
                        Content        = new BlogMLTextConstruct(content, BlogMLContentType.Html),
                        Excerpt        = new BlogMLTextConstruct(child.GetValue <string>("excerpt")),
                        Url            = postUrl
                    };

                    var author = blogMlDoc.Authors.FirstOrDefault(x => x.Title != null && x.Title.Content.InvariantEquals(child.GetValue <string>("author")));
                    if (author != null)
                    {
                        blogMlPost.Authors.Add(author.Id);
                    }

                    var categories = _tagService.GetTagsForEntity(child.Id, categoryGroup);

                    foreach (var category in categories)
                    {
                        blogMlPost.Categories.Add(category.Id.ToString());
                    }

                    var tags = _tagService.GetTagsForEntity(child.Id, tagGroup).Select(t => t.Text).ToList();
                    if (tags?.Any() == true)
                    {
                        blogMlPost.AddExtension(
                            new Syndication.BlogML.TagsSyndicationExtension()
                        {
                            Context = { Tags = new Collection <string>(tags) }
                        });
                    }

                    //add the image attached if there is one
                    if (child.HasProperty("postImage"))
                    {
                        try
                        {
                            var val  = child.GetValue <string>("postImage");
                            var json = JsonConvert.DeserializeObject <JObject>(val);
                            var src  = json.Value <string>("src");

                            var mime = ImageMimeType(src);

                            if (!mime.IsNullOrWhiteSpace())
                            {
                                var imageUrl = new Uri(postAbsoluteUrl.GetLeftPart(UriPartial.Authority) + src.EnsureStartsWith('/'), UriKind.Absolute);
                                blogMlPost.Attachments.Add(new BlogMLAttachment
                                {
                                    Content     = string.Empty, //this is used for embedded resources
                                    Url         = imageUrl,
                                    ExternalUri = imageUrl,
                                    IsEmbedded  = false,
                                    MimeType    = mime
                                });
                            }
                        }
                        catch (Exception ex)
                        {
                            _logger.Error <BlogMlExporter>(ex, "Could not add the file to the blogML post attachments");
                        }
                    }



                    blogMlDoc.AddPost(blogMlPost);
                }

                pageIndex++;
            } while (posts.Length == pageSize);
        }
示例#24
0
        public void GetPagedResultsByQuery_FilterMatchingAll()
        {
            // Arrange
            var provider = TestObjects.GetScopeProvider(Logger);

            using (var scope = provider.CreateScope())
            {
                var repository = CreateRepository(provider, out _);

                // Act
                var query = scope.SqlContext.Query <IMedia>().Where(x => x.Level == 2);

                var filter = scope.SqlContext.Query <IMedia>().Where(x => x.Name.Contains("Test"));
                var result = repository.GetPage(query, 0, 1, out var totalRecords, filter, Ordering.By("SortOrder"));

                // Assert
                Assert.That(totalRecords, Is.EqualTo(2));
                Assert.That(result.Count(), Is.EqualTo(1));
                Assert.That(result.First().Name, Is.EqualTo("Test Image"));
            }
        }
示例#25
0
        public void GetPagedResultsByQuery_AlternateOrder()
        {
            // Arrange
            IScopeProvider provider = ScopeProvider;

            using (IScope scope = provider.CreateScope())
            {
                MediaRepository repository = CreateRepository(provider, out MediaTypeRepository mediaTypeRepository);

                // Act
                IQuery <IMedia>      query  = provider.CreateQuery <IMedia>().Where(x => x.Level == 2);
                IEnumerable <IMedia> result = repository.GetPage(query, 0, 1, out long totalRecords, null, Ordering.By("Name"));

                // Assert
                Assert.That(totalRecords, Is.GreaterThanOrEqualTo(2));
                Assert.That(result.Count(), Is.EqualTo(1));
                Assert.That(result.First().Name, Is.EqualTo("Test File"));
            }
        }
示例#26
0
        public void GetPagedResultsByQuery_DescendingOrder()
        {
            // Arrange
            var provider = TestObjects.GetScopeProvider(Logger);

            using (var scope = provider.CreateScope())
            {
                MediaTypeRepository mediaTypeRepository;
                var repository = CreateRepository(provider, out mediaTypeRepository);

                // Act
                var  query = scope.SqlContext.Query <IMedia>().Where(x => x.Level == 2);
                long totalRecords;
                var  result = repository.GetPage(query, 0, 1, out totalRecords, null, Ordering.By("SortOrder", Direction.Descending));

                // Assert
                Assert.That(totalRecords, Is.GreaterThanOrEqualTo(2));
                Assert.That(result.Count(), Is.EqualTo(1));
                Assert.That(result.First().Name, Is.EqualTo("Test File"));
            }
        }
示例#27
0
        public void GetPagedResultsByQuery_FilterMatchingAll()
        {
            // Arrange
            IScopeProvider provider = ScopeProvider;

            using (IScope scope = provider.CreateScope())
            {
                MediaRepository repository = CreateRepository(provider, out _);

                // Act
                IQuery <IMedia> query = provider.CreateQuery <IMedia>().Where(x => x.Level == 2);

                IQuery <IMedia>      filter = provider.CreateQuery <IMedia>().Where(x => x.Name.Contains("Test"));
                IEnumerable <IMedia> result = repository.GetPage(query, 0, 1, out long totalRecords, filter, Ordering.By("SortOrder"));

                // Assert
                Assert.That(totalRecords, Is.EqualTo(2));
                Assert.That(result.Count(), Is.EqualTo(1));
                Assert.That(result.First().Name, Is.EqualTo("Test Image"));
            }
        }
        public PagedResult <ContentItemBasic <ContentPropertyBasic> > GetChildren(int id,
                                                                                  int pageNumber           = 0,
                                                                                  int pageSize             = 0,
                                                                                  string orderBy           = "SortOrder",
                                                                                  Direction orderDirection = Direction.Ascending,
                                                                                  bool orderBySystemField  = true,
                                                                                  string filter            = "")
        {
            //if a request is made for the root node data but the user's start node is not the default, then
            // we need to return their start nodes
            if (id == Constants.System.Root && UserStartNodes.Length > 0 && UserStartNodes.Contains(Constants.System.Root) == false)
            {
                if (pageNumber > 0)
                {
                    return(new PagedResult <ContentItemBasic <ContentPropertyBasic> >(0, 0, 0));
                }
                var nodes = _mediaService.GetByIds(UserStartNodes).ToArray();
                if (nodes.Length == 0)
                {
                    return(new PagedResult <ContentItemBasic <ContentPropertyBasic> >(0, 0, 0));
                }
                if (pageSize < nodes.Length)
                {
                    pageSize = nodes.Length; // bah
                }
                var pr = new PagedResult <ContentItemBasic <ContentPropertyBasic> >(nodes.Length, pageNumber, pageSize)
                {
                    Items = nodes.Select(_umbracoMapper.Map <IMedia, ContentItemBasic <ContentPropertyBasic> >)
                };
                return(pr);
            }

            // else proceed as usual

            long          totalChildren;
            List <IMedia> children;

            if (pageNumber > 0 && pageSize > 0)
            {
                IQuery <IMedia> queryFilter = null;
                if (filter.IsNullOrWhiteSpace() == false)
                {
                    //add the default text filter
                    queryFilter = _sqlContext.Query <IMedia>()
                                  .Where(x => x.Name.Contains(filter));
                }

                children = _mediaService
                           .GetPagedChildren(
                    id, (pageNumber - 1), pageSize,
                    out totalChildren,
                    queryFilter,
                    Ordering.By(orderBy, orderDirection, isCustomField: !orderBySystemField)).ToList();
            }
            else
            {
                //better to not use this without paging where possible, currently only the sort dialog does
                children      = _mediaService.GetPagedChildren(id, 0, int.MaxValue, out var total).ToList();
                totalChildren = children.Count;
            }

            if (totalChildren == 0)
            {
                return(new PagedResult <ContentItemBasic <ContentPropertyBasic> >(0, 0, 0));
            }

            var pagedResult = new PagedResult <ContentItemBasic <ContentPropertyBasic> >(totalChildren, pageNumber, pageSize);

            pagedResult.Items = children
                                .Select(_umbracoMapper.Map <IMedia, ContentItemBasic <ContentPropertyBasic> >);

            return(pagedResult);
        }
示例#29
0
        public PagedResult <EntityBasic> GetPagedDescendants(
            int id,
            UmbracoEntityTypes type,
            int pageNumber,
            int pageSize,
            bool ignoreUserStartNodes,
            string orderBy           = "SortOrder",
            Direction orderDirection = Direction.Ascending,
            string filter            = "")
        {
            if (pageNumber <= 0)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
            if (pageSize <= 0)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }

            var objectType = ConvertToObjectType(type);

            if (objectType.HasValue)
            {
                IEnumerable <IUmbracoEntity> entities;
                long totalRecords;

                if (id == Constants.System.Root)
                {
                    // root is special: we reduce it to start nodes

                    int[] aids = null;
                    switch (type)
                    {
                    case UmbracoEntityTypes.Document:
                        aids = Security.CurrentUser.CalculateContentStartNodeIds(Services.EntityService);
                        break;

                    case UmbracoEntityTypes.Media:
                        aids = Security.CurrentUser.CalculateMediaStartNodeIds(Services.EntityService);
                        break;
                    }

                    entities = aids == null || aids.Contains(Constants.System.Root) || ignoreUserStartNodes
                        ? Services.EntityService.GetPagedDescendants(objectType.Value, pageNumber - 1, pageSize, out totalRecords,
                                                                     SqlContext.Query <IUmbracoEntity>().Where(x => x.Name.Contains(filter)),
                                                                     Ordering.By(orderBy, orderDirection), includeTrashed : false)
                        : Services.EntityService.GetPagedDescendants(aids, objectType.Value, pageNumber - 1, pageSize, out totalRecords,
                                                                     SqlContext.Query <IUmbracoEntity>().Where(x => x.Name.Contains(filter)),
                                                                     Ordering.By(orderBy, orderDirection));
                }
                else
                {
                    entities = Services.EntityService.GetPagedDescendants(id, objectType.Value, pageNumber - 1, pageSize, out totalRecords,
                                                                          SqlContext.Query <IUmbracoEntity>().Where(x => x.Name.Contains(filter)),
                                                                          Ordering.By(orderBy, orderDirection));
                }

                if (totalRecords == 0)
                {
                    return(new PagedResult <EntityBasic>(0, 0, 0));
                }

                var pagedResult = new PagedResult <EntityBasic>(totalRecords, pageNumber, pageSize)
                {
                    Items = entities.Select(MapEntities())
                };

                return(pagedResult);
            }

            //now we need to convert the unknown ones
            switch (type)
            {
            case UmbracoEntityTypes.PropertyType:
            case UmbracoEntityTypes.PropertyGroup:
            case UmbracoEntityTypes.Domain:
            case UmbracoEntityTypes.Language:
            case UmbracoEntityTypes.User:
            case UmbracoEntityTypes.Macro:
            default:
                throw new NotSupportedException("The " + typeof(EntityController) + " does not currently support data for the type " + type);
            }
        }
        /// <summary>
        /// Updates indexes based on content changes
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public void Handle(ContentCacheRefresherNotification args)
        {
            if (!_umbracoIndexingHandler.Enabled)
            {
                return;
            }
            if (Suspendable.ExamineEvents.CanIndex == false)
            {
                return;
            }

            if (args.MessageType != MessageType.RefreshByPayload)
            {
                throw new NotSupportedException();
            }

            // Used to track permanent deletions so we can bulk delete from the index
            // when needed. For example, when emptying the recycle bin, else it will
            // individually update the index which will be much slower.
            HashSet <int> deleteBatch = null;

            foreach (var payload in (ContentCacheRefresher.JsonPayload[])args.MessageObject)
            {
                if (payload.ChangeTypes.HasType(TreeChangeTypes.Remove))
                {
                    if (deleteBatch == null)
                    {
                        deleteBatch = new HashSet <int>();
                    }

                    deleteBatch.Add(payload.Id);
                }
                else if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll))
                {
                    // ExamineEvents does not support RefreshAll
                    // just ignore that payload
                    // so what?!

                    // TODO: Rebuild the index at this point?
                }
                else // RefreshNode or RefreshBranch (maybe trashed)
                {
                    if (deleteBatch != null && deleteBatch.Contains(payload.Id))
                    {
                        // the same node has already been deleted, to ensure ordering is
                        // handled, we'll need to execute all queued deleted items now
                        // and reset the deleted items list.
                        _umbracoIndexingHandler.DeleteIndexForEntities(deleteBatch, false);
                        deleteBatch = null;
                    }

                    // don't try to be too clever - refresh entirely
                    // there has to be race conditions in there ;-(

                    var content = _contentService.GetById(payload.Id);
                    if (content == null)
                    {
                        // gone fishing, remove entirely from all indexes (with descendants)
                        _umbracoIndexingHandler.DeleteIndexForEntity(payload.Id, false);
                        continue;
                    }

                    IContent published = null;
                    if (content.Published && _contentService.IsPathPublished(content))
                    {
                        published = content;
                    }

                    if (published == null)
                    {
                        _umbracoIndexingHandler.DeleteIndexForEntity(payload.Id, true);
                    }

                    // just that content
                    _umbracoIndexingHandler.ReIndexForContent(content, published != null);

                    // branch
                    if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch))
                    {
                        var       masked   = published == null ? null : new List <int>();
                        const int pageSize = 500;
                        var       page     = 0;
                        var       total    = long.MaxValue;
                        while (page * pageSize < total)
                        {
                            var descendants = _contentService.GetPagedDescendants(content.Id, page++, pageSize, out total,
                                                                                  //order by shallowest to deepest, this allows us to check it's published state without checking every item
                                                                                  ordering: Ordering.By("Path", Direction.Ascending));

                            foreach (var descendant in descendants)
                            {
                                published = null;
                                if (masked != null) // else everything is masked
                                {
                                    if (masked.Contains(descendant.ParentId) || !descendant.Published)
                                    {
                                        masked.Add(descendant.Id);
                                    }
                                    else
                                    {
                                        published = descendant;
                                    }
                                }

                                _umbracoIndexingHandler.ReIndexForContent(descendant, published != null);
                            }
                        }
                    }
                }

                // NOTE
                //
                // DeleteIndexForEntity is handled by UmbracoContentIndexer.DeleteFromIndex() which takes
                //  care of also deleting the descendants
                //
                // ReIndexForContent is NOT taking care of descendants so we have to reload everything
                //  again in order to process the branch - we COULD improve that by just reloading the
                //  XML from database instead of reloading content & re-serializing!
                //
                // BUT ... pretty sure it is! see test "Index_Delete_Index_Item_Ensure_Heirarchy_Removed"
            }

            if (deleteBatch != null)
            {
                // process the delete batch
                _umbracoIndexingHandler.DeleteIndexForEntities(deleteBatch, false);
            }
        }