public Dictionary<string, object> ConvertArticle(Article article, IArticleFilter filter) { if (article == null || !article.Visible || article.Archived || !filter.Matches(article)) { return null; } var dict = new Dictionary<string, object> { { IdProp, article.Id } }; foreach (ArticleField field in article.Fields.Values) { if (field is ExtensionArticleField extensionArticleField) { MergeExtensionFields(dict, extensionArticleField, filter); } else { object value = ConvertField(field, filter); if (value != null && !(value is string && string.IsNullOrEmpty((string)value))) { dict[field.FieldName] = value; } } } return dict; }
public void AddExtensionArticle(int parentId, Article article) { if (!_extensionMap.ContainsKey(parentId)) { _extensionMap[parentId] = new List <Article>(); } _extensionMap[parentId].Add(article); }
private void ValidateDates(Article newArticle, Article existingArticle) { if (existingArticle != null && newArticle.Modified != default(DateTime) && newArticle.Modified != existingArticle.Modified) { _outdatedArticleIds.Add(newArticle.Id); } }
private IEnumerable <object> GetFields(Article article, CallContext ctx, bool omitId = false) { var systemFields = GetAttributes(article); var fields = (article.Fields.Values .Where( x => (x is PlainArticleField && !string.IsNullOrEmpty(((PlainArticleField)x).Value) || !(x is PlainArticleField))) .Select(x => Convert(x, ctx)).Where(x => x != null)); if (omitId) { return(fields); } return(systemFields.Union(fields)); }
private Article DeserializeArticle(IProductDataSource productDataSource, Models.Configuration.Content definition, DBConnector connector, Context context) { if (productDataSource == null) { return(null); } int id = productDataSource.GetArticleId(); context.TakeIntoAccount(id); var qpContent = _contentService.Read(definition.ContentId); Article article = new Article { Id = id, ContentName = qpContent.NetName, Modified = productDataSource.GetModified(), ContentId = definition.ContentId, ContentDisplayName = qpContent.Name, PublishingMode = definition.PublishingMode, IsReadOnly = definition.IsReadOnly, Visible = true }; foreach (Field fieldInDef in definition.Fields.Where(x => !(x is BaseVirtualField) && !(x is Dictionaries))) { var field = DeserializeField(fieldInDef, _fieldService.Read(fieldInDef.FieldId), productDataSource, connector, context); article.Fields[field.FieldName] = field; } if (definition.LoadAllPlainFields) { var qpFields = _fieldService.List(definition.ContentId); foreach (var plainFieldFromQp in qpFields.Where(x => x.RelationType == RelationType.None && definition.Fields.All(y => y.FieldId != x.Id))) { article.Fields[plainFieldFromQp.Name] = DeserializeField(new PlainField { FieldId = plainFieldFromQp.Id }, plainFieldFromQp, productDataSource, connector, context); } } return(article); }
private IEnumerable <Article> GetChildArticles(ArticleField field, IArticleFilter filter) { if (field is IGetArticle getArticle) { Article childArticle = getArticle.GetItem(filter); return(childArticle != null ? new[] { childArticle } : new Article[0]); } else if (field is IGetArticles getArticles) { return(getArticles.GetArticles(filter)); } else { return(new Article[0]); } }
public string SerializeProduct(Article article, IArticleFilter filter, bool includeRegionTags = false) { var sw = new Stopwatch(); sw.Start(); Dictionary<string, object> convertedArticle = ConvertArticle(article, filter); sw.Stop(); _logger.Debug("Product {1} conversion took {0} sec", sw.Elapsed.TotalSeconds, article.Id); sw.Reset(); sw.Start(); string productJson = JsonConvert.SerializeObject(convertedArticle, Formatting.Indented); sw.Stop(); _logger.Debug("Product {1} serializing took {0} sec", sw.Elapsed.TotalSeconds, article.Id); string result; if (includeRegionTags && article.GetField("Regions") is MultiArticleField regionField) { sw.Reset(); sw.Start(); int[] regionIds = regionField.Items.Keys.ToArray(); TagWithValues[] tags = _regionTagReplaceService.GetRegionTagValues(productJson, regionIds); string tagsJson = JsonConvert.SerializeObject( tags, Formatting.Indented, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }); sw.Stop(); _logger.Debug("Product {1} enrichment with regional tags took {0} sec", sw.Elapsed.TotalSeconds, article.Id); result = $"{{ \"{ProductProp}\" : {productJson}, \"{RegionTagsProp}\" : {tagsJson} }}"; } else { result = $"{{ \"{ProductProp}\" : {productJson} }}"; } return result; }
public string GetProductXml(Article article, IArticleFilter filter) { return(ProcessProductWithTags(filter, article)); }
private IEnumerable <object> GetAttributes(Article article) { yield return(new XElement("Id", article.Id)); }
private object Convert(Article article, CallContext ctx, bool omitType = false, bool addXsi = false, string forceName = null) { if (article == null) { return(null); } if (article.ContentId == default(int) && forceName == null) { return(""); } if (!article.Visible || article.Archived) { return(null); } if (omitType) { return(GetFields(article, ctx)); } var fs = GetFields(article, ctx); string nodeName = forceName ?? article.ContentName; if (string.IsNullOrWhiteSpace(nodeName)) { throw new Exception( string.Format("Error while xml generation: ContentName is not filled in the article. ContentId={0} Id={1}", article.ContentId, article.Id)); } var node = new XElement(nodeName, fs); if (addXsi) { var typeField = _settingsService.GetSetting(SettingsTitles.PRODUCT_TYPES_FIELD_NAME); ArticleField f; if (!string.IsNullOrEmpty(typeField)) { typeField = "Type"; } if (article.Fields.TryGetValue(typeField, out f)) { XNamespace ns = "http://www.w3.org/2001/XMLSchema-instance"; var sf = f as SingleArticleField; var pf = f as PlainArticleField; if (sf != null && sf.Item != null) { node.Add(new XAttribute(ns + "type", sf.Item.ContentName)); } else if (pf != null && !string.IsNullOrEmpty(pf.Value)) { node.Add(new XAttribute(ns + "type", pf.Value)); } } } return(node); }
/// <exception cref="ArgumentNullException" /> /// <exception cref="InvalidOperationException" /> private void ProcessArticlesTree(Article newArticle, Article existingArticle, Content definition) { if (newArticle == null || !_filter.Matches(newArticle)) { return; } if (!_filter.Matches(existingArticle)) { existingArticle = null; } if (definition == null) { throw new ArgumentNullException(nameof(definition)); } ValidateDates(newArticle, existingArticle); ArticleData newArticleUpdateData = new ArticleData { ContentId = definition.ContentId, Id = newArticle.Id }; List <int> plainFieldIds = definition.Fields .Where(x => x is PlainField) .Select(x => x.FieldId) .ToList(); if (definition.LoadAllPlainFields) { plainFieldIds.AddRange( _fieldService.List(definition.ContentId) .Where(x => x.RelationType == RelationType.None && definition.Fields.All(y => y.FieldId != x.Id)) .Select(x => x.Id)); } // TODO: исключаем Readonly поля var updatedFields = newArticle.Fields.Values .OfType <PlainArticleField>() .Where(x => plainFieldIds.Contains(x.FieldId.Value) && (existingArticle == null || existingArticle.Fields.Values .OfType <PlainArticleField>() .All(y => y.FieldId != x.FieldId || !HasEqualNativeValues(x, y)))) .Select(x => new FieldData { Id = x.FieldId.Value, Value = x.Value }); newArticleUpdateData.Fields.AddRange(updatedFields); var associationFieldsInfo = ( from fieldDef in definition.Fields.OfType <Association>() join field in newArticle.Fields.Values on fieldDef.FieldId equals field.FieldId select new { field, oldField = existingArticle?.Fields.Values.SingleOrDefault(x => x.FieldId == field.FieldId), fieldDef }).ToArray(); foreach (var fieldToSyncInfo in associationFieldsInfo) { if (fieldToSyncInfo.fieldDef is BackwardRelationField backwardRelationFieldDef) { BackwardArticleField oldField = (BackwardArticleField)fieldToSyncInfo.oldField; BackwardArticleField field = (BackwardArticleField)fieldToSyncInfo.field; int[] idsToAdd = field.GetArticles(_filter) .Where(x => oldField == null || oldField.GetArticles(_filter).All(y => y.Id != x.Id)) .Select(x => x.Id) .ToArray(); int[] idsToRemove = oldField?.GetArticles(_filter) .Select(x => x.Id) .Where(x => field.GetArticles(_filter).All(y => y.Id != x)) .ToArray() ?? new int[0]; if (idsToAdd.Any()) { _updateData.AddRange(idsToAdd.Select(x => new ArticleData { Id = x, ContentId = backwardRelationFieldDef.Content.ContentId, Fields = new List <FieldData> { new FieldData { Id = field.FieldId.Value, ArticleIds = new[] { newArticle.Id } } } })); } if (idsToRemove.Any()) { if (backwardRelationFieldDef.DeletingMode == DeletingMode.Delete) { foreach (int idToRemove in idsToRemove) { _articlesToDelete[idToRemove] = backwardRelationFieldDef.Content; } } else { _updateData.AddRange(idsToRemove.Select(x => new ArticleData { Id = x, ContentId = backwardRelationFieldDef.Content.ContentId })); } } } else if (fieldToSyncInfo.fieldDef is ExtensionField extensionFieldDef) { ExtensionArticleField oldField = (ExtensionArticleField)fieldToSyncInfo.oldField; ExtensionArticleField field = (ExtensionArticleField)fieldToSyncInfo.field; if (oldField == null || field.Value != oldField.Value) { newArticleUpdateData.Fields.Add(new FieldData { Id = extensionFieldDef.FieldId, Value = field.Value, ArticleIds = field.Item == null ? null : new[] { field.Item.Id } }); if (oldField?.Item != null) { _articlesToDelete[oldField.Item.Id] = extensionFieldDef .ContentMapping[oldField.Item.ContentId]; } } } else if (fieldToSyncInfo.field is SingleArticleField) { SingleArticleField oldField = (SingleArticleField)fieldToSyncInfo.oldField; SingleArticleField field = (SingleArticleField)fieldToSyncInfo.field; EntityField entityFieldDef = (EntityField)fieldToSyncInfo.fieldDef; Article item = field.GetItem(_filter); Article oldItem = oldField?.GetItem(_filter); if (item?.Id != oldItem?.Id) { newArticleUpdateData.Fields.Add(new FieldData { Id = field.FieldId.Value, ArticleIds = item == null ? null : new[] { item.Id } }); if (oldItem != null && entityFieldDef.DeletingMode == DeletingMode.Delete) { _articlesToDelete[oldItem.Id] = entityFieldDef.Content; } } } else if (fieldToSyncInfo.field is MultiArticleField) { MultiArticleField oldField = (MultiArticleField)fieldToSyncInfo.oldField; MultiArticleField field = (MultiArticleField)fieldToSyncInfo.field; EntityField entityFieldDef = (EntityField)fieldToSyncInfo.fieldDef; var items = field.GetArticles(_filter).ToArray(); var oldItems = oldField?.GetArticles(_filter).ToArray(); if (items.Length != (oldItems?.Length ?? 0) || items.Any(x => oldItems.All(y => y.Id != x.Id))) { newArticleUpdateData.Fields.Add(new FieldData { Id = field.FieldId.Value, ArticleIds = items.Select(x => x.Id).ToArray() }); if (entityFieldDef.DeletingMode == DeletingMode.Delete) { int[] idsToRemove = oldItems? .Where(x => items.All(y => y.Id != x.Id)) .Select(x => x.Id) .ToArray() ?? new int[0]; foreach (int idToRemove in idsToRemove) { _articlesToDelete[idToRemove] = entityFieldDef.Content; } } } } } if (newArticleUpdateData.Fields.Any()) { _updateData.Add(newArticleUpdateData); } foreach (var fieldInfo in associationFieldsInfo .Where(x => x.fieldDef.UpdatingMode == UpdatingMode.Update || x.fieldDef is ExtensionField)) { Article[] oldFieldsArticles = fieldInfo.oldField == null ? new Article[0] : GetChildArticles(fieldInfo.oldField, _filter).ToArray(); foreach (Article childArticle in GetChildArticles(fieldInfo.field, _filter)) { Content childArticleDef = fieldInfo.fieldDef.GetContents() .SingleOrDefault(x => x.ContentId == childArticle.ContentId); if (childArticleDef == null) { throw new InvalidOperationException($@"There is an conflict in product definition field {fieldInfo.field.FieldId} between ContentId={childArticle.ContentId} and Articleid={childArticle.Id}"); } Article oldChildArticle = oldFieldsArticles.SingleOrDefault(x => x.Id == childArticle.Id); ProcessArticlesTree(childArticle, oldChildArticle, childArticleDef); } } }
/// <exception cref="ProductUpdateConcurrencyException" /> public InsertData[] Update(Article product, ProductDefinition definition, bool isLive = false, bool createVersions = false) { Article oldProduct = _productService.GetProductById(product.Id, isLive, definition); _updateData.Clear(); _articlesToDelete.Clear(); _outdatedArticleIds.Clear(); _filter = isLive ? ArticleFilter.LiveFilter : ArticleFilter.DefaultFilter; ProcessArticlesTree(product, oldProduct, definition.StorageSchema); if (_outdatedArticleIds.Any()) { throw new ProductUpdateConcurrencyException(_outdatedArticleIds.ToArray()); } using (var transaction = _createTransaction()) { _logger.Info() .Message("Batch update for product {id} started", product.Id) .Property("updateData", _updateData.ToDictionary(n => n.ToString(), m => m.Fields)) .Write(); InsertData[] idMapping = _articleService.BatchUpdate(_updateData, createVersions); _logger.Info("Batch update for product {id} completed", product.Id); if (_articlesToDelete.Any()) { _logger.Info() .Message("Deleting articles for product {id} started", product.Id) .Property("articlesToDelete", _articlesToDelete.Keys) .Write(); foreach (KeyValuePair <int, Content> articleToDeleteKv in _articlesToDelete) { try { var qpArticle = _articleService.Read(articleToDeleteKv.Key); _deleteAction.DeleteProduct( qpArticle, new ProductDefinition { StorageSchema = articleToDeleteKv.Value }, true, false, null); } catch (MessageResultException ex) { _logger.Error() .Exception(ex) .Message("Cannot remove article {id}", articleToDeleteKv.Key) .Write(); } } _logger.Info("Deleting articles for product {id} completed", product.Id); } transaction.Commit(); return(idMapping); } }