Orchard Core Beta 3 is here! Read our post about the latest improvements of the new release and a way about how we could store static media files in the future using Orchard Core! On Orchard Core Orchard Core Beta 3 release We are proudly announcing that the community shipped Orchard Core Beta 3 release! Head to the GitHub page of Orchard Core to download the assets of the new release, where you can also find the list of changes for this release! Fixing DataAnnotations localization Using the PO file string localizer, users can't localize data annotations. When you have a model with some Required or DisplayName attributes, the value of the Error Message will be translated automatically using the StringLocalizer. In Orchard 1.x it never worked, but in Orchard Core, it supposed to work because it's using the correct service for translating which Orchard implements. The IStringLocalizer is created by a StringLocalizerFactory. The question is should a StringLocalizer know about the culture supposed to render or is it when we call _localizer.GetString that we should check what is the current culture? Our assumption was that the localizer should be localize for a specific culture because it had a method called WithCulture to return a new one for another culture. But actually, it's not working in this way. In our case is that we were creating a localizer for the current culture (like English, because it's our default), then we go on a page for Spanish, and it will still reuse the same localizer. Now we do the culture resolution when the GetString is called on the instance. It worked in the case of views, because the views get a new StringLocalizer instance every time they rendered, but not when the attributes are evaluated. It's important because this package is not just for the Orchard Core Framework or the CMS, it's a NuGet package, that anyone can use to localize their ASP.NET applications based on PO files. We support pluralization and dynamic PO files too. How to create Media content items in Orchard Core? In Orchard Core media are not content items, they are just files with a path. The Media Field is just a list of path to a file that is handled by the media storage. Most of the time we don't need a content item for the media. It's faster to store just the path and then to load the content item to store the data and render an image. But we can have Media content items by just create a content type (call it Image, Video or whatever) and attach a Media Field to that. Then you can have content pickers on your other content items to select this kind of media. Localization files guide Last week we wrote about that the new localization guide has been added to Orchard Core documentation. And we have good progress on some translations! As you can see, the Arabic is over 40%, the Chinese more than 80% ready! And we have just added the Orchard Core project to Crowdin! The documentation just has the dev version Now when you visit the ReadTheDocs page of Orchard Core, you will not see the option to read the latest or the dev version of the documentation, only the dev version is available. There can't be any misunderstanding about which branch you are targeting, we always show the latest branch (dev branch), and if we have documentation that only relates to the dev branch, we should make some notes in the documentation to say this won't work with the master. Improve the create button on List Part When you have only one content type attached to a List Part, then instead of having a drop down to select it, there is a button to create the given content type. Storing static Media By using the IFileStore, you can get files, store documents into a file store, etc. The IMediaFileStore implements IFileStore and provides two additional methods: MapPathToPublicUrlKnowing a path for media (like /images/thumbnail.png), this method will return the public URL that we need to render when we render a media. This file store can serve the correct media when it's requested. It's a bridge between an HTTP request to the file that is contained by the store. The default one is using the file system implementation (the folder), so it's just saying whenever you want to access /images/thumbnail.png, it will return /media/images/thumbnail.png. MapPublicUrlToPathThis is the opposite. When we have a public URL, can you guess which image it will present? This is done for a very specific reason. If we have an Azure Blob Storage, we want to render not the local path to the image, but the path to Azure Blob Storage. When we have an image to the media manager, it will store the image for example on Azure Blob Storage and then when we get the path for this media from a content item we need to find what is the public URL of this media in Azure. So the custom implementation for Azure Blob Storage will return http://mystorageaccount.blob.core.windows.net/mycontainer/myblob. The next step would be to remove the whole IMediaFileStore interface. Therefore we don't have to care about where publicly the media is accessible.What about serving the Blob Storage from our web server? When we go in /media/myimage.png, the path will be: "myimage.png". In case of an Azure Blob Storage, the public URL will be http://mystorageaccount.blob.core.windows.net/mycontainer/myimage.png. This is the URL, where the browser finds the image. When /media/myimage.png is requested, Orchard will download the blob (corresponding to that from Azure, and either render it directly on the request or save it as a cache locally) and serve it for the next request without having to ask for Azure. In terms of perf, it won't be slow and it opens other possibilities: 1. Resizing images If we do a resize, we could use the following URL: http://mystorageaccount.blob.core.windows.net/mycontainer/myimage.png?width=123 But this won't work, because if we do something like this, the server of Azure doesn't know about ImageSharp or whatever. This thing can only be done if we server the image by ourself. To be able to resize images, we need to process them, that means we need to load them and serve them not from a public URL. 2. CDNs Content Delivery Networks are local caches of the files we request. Some people use Azure Blob Storage because they want CDNs. But we can use CDNs even for our local images. The idea is to use CDNs even for Blob Storage. We could say that yes, we have Blob Storage, but just for storage, it's not for serving the images. We could also make it completely private, so nobody can access our Azure Blob Storage. It's the Orchard web server that will download the media, serve it in a way (/media/images/thumbnail.png) and if we have a CDN, then the CDN itself is responsible for asking us for the media (which will download from Blog Storage, so it will be slower for the first time), but then every time the users will hit the CDN, then they will get the image from the CDN, not from us. We won't even serve the image. Instead of having an interface, that gives us a public URL, every time we render a media is to have a configuration setting with the public CDN prefix for media files. In this configuration, we would define //myownsite.azurecdn.com. What you would do is configure Azure CDN to be a CDN on your website which is mywebsite.com. And whenever we render a media file, we would prefix the URL with that. The browser will ask the CDN for the resource, which will itself ask our site for the resource and then get the blob storage thing. We serve it, that means it will be resizeable, even with a CDN. If we make a request like //myownsite.azurecdn.com/foo.png?width=123, it will request that on our server, so we can resize everything. That would work with any CDN, like Azure CDN, Akamai, Horizon, CloudFlare, etc. Another option is to add a configuration setting with public CDN for resources. When we define a resource, we can set the debug version and the minified version, and also the CDN version. And we also have a setting to say I want to use a CDN version of my resources instead of the local ones (if they are available). We could also be able to set the CDN prefix for these files too, and then they would be also cached by a full CDN and the site would be much faster. However, we need to purge ImageSharp cache automatically with some thresholds. If we cache media items, need to have a way to purge. That's doable because every time we store a media we can store how much data we already stored like in a file that is also on the file system. There are many ways to do that. There is a website using the Blog recipe under sebastienrosdotcom.azurewebsites.net. It's hosted on the east coast, if we look at the Network tab using Google Chrome Developer Tools, it takes about 150 ms to render the page in Hungary, plus the static files, which are either in CDN or loaded by Orchard Core. Seb enabled the CDN and mapped this website to cdn.sebastienros.com. In the first time, it will be slower, but after if you hit F5, it takes less ms. On Lombiq Orchard Dojo Newsletter Now we have 56 subscribers of the Lombiq's Orchard Dojo Newsletter! 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 every time when 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!