Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
@@ -0,0 +1,23 @@
namespace VirtoCommerce.CatalogModule.Core.Extensions;

public static partial class StringExtensions
{
public static string SoftTruncate(this string value, int maxLength)
{
if (string.IsNullOrEmpty(value) || value.Length <= maxLength)
{
return value;
}

value = value[..maxLength];

var idx = value.LastIndexOf(' ');

if (idx != -1)
{
return value[..idx].Trim();
}

return value;
}
}
1 change: 1 addition & 0 deletions src/VirtoCommerce.CatalogModule.Core/ModuleConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,5 +208,6 @@ public static IEnumerable<SettingDescriptor> AllSettings

public const string OutlineDelimiter = "___";
public const string OperationLogVariationMarker = "MainProductId:";
public const int MaxSEOTitleLength = 70;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
using System.Threading.Tasks;
using Newtonsoft.Json;
using VirtoCommerce.AssetsModule.Core.Assets;
using VirtoCommerce.CatalogModule.Core;
using VirtoCommerce.CatalogModule.Core.Extensions;
using VirtoCommerce.CatalogModule.Core.Model;
using VirtoCommerce.CatalogModule.Core.Model.Search;
using VirtoCommerce.CatalogModule.Core.Search;
using VirtoCommerce.CatalogModule.Core.Services;
using VirtoCommerce.CoreModule.Core.Seo;
using VirtoCommerce.Platform.Core.Common;
using VirtoCommerce.Platform.Core.ExportImport;

Expand Down Expand Up @@ -232,8 +235,29 @@ public async Task DoImportAsync(Stream inputStream, ExportImportOptions options,

private async Task ImportCatalogsAsync(JsonTextReader reader, ExportImportProgressInfo progressInfo, Action<ExportImportProgressInfo> progressCallback, ICancellationToken cancellationToken)
{
await reader.DeserializeArrayWithPagingAsync<Catalog>(_jsonSerializer, _batchSize,
_catalogService.SaveChangesAsync,
await reader.DeserializeArrayWithPagingAsync<Catalog>(_jsonSerializer, _batchSize, async catalogs =>
{
foreach (var catalog in catalogs)
{
if (catalog.SeoInfos == null || !catalog.SeoInfos.Any())
{
var defaultLanguage = catalog.Languages.First(x => x.IsDefault).LanguageCode;
var seoInfo = AbstractTypeFactory<SeoInfo>.TryCreateInstance();
seoInfo.LanguageCode = defaultLanguage;
seoInfo.SemanticUrl = "catalog";
seoInfo.PageTitle = "Catalog";
catalog.SeoInfos = [seoInfo];
}

foreach (var seoInfo in catalog.SeoInfos)
{
seoInfo.SemanticUrl ??= "catalog";
seoInfo.PageTitle ??= "Catalog";
}
}

await _catalogService.SaveChangesAsync(catalogs);
},
processedCount =>
{
progressInfo.Description = $"{processedCount} catalogs have been imported";
Expand All @@ -244,37 +268,58 @@ await reader.DeserializeArrayWithPagingAsync<Catalog>(_jsonSerializer, _batchSiz
private async Task ImportCategoriesAsync(JsonTextReader reader, ExportImportProgressInfo progressInfo, Action<ExportImportProgressInfo> progressCallback, ICancellationToken cancellationToken)
{
var processedCount = 0;

var categoriesByHierarchyLevel = new Dictionary<int, IList<Category>>();
var categoryLinks = new List<CategoryLink>();

await reader.DeserializeArrayWithPagingAsync<Category>(_jsonSerializer, _batchSize, async items =>
{
var categories = new List<Category>();

foreach (var item in items)
foreach (var category in items)
{
var slugUrl = category.Name.GenerateSlug();

if (category.SeoInfos.IsNullOrEmpty() && !string.IsNullOrEmpty(slugUrl))
{
var catalog = await _catalogService.GetNoCloneAsync(category.CatalogId);
var defaultLanguage = catalog?.Languages.First(x => x.IsDefault).LanguageCode;
var seoInfo = AbstractTypeFactory<SeoInfo>.TryCreateInstance();
seoInfo.LanguageCode = defaultLanguage;
seoInfo.SemanticUrl = slugUrl;
seoInfo.PageTitle = category.Name.SoftTruncate(ModuleConstants.MaxSEOTitleLength);
category.SeoInfos = [seoInfo];
}

foreach (var seoInfo in category.SeoInfos)
{
if (string.IsNullOrEmpty(seoInfo.SemanticUrl) && !string.IsNullOrEmpty(slugUrl))
{
seoInfo.SemanticUrl = slugUrl;
}
seoInfo.PageTitle ??= category.Name.SoftTruncate(ModuleConstants.MaxSEOTitleLength);
}

// clear category links (to save later)
foreach (var link in item.Links.Where(x => x.EntryId == null))
foreach (var link in category.Links.Where(x => x.EntryId == null))
{
link.ListEntryId = item.Id;
link.ListEntryId = category.Id;
}

categoryLinks.AddRange(item.Links);
item.Links = new List<CategoryLink>();
categoryLinks.AddRange(category.Links);
category.Links = new List<CategoryLink>();

if (item.Level > 0)
if (category.Level > 0)
{
if (!categoriesByHierarchyLevel.ContainsKey(item.Level))
if (!categoriesByHierarchyLevel.ContainsKey(category.Level))
{
categoriesByHierarchyLevel.Add(item.Level, new List<Category>());
categoriesByHierarchyLevel.Add(category.Level, new List<Category>());
}

categoriesByHierarchyLevel[item.Level].Add(item);
categoriesByHierarchyLevel[category.Level].Add(category);
}
else
{
categories.Add(item);
categories.Add(category);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ public class CatalogModuleCatalogsController : Controller
private readonly IAuthorizationService _authorizationService;

public CatalogModuleCatalogsController(
ICatalogService catalogService
, ICatalogSearchService catalogSearchService
, IAuthorizationService authorizationService
ICatalogService catalogService,
ICatalogSearchService catalogSearchService,
IAuthorizationService authorizationService
)
{
_catalogService = catalogService;
Expand Down Expand Up @@ -158,6 +158,7 @@ public async Task<ActionResult<Catalog>> CreateCatalog([FromBody] Catalog catalo
var seoInfo = AbstractTypeFactory<SeoInfo>.TryCreateInstance();
seoInfo.LanguageCode = defaultLanguage;
seoInfo.SemanticUrl = "catalog";
seoInfo.PageTitle = "Catalog";
catalog.SeoInfos = [seoInfo];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using VirtoCommerce.CatalogModule.Core;
using VirtoCommerce.CatalogModule.Core.Extensions;
using VirtoCommerce.CatalogModule.Core.Model;
using VirtoCommerce.CatalogModule.Core.Services;
using VirtoCommerce.CatalogModule.Data.Authorization;
Expand Down Expand Up @@ -134,7 +135,8 @@ public async Task<ActionResult> CreateOrUpdateCategory([FromBody] Category categ
var seoInfo = AbstractTypeFactory<SeoInfo>.TryCreateInstance();
seoInfo.LanguageCode = defaultLanguage;
seoInfo.SemanticUrl = slugUrl;
category.SeoInfos = new[] { seoInfo };
seoInfo.PageTitle = category.Name.SoftTruncate(ModuleConstants.MaxSEOTitleLength);
category.SeoInfos = [seoInfo];
}
}
}
Expand Down