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!

Delivering a Node.js asset pipeline as a NuGet package

How long does it take you to set up a Node.js-based frontend asset pipeline? That lints and compiles your SCSS, JavaScript, Markdown? That makes code style inconsistencies a tale of the past? In .NET? With Lombiq Node.js Extensions, it'll take you under a minute. In this post, we will provide an overview of what we’ve squeezed into Lombiq Node.js Extensions, how we packaged it for NuGet, why we ditched Gulp, and some gems on MSBuild integration. If you want to see a demo of Lombiq Node.js Extensions in action, check out our recent This week in Orchard post. Web Development in 2023 needs Node.js Even in the .NET world, Node.js has become a first-class citizen for all matters frontend. Grunt, Gulp, Babel, Webpack – all Node.js-based tools – have conquered the frontend landscape. Orchard Core uses Gulp to build its modules’ assets. At Lombiq, we used to use Gulp to build our assets. But it's... Time to say goodbye While Gulp provides a great development experience, there are downsides: Gulp wrappers around NPM packages were not always available or up to date. Sometimes, they caused unexpected errors after updates. Lack of support for Node.js 14 and above became an issue. Plain NPM and Node.js scripts can do the same work as a Gulp script. One less moving part in our tooling ecosystem means less friction. Based on experience with our Gulp Extensions project, we've built a tool that eliminates its weaknesses. Building a reusable frontend asset pipeline on top of Node.js Why? - So you can simply plug it into your projects. Like we do! How? - Read on! What? - Here's an excerpt of what Lombiq Node.js Extensions does: lints SCSS, compiles, autoprefixes and minifies it, generating source maps on the way lints JS, minifies it, generates source maps lints Markdown provides sensible defaults for Orchard-based web development, i.e. needs no manual configuration in the default scenario! can copy assets to a webroot folder, like image files and frontend libraries is usable from a NuGet package integrates tightly with MSBuild adds generated assets to the project's assembly allows to keep generated assets excluded from version control uses PNPM to efficiently install packages and execute scripts (if you don't know it, PNPM is a Node.js package manager with a lot of advantage over NPM) The important pieces Let's look at some of the magic sauce that makes all this possible! MSBuild takes care of the build We use our Lombiq NPM MSBuild Targets project to hook the necessary Node.js work into the MSBuild build pipeline. All the asset manipulation happens before a project's Compile target executes. That allows us to generate frontend assets and embed them into the project's assembly as part of the .NET build. There was a challenge here, though: Files that are generated during the build need to be added manually to the EmbeddedResource item group, else they won't be embedded in the DLL. Here's the relevant piece from the AddGeneratedFilesToEmbeddedResourceList target in Lombiq.NodeJs.Extensions.targets: <ItemGroup> <EmbeddedResource Include="@(NodeJsExtensionsTargetFiles)" WithCulture="false" Type="Non-Resx" /> </ItemGroup> Node.js complexities are handled under the hood Setting up Stylelint, ESLint, Markdownlint and other NPM packages can be time-consuming and complex. Lombiq Node.js Extensions has it all set up and configured out of the box! When your project integrates Lombiq Node.js Extensions and you accept the defaults, you are done. Installation and execution of the SCSS and JavaScript pipelines happen automagically. You even get live ESLint support in Visual Studio with our preconfigured settings! And if you need, you are free to extend them. Packing it up with NuGet Configuring NuGet packaging can be tedious, but here's our successful configuration. Most of all, we include all the files that are necessary to later install this project as an NPM package in any consuming project. As the documentation states: A package [can be] a folder containing a program described by a package.json file. Using Lombiq Node.js Extensions from a NuGet package is the simplest way to get started. When using it from a Git submodule, you will need to edit your project file. Outsource your frontend asset pipelines Are you still writing CSS, because you don't want to invest in setting up the necessary tooling to compile SCSS as part of your build? Do you still argue over your JavaScript files' code styling? Is inconsistent Markdown formatting plaguing you? Let Lombiq Node.js Extensions handle all that for you. Get the NuGet package today! Should you need any further help, file an issue or contact us for commercial-grade support.

Our full Orchard Core tutorial series, the Dojo Course 3 is here!

After a long wait, the new Orchard Core version of our legendary Dojo Course tutorial series is here, the Dojo Course 3! Are you a newcomer and want to learn Orchard Core from the ground up, both from a user's and a developer's perspective? Are you somewhat familiar with Orchard Core but would like to get up to speed and become an Orchard pro? Look no further, check out Dojo Course 3! Dojo Course 3 guides you from the very basics of Orchard Core all up to be able to write your own themes and modules, utilizing various APIs of Orchard. We're publishing a tutorial video every day for 40 days starting on 1 December. So, this is your 40 days of Orchard :).

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 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!