Building a showcase site to display sample events and auto deploying them on each PR merge in Open Event Webapp

Open Event Webapp generates static websites of the event fed to it in the form of JSON data. Earlier, we used to automatically deploy the FOSSASIA Summit event website on the Github pages of the repository on every merge of a pull request.  The presence of it helped in showcasing and testing purposes. But a single event was not enough. Events are diverse, taking place in a large number of rooms, h a variety of sessions and extending over several days. So, having multiple events on the showcase site and continuously showcasing and testing them was a really great idea.

Here is the feature request for it. I will be explaining the major portions of it. The whole code can be found here

First of all, we need to build the main index page which we will showcase our sample events. It will contain the small images of the event and on clicking them, the selected event will be opened. For displaying features and capability of the generator,  we have two separate sections of events: one having the single session page and the other having the expandable sessions page. There will be links to the other components of the Open Event Ecosystem like Android App generator,  Web App generatorOrganizer App, and Eventyay.

The contents of that page are kept in the overviewSite folder. It contains the event background images of the events and the main index file. The basic structure of the file is shown below. The whole file can be viewed here

<div class="container-fluid bg-3 text-center">
 <div class="row margin">
   <div class="col-md-6 col-sm-12 col-xs-12">
     <div class="row">
       <h2 class="margin"> Apps with expandable sessions page </h2>
       <div class="col-md-6 col-sm-6 col-xs-12 margin">
         <p><strong>Open Tech Summit 2017</strong></p>
         <a href='./OpenTechSummit/index.html' target='_blank'>
           <img src="./otssmall.jpg" class=""  alt="Image">
         </a>
       </div>
      </div>
    </div>
   <div class="col-md-6 col-sm-12 col-xs-12">
     <div class="row">
       <h2 class="margin"> Apps with single sessions page </h2>
       <div class="col-md-6 col-sm-6 col-xs-12 margin">
         <p><strong>Mozilla All Hands 2017</strong></p>
         <a href='./MozillaAllHands2017/index.html' target='_blank'>
           <img src="./mozilla_banner.jpg" class=""  alt="Image">
         </a>
       </div>
     </div>
   </div>
 </div>
</div>

But, this is just the front end of the page. We haven’t generated the sample events yet and neither have we made any changes to make them auto deploy on each PR merge. The test script of the app which contains unit, acceptance and selenium tests runs on each PR made against the repo when Travis CI build is triggered. So, it makes sense to write code for generating the event there itself. When the test script has finished executing, all of the events would have been generated and present inside a single folder named [email protected] (We needed something short and easy to remember and the name doesn’t really matter). We then copy the contents of the overviewSite folder into the above folder. It already contains the folder of different sample events.

Here is the related code. The full test script file can be found here

describe('generate', function() {
 describe('.create different event sites and copy assets of overview site', function() {
   // Sample events are generated inside the [email protected] folder
   it('should generate the Mozilla All Hands 2017', function(done) {
     var data = {};
     // API endpoint configuration of the All Hands 2017 event and the session style set to single pages
     data.body = {
       "email": "[email protected]",
       "name": "Open Event",
       "apiendpoint":    "https://raw.githubusercontent.com/fossasia/open-event/master/sample/MozillaAllHands17",
       "sessionMode": "single",
       "datasource": "eventapi",
       "assetmode" : "download"
     };
     generator.createDistDir(data, 'Socket', function(appFolder) {
       assert.equal(appFolder, "[email protected]/MozillaAllHands2017");
       done();
     });
   });
   // For copying the static files required for the showcase site
   it('should copy all the static files', function(done) {
     var staticPath = __dirname + '/../src/backend/overviewSite/';
     function copyStatic(fileName) {
       // Copy the static files in the overviewSite folder to the [email protected] folder
     }
   });
 });
});

Everything is almost done now. We then just make some changes in the deploy script to publish the whole folder containing the different event samples instead of a particular sample and everything works fine.

We navigate to [email protected] inside the dist directory and initialize a git repository there. We then set the username and email and fetch the contents of the gh-pages branch of the official repo (using the Github token. It is defined in the Travis Settings as a private environment variable) and reset it. We then add all the files present in the current directory to the staging area, commit them and push it to the upstream repository. Here is the relevant code. The whole github deploy script file can be viewed here

eval cd [email protected]/
git init
git config --global user.name "Travis CI"
git config --global user.email "[email protected]"
git remote add upstream "https:[email protected]/"${TRAVIS_REPO_SLUG}".git"
git fetch upstream
git reset upstream/gh-pages
touch .
git add -A .
git commit -m "rebuild pages at ${rev}"
git push -q upstream HEAD:gh-pages

Here is the screenshot showing the successful deployment of the sample events. You can view the whole log here. A screenshot of it is below. You can view the higher quality image by clicking on it.

29e53dc0-8841-4329-8a37-8d2e9f08756c.png

After all the work, this is how the showcase page looks like. Samples are re-generated and the site is auto-deployed on every Pull Request merged in the repository.

66804d19-6862-494f-80b6-e1749493e780.png

On clicking on any of the listed events, we jump to the index page of that particular event. Like, If we click on the Facebook Developer Conference, a new tab is opened and we are taken to the front page of that event!

6a47f623-82c6-49e2-af1c-4d152aaaf9b7.png

References

Implementing Single Session Pages in Open Event Webapp

Recently, we implemented a feature request in the Open Event Webapp which allows the organizers of an event to choose the style of the sessions displayed on the event web site. Before this feature of individual session pages was implemented, we had a single default style of displaying collapsible session elements on the page. A small preview of the session containing its title, type, name, position, and picture of the speaker(s) presenting it would be shown on the different pages. When the user will click on this session element, it would collapse showing detailed information about it.

Before Clicking

2c12db41-d999-423a-bdee-6b639cbdbb0b.png

After Clicking on the first session element

fe1a3362-ebad-42a0-ba9d-0909c2de21a9.png

While this default behavior of collapse of session element on click (shown above) works well in most of the cases, it might not be apt in situations where the session contains a large amount of detail and a higher number of speakers presenting it. Single session pages would work better in that case.

So, we provided an option to select it on the generator form itself. We provided an input field asking which session style does the organizer want? Is it the single session style or the expandable session? The organizer can then select one of them and the site will be generated according to that!!

2989ee1f-509c-400d-94cd-023a06a3601d.png

The whole work was huge and you can view all of it here. I will only be describing the major parts of it here.

The first challenge was to make a template (handlebars) file for the individual sessions. This template would take a single session object as an input. The object would contain all the details about the session and after compilation during generation, a unique individual page for that session would be created.

Here is the basic structure of the template. You can view the whole file here

<div class="container session-container">
 <!-- Contains all the information about the session -->
 <div class="row single-session" id="{{session_id}}">
   <!-- Displaying the date, start and end time of the session -->
   <h3> {{startDay}} </h3>
   <div class = "eventtime"><span class="time-track">{{start}} - {{end}}</span></div>
   <div class="session-content">
     <div class="sizeevent event" id="event-title">
       <!-- Display the title, type and the track of the session -->
     </div>
     <div>
       <!-- Short abstract of the session -->
       {{{description}}}
       <div class="session-speakers-list">
         <!-- Contains detailed information about the speakers of the session. Display their picture, show their position, short biography and social links links like Github, Twitter and LinkedIn -->
       </div>
     </div>
   </div>
 </div>
</div>

But the work is not completed yet. We have to check the style of the session selected by the organizer and if he/she has selected the single session option, then we have to pass all the sessions to this template file during the generation process and create the pages. If the mode selected is expandable, then we carry out the normal generation procedure. Else, we extract every session from the JSON data and feed into to the above template. Since the number of sessions can be quite large, we don’t generate them alongside the other pages like tracks, schedule, rooms, speakers and so on. Instead, we create a new folder named sessions and put in all of the new individual pages there in one place. It helps to keep the directory clean and modularized. Also, since we are placing it inside of a session folder, we will have to update the links to the main pages in the navbar section. Like, instead of track.html, it will be ../tracks.html now. The file is given a common format name of session_sessionId where sessionId is the id of that particular session.

Here is the related code. It is taken from generator.js file in the project

function templateGenerate() {
 if(mode == 'single') {
   function checkLinks() {
     // Made necessary modifications in the links to the main pages
   }
   //jsonData contains all the information about the event
   var trackArr = jsonData.tracks;
   for(var i = 0; i < trackArr.length; i++) {
     var sessionArr = trackArr[i].sessions;
     for(var j = 0; j < sessionArr.length; j++) {
       var sessionObj = JSON.parse(JSON.stringify(sessionArr[j]));
       // Do some modifications in the sessionObj to include the track background and font color and additional links
     } 
   }
   var data = {session: sessionObj};
   checkLinks();
   // Pass the session object to the template, compile and minify the HTML file and place into the sessions folder
   fs.writeFileSync(distHelper.distPath + '/' + appFolder + '/sessions/session_' + sessionId + '.html', minifyHtml(sessiontpl(data)));
 }
}

Ok, most of the things are done now. Just one simple step is missing. When the user clicks on the small session element on the tracks, rooms or the schedule page, then we collapse the session element or open up a brand new session page for showing detailed information about the session depending upon what option the organizer has selected. So, we will have to make little appropriate change for handling this as well. Below code is taken from the tracks page template file

<div class = "room-filter">
 {{#if ../../../mode}}
   <div class="sizeevent event">
 {{else}}
   <div class="sizeevent event" data-toggle="collapse" data-target="#desc-{{session_id}}, #desc2-{{session_id}}">
 {{/if}}
</div>

$('.room-filter').click(function () {
 var sessionMode = "{{mode}}";
 var id =  $(this).attr('id');
 if(sessionMode == 'single') {
   var curUrl = window.location.href;
   var newUrl = curUrl.substring(0, curUrl.lastIndexOf('/') + 1) + 'sessions/session_' + id + ".html";
   window.location.href = newUrl;
 }
});

We are simply checking the mode and if it is set, it means that the session style is a single page. So, we don’t include the bootstrap collapse classes in that case. We handle that click event in the javascript part and appropriate redirect the user to the unique page for that session.

So, after all this hard work, this is how it looks like.

Before clicking

0fdb3641-2579-4537-8f36-910c636cf172.png

After clicking on the first session element, a new page is opened

e5d2ed0d-c54a-44fd-b30c-af440936c8ed.png

Resources