Blog

GPU Day case study: Migrating another Orchard 1 DotNest site to Orchard Core with smart compromises for quicker implementation

We’ve already presented a case study for the Show Orchard website focusing on migrating an Orchard 1 DotNest website to Orchard Core with content migration using the Orchard 1 Recipe Migration feature; you can read that here. Now, the GPU Day website is very similar except the content migration was simplified and we didn’t overhaul the website markup and styling. It’s a great example of how you can easily transition to Orchard Core keeping everything as is if you don’t have the time or budget to modernize every area of the site. As most of what we at Lombiq do, this project is also open-source. If you’d like to follow along, you can have a look at the theme’s source code here. This is a post by Márk Bartha, software engineer at Lombiq. Introduction GPU Day is a conference organized by the Wigner Scientific Computational Laboratory that focuses on massively parallel computing, visualization, and data analysis in both scientific and industrial applications. We also presented our Hastlayer .NET hardware accelerator project many times there too. The website serves as an information hub for these annual conferences. It was initially running on Orchard 1 DotNest, but now it was time to migrate it to Orchard Core. While these migrations always come with certain challenges due to the new features introduced in Orchard Core, we tried to keep things easy by not changing the frontend of the site, even though it's somewhat outdated. Rethinking content types Each GPU Day event needed to display various types of information, such as the schedule, speakers, and location details. Previously, with Orchard 1, this process required creating separate content items for each type of information and then linking them using the Projection or Content Elements in the Layouts editor. However, Orchard Core offered a more streamlined approach and we could simplify it. First, there was no real need for the option to organize the elements' position, so we didn’t just replace it with FlowPart. We used content fields instead, for example, the "About GPU Day" section is now just an HtmlField instead of a separate content item. For organizing the event schedule, we introduced an Event Day content type to represent each day of the event, capturing its title and date. Additionally, we created an Event content type for each specific activity, detailing its time and other relevant information. There’s another content type called Event with their details along with the date and time of it. With all this information we were able to add a BagPart called Schedule to the GPU Day type and let the admins add Event Day items. Then, there was a query responsible for fetching all the Event items where their day matches with the one set to the Event Day item. The Speaker items, similarly to the Event items, are managed separately, but we also use a Taxonomy called Year. It’s the same implementation we had on the Orchard 1 site: if the Year value matches with the one selected in the Year TaxonomyField on the GPUDay type, then a query can fetch the related speakers. Migrating the old events While we didn't utilize the migration utility, we still needed to transfer all the past events. Although there weren't many content items, recreating the events manually would have required us to also create all the speakers and scheduled programs, and there were around a hundred of them. Considering these events are already in the past, the easiest solution was to create a content type called Old GPU Day with an HtmlBodyPart and simply grab the markup from the live site. The only adjustments needed were to the image URLs. The relative paths were fine since we copied the entire media library, but in the old system, they were prefixed with "https://dotneststatic.com/”. We had to replace this with a Liquid media filter (e.g., https://dotneststatic.com/media/images/wigner-logo.png became {{ "~/mediatheme/images/wigner-logo.png" | href }}). Finally, we ensured that these are rendered exactly the same as the normal GPU Day items and that the queries listing the old events capture both the old and new ones, ensuring that a visitor won’t notice any discrepancies. Keeping the original styling completely Back then, the theme was originally built on a third-party template. We implemented multiple shape templates where the markup followed the original design. With the migration, we didn’t aim to overhaul or modernize the styling. We still wanted to use our NodeJS Extensions for asset compilation, though. So, we simply copied over the old .scss files along with the third-party assets and ensured they compiled, resulting in the same styling. It was more or less straightforward; however, we had to fix a few breaking changes with the new compiler, such as using math.div() instead of a slash (see more about this here). To keep the markup as it was, we copied the original Liquid code into their corresponding templates. The Liquid code had to be modified since it is not entirely compatible with Orchard Core, which uses a different library and set of filters. For example, the {% Href "~/Themes/GpuDay.Theme/Images/wigner-logo.png" %} code was changed to {{ "~/mediatheme/images/wigner-logo.png" | href }}. Lombiq modules and utilities involved During development, the NodeJS Extensions utility proved invaluable, as it automatically compiles all assets during the build process. The Lombiq Media Theme is an ideal choice for developing a media theme for DotNest, compiling your templates and assets into a deployable package; refer to the documentation for further details. Lastly, we used the Lombiq Privacy module to ensure GDPR compliance. Summary Migrating old websites from Orchard to Orchard Core doesn’t have to be a huge hassle if you're willing to make some compromises. We were content to use the old markup and assets, and we managed to store the entire HTML markup of the old events in an HtmlField to keep the process simple and less labor-intensive. Ultimately, the admins can now enjoy the more user-friendly content editing capabilities of Orchard Core to create this year's GPU Day event – see what Balázs Kacskovics told us about it: Our work became much easier with the new admin panel. With fewer menu items, it became much more transparent and intuitive than before. Now, finding the relevant options and content items is a simple task. Also, fewer content types mean less hassle for us, e.g. the editing of the new GPUDay content item is more efficient and less time-consuming, also we can make fewer mistakes with the new frame. If you're considering migrating your Orchard 1 website to DotNest Core, this is an excellent time. Should you have any questions or encounter challenges don’t hesitate to reach out to us. We're always ready to bring our expertise to your unique project needs. Get in touch with us today, and let's start making your Orchard Core website even better!

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.