Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using Grand.Business.Core.Interfaces.Storage;
using Grand.Business.Core.Utilities.System;
using Grand.Domain.Catalog;
using Grand.Domain.Media;
using Grand.Domain.Seo;
using Grand.Infrastructure.Configuration;
using Grand.Infrastructure.Mapper;
Expand Down Expand Up @@ -44,10 +43,12 @@ public BrandImportDataObject(
_logger = logger;
}

public async Task Execute(IEnumerable<BrandDto> data)
public async Task Execute(IEnumerable<BrandDto> data, CancellationToken cancellationToken = default)
{
var result = await Import(data.ToList(), false, ImportMode.Panel);
var result = await Import(data.ToList(), false, ImportMode.Panel, cancellationToken);
ImportRows.LogRejected(_logger, "brand", result);
//what was saved stays saved; the panel must not report a cancelled import as done
cancellationToken.ThrowIfCancellationRequested();
}

/// <summary>Row API: an unknown Id is rejected, a row without Id is matched by SeName, else (best effort) by Name</summary>
Expand All @@ -61,11 +62,13 @@ public Task<ImportBatchResult> Import(IReadOnlyList<BrandDto> rows, bool dryRun,
private Task<ImportBatchResult> Import(IReadOnlyList<BrandDto> rows, bool dryRun, ImportMode mode,
CancellationToken cancellationToken = default)
{
return ImportRows.RunBatch(rows, dryRun, r => r.Id, Key, (row, dto) => ImportRow(row, dto, dryRun, mode),
mode == ImportMode.Row, _logger, cancellationToken);
return ImportRows.RunBatch(rows, dryRun, r => r.Id, Key,
(row, dto) => ImportRow(row, dto, dryRun, mode, cancellationToken), mode == ImportMode.Row, _logger,
cancellationToken);
}

private async Task<ImportRowResult> ImportRow(int row, BrandDto dto, bool dryRun, ImportMode mode)
private async Task<ImportRowResult> ImportRow(int row, BrandDto dto, bool dryRun, ImportMode mode,
CancellationToken cancellationToken)
{
var errors = new List<string>();
var warnings = new List<string>();
Expand Down Expand Up @@ -109,10 +112,14 @@ private async Task<ImportRowResult> ImportRow(int row, BrandDto dto, bool dryRun
return new ImportRowResult(row, ImportRowStatus.Rejected, "", key, errors, warnings);
}

//downloaded before anything is written, so a cancelled download leaves the row untouched
var image = await ImportPictures.Download(dto.Picture, _securityConfig.PictureImportAllowedPrivateHosts,
cancellationToken);

if (isNew) await _brandService.InsertBrand(brand);
else await _brandService.UpdateBrand(brand);

await UpdateBrandData(dto, brand);
await UpdateBrandData(brand, image);
return new ImportRowResult(row, isNew ? ImportRowStatus.Created : ImportRowStatus.Updated, brand.Id, key,
errors, warnings);
}
Expand Down Expand Up @@ -146,7 +153,7 @@ private static string Key(BrandDto dto)
return (found, found != null, error);
}

private async Task UpdateBrandData(BrandDto brandDto, Brand brand)
private async Task UpdateBrandData(Brand brand, DownloadedImage image)
{
if (string.IsNullOrEmpty(brand.BrandLayoutId))
{
Expand All @@ -159,12 +166,9 @@ private async Task UpdateBrandData(BrandDto brandDto, Brand brand)
brand.BrandLayoutId = (await _brandLayoutService.GetAllBrandLayouts()).FirstOrDefault()?.Id;
}

if (!string.IsNullOrEmpty(brandDto.Picture))
{
var picture = await LoadPicture(brandDto.Picture, brand.Name, brand.PictureId);
if (picture != null)
brand.PictureId = picture.Id;
}
var picture = await ImportPictures.Save(_pictureService, image, brand.Name, brand.PictureId);
if (picture != null)
brand.PictureId = picture.Id;

var seName = brand.SeName ?? brand.Name;
seName = await _seNameService.ValidateSeName(brand, seName, brand.Name, true);
Expand All @@ -178,45 +182,4 @@ private static bool ValidBrand(Brand brand)
{
return !string.IsNullOrEmpty(brand.Name);
}

/// <summary>
/// Creates or loads the image
/// </summary>
/// <param name="pictureUrl">The URL of the image</param>
/// <param name="name">The name of the object</param>
/// <param name="picId">Image identifier, may be null</param>
/// <returns>The image or null if the image has not changed</returns>
private async Task<Picture> LoadPicture(string pictureUrl, string name, string picId = "")
{
if (string.IsNullOrEmpty(pictureUrl))
return null;

//only http(s) URLs to public or explicitly allowed hosts are downloaded - a local path would let the
//import read any file on the server
var image = await DownloadUrl.DownloadImage(pictureUrl, _securityConfig.PictureImportAllowedPrivateHosts);
if (image == null)
return null;

var mimeType = image.MimeType;
var newPictureBinary = image.Binary;
var pictureAlreadyExists = false;
if (!string.IsNullOrEmpty(picId))
{
//compare with existing product pictures
var existingPicture = await _pictureService.GetPictureById(picId);

var existingBinary = await _pictureService.LoadPictureBinary(existingPicture);
//picture binary after validation (like in database)
var validatedPictureBinary = _pictureService.ValidatePicture(newPictureBinary, mimeType);
if (existingBinary.SequenceEqual(validatedPictureBinary) ||
existingBinary.SequenceEqual(newPictureBinary))
pictureAlreadyExists = true;
}

if (pictureAlreadyExists) return null;

var newPicture = await _pictureService.InsertPicture(newPictureBinary, mimeType,
_pictureService.GetPictureSeName(name));
return newPicture;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ public BrandSchemaProperty(IPictureService pictureService)
_pictureService = pictureService;
}

public virtual async Task<PropertyByName<Brand>[]> GetProperties(IEnumerable<Brand> items)
public virtual async Task<PropertyByName<Brand>[]> GetProperties(IEnumerable<Brand> items,
CancellationToken cancellationToken = default)
{
var pictures = await ExportPictures.Preload(items.Select(x => x.PictureId), GetPictures);
var pictures =
await ExportPictures.Preload(items.Select(x => x.PictureId), GetPictures, cancellationToken);

var properties = new[] {
new PropertyByName<Brand>("Id", p => p.Id),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using Grand.Business.Core.Interfaces.Storage;
using Grand.Business.Core.Utilities.System;
using Grand.Domain.Catalog;
using Grand.Domain.Media;
using Grand.Domain.Seo;
using Grand.Infrastructure.Configuration;
using Grand.Infrastructure.Mapper;
Expand Down Expand Up @@ -44,10 +43,12 @@ public CategoryImportDataObject(
_logger = logger;
}

public async Task Execute(IEnumerable<CategoryDto> data)
public async Task Execute(IEnumerable<CategoryDto> data, CancellationToken cancellationToken = default)
{
var result = await Import(data.ToList(), false, ImportMode.Panel);
var result = await Import(data.ToList(), false, ImportMode.Panel, cancellationToken);
ImportRows.LogRejected(_logger, "category", result);
//what was saved stays saved; the panel must not report a cancelled import as done
cancellationToken.ThrowIfCancellationRequested();
}

/// <summary>Row API: an unknown Id is rejected, a row without Id is matched by SeName, else (best effort) by Name</summary>
Expand All @@ -61,11 +62,13 @@ public Task<ImportBatchResult> Import(IReadOnlyList<CategoryDto> rows, bool dryR
private Task<ImportBatchResult> Import(IReadOnlyList<CategoryDto> rows, bool dryRun, ImportMode mode,
CancellationToken cancellationToken = default)
{
return ImportRows.RunBatch(rows, dryRun, r => r.Id, Key, (row, dto) => ImportRow(row, dto, dryRun, mode),
mode == ImportMode.Row, _logger, cancellationToken);
return ImportRows.RunBatch(rows, dryRun, r => r.Id, Key,
(row, dto) => ImportRow(row, dto, dryRun, mode, cancellationToken), mode == ImportMode.Row, _logger,
cancellationToken);
}

private async Task<ImportRowResult> ImportRow(int row, CategoryDto dto, bool dryRun, ImportMode mode)
private async Task<ImportRowResult> ImportRow(int row, CategoryDto dto, bool dryRun, ImportMode mode,
CancellationToken cancellationToken)
{
var errors = new List<string>();
var warnings = new List<string>();
Expand Down Expand Up @@ -114,10 +117,14 @@ private async Task<ImportRowResult> ImportRow(int row, CategoryDto dto, bool dry
return new ImportRowResult(row, ImportRowStatus.Rejected, "", key, errors, warnings);
}

//downloaded before anything is written, so a cancelled download leaves the row untouched
var image = await ImportPictures.Download(dto.Picture, _securityConfig.PictureImportAllowedPrivateHosts,
cancellationToken);

if (isNew) await _categoryService.InsertCategory(category);
else await _categoryService.UpdateCategory(category);

await UpdateCategoryData(dto, category, parentMissing);
await UpdateCategoryData(dto, category, parentMissing, image);
return new ImportRowResult(row, isNew ? ImportRowStatus.Created : ImportRowStatus.Updated, category.Id, key,
errors, warnings);
}
Expand Down Expand Up @@ -153,7 +160,8 @@ private static string Key(CategoryDto dto)
return (found, found != null, error);
}

private async Task UpdateCategoryData(CategoryDto categoryDto, Category category, bool parentMissing)
private async Task UpdateCategoryData(CategoryDto categoryDto, Category category, bool parentMissing,
DownloadedImage image)
{
if (string.IsNullOrEmpty(category.CategoryLayoutId))
{
Expand All @@ -174,12 +182,9 @@ private async Task UpdateCategoryData(CategoryDto categoryDto, Category category
await _categoryService.GetCategoryById(category.ParentCategoryId) == null)
category.ParentCategoryId = string.Empty;

if (!string.IsNullOrEmpty(categoryDto.Picture))
{
var picture = await LoadPicture(categoryDto.Picture, category.Name, category.PictureId);
if (picture != null)
category.PictureId = picture.Id;
}
var picture = await ImportPictures.Save(_pictureService, image, category.Name, category.PictureId);
if (picture != null)
category.PictureId = picture.Id;

var seName = category.SeName ?? category.Name;
seName = await _seNameService.ValidateSeName(category, seName, category.Name, true);
Expand All @@ -192,45 +197,4 @@ private static bool ValidCategory(Category category)
{
return !string.IsNullOrEmpty(category.Name);
}

/// <summary>
/// Creates or loads the image
/// </summary>
/// <param name="pictureUrl">The URL of the image</param>
/// <param name="name">The name of the object</param>
/// <param name="picId">Image identifier, may be null</param>
/// <returns>The image or null if the image has not changed</returns>
private async Task<Picture> LoadPicture(string pictureUrl, string name, string picId = "")
{
if (string.IsNullOrEmpty(pictureUrl))
return null;

//only http(s) URLs to public or explicitly allowed hosts are downloaded - a local path would let the
//import read any file on the server
var image = await DownloadUrl.DownloadImage(pictureUrl, _securityConfig.PictureImportAllowedPrivateHosts);
if (image == null)
return null;

var mimeType = image.MimeType;
var newPictureBinary = image.Binary;
var pictureAlreadyExists = false;
if (!string.IsNullOrEmpty(picId))
{
//compare with existing product pictures
var existingPicture = await _pictureService.GetPictureById(picId);

var existingBinary = await _pictureService.LoadPictureBinary(existingPicture);
//picture binary after validation (like in database)
var validatedPictureBinary = _pictureService.ValidatePicture(newPictureBinary, mimeType);
if (existingBinary.SequenceEqual(validatedPictureBinary) ||
existingBinary.SequenceEqual(newPictureBinary))
pictureAlreadyExists = true;
}

if (pictureAlreadyExists) return null;

var newPicture = await _pictureService.InsertPicture(newPictureBinary, mimeType,
_pictureService.GetPictureSeName(name));
return newPicture;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ public CategorySchemaProperty(IPictureService pictureService)
_pictureService = pictureService;
}

public virtual async Task<PropertyByName<Category>[]> GetProperties(IEnumerable<Category> items)
public virtual async Task<PropertyByName<Category>[]> GetProperties(IEnumerable<Category> items,
CancellationToken cancellationToken = default)
{
var pictures = await ExportPictures.Preload(items.Select(x => x.PictureId), GetPictures);
var pictures =
await ExportPictures.Preload(items.Select(x => x.PictureId), GetPictures, cancellationToken);

var properties = new[] {
new PropertyByName<Category>("Id", p => p.Id),
Expand Down
Loading
Loading