A module
A module derives from CmsModule (namespace Relatude.DB.CMS.Modules), has a name, and says in
Configure what it is made of. The smallest useful module is a model type and some data to start
with:
namespace Venues.Models; // the module's model types, alone in their namespace
[Node(Id = "87942bcb-5637-401b-bb56-3dbe0ca094fc")]
public interface IVenue : IItem {
[IntegerProperty(Indexed = true)] int Seats { get; set; }
}public sealed class VenuesModule : CmsModule {
public override string Name => "venues";
public static readonly Guid Workshop = new("fe477557-bf6a-43c2-bf37-64336e3e8045");
public override void Configure(CmsModuleBuilder module) => module
.Datamodel(dm => dm.AddNamespace<IVenue>())
.Seed(1, "A first venue", c => {
if (c.Store.TryGet<IVenue>(Workshop, out _)) return; // made by an earlier run
c.Store.CreateAndInsert<IVenue>(v => { v.Name = "The workshop"; v.Seats = 24; }, Workshop);
});
}The events module's types are in Events.Models. IEventPage : IPage, IMeta is a page with
StartsUtc, EndsUtc, a Status (Scheduled, Cancelled or Ended) and a Venue.
IEventRegistration : IItem has Email, Seats and an Event. The relation to venues is declared
there too, so the venues module knows nothing about events. The module uses most of what a module
can have:
public sealed class EventsModule : CmsModule {
public override string Name => "events";
public override string Title => "Events";
public override string Description => "Events at venues, with registration and a calendar feed.";
public static readonly Guid Managers = new("6549f757-c808-4c73-bac2-ea96b826ef49");
public Guid? ParentPageId { get; init; } // an option the host sets
public override void Configure(CmsModuleBuilder module) {
module.Services.AddSingleton<EventCalendar>(); // the module's own services
module
.DependsOn<VenuesModule>()
.Datamodel(dm => dm.AddNamespace<IEventPage>())
.TransactionPlugin<EventPagePlugin>()
.Job("events.close-past", "Marks the events that have ended as ended.", ClosePast)
.Settings<EventSettingsDefinition>()
.Seed(1, "Two events", EventSeed.Events)
.Seed(2, "Events under the host's page", c => EventSeed.ListUnder(c, ParentPageId))
.UserGroup(Managers, "Event managers")
.Command("events.upcoming", EventCommands.Upcoming, readOnly: true)
.Command("events.cancel", EventCommands.Cancel)
.Endpoints(EventEndpoints.Map)
.Rendering(o => o.RenderRazorView<IEventPage>("EventPage"))
.Views()
.InterfacePlugin();
}
}| Builder method | Adds |
|---|---|
Services |
the module's own services |
DependsOn<T>() |
a module it builds on, and is active only while that one is |
RequiresLicense("feature") |
a feature the installation's license must include |
AppliesTo((datamodel, database) => …) |
which databases it is part of; by default those with the CMS content model |
Datamodel(dm => …) |
model types |
TransactionPlugin<T>(), OnStoreInit(c => …) |
transaction plugins and task runners |
Job(name, description, run) |
a job for the admin UI's scheduler |
Settings<TDefinition>() |
a custom settings set, per database or per server |
Seed(n, description, c => …) |
seed data, in numbered steps |
UserGroup(id, name) |
a user group with a fixed id, made where missing; needs ICMSUserGroup in the datamodel |
Log(definition) |
a custom log, made where missing |
OnStoreOpened(c => …) |
start-up work, after the seed data |
Command(type, handler, readOnly) |
a command for the CMS API |
Endpoints(e => …) |
routes of its own |
Rendering(o => …) |
code defaults, selectors and template kinds |
Views() |
the views, controllers and Razor Pages compiled into the module |
InterfacePlugin(entry, folder) |
its interface plugin |
Work done on a store gets a CmsModuleContext: the store as the master admin (Store), the
application's services (Services) and the module (Module).