How to Bulk-add Gigs in an AudioTheme Theme

In “A Promenade of Promenades”, I wrote about using AudioTheme’s Promenade theme to give a clear minimalist visual design to a Summertown teacher’s WordPress website. This teacher gives regular weekly classes, and asked me to list them on the site, all the way up to mid-2018.

Promenade, as I also mentioned last time, was designed for musicians, and has a feature for displaying lists of gigs on the home page. It can be customised to use the word “class” instead of “gig”, so making the list suit the vocation is no problem. However, to add a gig, you have to do much the same as when writing a blog post. You need to type the name of the gig as a title. For the band Manowar who I found use Promenade, this could be something like “The Final Battle”; for my teacher, it’s more likely to be “Music and Movement”. You also have to select the date from a calendar, the time from a menu, and the venue from a list of locations known to Google Maps that I’ve previously set up. For the classes she wanted me to add, this would have amounted to about 350 title-typings and date-time-venue selections. Ouch. So this was definitely worth trying to automate.

I got in touch with Anna DiTommaso and Brady Vercher from AudioTheme Support. Both were extremely helpful, and I want to thank them here, because their advice on various customisations saved me several days of trial and error. Brady is one of AudioTheme’s developers, and helped me understand how gigs are stored. With his help, I was able to write a PHP script for bulk-adding classes, and I’m reproducing it here in case it’s useful to anyone else. I ran it from the command-line using Justyn’s Magic Includer, which I’ll write about in a later post.

One final point is that with musical gigs, it’s often not important to know when they end. Everybody’s too drunk, stoned or stunned to care. But my customer did want this shown. I discovered that the easiest way to do it was to standardise on writing the start time and the end time in the gig-description field, as e.g. “9-10am” or “5:50-6:30pm”. I then copied the files audiotheme/single-gig.php and audiotheme/parts/gig-meta.php into my child theme, and edited them so that the description field was always visible.


/* add_classes.php */

This script adds all Jenny's classes
to her Promenade-themed site, between the 
terms returned by function terms(). 
It defines add_classes() to do so. 
Use delete_all_classes() to delete them 
all if there are problems.

/* Returns an array of class structures.
   Each element represents one kind of class,
   held weekly during term. The fields are
   for my convenience in writing the
   information only: they'll need converting
   to what AudioTheme expects, which I do
   in function add_class().
function classes()
  $class1 = array( 'title' => 'Music and Movement'
                 , 'venue' => 'Little Acorns, Headington'
                 , 'descr' => '4-5pm'
                 , 'day'   => 'Thursday'
                 , 'time'  => '4pm'

  $class2 = array( 'title' => 'Listen, Move, Dance'
                 , 'venue' => 'Revolution Dance Studio, Cowley'
                 , 'descr' => '7-8pm'
                 , 'day'   => 'Thursday'
                 , 'time'  => '7pm'

  $class3 = array( 'title' => 'Rhythm and Rhyme'
                 , 'venue' => 'Yarnton Church Hall'
                 , 'descr' => '6-7pm'
                 , 'day'   => 'Friday'
                 , 'time'  => '6pm'

  return array( $class1, $class2, $class3 );

/* Returns an array of pairs. Each
   pair is an array representing a term,
   its first element being the start date
   and its second the end date. As with
   classes(), these fields are for me,
   and mean nothing to AudioTheme.
function terms()
  return array( array( new DateTime('2016-Oct-01')
                     , new DateTime('2016-Dec-17') 
              , array( new DateTime('2017-Jan-07')
                     , new DateTime('2017-Mar-23') 
              , array( new DateTime('2017-Apr-20')
                     , new DateTime('2017-Jul-06') 
              , array( new DateTime('2017-Oct-01')
                     , new DateTime('2017-Dec-17') 
              , array( new DateTime('2018-Jan-07')
                     , new DateTime('2018-Mar-24') 
              , array( new DateTime('2018-Apr-20')
                     , new DateTime('2018-Jul-06') 

/* Adds all classes.
function add_classes()
  foreach ( classes() as $class )
    add_class( $class );

/* Adds one class. Classes are assumed to start
   on the first relevant day of October 2016.
function add_class( $class )
  $start_string = 'First ' . $class['day'] . ' of Oct 2016';
  $start_date_time  = new DateTime( $start_string );
  $interval    = new DateInterval( 'P1W' );

  $recurrences = 250;
  $dates = new DatePeriod( $start_date_time, $interval, $recurrences );

  // The above code sets up $dates as a structure
  // representing 250 successive weekly time points. 
  // 250 may actually be slightly too many, but it 
  // will be filtered by the final term anyway.

  // Now loop over these timepoints, and for each one,
  // get the info about the class and add it as a gig.

  foreach ( $dates as $date ) {
    if ( is_in_terms( $date, terms() ) ) {
      echo $class['title'] . ' ' . 
           $class['venue'] . ' ' . 
           $class['descr'] . ' ' .
           $date->format( 'D d M Y' ) . "\n";

      add_gig( $class['title']
            , sanitize_title( $class['title'] )
              // This is the WordPress slug.
            , $date->format( 'D d M Y' )
            , $class['time'] 
            , $class['descr'] 
            , $class['venue'] 

/* Returns true if $date falls within one
   of the terms.
function is_in_terms( $date, $terms )
  if ( sizeof( $terms ) == 0 )
    return false;
  else if ( is_in_term( $date, $terms[0] ) )
    return true;
    return is_in_terms( $date, array_slice( $terms, 1 ) );

/* Returns true if $date is within $term.
   $term is an array representing a pair of
   dates as above.
function is_in_term( $date, $term )
  return is_between_dates( $date, $term[0], $term[1] );

/* Returns true if $date is between $start
   and $end.
function is_between_dates( DateTime $date
                         , DateTime $start
                         , DateTime $end
  return $date >= $start && $date <= $end;

/* Adds a gig with the relevant fields. This is where
   I interface with AudioTheme.
   $title is the main title, e.g. Music and Movement. 
   $name is a slug, e.g. music-and-movement. 
   $date is a date in any reasonable format, e.g. 
   '22 Jan 2017'. 
   $time is the same for time, e.g. '2pm'. 
   $description should be of the form '9-10am' or 
   '2:30-3:30pm'. All classes last for an hour,
   and this enables us to let site visitors see the
   end time.
   $venue is one of the venue names. I have made
   sure that these are all known to Google Maps,
   so that they display properly in the single-gig
function add_gig( $title, 
  // This code is based on source code shown me by
  // Brady Vercher at AudioTheme Support. He said 
  // that gigs are stored as a custom post type (CPT), 
  // with the various properties saved as post meta, 
  // so I could probably just loop through my classes 
  // and use wp_insert_post() and add_post_meta() to 
  // insert them. He also said he uses a library called 
  // Posts 2 Posts to store the relationships between 
  // gigs and venues, so those would need to be accounted 
  // for separately. However, I don't think I've had to 
  // worry about that. My code works anyway, if I get 
  // venue IDs as below and pass those to 
  // set_audiotheme_gig_venue_id().

  $venue_post = get_page_by_title( $venue, OBJECT, 'audiotheme_venue' );
  $venue_id = $venue_post->ID;

  $args = array(
    'post_type'      => 'audiotheme_gig',
    'post_author'    => jenny_id(),
    'post_content'   => '',
    'post_title'     => $title,
    'post_excerpt'   => $description,
    'post_status'    => 'publish',
    'comment_status' => 'closed',
    'ping_status'    => 'closed',
    'post_password'  => '',
    'post_name'      => $name,
    'to_ping'        => '',
    'pinged'         => '',
    'post_parent'    => 0
  $post_id = wp_insert_post( $args );
  set_audiotheme_gig_venue_id( $post_id, $venue_id );

  $dt = date_parse( $date . ' ' . $time );
  $datetime = sprintf( '%d-%s-%s %s:%s:%s',
                       zeroise( $dt['month'], 2 ),
                       zeroise( $dt['day'], 2 ),
                       zeroise( $dt['hour'], 2 ),
                       zeroise( $dt['minute'], 2 ),
                       zeroise( $dt['second'], 2 )
  update_post_meta( $post_id, '_audiotheme_gig_datetime', $datetime );
  // I don't know how much of the date reformatting
  // is necessary. I'm just imitating AudioTheme's code.

  $t = date_parse( $time );
  $time = sprintf( '%s:%s:%s',
                   zeroise( $t['hour'], 2 ),
                   zeroise( $t['minute'], 2 ),
		   zeroise( $t['second'], 2 ) 
  update_post_meta( $post_id, '_audiotheme_gig_time', $time );

/* Gets the user ID for the site's owner,
   Jenny. I use this for the 'post_author' 
   argument in $args in add_gig(), so that 
   classes appear with a sensible author. 
function jenny_id()
  $jenny = get_user_by( 'login', 'Jenny' );
  $jenny_id = $jenny->id;

/* Deletes all the classes. This function
   isn't called above, but I use it if
   I've mis-added so many classes that
   deleting them manually isn't feasible.
function delete_all_classes()
  $args = array( 'post_type' => 'audiotheme_gig'
               , 'posts_per_page' => -1
               , 'post_status' => 'any'
               , 'fields' => 'ids'

  $gigs = get_posts( $args );
  // Each element of $gigs is an ID.
  // That's because of the 
  //   'fields' => 'ids'
  // above.

  foreach( $gigs as $gig_id )
    wp_delete_post( $gig_id, true );


Leave a Reply

Your email address will not be published. Required fields are marked *