private ContentSchemaJsonRef GetSchemaRef(ContentSchema contentSchema, SchemaContext context) { string name = context.DefinitionNamesBySchema[contentSchema]; return(new ContentSchemaJsonRef { ContentId = contentSchema.ContentId, Ref = $"#/{nameof(ProductSchema.Definitions)}/{name}" }); }
/// <summary> /// Заполняем словарь пустыми схемами /// </summary> private void CreateEmptyContentSchemas( ContentSchema contentSchema, Dictionary <int, ContentSchema> shapesByContentId) { if (!shapesByContentId.ContainsKey(contentSchema.ContentId)) { var copy = contentSchema.ShallowCopy(); copy.Fields = new Dictionary <string, FieldSchema>(); shapesByContentId[contentSchema.ContentId] = copy; } VisitChildSchemas(contentSchema, shapesByContentId, CreateEmptyContentSchemas); }
/// <summary> /// Построить схему корневого DPC-контента и набор ссылок на повторяющиеся контенты /// </summary> private ProductSchema GetProductSchema(ContentSchema contentSchema, SchemaContext context) { context.DefinitionNamesBySchema = context.RepeatedSchemas .GroupBy(schema => String.IsNullOrEmpty(schema.ContentName) ? $"Content{schema.ContentId}" : schema.ContentName) .SelectMany(group => group.Select((schema, i) => new { Key = schema, Value = $"{group.Key}{(i == 0 ? "" : i.ToString())}", })) .ToDictionary(pair => pair.Key, pair => pair.Value); return(new ProductSchema { Content = DeduplicateContentSchema(contentSchema, context, new HashSet <ContentSchema>()), Definitions = context.DefinitionNamesBySchema .ToDictionary(pair => pair.Value, pair => pair.Key), }); }
private void VisitChildSchemas( ContentSchema contentSchema, Dictionary <int, ContentSchema> schemasByContentId, Action <ContentSchema, Dictionary <int, ContentSchema> > action) { foreach (FieldSchema fieldSchema in contentSchema.Fields.Values) { if (fieldSchema is RelationFieldSchema relationFieldSchema) { if (relationFieldSchema.RelatedContent is ContentSchema childContentSchema) { action.Invoke(childContentSchema, schemasByContentId); } } else if (fieldSchema is ExtensionFieldSchema extensionFieldSchema) { foreach (var childContentSchema in extensionFieldSchema.ExtensionContents.Values.OfType <ContentSchema>()) { action.Invoke(childContentSchema, schemasByContentId); } } } }
/// <summary> /// Генерация схемы для редактирования контентов /// </summary> /// <exception cref="NotSupportedException" /> /// <exception cref="InvalidOperationException" /> public ProductSchema GetProductSchema(Content content) { if (content == null) { throw new ArgumentNullException(nameof(content)); } _contentService.LoadStructureCache(); _fieldService.LoadStructureCache(); VirtualFieldContext virtualFieldContext = _virtualFieldContextService.GetVirtualFieldContext(content); var context = new SchemaContext { Dictionaries = content.Fields.OfType <Dictionaries>().SingleOrDefault(), IgnoredFields = virtualFieldContext.IgnoredFields, }; ContentSchema contentSchema = GetContentSchema(content, context, ""); ProductSchema productSchema = GetProductSchema(contentSchema, context); return(productSchema); }
/// <summary> /// Объединяем поля схем контентов /// </summary> private void FillMergedContentSchemas( ContentSchema contentSchema, Dictionary <int, ContentSchema> schemasByContentId) { ContentSchema mergedContentSchema = schemasByContentId[contentSchema.ContentId]; if (!contentSchema.IsReadOnly) { // Один и тот же контент может использоваться для чтения и для изменения // в разных частях схемы. В этом случае считаем, что контент не является ReadOnly. mergedContentSchema.IsReadOnly = false; } if (!contentSchema.ForExtension) { // Один и тот же контент может использоваться как Extension и как Relation // в разных частях схемы. В этом случае считаем, что контент не является Extension. mergedContentSchema.ForExtension = false; } foreach (FieldSchema fieldSchema in contentSchema.Fields.Values) { // Перезаписываем все сложные поля, потому что для одного и того же контента, // в одном месте ProductDefinition заданное поле может быть RelationField | ExtensionField, // а в другом месте у контента стоит флаг LoadAllPlainFields, // и поле является PlainField c типом FieldExactTyes.O2MRelation | FieldExactTyes.Classifier if (fieldSchema is ExtensionFieldSchema extFieldSchema) { var copy = extFieldSchema.ShallowCopy(); copy.ExtensionContents = extFieldSchema.ExtensionContents.ToDictionary( pair => pair.Key, pair => (IContentSchema) new ContentSchemaIdRef { ContentId = pair.Value.ContentId, }); // Объединяем наборы допустимых контентов if (mergedContentSchema.Fields .TryGetValue(fieldSchema.FieldName, out FieldSchema mergedFieldSchema) && mergedFieldSchema is ExtensionFieldSchema mergedExtensionSchema) { foreach (var pair in mergedExtensionSchema.ExtensionContents) { if (!copy.ExtensionContents.ContainsKey(pair.Key)) { copy.ExtensionContents[pair.Key] = pair.Value; } } } mergedContentSchema.Fields[fieldSchema.FieldName] = copy; } else if (fieldSchema is SingleRelationFieldSchema singleFieldSchema) { var copy = singleFieldSchema.ShallowCopy(); copy.RelatedContent = new ContentSchemaIdRef { ContentId = singleFieldSchema.RelatedContent.ContentId, }; copy.ClearContextDependentProps(); mergedContentSchema.Fields[fieldSchema.FieldName] = copy; } else if (fieldSchema is MultiRelationFieldSchema multiFieldSchema) { var copy = multiFieldSchema.ShallowCopy(); copy.RelatedContent = new ContentSchemaIdRef { ContentId = multiFieldSchema.RelatedContent.ContentId, }; copy.ClearContextDependentProps(); mergedContentSchema.Fields[fieldSchema.FieldName] = copy; } else if (!mergedContentSchema.Fields.ContainsKey(fieldSchema.FieldName)) { mergedContentSchema.Fields[fieldSchema.FieldName] = fieldSchema; } VisitChildSchemas(contentSchema, schemasByContentId, FillMergedContentSchemas); } }
/// <exception cref="NotSupportedException" /> /// <exception cref="InvalidOperationException" /> private RelationFieldSchema GetRelationFieldSchema( EntityField entityField, Quantumart.QP8.BLL.Field qpField, SchemaContext context, string path) { ContentSchema contentSchema = GetContentSchema(entityField.Content, context, path); string relationCondition = _editorPreloadingService.GetRelationCondition(entityField, qpField); ArticleObject[] preloadedArticles = new ArticleObject[0]; if (entityField.PreloadingMode == PreloadingMode.Eager) { preloadedArticles = _editorPreloadingService.PreloadRelationArticles( entityField, relationCondition, context.VisitedPreloadedArticles, context.Dictionaries); } string[] displayFieldNames = contentSchema.Fields.Values .OfType <PlainFieldSchema>() .Where(f => f.FieldType != FieldExactTypes.Textbox && f.FieldType != FieldExactTypes.VisualEdit) .OrderByDescending(f => f.ViewInList) .ThenBy(f => f.FieldOrder) .Take(Math.Max(qpField.ListFieldTitleCount, 1)) .Select(f => f.FieldName) .ToArray(); if (qpField.ExactType == FieldExactTypes.O2MRelation && !(entityField is BackwardRelationField) || qpField.ExactType == FieldExactTypes.M2ORelation && entityField is BackwardRelationField) { return(new SingleRelationFieldSchema { RelatedContent = contentSchema, UpdatingMode = entityField.UpdatingMode, IsDpcBackwardField = entityField is BackwardRelationField, RelationCondition = relationCondition, DisplayFieldNames = displayFieldNames, PreloadingMode = entityField.PreloadingMode, PreloadedArticles = preloadedArticles, }); } if (qpField.ExactType == FieldExactTypes.M2MRelation || qpField.ExactType == FieldExactTypes.O2MRelation && entityField is BackwardRelationField || qpField.ExactType == FieldExactTypes.M2ORelation && !(entityField is BackwardRelationField)) { int? orderFieldId = qpField.TreeOrderFieldId ?? qpField.ListOrderFieldId ?? qpField.OrderFieldId; bool orderByTitle = qpField.TreeOrderByTitle || qpField.ListOrderByTitle || qpField.OrderByTitle; string orderByFieldName = contentSchema.Fields.Values .OfType <PlainFieldSchema>() .Where(f => f.FieldId == orderFieldId) .Select(f => f.FieldName) .FirstOrDefault(); if (orderByFieldName == null && orderByTitle) { orderByFieldName = displayFieldNames.FirstOrDefault(); } int?maxDataListItemCount = null; if (qpField.ExactType == FieldExactTypes.M2ORelation && qpField.BackRelationId != null) { // MaxDataListItemCount лежит в соотв. поле O2MRelation var reverseField = _fieldService.Read(qpField.BackRelationId.Value); if (reverseField.MaxDataListItemCount > 0) { maxDataListItemCount = reverseField.MaxDataListItemCount; } } else if (qpField.MaxDataListItemCount > 0) { maxDataListItemCount = qpField.MaxDataListItemCount; } return(new MultiRelationFieldSchema { RelatedContent = contentSchema, UpdatingMode = entityField.UpdatingMode, IsDpcBackwardField = entityField is BackwardRelationField, RelationCondition = relationCondition, DisplayFieldNames = displayFieldNames, OrderByFieldName = orderByFieldName, MaxDataListItemCount = maxDataListItemCount, PreloadingMode = entityField.PreloadingMode, PreloadedArticles = preloadedArticles, }); } throw new NotSupportedException($"Relation type {qpField.ExactType} is not supported"); }
/// <summary> /// Рекурсивно обходит <see cref="Content"/>, генерирует корневую схему и заполняет /// <see cref="SchemaContext.SchemasByContent"/> - созданные схемы контентов /// и <see cref="SchemaContext.RepeatedContents"/> - повторяющиеся контенты /// </summary> /// <exception cref="NotSupportedException" /> /// <exception cref="InvalidOperationException" /> private ContentSchema GetContentSchema(Content content, SchemaContext context, string path) { if (context.SchemasByContent.ContainsKey(content)) { ContentSchema repeatedSchema = context.SchemasByContent[content]; context.RepeatedSchemas.Add(repeatedSchema); return(repeatedSchema); } ContentSchema contentSchema = CreateContentSchema(content, path); context.SchemasByContent[content] = contentSchema; var qpFields = _fieldService.List(content.ContentId).ToArray(); var fieldsToAdd = content.Fields .Where(field => !(field is Dictionaries || field is BaseVirtualField) && !context.IgnoredFields.Contains(Tuple.Create(content, field))); foreach (Field field in fieldsToAdd) { if (field.FieldName == null) { throw new InvalidOperationException( $"FieldName is null: {new { field.FieldId, field.FieldName }}"); } var qpField = field is BackwardRelationField ? _fieldService.Read(field.FieldId) : qpFields.SingleOrDefault(f => f.Id == field.FieldId); if (qpField == null) { throw new InvalidOperationException( $@"There is a field id={field.FieldId} specified in the definition and missing in the content id={content.ContentId}" ); } if (qpField.ExactType != FieldExactTypes.DynamicImage) { contentSchema.Fields[field.FieldName] = GetFieldSchema(field, qpField, context, path); } } if (content.LoadAllPlainFields) { var qpFieldsToAdd = qpFields .Where(qpField => qpField.RelationType == Quantumart.QP8.BLL.RelationType.None && qpField.ExactType != FieldExactTypes.DynamicImage && content.Fields.All(field => field.FieldId != qpField.Id) && !context.IgnoredFields .Any(tuple => tuple.Item1.Equals(content) && tuple.Item2.FieldId == qpField.Id)); foreach (var qpField in qpFieldsToAdd) { contentSchema.Fields[qpField.Name] = GetFieldSchema(null, qpField, context, path); } } contentSchema.DisplayFieldName = contentSchema.Fields.Values .OfType <PlainFieldSchema>() .Where(f => f.FieldType == FieldExactTypes.String) .OrderByDescending(f => f.ViewInList) .ThenBy(f => f.FieldOrder) .Select(f => f.FieldName) .FirstOrDefault(); return(contentSchema); }