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.

No Comments

Add a Comment