Blog

Migrating a standalone Orchard site to DotNest - Show Orchard case study

Show Orchard is a website for showing representative Orchard CMS (and now Orchard Core) websites all around the internet. It was started by Ryan Drew Burnett, but since he doesn't work with Orchard anymore, as announced earlier it is now maintained by our team at Lombiq Technologies. Our first task was to migrate it to DotNest, so here we are, see how we did it using basic Orchard features and Media Theme. Update as of 16.10.2023: Show Orchard now runs on Orchard Core, and you can check out the corresponding more recent case study here. This is a guest post by Márk Bartha, software engineer at Lombiq. Click here to open the repository page of the theme project. Original theme: RDB.ShowOrchard.zip Original module: RDB.Showtime.zip How to start moving your standard Orchard website to DotNest So let's say you already have a standard Orchard website with a custom theme and maybe some simple custom modules and you want to move it to DotNest. You probably have a couple of content types (e.g. Pages, BlogPosts or other custom types created in Migrations) that you need to recreate. So the first step would be to use the ImportExport module to create an export file that includes everything you need on your new Orchard website. And then do some planning and find out what other widgets, queries etc. you need to have the same functionality as you implemented in your standard Orchard theme and modules. After that you can head over to DotNest and create your site. After setting up the site, importing your recipe and creating your custom items you can start theming. We did the same too (see the ShowOrchard.recipe.xml file in the Recipes folder of the theme repository) so now we have the following: Category TaxonomyTerm to categorize Websites, Website content type with TitlePart, AutoroutePart, BodyPart and WebsitePart that includes the following fields: Category: TaxonomyField for selecting Category term Developer: LinkField to reference the developer or company that created the website Interview: BooleanField that indicates whether the developer or company has been interviewed about their website, it will be used for a Query on a Page to show Screenshot: MediaLibraryPickerField to display a screenshot of the website Website: LinkField to reference the actual website configured PageSize on SiteSettingsPart, index for categories which is configured in the SearchSettingsPart and there are commands for initialization, different Queries for Websites, Interviews and there are two special ones for the "Website Stepper" which will be described later in the article, and finally the actual content items from the old site including Websites, Layers etc. After you have all the content types and queries created on DotNest you are ready to start Media Theme development. In your theme you probably had some custom shapes, mostly shape overrides so you need to recreate them using Liquid. When you are done with that and all seems good you can start working on your custom implementations if necessary. These custom implementations replace the simple features coming from custom modules with basic Orchard features or some code that works in Razor but won't work in Liquid. Let's see some interesting features that were on the original Show Orchard websites and how we implemented them on DotNest. Related Websites widget If you check any Website on Show Orchard, you will see a grid on the bottom containing similar websites in the database. To implement this we needed to create a Query that will search for websites with the same category as the one currently displayed but somehow exclude the currently displayed one. Search filter was used with the {Request.Content.Fields.WebsitePart.Category.Terms} search query to match the category, ContentTypes filter to match the Website content type and CreatedUtc filter to exclude the currently displayed Website. This last one is a workaround because there is no way to filter for content IDs so we had to filter for the creation date. Which also was tricky since Orchard will localize any date you give in the form of the filter so when you give {Request.Content.Date} in the input the evaluated date will be UTC but it will be localized by the filter. Unfortunately, this won't work because the localized date can't be compared to the UTC create date of other Websites so the currently displayed website will be in the query result - except if the timezone set in Orchard is UTC. The solution was to use intervals and knowing that at most one website will be added per day we could exclude all items that has been created between {Request.Content.Date.Format:yyyy-MM-dd} 0:00 and {Request.Content.Date.Format:yyyy-MM-dd} 23:59. For the layout we used an unordered HTML list where we specified the RelatedWebsite as display type. LayoutWidget was used to display the Query results. Also related-websites class was added for styling purposes. The layout itself was pretty simple, the QueryElement was added inside the grid layout elements. Website stepper Another exciting challenge was to add two links that will display the title of the website created before and after the currently displayed website - which actually behaves like "Next" and "Previous" pager buttons. Our solution was to use two different queries for those. One for filtering websites created before the currently displayed site and one for after. The problem was the same as before, we couldn't use the {Request.Content.Date} alone since the timezone conversion would change it and one of the query results might contain the currently displayed content item too. The final solution was similar as the one used for the Related Websites, we used the {Request.Content.Date.Format:yyyy-MM-dd} 0:00 and {Request.Content.Date.Format:yyyy-MM-dd} 23:59 parameters. We used raw layout with "WebsiteStepperNext" and "WebsiteStepperPrevious" display types given so we could easily override the Query results. LayoutWidgets were used to render the first item found in each query. In the Content-Website.WebsiteStepperNext.liquid and Content-Website.WebsiteStepperPrevious.liquid we overrode the content item shapes to contain only the title as links to the websites. <a href="{% Href Model.ContentItem.AutoroutePart.DisplayAlias %}" class="website-stepper next-stepper btn btn-default pull-right"> {{ Model.ContentItem.TitlePart.Title }} → </a> Displaying interviews in a custom zone If a website has interview content we needed to display it right after the content metadata, however, we couldn't use the Content zone because it was displayed right before the Meta zone. In Liquid we could easily display custom zones. After this we can target shapes to this zone in the Placement.info. This is the content of the Content.Website-Detail.liquid file: <article class="content-item content-item-detail website website-detail"> <header class="content-item-header"> {{ Model.Header | Display }} </header> {{ Model.Content | Display }} <div class="content-item-meta"> {{ Model.Meta | Display }} </div> {% if Model.Interview != null %} {{ Model.Interview | Display }} {% endif %} </article> Using Bootstrap classes everywhere As you may noticed the previous markups all contain Bootstrap classes. To use Bootstrap classes we needed to override a few more shapes. The most interesting ones are the Navigation-related shapes because these are pretty tricky ones even in the original .cshtml files. The menu name was "Main Menu" so we could use the related alternate. The Menu-main-menu.liquid shape was pretty simple, the DisplayChildren liquid tag could be used on the Model object using nav tag and the Bootstrap nav class. <nav> <ul class="nav"> {{ Model | DisplayChildren }} </ul> </nav> The MenuItem-main-menu.liquid shape was a bit more complicated because we had to add specific classes to list items and also handle multi-level menu items. {% assign activeClass = "" %} {% assign dropdownClass = "" %} {% assign subMenuItemCount = Model.Items | Size %} {% assign menuText = Model.Text | Strip %} {% if Model.Selected %} {% assign activeClass = "active" %} {% endif %} {% if subMenuItemCount > 0 %} {% assign dropdownClass = "dropdown" %} {% endif %} <li class="{{ activeClass}} {{ dropdownClass }}"> {% if subMenuItemCount > 0 %} <a class="dropdown-toggle" data-toggle="dropdown" href="{% Href Model.Item.Href %}" role="button" aria-haspopup="true" aria-expanded="false"> <span class="dd-caret">{{ Model.Item.Text }}</span> </a> {% else %} <a href="{{ Model.Item.Href }}">{{ Model.Item.Text }}</a> {% endif %} {% if subMenuItemCount > 0 %} <ul class="dropdown-menu"> {{ Model | DisplayChildren }} </ul> {% endif %} </li> Go and check further .liquid files to see how we override some more shapes such as Layout.liquid, Elements/Column.liquid and Elements/Grid.liquid etc. Using resources To include any stylesheets and shapes we used a Resources.liquid file. Here we also added some meta tags. Fortunately, it was really easy to do it using Liquid. {% SetMeta, Name: "viewport", Content: "width=device-width, initial-scale=1.0" %} {% SetMeta, HttpEquiv: "X-UA-Compatible", Content: "IE=edge,chrome=1" %} {% ScriptRequire "jQuery", head %} {% Script "~/Core/Shapes/Scripts/html5.js", head %} {% Script "~/Themes/Lombiq.PrettyGoodBootstrapBaseTheme/Content/Bootstrap/dist/js/bootstrap.min.js", head %} {% Style "//fonts.googleapis.com/css?family=Raleway:400,700" %} Conclusion Migrating Show Orchard to DotNest is a pretty good example of how to move our simple Orchard website to DotNest where we don't need to worry about deployment processes or server operations anymore. Most of the migrations can be easily done using ImportExport, however theming needs some further work on reimplementing the same markup and adjusting the styling if necessary.

DotNest is back in the Azure Marketplace - and it's in Microsoft AppSource!

A while ago DotNest was part of the Azure Marketplace - and it's back again! Actually not just there, but also in the Microsoft AppSource catalog. The Azure Marketplace is mostly aimed at businesses already using Azure. AppSource is frequently utilized by any business users to source solutions for their needs, and we believe DotNest can provide a suitable platform for a lot of web-centric business use-cases. Having these two new offers published will help drive more people to DotNest and Orchard, strengthening our ecosystem. Do you know of somebody looking for a CMS, especially a hosted one? Ping them to check out these marketplace offers!

Win a year of free custom domain usage: Still time to fill out the developer survey!

As we've written about before we ask you to tell us everything you wish DotNest would do differently. The DotNest developer survey is still on, but not for long: We'll close it on the 23rd. If you haven't filled it out here's the time, because you may also win a year of custom domain usage! Just fill out the survey and you'll take part in the draw*. We'll raffle off three full-year custom domain usage prizes (worth USD 66) and let you know in an e-mail whether you've won! (Just be sure to supply your e-mail address on the form.) If you've already filled out the survey don't worry, you're eligible to win as well of course. (*Lombiq team members and their family members may not take part in the draw.) So tell us your thoughts in the survey, enjoy an improved DotNest and win!

Conference site with custom Bootstrap template - GPU Day case study

The GPU Day conference series organized by the Wigner Research Centre for Physics of the Hungarian Academy of Sciences is dedicated to the fields of parallel and high-performance computing, visualization and data analysis in scientific and industrial applications. On the last three conferences Lombiq presented its cutting edge technology, called Hastlayer. The conference site was developed by Gábor Domonkos from Lombiq here on DotNest which is a great proof of how far you can get using only the built-in DotNest features and Media Theme for theming. This is a guest post by Gábor Domonkos, software engineer at Lombiq. Click here to open the repository page of the theme project. Requirements The goal was to have a website for the organizers of the GPU Day, that can be used to easily manage the content of the conference and list the details of every conference in the series. GPU Day requires responsive website for the attendees to be able to view it from mobile devices. Let's see what features are implemented on this site hosted on DotNest Find and apply a responsive theme for the site Create speakers Create talks and assign speaker(s) to the talks Be able to show the abstract of every talk in a modal window Have a modal window where users can watch the recorded videos of the talks using an embedded YouTube player Have a skeleton content type for every GPU Day that can be easily customized and filled with data Have an embedded Google Maps Create a contact us form where users can send message to the organizers via email Add reCAPTCHA for the contact us form Some of these features are pretty trivial to achieve in Orchard. To keep this case study exciting only the most interesting implementations will be described in-depth. Find and apply a responsive theme You can find various websites that works as a marketplace for themes and templates. GPU Day uses the Simple - Multipurpose template from WrapBootstrap.com, which is compatible with Bootstrap 3.3.x. As you can read the hints of Media Theme Deployment Configuration on the Orchard dashboard, the maximal size of the package can be 25MB and can only contain at most 250 deployable files (if you just want to store some files in the package but don't want to deploy them then put them into folders called "NotDeployed" anywhere, you can even use multiple such folders; files in there won't count). So the files of the purchased theme have to go under the NotDeployed folder, as you can see in the theme's repository. You can import the necessary third-party scripts and css files for your theme using the Layout.liquid file in the following way: {% Script "https://code.jquery.com/jquery-3.2.1.min.js" %} {% Style "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" %} {% Script "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" %} {% StyleRequire "FontAwesome" %} From now the classes predefined by the theme and other third-party libraries can be easily added to the HTML tags in the liquid files. For instance, the site can show the URL of the LinkedIn profile of the speaker, if we set up the value of this field using the Orchard dashboard. Here the social-icon class from the SimpleTemplate and the fa fa-linkedin classes from Font Awesome has been added to the <a> and <i> tags. {% if Model.ContentItem.SpeakerPart.SpeakerLinkedInProfile.Value != null %} <a href="{{ Model.ContentItem.SpeakerPart.SpeakerLinkedInProfile.Value }}" target="_blank" class="social-icon" title="LinkedIn"><i class="fa fa-linkedin"></i></a> {% endif %} Creating speakers Speakers are content types, that represent the performers of the conference and can be assigned to talks. Every speaker must have a title (which is the name of the person), a photo, a company/institute and a year. The value of the year field will be used in the projection widgets to show in which year(s) the speaker has given a presentation. Users can also add the e-mail address of the speakers. To prevent showing the original e-mail address in the front-end, the Content-Speaker.Summary.liquid file replaces the @ sign in the entered address to (_at_) text. When the user clicks on the e-mail address, the main.js file reverts this change and then the user can send the message to the valid address. Masking the addresses in the HTML is necessary to prevent the spambots to collect the e-mail addresses and send mails to the speakers. Creating talks and assigning speaker(s) to talks The talk content part has several fields attached to it. The most important is the title of the talk and the speaker(s) of the talk. Using content picker fields and set up to only pick from Speaker content types makes easier to connect talks and speakers. Showing the abstract of every talk in a modal window By clicking on the title of the talk, users can read the abstracts in a modal window. For that, the organizers can set the text of the abstract when creating the talk using an WYSIWYG HTML editor. This piece of liquid markup from the Content-Talk.Summary.liquid file shows how to open a Bootstrap modal in a liquid file. {% if Model.ContentItem.TalkPart.AbstractText.Value != null %} <a href="#" data-toggle="modal" data-target=".bs-modal-lg-{{ Model.ContentItem.TalkPart.Id }}">{{ Model.ContentItem.TitlePart.Title }}</a> {% else %} {{ Model.ContentItem.TitlePart.Title }} {% endif %} The main thing here is data-target attribute. Every conference has multiple talks, and therefore multiple modal windows. The value of this attribute shows which window has to be open when the user clicks on the title of the talk. So when defining the template of the modal window the class referenced in the data-target attribute has to be the same. <div class="modal fade bs-modal-lg-{{ Model.ContentItem.TalkPart.Id }}" tabindex="-1" role="dialog"> <div class="modal-dialog modal-lg" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button> <h2 class="modal-title">Abstract</h2> </div> <div class="modal-body"> {{ Model.ContentItem.TalkPart.AbstractText.Value }} </div> </div> </div> </div> Embedded YouTube videos in a Bootstrap modal window The main goal was to set the YouTube URL as easy as possible from the dashboard, so the organizers can set the value of the video in the common format, like: https://www.youtube.com/watch?v=mu_VWWEi-GI They no need to bother with the embedded format. If the video URL is set, the site will show a little icon, same with the abstract. {% if Model.ContentItem.TalkPart.Recording.Value != null %} <a href="#" id="video" data-id="{{ Model.ContentItem.TalkPart.Recording.Value }}" data-toggle="modal" data-target=".bs-modal-lg-{{ Model.ContentItem.TalkPart.Id }}-video"><i class="fa fa-youtube-play" aria-hidden="true"></i></a> {% endif %} When showing the embedded YouTube video we construct the modal window in the same way, but the content of the window is slightly different. <div class="modal fade bs-modal-lg-{{ Model.ContentItem.TalkPart.Id }}-video embedYouTubeModal" tabindex="-1" role="dialog"> <div class="modal-dialog modal-lg" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button> <h2 class="modal-title">Recording</h2> </div> <div class="modal-body"> <div class="embed-responsive embed-responsive-16by9"> <iframe class="embed-responsive-item"></iframe> </div> </div> </div> </div> </div> Because of the value of the recording here could be in a common YouTube video URL format, it may be necessary to convert it to be usable in an embedded way and set that value to the <iframe>. convertYouTubeUrlToEmbedMarkup: function () { $(".event-part").find("#video").on("click", function () { var youTubeUrl = $(this).data("id"); var modalWindowId = $(this).data("target"); modalWindowId = modalWindowId.substring(1, modalWindowId.length); var regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/; var match = youTubeUrl.match(regExp); if (match && match[2].length == 11) { $("." + modalWindowId).find(".embed-responsive-item").prop("src", "https://www.youtube.com/embed/" + match[2]); } }); } GPU Day skeleton content type To create a new page for the upcoming conference, the organizers have to add a new GPU Day content item. Here they can set the title, subtitle, date and many other options to fill the details of the new event. The Layout editor of this content type comes with a prefilled content. The different layout elements need to be in a given order and have different CSS classes to display in the front-end in the proper way. Therefore the organizers do not need to bother with aligning the elements and adding the necessary classes to them. They just need to select the content items and create widgets that will render content and publish their work without building the layout from scratch. Have an embedded Google Maps The Google Maps content type has an APIKey textfield which stores the Google API key. To use the Maps JavaScript API you must register your app project on the Google Cloud Platform Console, and get a Google API key which you can add to your app. The following liquid code adds the API key to the application: <div id="map" class="mb0" style="position: relative; overflow: hidden;"></div> <script src="https://maps.googleapis.com/maps/api/js?key={{ Model.ContentItem.GoogleMapsPart.APIKey.Value }}"></script> Now - by using the Google Map api v3 - it is possible to customize the embedded Google Maps. The following adds a map with a marker on it. Without going into details: new google.maps.Map() creates a new Google maps object. The center property tells the API where to center the map. The map coordinates are set in the following order: latitude, longitude. Defining a new marker with the new google.maps.Marker() puts a marker on the map. The position property sets the position of the marker. if (document.getElementById("map") && typeof google === "object") { // Map pin coordinates and content of pin box var locations = [ [ "<address><strong>Address:</strong> 29-33, Konkoly-Thege Miklos u, 1121 - Budapest, Hungary</address>", 47.487416, 18.954929 ] ]; var map = new google.maps.Map(document.getElementById("map"), { zoom: 14, center: new google.maps.LatLng(47.487416, 18.954929), // Map Center coordinates scrollwheel: false, mapTypeId: google.maps.MapTypeId.ROADMAP }); var infowindow = new google.maps.InfoWindow(); var marker, i; for (i = 0; i < locations.length; i++) { marker = new google.maps.Marker({ position: new google.maps.LatLng(locations[i][1], locations[i][2]), map: map, animation: google.maps.Animation.DROP }); google.maps.event.addListener(marker, "click", (function (marker, i) { return function () { infowindow.setContent(locations[i][0]); infowindow.open(map, marker); } })(marker, i)); } } Adding a contact us form The visitors on the site may want to send a message to the organizers. The Dynamic Forms feature is the best solution for this, since it has a Workflows activity that is fired after a dynamic form has been submitted. One can use this activity as a workflow trigger followed by an Email activity that can send the form values via email to the organizers. Adding reCAPTCHA for the contact us form Because any visitor can submit this form it's necessary to add a spam protection to it. By enabling the Anti-Spam Element feature, you have the ability to add a Re Captcha Element to the dynamic form. You can easily get a reCAPTCHA API key from Google that you can copy over to the Orchard dashboard in the form under the /Admin/Settings/Spam URL. If you liked this case study and realized how cool features you have to create a nice website, just create your own site on DotNest for free. If you have any questions, then leave a comment below.

Tell us what you need so DotNest can become the best Orchard hosting platform!

DotNest has been around for a while (4 years to be exact) and has changed a lot. We at Lombiq listened to your continuous ad-hoc feedback and improved the service where we could. This time we ask you to tell your opinion directly: What would you change to make DotNest the best developer-focused Orchard hosting platform? We've prepared a five-minute survey and it would be awesome if you could fill it out! Click here to see it, and if you tell us your e-mail address we'll address your feedback in a reply too. Thanks in advance!

GDPR compliance of DotNest and how you can make your own DotNest sites compliant

As you've surely noticed now, also from the barrage of privacy policy updates you should've received, in the EU the GDPR, or General Data Protection Regulation, takes effect today. This greatly enhances how your personal data should be protected by companies, but if you're a company, you also need to make sure you comply with it (if you manage EU citizens' data then you should, even if your company is not incorporated in the EU). DotNest itself is compliant with the GDPR; read our updated privacy policy here. However, as an owner of a DotNest site it's your responsibility to make your site compliant too. How this affects you and how we help you with this you can learn more about on this Knowledge Base article. Make sure that you check our updated privacy policy and that your company is prepared!

Show Orchard is back online, on DotNest

You may have heard of Show Orchard, but only if you've been in the Orchard CMS community for a longer time: Show Orchard is a showcase for great Orchard websites, but has been down for the better part of the last year. Now it's back online, but as a DotNest site, with a Media Theme, and maintained by Lombiq. Check it out for some of the nicest Orchard websites and if you know one that's not listed send it to guys at showorchard.com! Also, a case study is coming!

Develop themes for your DotNest sites with the DotNest SDK!

We've talked about this idea within Lombiq for quite a while and it's finally here! The DotNest SDK is the easiest way for developing themes for DotNest sites before deploying them as a Media Theme. It contains the same Orchard code base and all the open-source modules and themes on top of that as what's running on DotNest, so you can run your site locally with confidence that it's going to behave almost exactly the same as the live one. We also have a nice developer story with automatic updates from the SDK to your repository to make sure that you're able to work the latest code (make sure to read the Readme!). The DotNest SDK is available as a Git repository on GitHub and as a Mercurial repository on Bitbucket with continuous bi-directional synchronization powered by Git-Hg Mirror, another awesome Lombiq project.

New module added: Feed Aggregator

Most of the time you don't just want to have a website standing alone but you also want to hook it up with other services, social media integrations being the most basic example. Now we added the Feed Aggregator module to give you more options: It allows you to automatically sync content from RSS and Atom feeds to your DotNest site. So if you want to import content from other blogs, embed images from a remote image gallery or list videos from a YouTube channel, now you can do this easily. The new module also runs Orchard Blogs: Think of it as your Orchard-related feed reader. It automatically syncs blog posts from the Orchard blogosphere. So go ahead, create a DotNest site and check out how to sync some feeds!

New theme: Basic Blog Theme

There are a lot of modules available on DotNest, but not that many themes to style your sites. You asked for more, and we listened: the Basic Blog Theme by Márk Bartha is now available for all DotNest sites! The theme is simple but good-looking, and is (surprise!) especially well suited for styling a blog. Did you procrastinate on starting your own blog just because "nah man, DotNest still doesn't have a theme that I'd use on my blog"? You don't have to wait any more, add your blog for free!