Our blog contains the activity stream of Orchard Dojo: general news, new resources or tutorials are announced here.

Featured tags

IIS
API
All tags >

How to change the idle logout time in Orchard Core - Orchard Core Nuggets

Keeping user accounts secure is important. One aspect of this is to constrain how long people can remain logged in: If they share a device, especially a public one, then it's better to be on the safe side and automatically log them out after some time of inactivity. Here is how you can do it in Orchard Core! Since an Orchard Core-using app is in the end just an ASP.NET Core app, you can use the standard Identity options to configure how user sessions work. What governs how long users remain logged in is mostly the cookie settings. This is how you can set it from the Program file of your web app: builder.Services .AddOrchardCms() .ConfigureServices(services => services.ConfigureApplicationCookie(options => options.ExpireTimeSpan = TimeSpan.FromMinutes(30))); This logs users out after 30 minutes of inactivity. You can also set options.SlidingExpiration = false if you'd like this to be counted from the time they logged in, as opposed to the last time they did something in the app. While the above example shows how to do this in the root web app, you can similarly set this from the Startup class of a module or theme. That's it! It's worth checking out the official Orchard Core docs on configuring other Identity options too. Did you like this post? It's part of our Orchard Core Nuggets series where we answer common Orchard questions, be it about user-facing features or developer-level issues. Check out the other posts for more such bite-sized Orchard Core tips and let us know if you have another question!

How to do a security scan of an Orchard Core app - Orchard Core Nuggets

You don't want malicious people to crack your web apps to use them for spamming, cryptocurrency mining, and spreading malware, nor do you want them to get access to your users' personal data (if you actually do want to cooperate with criminals, you don't need to read further). Thus, you want your app to be secure. One aspect of achieving this is to do penetration testing on your app. Thankfully, much of this can be automated, and with the help of Lombiq UI Testing Toolbox for Orchard Core and Zed Attack Proxy (ZAP) you can conveniently do this for your Orchard Core app. Let's see how! First, install v8.2.1-alpha.6.osoe-351 or greater of the UI Testing Toolbox from NuGet because that's the one that added security scanning. There are a couple of minor breaking changes that should affect nobody, really, in this, so it'll be part of the upcoming v.9.0.0 (but for that, we're waiting for Orchard Core 1.8). Set up UI testing as explained in the UI Testing Toolbox's documentation. While we're focusing on security scanning here, the UI Testing Toolbox can do a lot, and I really mean a lot more, including one-liners to test if the basic Orchard Core features still work in your app, or unleashing automated monkey testing to try to break your app. We never work on an Orchard Core app without its safety net! Add one or more test cases to run ZAP's security scan. Since all the configuration of ZAP is available to you, customization is unlimited, but to give you a glimpse, this is how a basic security scan that's already a good start would look like: [Fact] public Task BasicSecurityScanShouldPass() => ExecuteTestAfterSetupAsync(context => context.RunAndAssertBaselineSecurityScanAsync()); And that's it! OK, I might have omitted the last step here: 4. Fix all the security issues ZAP finds, because it'll definitely find at least a couple of them! This was just a short teaser, but be sure to check out the UI Testing Toolbox's security scanning documentation, because we tried to summarize everything necessary to get you going there, including samples that you can just copy-paste. Do you want to see security scanning in action? Check out the demo video too! Also, security starts with quality code. Check out our Lombiq .NET Analyzers project to get automated checks for your code too, including pointing out potential security issues even before running the app. Did you like this post? It's part of our Orchard Core Nuggets series where we answer common Orchard questions, be it about user-facing features or developer-level issues. Check out the other posts for more such bite-sized Orchard Core tips and let us know if you have another question!

How to debug a NuGet-based Orchard Core solution - Orchard Core Nuggets

How can you debug Orchard Core code when you’re working with a solution that loads Orchard packages from NuGet? Orchard’s packages are built with symbols so you can actually use them as source too! First, in Visual Studio be sure to uncheck “Enable Just My Code” under Tools → Options → Debugging → General. Then you can debug almost as usual: If you want to step into Orchard’s code from your own code then just put a breakpoint into your code as usual and hit Step Into (F11). It’ll open the Orchard source and debugging will work as usual. If you want to place a breakpoint anywhere in the Orchard Code that you can’t step into from yours (like a controller action) then do the following: Add a reference to the type anywhere in your own code. E.g. just write ItemController somewhere and import the namespace for OrchardCore.Contents.Controllers.ItemController. Hit Go To Definition (F12) on the type. Now you’re at the decompiled source of the type. This won’t be perfect but at least you’ll be able to place a breakpoint at the beginning of the method or other member you want to debug. Be sure to tick “Allow the source code to be different from the original” under the breakpoint’s settings. Run the app with the debugger attached. Your breakpoint will be hit and then the nice symbol sources will be used, just as when you step into Orchard’s code. Note that with “Enable Just My Code” you’ll also potentially see exceptions from the libraries you use or from .NET, even if they’re swallowed down the line. Depending on your work it might be better to keep the option ticked most of the time. Alternatively, you can also try opening a full Orchard source solution (with the Orchard version closest to what you’re using) and attach it to your own app. This sometimes works too but you can only debug Orchard’s own code in that case, not yours. Did you like this post? It's part of our Orchard Core Nuggets series where we answer common Orchard questions, be it about user-facing features or developer-level issues. Check out the other posts for more such bite-sized Orchard Core tips and let us know if you have another question!

How to access services from another tenant in Orchard Core - Orchard Core Nuggets

The multi-tenancy feature of Orchard Core is great: A tenant is basically a subsite with its own independent content and configuration, under its own domain or URL prefix. You can use tenants to e.g. host websites for multiple customers of yours from a single Orchard Core app. The sites won't know anything about each other but they'll run from the same app built from the same codebase, and have access to the same modules and themes. This makes maintaining such sites very efficient, both for hosting and for development. What if you want tenants to be not that isolated though? What if there is certain content or configuration that you actually want to share among tenants or some functionality that you want to centralize on one tenant? You can use the APIs we show below to cross tenant boundaries and use any service from another tenant! Back in the day, this was also possible with Orchard 1. What we'll see here is a simple controller (just for the sake of easy demonstration, you can do the same thing anywhere). In the Index action, we'll fetch content items from another tenant with the IContentManager service that you already know. This is just an example though, really you can access any other service as well. public class CrossTenantServicesController : Controller { private readonly IShellHost _shellHost; // We'll need IShellHost to access services from a currently running shell's dependency injection container // (Service Provider). public CrossTenantServicesController(IShellHost shellHost) => _shellHost = shellHost; // A simple route for convenience. You can access this from under /CrossTenantServices?contentItemId=ID. Here // ID needs to be a content item ID that you can get e.g. from the URL when you open an item to edit from the // admin (it looks something like "4da2sme18cc2k2rpdgwx3d4cwj" which is NOT made by a cat walking across the // keyboard!). [Route("CrossTenantServices")] public async Task<string> Index(string contentItemId) { // Even if you don't create tenants, there will still be a single tenant in an Orchard app, the Default // tenant. For all other tenants you create you can provide the technical name. // In this example, we'll access content items from the Default tenant but this works for any tenant of // course. Create a tenant in your app (enable the Tenants feature and then create it from under // Configuration / Tenants), enable the Training Demo on it too and check out how this works there! // First you have to retrieve the tenant's shell scope that contains the shell's Service Provider. Note // that there is also an IShellSettingsManager service that you can use to access the just shell settings // for all tenants (shell settings are a tenant's basic settings, like its technical name and its URL). var shellScope = await _shellHost.GetScopeAsync("Default"); // We'll just return the title of the content item from this action but you can do anything else with the // item too, like displaying it. string title = null; // With UsingAsync() we'll create a block where everything is executed within the context of that other // tenant. It's a bit similar to being inside a controller action, but remember that all of this is running // on the Default tenant, even if you're looking at it from another tenant you've created. await shellScope.UsingAsync(async scope => { // You can resolve any service from the shell's Service Provider. This serves instead of injecting // services in the constructor. var contentManager = scope.ServiceProvider.GetRequiredService<IContentManager>(); // We can use IContentManager as usual, it'll just work. // Note that for the sake of simplicity there is no error handling for missing content items here, or // any authorization. It's up to you to add those :). var contentItem = await contentManager.GetAsync(contentItemId); // DisplayText is what you've already learned about in PersonPartHandler. title = contentItem.DisplayText; }); return title; } } Pretty neat, right? If you'd like to play with the code and see it in action check it out in our Training Demo module! Note that a shortcut to achieving this is now part of our Helpful Libraries too! Did you like this post? It's part of our Orchard Core Nuggets series where we answer common Orchard questions, be it about user-facing features or developer-level issues. Check out the other posts for more such bite-sized Orchard Core tips and let us know if you have another question!

How can I call an external API from a workflow task? - Orchard Core Nuggets

You have several options to send an HTTP request to an external API in Orchard Core, but maybe you haven't tried the Http Request Task. Let's see quickly how you can hook up a workflow! The HTTP Request Task comes from the HTTP Workflows Activities feature, so before doing anything, don't forget the enable that module. Now navigate to the Workflows option from the menu and hit Create Workflow Type to add your workflow. JSONPlaceholder is a nice fake online REST API that you can use whenever you need some fake data. It comes with a set of 6 common resources and we are going to use the first one and making a GET HTTP request to get 100 user posts in a JSON format. Choose the Add Task button in the Workflow editor and select the HTTP Request one from the HTTP category. Here you can see a nice editor where you can provide the details of your request. We added a custom title to our activity as Get 100 posts. The URL will be the URL provided by the JSONPlaceholder API. To get the posts we have to make a GET request. If you would like to add a new post, make a POST request to the same endpoint, and provide the body to send. Don't forget to handle the 201 HTTP response code, because this will be the number that will show you that you are good to go and the server faked that your content was created. You may notice that you can type Liquid code everywhere in this editor. Let's say that the title of our new post will be the name of our site. To do that, we can pass the {{ Site.SiteName }} Liquid expression. As we mentioned, we get 201 if everything goes well. If the server returned with anything else, then something bad happened and we should handle that in our workflow of course. To handle the failed requests, we can use the Unhandled HTTP Status branch of the HTTP Request Task (we named in Create a new post) and notice the user somehow. OK, it's not a useful workflow, because we are only dealing with the response codes, but not with the response body. But how can we use the response details? To find the answer we have to check the source code of the HTTP Request Task activity and take a look at the ExecutyAsync method. Here you can see that the code uses the LastResult of the workflowContext and the LastResult has a Body property where you can find the response body itself. The LastResult property of the WorkflowExecutionContext is an object, that means you can easily put everything into the LastResult, but it could be a little bit harder to get the content from it. public override async Task<ActivityExecutionResult> ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityContext activityContext) { using (var httpClient = new HttpClient()) { var headersText = await _expressionEvaluator.EvaluateAsync(Headers, workflowContext); var headers = ParseHeaders(headersText); foreach (var header in headers) { httpClient.DefaultRequestHeaders.TryAddWithoutValidation(header.Key, header.Value); } var httpMethod = HttpMethod; var url = await _expressionEvaluator.EvaluateAsync(Url, workflowContext); var request = new HttpRequestMessage(new HttpMethod(httpMethod), url); var postMethods = new[] { HttpMethods.Patch, HttpMethods.Post, HttpMethods.Put }; if (postMethods.Any(x => string.Equals(x, httpMethod, StringComparison.OrdinalIgnoreCase))) { var body = await _expressionEvaluator.EvaluateAsync(Body, workflowContext); var contentType = await _expressionEvaluator.EvaluateAsync(ContentType, workflowContext); request.Content = new StringContent(body, Encoding.UTF8, contentType); } var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead); var responseCodes = ParseResponseCodes(HttpResponseCodes); var outcome = responseCodes.FirstOrDefault(x => x == (int)response.StatusCode); workflowContext.LastResult = new { Body = await response.Content.ReadAsStringAsync(), Headers = response.Headers.ToDictionary(x => x.Key), StatusCode = response.StatusCode, ReasonPhrase = response.ReasonPhrase, IsSuccessStatusCode = response.IsSuccessStatusCode }; return Outcomes(outcome != 0 ? outcome.ToString() : "UnhandledHttpStatus"); } } Imagine you are implementing a custom activity that creates content items based on the response body. For that, you need to get the JSON then serialize it to a typed class. That makes the data easier to work with. In the ExecutyAsync method of your custom activity you can get the JSON like: var responseBody = workflowContext.LastResult.GetType().GetProperty("Body").GetValue(workflowContext.LastResult).ToString(); Here the responseBody variable will contain a JSON in a string. You may notice that the value of the title here is Orchard Core, which comes from the {{ Site.SiteName }} Liquid value. To deserialize the response to typed classes you can use the built-in Json.NET package in Orchard Core and for instance, use the JsonConvert.DeserializeObject method of it. And that's it, now do what you want with the data. Did you like this post? It's part of our Orchard Core Nuggets series where we answer common Orchard questions, be it about user-facing features or developer-level issues. Check out the other posts for more such bite-sized Orchard Core tips and let us know if you have another question!

How to fix "InvalidDataException: Form value count limit 1024 exceeded." in Orchard Core - Orchard Core Nuggets

Let's suppose you're building your shiny new Orchard Core website. In there you're also building a shiny new page, with Flow Part obviously. You throw together a lot of widgets to get all the bells and whistles on the page, then you save it and BAM! Internal Server Error: "InvalidDataException: Form value count limit 1024 exceeded." What's this, did I break Orchard? Is Orchard trying to break me? Let's fix this! The exception happens because the structure you've built with Flow Part is simply too large for the default ASP.NET Core limits. This is not something that would be too extreme to achieve, actually: If you have more complex widgets (with a lot of fields each) and you put several of them into a complex nested structure you can quite possibly build something by hand that would fail like this. Ask how I know! You can increase the limits restricting posted form value sizes, and in this particular case you'd need to change the ValueCountLimit value of FormOptions. Put this into the Startup class of your Orchard-based web app project (it won't work in a module or theme!): services.Configure This increased the limit to 4096, plenty more than the default. However, keep in mind that these limits have their uses: Malicious users could try to post large pieces of data to your server, trying to overwhelm it for example. So only increase this as much you only need! Note BTW that we're using the ASP.NET Core configuration API here, something which is also demonstrated in detail in our Training Demo Module, so follow up learning there! Did you like this post? It's part of our Orchard Core Nuggets series where we answer common Orchard questions, be it about user-facing features or developer-level issues. Check out the other posts for more such bite-sized Orchard Core tips and let us know if you have another question!

How to add a breadcrumb menu in Orchard Core - Orchard Core Nuggets

A breadcrumb menu is a simple but effective navigation aid that shows the user where they are in the site's content structure and which path they can take back. It's also part of the web accessibility guidelines. However, there's no feature built into Orchard Core for this. Nevertheless, how to create a breadcrumb menu? It's actually really easy, you just have to copy a piece of code from Orchard Dojo! Oh wait, we're Orchard Dojo. So here you go: @inject OrchardCore.ContentManagement.IContentAliasManager ContentAliasManager; @inject OrchardCore.ContentManagement.IContentManager ContentManager; @inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContextAccessor; @using OrchardCore.ContentManagement; @using OrchardCore.Menu.Models; @{ // Retrieving the menu we want to build the breadcrum menu for, in this case the one with the alias "main-menu". var menu = await ContentManager.GetAsync(await ContentAliasManager.GetContentItemIdAsync("alias:main-menu")); // We'll need the current URL to be able to check which menu item corresponds to the page. var currentRelativeUrl = HttpContextAccessor.HttpContext.Request.Path; var breadcrumbItems = new Stack<ContentItem>(); // Building the path in the menu tree to the current item. bool SearchActiveItem(IEnumerable<ContentItem> menuItems) { if (menuItems == null) return false; // Note that for the sake of simplicity this will work only with Link Menu Items, not with Conten Menu Items. foreach (var menuItem in menuItems.Select(item => item.As<LinkMenuItemPart>()).Where(item => item != null)) { if (!string.IsNullOrEmpty(menuItem.Url)) { var url = menuItem.Url; if (url.StartsWith("~")) url = url.Substring(1); if (url.Equals(currentRelativeUrl, StringComparison.OrdinalIgnoreCase)) { breadcrumbItems.Push(menuItem.ContentItem); return true; } else { if (SearchActiveItem(menuItem.ContentItem.As<MenuItemsListPart>()?.MenuItems)) { breadcrumbItems.Push(menuItem.ContentItem); return true; } } } } return false; } SearchActiveItem(menu.As<MenuItemsListPart>().MenuItems); } @* Using the Bootstrap breadcrumb classes. *@ <ul class="breadcrumb"> <li class="breadcrumb-item"> <a href="@Url.Content("~/")">@T["Home"]</a> </li> @while (breadcrumbItems.TryPop(out var menuItem)) { var linkItem = menuItem.As<LinkMenuItemPart>(); <li class="breadcrumb-item"> <a href="@Url.Content(linkItem.Url)">@linkItem.Name</a> </li> } </ul> Wait, wait, slow a bit down. What is this? This is the code that would render a simple breadcrumb menu for the menu with the alias "main-menu". If you've set up your Orchard site with any of the built-in recipes you already should have such a menu. But do take a look at the code in detail, we have a lot of helpful comments in there! Once you're clear on what this piece of code does you can copy it somewhere in your theme to display it. For example, you can just put it into the Layout shape template. A cleaner approach would be to put it into its own separate template, like BreadcrumbMenu.cshtml, and display it in one of the many ways possible. Pretty much that's it! Now let's suppose you built e.g. a menu like this: With the above snippet you can have a breadcrumb menu like this when you open the Module Development page: This just uses the default Bootstrap breadcrumbs styling. And that's it, we're done with our breadcrumbs, enjoy! To be fair, this could use some work to make it bulletproof. We could e.g. optimize away the whole lookup when we're on the homepage. Then, it only supports only a single menu item for a given page. Nevertheless, it should get you going. If you'd like to see a generic and more advanced breadcrumb menu feature in Orchard check out this issue. Did you like this post? It's part of our Orchard Core Nuggets series where we answer common Orchard questions, be it about user-facing features or developer-level issues. Check out the other posts for more such bite-sized Orchard Core tips and let us know if you have another question!

How to add a culture URL segment for localization in Orchard Core - Orchard Core Nuggets

So you're building a localized Orchard Core site and want all URLs to be in the form of /culture-name/rest/of/the/url, e.g. /hu-HU/my-page. (Figure out what "hu-HU" is! Hint: It's not an owl, neither a rock band from Mongolia!) What do you need to achieve this? Well, we've already seen how to localize content items to achieve this, but that's just for content pages. There is one small piece missing though: How to get the same functionality for controller actions, i.e. coded pages? There is nothing built into Orchard Core for this, actually. And the reason is that all you need is available in ASP.NET Core MVC already. First, you'll need to set up RouteDataRequestCultureProvider so the URL segment indicating the culture will be used to set the culture of the current request. Just add this to your module's or theme's Startup class (if you don't yet know how to build a module, check out our Training Demo!): services.Configure So far so good. Next, you'll need the controller actions you want to be culture-aware to be routed in a way that the culture name is included in the URL: public class CultureAwareController : Controller { [Route("{culture}/culture-aware"] public ActionResult CultureAwareAction() { // Build the result here. } } So now you'll be able to reach this action from under /hu-HU/culture-aware for example. There's one final part missing: Building URLs for these actions. This is quite simple too, you'd e.g. create a link for this action like following: <a asp-action="CultureAwareAction" asp-controller="CultureAware" asp-area="CultureAwareModule" asp-all-route-data="routeParams">Click here</a> That's it! Of course, this can get more complex. You can make route configuration as well as URL generation easier by centralizing this culture parameter handling, which is useful if you have loads of such controllers and links. Did you like this post? It's part of our Orchard Core Nuggets series where we answer common Orchard questions, be it about user-facing features or developer-level issues. Check out the other posts for more such bite-sized Orchard Core tips and let us know if you have another question!

How to add a favicon under /favicon.ico in Orchard Core - Orchard Core Nuggets

Every website needs a favicon of course and you can easily add one to your Orchard Core site from a theme or module with a link tag in a template. However, there's a catch: Certain browsers will still search for it (as a first attempt) under the path /favicon.ico. This can be a tiny bit detrimental to the client-side performance, and show up as annoying errors in your logs. So what can you do to serve a favicon under that path too? You could do e.g. the following: Add an actual file to your web project's wwwroot folder directly. This will work but you'll most likely have more than one icon for the site, and you'll keep them in a theme. So having two places with icons is less than ideal. Serve the same file that you have in your theme with a middleware or something. Doable but you'd teach the affected browsers that what they're doing is actually acceptable :). Redirect a /favicon.ico request to the actual favicon. This is what we'll do with the code snippet below! Open up your theme's Startup class and add this to its Configure() method (or add the method first if you haven't used it otherwise): public override void Configure(IApplicationBuilder app, IEndpointRouteBuilder routes, IServiceProvider serviceProvider) => app.Map("/favicon.ico", appBuilder => appBuilder.Run(context => { context.Response.Redirect("/My.Theme/favicon.ico", true); return Task.CompletedTask; })); As you can see this simple snippet will listen on the /favicon.ico path and redirect the client to the favicon you have in your theme (in this case we assumed it's in the root of your theme's wwwroot folder but of course it can be in any subfolder). Very simple! Did you like this post? It's part of our Orchard Core Nuggets series where we answer common Orchard questions, be it about user-facing features or developer-level issues. Check out the other posts for more such bite-sized Orchard Core tips and let us know if you have another question!

4 ways to display something from your module nested within a page in Orchard Core - Orchard Core Nuggets

A common question during Orchard Core development, something that came up again recently, is how to display something within the context of an Orchard page when that piece of data comes from your module? How can you "inject" something into the Orchard layout when you want to display e.g. a list of products retrieved from an external API? There are a couple of ways to do this depending on what exactly you need. All are fairly straightforward so let's see a quick rundown! Creating a whole page from you module If you want a whole page served by just your module then it's really simple: Create a module, add a controller as you'd do in standard ASP.NET Core MVC, make an action produce a view and that's it! The view will be wrapped into the Orchard layout so the theme you've selected will be visible around it: The basic styling will be there, the header and menu, any widgets you have put onto layers... Our Training Demo module has a simple sample exactly for this, just check it out and you'll see what we're talking about. The above screenshot comes from our Open-Source Orchard Core Extensions solution BTW. Creating a widget Widgets (see official docs) are basically little boxes of content or other functionality that you can put anywhere on the site. For example, an infobox about the site, a search box, a recent articles box, a footer can all be widgets. You can use them in two ways: Add a widget to a content item with Flow Part: Flow Part can be used to build flexible layouts out of various widgets, including nesting them (like putting widgets into a Container Widget). When you set up Orchard with the built-in Agency recipe and theme then you'll get a Page content type that has Flow Part out of the box: You can get the same content type from our Helpful Extensions Orchard Core module too. Another option to use widgets is to put them onto a layer, a sort of container of widgets. There you can place the widget into a global zone, an area of the Orchard layout, like the header, footer, or sidebars. The widgets on a layer will be displayed whenever the layer rule of that layer matches (you can think of it as a logic expression producing a boolean value), like on every page except the home page or on every page but only for authenticated visitors. For more info check out the docs. OK, but how can you create a widget? A widget is just a content item whose content type has its stereotype set as "Widget". You can change this value from the admin from under Content / Content Definitions and also from code. So, basically, the task is to create a content part of yours that'll display the data you want to show from its driver, then create a widget content type where that part is attached. Seems like a lot? It isn't, check out the relevant content part development tutorial again from the Training Demo, including creating a widget. Injecting a shape into the layout This can be a lot easier than developing a widget but also less flexible to use. You can also write your own code in a template (like a cshtml Razor file) and inject that into the Orchard layout directly. You can see an example of injecting a shape in the Training Demo module too. Displaying a shape from a Liquid Widget The Liquid Widget is a widget that can render a piece of Liquid markup. While there is such a widget in the Agency theme and you can throw it together from the admin quickly too the Helpful Libraries module has it built-in as well. With this widget and Orchard's pretty advanced Liquid support you can of course just write Liquid directly. However, for more complex apps maintaining templates editable from the admin quickly becomes an issue so we'd recommend keeping code in your modules and themes. The good news is that once you have a piece of Razor code in a cshtml template (or Liquid in a .liquid template) then you can just display it from a Liquid Widget! For example, if you have a WeatherData.cshtml template fetching some weather information from an external API then you can display it from a Liquid Widget, and thus use it just as any widget with this little piece of code: {% shape "WeatherData" %} There's more to it, check out the docs on the shape Liquid tag. Conclusion Pretty much that's it. There are other ways too but these are the most straightforward and most flexible ones. Do you have another technique you'd like others to know? Add below in the comments! Did you like this post? It's part of our Orchard Core Nuggets series where we answer common Orchard questions, be it about user-facing features or developer-level issues. Check out the other posts for more such bite-sized Orchard Core tips and let us know if you have another question!