FAQ Data Models
The FAQ data model consists of two core concepts: FAQ Item and FAQ List. Together, they define how FAQ content is structured, generated, and managed.
FAQ Item
A FAQ Item represents a single question - answer pair generated from source text or created manually. It is the smallest unit of FAQ content and is typically rendered as one expandable or inline entry in the UI.
A FAQ Item can be implemented either as:
- An Optimizely block, or
- A plain model
Example: FAQ Item as a block
using Epinova.CMS.AiAssistant.Faqs;
using System.ComponentModel.DataAnnotations;
[ContentType(GUID = "411E6051-319F-46B6-A8E6-5DAF38419819")]
public class FaqBlock : BlockData, IFaq
{
[CultureSpecific]
public virtual string Question { get; set; }
[CultureSpecific]
public virtual string Answer { get; set; }
[ScaffoldColumn(false)]
[CultureSpecific] // Important for use with the LanguageManager add-on
public virtual Guid Id { get; set; } = Guid.NewGuid();
public override void SetDefaultValues(ContentType contentType)
{
base.SetDefaultValues(contentType);
// Only set a new GUID if the current one is empty
if (Id == Guid.Empty)
{
Id = Guid.NewGuid();
}
}
}
IFaq Marker Interface
IFaq is a marker interface used to identify a model as FAQ Item.
IFaq interface
public interface IFaq
{
public Guid Id { get; set; }
}
Built-in FAQ Item Models
The following plain models are provided out of the box:
- Epinova.CMS.AiAssistant.Faqs.Faq
- Epinova.CMS.AiAssistant.Faqs.XhtmlStringFaq
Faq plain model
public class Faq : IFaq
{
[Editable(false)]
public virtual Guid Id { get; set; } = Guid.NewGuid();
public virtual string Question { get; set; } = string.Empty;
public virtual string Answer { get; set; } = string.Empty;
}
XhtmlStringFaq plain model
public class XhtmlStringFaq : IFaq
{
[Editable(false)]
public virtual Guid Id { get; set; } = Guid.NewGuid();
public virtual string? Question { get; set; }
public virtual XhtmlString? Answer { get; set; }
}
FAQ List
A FAQ List is a collection of FAQ Items managed as a single unit. There're multiple ways to define FAQ list, depends on how FAQ items are represented.
Example: Using the FAQ item in different scenarios
public virtual IList<FaqBlock> FaqBlockList { get; set; }
public virtual IList<Faq> FaqList { get; set; }
public virtual IList<XhtmlStringFaq> XhtmlStringFaqList { get; set; }
Note: You can mark these properties with [CultureSpecific] attribute to enable multilingual support.
Supported Usage Scenarios
1. FAQ Block (Shared Block) Scenario
Use this scenario when FAQs are added via a shared FAQ block placed in a content area.
Source text
The main text content located at the same level as the content area in which the FAQ block is placed.
Behavior
FAQs are generated only from text fields available on the content.
Limitation
Because a shared block can be reused across multiple pages or content items, the source text may differ between usages, therefore the generated FAQs may be based on content that does not match the current page context.
Example: FAQ List block
[ContentType(GUID = "DBC1C6F9-B2A8-4F01-85E7-6E69DE03FA6F")]
public class FaqListBlock : BlockData
{
[AiFaqsGenerator]
public virtual IList<FaqBlock> FaqList { get; set; }
}
The AiFaqsGenerator attribute is applied to the property to specify the content source used for generating FAQs.
By default, the attribute does not require a source property to be specified. If no source property name is provided, the FAQ generator uses the page's content as the source text for generating FAQs.
You may optionally specify a source property name to control which content field is used for generation. The source property must exist on the same content type as the property decorated with AiFaqsGenerator.
Note: A shared block must be referenced by at least one page before it can be included in the scan.
2. FAQ Page Scenario
Use this scenario when a page contains a main text content and a list of FAQs rendered on the same page.
Source text
The main text content of the page.
FAQs content
The page property that stores the FAQ item list.
Behavior
FAQs are generated from the page's content and displayed alongside the main content.
There're multiple ways to configure FAQ List on a page, depending on requirements.
- A list of FAQ item blocks
- A property list of plain FAQ models.
Example: FAQ page with a list of FAQ blocks
[ContentType(GUID = "77D2E45C-ED9B-40E7-B672-E693616D3472")]
public class FaqPage : PageData
{
public virtual XhtmlString MainBody { get; set; }
public virtual ContentArea MainContentArea { get; set; }
[AiFaqsGenerator]
public virtual IList<FaqBlock> FaqBlockList { get; set; }
}
Example: FAQ page with a list of XhtmlStringFaq model
[ContentType(GUID = "77D2E45C-ED9B-40E7-B672-E693616D3472")]
public class FaqPage : PageData
{
public virtual XhtmlString MainBody { get; set; }
public virtual ContentArea MainContentArea { get; set; }
[AiFaqsGenerator]
[EditorDescriptor(EditorDescriptorType = typeof(XhtmlStringFaqListEditorDescriptor))]
public virtual IList<XhtmlStringFaq> XhtmlStringFaqList { get; set; }
}
FAQ Model Converter
AI Assistant uses its own internal model to store and manage FAQ content. Because FAQ Item model can vary between applications, a model converter is requried to map site-specific FAQ Item model to the AI Assistant model.
Built-in model converters are provided for:
- Faq
- XhtmlStringFaq
If FaqBlock is used as a FAQ Item, a custom model converter must be implemented.
Example: FAQ block model converter
public class FaqBlockModelConverter : FaqModelConverterBase<FaqBlock>
{
public override bool IsXhtmlStringQuestion => false;
public override bool IsXhtmlStringAnswer => false;
public override FaqBlock ConvertFromModel(IContentRepository contentRepository, ContentReference? parentContentLink, FaqModel model)
{
var block = contentRepository.GetDefault<FaqBlock>(parentContentLink);
block.Id = model.Id;
block.Question = model.Question;
block.Answer = model.Answer;
return block;
}
public override FaqModel ConvertToModel(FaqBlock faq) =>
new()
{
Id = faq.Id == Guid.Empty ? Guid.NewGuid() : faq.Id,
Question = faq.Question ?? string.Empty,
Answer = faq.Answer ?? string.Empty
};
}
Registering Model Converters
using Epinova.CMS.AiAssistant.Faqs;
public void ConfigureServices(IServiceCollection services)
{
....
services
.AddAiAssistant()
.AddFaqModelConverter<FaqModelConverter>() // When Faq is used
.AddFaqModelConverter<XhtmlStringFaqModelConverter>() // When XhtmlStringFaq is used
.AddFaqModelConverter<FaqBlockModelConverter>(); // When FaqBlock is used
}
FAQ Configuration Summary
To configure FAQ generation in your site, follow these steps:
1. Define FAQ Item Models
Choose how a FAQ Item is represented in your solution:
- Use a built-in plain model Faq or XhtmlStringFaq
- Create a custom model or Optimizely block that implements IFaq
Ensure each FAQ Item has a unique Id.
2. Define a FAQ List
Create a property that represents a FAQ List by using one of the following:
- A list of FAQ Item blocks
- A list of plain FAQ models
- A local FAQ List block
This property will act as the container for generated FAQs.
3. Bind Source Text Using AiFaqsGenerator
Decorate the FAQ List property with the AiFaqsGenerator attribute to specify the source text used for FAQ generation.
4. Choose the Usage Scenario
Select the appropriate usage pattern based on content requirements:
- FAQ Page - for page-specific, tightly coupled FAQs
- FAQ Block (Shared block) - for reusable or generic FAQs
Be aware of the limitations of shared blocks when content context differs.
5. Implement a FAQ Model Converter (If Required)
If a custom FAQ Item type is used (for example, FaqBlock), implement a FAQ model converter to map between site models and the AI Assistant internal model.
Built-in converters are available for Faq and XhtmlStringFaq.
6. Register FAQ Model Converters
Register all required model converters during application startup.