orchardcore-module-creator
Creates new Orchard Core modules with proper structure, manifest, startup, and patterns. Use when the user needs to create a new module, add content parts, fields, drivers, handlers, or admin function
SKILL.mdsrc/CrestApps.AgentSkills/orchardcore/orchardcore-module-creator/SKILL.md
GitHubOrchard Core Module Creator
Step-by-step creation of a module for an Orchard Core site built on NuGet packages, with code templates for each kind of component. orchardcore-modules covers module structure, feature design, and installing third-party modules in more depth; a short section at the end lists what differs when contributing to the Orchard Core repository itself.
Prerequisites
- .NET SDK 10.0+
- An Orchard Core web project (startup project) referencing
OrchardCore.Application.Cms.Targets
Module Creation Workflow
Step 1: Determine Module Type
| Type | Description | Key Components |
|---|---|---|
| Content Part | Adds data/behavior to content items | Part, Driver, Views |
| Content Field | Custom field type | Field, Driver, Views |
| Settings | Site-wide configuration | Settings class, SiteDisplayDriver, options |
| Admin Feature | Admin pages/tools | Controller, Views with <breadcrumb> and <admin-list>, Menu |
| API | JSON endpoints | Minimal API endpoint class + Startup route registration |
| Background Task | Scheduled jobs | IBackgroundTask |
Step 2: Scaffold the Project
dotnet new install OrchardCore.ProjectTemplates
dotnet new ocmodulecms -n Contoso.YourModule -o src/Modules/Contoso.YourModule
dotnet sln add src/Modules/Contoso.YourModule/Contoso.YourModule.csproj
dotnet add src/Contoso.Web/Contoso.Web.csproj reference src/Modules/Contoso.YourModule/Contoso.YourModule.csproj
--AddPart true --PartName Ratingalso generates aRatingPartwith its driver, handler, settings, migration, and views (Partis appended to the name);-ovpicks the Orchard Core package version.- Use
dotnet new ocmodulemvcfor a plain MVC module without content management. - The module must be referenced by the web project; unreferenced modules are not discovered.
Step 3: Create Required Files
Every module needs:
- Manifest.cs - Module metadata and features
- Startup.cs - Service registration
- Contoso.YourModule.csproj - Project file referencing
OrchardCore.Module.Targets
Add a ConfigurationSchema.json (and the JsonSchemaSegment build item) when the module reads a configuration section; see orchardcore-configuration. See references/module-structure.md for templates.
Step 4: Add Components Based on Type
Content Part modules:
Models/YourPart.cs
ViewModels/YourPartViewModel.cs
Drivers/YourPartDisplayDriver.cs
Views/YourPart.cshtml
Views/YourPart_Edit.cshtml
Admin modules:
Controllers/AdminController.cs
Views/Admin/Index.cshtml
AdminMenu.cs
Permissions.cs (IPermissionProvider)
YourModulePermissions.cs (static permission definitions, usually in a Core/Abstractions project)
Data-storing modules:
Migrations.cs
Indexes/YourIndex.cs
See references/patterns.md for code templates and references/examples.md for complete examples.
Step 5: Register in Startup.cs
public sealed class Startup : StartupBase
{
public override void ConfigureServices(IServiceCollection services)
{
services.AddContentPart<YourPart>()
.UseDisplayDriver<YourPartDisplayDriver>();
services.AddScoped<IYourService, YourService>();
services.AddDataMigration<Migrations>();
services.AddPermissionProvider<Permissions>();
services.AddNavigationProvider<AdminMenu>();
}
}
// Extends another module only when it is enabled; no extra feature to manage.
[RequireFeatures("OrchardCore.Deployment")]
public sealed class DeploymentStartup : StartupBase
{
public override void ConfigureServices(IServiceCollection services)
{
services.AddDeployment<YourDeploymentSource, YourDeploymentStep, YourDeploymentStepDriver>();
}
}
Startup Attributes
| Attribute | Effect |
|---|---|
[Feature("Id")] |
Binds the startup or component to one of the features declared by this module's manifest. |
[RequireFeatures("A", "B")] |
Composes the type only when all listed features are enabled. |
[RequiredStartup] (4.0) |
Composes the public type for every tenant, even when the feature that defines it is disabled. Its services are attributed to Application.DefaultFeatureId. Combine with [RequireFeatures] to still require other features. |
// Always registered, even when 'Contoso.YourModule' is disabled,
// as long as OrchardCore.Contents is enabled.
[RequiredStartup]
[RequireFeatures("OrchardCore.Contents")]
public sealed class AlwaysOnStartup : StartupBase
{
public override void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IContentHandler, YourCleanupHandler>();
}
}
- The
OCSG002analyzer warns when[Feature("...")]names a feature that is not declared by the current module; such a target is silently ignored at runtime. To depend on another module's feature, use[RequireFeatures("...")]instead of[Feature("...")]. - Shape table providers gated with
[RequireFeatures]are now honored per shell, so their alternates and wrappers no longer leak into tenants that lack the required feature.
JSON API Endpoints
When a module exposes an endpoint that primarily returns JSON, prefer Minimal APIs over MVC ApiController classes.
public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider)
{
routes.AddGetWeatherEndpoint();
}
internal static class GetWeatherEndpoint
{
public static IEndpointRouteBuilder AddGetWeatherEndpoint(this IEndpointRouteBuilder builder)
{
builder.MapGet("api/weather", HandleAsync)
.WithName("YourModuleGetWeather")
.AllowAnonymous()
.DisableAntiforgery();
return builder;
}
[Authorize(AuthenticationSchemes = OrchardCoreConstants.AuthenticationSchemes.Api)]
private static async Task<IResult> HandleAsync(
IAuthorizationService authorizationService,
HttpContext httpContext)
{
if (!await authorizationService.AuthorizeAsync(httpContext.User, YourModulePermissions.ViewWeather))
{
return httpContext.ChallengeOrForbid(OrchardCoreConstants.AuthenticationSchemes.Api);
}
return TypedResults.Ok(new { ok = true });
}
}
- 401/403 responses from the shared
Apiauthentication scheme, and responses of endpoints marked[SkipStatusCodePages], now carry an RFC 9457 Problem Details body (application/problem+json). ReturnTypedResults.Problem(...)/ValidationProblem(...)for your own errors so clients get one format. - Give each endpoint a unique, stable name with
.WithName(...)(one per verb, it becomes the OpenAPIoperationId) so theOrchardCore.OpenApimodule documents it cleanly; seeorchardcore-openapi. - Use MVC controllers for HTML/admin pages; use Minimal APIs for JSON endpoints.
Orchard Core 4.0 Authoring Rules
- Display drivers: build shapes with
Initialize<TModel>()(prefer the typed-state overloads withstaticlambdas) orFactory();Shape()is obsolete. Initializer delegates areValueTask-based. Overrides of the 3-parameterFactory()must move toFactory<TBuilderState, TInitState>(). - Permissions: declare them in a static
*Permissionsclass withLocalizationSource.Create("Description", typeof(DeclaringType))descriptions, and expose them through anIPermissionProviderregistered withAddPermissionProvider<T>(). Use the current permission classes (MediaPermissions,SettingsPermissions,UsersPermissions,DeploymentPermissions,GraphQLPermissions, ...); the old*.Permissions.*members andCommonPermissionswere removed, andStandardPermissions.SiteOwneris gone (use the Administrator role). - Deployment steps: assign
CategoryandTitleas staticLocalizationSourceinstances in the parameterless constructor. - Admin views: declare the page title with
<breadcrumb name="..." title="@T["..."]">inside theTitlezone instead of<h1>; render lists with<admin-list>so List/Grid layouts work (seeorchardcore-breadcrumbsandorchardcore-admin-lists). - Admin menu: derive from
AdminNavigationProvider; there is no legacy admin menu layout any more. - Front-end scripts in admin: admin pages no longer load jQuery or jQuery UI. Use native DOM APIs and Bootstrap's JS API. Field and part editor scripts that can be injected via Flow, Bag, or Widgets "Add" must use
<script at="Foot" type="module">and must not wait forDOMContentLoaded. Admin Vue apps use Vue 3. - Temporary files: inject
ITempDirectoryProviderinstead ofPath.GetTempPath(). - User uploads: run every uploaded file through the file event pipeline. Storing it with
IMediaFileStore.CreateFileFromStreamAsync(path, stream)does not do that (it only runsIMediaCreatingEventHandlers); use theMediaFileStoreExtensions.CreateFileFromStreamAsync(fileCreationService, path, stream, ...)overload, or callFileCreationService.CreateAsync()yourself for other stores and abort whenSucceededisfalse. Seeorchardcore-file-upload-security. - Settings-backed options: read
IOptionsMonitor<T>.CurrentValue; seeorchardcore-site-settings. - Configuration sections: name them
OrchardCore:YourService[:Provider]without anOrchardCore_prefix. - Static files: module
wwwrootfiles are served from/{ModuleId}/...; the application's ownwwwrootis resolved through the web root, so do not build paths fromApplication.Root.
Step 6: Build and Test
dotnet build
dotnet run --project src/Contoso.Web
# Enable the feature in Admin → Tools → Features, or with a recipe "feature" step
Use the orchardcore-tester skill to verify the feature in a browser.
Quick Reference
Naming Conventions
| Item | Convention | Example |
|---|---|---|
| Module project and ID | Company.ModuleName |
Contoso.Rating |
| Namespace | Company.ModuleName |
Contoso.Rating |
| Feature ID | Module ID, or Company.ModuleName.SubFeature |
Contoso.Rating, Contoso.Rating.Api |
| Content Part | NamePart |
RatingPart |
| Driver | NamePartDisplayDriver |
RatingPartDisplayDriver |
| Permissions | NamePermissions (static) |
RatingPermissions |
| View | PartName.cshtml |
RatingPart.cshtml |
| Edit View | PartName_Edit.cshtml |
RatingPart_Edit.cshtml |
| Static files | ~/{ModuleId}/... |
~/Contoso.Rating/js/rating.js |
Common Package References
<!-- Marks the project as a module (required) -->
<PackageReference Include="OrchardCore.Module.Targets" Version="3.*" />
<!-- Content parts, fields, drivers -->
<PackageReference Include="OrchardCore.ContentManagement" Version="3.*" />
<PackageReference Include="OrchardCore.ContentTypes.Abstractions" Version="3.*" />
<PackageReference Include="OrchardCore.DisplayManagement" Version="3.*" />
<!-- Admin UI -->
<PackageReference Include="OrchardCore.Admin.Abstractions" Version="3.*" />
Feature Categories
Use in Manifest.cs: Content Management, Content, Navigation, Security, Infrastructure, Theming, Developer.
Inside the Orchard Core repository
Only when contributing a module to Orchard Core itself: create it under src/OrchardCore.Modules/OrchardCore.ModuleName, use ProjectReferences to src/OrchardCore/* projects instead of packages, fill the manifest with ManifestConstants.OrchardCoreTeam, OrchardCoreWebsite, and OrchardCoreVersion, run the app from src/OrchardCore.Cms.Web, and follow the repository's AGENTS.md for build and test commands.
References
references/module-structure.md- Directory layout and file templatesreferences/patterns.md- Code patterns (parts, drivers, handlers, permissions, uploads, temp files)references/examples.md- Complete module examples