/// <summary> /// Gets the sorted list of children items from the query. /// </summary> /// <param name="options">The options that describe the current operation.</param> /// <param name="cachedEntityType">The cached entity type object that describes the items.</param> /// <param name="itemsQry">The items qry.</param> /// <returns>A list of child items that should be included in the results.</returns> private List <TreeItemViewModel> GetChildrenItems(CategoryItemTreeOptions options, EntityTypeCache cachedEntityType, IQueryable <ICategorized> itemsQry) { // Do a ToList() to load from database prior to ordering // by name, just in case Name is a virtual property. var itemsList = itemsQry.ToList(); var entityTypeIsSchedule = cachedEntityType.Id == EntityTypeCache.GetId <Rock.Model.Schedule>(); List <ICategorized> sortedItemsList; // Sort the items by the name, unless it is a schedule. if (entityTypeIsSchedule && itemsList.OfType <Rock.Model.Schedule>() != null) { sortedItemsList = itemsList.OfType <Rock.Model.Schedule>().ToList().OrderByOrderAndNextScheduledDateTime().OfType <ICategorized>().ToList(); } else { sortedItemsList = itemsList.OrderBy(i => i.Name).ToList(); } var children = new List <TreeItemViewModel>(); // Walk each item from the sorted list and determine if we it // should be added to the list of children. foreach (var categorizedItem in sortedItemsList) { // Ensure the person is authorized to view this item and that // the item is of type IEntity so we can get to the Guid. if (categorizedItem.IsAuthorized(Authorization.VIEW, Person) && categorizedItem is IEntity categorizedEntityItem) { var categoryItem = new TreeItemViewModel { Value = categorizedEntityItem.Guid.ToString(), Text = categorizedItem.Name, IconCssClass = categorizedItem.GetPropertyValue("IconCssClass") as string ?? options.DefaultIconCssClass }; if (categorizedItem is IHasActiveFlag activatedItem) { categoryItem.IsActive = activatedItem.IsActive; } children.Add(categoryItem); } } return(children); }
/// <summary> /// Determines if the category has any child categories or items. /// </summary> /// <param name="options">The options that describe the current operation.</param> /// <param name="categoryService">The category service.</param> /// <param name="serviceInstance">The service instance used to access the items.</param> /// <param name="categoryId">The category item identifier.</param> /// <returns><c>true</c> if the category has children, <c>false</c> otherwise.</returns> private bool DoesCategoryHaveChildren(CategoryItemTreeOptions options, CategoryService categoryService, IService serviceInstance, string categoryId) { var parentGuid = categoryId.AsGuid(); // First try a simple query on categories. This is the cheaper // of the two operations. foreach (var childCategory in categoryService.Queryable().Where(c => c.ParentCategory.Guid == parentGuid)) { if (childCategory.IsAuthorized(Authorization.VIEW, Person)) { return(true); } } // If we didn't find any children from the above then try looking // for any items that reference this category. var itemOptions = new CategorizedItemQueryOptions { CategoryGuid = parentGuid, IncludeInactiveItems = options.IncludeInactiveItems, IncludeUnnamedEntityItems = options.IncludeUnnamedEntityItems, ItemFilterPropertyName = options.ItemFilterPropertyName, ItemFilterPropertyValue = options.ItemFilterPropertyValue }; var childItems = categoryService.GetCategorizedItemQuery(serviceInstance, itemOptions); if (childItems != null) { foreach (var categorizedItem in childItems) { if (categorizedItem != null && categorizedItem.IsAuthorized(Authorization.VIEW, Person)) { return(true); } } } return(false); }
/// <summary> /// Gets the categorized tree items. This supports various options /// for filtering and determine how much data to load. /// </summary> /// <param name="options">The options that describe the request.</param> /// <returns>A list of view models that describe the tree of categories and items.</returns> public List <TreeItemViewModel> GetCategorizedTreeItems(CategoryItemTreeOptions options = null) { options = options ?? DefaultCategoryItemTreeOptions; // Initialize the basic query. var categoryService = new CategoryService(RockContext); var childQueryOptions = new ChildCategoryQueryOptions { ParentGuid = options.ParentGuid, EntityTypeGuid = options.EntityTypeGuid, IncludeCategoryGuids = options.IncludeCategoryGuids, ExcludeCategoryGuids = options.ExcludeCategoryGuids, EntityTypeQualifierColumn = options.EntityTypeQualifierColumn, EntityTypeQualifierValue = options.EntityTypeQualifierValue }; var qry = categoryService.GetChildCategoryQuery(childQueryOptions); // Cache the service instance for later use. If we have specified an // entity type then this ends up getting set. IService serviceInstance = null; var cachedEntityType = options.EntityTypeGuid.HasValue ? EntityTypeCache.Get(options.EntityTypeGuid.Value) : null; // If we have been requested to limit the results to a specific entity // type then apply those filters. if (cachedEntityType != null) { // Attempt to initialize the entity service instance. This also // checks if the entity supports the active flag. if (cachedEntityType.AssemblyName != null) { Type entityType = cachedEntityType.GetEntityType(); if (entityType != null) { Type[] modelType = { entityType }; Type genericServiceType = typeof(Rock.Data.Service <>); Type modelServiceType = genericServiceType.MakeGenericType(modelType); serviceInstance = Activator.CreateInstance(modelServiceType, new object[] { new RockContext() }) as IService; } } } var categoryList = qry.OrderBy(c => c.Order).ThenBy(c => c.Name).ToList(); // Get all the categories from the query and then filter on security. var categoryItemList = categoryList .Where(c => c.IsAuthorized(Authorization.VIEW, Person)) .Select(c => new TreeItemViewModel { Value = c.Guid.ToString(), Text = c.Name, IsFolder = true, IconCssClass = c.IconCssClass }) .ToList(); if (options.GetCategorizedItems) { var itemOptions = new CategorizedItemQueryOptions { CategoryGuid = options.ParentGuid, IncludeInactiveItems = options.IncludeInactiveItems, IncludeUnnamedEntityItems = options.IncludeUnnamedEntityItems, ItemFilterPropertyName = options.ItemFilterPropertyName, ItemFilterPropertyValue = options.ItemFilterPropertyValue }; var itemsQry = categoryService.GetCategorizedItemQuery(serviceInstance, itemOptions); if (itemsQry != null) { var childItems = GetChildrenItems(options, cachedEntityType, itemsQry); categoryItemList.AddRange(childItems); } } if (options.LazyLoad) { // Try to figure out which items have viewable children in // the existing list and set them appropriately. foreach (var categoryItemListItem in categoryItemList) { if (categoryItemListItem.IsFolder) { categoryItemListItem.HasChildren = DoesCategoryHaveChildren(options, categoryService, serviceInstance, categoryItemListItem.Value); } } } else { foreach (var item in categoryItemList) { var parentGuid = item.Value.AsGuidOrNull(); if (item.Children == null) { item.Children = new List <TreeItemViewModel>(); } GetAllDescendants(item, Person, categoryService, serviceInstance, cachedEntityType, options); } } // If they don't want empty categories then filter out categories // that do not have child items. if (!options.IncludeCategoriesWithoutChildren) { categoryItemList = categoryItemList .Where(a => !a.IsFolder || (a.IsFolder && a.HasChildren)) .ToList(); } return(categoryItemList); }
/// <summary> /// Gets all both category and non-category item decendents for the provided categoryItem. /// This method updates the provided categoryItem. /// </summary> /// <param name="categoryItem">The category item.</param> /// <param name="currentPerson">The current person.</param> /// <param name="categoryService">The category service.</param> /// <param name="serviceInstance">The service instance.</param> /// <param name="cachedEntityType">The cached entity type of the items.</param> /// <param name="options">The options that describe the current operation.</param> /// <returns></returns> private TreeItemViewModel GetAllDescendants(TreeItemViewModel categoryItem, Person currentPerson, CategoryService categoryService, IService serviceInstance, EntityTypeCache cachedEntityType, CategoryItemTreeOptions options) { if (categoryItem.IsFolder) { var parentGuid = categoryItem.Value.AsGuidOrNull(); var childCategories = categoryService.Queryable() .AsNoTracking() .Where(c => c.ParentCategory.Guid == parentGuid) .OrderBy(c => c.Order) .ThenBy(c => c.Name); foreach (var childCategory in childCategories) { if (childCategory.IsAuthorized(Authorization.VIEW, currentPerson)) { // This category has child categories that the person can view so add them to categoryItemList categoryItem.HasChildren = true; var childCategoryItem = new TreeItemViewModel { Value = childCategory.Guid.ToString(), Text = childCategory.Name, IsFolder = true, IconCssClass = childCategory.GetPropertyValue("IconCssClass") as string ?? options.DefaultIconCssClass }; if (childCategory is IHasActiveFlag activatedItem) { childCategoryItem.IsActive = activatedItem.IsActive; } var childCategorizedItemBranch = GetAllDescendants(childCategoryItem, currentPerson, categoryService, serviceInstance, cachedEntityType, options); if (categoryItem.Children == null) { categoryItem.Children = new List <TreeItemViewModel>(); } categoryItem.Children.Add(childCategorizedItemBranch); } } // now that we have taken care of the child categories get the items for this category. if (options.GetCategorizedItems) { var itemOptions = new CategorizedItemQueryOptions { CategoryGuid = parentGuid, IncludeInactiveItems = options.IncludeInactiveItems, IncludeUnnamedEntityItems = options.IncludeUnnamedEntityItems, ItemFilterPropertyName = options.ItemFilterPropertyName, ItemFilterPropertyValue = options.ItemFilterPropertyValue }; var childQry = categoryService.GetCategorizedItemQuery(serviceInstance, itemOptions); if (childQry != null) { var childItems = GetChildrenItems(options, cachedEntityType, childQry); categoryItem.Children = new List <TreeItemViewModel>(); categoryItem.Children.AddRange(childItems); categoryItem.HasChildren = childItems.Any(); } } } return(categoryItem); }