Beispiel #1
0
        public virtual async Task <int> SendNotificationsToSubscribersAsync(Product product)
        {
            Guard.NotNull(product, nameof(product));

            var numberOfMessages  = 0;
            var subscriptionQuery = _db.BackInStockSubscriptions.ApplyStandardFilter(product.Id, null, null);
            var pager             = new FastPager <BackInStockSubscription>(subscriptionQuery);

            while ((await pager.ReadNextPageAsync <BackInStockSubscription>()).Out(out var subscriptions))
            {
                foreach (var subscription in subscriptions)
                {
                    // Ensure that the customer is registered (simple and fast way).
                    if (subscription?.Customer?.Email?.IsEmail() ?? false)
                    {
                        await _messageFactory.SendBackInStockNotificationAsync(subscription);

                        ++numberOfMessages;
                    }
                }

                _db.BackInStockSubscriptions.RemoveRange(subscriptions);

                await _db.SaveChangesAsync();
            }

            return(numberOfMessages);
        }
Beispiel #2
0
        public virtual CombinationAvailabilityInfo IsCombinationAvailable(
            Product product,
            IEnumerable <ProductVariantAttribute> attributes,
            IEnumerable <ProductVariantAttributeValue> selectedValues,
            ProductVariantAttributeValue currentValue)
        {
            if (product == null ||
                _performanceSettings.MaxUnavailableAttributeCombinations <= 0 ||
                !(selectedValues?.Any() ?? false))
            {
                return(null);
            }

            // Get unavailable combinations.
            var unavailableCombinations = _cache.Get(UNAVAILABLE_COMBINATIONS_KEY.FormatInvariant(product.Id), () =>
            {
                var data  = new Dictionary <string, CombinationAvailabilityInfo>();
                var query = _pvacRepository.TableUntracked.Where(x => x.ProductId == product.Id);

                if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStockByAttributes)
                {
                    query = query.Where(x => !x.IsActive || (x.StockQuantity <= 0 && !x.AllowOutOfStockOrders));
                }
                else
                {
                    query = query.Where(x => !x.IsActive);
                }

                // Do not proceed if there are too many unavailable combinations.
                var unavailableCombinationsCount = query.Count();

                if (unavailableCombinationsCount <= _performanceSettings.MaxUnavailableAttributeCombinations)
                {
                    var pager = new FastPager <ProductVariantAttributeCombination>(query);

                    while (pager.ReadNextPage(out var combinations))
                    {
                        foreach (var combination in combinations)
                        {
                            var map = DeserializeProductVariantAttributes(combination.AttributesXml);
                            if (map.Any())
                            {
                                // <ProductVariantAttribute.Id>:<ProductVariantAttributeValue.Id>[,...]
                                var valuesKeys = map
                                                 .OrderBy(x => x.Key)
                                                 .Select(x => $"{x.Key}:{string.Join(",", x.Value.OrderBy(y => y))}");

                                data[string.Join("-", valuesKeys)] = new CombinationAvailabilityInfo
                                {
                                    IsActive     = combination.IsActive,
                                    IsOutOfStock = combination.StockQuantity <= 0 && !combination.AllowOutOfStockOrders
                                };
                            }
                        }
                    }
                }
Beispiel #3
0
        public void MigrateMediaFiles_Old(SmartObjectContext ctx)
        {
            var query = ctx.Set <MediaFile>();
            //.Where(x => x.Version == 0)
            //.Include(x => x.MediaStorage);

            var pager = new FastPager <MediaFile>(query, 1000);

            using (var scope = new DbContextScope(ctx,
                                                  hooksEnabled: false,
                                                  autoCommit: false,
                                                  proxyCreation: false,
                                                  validateOnSave: false,
                                                  lazyLoading: false))
            {
                while (pager.ReadNextPage(out var files))
                {
                    foreach (var file in files)
                    {
                        if (file.Version > 0)
                        {
                            continue;
                        }

                        if (file.Extension.IsEmpty())
                        {
                            file.Extension = MimeTypes.MapMimeTypeToExtension(file.MimeType);
                        }

                        var name = file.Name;
                        if (name.IsEmpty())
                        {
                            name = file.Id.ToString(System.Globalization.CultureInfo.InvariantCulture);
                        }
                        else
                        {
                            name = MediaHelper.NormalizeFileName(file.Name.Truncate(292));
                        }

                        file.Name         = name + "." + file.Extension;
                        file.CreatedOnUtc = file.UpdatedOnUtc;
                        file.Version      = 1;

                        ProcessMediaFile(file);
                    }

                    // Save to DB
                    int num = scope.Commit();

                    // Breathe
                    ctx.DetachEntities <MediaFile>(deep: true);
                }
            }
        }
        public virtual void DeleteDeliveryTime(DeliveryTime deliveryTime)
        {
            if (deliveryTime == null)
            {
                return;
            }

            // Remove associations to deleted products.
            using (var scope = new DbContextScope(_productRepository.Context, autoDetectChanges: false, validateOnSave: false, hooksEnabled: false, autoCommit: false))
            {
                var productsQuery = _productRepository.Table.Where(x => x.Deleted && x.DeliveryTimeId == deliveryTime.Id);
                var productsPager = new FastPager <Product>(productsQuery, 500);

                while (productsPager.ReadNextPage(out var products))
                {
                    if (products.Any())
                    {
                        products.Each(x => x.DeliveryTimeId = null);
                        scope.Commit();
                    }
                }

                var attributeCombinationQuery =
                    from ac in _attributeCombinationRepository.Table
                    join p in _productRepository.Table on ac.ProductId equals p.Id
                    where p.Deleted && ac.DeliveryTimeId == deliveryTime.Id
                    select ac;
                var attributeCombinationPager = new FastPager <ProductVariantAttributeCombination>(attributeCombinationQuery, 1000);

                while (attributeCombinationPager.ReadNextPage(out var attributeCombinations))
                {
                    if (attributeCombinations.Any())
                    {
                        attributeCombinations.Each(x => x.DeliveryTimeId = null);
                        scope.Commit();
                    }
                }
            }

            // Warn if there are associations to active products.
            if (IsAssociated(deliveryTime.Id))
            {
                throw new SmartException(T("Admin.Configuration.DeliveryTimes.CannotDeleteAssignedProducts"));
            }

            _deliveryTimeRepository.Delete(deliveryTime);
        }
        /// <summary>
        /// Prevents saving of delivery time if it's referenced in products or attribute combinations
        /// and removes associations to deleted products and attribute combinations.
        /// </summary>
        protected override async Task <HookResult> OnDeletingAsync(DeliveryTime entity, IHookedEntity entry, CancellationToken cancelToken)
        {
            // Remove associations to deleted products.
            var productsQuery = _db.Products.Where(x => x.Deleted && x.DeliveryTimeId == entity.Id);

            var productsPager = new FastPager <Product>(productsQuery, 500);

            while ((await productsPager.ReadNextPageAsync <Product>()).Out(out var products))
            {
                if (products.Any())
                {
                    products.Each(x => x.DeliveryTimeId = null);
                }
            }

            var attributeCombinationQuery =
                from ac in _db.ProductVariantAttributeCombinations
                join p in _db.Products.AsNoTracking() on ac.ProductId equals p.Id
                where p.Deleted && ac.DeliveryTimeId == entity.Id
                select ac;

            var attributeCombinationPager = new FastPager <ProductVariantAttributeCombination>(attributeCombinationQuery, 1000);

            while ((await attributeCombinationPager.ReadNextPageAsync <ProductVariantAttributeCombination>()).Out(out var attributeCombinations))
            {
                if (attributeCombinations.Any())
                {
                    attributeCombinations.Each(x => x.DeliveryTimeId = null);
                }
            }

            // Warn and throw if there are associations to active products or attribute combinations.
            var query =
                from x in _db.Products
                where x.DeliveryTimeId == entity.Id || x.ProductVariantAttributeCombinations.Any(c => c.DeliveryTimeId == entity.Id)
                select x.Id;

            if (await query.AnyAsync(cancellationToken: cancelToken))
            {
                // Prohibit saving of associated entities.
                entry.State = Smartstore.Data.EntityState.Detached;
                throw new SmartException("The delivery time cannot be deleted. It has associated products or product variants.");
            }

            return(HookResult.Ok);
        }
        // AJAX.
        public ActionResult AllProductAttributes(string label, int selectedId)
        {
            var query         = _productAttributeService.GetAllProductAttributes(0, int.MaxValue).SourceQuery;
            var pager         = new FastPager <ProductAttribute>(query, 500);
            var allAttributes = new List <dynamic>();

            while (pager.ReadNextPage(out var attributes))
            {
                foreach (var attribute in attributes)
                {
                    dynamic obj = new
                    {
                        attribute.Id,
                        attribute.DisplayOrder,
                        Name = attribute.GetLocalized(x => x.Name).Value
                    };

                    allAttributes.Add(obj);
                }
            }

            var data = allAttributes
                       .OrderBy(x => x.DisplayOrder)
                       .Select(x => new ChoiceListItem
            {
                Id       = x.Id.ToString(),
                Text     = x.Name,
                Selected = x.Id == selectedId
            })
                       .ToList();

            if (label.HasValue())
            {
                data.Insert(0, new ChoiceListItem
                {
                    Id       = "0",
                    Text     = label,
                    Selected = false
                });
            }

            return(new JsonResult {
                Data = data, JsonRequestBehavior = JsonRequestBehavior.AllowGet
            });
        }
Beispiel #7
0
        public void MigrateMediaFiles(SmartObjectContext ctx)
        {
            var query = ctx.Set <MediaFile>()
                        //.Where(x => x.Version == 0)
                        .Include(x => x.MediaStorage);

            var pager = new FastPager <MediaFile>(query, 1000);

            using (var scope = new DbContextScope(ctx,
                                                  hooksEnabled: false,
                                                  autoCommit: false,
                                                  proxyCreation: false,
                                                  validateOnSave: false,
                                                  lazyLoading: false))
            {
                while (pager.ReadNextPage(out var files))
                {
                    foreach (var file in files)
                    {
                        if (file.Version > 0)
                        {
                            continue;
                        }

                        if (file.Extension.IsEmpty())
                        {
                            file.Extension = MimeTypes.MapMimeTypeToExtension(file.MimeType);
                        }

                        file.Name         = file.Name + "." + file.Extension;
                        file.CreatedOnUtc = file.UpdatedOnUtc;
                        file.Version      = 1;

                        ProcessMediaFile(file);
                    }

                    // Save to DB
                    int num = scope.Commit();

                    // Breathe
                    ctx.DetachEntities <MediaFile>(deep: true);
                }
            }
        }
            public override IEnumerable <NamedEntity> Enlist()
            {
                var pager = new FastPager <Product>(Query.AsNoTracking(), Context.MaximumNodeCount);

                while (pager.ReadNextPage(x => new { x.Id, x.UpdatedOnUtc }, x => x.Id, out var products))
                {
                    if (Context.CancellationToken.IsCancellationRequested)
                    {
                        break;
                    }

                    foreach (var x in products)
                    {
                        yield return(new NamedEntity {
                            EntityName = "Product", Id = x.Id, LastMod = x.UpdatedOnUtc
                        });
                    }
                }
            }
Beispiel #9
0
        // AJAX.
        public ActionResult AllCustomerRoles(string label, string selectedIds, bool?includeSystemRoles)
        {
            var rolesQuery = _customerService.GetAllCustomerRoles(true).SourceQuery;

            if (!(includeSystemRoles ?? true))
            {
                rolesQuery = rolesQuery.Where(x => !x.IsSystemRole);
            }

            var rolesPager    = new FastPager <CustomerRole>(rolesQuery, 500);
            var customerRoles = new List <CustomerRole>();
            var ids           = selectedIds.ToIntArray();

            while (rolesPager.ReadNextPage(out var roles))
            {
                customerRoles.AddRange(roles);
            }

            var list = customerRoles
                       .OrderBy(x => x.Name)
                       .Select(x => new ChoiceListItem
            {
                Id       = x.Id.ToString(),
                Text     = x.Name,
                Selected = ids.Contains(x.Id)
            })
                       .ToList();

            if (label.HasValue())
            {
                list.Insert(0, new ChoiceListItem
                {
                    Id       = "0",
                    Text     = label,
                    Selected = false
                });
            }

            return(new JsonResult {
                Data = list, JsonRequestBehavior = JsonRequestBehavior.AllowGet
            });
        }
        /// <summary>
        /// Gets a list of all available customer roles.
        /// </summary>
        /// <param name="label">Text for optional entry. If not null an entry with the specified label text and the Id 0 will be added to the list.</param>
        /// <param name="selectedIds">Ids of selected entities.</param>
        /// <param name="includeSystemRoles">Specifies whether to include system roles.</param>
        /// <returns>List of all customer roles as JSON.</returns>
        public async Task <IActionResult> AllCustomerRoles(string label, string selectedIds, bool?includeSystemRoles)
        {
            var rolesQuery = _db.CustomerRoles
                             .AsNoTracking()
                             .ApplyStandardFilter(true).AsQueryable();

            if (!(includeSystemRoles ?? true))
            {
                rolesQuery = rolesQuery.Where(x => x.IsSystemRole);
            }

            var rolesPager    = new FastPager <CustomerRole>(rolesQuery, 500);
            var customerRoles = new List <CustomerRole>();
            var ids           = selectedIds.ToIntArray();

            while ((await rolesPager.ReadNextPageAsync <CustomerRole>()).Out(out var roles))
            {
                customerRoles.AddRange(roles);
            }

            var list = customerRoles
                       .OrderBy(x => x.Name)
                       .Select(x => new ChoiceListItem
            {
                Id       = x.Id.ToString(),
                Text     = x.Name,
                Selected = ids.Contains(x.Id)
            })
                       .ToList();

            if (label.HasValue())
            {
                list.Insert(0, new ChoiceListItem
                {
                    Id       = "0",
                    Text     = label,
                    Selected = false
                });
            }

            return(new JsonResult(list));
        }
            public override IEnumerable <NamedEntity> Enlist()
            {
                // Enlist forum groups
                foreach (var group in _groups)
                {
                    yield return(new NamedEntity {
                        EntityName = nameof(ForumGroup), Id = group.Id, LastMod = group.UpdatedOnUtc
                    });
                }

                // Enlist forums
                foreach (var forum in _forums)
                {
                    yield return(new NamedEntity {
                        EntityName = nameof(Forum), Id = forum.Id, LastMod = forum.UpdatedOnUtc
                    });
                }

                // Enlist topics
                var pager = new FastPager <ForumTopic>(_topicsQuery.AsNoTracking(), _context.MaximumNodeCount);

                while (pager.ReadNextPage(x => new { x.Id, x.UpdatedOnUtc, x.Subject }, x => x.Id, out var topics))
                {
                    if (_context.CancellationToken.IsCancellationRequested)
                    {
                        break;
                    }

                    foreach (var x in topics)
                    {
                        yield return(new NamedEntity
                        {
                            EntityName = nameof(ForumTopic),
                            Slug = (new ForumTopic {
                                Subject = x.Subject
                            }).GetSeName(),
                            Id = x.Id,
                            LastMod = x.UpdatedOnUtc
                        });
                    }
                }
            }
Beispiel #12
0
        // AJAX.
        public ActionResult AllNews(string selectedIds)
        {
            var query        = _newsService.GetAllNews(0, 0, int.MaxValue, 0, true).SourceQuery;
            var pager        = new FastPager <NewsItem>(query, 500);
            var allNewsItems = new List <dynamic>();
            var ids          = selectedIds.ToIntArray().ToList();

            while (pager.ReadNextPage(out var newsItems))
            {
                foreach (var newsItem in newsItems)
                {
                    dynamic obj = new
                    {
                        newsItem.Id,
                        newsItem.CreatedOnUtc,
                        Title = newsItem.GetLocalized(x => x.Title).Value
                    };

                    allNewsItems.Add(obj);
                }
            }

            var data = allNewsItems
                       .OrderByDescending(x => x.CreatedOnUtc)
                       .Select(x => new ChoiceListItem
            {
                Id       = x.Id.ToString(),
                Text     = x.Title,
                Selected = ids.Contains(x.Id)
            })
                       .ToList();

            return(new JsonResult {
                Data = data, JsonRequestBehavior = JsonRequestBehavior.AllowGet
            });
        }
Beispiel #13
0
        public virtual bool Move(Provider <IMediaStorageProvider> sourceProvider, Provider <IMediaStorageProvider> targetProvider)
        {
            Guard.NotNull(sourceProvider, nameof(sourceProvider));
            Guard.NotNull(targetProvider, nameof(targetProvider));

            var success = false;
            var utcNow  = DateTime.UtcNow;
            var context = new MediaMoverContext(sourceProvider.Metadata.SystemName, targetProvider.Metadata.SystemName);

            var source = sourceProvider.Value as ISupportsMediaMoving;
            var target = targetProvider.Value as ISupportsMediaMoving;

            // Source and target must support media storage moving
            if (source == null)
            {
                throw new ArgumentException(T("Admin.Media.StorageMovingNotSupported", sourceProvider.Metadata.SystemName));
            }

            if (target == null)
            {
                throw new ArgumentException(T("Admin.Media.StorageMovingNotSupported", targetProvider.Metadata.SystemName));
            }

            // Source and target provider must not be equal
            if (sourceProvider.Metadata.SystemName.IsCaseInsensitiveEqual(targetProvider.Metadata.SystemName))
            {
                throw new ArgumentException(T("Admin.Media.CannotMoveToSameProvider"));
            }

            // We are about to process data in chunks but want to commit ALL at once after ALL chunks have been processed successfully.
            // AutoDetectChanges true required for newly inserted binary data.
            using (var scope = new DbContextScope(ctx: _services.DbContext,
                                                  autoDetectChanges: true,
                                                  proxyCreation: false,
                                                  validateOnSave: false,
                                                  autoCommit: false))
            {
                using (var transaction = scope.DbContext.BeginTransaction())
                {
                    try
                    {
                        var pager = new FastPager <MediaFile>(_mediaFileRepo.Table, PAGE_SIZE);
                        while (pager.ReadNextPage(out var files))
                        {
                            foreach (var file in files)
                            {
                                // Move item from source to target
                                source.MoveTo(target, context, file);

                                file.UpdatedOnUtc = utcNow;
                                ++context.MovedItems;
                            }

                            scope.DbContext.SaveChanges();

                            // Detach all entities from previous page to save memory
                            scope.DbContext.DetachEntities(files, deep: true);
                        }

                        transaction.Commit();
                        success = true;
                    }
                    catch (Exception exception)
                    {
                        success = false;
                        transaction.Rollback();

                        _services.Notifier.Error(exception);
                        _logger.Error(exception);
                    }
                }
            }


            if (success)
            {
                _services.Settings.SetSetting("Media.Storage.Provider", targetProvider.Metadata.SystemName);
            }

            // inform both provider about ending
            source.OnCompleted(context, success);
            target.OnCompleted(context, success);

            if (success && context.ShrinkDatabase)
            {
                _services.DbContext.ShrinkDatabase();
            }

            return(success);
        }
        public override async Task ExecuteAsync(TaskExecutionContext ctx)
        {
            var count      = 0;
            var numDeleted = 0;
            var numAdded   = 0;
            var roleQuery  = _customerRoleRepository.TableUntracked.Expand(x => x.RuleSets);

            if (ctx.Parameters.ContainsKey("CustomerRoleIds"))
            {
                var roleIds = ctx.Parameters["CustomerRoleIds"].ToIntArray();
                roleQuery = roleQuery.Where(x => roleIds.Contains(x.Id));

                numDeleted = _customerRoleMappingRepository.Context.ExecuteSqlCommand(
                    "Delete From [dbo].[CustomerRoleMapping] Where [CustomerRoleId] In ({0}) And [IsSystemMapping] = 1",
                    false,
                    null,
                    string.Join(",", roleIds));
            }
            else
            {
                numDeleted = _customerRoleMappingRepository.Context.ExecuteSqlCommand("Delete From [dbo].[CustomerRoleMapping] Where [IsSystemMapping] = 1");
            }

            var roles = await roleQuery
                        .Where(x => x.Active && x.RuleSets.Any(y => y.IsActive))
                        .ToListAsync();

            using (var scope = new DbContextScope(ctx: _customerRoleMappingRepository.Context, autoDetectChanges: false, validateOnSave: false, hooksEnabled: false, autoCommit: false))
            {
                foreach (var role in roles)
                {
                    var ruleSetCustomerIds = new HashSet <int>();

                    ctx.SetProgress(++count, roles.Count, $"Add customer assignments for role \"{role.SystemName.NaIfEmpty()}\".");

                    // Execute active rule sets and collect customer ids.
                    foreach (var ruleSet in role.RuleSets.Where(x => x.IsActive))
                    {
                        if (ctx.CancellationToken.IsCancellationRequested)
                        {
                            return;
                        }

                        if (_ruleFactory.CreateExpressionGroup(ruleSet, _targetGroupService) is FilterExpression expression)
                        {
                            var filterResult = _targetGroupService.ProcessFilter(expression, 0, 500);
                            var resultPager  = new FastPager <Customer>(filterResult.SourceQuery, 500);

                            while (true)
                            {
                                var customerIds = await resultPager.ReadNextPageAsync(x => x.Id, x => x);

                                if (!(customerIds?.Any() ?? false))
                                {
                                    break;
                                }

                                ruleSetCustomerIds.AddRange(customerIds);
                            }
                        }
                    }

                    // Add mappings.
                    if (ruleSetCustomerIds.Any())
                    {
                        foreach (var chunk in ruleSetCustomerIds.Slice(500))
                        {
                            if (ctx.CancellationToken.IsCancellationRequested)
                            {
                                return;
                            }

                            foreach (var customerId in chunk)
                            {
                                _customerRoleMappingRepository.Insert(new CustomerRoleMapping
                                {
                                    CustomerId      = customerId,
                                    CustomerRoleId  = role.Id,
                                    IsSystemMapping = true
                                });

                                ++numAdded;
                            }

                            await scope.CommitAsync();
                        }

                        try
                        {
                            _customerRoleMappingRepository.Context.DetachEntities <CustomerRoleMapping>();
                        }
                        catch { }
                    }
                }
            }

            if (numAdded > 0 || numDeleted > 0)
            {
                _cacheManager.RemoveByPattern(AclService.ACL_SEGMENT_PATTERN);
            }

            Debug.WriteLineIf(numDeleted > 0 || numAdded > 0, $"Deleted {numDeleted} and added {numAdded} customer assignments for {roles.Count} roles.");
        }
Beispiel #15
0
        public void MigrateDownloads(SmartObjectContext ctx)
        {
            var sql           = "SELECT * FROM [Download] WHERE [MediaFileId] IS NULL AND [UseDownloadUrl] = 0";
            var downloadStubs = ctx.SqlQuery <DownloadStub>(sql).ToDictionary(x => x.Id);

            var downloadsFolderId = _albumRegistry.GetAlbumByName(SystemAlbumProvider.Downloads)?.Id;
            var messagesFolderId  = _albumRegistry.GetAlbumByName(SystemAlbumProvider.Messages)?.Id;
            var newFiles          = new List <MediaFile>();

            using (var scope = new DbContextScope(ctx,
                                                  validateOnSave: false,
                                                  hooksEnabled: false,
                                                  autoCommit: false,
                                                  autoDetectChanges: false))
            {
                var messageTemplates = ctx.Set <MessageTemplate>()
                                       .Where(x => x.Attachment1FileId.HasValue || x.Attachment2FileId.HasValue || x.Attachment3FileId.HasValue)
                                       .ToList();

                // Key = Download.Id
                var messageTemplatesDict = new Dictionary <int, MessageTemplate>();
                foreach (var mt in messageTemplates)
                {
                    if (mt.Attachment1FileId.HasValue)
                    {
                        messageTemplatesDict[mt.Attachment1FileId.Value] = mt;
                    }
                    if (mt.Attachment2FileId.HasValue)
                    {
                        messageTemplatesDict[mt.Attachment2FileId.Value] = mt;
                    }
                    if (mt.Attachment3FileId.HasValue)
                    {
                        messageTemplatesDict[mt.Attachment3FileId.Value] = mt;
                    }
                }

                var hasPostProcessor = _isFsProvider || messageTemplatesDict.Count > 0;

                var query = ctx.Set <Download>().Where(x => x.MediaFileId == null);
                var pager = new FastPager <Download>(query, 1000);

                while (pager.ReadNextPage(out var downloads))
                {
                    foreach (var d in downloads)
                    {
                        var stub = downloadStubs.Get(d.Id);
                        if (stub == null)
                        {
                            continue;
                        }

                        if (stub.UseDownloadUrl || string.IsNullOrEmpty(stub.Extension))
                        {
                            // Something weird has happened in the past
                            continue;
                        }

                        if (stub.Filename == "undefined" || string.IsNullOrEmpty(stub.Filename))
                        {
                            stub.Filename = stub.Id.ToString(CultureInfo.InvariantCulture);
                        }

                        var isMailAttachment = false;
                        if (messageTemplatesDict.TryGetValue(stub.Id, out var mt))
                        {
                            isMailAttachment = true;
                        }

                        // Create and insert new MediaFile entity for the download
                        var file = new MediaFile
                        {
                            CreatedOnUtc   = stub.UpdatedOnUtc,
                            UpdatedOnUtc   = stub.UpdatedOnUtc,
                            Extension      = stub.Extension.TrimStart('.'),
                            Name           = stub.Filename.Truncate(292), // Extension appended later in MigrateFiles()
                            MimeType       = stub.ContentType,
                            MediaType      = MediaType.Image,             // Resolved later in MigrateFiles()
                            FolderId       = isMailAttachment ? messagesFolderId : downloadsFolderId,
                            IsTransient    = stub.IsTransient,
                            MediaStorageId = stub.MediaStorageId,
                            Version        = 0 // Ensure that this record gets processed by MigrateFiles()
                        };

                        // Assign new file to download
                        d.MediaFile = file;

                        // To be able to move files later
                        if (hasPostProcessor)
                        {
                            newFiles.Add(file);
                        }
                    }

                    // Save to DB
                    int num = scope.Commit();

                    if (hasPostProcessor)
                    {
                        var downloadsDict = downloads.ToDictionary(x => x.Id);

                        if (_isFsProvider)
                        {
                            // Copy files from "Media/Downloads" to "Media/Storage" folder
                            MoveDownloadFiles(newFiles.ToDictionary(x => x.Id), downloadsDict, downloadStubs);
                        }

                        // MessageTemplate attachments (Download > MediaFile)
                        if (messageTemplatesDict.Count > 0)
                        {
                            ReRefMessageTemplateAttachments(ctx, messageTemplatesDict, downloadsDict);
                        }

                        newFiles.Clear();
                    }

                    // Breathe
                    ctx.DetachEntities <MessageTemplate>();
                    ctx.DetachEntities <Download>(deep: true);
                }
            }
        }
Beispiel #16
0
        public virtual async Task <bool> MoveAsync(
            Provider <IMediaStorageProvider> sourceProvider,
            Provider <IMediaStorageProvider> targetProvider,
            CancellationToken cancelToken = default)
        {
            Guard.NotNull(sourceProvider, nameof(sourceProvider));
            Guard.NotNull(targetProvider, nameof(targetProvider));

            // Source must support sending
            if (sourceProvider.Value is not IMediaSender sender)
            {
                throw new ArgumentException(T("Admin.Media.StorageMovingNotSupported", sourceProvider.Metadata.SystemName));
            }

            // Target must support receiving
            if (targetProvider.Value is not IMediaReceiver receiver)
            {
                throw new ArgumentException(T("Admin.Media.StorageMovingNotSupported", targetProvider.Metadata.SystemName));
            }

            // Source and target provider must not be equal
            if (sender == receiver)
            {
                throw new ArgumentException(T("Admin.Media.CannotMoveToSameProvider"));
            }

            var success = false;
            var utcNow  = DateTime.UtcNow;
            var context = new MediaMoverContext(sender, receiver);

            // We are about to process data in chunks but want to commit ALL at once after ALL chunks have been processed successfully.
            // AutoDetectChanges true required for newly inserted binary data.
            using (var scope = new DbContextScope(ctx: _db, autoDetectChanges: true, retainConnection: true))
            {
                using (var transaction = await _db.Database.BeginTransactionAsync(cancelToken))
                {
                    try
                    {
                        var pager = new FastPager <MediaFile>(_db.MediaFiles, PAGE_SIZE);
                        while ((await pager.ReadNextPageAsync <MediaFile>()).Out(out var files))
                        {
                            if (cancelToken.IsCancellationRequested)
                            {
                                break;
                            }

                            foreach (var file in files)
                            {
                                // Move item from source to target
                                await sender.MoveToAsync(receiver, context, file);

                                file.UpdatedOnUtc = utcNow;
                                ++context.MovedItems;
                            }

                            if (!cancelToken.IsCancellationRequested)
                            {
                                await _db.SaveChangesAsync(cancelToken);

                                // Detach all entities from previous page to save memory
                                scope.DbContext.DetachEntities(files, deep: true);
                            }
                        }

                        if (!cancelToken.IsCancellationRequested)
                        {
                            await transaction.CommitAsync(cancelToken);

                            success = true;
                        }
                        else
                        {
                            success = false;
                            await transaction.RollbackAsync(CancellationToken.None);
                        }
                    }
                    catch (Exception exception)
                    {
                        success = false;
                        await transaction.RollbackAsync(cancelToken);

                        _notifier.Error(exception);
                        Logger.Error(exception);
                    }
                }
            }


            if (success)
            {
                await _settingService.ApplySettingAsync("Media.Storage.Provider", targetProvider.Metadata.SystemName);

                await _db.SaveChangesAsync(cancelToken);
            }

            // Inform both provider about ending
            await sender.OnCompletedAsync(context, success, cancelToken);

            await receiver.OnCompletedAsync(context, success, cancelToken);

            return(success);
        }
        public override async Task ExecuteAsync(TaskExecutionContext ctx)
        {
            var count      = 0;
            var clearCache = false;
            var roleQuery  = _customerRoleRepository.TableUntracked.Expand(x => x.RuleSets);

            if (ctx.Parameters.ContainsKey("CustomerRoleIds"))
            {
                var rolesIds = ctx.Parameters["CustomerRoleIds"].ToIntArray();
                roleQuery = roleQuery.Where(x => rolesIds.Contains(x.Id));
            }

            var roles = await roleQuery.ToListAsync();

            if (!roles.Any())
            {
                return;
            }

            using (var scope = new DbContextScope(ctx: _customerRoleMappingRepository.Context, autoDetectChanges: false, validateOnSave: false, hooksEnabled: false, autoCommit: false))

                foreach (var role in roles)
                {
                    try
                    {
                        ctx.SetProgress(++count, roles.Count, $"Sync customer assignments for role {role.SystemName.NaIfEmpty()}.");

                        _customerRoleMappingRepository.Context.DetachEntities(x => x is CustomerRoleMapping);
                    }
                    catch { }

                    var ruleSetCustomerIds  = new HashSet <int>();
                    var existingCustomerIds = new HashSet <int>();
                    var numDeleted          = 0;
                    var numAdded            = 0;

                    // Execute active rule sets and collect customer ids.
                    // Delete old mappings if the role is inactive or has no assigned rule sets.
                    if (role.Active)
                    {
                        foreach (var ruleSet in role.RuleSets.Where(x => x.IsActive))
                        {
                            if (ctx.CancellationToken.IsCancellationRequested)
                            {
                                return;
                            }

                            var expression = _ruleFactory.CreateExpressionGroup(ruleSet, _targetGroupService) as FilterExpression;
                            if (expression != null)
                            {
                                var filterResult = _targetGroupService.ProcessFilter(expression, 0, 500);
                                var resultPager  = new FastPager <Customer>(filterResult.SourceQuery, 500);

                                for (var i = 0; i < 9999999; ++i)
                                {
                                    var customerIds = await resultPager.ReadNextPageAsync(x => x.Id, x => x);

                                    if (!(customerIds?.Any() ?? false))
                                    {
                                        break;
                                    }

                                    ruleSetCustomerIds.AddRange(customerIds);
                                }
                            }
                        }
                    }

                    // Sync mappings.
                    var query = _customerRoleMappingRepository.Table.Where(x => x.CustomerRoleId == role.Id && x.IsSystemMapping);
                    var pager = new FastPager <CustomerRoleMapping>(query, 500);

                    // Mappings to delete.
                    for (var i = 0; i < 9999999; ++i)
                    {
                        if (ctx.CancellationToken.IsCancellationRequested)
                        {
                            return;
                        }

                        var mappings = await pager.ReadNextPageAsync <CustomerRoleMapping>();

                        if (!(mappings?.Any() ?? false))
                        {
                            break;
                        }

                        foreach (var mapping in mappings)
                        {
                            if (!role.Active || !ruleSetCustomerIds.Contains(mapping.CustomerId))
                            {
                                _customerRoleMappingRepository.Delete(mapping);

                                ++numDeleted;
                                clearCache = true;
                            }
                            else
                            {
                                existingCustomerIds.Add(mapping.CustomerId);
                            }
                        }

                        await scope.CommitAsync();
                    }

                    // Mappings to add.
                    if (role.Active)
                    {
                        var toAdd = ruleSetCustomerIds.Except(existingCustomerIds).ToList();
                        if (toAdd.Any())
                        {
                            foreach (var chunk in toAdd.Slice(500))
                            {
                                if (ctx.CancellationToken.IsCancellationRequested)
                                {
                                    return;
                                }

                                foreach (var customerId in chunk)
                                {
                                    _customerRoleMappingRepository.Insert(new CustomerRoleMapping
                                    {
                                        CustomerId      = customerId,
                                        CustomerRoleId  = role.Id,
                                        IsSystemMapping = true
                                    });

                                    ++numAdded;
                                    clearCache = true;
                                }

                                await scope.CommitAsync();
                            }
                        }
                    }

                    Debug.WriteLineIf(numDeleted > 0 || numAdded > 0, $"Customer assignments for {role.SystemName.NaIfEmpty()}: deleted {numDeleted}, added {numAdded}.");
                }

            if (clearCache)
            {
                _cacheManager.RemoveByPattern(AclService.ACL_SEGMENT_PATTERN);
            }
        }
        public async Task Run(TaskExecutionContext ctx, CancellationToken cancelToken = default)
        {
            //var count = 0;
            var numDeleted = 0;
            var numAdded   = 0;
            var rolesCount = 0;

            using (var scope = new DbContextScope(_db, autoDetectChanges: false, hooksEnabled: false, deferCommit: true))
            {
                // Delete existing system mappings.
                var deleteQuery = _db.CustomerRoleMappings.Where(x => x.IsSystemMapping);
                if (ctx.Parameters.ContainsKey("CustomerRoleIds"))
                {
                    var roleIds = ctx.Parameters["CustomerRoleIds"].ToIntArray();
                    deleteQuery = deleteQuery.Where(x => roleIds.Contains(x.CustomerRoleId));
                }

                numDeleted = await deleteQuery.BatchDeleteAsync(cancelToken);

                // Insert new customer role mappings.
                var roles = await _db.CustomerRoles
                            .Include(x => x.RuleSets)
                            .AsNoTracking()
                            .Where(x => x.Active && x.RuleSets.Any(y => y.IsActive))
                            .ToListAsync(cancelToken);

                rolesCount = roles.Count;

                foreach (var role in roles)
                {
                    var ruleSetCustomerIds = new HashSet <int>();

                    // TODO: (mg) (core) Complete TargetGroupEvaluatorTask (TaskExecutionContext required).
                    //ctx.SetProgress(++count, roles.Count, $"Add customer assignments for role \"{role.SystemName.NaIfEmpty()}\".");

                    // Execute active rule sets and collect customer ids.
                    foreach (var ruleSet in role.RuleSets.Where(x => x.IsActive))
                    {
                        if (cancelToken.IsCancellationRequested)
                        {
                            return;
                        }

                        var expressionGroup = await _ruleService.CreateExpressionGroupAsync(ruleSet, _targetGroupService);

                        if (expressionGroup is FilterExpression expression)
                        {
                            var filterResult = _targetGroupService.ProcessFilter(expression, 0, 500);
                            var resultPager  = new FastPager <Customer>(filterResult.SourceQuery, 500);

                            while ((await resultPager.ReadNextPageAsync(x => x.Id, x => x)).Out(out var customerIds))
                            {
                                ruleSetCustomerIds.AddRange(customerIds);
                            }
                        }
                    }

                    // Add mappings.
                    if (ruleSetCustomerIds.Any())
                    {
                        foreach (var chunk in ruleSetCustomerIds.Slice(500))
                        {
                            if (cancelToken.IsCancellationRequested)
                            {
                                return;
                            }

                            foreach (var customerId in chunk)
                            {
                                _db.CustomerRoleMappings.Add(new CustomerRoleMapping
                                {
                                    CustomerId      = customerId,
                                    CustomerRoleId  = role.Id,
                                    IsSystemMapping = true
                                });

                                ++numAdded;
                            }

                            await scope.CommitAsync(cancelToken);
                        }

                        try
                        {
                            scope.DbContext.DetachEntities <CustomerRoleMapping>();
                        }
                        catch { }
                    }
                }
            }

            if (numAdded > 0 || numDeleted > 0)
            {
                await _cache.RemoveByPatternAsync(AclService.ACL_SEGMENT_PATTERN);
            }

            Debug.WriteLineIf(numDeleted > 0 || numAdded > 0, $"Deleted {numDeleted} and added {numAdded} customer assignments for {rolesCount} roles.");
        }
Beispiel #19
0
        public virtual void InstallPermissions(IPermissionProvider[] permissionProviders, bool removeUnusedPermissions = false)
        {
            if (!(permissionProviders?.Any() ?? false))
            {
                return;
            }

            Dictionary <string, CustomerRole> existingRoles = null;
            var allPermissionNames = _permissionRepository.TableUntracked.Select(x => x.SystemName).ToList();
            var existing           = new HashSet <string>(allPermissionNames, StringComparer.InvariantCultureIgnoreCase);
            var added = new HashSet <string>(StringComparer.InvariantCultureIgnoreCase);
            var providerPermissions = new HashSet <string>(StringComparer.InvariantCultureIgnoreCase);
            var log        = existing.Any();
            var clearCache = false;

            if (existing.Any())
            {
                var permissionsMigrated = existing.Contains(Permissions.System.AccessShop) && !existing.Contains("PublicStoreAllowNavigation");
                if (!permissionsMigrated)
                {
                    // Migrations must have been completed before permissions can be added or deleted.
                    return;
                }
            }

            try
            {
                using (var scope = new DbContextScope(_permissionRepository.Context, validateOnSave: false, hooksEnabled: false, autoCommit: false))
                {
                    // Add new permissions.
                    foreach (var provider in permissionProviders)
                    {
                        try
                        {
                            var systemNames        = provider.GetPermissions().Select(x => x.SystemName);
                            var missingSystemNames = systemNames.Except(existing);

                            if (removeUnusedPermissions)
                            {
                                providerPermissions.AddRange(systemNames);
                            }

                            if (missingSystemNames.Any())
                            {
                                var defaultPermissions = provider.GetDefaultPermissions();
                                foreach (var systemName in missingSystemNames)
                                {
                                    var roleNames = defaultPermissions
                                                    .Where(x => x.PermissionRecords.Any(y => y.SystemName == systemName))
                                                    .Select(x => x.CustomerRoleSystemName);

                                    var newPermission = new PermissionRecord {
                                        SystemName = systemName
                                    };

                                    foreach (var roleName in new HashSet <string>(roleNames, StringComparer.InvariantCultureIgnoreCase))
                                    {
                                        if (existingRoles == null)
                                        {
                                            existingRoles = new Dictionary <string, CustomerRole>();

                                            var rolesQuery = _customerService.Value.GetAllCustomerRoles(true).SourceQuery;
                                            rolesQuery = rolesQuery.Where(x => !string.IsNullOrEmpty(x.SystemName));

                                            var rolesPager = new FastPager <CustomerRole>(rolesQuery, 500);

                                            while (rolesPager.ReadNextPage(out var roles))
                                            {
                                                roles.Each(x => existingRoles[x.SystemName] = x);
                                            }
                                        }

                                        if (!existingRoles.TryGetValue(roleName, out var role))
                                        {
                                            role = new CustomerRole
                                            {
                                                Active     = true,
                                                Name       = roleName,
                                                SystemName = roleName
                                            };

                                            _customerService.Value.InsertCustomerRole(role);
                                            scope.Commit();
                                            existingRoles[roleName] = role;
                                        }

                                        newPermission.PermissionRoleMappings.Add(new PermissionRoleMapping
                                        {
                                            Allow          = true,
                                            CustomerRoleId = role.Id
                                        });
                                    }

                                    _permissionRepository.Insert(newPermission);

                                    clearCache = true;
                                    added.Add(newPermission.SystemName);
                                    existing.Add(newPermission.SystemName);
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            Logger.Error(ex);
                        }
                    }

                    scope.Commit();

                    if (log && added.Any())
                    {
                        Logger.Info(T("Admin.Permissions.AddedPermissions", string.Join(", ", added)));
                    }

                    // Remove permissions no longer supported by providers.
                    if (removeUnusedPermissions)
                    {
                        var toDelete = existing.Except(providerPermissions).ToList();
                        if (toDelete.Any())
                        {
                            clearCache = true;

                            foreach (var chunk in toDelete.Slice(500))
                            {
                                var entities = _permissionRepository.Table.Where(x => chunk.Contains(x.SystemName)).ToList();
                                entities.Each(x => _permissionRepository.Delete(x));
                                scope.Commit();
                            }

                            if (log)
                            {
                                Logger.Info(T("Admin.Permissions.RemovedPermissions", string.Join(", ", toDelete)));
                            }
                        }
                    }
                }
            }
            finally
            {
                if (clearCache)
                {
                    _cacheManager.RemoveByPattern(PERMISSION_TREE_PATTERN_KEY);
                }
            }
        }
Beispiel #20
0
        public virtual void InheritAclIntoChildren(
            int categoryId,
            bool touchProductsWithMultipleCategories = false,
            bool touchExistingAcls = false,
            bool categoriesOnly    = false)
        {
            var allCustomerRoles  = _customerService.GetAllCustomerRoles(true);
            var referenceCategory = GetCategoryById(categoryId);
            var referenceRoleIds  = _aclService.GetCustomerRoleIdsWithAccessTo(referenceCategory);

            using (var scope = new DbContextScope(ctx: _aclRepository.Context, autoDetectChanges: false, proxyCreation: false, validateOnSave: false, autoCommit: false))
            {
                ProcessCategory(scope, referenceCategory);
            }

            _cache.RemoveByPattern(AclService.ACL_SEGMENT_PATTERN);


            void ProcessCategory(DbContextScope scope, Category c)
            {
                // Process sub-categories.
                var subCategories = GetAllCategoriesByParentCategoryId(c.Id, true);

                foreach (var subCategory in subCategories)
                {
                    if (subCategory.SubjectToAcl != referenceCategory.SubjectToAcl)
                    {
                        subCategory.SubjectToAcl = referenceCategory.SubjectToAcl;
                        _categoryRepository.Update(subCategory);
                    }

                    var existingAclRecords = _aclService.GetAclRecords(subCategory).ToDictionarySafe(x => x.CustomerRoleId);

                    foreach (var role in allCustomerRoles)
                    {
                        if (referenceRoleIds.Contains(role.Id))
                        {
                            if (!existingAclRecords.ContainsKey(role.Id))
                            {
                                _aclRepository.Insert(new AclRecord {
                                    CustomerRoleId = role.Id, EntityId = subCategory.Id, EntityName = "Category"
                                });
                            }
                        }
                        else if (existingAclRecords.TryGetValue(role.Id, out var aclRecordToDelete))
                        {
                            _aclRepository.Delete(aclRecordToDelete);
                        }
                    }
                }

                scope.Commit();

                // Process products.
                var categoryIds = new HashSet <int>(subCategories.Select(x => x.Id));

                categoryIds.Add(c.Id);

                var searchQuery   = new CatalogSearchQuery().WithCategoryIds(null, categoryIds.ToArray());
                var productsQuery = _catalogSearchService.PrepareQuery(searchQuery);
                var productsPager = new FastPager <Product>(productsQuery, 500);

                while (productsPager.ReadNextPage(out var products))
                {
                    foreach (var product in products)
                    {
                        if (product.SubjectToAcl != referenceCategory.SubjectToAcl)
                        {
                            product.SubjectToAcl = referenceCategory.SubjectToAcl;
                            _productRepository.Update(product);
                        }

                        var existingAclRecords = _aclService.GetAclRecords(product).ToDictionarySafe(x => x.CustomerRoleId);

                        foreach (var role in allCustomerRoles)
                        {
                            if (referenceRoleIds.Contains(role.Id))
                            {
                                if (!existingAclRecords.ContainsKey(role.Id))
                                {
                                    _aclRepository.Insert(new AclRecord {
                                        CustomerRoleId = role.Id, EntityId = product.Id, EntityName = "Product"
                                    });
                                }
                            }
                            else if (existingAclRecords.TryGetValue(role.Id, out var aclRecordToDelete))
                            {
                                _aclRepository.Delete(aclRecordToDelete);
                            }
                        }
                    }

                    scope.Commit();
                }

                scope.Commit();

                try
                {
                    scope.DbContext.DetachEntities(x => x is Product || x is Category || x is AclRecord, false);
                }
                catch { }

                foreach (var subCategory in subCategories)
                {
                    ProcessCategory(scope, subCategory);
                }
            }
        }
Beispiel #21
0
        public virtual void InheritStoresIntoChildren(
            int categoryId,
            bool touchProductsWithMultipleCategories = false,
            bool touchExistingAcls = false,
            bool categoriesOnly    = false)
        {
            var allStores              = _storeService.GetAllStores();
            var referenceCategory      = GetCategoryById(categoryId);
            var referenceStoreMappings = _storeMappingService.GetStoresIdsWithAccess(referenceCategory);

            using (var scope = new DbContextScope(ctx: _storeMappingRepository.Context, autoDetectChanges: false, proxyCreation: false, validateOnSave: false, autoCommit: false))
            {
                ProcessCategory(scope, referenceCategory);
            }

            _cache.RemoveByPattern(StoreMappingService.STOREMAPPING_SEGMENT_PATTERN);


            void ProcessCategory(DbContextScope scope, Category c)
            {
                // Process sub-categories.
                var subCategories = GetAllCategoriesByParentCategoryId(c.Id, true);

                foreach (var subCategory in subCategories)
                {
                    if (subCategory.LimitedToStores != referenceCategory.LimitedToStores)
                    {
                        subCategory.LimitedToStores = referenceCategory.LimitedToStores;
                        _categoryRepository.Update(subCategory);
                    }

                    var existingStoreMappingsRecords = _storeMappingService.GetStoreMappings(subCategory).ToDictionary(x => x.StoreId);

                    foreach (var store in allStores)
                    {
                        if (referenceStoreMappings.Contains(store.Id))
                        {
                            if (!existingStoreMappingsRecords.ContainsKey(store.Id))
                            {
                                _storeMappingRepository.Insert(new StoreMapping {
                                    StoreId = store.Id, EntityId = subCategory.Id, EntityName = "Category"
                                });
                            }
                        }
                        else if (existingStoreMappingsRecords.TryGetValue(store.Id, out var storeMappingToDelete))
                        {
                            _storeMappingRepository.Delete(storeMappingToDelete);
                        }
                    }
                }

                scope.Commit();

                // Process products.
                var categoryIds = new HashSet <int>(subCategories.Select(x => x.Id));

                categoryIds.Add(c.Id);

                var searchQuery   = new CatalogSearchQuery().WithCategoryIds(null, categoryIds.ToArray());
                var productsQuery = _catalogSearchService.PrepareQuery(searchQuery);
                var productsPager = new FastPager <Product>(productsQuery, 500);

                while (productsPager.ReadNextPage(out var products))
                {
                    foreach (var product in products)
                    {
                        if (product.LimitedToStores != referenceCategory.LimitedToStores)
                        {
                            product.LimitedToStores = referenceCategory.LimitedToStores;
                            _productRepository.Update(product);
                        }

                        var existingStoreMappingsRecords = _storeMappingService.GetStoreMappings(product).ToDictionary(x => x.StoreId);

                        foreach (var store in allStores)
                        {
                            if (referenceStoreMappings.Contains(store.Id))
                            {
                                if (!existingStoreMappingsRecords.ContainsKey(store.Id))
                                {
                                    _storeMappingRepository.Insert(new StoreMapping {
                                        StoreId = store.Id, EntityId = product.Id, EntityName = "Product"
                                    });
                                }
                            }
                            else if (existingStoreMappingsRecords.TryGetValue(store.Id, out var storeMappingToDelete))
                            {
                                _storeMappingRepository.Delete(storeMappingToDelete);
                            }
                        }
                    }

                    scope.Commit();
                }

                scope.Commit();

                try
                {
                    scope.DbContext.DetachEntities(x => x is Product || x is Category || x is StoreMapping, false);
                }
                catch { }

                foreach (var subCategory in subCategories)
                {
                    ProcessCategory(scope, subCategory);
                }
            }
        }
Beispiel #22
0
        public virtual async Task <(int AffectedCategories, int AffectedProducts)> InheritStoresIntoChildrenAsync(
            int categoryId,
            bool touchProductsWithMultipleCategories = false,
            bool touchExistingAcls = false,
            bool categoriesOnly    = false)
        {
            var categoryEntityName = nameof(Category);
            var productEntityName  = nameof(Product);
            var affectedCategories = 0;
            var affectedProducts   = 0;

            var allStoreIds = await _db.Stores
                              .AsQueryable()
                              .Select(x => x.Id)
                              .ToListAsync();

            var referenceCategory = await _db.Categories.FindByIdAsync(categoryId);

            var referenceStoreMappingIds = await _storeMappingService.GetAuthorizedStoreIdsAsync(referenceCategory);

            using (var scope = new DbContextScope(_db, autoDetectChanges: false))
            {
                await ProcessCategory(scope, referenceCategory);
            }

            _cache.RemoveByPattern(StoreMappingService.STOREMAPPING_SEGMENT_PATTERN);

            return(affectedCategories, affectedProducts);

            async Task ProcessCategory(DbContextScope scope, Category c)
            {
                // Process sub-categories.
                var subCategories = await _db.Categories
                                    .AsQueryable()
                                    .Where(x => x.ParentCategoryId == c.Id)
                                    .OrderBy(x => x.DisplayOrder)
                                    .ToListAsync();

                foreach (var subCategory in subCategories)
                {
                    if (subCategory.LimitedToStores != referenceCategory.LimitedToStores)
                    {
                        subCategory.LimitedToStores = referenceCategory.LimitedToStores;
                    }

                    var storeMappings = await _db.StoreMappings
                                        .ApplyEntityFilter(subCategory)
                                        .ToListAsync();

                    var storeMappingsDic = storeMappings.ToDictionarySafe(x => x.StoreId);

                    foreach (var storeId in allStoreIds)
                    {
                        if (referenceStoreMappingIds.Contains(storeId))
                        {
                            if (!storeMappingsDic.ContainsKey(storeId))
                            {
                                _db.StoreMappings.Add(new StoreMapping {
                                    StoreId = storeId, EntityId = subCategory.Id, EntityName = categoryEntityName
                                });
                            }
                        }
                        else if (storeMappingsDic.TryGetValue(storeId, out var storeMappingToDelete))
                        {
                            _db.StoreMappings.Remove(storeMappingToDelete);
                        }
                    }
                }

                await scope.CommitAsync();

                affectedCategories += subCategories.Count;

                // Process products.
                var categoryIds = new HashSet <int>(subCategories.Select(x => x.Id))
                {
                    c.Id
                };

                var searchQuery   = new CatalogSearchQuery().WithCategoryIds(null, categoryIds.ToArray());
                var productsQuery = _catalogSearchService.PrepareQuery(searchQuery);
                var productsPager = new FastPager <Product>(productsQuery, 500);

                while ((await productsPager.ReadNextPageAsync <Product>()).Out(out var products))
                {
                    foreach (var product in products)
                    {
                        if (product.LimitedToStores != referenceCategory.LimitedToStores)
                        {
                            product.LimitedToStores = referenceCategory.LimitedToStores;
                        }

                        var storeMappings = await _db.StoreMappings
                                            .ApplyEntityFilter(product)
                                            .ToListAsync();

                        var storeMappingsDic = storeMappings.ToDictionarySafe(x => x.StoreId);

                        foreach (var storeId in allStoreIds)
                        {
                            if (referenceStoreMappingIds.Contains(storeId))
                            {
                                if (!storeMappingsDic.ContainsKey(storeId))
                                {
                                    _db.StoreMappings.Add(new StoreMapping {
                                        StoreId = storeId, EntityId = product.Id, EntityName = productEntityName
                                    });
                                }
                            }
                            else if (storeMappingsDic.TryGetValue(storeId, out var storeMappingToDelete))
                            {
                                _db.StoreMappings.Remove(storeMappingToDelete);
                            }
                        }
                    }

                    await scope.CommitAsync();

                    affectedProducts += products.Count;
                }

                await scope.CommitAsync();

                try
                {
                    scope.DbContext.DetachEntities(x => x is Product || x is Category || x is StoreMapping, false);
                }
                catch { }

                foreach (var subCategory in subCategories)
                {
                    await ProcessCategory(scope, subCategory);
                }
            }
        }
        public IEnumerable <MediaTrackAction> DetectAllTracks(string albumName)
        {
            var ctx        = _dbContext;
            var entityName = string.Empty;

            // TODO: Messages, Forums (?), Store (?)

            // Products
            if (albumName == Products)
            {
                // Products
                {
                    var name = nameof(ProductMediaFile);
                    var p    = new FastPager <ProductMediaFile>(ctx.Set <ProductMediaFile>().AsNoTracking());
                    while (p.ReadNextPage(x => new { x.Id, x.ProductId, x.MediaFileId }, x => x.Id, out var list))
                    {
                        foreach (var x in list)
                        {
                            yield return(new MediaTrackAction {
                                EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId
                            });
                        }
                    }
                }

                // ProductAttributeOption
                {
                    var name = nameof(ProductAttributeOption);
                    var p    = new FastPager <ProductAttributeOption>(ctx.Set <ProductAttributeOption>().AsNoTracking().Where(x => x.MediaFileId > 0));
                    while (p.ReadNextPage(x => new { x.Id, x.MediaFileId }, x => x.Id, out var list))
                    {
                        foreach (var x in list)
                        {
                            yield return(new MediaTrackAction {
                                EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId
                            });
                        }
                    }
                }

                // ProductVariantAttributeValue
                {
                    var name = nameof(ProductVariantAttributeValue);
                    var p    = new FastPager <ProductVariantAttributeValue>(ctx.Set <ProductVariantAttributeValue>().AsNoTracking().Where(x => x.MediaFileId > 0));
                    while (p.ReadNextPage(x => new { x.Id, x.MediaFileId }, x => x.Id, out var list))
                    {
                        foreach (var x in list)
                        {
                            yield return(new MediaTrackAction {
                                EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId
                            });
                        }
                    }
                }

                // SpecificationAttributeOption
                {
                    var name = nameof(SpecificationAttributeOption);
                    var p    = new FastPager <SpecificationAttributeOption>(ctx.Set <SpecificationAttributeOption>().AsNoTracking().Where(x => x.MediaFileId > 0));
                    while (p.ReadNextPage(x => new { x.Id, x.MediaFileId }, x => x.Id, out var list))
                    {
                        foreach (var x in list)
                        {
                            yield return(new MediaTrackAction {
                                EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId
                            });
                        }
                    }
                }

                yield break;
            }

            // Categories
            if (albumName == Categories)
            {
                var name = nameof(Category);
                var p    = new FastPager <Category>(ctx.Set <Category>().AsNoTracking().Where(x => x.MediaFileId.HasValue));
                while (p.ReadNextPage(x => new { x.Id, x.MediaFileId }, x => x.Id, out var list))
                {
                    foreach (var x in list)
                    {
                        yield return(new MediaTrackAction {
                            EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId.Value
                        });
                    }
                }

                yield break;
            }

            // Brands
            if (albumName == Brands)
            {
                var name = nameof(Manufacturer);
                var p    = new FastPager <Manufacturer>(ctx.Set <Manufacturer>().AsNoTracking().Where(x => x.MediaFileId.HasValue));
                while (p.ReadNextPage(x => new { x.Id, x.MediaFileId }, x => x.Id, out var list))
                {
                    foreach (var x in list)
                    {
                        yield return(new MediaTrackAction {
                            EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId.Value
                        });
                    }
                }

                yield break;
            }

            // BlogPost
            if (albumName == Blog)
            {
                var name = nameof(BlogPost);
                var p    = new FastPager <BlogPost>(ctx.Set <BlogPost>().AsNoTracking().Where(x => x.MediaFileId.HasValue || x.PreviewMediaFileId.HasValue));
                while (p.ReadNextPage(x => new { x.Id, x.MediaFileId, x.PreviewMediaFileId }, x => x.Id, out var list))
                {
                    foreach (var x in list)
                    {
                        if (x.MediaFileId.HasValue)
                        {
                            yield return new MediaTrackAction {
                                       EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId.Value
                            }
                        }
                        ;
                        if (x.PreviewMediaFileId.HasValue)
                        {
                            yield return new MediaTrackAction {
                                       EntityId = x.Id, EntityName = name, MediaFileId = x.PreviewMediaFileId.Value
                            }
                        }
                        ;
                    }
                }

                yield break;
            }

            // NewsItem
            if (albumName == News)
            {
                var name = nameof(NewsItem);
                var p    = new FastPager <NewsItem>(ctx.Set <NewsItem>().AsNoTracking().Where(x => x.MediaFileId.HasValue || x.PreviewMediaFileId.HasValue));
                while (p.ReadNextPage(x => new { x.Id, x.MediaFileId, x.PreviewMediaFileId }, x => x.Id, out var list))
                {
                    foreach (var x in list)
                    {
                        if (x.MediaFileId.HasValue)
                        {
                            yield return new MediaTrackAction {
                                       EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId.Value
                            }
                        }
                        ;
                        if (x.PreviewMediaFileId.HasValue)
                        {
                            yield return new MediaTrackAction {
                                       EntityId = x.Id, EntityName = name, MediaFileId = x.PreviewMediaFileId.Value
                            }
                        }
                        ;
                    }
                }

                yield break;
            }

            // Customer
            if (albumName == Customers)
            {
                var name = nameof(Customers);

                // Avatars
                var p = new FastPager <GenericAttribute>(ctx.Set <GenericAttribute>().AsNoTracking()
                                                         .Where(x => x.KeyGroup == nameof(Customer) && x.Key == SystemCustomerAttributeNames.AvatarPictureId));
                while (p.ReadNextPage(x => new { x.Id, x.EntityId, x.Value }, x => x.Id, out var list))
                {
                    foreach (var x in list)
                    {
                        var id = x.Value.ToInt();
                        if (id > 0)
                        {
                            yield return(new MediaTrackAction {
                                EntityId = x.EntityId, EntityName = name, MediaFileId = id
                            });
                        }
                    }
                }

                yield break;
            }

            // Downloads
            if (albumName == Downloads)
            {
                var name = nameof(Download);
                var p    = new FastPager <Download>(ctx.Set <Download>().AsNoTracking().Where(x => x.MediaFileId.HasValue));
                while (p.ReadNextPage(x => new { x.Id, x.MediaFileId }, x => x.Id, out var list))
                {
                    foreach (var x in list)
                    {
                        yield return(new MediaTrackAction {
                            EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId.Value
                        });
                    }
                }

                yield break;
            }

            // Messages
            if (albumName == Messages)
            {
                var name = nameof(MessageTemplate);
                var p    = new FastPager <MessageTemplate>(ctx.Set <MessageTemplate>().AsNoTracking());
                while (p.ReadNextPage(x => new { x.Id, x.Attachment1FileId, x.Attachment2FileId, x.Attachment3FileId }, x => x.Id, out var list))
                {
                    foreach (var x in list)
                    {
                        if (x.Attachment1FileId.HasValue)
                        {
                            yield return new MediaTrackAction {
                                       EntityId = x.Id, EntityName = name, MediaFileId = x.Attachment1FileId.Value
                            }
                        }
                        ;
                        if (x.Attachment2FileId.HasValue)
                        {
                            yield return new MediaTrackAction {
                                       EntityId = x.Id, EntityName = name, MediaFileId = x.Attachment2FileId.Value
                            }
                        }
                        ;
                        if (x.Attachment3FileId.HasValue)
                        {
                            yield return new MediaTrackAction {
                                       EntityId = x.Id, EntityName = name, MediaFileId = x.Attachment3FileId.Value
                            }
                        }
                        ;
                    }
                }

                yield break;
            }
        }

        #endregion
    }
}
Beispiel #24
0
        public override async Task ExecuteAsync(TaskExecutionContext ctx)
        {
            var count         = 0;
            var numDeleted    = 0;
            var numAdded      = 0;
            var numCategories = 0;
            var pageSize      = 500;
            var pageIndex     = -1;

            var categoryIds = ctx.Parameters.ContainsKey("CategoryIds")
                ? ctx.Parameters["CategoryIds"].ToIntArray()
                : null;

            // Hooks are enabled because search index needs to be updated.
            using (var scope = new DbContextScope(ctx: _productCategoryRepository.Context, autoDetectChanges: false, validateOnSave: false, hooksEnabled: true, autoCommit: false))
            {
                // Delete existing system mappings.
                var deleteQuery = _productCategoryRepository.Table.Where(x => x.IsSystemMapping);
                if (categoryIds != null)
                {
                    deleteQuery = deleteQuery.Where(x => categoryIds.Contains(x.CategoryId));
                }

                var pager = new FastPager <ProductCategory>(deleteQuery, pageSize);

                while (pager.ReadNextPage(out var mappings))
                {
                    if (ctx.CancellationToken.IsCancellationRequested)
                    {
                        return;
                    }

                    if (mappings.Any())
                    {
                        _productCategoryRepository.DeleteRange(mappings);
                        numDeleted += await scope.CommitAsync();
                    }
                }

                try
                {
                    _productCategoryRepository.Context.DetachEntities <ProductCategory>();
                }
                catch { }

                // Insert new product category mappings.
                var categoryQuery = _categoryRepository.TableUntracked.Expand(x => x.RuleSets);
                if (categoryIds != null)
                {
                    categoryQuery = categoryQuery.Where(x => categoryIds.Contains(x.Id));
                }

                var categories = await categoryQuery
                                 .Where(x => x.Published && !x.Deleted && x.RuleSets.Any(y => y.IsActive))
                                 .ToListAsync();

                numCategories = categories.Count;

                foreach (var category in categories)
                {
                    var ruleSetProductIds = new HashSet <int>();

                    ctx.SetProgress(++count, categories.Count, $"Add product mappings for category \"{category.Name.NaIfEmpty()}\".");

                    // Execute active rule sets and collect product ids.
                    foreach (var ruleSet in category.RuleSets.Where(x => x.IsActive))
                    {
                        if (ctx.CancellationToken.IsCancellationRequested)
                        {
                            return;
                        }

                        if (_ruleFactory.CreateExpressionGroup(ruleSet, _productRuleProvider) is SearchFilterExpression expression)
                        {
                            pageIndex = -1;
                            while (true)
                            {
                                // Do not touch searchResult.Hits. We only need the product identifiers.
                                var searchResult = _productRuleProvider.Search(new SearchFilterExpression[] { expression }, ++pageIndex, pageSize);
                                ruleSetProductIds.AddRange(searchResult.HitsEntityIds);

                                if (pageIndex >= (searchResult.TotalHitsCount / pageSize))
                                {
                                    break;
                                }
                            }
                        }
                    }

                    // Add mappings.
                    if (ruleSetProductIds.Any())
                    {
                        foreach (var chunk in ruleSetProductIds.Slice(500))
                        {
                            if (ctx.CancellationToken.IsCancellationRequested)
                            {
                                return;
                            }

                            foreach (var productId in chunk)
                            {
                                _productCategoryRepository.Insert(new ProductCategory
                                {
                                    ProductId       = productId,
                                    CategoryId      = category.Id,
                                    IsSystemMapping = true
                                });

                                ++numAdded;
                            }

                            await scope.CommitAsync();
                        }

                        try
                        {
                            _productCategoryRepository.Context.DetachEntities <ProductCategory>();
                        }
                        catch { }
                    }
                }
            }

            Debug.WriteLineIf(numDeleted > 0 || numAdded > 0, $"Deleted {numDeleted} and added {numAdded} product mappings for {numCategories} categories.");
        }