Пример #1
0
 /// <exception cref="NotSupportedException" />
 /// <exception cref="InvalidOperationException" />
 public string GetEditorJsonSchemaString(Content definition)
 {
     JSchema schema = GetSchema(
         definition,
         forList: false,
         includeRegionTags: false,
         excludeVirtualFields: true);
     
     return JsonConvert.SerializeObject(schema);
 }
Пример #2
0
        /// <exception cref="NotSupportedException" />
        /// <exception cref="InvalidOperationException" />
        private JSchema GetVirtualFieldSchema(
            BaseVirtualField baseVirtualField, Content definition, SchemaContext context)
        {
            if (baseVirtualField is VirtualMultiEntityField virtualMultiEntityField)
            {
                var boundFieldKey = Tuple.Create(definition, virtualMultiEntityField.Path);

                Field boundField = context.VirtualFields[boundFieldKey];

                var contentForArrayFields = ((EntityField)boundField).Content;

                var itemSchema = new JSchema { Type = JSchemaType.Object };

                foreach (BaseVirtualField childField in virtualMultiEntityField.Fields)
                {
                    JSchema childfieldSchema = GetVirtualFieldSchema(childField, contentForArrayFields, context);

                    itemSchema.Properties[childField.FieldName] = childfieldSchema;
                }

                return new JSchema { Type = JSchemaType.Array, Items = { itemSchema } };
            }
            else if (baseVirtualField is VirtualEntityField virtualEntityField)
            {
                BaseVirtualField[] fields = virtualEntityField.Fields;

                var virtualEntityFieldSchema = new JSchema { Type = JSchemaType.Object };

                foreach (BaseVirtualField childField in fields)
                {
                    JSchema childfieldSchema = GetVirtualFieldSchema(childField, definition, context);

                    virtualEntityFieldSchema.Properties[childField.FieldName] = childfieldSchema;
                }

                return virtualEntityFieldSchema;
            }
            else if (baseVirtualField is VirtualField virtualField)
            {
                if (virtualField.Converter != null)
                {
                    return new JSchema { Type = ConvertTypeToJsType(virtualField.Converter.OutputType) };
                }
                else
                {
                    var virtualFieldKey = Tuple.Create(definition, virtualField.Path);

                    return GetFieldSchema(context.VirtualFields[virtualFieldKey], null, context, false);
                }
            }
            else
            {
                throw new NotSupportedException($"Field type {baseVirtualField.GetType()} is not supported");
            }
        }
Пример #3
0
        public static DefinitionTreeNode[] GetObjectsFromPath(Content rootContent, string path, IFieldService fieldService, DefinitionEditorService definitionEditorService, ContentService contentService)
        {
            //запрос корня
            if (string.IsNullOrEmpty(path))
            {
                return new[] { new DefinitionTreeNode(rootContent, string.Empty, null, false, false, contentService) }
            }
            ;

            bool notFoundInDef;

            object foundObject = definitionEditorService.GetObjectFromPath(rootContent, path, out notFoundInDef);

            if (foundObject is Content)
            {
                return(GetContentFields((Content)foundObject, fieldService, path, foundObject == rootContent));
            }

            var contentsFromDef = definitionEditorService.GetContentsFromField((Field)foundObject);

            var nodesFromContentsInDef = contentsFromDef
                                         .Select(x => new DefinitionTreeNode(x, path, null, foundObject is Dictionaries, false, contentService))
                                         .ToArray();

            if (foundObject is ExtensionField)
            {
                var allExtensions = fieldService.ListRelated(fieldService.Read(((Field)foundObject).FieldId).ContentId)
                                    .Where(x => x.Aggregated)
                                    .Select(x => x.Content)
                                    .ToArray();

                return(nodesFromContentsInDef
                       .Concat(allExtensions
                               .Where(x => contentsFromDef.All(y => y.ContentId != x.Id))
                               .Select(x => new DefinitionTreeNode(x, path)))
                       .ToArray());
            }
            else if (foundObject is Dictionaries)
            {
                int[] allContentIds = definitionEditorService.GetAllContentIdsFromTree(rootContent);

                return(nodesFromContentsInDef
                       .Concat(allContentIds
                               .Where(x => contentsFromDef.All(y => y.ContentId != x))
                               .Select(x => new DefinitionTreeNode(contentService.Read(x), path))
                               .OrderBy(x => x.text))
                       .ToArray());
            }
            else
            {
                return(nodesFromContentsInDef);
            }
        }
    }
Пример #4
0
        public Article DeserializeProduct(JObject rootArticleDictionary, Content definition)
        {
            var product = rootArticleDictionary.SelectToken("product");
            if (product != null)
            {
                rootArticleDictionary = (JObject)product;
            }

            var productDeserializer = ObjectFactoryBase.Resolve<IProductDeserializer>();

            return productDeserializer.Deserialize(new JsonProductDataSource(rootArticleDictionary), definition);
        }
Пример #5
0
        private void FillDefinitionsFields(DefinitionsFields definitionsFields, Content definition, Field[] parentFields, int rootContentId)
        {
            if (definition.LoadAllPlainFields)
            {
                if (!definitionsFields.ContentIdsDependentOfAllPlainFields.ContainsKey(definition.ContentId))
                {
                    definitionsFields.ContentIdsDependentOfAllPlainFields[definition.ContentId] = new List <PathToRootContent>();
                }

                definitionsFields.ContentIdsDependentOfAllPlainFields[definition.ContentId].Add(new PathToRootContent(rootContentId, parentFields));
            }

            foreach (var field in definition.Fields)
            {
                if (field is BackwardRelationField)
                {
                    if (!definitionsFields.MonitoredBackwardRelationFields.ContainsKey(field.FieldId))
                    {
                        definitionsFields.MonitoredBackwardRelationFields[field.FieldId] = new List <PathToRootContent>();
                    }

                    definitionsFields.MonitoredBackwardRelationFields[field.FieldId].Add(new PathToRootContent(rootContentId, parentFields));
                }
                else
                {
                    if (!definitionsFields.MonitoredFieldIds.ContainsKey(field.FieldId))
                    {
                        definitionsFields.MonitoredFieldIds[field.FieldId] = new List <PathToRootContent>();
                    }

                    definitionsFields.MonitoredFieldIds[field.FieldId].Add(new PathToRootContent(rootContentId, parentFields));
                }

                var childDefinitionParentFields = new[] { field }.Concat(parentFields).ToArray();

                if (field is BackwardRelationField)
                {
                    FillDefinitionsFields(definitionsFields, ((BackwardRelationField)field).Content, childDefinitionParentFields, rootContentId);
                }
                else
                if (field is EntityField)
                {
                    FillDefinitionsFields(definitionsFields, ((EntityField)field).Content, childDefinitionParentFields, rootContentId);
                }
                else if (field is ExtensionField)
                {
                    foreach (var extFieldDefinition in ((ExtensionField)field).ContentMapping.Select(x => x.Value))
                    {
                        FillDefinitionsFields(definitionsFields, extFieldDefinition, childDefinitionParentFields, rootContentId);
                    }
                }
            }
        }
Пример #6
0
        public DefinitionTreeNode(Content content, string parentPath, string ownPath, bool isFromDictionaries, bool notInDefinition, ContentService contentService)
        {
            Id = ownPath ?? parentPath + Separator + content.ContentId;

            text = string.IsNullOrEmpty(content.ContentName) ? contentService.Read(content.ContentId).Name : content.ContentName;

            expanded = hasChildren = !isFromDictionaries && !notInDefinition;

            imageUrl = "images/icons/content.gif";

            IconName = "document";

            NotInDefinition = notInDefinition;
        }
Пример #7
0
        private JSchema CreateContentSchema(Content content)
        {
            var qpContent = _contentService.Read(content.ContentId);

            var schema = new JSchema { Type = JSchemaType.Object, AllowAdditionalProperties = false };

            schema.Description =
                !IsHtmlWhiteSpace(qpContent.Description)
                    ? qpContent.Description
                    : !IsHtmlWhiteSpace(qpContent.FriendlySingularName)
                        ? qpContent.FriendlySingularName
                        : !IsHtmlWhiteSpace(qpContent.Name)
                            ? qpContent.Name
                            : null;
            return schema;
        }
Пример #8
0
        private static DefinitionTreeNode[] GetContentFields(Content content, IFieldService fieldService, string parentPath, bool isRootContent)
        {
            var qpFields = fieldService.List(content.ContentId).Concat(fieldService.ListRelated(content.ContentId)).ToArray();

            var fieldsNotInDef = qpFields.Where(x => content.Fields.All(y => y.FieldId != x.Id) && (!x.Aggregated || x.ContentId == content.ContentId));

            var contentFields = content.Fields
                                .Select(x => new DefinitionTreeNode(x, parentPath, null, qpFields.All(y => y.Id != x.FieldId) && !(x is Dictionaries) && !(x is BaseVirtualField), false));

            if (isRootContent && !content.Fields.Any(x => x is Dictionaries))
            {
                contentFields = contentFields.Concat(new[] { new DefinitionTreeNode(new Dictionaries(), parentPath, null, false, true) });
            }

            return(contentFields
                   .Concat(fieldsNotInDef.Select(x => new DefinitionTreeNode(x, parentPath, null, x.RelationType != RelationType.None || !content.LoadAllPlainFields, x.ContentId != content.ContentId, fieldService)))
                   .ToArray());
        }
Пример #9
0
        /// <param name="qpField">Can be null</param>
        /// <exception cref="NotSupportedException" />
        /// <exception cref="InvalidOperationException" />
        private JSchema GetFieldSchema(
            Field field, Quantumart.QP8.BLL.Field qpField, SchemaContext context, bool forList)
        {
            if (qpField == null && !(field is BaseVirtualField))
            {
                qpField = _fieldService.Read(field.FieldId);
            }

            if (field is PlainField)
            {
                return GetPlainFieldSchema(qpField);
            }
            else if (field is BackwardRelationField backwardRelationalField)
            {
                JSchema backwardItemSchema = GetSchemaRecursive(backwardRelationalField.Content, context, forList);

                return AttachFieldData(qpField, new JSchema
                {
                    Type = JSchemaType.Array, Items = { backwardItemSchema }
                });
            }
            else if (field is EntityField entityField)
            {
                Content fieldContent = entityField.Content;

                if (qpField.RelationType == Quantumart.QP8.BLL.RelationType.OneToMany)
                {
                    return AttachFieldData(qpField, GetSchemaRecursive(fieldContent, context, forList));
                }
                else
                {
                    JSchema arrayItemSchema = GetSchemaRecursive(fieldContent, context, forList);

                    return AttachFieldData(qpField, new JSchema
                    {
                        Type = JSchemaType.Array, Items = { arrayItemSchema }
                    });
                }
            }
            else
            {
                throw new NotSupportedException($"Field type {field.GetType()} is not supported");
            }
        }
Пример #10
0
        private void ClearExstensions(Content content, int[] exstensionContentIds, List <int> hashCodes)
        {
            int hashCode = content.GetHashCode();

            if (hashCodes == null)
            {
                hashCodes = new List <int> {
                    hashCode
                };
            }
            else if (hashCodes.Contains(hashCode))
            {
                return;
            }
            else
            {
                hashCodes.Add(hashCode);
            }

            var exstensions = content.Fields.OfType <ExtensionField>();

            foreach (var field in exstensions)
            {
                if (field.ContentMapping.Values.Any(c => exstensionContentIds.Contains(c.ContentId)))
                {
                    int[] ids = field.ContentMapping.Where(e => !exstensionContentIds.Contains(e.Value.ContentId)).Select(e => e.Key).ToArray();

                    foreach (int id in ids)
                    {
                        field.ContentMapping.Remove(id);
                    }
                }

                foreach (var c in field.ContentMapping.Values)
                {
                    ClearExstensions(c, exstensionContentIds, hashCodes);
                }
            }

            foreach (var field in content.Fields.OfType <EntityField>())
            {
                ClearExstensions(field.Content, exstensionContentIds, hashCodes);
            }
        }
Пример #11
0
        /// <summary>
        ///     возвращает Content или Field из rootContent по пути
        /// </summary>
        /// <param name="rootContent"></param>
        /// <param name="path">разделенные Separator идшники контентов и полей</param>
        /// <param name="notFoundInDef">
        ///     был ли в rootContent найден по этому пути объект, если нет, будет возвращен с дефолтными
        ///     настройками
        /// </param>
        /// <returns></returns>
        public object GetObjectFromPath(Content rootContent, string path, out bool notFoundInDef)
        {
            var entityIds = path.Split(new[] { PathSeparator }, StringSplitOptions.RemoveEmptyEntries)
                            .Select(x =>
            {
                int fieldId;

                if (int.TryParse(x, out fieldId))
                {
                    return((object)fieldId);
                }
                else
                {
                    return(x.Substring(VirtualFieldPrefix.Length));
                }
            });

            return(GetObjectFromDef(rootContent, out notFoundInDef, entityIds));
        }
Пример #12
0
        public Field GetFieldByPath(string path, Content definition, out bool hasFilter, out Content parent)
        {
            var articleData = DPathProcessor.VerifyAndParseExpression(path).ToList();

            hasFilter = articleData.Any(ad => ad.FiltersData.Any());
            parent    = definition;

            Field currentField = null;

            foreach (var fieldName in articleData.Select(ad => ad.FieldName))
            {
                if (currentField != null)
                {
                    var currentEntityField = currentField as EntityField;
                    if (currentEntityField == null)
                    {
                        throw new Exception("Schema generator requires virtual field to relate to EntityField type or its descendant");
                    }

                    parent = currentEntityField.Content;
                }

                var nonVirtualFields = parent.Fields.Where(x => !(x is BaseVirtualField)).ToArray();
                if (nonVirtualFields.All(x => x.FieldName != fieldName) && parent.LoadAllPlainFields)
                {
                    var allPlainFields = _fieldService.List(parent.ContentId).Where(x => x.RelationType == RelationType.None);
                    currentField = new PlainField
                    {
                        FieldId   = allPlainFields.Single(x => x.Name == fieldName).Id,
                        FieldName = fieldName
                    };
                }
                else
                {
                    currentField = nonVirtualFields.Single(x => x.FieldName == fieldName);
                }
            }

            return(currentField);
        }
Пример #13
0
 public Article DeserializeProduct(string productJson, Content definition)
 {
     return DeserializeProduct(JsonConvert.DeserializeObject<JObject>(productJson), definition);
 }
Пример #14
0
        /// <exception cref="NotSupportedException" />
        /// <exception cref="InvalidOperationException" />
        private JSchema GetSchema(Content definition, bool forList, bool includeRegionTags, bool excludeVirtualFields)
        {
            _contentService.LoadStructureCache();
            _fieldService.LoadStructureCache();

            VirtualFieldContext virtualFieldContext = _virtualFieldContextService.GetVirtualFieldContext(definition);

            var context = new SchemaContext
            {
                VirtualFields = virtualFieldContext.VirtualFields,
                IgnoredFields = virtualFieldContext.IgnoredFields,
                ExcludeVirtualFields = excludeVirtualFields
            };

            JSchema rootSchema = GetSchemaRecursive(definition, context, forList);
            
            if (includeRegionTags)
            {
                JSchema schemaWithRegionTags = new JSchema { Type = JSchemaType.Object };

                schemaWithRegionTags.Properties.Add(ProductProp, rootSchema);

                JSchema regionTagsSchema = new JSchema { Type = JSchemaType.Array };

                JSchema regionTagSchema = new JSchema { Type = JSchemaType.Object };

                regionTagSchema.Properties.Add("title", new JSchema { Type = JSchemaType.String });

                JSchema valuesSchema = new JSchema { Type = JSchemaType.Array };

                JSchema valueSchema = new JSchema { Type = JSchemaType.Object };

                valueSchema.Properties.Add("value", new JSchema { Type = JSchemaType.String });

                JSchema regionIdsSchema = new JSchema { Type = JSchemaType.Array };

                regionIdsSchema.Items.Add(new JSchema { Type = JSchemaType.Integer });

                valueSchema.Properties.Add("regionsId", regionIdsSchema);

                valueSchema.Required.Add("regionsId");

                valueSchema.Required.Add("value");

                valuesSchema.Items.Add(valueSchema);

                regionTagSchema.Properties.Add("values", valuesSchema);

                regionTagSchema.Required.Add("title");

                regionTagSchema.Required.Add("values");

                regionTagsSchema.Items.Add(regionTagSchema);

                schemaWithRegionTags.Properties.Add(RegionTagsProp, regionTagsSchema);

                FillSchemaDefinitions(schemaWithRegionTags, context);

                DeduplicateJsonSchema(schemaWithRegionTags, context);

                return schemaWithRegionTags;
            }
            
            FillSchemaDefinitions(rootSchema, context);

            DeduplicateJsonSchema(rootSchema, context);

            return rootSchema;
        }
Пример #15
0
 /// <exception cref="NotSupportedException" />
 /// <exception cref="InvalidOperationException" />
 public JSchema GetSchema(Content definition, bool forList = false, bool includeRegionTags = false)
 {
     return GetSchema(definition, forList, includeRegionTags, excludeVirtualFields: false);
 }
Пример #16
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="rootContent"></param>
        /// <param name="notFoundInDef"></param>
        /// <param name="entityIds">int c id поля для невиртуальных полей и string с именем поля для виртуальных</param>
        /// <returns></returns>
        private object GetObjectFromDef(Content rootContent, out bool notFoundInDef, IEnumerable <object> entityIds)
        {
            object[] entityIdsToSearch = entityIds
                                         .Skip(1) //корень ровно один поэтому первый компонент пути нам не нужен
                                         .ToArray();

            object currentPositionObj = rootContent;

            notFoundInDef = false;

            foreach (object entityId in entityIdsToSearch)
            {
                if (currentPositionObj is Content)
                {
                    var currContent = (Content)currentPositionObj;

                    if (entityId is int)
                    {
                        currentPositionObj = currContent.Fields.SingleOrDefault(x => x.FieldId == (int)entityId);
                    }
                    else
                    {
                        currentPositionObj = currContent.Fields.SingleOrDefault(x => x.FieldName == (string)entityId && x is BaseVirtualField);
                    }

                    if (currentPositionObj == null)
                    {
                        //дурацкая система что Dictionaries это поле с id=0
                        if (entityId is int && (int)entityId == 0)
                        {
                            notFoundInDef = true;

                            return(new Dictionaries());
                        }

                        if (entityId is int)
                        {
                            int enitityIdInt = (int)entityId;

                            var qpField = _fieldService.Read(enitityIdInt);

                            if (qpField == null)
                            {
                                notFoundInDef = true;

                                return(null);
                            }

                            if (qpField.RelationType == RelationType.None && !qpField.IsClassifier)
                            {
                                currentPositionObj = new PlainField {
                                    FieldId = enitityIdInt, FieldName = qpField.Name
                                };

                                notFoundInDef = !currContent.LoadAllPlainFields;
                            }
                            else
                            {
                                using (_fieldService.CreateQpConnectionScope())
                                {
                                    if (qpField.ContentId == currContent.ContentId)
                                    {
                                        if (!qpField.IsClassifier)
                                        {
                                            currentPositionObj = new EntityField
                                            {
                                                FieldId     = enitityIdInt,
                                                FieldName   = qpField.Name,
                                                CloningMode = CloningMode.UseExisting,
                                                Content     =
                                                    new Content {
                                                    ContentId = qpField.RelateToContentId.Value, ContentName = qpField.RelatedToContent.Name
                                                }
                                            }
                                        }
                                    }
                                    ;
                                    else
                                    {
                                        currentPositionObj = new ExtensionField
                                        {
                                            FieldId     = qpField.Id,
                                            FieldName   = qpField.Name,
                                            CloningMode = CloningMode.UseExisting
                                        };

                                        var classifierContents = _fieldService.ListRelated(qpField.ContentId)
                                                                 .Where(x => x.Aggregated)
                                                                 .Select(x => x.Content);

                                        foreach (var classifierContent in classifierContents)
                                        {
                                            ((ExtensionField)currentPositionObj).ContentMapping.Add(
                                                classifierContent.Id,
                                                new Content {
                                                ContentId = classifierContent.Id, ContentName = classifierContent.Name
                                            });
                                        }
                                    }
                                    else
                                    {
                                        currentPositionObj = new BackwardRelationField
                                        {
                                            FieldId   = qpField.Id,
                                            FieldName = qpField.Name,
                                            Content   = new Content {
                                                ContentId = qpField.ContentId, ContentName = qpField.Content.Name
                                            },
                                            CloningMode = CloningMode.UseExisting
                                        };
                                    }
                                }

                                notFoundInDef = !qpField.IsClassifier || !currContent.LoadAllPlainFields;
                            }
                        }
Пример #17
0
        /// <summary>
        /// Рекурсивно обходит <see cref="Content"/>, генерирует корневую схему и заполняет
        /// <see cref="SchemaContext.SchemasByContent"/> - созданные схемы контентов
        /// и <see cref="SchemaContext.RepeatedContents"/> - повторяющиеся контенты
        /// </summary>
        /// <exception cref="NotSupportedException" />
        /// <exception cref="InvalidOperationException" />
        private JSchema GetSchemaRecursive(Content definition, SchemaContext context, bool forList)
        {
            if (context.SchemasByContent.ContainsKey(definition))
            {
                context.RepeatedContents.Add(definition);
                return context.SchemasByContent[definition];
            }
            
            var contentSchema = CreateContentSchema(definition);

            context.SchemasByContent[definition] = contentSchema;

            var qpFields = _fieldService.List(definition.ContentId).ToArray();

            contentSchema.Properties.Add(IdProp, new JSchema
            {
                Type = JSchemaType.Integer, Minimum = 0, ExclusiveMinimum = true
            });

            contentSchema.Required.Add(IdProp);

            foreach (Field field in definition.Fields
                .Where(f => !(f is Dictionaries)
                    && (!forList || f is PlainField plainField && plainField.ShowInList)))
            {
                if (field.FieldName == null)
                {
                    throw new InvalidOperationException(
                        $"FieldName is null: {new { field.FieldId, field.FieldName }}");
                }

                if (field is BaseVirtualField baseVirtualField)
                {
                    if (!context.ExcludeVirtualFields)
                    {
                        contentSchema.Properties[field.FieldName] = GetVirtualFieldSchema(
                            baseVirtualField, definition, context);
                    }
                }
                else if (!context.IgnoredFields.Contains(Tuple.Create(definition, field)))
                {
                    var qpField = field is BackwardRelationField
                        ? _fieldService.Read(field.FieldId)
                        : qpFields.SingleOrDefault(x => x.Id == field.FieldId);

                    if (qpField == null)
                    {
                        throw new InvalidOperationException($@"There is a field id={
                            field.FieldId} which specified in product definition and missing in the content id={definition.ContentId}");
                    }
                    
                    if (field is ExtensionField extensionField)
                    {
                        MergeExtensionFieldSchema(extensionField, qpField, contentSchema, context);
                    }
                    else
                    {
                        contentSchema.Properties[field.FieldName] = GetFieldSchema(field, qpField, context, forList);
                    }
                }
            }

            if (definition.LoadAllPlainFields && !forList)
            {
                var fieldsToAdd = qpFields
                    .Where(f => f.RelationType == Quantumart.QP8.BLL.RelationType.None
                        && definition.Fields.All(y => y.FieldId != f.Id))
                    .Where(f => !context.IgnoredFields
                        .Any(t => t.Item1.Equals(definition) && t.Item2.FieldId == f.Id));

                foreach (var qpField in fieldsToAdd)
                {
                    contentSchema.Properties[qpField.Name] = GetPlainFieldSchema(qpField);
                }
            }

            if (forList)
            {
                return new JSchema { Type = JSchemaType.Array, Items = { contentSchema } };
            }

            return contentSchema;
        }
Пример #18
0
        /// <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);
                }
            }
        }