Category Archives: thirdspace

Weekly Web Harvest for 2017-08-06

reveal js tweak for iframe backgrounds

I’m a fan of reveal.js and have been using it to build all of my recent presentations. It feels like it fits what I do really well. The fact that it’s just a website that behaves a certain way means I can do all kinds of fun things that blend both the web-based examples and my ability to annotate and manipulate the web itself.1 There’s also quite a bit of beauty in tools that continue to offer more opportunities to grow as you learn. Anyway . . .

One common action in my presentations is to embed a live website as a full size background element. Reveal makes that easy with a data-background-iframe element.

<section data-background-iframe="https://rampages.us/"></section>

That’s useful for a variety of reasons but one struggle I’ve had is that the place where slide text would go overlays the website in a way that makes interacting with it difficult. You can see that overlay div in blue in screenshot below. That’s fine when I put text over the website but not what I want if I want to actually demo some aspect of the website when I speak. Previously, I’ve just hit inspect element and hidden that div during presentations but that’s no way to live.
website screenshot indicating blue div blocking access to central portion of embedded website

Given I can add my own javascript, I figured I’d just make something that checked to see if I was using an iframe background and, if so, set the z-index of the text field to go behind that. The script below does that. One little thing that hung me up was that for a data element that has multiple hyphens like data-background-iframe it is referenced with camelCase like dataset.backgroundIframe

  var iframeSlides = document.getElementsByTagName('section'); //gets all the section divs
                
                for(var i = 0; i < iframeSlides.length; i++ ){
                    if (iframeSlides[i].dataset.backgroundIframe){ //looks to see if they have a data-background-iframe element
                        console.log(iframeSlides[i].parentNode.style.zIndex = "-1"); //.style.display = 'none'  also failing style.visibility = 'hidden'; //if it does set the parent element to go to the back
                    }
                }

So no big deal but little things like this add up and I love being able to make something behave just the way I want.


1 Pretty sure I could fit synergy in that sentence somewhere.

reveal js tweak for iframe backgrounds

I’m a fan of reveal.js and have been using it to build all of my recent presentations. It feels like it fits what I do really well. The fact that it’s just a website that behaves a certain way means I can do all kinds of fun things that blend both the web-based examples and my ability to annotate and manipulate the web itself.1 There’s also quite a bit of beauty in tools that continue to offer more opportunities to grow as you learn. Anyway . . .

One common action in my presentations is to embed a live website as a full size background element. Reveal makes that easy with a data-background-iframe element.

<section data-background-iframe="https://rampages.us/"></section>

That’s useful for a variety of reasons but one struggle I’ve had is that the place where slide text would go overlays the website in a way that makes interacting with it difficult. You can see that overlay div in blue in screenshot below. That’s fine when I put text over the website but not what I want if I want to actually demo some aspect of the website when I speak. Previously, I’ve just hit inspect element and hidden that div during presentations but that’s no way to live.
website screenshot indicating blue div blocking access to central portion of embedded website

Given I can add my own javascript, I figured I’d just make something that checked to see if I was using an iframe background and, if so, set the z-index of the text field to go behind that. The script below does that. One little thing that hung me up was that for a data element that has multiple hyphens like data-background-iframe it is referenced with camelCase like dataset.backgroundIframe

  var iframeSlides = document.getElementsByTagName('section'); //gets all the section divs
                
                for(var i = 0; i < iframeSlides.length; i++ ){
                    if (iframeSlides[i].dataset.backgroundIframe){ //looks to see if they have a data-background-iframe element
                        console.log(iframeSlides[i].parentNode.style.zIndex = "-1"); //.style.display = 'none'  also failing style.visibility = 'hidden'; //if it does set the parent element to go to the back
                    }
                }

So no big deal but little things like this add up and I love being able to make something behave just the way I want.


1 Pretty sure I could fit synergy in that sentence somewhere.

Archiving Slack Channels

Since we’re making channels in Slack via our project creation, it made sense to archive them when the project was completed.

In projects (this particular post type) we have a custom field for the start date of the project and one for the end date of the project.

Step one is to check on updates whether the post has the end-date field filled out. In my case, this is one of the legacy ACF fields that survived my great metadata purge. So checking it is done like so . . .

function projectClosed($id){
	if(get_field('end_date', $id)){
		return true;
	} else {
		return false;
	}
}

The Slack archive API piece looks like this.


function slackArchive($id, $post){
        $token ='YOUR_TOKEN_GOES_HERE';
	$id = $post->ID;
	if (projectClosed($id)){
		$channelName = substr($post->post_name,0,21);
		$channelId = getSlackChannelId('p-'.$channelName);
		$url = 'https://slack.com/api/channels.archive?token='.$token.'&channel='.$channelId.'&pretty=1';
		@file_get_contents($url);	
	}
}

And finally we run this function when projects are updated like so.

add_action( 'save_post_project', 'slackArchive', 10, 2);

We’re still experimenting with this workflow and archiving is a decent start. You can easily reactivate it and results still turn up in searches.

It’s likely we’ll also rename it from p-whatever to z-whatever to get it out of the way.

WP API Posts Plugin

This is a little plugin I wrote while working with Jon Becker to build out the Ed Leadership Hub site.

Essentially, we wanted students to be able to fill out a quick form and build out a profile page. As is my my wont, I went the Gravity Forms route. They could give a short bio, twitter account, and their portfolio URL. We’ll be tying in the the posts via Feed WordPress but it seemed like more hassle than it was worth to map the author id to this page. Pretty fun to be able to build out a solution on-the-fly and since all students were going to be in rampages I didn’t have to worry about WP not being upgraded or running from a non-HTTPS server.

This chunk of the plugin builds out the HTML from the shortcode. You can see the data-url, data-num elements being populated. There’s a few other things in there I haven’t yet activated.

function altlab_getpost_shortcode( $atts, $content = null ) {
    extract(shortcode_atts( array(
         'url' => '', //author id - sep multiple w commas       
         'display' => '', //defaults to list but grid with thumbnail featured images    
         'number' => ''   
    ), $atts));         
    if($url){
        $url = 'data-url="'.$url.'"';
    }    
    if($number){
        $num = 'data-num="'.$number.'"';
    } else {
        $num = 'data-num="10"';
    }
     if($display){
        $num = 'data-display="'.$display.'"';
    } else {
        $display = 'data-display="list"';
    }
    //$html = '<ul id="altlab-getposts" class="container" ' . $url . ' ' .  $num . ' ' . $display .'></ul>';
    $html = '<ul id="altlab-getposts" class="container" ' . $url . ' ' . $num . '></ul>';
    return  $html;
}
add_shortcode( 'get-posts', 'altlab_getpost_shortcode' );

Using data elements is the easiest way I’ve seen to pass variables to the javascript . . . the javascript looks for the altlab-getsposts id and then pulls the data elements from it. Once again, there’s some extra stuff in there for down the road that I haven’t implemented here.

function getResourceRestrictions(){
    var element = document.getElementById('altlab-getposts'); 
    if(element.dataset.cats){
      var cats = '&categories='+element.dataset.cats;
    } else {
      cats = "";
    }
    if(element.dataset.authors){
      var authors = '&author='+element.dataset.authors;
    }else {
      authors = "";
    }
    if(element.dataset.num){
      var num = '&per_page='+element.dataset.num;
    } else {
      num = "&per_page=10";
    }
    return cats + authors + num;
}  

Weekly Web Harvest for 2017-07-30

Weekly Web Harvest for 2017-07-30

Event Calendar & Participation

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('

Oops! We could not locate your form.

');?> </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.

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

Contact us

Academic Learning Transformation Lab - ALT Lab
1000 Floyd Ave, Suite 4102 | Richmond, Virginia 23284
altlab@vcu.edu | 804-827-5181

Last updated: April 27, 2016

Virginia Commonwealth University

HTML tutorial