Event Calendar & Participation

One pretty common need I’m starting to see around community-engaged learning is a way for students/faculty to submit events to a central calendar and then indicate their participation in various events. That comes with various program requirements. People want specific reflection patterns per event and have different ideas around what an event counts for in their program. That comes with additional metadata requirements, dashboard views etc.

We did something like this with cultural events when we made the RVArts.org site.1 I’ve got at least three programs interested in this process and some are pursuing products like Give Pulse.

So I took advantage of the request from the da Vinci Center to look at how quickly we could make a functional prototype that would –

  • create a calendar of upcoming approved events for students
  • allow students to submit reflections on those events with a particular structure
  • allow students to submit additional events for approval
  • generate data visualization and reporting for student reflection and for program analysis purposes

I took the more difficult route and assumed we’d have no user accounts just to see what that felt like. With user accounts this become easier. Even with this restriction I was able to build out a functional custom theme in around three hours. Next time, it’d be considerably faster. We could easily bring this down to minutes and then customize from there. It’s a different concept around how to scale but one that makes more sense to me.

Calendar Events

Creating calendar events in WordPress was pretty straight forward. I stuck with the Events Calendar Pro plugin because it’s amazingly powerful and flexible. Even the free version gives you most everything you’d need.

For student submissions we can guide that through my other favorite plugin, Gravity Forms. We just need to map the form fields to the event custom post type and the subsequent custom fields.

First, there’s a handy plugin (Gravity Forms to Custom Post Types) for Gravity Forms that allows you to map the normal post creation option to a custom post type. You’ll now have another option under the Post Fields>Title element to select the post type.
Gravity Forms Post Title element view with custom post type options

The custom fields that Events Calendar uses can be found here. Now we just have to add Post Fields>Custom Field elements to map it to what Events Calendar needs. For example we need _EventStartDate which is composed of a date and time. I make those normal input fields and then create a content template for the custom field and map it together as seen below.
Custom field in gravity forms with content template mapping two other fields

You’d do this for all the elements you want in your event but I won’t make you suffer by going through all of them.

You can build out a pretty decent front-end workflow that way. You also have options to add additional required elements and guide that process in a way that’d be considerably more complicated in the backend of WordPress. If you wanted to include a way to track who submitted what events (without user accounts) you could add an additional custom field for email address and treat that as the unique value representing the student submitter.

Thinking long-term, I’ll make a Gravity Forms bare-bones template (you can export/import these easily) that holds all the essentials for an event and next time around we can do this in a few minutes with the ability to customize. That’s the beautiful thing. It’s scalable and fully customizable at the same time.

Reflections and Attendance

Now we need the ability to create a way for students to signal attendance at the event. That pattern can be as simple or complex as you’d like. To do it well we need to associate the reflection with the event and with the submitter. In this case, we also need to know how many hours it was worth and track that.

Once again, I stuck with Gravity Forms.2 I decided to make posts in a particular category with a tag to specify the event to keep things simple. I could have gone for a full custom post type with a custom taxonomy but it seemed excessive for this. If we do more with students on the backend it might be worth it to simplify that experience.

In any case, I wanted a few things to happen without the need for student action. It makes the activity faster and easier while also making sure certain things happen without the option for data entry errors.

For instance, I set automatic entry for the following elements:

  • Title- A template that reads – Reflection on {embed_post:post_title}3 – that field is then hidden from the user since it’s automatic.
  • Hours custom field- I added a custom element for events that lets you assign how many hours they are worth. I then wrote a little javascript to assign that value to the form field which is then hidden as well.
      jQuery(document).ready(function() {
            if (document.getElementById('tribe-events')){
               var idea = document.getElementsByClassName('tribe-meta-value')[0]; //get hrs value
               var hrs = idea.innerHTML;
               var field = document.getElementById('input_4_7'); //get hrs field
               field.value = hrs; //assign hrs value
            }
        });
    
  • Category- you can set auto categorization in the title element in Gravity Forms so I did
  • Tag- the tag is built with two elements – event-{embed_post:ID} – that’s Gravity Forms default again. It pulls the ID from whatever the form is embedded in and I prepended event- to make it more logical for the humans. It’s also handy in case we ever need to do something based on the ID of the particular event.

Now we’ve got events which can be created normally, through a front-end Gravity form, and we’ve got a reflection form. I opted to customize the Event template so that the reflection form is appended to each event.

<button type="button" class="btn btn-default reflect-button" aria-label="reflect on attendance" id="student-log-button"><i class="fa fa-pencil" aria-hidden="true"></i>Reflect</button>
	<div id="student-log">
		<?php echo do_shortcode('[gravityform id="4" title="false" description="false"]');?>
	</div>

The following javascript makes the form hide/show.

//hide and show button for reflection 
jQuery( "#student-log-button" ).click(function() {
  jQuery( "#student-log" ).toggle( "slow", function() {
    // Animation complete.
  });
});

Now, it seems like we’d want to gather the reflections with each event to show participation and encourage people to see what others are writing. Since we built the tag based on the ID of the event where the form lives, we can then run a query based on $tag = ‘event-‘.$event_id;

<div class="row student-reflections">
				<?php echo 'event-'.$event_id;	?>
					<?php 
					$tag = 'event-'.$event_id;					
			          // args
			          $args = array(
			            'numberposts' => -1,
			            'post_type'   => 'post',
			            'tag' => $tag,			           
			         );
			          // query
			          $the_query = new WP_Query( $args );
					?>
			      <div class="col-md-12"><h2><?php echo $the_query->$post_count;?> Reflections</h2></div>
			        <?php if( $the_query->have_posts() ): ?>
			          <?php while ( $the_query->have_posts() ) : $the_query->the_post(); ?>
			            <div class="col-md-3 reflection-thumb">
			                    <div class="col-md-12">
				                     <a class="reflection-item-title" href="<?php the_permalink(); ?>">
				                     	 <?php the_post_thumbnail('thumbnail', array('class' => 'reflection-item-image')); ?> 
				                     </a>
			                    </div>
			            </div>
			          <?php endwhile; ?>
			        <?php endif; ?>
			          <?php wp_reset_query();  // Restore global post data stomped by the_post(). ?>
			     </div>

Data

The other little trick was assigning the email address as a custom field. That’s key as it lets us easily search against that field. It’s only necessary if students don’t have accounts but it’s worth playing out.

In this case, I create a custom page template for WordPress. It displays data based on a URL parameter.

This little chunk just gets the user email from the URL.

function getUser(){
    $email = htmlspecialchars($_GET["email"]); 
    return $email;
}
<?php         
        $email = getUser();
          // args

          $args = array(
            'numberposts' => -1,
            'post_type'   => 'post',
           'meta_query' => array(
				array(
					'key' => 'userEmail',
					'value' => $email,
				)
			)
            
          );

          // query
          $the_query = new WP_Query( $args );
          ?>
    </div>  
      <div class="student-events row">  
      <div class="col-md-12"><h2>Events Attended</h2></div>
        <?php if( $the_query->have_posts() ): ?>
          <?php while ( $the_query->have_posts() ) : $the_query->the_post(); ?>
            <div class="col-md-2">
                      <a class="faculty-project-item-title" href="<?php the_permalink(); ?>"><?php the_title(); ?></a>
                      <div class="event-hours"><?php echo 'hours:<span class="hours-data">' .theHours(get_the_ID()) . '</span>';?></div>  
                      <div class="faculty-project-item-excerpt"><?php the_excerpt();?></div>
            </div>
          <?php endwhile; ?>
        <?php endif; ?>

          <?php wp_reset_query();  // Restore global post data stomped by the_post(). ?>
      </div>

Now I’ve got all the reflections submitted with that email address as a custom field. I’m displaying the hours custom field data in a div with the class hours-data. I can run a little javascript to add that up and stick it in div.

 jQuery(document).ready(function() {
    var theHrs = 0;
    if (document.getElementById('totalHours')){
      var getHrs = document.getElementsByClassName('hours-data');
      console.log(getHrs);
      for(var i = 0; i < getHrs.length; i++){
        theHrs += parseInt(getHrs[i].innerHTML, 10);        
      }
      document.getElementById('totalHours').innerHTML = theHrs;
    }
  });

You end up with a page like this and the ability to change user data by changing email.

https://rampages.us/davinci-events/student-data/?email=woodwardtw@vcu.edu

Clearly, it hasn’t been prettied up but functional and with endless options to customize based on needs.
Screen Shot 2017-08-06 at 11.09.50 AM

Long-Term

There are a variety of things to think about. We can spin up something like this for a group in an hour or two. It’ll get faster with each iteration and the functions it does out of the box will likely increase significantly. I’m also looking at our options for knitting all these calendars together — that is with other similar installs and with other calendaring systems. Functionally, I can do the first with FeedWordPress and RSS. We can do that selectively based on category or through a button which assigns a category to push the event to another calendar site. We can also pull from Facebook with an additional plugin and we can pull into Google Calendar and do some other tricks. Lots of options to think about all depending on needs and wants.

Aside

We’re also nearly at go-live for the new ALT Lab site. You can see the project element doing some fun stuff with data on this project here. Matt made the ‘in progress’ gif which is an awesome touch. You can see more of his reflection on some ALT Lab logo design considerations here. Once we get that whole site wrapped up, I’ll give a better write up.


1 Currently empty but being revived this semester.

2 What can I say? It’s super-fast for building things like this.

3 That’s Gravity Form’s templating which is very very useful