The OnTopic.Editor.AspNetCore project provides a web-based interface for the OnTopic Library. The editor is distributed as a Razor Class Library via NuGet so it can easily be added to a website that implements the OnTopic.AspNetCore.Mvc library.
When a topic is edited in the OnTopic Editor, every attribute is exposed as a particular attribute type—such as a text box, dropdown menu, etc. Those attribute types are implemented as plugins, which can be developed by third-party developers as a means of customizing or extending the editor. The OnTopic.Editor.AspNetCore.Attributes provides a standard set of attribute types for the editor. The following summarizes the structure of plugins.
The {Type}ViewComponent) (e.g., BooleanViewComponent) is responsible for evaluating contextual information against services to return view models for each attribute type.
The {Type}AttributeDescriptor (e.g., BooleanAttributeDescriptor) derives from AttributeDescriptor and is primarily responsible for customizing the ModelType for cases where the attribute should not be stored in Topic.Attributes.
public class FooAttributeDescriptor: AttributeDescriptor {
public FooAttributeDescriptor(string key, string contentType, Topic parent, int id = -1): base(key, contentType, parent, id) {
ModelType = ModelType.Reference
}
}- The signature of the constructor should match
Topic, which this derives from. That allows theTopicFactoryto create new instances of it. - Generally, the only customization required is to modify the
ModelType, if it shouldn't be set to the default ofModelType.ScalarValue.
The {Type}AttributeDescriptorViewModel (e.g., BooleanAttributeDescriptorViewModel) exposes the configuration of the AttributeDescriptor to the view. It will typically have a property for each attribute associated with the AttributeDescriptor in the Editor. For exxample,
public record FooAttributeDescriptorViewModel: AttributeDescriptorViewModel {
public string Height { get; init; }
}In this example, the FooAttributeDescriptorViewModel has a single configuration value, which specifies the Height of the view component.
In addition to exposing configurable properties for the attribute, the attribute descriptor view model is also responsble for registering client-side resources that should be injected into the header or footer of the layout. This can be done in the constructor using the following calls:
public record FooAttributeDescriptorViewModel: AttributeDescriptorViewModel {
public FooAttributeDescriptorViewModel() {
StyleSheets.Register(GetNamespacedUri("%/Shared/Styles/Foo.css"));
Scripts.Register(GetNamespacedUri("%/Shared/Scripts/Foo.js"));
}
}Note that the GetNameSpaceUri() helper will automatically replace the %/ with the Razor Class Library namespace for the plugin's assembly—e.g., /_Content/FooAttribute/.
Typically, view components will return an instance of the AttributeViewModel<T> view model, where T is the AttributeDescriptorViewModel. On occassion, however, there is additional non-configuration data that needs to be returned to the view. For example, an attribute type which displays a dropdown list may include a Collection<SelectListItem> to include the data for that dropdown list:
public record FooViewModel {
public Collection<SelectListItem> Values { get; } = new();
}The {Type}AttributeBindingModel (e.g., BooleanAttributeBindingModel) is assembled by the AttributeBindingModelBinder to bind attribute data posted to the EditorController to an EditorBindingModel. Typically, there's no need to customize this unless there's a need to translate how the value is read. If there's a need to translate the value submitted from the editor prior to it being set on the Topic, implementers can override the GetValue() method:
public record FooBindingModel {
public string GetValue() => Value.ToLower();
}Per ASP.NET Core conventions, views must be stored in /Views/Editor/Components/{Type}/Default.cshtml. To ensure they are consistently rendered in the OnTopic Editor, and property bound to the EditorBindingModel, they should have their layout set to:
@{
Layout = "~/Areas/Editor/Views/Editor/Components/_Layout.cshtml";
}This will automatically add the header, tooltip, as well as hidden metada needed to correctly bind to the appropriate AttributeBindingModel.
Typically, consumers of the OnTopic Editor shouldn't need to know much about the architecture of the code. The following overview of types used by the OnTopic Editor may, however, be of use to those looking to extend the functionality of the editor.
EditorController: The primary controller fo the OnTopic Editor, with actions for/Edit,/Import,/Export,/Move,/Delete,/Rollback, and/Json.
ContentTypeListViewComponent: A view component for displaying a list of content types that can be created in the current context. Used for opening the new topic form.ContentTypeListAttributeDescriptorViewModel: The attribute descriptor for theContentTypeListview component; includes anEnableModalproperty for optinally opening the new topic form in a popup window.ContentTypeListViewModel: The view model for theContentTypeview component includes, critically, aTopicListofSelectListItems for binding to the dropdown list.
EditorViewModelLookupService: Looks up view models associated with plugins needed to render the OnTopic Editor views.TopicQueryService: Queries the topic graph based on a set ofTopicQueryOptionsand results a collection ofQueryResultTopicViewModels.TopicQueryOptions: Arguments and options for theEditorViewModelLookupService.QueryResultTopicViewModel: Model for returning results from theTopicQueryService.
AttributeBindingModelBinder: Uses metadata fromComponents/_Layout.cshtmlto identify an appropriateAttributeBindingModelfor each attribute, and bind the form data to it.AttributeBindingModelBinderProvider: Infrastructure component for constructing a newAttributeBindingModelBinder.AttributeBindingModelLookupService: Looks up the appropriateAttributeBindingModelfor a given attribute name.
EditorViewModel: The core view model used to represent each page in the OnTopic Editor.EditingTopicViewModel: The current topic being edited; exposed via theTopicproperty on theEditorViewModel.
ContenTypeDescriptorViewModel: Provides metadata associated with a givenContentTypeDescriptor; exposed via theContentTypeproperty on theEditorViewModel.AttributeDescriptorViewModel: Provides metadata associated with a specificAttributeTypeDescriptor; exposed via theAttributeDescriptorsproperty of theContentTypeDescriptorViewModel.
AttributeViewModel: Contains the serialized metadata for an attribute, with a link to the associatedAttributeDescriptorViewModelandEditingTopicViewModel; constructed byEdit.cshtmlfor each attribute type view component.ClientResourceCollection: Provides a base class for registering client-side resources associated with a given attribute.ClientResource<T>: Provides a base class for representing an individual client-side resources.ScriptCollection: A concrete implementation of theClientResourceColletionfor registering client-side scripts.ScriptResource: A concrete implementation of theClientResourcefor representing a client-side script.
StyleSheetCollection: A concrete implementation of theClientResourceColletionfor registering client-side style sheets.StyleSheetResource: A concrete implementation of theClientResourcefor representing a client-side script.
EditorBindingModel: The core binding model used to respond toPOSTrequests in the OnTopic Editor.EditorAttributeCollection: A collection ofAttributeBindingModelinstance, exposed by theAttributesproperty of theEditorBindingModel.AttributeBindingModel: Binds the metadata and submitted value for an individual attribute control, as submitted via aPOSTrequest.