/// <summary> /// Adds the child. /// </summary> /// <param name="item">The item.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> /// <exception cref="System.InvalidOperationException">Unable to add + item.Name</exception> public async Task AddChild(BaseItem item, CancellationToken cancellationToken) { item.Parent = this; if (item.Id == Guid.Empty) { item.Id = LibraryManager.GetNewItemId(item.Path, item.GetType()); } if (ActualChildren.Any(i => i.Id == item.Id)) { throw new ArgumentException(string.Format("A child with the Id {0} already exists.", item.Id)); } if (item.DateCreated == DateTime.MinValue) { item.DateCreated = DateTime.UtcNow; } if (item.DateModified == DateTime.MinValue) { item.DateModified = DateTime.UtcNow; } AddChildInternal(item); await LibraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false); await ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false); }
/// <summary> /// Gets allowed children of an item /// </summary> /// <param name="user">The user.</param> /// <param name="indexBy">The index by.</param> /// <returns>IEnumerable{BaseItem}.</returns> /// <exception cref="System.ArgumentNullException"></exception> public virtual IEnumerable <BaseItem> GetChildren(User user, string indexBy = null) { if (user == null) { throw new ArgumentNullException(); } //the true root should return our users root folder children if (IsPhysicalRoot) { return(user.RootFolder.GetChildren(user, indexBy)); } IEnumerable <BaseItem> result = null; if (!string.IsNullOrEmpty(indexBy)) { result = GetIndexedChildren(user, indexBy); } // If indexed is false or the indexing function is null if (result == null) { result = ActualChildren.Where(c => c.IsVisible(user)); } return(result); }
/// <summary> /// Removes the child. /// </summary> /// <param name="item">The item.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> /// <exception cref="System.InvalidOperationException">Unable to remove + item.Name</exception> public Task RemoveChild(BaseItem item, CancellationToken cancellationToken) { RemoveChildrenInternal(new[] { item }); item.Parent = null; return(ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken)); }
protected void RemoveChildrenInternal(IEnumerable <BaseItem> children) { var ids = children.Select(i => i.Id).ToList(); lock (_childrenSyncLock) { _children = ActualChildren.Where(i => !ids.Contains(i.Id)).ToList(); } }
protected void AddChildInternal(BaseItem child) { lock (_childrenSyncLock) { var newChildren = ActualChildren.ToList(); newChildren.Add(child); _children = newChildren; } }
protected void AddChildrenInternal(IEnumerable <BaseItem> children) { lock (_childrenSyncLock) { var newChildren = ActualChildren.ToList(); newChildren.AddRange(children); _children = newChildren; } }
private async Task RefreshMetadataRecursive(MetadataRefreshOptions refreshOptions, bool recursive, IProgress <double> progress, CancellationToken cancellationToken) { var children = ActualChildren.ToList(); var percentages = new Dictionary <Guid, double>(children.Count); var tasks = new List <Task>(); foreach (var child in children) { if (tasks.Count >= 3) { await Task.WhenAll(tasks).ConfigureAwait(false); tasks.Clear(); } cancellationToken.ThrowIfCancellationRequested(); var innerProgress = new ActionableProgress <double>(); // Avoid implicitly captured closure var currentChild = child; innerProgress.RegisterAction(p => { lock (percentages) { percentages[currentChild.Id] = p / 100; var percent = percentages.Values.Sum(); percent /= children.Count; percent *= 100; progress.Report(percent); } }); if (child.IsFolder) { await RefreshChildMetadata(child, refreshOptions, recursive, innerProgress, cancellationToken) .ConfigureAwait(false); } else { // Avoid implicitly captured closure var taskChild = child; tasks.Add(Task.Run(async() => await RefreshChildMetadata(taskChild, refreshOptions, false, innerProgress, cancellationToken).ConfigureAwait(false), cancellationToken)); } } await Task.WhenAll(tasks).ConfigureAwait(false); progress.Report(100); }
/// <summary> /// Clears the children. /// </summary> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> public Task ClearChildren(CancellationToken cancellationToken) { var items = ActualChildren.ToList(); ClearChildrenInternal(); foreach (var item in items) { LibraryManager.ReportItemRemoved(item); } return(ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken)); }
/// <summary> /// Removes the child. /// </summary> /// <param name="item">The item.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> /// <exception cref="System.InvalidOperationException">Unable to remove + item.Name</exception> public Task RemoveChild(BaseItem item, CancellationToken cancellationToken) { RemoveChildrenInternal(new[] { item }); item.SetParent(null); if (!EnableNewFolderQuerying()) { return(ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken)); } return(Task.FromResult(true)); }
private async Task RefreshMetadataRecursive(MetadataRefreshOptions refreshOptions, bool recursive, IProgress <double> progress, CancellationToken cancellationToken) { var children = ActualChildren.ToList(); var percentages = new Dictionary <Guid, double>(children.Count); var numComplete = 0; var count = children.Count; foreach (var child in children) { cancellationToken.ThrowIfCancellationRequested(); if (child.IsFolder) { var innerProgress = new ActionableProgress <double>(); // Avoid implicitly captured closure var currentChild = child; innerProgress.RegisterAction(p => { lock (percentages) { percentages[currentChild.Id] = p / 100; var innerPercent = percentages.Values.Sum(); innerPercent /= count; innerPercent *= 100; progress.Report(innerPercent); } }); await RefreshChildMetadata(child, refreshOptions, recursive, innerProgress, cancellationToken) .ConfigureAwait(false); } else { await RefreshChildMetadata(child, refreshOptions, false, new Progress <double>(), cancellationToken) .ConfigureAwait(false); } numComplete++; double percent = numComplete; percent /= count; percent *= 100; progress.Report(percent); } progress.Report(100); }
/// <summary> /// Validates the children internal. /// </summary> /// <param name="progress">The progress.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="recursive">if set to <c>true</c> [recursive].</param> /// <param name="refreshChildMetadata">if set to <c>true</c> [refresh child metadata].</param> /// <param name="refreshOptions">The refresh options.</param> /// <param name="directoryService">The directory service.</param> /// <returns>Task.</returns> protected async virtual Task ValidateChildrenInternal(IProgress <double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService) { var locationType = LocationType; cancellationToken.ThrowIfCancellationRequested(); var validChildren = new List <BaseItem>(); if (locationType != LocationType.Remote && locationType != LocationType.Virtual) { IEnumerable <BaseItem> nonCachedChildren; try { nonCachedChildren = GetNonCachedChildren(directoryService); } catch (IOException ex) { nonCachedChildren = new BaseItem[] { }; Logger.ErrorException("Error getting file system entries for {0}", ex, Path); } if (nonCachedChildren == null) { return; //nothing to validate } progress.Report(5); //build a dictionary of the current children we have now by Id so we can compare quickly and easily var currentChildren = GetActualChildrenDictionary(); //create a list for our validated children var newItems = new List <BaseItem>(); cancellationToken.ThrowIfCancellationRequested(); foreach (var child in nonCachedChildren) { BaseItem currentChild; if (currentChildren.TryGetValue(child.Id, out currentChild)) { if (IsValidFromResolver(currentChild, child)) { var currentChildLocationType = currentChild.LocationType; if (currentChildLocationType != LocationType.Remote && currentChildLocationType != LocationType.Virtual) { currentChild.DateModified = child.DateModified; } currentChild.IsOffline = false; validChildren.Add(currentChild); } else { newItems.Add(child); validChildren.Add(child); } } else { // Brand new item - needs to be added newItems.Add(child); validChildren.Add(child); } } // If any items were added or removed.... if (newItems.Count > 0 || currentChildren.Count != validChildren.Count) { // That's all the new and changed ones - now see if there are any that are missing var itemsRemoved = currentChildren.Values.Except(validChildren).ToList(); var actualRemovals = new List <BaseItem>(); foreach (var item in itemsRemoved) { if (item.LocationType == LocationType.Virtual || item.LocationType == LocationType.Remote) { // Don't remove these because there's no way to accurately validate them. validChildren.Add(item); } else if (!string.IsNullOrEmpty(item.Path) && IsPathOffline(item.Path)) { item.IsOffline = true; validChildren.Add(item); } else { item.IsOffline = false; actualRemovals.Add(item); } } if (actualRemovals.Count > 0) { RemoveChildrenInternal(actualRemovals); foreach (var item in actualRemovals) { LibraryManager.ReportItemRemoved(item); } } await LibraryManager.CreateItems(newItems, cancellationToken).ConfigureAwait(false); AddChildrenInternal(newItems); await ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false); } } progress.Report(10); cancellationToken.ThrowIfCancellationRequested(); if (recursive) { await ValidateSubFolders(ActualChildren.OfType <Folder>().ToList(), directoryService, progress, cancellationToken).ConfigureAwait(false); } progress.Report(20); if (refreshChildMetadata) { var container = this as IMetadataContainer; var innerProgress = new ActionableProgress <double>(); innerProgress.RegisterAction(p => progress.Report((.80 * p) + 20)); if (container != null) { await container.RefreshAllMetadata(refreshOptions, innerProgress, cancellationToken).ConfigureAwait(false); } else { await RefreshMetadataRecursive(refreshOptions, recursive, innerProgress, cancellationToken); } } progress.Report(100); }
public IEnumerable <BaseItem> GetHiddenChildren() { return(ActualChildren.Where(i => i.IsHidden)); }
/// <summary> /// Compare our current children (presumably just read from the repo) with the current state of the file system and adjust for any changes /// ***Currently does not contain logic to maintain items that are unavailable in the file system*** /// </summary> /// <param name="progress">The progress.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="recursive">if set to <c>true</c> [recursive].</param> /// <returns>Task.</returns> protected async virtual Task ValidateChildrenInternal(IProgress <double> progress, CancellationToken cancellationToken, bool?recursive = null) { // Nothing to do here if (LocationType != LocationType.FileSystem) { return; } cancellationToken.ThrowIfCancellationRequested(); var changedArgs = new ChildrenChangedEventArgs(this); //get the current valid children from filesystem (or wherever) var nonCachedChildren = GetNonCachedChildren(); if (nonCachedChildren == null) { return; //nothing to validate } progress.Report(5); //build a dictionary of the current children we have now by Id so we can compare quickly and easily var currentChildren = ActualChildren.ToDictionary(i => i.Id); //create a list for our validated children var validChildren = new ConcurrentBag <Tuple <BaseItem, bool> >(); cancellationToken.ThrowIfCancellationRequested(); Parallel.ForEach(nonCachedChildren, child => { BaseItem currentChild; if (currentChildren.TryGetValue(child.Id, out currentChild)) { currentChild.ResolveArgs = child.ResolveArgs; //existing item - check if it has changed if (currentChild.HasChanged(child)) { EntityResolutionHelper.EnsureDates(currentChild, child.ResolveArgs); changedArgs.AddUpdatedItem(currentChild); validChildren.Add(new Tuple <BaseItem, bool>(currentChild, true)); } else { validChildren.Add(new Tuple <BaseItem, bool>(currentChild, false)); } } else { //brand new item - needs to be added changedArgs.AddNewItem(child); validChildren.Add(new Tuple <BaseItem, bool>(child, true)); } }); // If any items were added or removed.... if (!changedArgs.ItemsAdded.IsEmpty || currentChildren.Count != validChildren.Count) { var newChildren = validChildren.Select(c => c.Item1).ToList(); //that's all the new and changed ones - now see if there are any that are missing changedArgs.ItemsRemoved = currentChildren.Values.Except(newChildren).ToList(); foreach (var item in changedArgs.ItemsRemoved) { Logger.Info("** " + item.Name + " Removed from library."); } var childrenReplaced = false; if (changedArgs.ItemsRemoved.Count > 0) { ActualChildren = new ConcurrentBag <BaseItem>(newChildren); childrenReplaced = true; } var saveTasks = new List <Task>(); foreach (var item in changedArgs.ItemsAdded) { Logger.Info("** " + item.Name + " Added to library."); if (!childrenReplaced) { _children.Add(item); } saveTasks.Add(Kernel.Instance.ItemRepository.SaveItem(item, CancellationToken.None)); } await Task.WhenAll(saveTasks).ConfigureAwait(false); //and save children in repo... Logger.Info("*** Saving " + newChildren.Count + " children for " + Name); await Kernel.Instance.ItemRepository.SaveChildren(Id, newChildren, CancellationToken.None).ConfigureAwait(false); } if (changedArgs.HasChange) { //force the indexes to rebuild next time IndexCache.Clear(); //and fire event LibraryManager.ReportLibraryChanged(changedArgs); } progress.Report(10); cancellationToken.ThrowIfCancellationRequested(); await RefreshChildren(validChildren, progress, cancellationToken, recursive).ConfigureAwait(false); progress.Report(100); }