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

Featured tags

IIS
API
SMS
SEO
All tags >

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

How to publish an Orchard Core app - Orchard Core Nuggets

Let's imagine you've already created an Orchard Core app and now it's time to show it to the world. How do you publish it, or rather, how do you create its publish package? When publishing an Orchard Core, or any .NET Core app (be it a desktop app for release or deploying a web app) you need to use the dotnet publish command (see its docs). For web apps running on Azure App Service our usual practice is to do a self-contained deployment, see the .NET Core publishing guidelines. A standard publish command for a 32b Windows App service is as following: dotnet publish SolutionName.sln --configuration Release --runtime win10-x86 --output C:\path-to-package-folder --self-contained true For a 64b App Service the runtime would be win10-x64. If you just want to do a quick debug publish then running dotnet publish in the folder of an Orchard-based web app’s solution without any parameters will create a published app in the YourWebApp\bin\Debug\netcoreapp3.1\publish folder. For a PowerShell script that does a publish and then zips up the package see this script in our HipChat to Microsoft Teams Migration Utility. 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!