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

Help shape Orchard Core's new website, a new website using Orchard Core - This week in Orchard (17/07/2026)

When you add a custom settings page, does it look like it belongs in the admin UI?

When you utilize workflow activities, does JavaScript syntax have to be the only option?

After checking out this week's Orchard news, both have better answers.

Thanks to Mike Alhayek, admin forms across settings and configuration pages now use the same ocat-* CSS class conventions, replacing legacy Bootstrap and Orchard-specific helpers. And all workflow tasks that previously only supported JavaScript now also support Liquid, with a new LiquidTask added to match ScriptTask.

Also this week: Nick Jackson's Orchard Core website redesign is open for community feedback, and GoRide joins the showcase of sites running on Orchard Core.

Read on for the details, or jump to the section that matters to you!

Latest tutorials

Featured tags

AI
SMS
IIS
MCP
API
SEO
SSL
All tags >

Audit Trail module UI improvements, new User Event Handlers - This week in Orchard (29/06/2021)

Improving the UI of the Audit Trail module, new user event handlers, feature to lock-out users on wrong password, and many more additions to Orchard Core in our current post! Orchard Core updates New User Event Handlers You can already work with several user event handlers. Let's say you want to do something after a user is created. For example, after a user has been created, I would like to send a welcome email to that user. And from now you will find more events if you open up the IUserEventHandler interface in the OrchardCore.Users.Abstractions module. Like now you can implement the CreatingAsync method, which occurs before a user is created. Or you can use the UpadatingAsync or UpdatedAsync events too! ListPart shape names for placement The issue is that placement doesn't take into consideration the display type convention, i.e. _SummaryAdmin when placing shapes, so the convention for shape names fails, and placing a ListPart shape tries to collect all three shapes, and render them. Fixed whitespace parsing in output tags in Fluid Let's say you have the following Liquid expression which is totally a valid one: {{ "foo" | size }} But what if you write this expression in the following way {{ "foo" | size}} it would say a parse error. But from now, if you have only one expression in different lines, the parser will work as expected. For example, now you can do something like this, this will be valid: {{ "foo" |size}} Lockout on wrong password The Lockout settings are set with the settings configured in ASP.NET Identity. Those options are used to define, for example, the TimeSpan a user is locked out for when a lockout occurs or the number of failed access attempts allowed before a user is locked out. So, from now you can define the logic where you want users to be locked out and you can also set how long you want to lock out that user after how many failed access. And if you check out the documentation you will find some lines about how you can read the configuration from an external config file. Here you can say that we want to allow this feature for every new user. And if they provide a wrong username or password 5 times a row, we lock out them for the next 5 minutes. And you also have an option to unlock users from the admin UI right away. If you navigate to Security -> Users you will find an Unlock button if the given user locked himself out. As you can see in the screen below, the user with the editor user name is a locked-out one. Adding docs for the absolute_url and href Liquid filters The page about the Liquid module in the documentation is a super-useful one because here you can see the list of all the available Liquid filters that you can use in Orchard Core. And now this page got an update by adding the absolute_url and href Liquid filters to it. The absolute_url creates the full absolute URL for the given relative virtual path and the href creates a content URL for a relative virtual path. Recommended for generating URLs in every case you want to refer to a relative path. Demos Audit Trail module UI improvements The Audit Trail module provides log records for creation, deletion of any content type, and events like user events. We wrote about the Audit Trail module for Orchard Core in this This week in Orchard post, if you haven't read about it yet, start by checking out that one first. Now, we will focus on the new UI. First of all, set up your site using the Blog recipe. After, head to the admin UI of your site and enable the Audit Trail and the Users Audit Trail features under Configuration -> Features. Audit Trail: Provides a log for recording and viewing back-end changes. Users Audit Trail: The users audit trail feature allows logging of user events. Now you can set up the Audit Trail module that you can do under Configuration -> Settings -> Audit Trail. Here you will see 3 different tabs: Events, Trimming, and Content. Under the Events tab, you can select what kind of events you would like to record. For example, you can say that I want to record the fact when an attempt to login failed, but I don't care about the event when a user was disabled. Under the Trimming tab, you can say how long you want to keep your records in your database. You can say that I want to keep the records for 30 days and delete the older ones. The Content tab is about setting the types of content whose events are recorded. If you don't want to log the changes for a given content type, you can just simply untick the given one. Let's say that now we want to log the changes of every content type. We need to do some changes to be able to see records in the Audit Trail list. Let's just modify the built-in blog post. Don't forget that you still have the Audit Trail Part that you can attach to any content type. If you do that, you can optionally provide a comment about this change for the Audit Trail. Now, let's open up the Audit Trail to check out our changes. As you can see here, we published the built-in blog post two times in a row. We also published the built-in About article but for this time, we attached the Audit Trail Part too. The content of the part (the comment) can be seen under each event. Also, notice that the user with the editor username has two failed login attempts. Now, let's hit the Details button near one of the content events. The Content tab here shows you some basic data about the event: the name of the event, the timestamp of the event, the current version, and so on. You can see the JSON representation of the content item under the Diff tab. Here you can choose between two types of views: Split: the content of the previous version can be seen on the left side of the screen and the content of the selected version can be seen on the right side of the screen. You can see the previous, changed data in red and the current, modified data in green. This screen shows that the ContentItemVersionId has just changed with the ModifiedUtc and the PublishedUtc values. The reason for that is we changed the Markdown text of the blog post, by adding a new paragraph to it. Unified: the meaning of the colors is the same here, the only difference is that you can see the whole changes in one view, instead of a split one. You will see a whole new user experience when you would like to list or sort the events in the Audit Trail list. Last week we wrote about the improved search filters to the contents and users list where you could read about the syntax for the filters. And slightly the same applies here too. If you click on the funnel icon before the search box and select the Filter Syntax one, you will see a modal window that shows you the available filters. This modal describes all the registered filters that you can use with the given properties. It also shows you the current values of the given filters and a little bit of information about the syntax. And then down the bottom, you just get the current filter and the normalized version of it which describes how it's being constructed. And as always, if you would like to see more about the new UI of the Audit Trail module in action, head to YouTube for a nice and detailed recording! News from the community Piotr Szmyd Last Tuesday June 22nd 2021, our dear friend Piotr Szmyd passed away. Piotr was a respected member of our community, a steering committee member, and a passionate contributor. Everyone loved him. By creating this issue we hope you can share a story or a thought to help keep his memory alive and show his family and his friends how much he meant to us. This picture was taken in LAX on September 2012 on the way to the first Orchard Harvest conference ever. From left to right: Zoltán Lehóczky, Benedek Farkas, Piotr Szmyd and Sipke Schoorstra. May our branches merge again, Piotr! Lombiq Helpful Extensions: Flows Helpful Extensions The Lombiq Helpful Extensions module is an Orchard Core module containing some handy extensions (e.g. filters for Projector). This time we will see the Flows Helpful Extensions which adds additional styling capabilities to the OrchardCore.Flows feature by making it possible to add classes to widgets in the Flow Part editor. But how can you use the Flows Helpful Extensions feature? Let's say you have a site set up with the Agency recipe. The first thing that you need to do is to navigate to Configuration -> Features and enable the Flows Helpful Extensions - Lombiq Helpful Extensions feature. Now, it's time to edit a content item that has a FlowPart attached. The Page will be great for us now. Let's say we add a new Html widget to the FlowPart of our new page. If you click on the new cog icon, you can add custom classes to this widget and remove the grid extension classes that will be added to your widget by default. OK, but what does it mean? What's the difference? The right side of the screen below shows you the original HTML and the one on the left shows you the modified one. As you can see, the my-custom-class CSS class is there and the widget-size-* and widget-align-* classes were removed because we put a tick on the Remove grid extension classes checkbox. Removing these classes is a good idea if you are using a CSS framework that adds CSS properties to these classes but you don't want to add styling to these elements based on these classes. In this case, you don't need to overwrite these properties. Orchard Dojo Newsletter Lombiq's Orchard Dojo Newsletter has 205 subscribers! We have started this newsletter to inform the community around Orchard with the latest news about the platform. By subscribing to this newsletter, you will get an e-mail whenever a new post is published to Orchard Dojo, including This week in Orchard of course. Do you know of other Orchard enthusiasts who you think would like to read our weekly articles? Tell them to subscribe here! If you are interested in more news around Orchard and the details of the topics above, don't forget to check out the recording of this week's Orchard meeting!

Improving search filters to the contents list - This week in Orchard (22/06/2021)

We can write about several nice improvements this week! Adding missing deployment steps, new lines about Shortcodes in the documentation, updating the content type editor, and a demo about the search filters improvements to the contents list! Orchard Core updates Deployment steps We have several cases where we have the recipe steps but not the deployment steps. For example, now you can export the Facebook login settings but without secrets (like passwords) because we don't handle that in the recipes right now. The newly added import/export deployment steps are: FacebookLogin GoogleAnalytics Microsoft Azure AD OpenIdValidationSettings Technical name of Content Type and Display Name of Content Parts in Edit Content Type page This feature is about to show a read-only input with the technical name of the content type when you would like to edit the content type definition. With this feature, you can easily see the difference between the display name and the technical name. Of course, you can guess the display name by the technical name, but not everyone knows that. You can check it out easily. Let's just navigate to the admin UI of Orchard Core and go to Content -> Content Definition -> Content Types. Edit one of the content types, and you will see the technical name and the display name of the content type. Shortcodes docs and rename Razor helper We have the HtmlToShortcodesAsync Razor helper that you can use to render Shortcodes. This one is now renamed to ShortcodesToHtmlAsync, and the old one is marked as obsolete. And you can also find some new lines in the documentation about how to render Shortcodes via a Liquid filter or Razor helper. Check allowed file extensions when importing media You can have a list in Orchard Core that contains the file extensions that can be uploaded to the media library. If you would like to upload a PDF file to the media library and the PDF extension is not allowed, you won't be able to upload that. However, if you want to import a PDF file via recipes, you could do that because the file extension checking was missing from there. It could be a security issue but if you have rights to execute recipes, that means you have already logged in as a user who has Security Critical permissions. And one more note here: if you do _logger.LogWarning, don't forget to check is the LogLeveL.Debug enabled _logger.IsEnabled(LogLevel.Debug), otherwise, don't write a warning to the log files. Remove deprecated SHA256CryptoServiceProvider usage While running a benchmark, it started failing, the SDK throwing some warning that the CryptographyAlgorithms.CreateSHA256() is deprecated and cannot be used in .NET 6. The documentation says that now we can do SHA256.Create(), and it works for all the versions that we are targeting. Someone else from Microsoft related to benchmarking and performance mentioned this issue because they are using Orchard Core as a source for the PGO. PGO means Performance Guided Optimizations which is a way to analyze what's happening in an app to optimize the IL generated by the JIT. Demos Improving search filters to the contents list A few weeks ago, you could see a demo regarding search filters and parsers for the contents list. This demo is just about showing you the final version of the search filters that you can also try out right now if you are using the preview packages of Orchard Core! Let's see how you can use this feature that makes browsing your content items much easier! We have a site set up with the Blog recipe. The blog recipe creates an Article content type and one Article content item by default. If you want to list all of the article content items, you just need to navigate to Content -> Content Items and select Article from the filter by content type dropdown menu. As you can see, this means that the Search textbox will be populated with the type:Article expression. It means if you write, type: and the name of the content type, you can list all of the content items from the given type. If you also want to sort the items by the created date (select Recently created from the Sort dropdown menu), the sort:Created text will also appear in the Search textbox. If you click on the funnel icon before the search box and select the Filter Syntax one, you will see a modal window that shows you the available filters. This modal describes all the registered filters that you can use with the given properties. It also shows you the current values of the given filters and a little bit of information about the syntax. And then down the bottom, you just get the current filter and the normalized version of it which describes how it's being constructed. And the same applies to the Users list too (Security -> Users). Here we filtered for the Enabled users where the name contains admin, and where the Administrator role applies. Finally, we sort them by their email address. If you would like to know more about this feature don't forget to check out this recording on YouTube! News from the community Lombiq Utility Scripts: create a trusted certificate Our Utility Scripts project is open source from last year! Many scripts for Orchard Core, Orchard CMS, Azure, SqlServer development. E.g., quick Orchard Core solution init, reset/reinstall. In the following lines, we would like to show you a nice and useful script that could be a very handful one during your Orchard Core development. The CreateTrustedCertificate script creates all necessary files and imports them into the local Certificate Store so that it can be used to access localhost sites via HTTPS. If you open up that file in GitHub, you will find several comments in the script that help you to see what are the steps that are script is actually doing! Orchard Dojo Newsletter Lombiq's Orchard Dojo Newsletter has 204 subscribers! We have started this newsletter to inform the community around Orchard with the latest news about the platform. By subscribing to this newsletter, you will get an e-mail whenever a new post is published to Orchard Dojo, including This week in Orchard of course. Do you know of other Orchard enthusiasts who you think would like to read our weekly articles? Tell them to subscribe here! If you are interested in more news around Orchard and the details of the topics above, don't forget to check out the recording of this week's Orchard meeting!

Admin Layers, move admin branding to a distinct shape - This week in Orchard (31/05/2021)

A new demo about Admin Layers, several performance improvements, and a new AdminBranding shape is waiting for you in our upcoming post! Oh, and have we mentioned that Lombiq is now more than 8 years old? :) Orchard Core updates Move admin branding to a distinct shape Now you will find a brand new AdminBranding shape in the default admin theme of Orchard Core that is about containing the favicon, and the default, clickable Orchard Core logo that redirects the user to the home page of the admin UI (the admin dashboard). And now, because this whole stuff is a new shape, you can easily customize this one too! Check out the updated documentation to read some words about it! Fix drop widget to empty zone Someone reported an issue that it's not possible to drag and drop widgets to an empty zone. Now, this has been fixed. Remove some async-await It's not a recommendation anymore to remove async/await when it can be removed. In this case here the Task from _session.Query can be returned directly, it doesn't have to be awaited. There is a benefit in terms of allocations but at the same time if there is an exception you lose where it's coming from. And the impact in terms of perf is minimal. So, it's not a recommendation to do it every time. Adding spatial features to content (Lucene indexed) You could see a demo a few weeks ago about adding spatial features to content. This module provides a GeoPointField, which can be used to give a geographic position to content. And this has been merged to the dev branch of Orchard Core with nice detailed documentation also! Don't forget to check out the docs to read about Terms specifications, Geo Bounding Box, Geo Distance, and many more! Reduce string allocations in MediaTokenService and use pooled StringBuilder instances with StringWriter Now the code is using StringBuilder from the StringBuilderPool instead of using String.Concat(), which is about allocating one buffer. There are other improvements with the queryStringTokenKey, which is not recreating a new string every time, just using the existing one. StringWriter will create a StringBuilder by using the StringBuilderPool and passing that to the StringWriter. It's not creating a new one, it's just reusing the existing one. Demos Admin Layers People want to change shapes or styles for various things on the admin UI without having a custom admin theme. For example, currently, it is not possible to change the admin panel's logo and icon without a custom theme (set the base theme to Admin). The new Admin Layers feature is very similar to the Admin Templates one. This new one is about working with the layers of the admin theme, which means you can put different kinds of widgets to the zones of the admin theme using different kinds of layers rules. Sounds familiar? Yeah, that's the goal of this one! Let's say you set up your site using the Blog recipe. After go to the Configuration -> Settings -> Features and find the Admin Layers one which enables admin users to render widgets across pages of the admin based on conditions. Now, the first thing to do is to set up the available zones for widgets as you would do that in the case of the front-end widgets as well. You can do that under Design -> Settings -> Admin Zones. Let's say we would like to work with the HeadMeta, Footer, Header, and NavbarTop zones for now. And let's add some layers too! If you navigate to Design -> Admin Widgets, you could see a very-very familiar UI to manage zones and layers. Here we have just added a simple layer called Always that has one boolean condition rule, which is True. Meaning the widgets on this layer will always be rendered. Now, let's do some testing! Let's say we add a simple Raw Html widget to the NavbarTop zone that is just about containing some bold text. After we will create a new Liquid Widget and render it in the HeadMeta zone. The Liquid Widget is just about containing some styling for the theme: {% styleblock at:"Head"%} body { background-color: gray !important; }{% endstyleblock %} Now we have created two widgets that we have displayed on the Always layer of the admin theme. Let's see how our theme looks like! First, notice the Some text in the NavbarTop zone near the moon icon. This is the Raw HTML widget in the NavbarTop zone. And the new background color is coming from our Liquid Widget. If you would like to know more about this feature, don't forget to check out the following recording on YouTube! News from the community Lombiq is 8 years old! May 21, 2021, is the 8th anniversary of founding Lombiq! On this special occasion, we have gathered 8 important factors of our company's life. We are glad that we took these steps at that time otherwise we might not be able to celebrate with our strong community here today. Check out our post to see which important factors we gathered together! Orchard Dojo Newsletter Lombiq's Orchard Dojo Newsletter has 202 subscribers! We have started this newsletter to inform the community around Orchard with the latest news about the platform. By subscribing to this newsletter, you will get an e-mail whenever a new post is published to Orchard Dojo, including This week in Orchard of course. Do you know of other Orchard enthusiasts who you think would like to read our weekly articles? Tell them to subscribe here! If you are interested in more news around Orchard and the details of the topics above, don't forget to check out the recording of this week's Orchard meeting!

Search filters to the contents list, Admin breadcrumbs - This week in Orchard (10/05/2021)

Two new demos are coming this week! One is about extending the search capabilities on the contents list page, the second one is about adding admin breadcrumbs to your site. But first, let's check out some other great additions to Orchard Core! Orchard Core updates Add shortcut to change password from admin Let's say you have an Orchard Core site and you are logged in as a user who has access to the admin panel. If you click on the user icon at the top-right corner of the admin theme (near the Visit Site one), you will see a pop-up window that displays the content of the UserMenu shape. This shape shows you the user name of the currently logged-in user, you can have the option to edit the user profile of that user and you can log off. Now this list has a new option, called Change password. If you click on that, you will be navigated to the page where you can change your password. Monaco editor Monaco Editor is a new editor mode, available for the Html Field. The Monaco Editor is the code editor that powers VS Code. A good page describing the code editor's features is here. You could see a nice demo about adding Monaco editor to Orchard Core a few weeks ago and now the Monaco editor is merged to the dev branch, which means you can have it right away if you are using the preview packages of Orchard Core! And that's not all! There was a bug in (it didn't trigger preview updates) when you did a live preview of your content item that has an HTML Field with a Monaco editor that is now also fixed! Prevent Razor compilation in production The community continuously testing Orchard Core in .NET 6 as well and doing some performance stuff if needed. This fix is about to not use Razor compilation in production. Of course, it's still enabled in development to be able to change the files dynamically. Performance optimizations And if we are talking about optimizations, let's see another one: the Roles are now cached. Every time you check for dynamic content type authorization (like for each type we generate the content type localization dynamically), it would re-instantiate the dynamic type. Check out how the code uses the new OwnerPermissionsByName dictionary to know more! Demos Search filters to the Contents list If you navigate to the admin UI of your Orchard Core site and find Content -> Content Items in the menu, you will get a list of all listable content items on your site. Here you can find a search text box on the top, that is used to find content items based on their display text values. If you type explore here, the logic will return you the blog post content item with the display text Man must explore, and this is exploration at its greatest (which is the predefined blog post that comes with the Blog recipe). But what about having a way to provide more complex queries here? For example, we want to return every content item where the display text contains a man or woman. In that case, you can write the following query in the search box: man OR woman. Or you can have other more complex queries like: man AND woman: list all content items where the display text contains man and woman at the same time. man NOT woman: list all content items where the display text contains man but not contains woman. man woman: same as man OR woman. But you can do a lot more by using the new filters like you can sort the content items by the created date! And that's not all of the additions that sit in this PR and just wait to be merged to the dev branch of Orchard Core! If you would like to know more with some implementation details as well, head to YouTube now for a recording! Admin Breadcrumbs This demo is about a feature that is in progress right now. You can find the code in this pull request. The goal of this feature is to have breadcrumbs in the admin. Let's check that out! Let's say you have the Workflows module enabled and have at least one workflow created. If you check out the following screen, you could see that the breadcrumb will be displayed in the top bar. Breadcrumbs can be useful when you have a details page, and you want to see the parent page also. Currently there are not so many pages that have breadcrumbs, but this could be changed in the future of course. If you would like to know more about the current progress of this feature, don't forget to head to YouTube to check out this recording! News from the community Consultant needed for scalable TeamCity+Azure setup Do you have a lot of experience in configuring and operating TeamCity, hosting apps in Azure, and building a scalable Continous Integration environment? We're looking for you! Check the details here! Orchard Dojo Newsletter Lombiq's Orchard Dojo Newsletter has 197 subscribers! We have started this newsletter to inform the community around Orchard with the latest news about the platform. By subscribing to this newsletter, you will get an e-mail whenever a new post published to Orchard Dojo, including This week in Orchard of course. Do you know of other Orchard enthusiasts who you think would like to read our weekly articles? Tell them to subscribe here! If you are interested in more news around Orchard and the details of the topics above, don't forget to check out the recording of this week's Orchard meeting!

Orchard Core Rebranding, new filters on the Users page - This week in Orchard (25/04/2021)

It's time to rebrand Orchard Core for the upcoming 1.0 release! Share your ideas in a visual survey, then check out the newest features of Orchard Core, like the new filters on the Users page or the upcoming sticky toolbar! Check out our post for more! Orchard Core updates Update branding There was a discussion last month about updating the branding of Orchard Core and we also wrote about that in this post. So why do we want to start rebranding Orchard? Branding is super important to make Orchard a widely known, competitive product, to truly make it an industry sensation. And now you can fill out a visual survey to help the community choosing the logo you would like to see! If you haven't voted, please choose the logo, the color, and the type of font that you are like the most. Thanks for voting, it means a lot to the community! And don't forget to follow this issue on GitHub where you can freely add your opinion about the new logo to make Orchard Core even better! Expose BackgroundTask Events Currently, if you want to know from code when a background task is executed and how long it takes there's no easy way to do it, you can't easily tap into background task execution. It's probably a problem unique to application performance monitoring (and otherwise necessary if you want to collect telemetry on background tasks). ModularBackgroundService could raise events when the execution of a background task starts and when it ends (perhaps separately when it completes successfully and with an error). Basically, raise events when there's logging currently. Now you have the availability to see when a given background task starts for example to be able to create diagnostics. The only thing you have to do is to implement the IBackgroundTaskEventHandler. The ExecutingAsync method will be invoked before executing the background task and the ExecutedAsync gets called just after the execution of a background task. The Lombiq Hosting - Azure Application Insights Orchard Core module enables easy integration of Azure Application Insights telemetry into Orchard. Just install the module, configure the instrumentation key from a configuration source (like the appsettings.json file) as normally for AI, and collected data will start appearing in the Azure Portal. The module will utilize these events there to make background task telemetry collection better. If you haven't heard about that module yet, take a look at this post! Support for SQL window functions Now Orchard Core has support for SQL window functions! Window functions are not part of SQL92 syntax, but they are quite useful and available in all modern database engines. They are part of SQL:2003 standard. lampersky extended SQL Parser and SQL Grammar to support OVER clause with PARTITION BY and ORDER BY so now you should be able to perform queries like: SELECT COUNT(1) OVER () as col1, COUNT(1) OVER (PARTITION BY Type) as col2, COUNT(1) OVER (PARTITION BY Type ORDER BY Type) as col3, COUNT(1) OVER (PARTITION BY Type, Id ORDER BY Type, Id DESC) as col4FROM Document; Now you are able to use other window functions (argumentless) like: ROW_NUMBER() RANK() DENSE_RANK() and more... Demos New filters on the Users page Head to the admin UI of Orchard Core then navigate to Security -> Users. If you check out that page closely you will see some new additions here. We already have a search feature here and now we have new filters too! You can sort users by user name and email address. You can say that you want to display only enabled/disabled users or all of them. But the most useful feature we think is the option that you can filter users by role. This is very useful if you have a lot of front-end users and you just want to see the authenticated ones. Head to YouTube now to see the new filters in action! Sticky action buttons Let's say you are on the admin UI of Orchard Core and you editing your content item. And that content item contains several parts/fields, which makes the editor page more complex and you need to scroll to be able to see every editor because it's now not fit on your screen. If you have just added a FlowPart or a BagPart to your content type, these will definitely make your editor longer. This makes publishing, saving, or previewing your content item a little harder. That would be great to have a sticky toolbar that stays in place if you scroll the page and you can see them all the time. This means a toolbar with the title of the current page on the left, and with the action buttons on the right. You can meet with the sticky toolbar on the create/edit content and on the Design -> Templates page because these are the longest ones where you may need this feature the most. This feature is still under development and might change in the future. If you are interested in a recording of this demo, head to YouTube now! News from the community Decoupled CMS Orchard Core tutorial for the Dojo Course After a long wait, last December we released the new Orchard Core version of our legendary Dojo Course tutorial series, 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. Here you can check out the Dojo Course 3 YouTube playlist. All the video tutorials are here, in the recommended viewing order. Note that the video descriptions contain links if any were mentioned in the video. And now we are thinking about making a decoupled CMS Orchard Core tutorial for the Dojo Course to extend it a little bit. Orchard for the admin and content store, Razor Pages for the frontend. What do you think about this one? Are you interested? If yes, please tell us your opinion under this Tweet to be able to create that kind of tutorial in the future that you would love to see! Orchard Dojo Newsletter Lombiq's Orchard Dojo Newsletter has 196 subscribers! We have started this newsletter to inform the community around Orchard with the latest news about the platform. By subscribing to this newsletter, you will get an e-mail whenever a new post published to Orchard Dojo, including This week in Orchard of course. Do you know of other Orchard enthusiasts who you think would like to read our weekly articles? Tell them to subscribe here! If you are interested in more news around Orchard and the details of the topics above, don't forget to check out the recording of this week's Orchard meeting!

Application Insights Module, HeadMeta zone alternate - This week in Orchard (11/04/2021)

The Application Insight module is now available for Orchard Core too! Check out our current post for tons of updates like the new HeadMeta zone alternate, the now() helper function in SQL Queries and many more! Orchard Core updates Support for now() helper function in SQL Queries If you do a query like this in the generic SQL language we support, this will be mapped to whatever database are you using. As you can see, if you are using the new now() helper function, you can get the current date and time. Check out the where condition of the query below: where the PublishedUtc value of the published BlogPost content types is bigger than the current date and time. Add HeadMeta zone alternate for layer widget wrapper When rendering resources using Layers in the HeadMeta zone the widget wrapper is applied, which makes for illegal HTML code inside the <head> of the webpage. Of course, you can render resources in the Content zone, but then you just end up with an empty widget wrapper. Which also makes no sense. The solution is to in the list of layer zones, also include a list of Zones which do not wrap with the widget wrapper. This is useful for other scenarios where the extra divs added by the widget wrapper are not wanted. Here you can see the two now Liquid templates for the Agency and the Blog theme, and the Razor one for the default TheTheme. Fix migrations from RC2 to the latest dev Dean Marcussen did some tests to validate that we can migrate easily without any issue from the RC2 release to the 1.0 release. The idea being is if you are using the RC2 release of Orchard Core and you want to migrate to 1.0, then there won't be any exception or database conflicts or whatever. This mainly means new or updated migration steps for the modules, or move something to a feature, or the migration dependencies are not ordered correctly. BackgroundTask Atomicity You could see a demo in this This week in Orchard post about workflows atomicity. Now here comes a continuation of that feature, the BackgroundTask Atomicity. In a nutshell, it means some additional settings like: Configurable per IBackgroundTask through a LockTimeout and an auto LockExpiration in milliseconds, by default, there are equal to 0 and in that case, it works as before without any locking. Configurable by code through the background task attribute. Or through the admin UI by enabling the existing OrchardCore.BackgroundTasks feature. Right now we have just implemented the IBackgroundTask interface and navigated to Configuration -> Tasks -> Background Tasks and hit the Edit button near the new background task that appears in the list. And in the Advanced tab of that background task, you can set the lock timeout and lock expiration values. Note that you need to enable the BackgroundTasks feature. Arrows for collapsible panels It's a minor change but fixes like this one make the admin UI of Orchard Core more consistent for the users who can access the admin panel. We have different collapsible panels in Orchard Core and the arrows were not consistent in any cases. Now, if you have a Bag Part attached to your content item, the arrow near the collapsible section will be directed to the right by default. And if you open that section, the arrow will be head to down. So now we use the same kind of arrows (Right if collapsed, Down if expanded) for all collapsible panels in the admin. And this also supports Arabic and RTL languages, where we need to direct the arrow left if the panel is closed. Demos Azure Application Insights module This Lombiq Hosting - Azure Application Insights Orchard Core module enables easy integration of Azure Application Insights telemetry into Orchard. Just install the module, configure the instrumentation key from a configuration source (like the appsettings.json file) as normally for AI, and collected data will start appearing in the Azure Portal. Application Insights is an application performance monitoring tool. Withing Azure you can create such Application Insight resources and it will give you nice metrics and other data about your app that is running or production or staging or something. This is not for local but rather for seeing what happens in an application that is actually deployed somewhere. The Readme.md file in the repository shows you how you can set up the module and do the basic configuration just by editing your appsettings.json file. Let's say we have a site installed with the Blog recipe. If you add the module to your solution and enable it (Configuration -> Features -> Lombiq Hosting - Azure Application Insights) you can go to the configured corresponding Application Insights account and for example, check out the live metrics. Or you can see what happened in a given request. You can see all the details correlated: the request, the page views, the dependency calls, everything. For example, you can see that there was a server-side request. The Item/Display action was called which actually returned you the content item that is the homepage. It happened on the development environment on the Default shell with the user named admin. If you would like to know more about this module, don't forget to head to the repository of the module and try out it now! And as always, here is a recording that you can check out anytime! News from the community Orchard Dojo Newsletter Lombiq's Orchard Dojo Newsletter has 194 subscribers! We have started this newsletter to inform the community around Orchard with the latest news about the platform. By subscribing to this newsletter, you will get an e-mail whenever a new post published to Orchard Dojo, including This week in Orchard of course. Do you know of other Orchard enthusiasts who you think would like to read our weekly articles? Tell them to subscribe here! If you are interested in more news around Orchard and the details of the topics above, don't forget to check out the recording of this week's Orchard meeting!

SEO Module, Drag and Drop Dashboard Widgets - This week in Orchard (04/04/2021)

A huge new module has been added to Orchard Core; the SEO Module! Let us write some lines about this module and the added drag and drop capabilities to the Dashboard Widgets. Check out our post for more! Orchard Core updates Rule Benchmark A few weeks ago you could see a demo about the Rules module, which allows you to build rules for the layers without using JavaScript. If you remember we mentioned that if you use the Rules module instead of JavaScript, the performance of the layers is much better. And now we can prove that we are right! If you open up the RuleBenchmark.cs file in the OrchardCore.Benchmarks project you can see some comments with exact numbers. The EvaluateIsHomepageWithJavascript is the one that uses JavaScript to evaluate the rule that is about the check if the currently opened page is the homepage or not. And the EvaluateIsHomepageWithRule is the same, but this time we did that with the Rules module. The first table showing you the results when you are using .NET Core 3.1 and the second table is showing you the results when you are using .NET 5. As you can see, there is not much difference between the two EvaluateIsHomepageWithJavascript methods (.NET 5 is a little slower but we can safely say that it's the same), but if you are using the Rules module and .NET 5, you will get the best performance at all. But the goal here is not to optimize Orchard Core for .NET, it's about showing the fact that if you are using the Rules module instead of the JavaScript expression, you can get better rule evaluations on your site. Add fieldTypeName to Query Schema for GraphQL If you create a query and you want to expose it as a GraphQL, you can create a custom GraphQL schema to define the metadata such that clients know what they can query. And now there is a custom property called Name that will contain the name of the query itself. Such that when we use GraphQL to do to run some dynamic queries like SQL queries or Lucene queries we can now access them through a custom name and not the generic name, which is the name of the query in the system. Because there could be conflicts between different query providers in GraphQL. For instance, the default name we use for Lucene and SQL queries is the custom name we pass. For the content types, we also use the name of the content type. So, if you have a query that has the same name as the content type then GraphQL won't know what to query. The content type or the custom query? So, by using this custom Name property, you can add a custom name if there is a conflict. You can read more about queries in the docs. Demos Resize, Drag and Drop Dashboard Widgets You could see and read about a great feature of Orchard Core a few weeks ago that is about to add cards to the homepage of the dashboard, which is about to represent a piece of functionality of a given feature or module. In the meantime, several other great features had been added to the Admin Dashboard feature, so, let's check out them quickly! The first thing you have to do is to go to Configuration -> Features and enable the Admin Dashboard feature. And now just simply need to navigate back to the homepage of your admin UI to see the admin dashboard that has just changed because of the newly enabled feature. You can see one predefined card here with the title Orchard Core, and the HTML Body of that widget. This content item has the Html Dashboard Widget content type and the DashboardWidget as the stereotype. Don't forget that if you would like your content type to act as a dashboard widget, you have to set the stereotype to DashboardWidget. So, back to the admin homepage. At the top-right of your screen, you will see a Manage Dashboard button. By hitting that button you will have the option to edit, delete or add new widgets to your dashboard. Every dashboard widget has a DashboardPart attached by default that contains three properties: Position, Width, and Height. These can be used to set the size and the position of the given widget. But from now you can also use drag and drop to move or resize your widget. The best way to represent the improvements of the dashboard widgets is a GIF that shows you how you can use the improved UI. And as always, if you would like to know more about Dashboard Widgets, head to YouTube for a recording! SEO Module Buzz Interactive developed a SEO module for Orchard Core and thanks to Dean Marcussen (who is working at this company) offered that the module can be integrated into Orchard Core as a contribution. And Antoine Griffard already did the integration. A huge thanks to them! Let's see this module in action! Set up your site using the Blog recipe, then head to the admin UI. Find Configuration -> Features where you will see the new module that you need to enable, called Seo, that provides SEO Meta features. If you enable it you are able to use an SEOMetaPart that you can attach to the content types that you want to be able to support SEO features. Let's attach this one to the Article content type for example. Now let's open the predefined About Article content item to see what's changed in the editor. The first thing that you may notice that now there is a new SEO tab that you can use to set up those SEO options that are specific only for this content item, like the page title, meta description, canonical URL, meta robots, and so on. But before doing more digging here let's go back to the modified content definition of the Article content type (Content -> Content Definition -> Content Types -> Article) and hit Edit near the SeoMeta part. Here you can see some additional editor settings. You can select that if you want to display the keywords or not, display the Open Graph or not, and so on. So, if you check all of these and navigate back to edit the Article content item, the SEO tab now will have more cards. You can easily open or collapse each of these cards and you can set the relevant settings under the given card. For example, if you collapse the Twitter one you can set the SEO settings related to Twitter. You may notice the little [/] icon in every editor of the SEO tab. That means you can inject Shortcodes to these input fields. Let's just add a simple [display_text] Shortcode to the site (you can see the values that you will need here) and use that in some places just for an example. Now to check how SEO works, navigate to the front end of your site and open our About article. If you view the page source, you will see the meta tags in the head of your site. If you would like to know more about this module, don't forget to check out this recording on YouTube! News from the community Orchard Dojo Newsletter Lombiq's Orchard Dojo Newsletter has 192 subscribers! We have started this newsletter to inform the community around Orchard with the latest news about the platform. By subscribing to this newsletter, you will get an e-mail whenever a new post published to Orchard Dojo, including This week in Orchard of course. Do you know of other Orchard enthusiasts who you think would like to read our weekly articles? Tell them to subscribe here! If you are interested in more news around Orchard and the details of the topics above, don't forget to check out the recording of this week's Orchard meeting!

Monaco Editor, Introduce ResourcePosition - This week in Orchard (13/03/2021)

The Monaco Editor is the code editor that powers VS Code. And from now you can use it in Orchard Core too! Check out our post for the latest improvements of Orchard Core and don't forget to take a look at our Orchard Ambassadors Toolbox! Orchard Core updates Add some comments to DataMigration class It can happen that you don't remember the correct syntax of the methods that you need to implement when you are adding your migration classes. It's useful to have it actually written down somewhere where you don't have to search. Now if you open up the DataMigration class in the OrchardCore.Data.Abstractions project you will find some comments about the correct syntax. Introduce ResourcePosition Imagine you have multiple resources (CSS or script files) to add in the footer and they don't have any dependencies. But maybe you want one of them to be at the end because it has to happen at the end. Then you can say now I want to be this resource to be the last one. But how can you do that? You can find a RequireDependencies method in the ResourceManagerTest that is about to test this new feature. Let's take a look! Here you can see that we defined a resource with the name first-resource and used the SetPosition method to set the position. The first-resource has one dependency: the first-dependency one that we have already defined in line 127. The code should inject the first-resource first but because it has a dependency to the first-dependency, the logic will inject the first-dependency resource first, then it will inject the first-resource resource. The same applies when you use the ResourcePosition.Last enum. We said that we want to define two resources as the last ones: the last-dependency and the last-resource. But the last-resource has a dependency on the last-dependency, and the last-dependency has a dependency on the another-dependency, so the correct order will be: another-dependency, last-dependency, and last-resource. Provide AdminUserId and other properties to recipes People are using recipe migrations (like the RecipeMigrator) to create content items because they can. But when you run them from setup or when you run a setup on a site, this doesn't set up some properties, like the owner of the content item. Now there is a new interface called IRecipeEnvironmentProvider that you can implement to provide different things to the recipes. The RecipeEnvironmentFeatureProvider is used to populate the environment with the AdminUserId, AdminUsername, and SiteName values that you can use when you are executing your recipes. Fix Active Directory logs an unnecessary warning during setup Let's say you activated the OrchardCore.Microsoft.Authentication.AzureAD feature during setup and configured it from the same recipe in the next step. You will see that the site is up and running but there is a warning in the log: OrchardCore.Microsoft.Authentication.Configuration.AzureADOptionsConfiguration|WARN|The AzureAD Authentication is not correctly configured. This has been fixed now by using LoadSettings() in place of GetSettings() for updating the settings in the related recipe steps. The authentication settings being entities held by the SiteSettings document, so as done in other places and for all shared documents. Demos Monaco Editor Monaco Editor is a new editor mode that is available for the Html Field. The Monaco Editor is the code editor that powers VS Code. A good page describing the code editor's features is here. Now let's see this editor in action! Let's say you set up your site using the Blog recipe. The Blog recipe contains an Article content type that will be perfect for us to play with. Head to the admin UI of Orchard Core and modify the content definition of the Article content type (Content -> Content Definition -> Content Types -> Article). Let's add a new Html Field to this content type and name it Monaco for example. Don't forget to Edit that Html Field and set the editor mode of that field to Monaco editor. You will see that there is a text area that you can use to configure the options of the editor. If you click on the Documentation for options link below you will be navigated to a page that explains which kind of options you can use in the configuration. Leave it on the default that means the editor will use the HTML language. Now if you save the content definition of the Article content type and edit the predefined Article content item, you will see the new field that you can use. You can see that it's the same editor as you can use in Visual Studio code. In this GIF we just showed you some minor functions of the editor by using the default options. But as we mentioned you can configure your editor as you want. Let's say you would like to change the theme of the editor. Modify the editor options and set the value of the theme string that is the initial theme to be used for rendering. The current out-of-the-box available themes are vs (default), vs-dark, hc-black. Let's try out the last one. There is a playground where you can find several examples to see how to use the different kinds of options. If you would like to know more about the Monaco Editor for Orchard Core, head to YouTube for a recording! Use custom settings to customize your theme In Orchard Core, you have the option to add as many settings to your site as you want. By using these settings you can set up some basic stuff like the name of the site, the default time zone, or the page title format. Some modules can provide their own settings. For example, if you enable the Facebook module, you can set the AppId, the AppSecret parameters that are necessary to make the connection between your site and a Facebook App. You can create a theme that can be easily customized just by using settings from the admin UI. To do that you need to implement your theme to support customization. But if you do that you can easily say what kind of navbar, header, logo, etc. you would like to use. Check out the following recording to see what you can achieve if you already have a theme like that. News from the community Orchard Ambassadors Toolbox Let us introduce the Orchard Ambassadors Toolbox! This repository contains a package of useful tools and content for those who want to evangelize Orchard Core. Check out the Readme.md file of the repository for a detailed description of what you can find in the repository. And if you followed us on YouTube, you may have seen our Showcasing Orchard Core CMS video that is also based on this template. Orchard Dojo Newsletter Lombiq's Orchard Dojo Newsletter has 192 subscribers! We have started this newsletter to inform the community around Orchard with the latest news about the platform. By subscribing to this newsletter, you will get an e-mail whenever a new post published to Orchard Dojo, including This week in Orchard of course. Do you know of other Orchard enthusiasts who you think would like to read our weekly articles? Tell them to subscribe here! If you are interested in more news around Orchard and the details of the topics above, don't forget to check out the recording of this week's Orchard meeting!

Parlot, Make deployment steps orderable - This week in Orchard (07/03/2021)

This week you can meet with Parlot, which is a fast, lightweight, and simple to use .NET parser combinator! Check out our post for the orderable deployment steps, the improvements of the Kast platform, and many more! Orchard Core updates Make deployment steps orderable This is about making deployment steps orderable in the UI, to allow drag and drop to get steps where you want them to be. UI only, as the choice to when steps run should be up to the user. Let's say you have plenty of plans where features don't want to be first - more common when deploying to existing sites, rather than building up recipes, but steps are for both. And now you can also find hints to the important steps that suggest they should go first, like "Content Definitions should be placed before any content steps." Update from node-sass to dart-sass This is about replacing gulp-sass with its newer gulp-dart-sass because node-saas is now deprecated and the latest node-sass doesn't compile on the latest NodeJS anymore. So, it's recommended on the gulp-sass repository be upgraded to gulp-dart-sass as node-sass is deprecated. You can read more about it in this article. Fix WorkflowBlockingActivitiesIndex table indices name length for PostgreSQL This is an interesting one, so we think this should deserve a few lines. Check out the Migrations file in the OrchardCore.Workflows module where you can see the creation of two different indices: IDX_WorkflowBlockingActivitiesIndex_DocumentId_ActivityId and IDX_WorkflowBlockingActivitiesIndex_DocumentId_ActivityName. Because of the PostgreSQL name length limit, it uses only IDX_WorkflowBlockingActivitiesIndex_DocumentId_Activity for both which causes an exception. The fix is just to reduce the length of these indices. Add Properties to SetupContext There is a SetupContext class that had some properties like SiteName, AdminUserName, AdminUserId, etc. This SetupContext class will be prepopulated by the setup screen and then passed to ISetupHandler. But now the ISetupHandler accepts an IDictionary to work with these properties. But why is it useful? When setting up a tenant or site sometimes you need to pass in some custom data and use it in your setup recipes. Like when a user registers on a site and he submits a form with firstname, lastname, etc. We then call a workflow that creates the tenant and executes a setup recipe. In this setup recipe, we could create a landing page and we want to assign the firstname, lastname to be set as the displaytext of the landing page content item. Or another use case would be to populate the custom user profile settings during setup. So, from now, the developers can populate the Properties bag from his workflow task or a custom setup screen if they would like to. You can see a good example in the ExecuteAsync method of the SetupTenantTask. Demos Parlot The Shortcodes repository by Sébastien Ros contains a Shortcodes processor for .NET with a focus on performance and simplicity. And now that Shortcodes processor is updated to use a new parser called Parlot. Parlot is a French pronunciation of the word like chat or someone who talks a lot. In French, you write it parlotte. Parlot is a hand-written parser for Shortcodes and that parser is now extracted to make it reusable. You can find adding and using Parlot in the Shortcodes module in this PR. If you check out that pull request, you will see that before this PR we had the Character.cs, Cursor.cs classes. Now they aren't here anymore, they are in the package. The code is almost the same. Now we have a ShortcodesParser.cs that is using Parlot. And in this file, you can find the grammar of Shortcodes. A text is based on shortcode and TEXT nodes. A Shortcode can have an identifier and arguments. An argument is like identifier equals value. It's actually could be just a value if you want. And a value is either a string or a number. This class contains a bunch of first-level methods like ParseNode, ParseRawText, and so on. If you check out the JSON benchmarks of Parlot, you will see a nice table about the performance of Parlot. And in that table, you can see the performance of parsing JSON documents. As you can see, it is ten times faster than something like Sprache, which is a famous parser. Pidgin has been created to be faster than Sprache and now Parlot is faster than Pidgins. If you take a look at the allocations, you can see that they are equal because it's just about allocating JSON. This benchmark creates an expression tree (AST) representing mathematical expressions with operator precedence and grouping. Same thing here. This table is about comparing the low level, the fluent API, and Piding. Even the Fluent API is five times faster than Pidgin. And in terms of allocation, it's a little bit better than Pidgin. Here you can see a demo video about Parlot and a lot more than that! Like stories about the NCalc library that is created by Sébastien Ros 10 years ago. NCalc is a mathematical expressions evaluator in .NET. NCalc can parse any expression and evaluate the result, including static or dynamic parameters and custom functions. And that library is used by the Sprache.Calc library. Sprache.Calc provides easy to use extensible expression evaluator based on the LinqyCalculator sample. The evaluator supports arithmetic operations, custom functions, and parameters. It takes a string representation of an expression and converts it to a structured LINQ expression instance which can easily be compiled to an executable delegate. In contrast with interpreted expression evaluators such as NCalc, compiled expressions perform just as fast as native C# methods. We can fill up the whole This week in Orchard post just by these libraries and the story behind Parlot. Or we can start to describe how the parser works and how you can extend it with your own implementations, but this may no longer be closely related to the topic of this series. So, as we just mentioned before: if you are interested in these topics, this will be your presentation! Resource Zones, Resource Layers, Resource Widgets Kast is an Australian company and one of their primary goals is to implement the Kast platform with the Kast Group Finder component. We worked together with Seth Cleaver (Co-founder and Director of Kast) on this tool to be able to create an intuitive self-service process that enables people within a church to easily find a suitable group to attend, simplify the administrative processes required for getting people into groups, and provide information to the group co-ordinators that might assist in planning and measuring effectiveness. Check out this case study about how we've developed this multi-tenant social group management platform for churches! The Kast platform is growing from time to time and this time you could see an improvement from Dean Marcussen which is about providing ways to edit static resources (like JavaScript and CSS) using the admin UI. The exact issue in GitHub is opened by Dody Gunawinata a while ago about the downside of the current theme system is that to change anything will require deployment. The way around is to include the JS/CSS in a template and include them in every other template. Check out the following recording for a possible solution! It didn't seem like the design was wanted for Orchard Core itself, so it will probably remain private at this stage. But if the people wanted it, it might be possible to make it available at some point as a contribution module. News from the community Orchard Dojo Newsletter Lombiq's Orchard Dojo Newsletter has 190 subscribers! We have started this newsletter to inform the community around Orchard with the latest news about the platform. By subscribing to this newsletter, you will get an e-mail whenever a new post published to Orchard Dojo, including This week in Orchard of course. Do you know of other Orchard enthusiasts who you think would like to read our weekly articles? Tell them to subscribe here! If you are interested in more news around Orchard and the details of the topics above, don't forget to check out the recording of this week's Orchard meeting!

Reimplement batching in YesSql, Azure Application Insights module - This week in Orchard (26/02/2021)

Refactoring the Content zone, reimplement batching in YesSql, hiding Setup recipes, adding more indexes to index tables, Azure Application Insights module and a lot more is waiting just for you in this post! Orchard Core updates Do not display Setup recipes in the admin UI You can easily list all of the existing recipes of your site under Configuration -> Recipes. If you create a recipe, like myrecipe.recipe.json, and put it in a Recipes folder of one of your modules, that recipe will be listed on this page. Let's add a simple one and check out the content of that page. You can see that our recipe with the display name My Recipe is on the list but wait! What happened with the other recipes? We have several other built-in recipes like the Blog, the Agency, and so on. Where are they? From now the logic in the Index method of the AdminController in the OrchardCore.Recipes module is slightly changed. If the recipe is a setup recipe (defined in the issetuprecipe of the JSON file) or has the hidden tag, the recipe will not appear in that list. You can see that our myrecipe has the false value for the issetuprecipe but the Blog recipe has the true value. Admin Dashboard Widgets We mentioned the Admin Dashboard widgets two times in This week in Orchard that allows you to add cards to the homepage of the dashboard, which is about to represent a piece of functionality of a given feature or module. You can find the first post here and the second one here. And now this feature has been merged to the dev branch of Orchard Core and there is also a new page in the Orchard Core Documentation about this module. It hasn't got too much information yet but the embedded recording could help you to see this feature in action. If you haven't heard about this feature, don't forget to check out the previous blog posts and the demo video! Use DocumentId in indexes This fix is also about using the correct combination of fields for each index that we use. For example, the index for the UserIndex table now contains every field of the table. So, from now you will find indexes for every index table. Refactor ContentZone - Tabs, Card, Column containers With more and more dynamic shapes coming into Orchard Core (all the tab/card grouping shapes, and also shapes like the ContentCard) it would be potentially useful to have a ShapesViewModel that could be used as a hard type abstract class to create specific targetted ViewModels, i.e. a TabViewModel. The problem with the ShapeViewModel is it doesn't support a list of positioned Items, so right now we are using Shape because many of these tab/card/content card shapes require a list of positioned items as well. So we was not intending that a ShapesViewModel was actually an IShape, just an abstract that could be used to build shapes with, and the mixin would still turn it into an IShape, by mixing in the ShapeViewModel which has all the required IShape properties. The idea being you could then use the IShapeFactory to create a shape, then add items to it. var shape = ShapeFactory.CreateAsync<TabViewModel>("Tab", m =>{ m.TabId = tabId});foreach(var item in thetabs){ shape.Add(item);} The current usage builds 3 different shapes, even when displaying on the front end, and there are no groups. Here we do a quick check first, to see if there are any groups, and if there are none, just render the zone directly. No extra shapes. Also Removes uses a dynamic, and moves to hard typed models. Uses ToLookup instead of GroupBy (better usage). Uses fewer dynamics for injected properties, i.e. DisplayAsync can be resolved directly to its interface instead of being dynamic (this needs evaluation, as we might do it for the other C# Shape Attributes, or not). If you check out the modifications of the ZoneShapes class, you will find the implementation of the ContentZone shape and building the TabContainer shape by using the new GroupingsViewModel. And by having the GroupingsViewModel ViewModel we can use that instead of a dynamic one as you can see in the TabContainer.cshtml file. Demos YesSql: Reimplement batching When we do updates on the content items and when one deletes a document all the index tables corresponding to the document type get a DELETE query. And this DELETE query is actually a single DELETE query. If we see a benchmark we can realize that the batching isn't working. So, if you would like to delete multiple content items, the code is just about to send multiple DELETE requests instead of sending one. If you run the query locally using SQL Server and you have 1000 indexes, it takes one second. So, you might not notice that it's slow. It's slow but it could look like that's because we have several indexes. Extra Indexes: 1, Elapsed 00:00:00.0371806Extra Indexes: 100, Elapsed 00:00:00.1317780Extra Indexes: 200, Elapsed 00:00:00.2826214Extra Indexes: 500, Elapsed 00:00:00.6213360Extra Indexes: 1000, Elapsed 00:00:01.2628232 But when you have the SQL Server in Azure, the deletion of 1000 indexes could take 28 seconds. At this point, you will start noticing the performance just after 100 indexes. Apparently, we have around 20 indexes in Orchard Core but you can quickly arrive at 100. Extra Indexes: 1, Elapsed 00:00:00.0838498Extra Indexes: 100, Elapsed 00:00:02.5644737Extra Indexes: 200, Elapsed 00:00:05.0814065Extra Indexes: 500, Elapsed 00:00:12.6558409Extra Indexes: 1000, Elapsed 00:00:28.6622291 In YesSql, when you update a document, it needs to update all the indexes, which means it will rebuild them. And an index can return multiple records like if I'm indexing the name of someone, I might want to index the first name, the last name, the middle name. These are three records, one per name. So, what it does is, it builds the three records in memory and it will send a query to delete any record that was associated with the document and then sends three inserts for the new records. So you have one for the delete and three for the inserts. But it sends a delete even for the indexes that didn't return anything because the fact that you didn't return anything might mean that there is nothing anymore associated. So you need to delete and send nothing. This way we get a delete per index. That's not optimal but that's how YesSql works and that's optimal for reads and not writes. To goal is to make reads faster than writes. Still, we should not have to send one independent query to do the deletes per index. That's what this issue is about. And Sébastien Ros managed to fix it. And here are some numbers. These are all for dummy indexes that never perform any writes. Just cause deletes. What we can see in the profiler is that everything easy to batch is batched together. The big batch with mostly deletes, and a couple of inserts, is now long-running, instead of many many short runs. Probably from the look of it, the insert is still expensive, so which pushes up the time run. Before batchingLocal Indexes 1, Elapsed 00:00:00.3004236 Indexes 100, Elapsed 00:00:00.2375774 Indexes 200, Elapsed 00:00:00.3583902 Indexes 500, Elapsed 00:00:00.7695818 Indexes 1000, Elapsed 00:00:01.2836934Remote Indexes 1, Elapsed 00:00:00.7207663 Indexes 100, Elapsed 00:00:03.3552247 Indexes 200, Elapsed 00:00:05.5547927 Indexes 500, Elapsed 00:00:13.8364514 Indexes 1000, Elapsed 00:00:27.2306443After BatchingLocal Indexes 1, Elapsed 00:00:00.2207824 Indexes 100, Elapsed 00:00:00.0910920 Indexes 200, Elapsed 00:00:00.1632908 Indexes 500, Elapsed 00:00:00.4007200 Indexes 1000, Elapsed 00:00:00.4559752RemoteIndexes 1, Elapsed 00:00:03.6326000Indexes 100, Elapsed 00:00:04.9639312Indexes 200, Elapsed 00:00:09.8273422Indexes 500, Elapsed 00:00:16.1340951Indexes 1000, Elapsed 00:00:15.2008296 And how it looks like is the following. If you create a blog post in Orchard Core and after the change, there is a single communication to the database that contains everything that you can see here. This contains creating the Document, creating the ContentItemIndex, updating the ContentItemIndex with the DocumentId, and so on. These are three indexes to update (ContentItemIndex, ContainedPartIndex and AutoroutePartIndex). insert into [Document] ([Id], [Type], [Content], [Version]) values (19, 'OrchardCore.ContentManagement.ContentItem, OrchardCore.ContentManagement.Abstractions', '{"ContentItemId":"4cpw0fnmjb1kp07dmzxx8n8ecg","ContentItemVersionId":"4953p18bj3gyy5yy82f7mj7w4y","ContentType":"BlogPost","DisplayText":"The title","Latest":true,"Published":false,"ModifiedUtc":"2020-12-31T00:31:34.3346095Z","PublishedUtc":null,"CreatedUtc":"2020-12-31T00:31:34.3346095Z","Owner":"48v9vt5vxznr5z9m1df9zmvjm8","Author":"admin","TitlePart":{"Title":"The title"},"AutoroutePart":{"Path":"blog/the-title","SetHomepage":false,"Disabled":false,"RouteContainedItems":false,"Absolute":false},"BlogPost":{"Subtitle":{"Text":"Subtitle"},"Image":{"Anchors":[],"Paths":[],"MediaTexts":[]},"Tags":{"TagNames":["Space"],"TaxonomyContentItemId":"4ykev5wxfcny7tvsahz9y64mwe","TermContentItemIds":["4nv0z7r24r1vw3sfpq7t6xws59"]},"Category":{"TaxonomyContentItemId":"4tpy2wv97bkbf0zkx8tyd1bm4q","TermContentItemIds":["4bsstr09f29rp0sgy85n9f07wj"]}},"MarkdownBodyPart":{"Markdown":"Some text"},"ContainedPart":{"ListContentItemId":"491emynv0kavbzhy40xmqv1wds","Order":0}}', 1);insert into [ContentItemIndex] ([ContentItemId], [ContentItemVersionId], [Published], [Latest], [ContentType], [ModifiedUtc], [PublishedUtc], [CreatedUtc], [Owner], [Author], [DisplayText]) values ('4cpw0fnmjb1kp07dmzxx8n8ecg', '4953p18bj3gyy5yy82f7mj7w4y', 0, 1, 'BlogPost', '2020-12-31T00:31:34', '', '2020-12-31T00:31:34', '48v9vt5vxznr5z9m1df9zmvjm8', 'admin', 'The title') ; select last_insert_rowid() [Id];update [ContentItemIndex] set [DocumentId] = 19 where [Id] = (last_insert_rowid());insert into [ContainedPartIndex] ([ListContentItemId], [Order]) values ('491emynv0kavbzhy40xmqv1wds', 0) ; select last_insert_rowid() [Id];update [ContainedPartIndex] set [DocumentId] = 19 where [Id] = (last_insert_rowid());insert into [AutoroutePartIndex] ([ContentItemId], [Path], [Published], [Latest], [ContainedContentItemId], [JsonPath]) values ('4cpw0fnmjb1kp07dmzxx8n8ecg', 'blog/the-title', 0, 1, '', '') ; select last_insert_rowid() [Id];update [AutoroutePartIndex] set [DocumentId] = 19 where [Id] = (last_insert_rowid()); When we create a blog post there aren't any delete requests, because there is nothing before but when we update we need to delete all the indexes and these calls contains unnecessary deletes because for example there is no LayerMetaDataIndex on the blog post but the logic here is hey, we are dealing with a content item and there is an index for all of the content items called LayerMetadataIndex. So it will send the delete just in case which is dumb. But you can see here for each index we have a delete and it used to be a single query communication query with the database. Now let's divide by two the part where we insert and update the indexes. Instead of having to insert something then update something for each index now, we have just inserted it. We still have all the deletes and some of them are still useless but these are quick. So we have just this request now when we update a single blog post. insert into [Document] ([Id], [Type], [Content], [Version]) values (23, 'OrchardCore.ContentManagement.ContentItem, OrchardCore.ContentManagement.Abstractions', '{"ContentItemId":"4cpw0fnmjb1kp07dmzxx8n8ecg","ContentItemVersionId":"4ypdrxm7xbndr0dvcpwaraa95g","ContentType":"BlogPost","DisplayText":"The title","Latest":true,"Published":false,"ModifiedUtc":"2020-12-31T05:55:56.8113646Z","PublishedUtc":"2020-12-31T01:22:37.7926461Z","CreatedUtc":"2020-12-31T00:31:34.3346095Z","Owner":"48v9vt5vxznr5z9m1df9zmvjm8","Author":"admin","TitlePart":{"Title":"The title"},"AutoroutePart":{"Path":"blog/the-title","SetHomepage":false,"Disabled":false,"RouteContainedItems":false,"Absolute":false},"BlogPost":{"Subtitle":{"Text":"Subtitle"},"Image":{"Anchors":[],"Paths":[],"MediaTexts":[]},"Tags":{"TagNames":["Space"],"TaxonomyContentItemId":"4ykev5wxfcny7tvsahz9y64mwe","TermContentItemIds":["4nv0z7r24r1vw3sfpq7t6xws59"]},"Category":{"TaxonomyContentItemId":"4tpy2wv97bkbf0zkx8tyd1bm4q","TermContentItemIds":["4bsstr09f29rp0sgy85n9f07wj"]}},"MarkdownBodyPart":{"Markdown":"Some text"},"ContainedPart":{"ListContentItemId":"491emynv0kavbzhy40xmqv1wds","Order":0}}', 1);delete from [ContentItemIndex] where [DocumentId] = 22;delete from [AliasPartIndex] where [DocumentId] = 22;delete from [LayerMetadataIndex] where [DocumentId] = 22;delete from [ContainedPartIndex] where [DocumentId] = 22;delete from [AutoroutePartIndex] where [DocumentId] = 22;delete from [TaxonomyIndex] where [DocumentId] = 22;insert into [ContentItemIndex] ([ContentItemId], [ContentItemVersionId], [Published], [Latest], [ContentType], [ModifiedUtc], [PublishedUtc], [CreatedUtc], [Owner], [Author], [DisplayText], [DocumentId]) values ('4cpw0fnmjb1kp07dmzxx8n8ecg', '4q3271jp1705etwnf52c0nnbwz', 1, 0, 'BlogPost', '2020-12-31T01:22:37', '2020-12-31T01:22:37', '2020-12-31T00:31:34', '48v9vt5vxznr5z9m1df9zmvjm8', 'admin', 'The title', 22) ; select last_insert_rowid() [Id];insert into [ContainedPartIndex] ([ListContentItemId], [Order], [DocumentId]) values ('491emynv0kavbzhy40xmqv1wds', 0, 22) ; select last_insert_rowid() [Id];insert into [AutoroutePartIndex] ([ContentItemId], [Path], [Published], [Latest], [ContainedContentItemId], [JsonPath], [DocumentId]) values ('4cpw0fnmjb1kp07dmzxx8n8ecg', 'blog/the-title', 1, 0, '', '', 22) ; select last_insert_rowid() [Id];insert into [TaxonomyIndex] ([TaxonomyContentItemId], [ContentItemId], [ContentType], [ContentPart], [ContentField], [TermContentItemId], [DocumentId]) values ('4ykev5wxfcny7tvsahz9y64mwe', '4cpw0fnmjb1kp07dmzxx8n8ecg', 'BlogPost', 'BlogPost', 'Tags', '4nv0z7r24r1vw3sfpq7t6xws59', 22) ; select last_insert_rowid() [Id];insert into [TaxonomyIndex] ([TaxonomyContentItemId], [ContentItemId], [ContentType], [ContentPart], [ContentField], [TermContentItemId], [DocumentId]) values ('4tpy2wv97bkbf0zkx8tyd1bm4q', '4cpw0fnmjb1kp07dmzxx8n8ecg', 'BlogPost', 'BlogPost', 'Category', '4bsstr09f29rp0sgy85n9f07wj', 22) ; select last_insert_rowid() [Id];update [Document] set [Content] = '{"ContentItemId":"4cpw0fnmjb1kp07dmzxx8n8ecg","ContentItemVersionId":"4q3271jp1705etwnf52c0nnbwz","ContentType":"BlogPost","DisplayText":"The title","Latest":false,"Published":true,"ModifiedUtc":"2020-12-31T01:22:37.6390174Z","PublishedUtc":"2020-12-31T01:22:37.7926461Z","CreatedUtc":"2020-12-31T00:31:34.3346095Z","Owner":"48v9vt5vxznr5z9m1df9zmvjm8","Author":"admin","TitlePart":{"Title":"The title"},"AutoroutePart":{"Path":"blog/the-title","SetHomepage":false,"Disabled":false,"RouteContainedItems":false,"Absolute":false},"BlogPost":{"Subtitle":{"Text":"Subtitle"},"Image":{"Anchors":[],"Paths":[],"MediaTexts":[]},"Tags":{"TagNames":["Space"],"TaxonomyContentItemId":"4ykev5wxfcny7tvsahz9y64mwe","TermContentItemIds":["4nv0z7r24r1vw3sfpq7t6xws59"]},"Category":{"TaxonomyContentItemId":"4tpy2wv97bkbf0zkx8tyd1bm4q","TermContentItemIds":["4bsstr09f29rp0sgy85n9f07wj"]}},"MarkdownBodyPart":{"Markdown":"Some text"},"ContainedPart":{"ListContentItemId":"491emynv0kavbzhy40xmqv1wds","Order":0}}', [Version] = 1 where [Id] = 22;insert into [ContentItemIndex] ([ContentItemId], [ContentItemVersionId], [Published], [Latest], [ContentType], [ModifiedUtc], [PublishedUtc], [CreatedUtc], [Owner], [Author], [DisplayText], [DocumentId]) values ('4cpw0fnmjb1kp07dmzxx8n8ecg', '4ypdrxm7xbndr0dvcpwaraa95g', 0, 1, 'BlogPost', '2020-12-31T05:55:56', '2020-12-31T01:22:37', '2020-12-31T00:31:34', '48v9vt5vxznr5z9m1df9zmvjm8', 'admin', 'The title', 23) ; select last_insert_rowid() [Id];insert into [ContainedPartIndex] ([ListContentItemId], [Order], [DocumentId]) values ('491emynv0kavbzhy40xmqv1wds', 0, 23) ; select last_insert_rowid() [Id];insert into [AutoroutePartIndex] ([ContentItemId], [Path], [Published], [Latest], [ContainedContentItemId], [JsonPath], [DocumentId]) values ('4cpw0fnmjb1kp07dmzxx8n8ecg', 'blog/the-title', 0, 1, '', '', 23) ; select last_insert_rowid() [Id]; Using the other PR we are able to teach the index when not to send deletes as there will never be any index related to this Document, so don't send deletes. After that, you can see there is no more LayerMetaDataIndex and no more AliasPartIndex calls because the blog post doesn't have these. delete from [ContentItemIndex] where [DocumentId] = 23;delete from [ContainedPartIndex] where [DocumentId] = 23;delete from [AutoroutePartIndex] where [DocumentId] = 23;delete from [TaxonomyIndex] where [DocumentId] = 23; This is by having a new method on the IndexProvider to explain when to not use the IndexProvider. So it would not even go to the Map method. In this case, we say if you don't have the AliasPart, don't use the IndexProvider. context.For<AliasPartIndex>() .When(c => c.Has<AliasPart>()) .Map(contentItem => Check out the following recording on YouTube to know more about this YesSql improvement! News from the community Lombiq Hosting - Azure Application Insights This new Orchard Core module from Lombiq enables easy integration of Azure Application Insights telemetry into Orchard. Just install the module, configure the instrumentation key from a configuration source (like the appsettings.json file) as normally for AI, and collected data will start appearing in the Azure Portal. Would like to learn more about our new module? Then head to the repository now where you can find every detail about how to set up and use that module in your site! Orchard Dojo Newsletter Lombiq's Orchard Dojo Newsletter has 191 subscribers! We have started this newsletter to inform the community around Orchard with the latest news about the platform. By subscribing to this newsletter, you will get an e-mail whenever a new post published to Orchard Dojo, including This week in Orchard of course. Do you know of other Orchard enthusiasts who you think would like to read our weekly articles? Tell them to subscribe here! If you are interested in more news around Orchard and the details of the topics above, don't forget to check out the recording of this week's Orchard meeting!