Recipes (OrchardCore.Recipes
)¶
Recipe file¶
A recipe is a json file used to execute different import and configuration steps.
You can add it in a Recipes
folder with a name like this {RecipeName}.recipe.json
and it will be available in the Configuration > Recipes admin page.
Recipe format¶
A recipe file should look like this:
{
"name": "Name",
"displayName": "Display Name of the recipe",
"description": "Description of the recipe",
"author": "Author",
"website": "https://website.net",
"version": "2.0",
"issetuprecipe": true|false,
"categories": [ "default" ],
"tags": [ "tag" ],
"variables": {
"variable1": "[js:uuid()]",
...
},
"steps": [
...
]
}
Note
if issetuprecipe
is equal to true, the recipe will be available in the Recipes list during the setup.
Note
Recipes, despite being JSON files, may contain comments: ```json // This is a comment.
```
Recipe steps¶
A recipe can execute multiple steps.
To create a new recipe step, implement the IRecipeStepHandler
interface and its ExecuteAsync
method:
public async Task ExecuteAsync(RecipeExecutionContext context)
Alternatively, you can extend NamedRecipeStepHandler
and implement the required abstract method, providing additional functionality for named steps.
Here are the available recipe steps:
Feature Step¶
The Feature step allows you to disable/enable some features.
{
"name": "feature",
"disable": [],
"enable": [
"OrchardCore.Admin",
"YourTheme",
"TheAdmin"
]
}
Warning
If you want to use your own theme (Ex: YourTheme
), do not forget to enable its feature or else, the theme layout will not be working after the execution of the recipe.
Themes Step¶
The Themes step allows you to set the admin and the site themes.
{
"name": "themes",
"admin": "TheAdmin",
"site": "YourTheme"
}
Settings Step¶
The Settings step allows you to set multiple settings.
{
"name": "settings",
"HomeRoute": {
"Action": "Display",
"Controller": "Item",
"Area": "OrchardCore.Contents",
"ContentItemId": "[js: variables('blogContentItemId')]"
},
"LayerSettings": {
"Zones": [ "Content", "Footer" ]
}
}
ContentDefinition Step¶
The Content step allows you to import some content types.
{
"name": "ContentDefinition",
"ContentTypes": [
{
"Name": "YourContentType",
...
}
}
Lucene Step¶
The Lucene index step allows you to run the Lucene indexation of content types.
You can also set the default Lucene Settings.
{
// Create the indices before the content items so they are indexed automatically.
"name": "lucene-index",
"Indices": [
{
"Search": {
"AnalyzerName": "standardanalyzer",
"IndexLatest": false,
"IndexedContentTypes": [
"Blog",
"BlogPost"
]
}
}
]
},
{
// Create the search settings.
"name": "Settings",
"LuceneSettings": {
"SearchIndex": "Search",
"DefaultSearchFields": [
"Content.ContentItem.FullText"
]
}
}
Reset Lucene Search Index Step¶
This Reset Lucene Index Step resets a Lucene index. Restarts the indexing process from the beginning in order to update current content items. It doesn't delete existing entries from the index.
The includeAll
property indicates whether to include all available Lucene indices. When set to true
, the Indices
property can be omitted.
{
"name": "lucene-index-reset",
"includeAll": false,
"Indices": [
"IndexName1", "IndexName2"
]
}
{
"name": "lucene-index-reset",
"includeAll": true
}
Rebuild Lucene Search Index Step¶
This Rebuild Lucene Index Step rebuilds a Lucene index. Deletes and recreates the full index content.
The includeAll
property indicates whether to include all available Lucene indices. When set to true
, the Indices
property can be omitted.
{
"name": "lucene-index-rebuild",
"includeAll": false,
"Indices": [
"IndexName1", "IndexName2"
]
}
{
"name": "lucene-index-rebuild",
"includeAll": true
}
Content Step¶
The Content step allows you to create content items.
{
"name": "content",
"Data": [
{
"ContentType": "Menu",
...
},
...
]
}
Note
There is also QueryBasedContentDeploymentStep
which produces exactly the same output as the Content Step, but based on a provided Query.
Media Step¶
The Media step allows you to import media files to the tenant Media folder.
{
"name": "media",
"Files": [
{
"TargetPath": "home-bg.jpg",
"SourcePath": "../wwwroot/img/home-bg.jpg"
},
{
"TargetPath": "post-bg.jpg",
"SourcePath": "../wwwroot/img/post-bg.jpg"
}
]
}
Layers Step¶
The Layers step allows you to create multiple layers.
{
"name": "layers",
"Layers": [
{
"Name": "Always",
"Rule": "true",
"Description": "The widgets in this layer are displayed on any page of this site."
},
{
"Name": "Homepage",
"Rule": "isHomepage()",
"Description": "The widgets in this layer are only displayed on the homepage."
}
]
}
Queries Step¶
The Queries step allows you to create multiple Lucene or SQL queries.
{
"name": "queries",
"Queries": [
{
"Source": "Lucene",
"Name": "RecentBlogPosts",
"Index": "Search",
"Template": "[file:text('Snippets/recentBlogPosts.json')]",
"Schema": "[js:base64('ew0KICAgICJ0eXBlIjogIkNvbnRlbnRJdGVtL0Jsb2dQb3N0Ig0KfQ==')]",
"ReturnContentItems": true
}
]
}
AdminMenu Step¶
The AdminMenu step allows you to create multiple admin menus.
{
"name": "AdminMenu",
"data": [
{
"Id": "[js:uuid()]",
"Name": "Admin menu",
"Enabled": true,
"MenuItems": [
...
]
}
]
}
Roles Step¶
The Roles step allows you to set permissions to specific roles.
{
"name": "Roles",
"Roles": [
{
"Name": "Anonymous",
"Description": "Anonymous role",
"Permissions": [
"ViewContent",
"QueryLuceneSearchIndex"
]
}
]
}
Warning
As of version 1.6, the default roles are no longer auto created. Setup recipe must define the default roles to be used. The Roles
feature will automatically map all known permissions to the defined roles each time a feature is enabled.
Template and AdminTemplate Step¶
The Template and AdminTemplate steps allow you to create Liquid Templates.
{
"name": "Templates",
"Templates": {
"Content__LandingPage": {
"Description": "A template for the Landing Page content type",
"Content": "[file:text('Snippets/landingpage.liquid')]"
}
}
}
Workflow Step¶
The WorkflowType step allows you to create a Workflow.
{
"name": "WorkflowType",
"data": [
{
"WorkflowTypeId": "[js: variables('workflowTypeId')]",
"Name": "User Registration",
...
}
]
}
Deployment Step¶
The Deployment step allows you to create a deployment plan with deployment steps. Also see Deployment.
{
"name": "deployment",
"Plans": [
{
"Name": "Export",
"Steps": [
{
"Type": "CustomFileDeploymentStep",
"Step": {
"FileName": "Export",
"FileContent": "Export",
"Id": "[js: uuid()]",
"Name": "CustomFileDeploymentStep"
}
},
{
"Type": "AllContentDeploymentStep",
"Step": {
"Id": "[js: uuid()]",
"Name": "AllContent"
}
}
]
}
CustomSettings Step¶
The CustomSettings step allows you to populate your custom settings with initial values.
{
"name": "custom-settings",
"MyCustomSettings": {
"ContentItemId": "400d6c7pwj8675crzacd6gyywt",
"ContentItemVersionId": null,
"ContentType": "MyCustomSettings",
"DisplayText": "",
"Latest": false,
"Published": false,
"ModifiedUtc": null,
"PublishedUtc": null,
"CreatedUtc": null,
"Owner": "",
"Author": "",
"MyCustomSettingsPart": {
"MyTextField": {
"Text": "My custom text"
}
}
}
}
Recipes Step¶
The Recipes step allows you to execute other recipes from the current recipe. You can use this to modularize your recipes. E.g. instead of having a single large setup recipe you can put content into multiple smaller ones and execute them from the setup recipe.
{
"name": "recipes",
"Values": [
{
"executionid": "MyApp",
"name": "MyApp.Pages"
},
{
"executionid": "MyApp",
"name": "MyApp.Blog"
}
]
}
As executionid
use a custom identifier to distinguish these recipe executions from others. As name
use the name
field from the given recipe's head (this is left blank when you export to recipes).
Other settings Step¶
Here are other available steps:
Command
FacebookLoginSettings
FacebookSettings
GitHubAuthentication
GoogleAnalyticsSettings
GoogleAuthenticationSettings
AzureADSettings
MicrosoftAccountSettings
OpenIdApplication
OpenIdClientSettings
OpenIdServerSettings
Twitter
Recipe helpers¶
Recipes can use script helpers like this:
{
"ContentItemId": "[js: uuid()]"
}
Name | Description |
---|---|
uuid() |
Generates a unique identifier for a content item. |
base64(string) |
Decodes the specified string from Base64 encoding. Use https://www.base64-image.de/ to convert your files to base64. |
html(string) |
Decodes the specified string from HTML encoding. |
gzip(string) |
Decodes the specified string from gzip/base64 encoding. Use http://www.txtwizard.net/compression to gzip your strings. |
Recipe Migrations¶
A recipe migration is a way to perform updates via a recipe file. The most common uses for this would be to update metadata like content types or workflows, but one could update anything that is updateable via a recipe.
Let's consider a simple scenario: adding a new asset. Now one could do this via the admin UI interface, but the purpose here is to demonstrate all the moving parts involved in a recipe migration.
In your module or theme project, create a class that inherits from OrchardCore.Data.Migration.DataMigration
(found in the OrchardCore.Data.Abstractions
package). Add a dependency for the IRecipeMigrator
service to this class. Provide CreateAsync()
and/or UpdateAsync()
methods that return Task<int>
to provide the migration steps. The class can placed anywhere in your project, but the recipe JSON files must be placed in a folder named Migrations
.
Here is an example of how initial and subsequent migrations can be authored. Use the CreateAsync()
method to provide the very first migration that runs and ensure that this method always returns 1. Use the UpdateFrom<version>Async()
to provide subsequent migrations; in this example, we have a migration that updates from version 1 to 2. The method names are case-sensitive and the naming convention must be followed for the migrations to be discovered and executed.
public sealed class Migrations : DataMigration
{
private readonly IRecipeMigrator _recipeMigrator;
public Migrations(IRecipeMigrator recipeMigrator)
{
_recipeMigrator = recipeMigrator;
}
public async Task<int> CreateAsync()
{
await _recipeMigrator.ExecuteAsync("migration.recipe.json", this);
return 1;
}
public async Task<int> UpdateFrom1Async()
{
await _recipeMigrator.ExecuteAsync("migrationV2.recipe.json", this);
return 2;
}
}
And here are the migration recipes referenced in the code above:
Migrations/migration.recipe.json
{
"steps": [
{
"name": "media",
"Files": [
{
"TargetPath": "about/1.jpg",
"SourcePath": "../wwwroot/img/about/1.jpg"
},
{
"TargetPath": "about/2.jpg",
"SourcePath": "../wwwroot/img/about/2.jpg"
}
]
}
]
}
Migrations/migrationV2.recipe.json
{
"steps": [
{
"name": "media",
"Files": [
{
"TargetPath": "about/1.jpg",
"SourcePath": "../wwwroot/img/about/1.jpg"
},
{
"TargetPath": "about/2.jpg",
"SourcePath": "../wwwroot/img/about/2.jpg"
},
{
"TargetPath": "about/3.jpg",
"SourcePath": "../wwwroot/img/about/3.jpg"
}
]
}
]
}