Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I really need your help with a feature I have never worked with so far.

I have a custom-post-type named wr_event. And I created this custom WP_Query to retrieve all posts for this post-type that are "younger" than yesterday. Fairly simple and this works like a charm.

function event_list_iCal() {

    $yesterday = time() - 24*60*60;
    $args = array(
        'post_type' => 'wr_event',
        'posts_per_page' => -1, // show all posts
        'meta_key' => 'event_date',
        'orderby' => 'meta_value_num',
        'order' => 'ASC',
        'meta_value' => $yesterday,
        'meta_compare' => '>',
    );

    $loop = new WP_Query( $args );
    $ical = "BEGIN:VCALENDAR
    VERSION:2.0
    PRODID:-//hacksw/handcal//NONSGML v1.0//EN";

    $posts = get_posts( $args );
    foreach( $posts as $post ) : setup_postdata($post);
        $ical .= "BEGIN:VEVENT
        UID:" . md5(uniqid(mt_rand(), true)) . "mysite.com
        DTSTAMP:" . gmdate('Ymd').'T'. gmdate('His') . "Z
        DTSTART:".unixToiCal(get_event_date($post), get_event_time($post))."00Z
        DTEND:".unixToiCal(get_event_end_date($post), get_event_end_time($post))."00Z
        SUMMARY:".get_the_title($post->ID)."
        DESCRIPTION:".get_the_content($post->ID)."
        END:VEVENT";
    endforeach;

    $ical .= "END:VCALENDAR";

    header('Content-type: text/calendar; charset=utf-8');
    header('Content-Disposition: inline; filename=calendar.ics');
    echo $ical;
    exit;
}

function unixToiCal($uStamp = 0, $tzone = 0.0) {
    $uStampUTC = $uStamp + ($tzone * 3600);       
    $stamp  = date("Ymd\THis\Z", $uStampUTC);
    return $stamp;       
} 

I use this function call in my index.php to list all "upcoming" events.

Creating a .ics file.

There is another feature that I'd like to have. I want to create a .ics (iCal) file on the fly with all "upcoming" events. So I guess this shoudln't be to hard as I already have the query.

Any ideas on that matter? I would really appreciate some help with this.

Update:

I have two more problems with the .ics Calendar file.

I have a function get_event_date($timestamp) that returns a timestamp of the event-date. However there is a (for me) rather complicated part in it.

There are two variables $date[0] and $time[0] that hold different formats. The $date[0] holds a timestamp 1347667200 and the $time[0] holds a string e.g. 14:00. I now need to calculate the final timestamp of the "date" plus "time" to pass it along to the unixToical() function.

if ( $timestamp ) { 
            if ( !empty( $time[0]) ) {
                $time = explode(':', $time[0]);
                $hours = $time[0];
                $minutes = $time[1];
            } else { 
                //$hours = "00";
                //$minutes = "00";
            }
            $seconds = "00";
            return $date[0] + ($hours * 60)  + $minutes;
            exit;
        }

The part where I set $hours to "00" is for when there is no time set. In that case I want to time in the final .ics to be "00:00" (midnight).

Any idea what I'm doing wrong here. I guess this might be the problem why when importing the calendar file to iCal only the first event is imported. (When opening the file with a text-editor all events are in there)

share|improve this question
    
Solution above looks good, why not compare the format of the file generated with a one you know works and post the differences, we can then tell you what to correct. –  Simon Forster Sep 2 '12 at 12:50
    
Thank you. See my updates above. I posted the code that is generated by my functions and the .ics code that is generated when exporting a .ics file directly form iCal under my mac. You can see my actual code I'm using right now above in the question. I have functions for returning the date as a timestamp and I found a function unixToiCal() that should convert the timestamp to the date needed. The functions for the event-time (get_event_time()) are returning the time in 14:00 format. I'm not that much of a php guy so I really need your detailed help with this. –  mathiregister Sep 2 '12 at 14:00
    
Da schau her... a Wiener. –  kaiser Sep 3 '12 at 1:48
1  
Hallo @kaiser - I bin a Tiroler. –  mathiregister Sep 3 '12 at 15:23
1  
If you open Console (In Applications/Utilities), that will show you any errors iCal reports and will help you diagnose the issue. I notice you're missing END:VEVENT lines; I'm not certain if those are necessary or not, I would guess they are, but I'm not familiar enough with the ical spec to say for certain. –  Matthew Boynes Sep 3 '12 at 15:36
show 3 more comments

2 Answers

up vote 2 down vote accepted
+150

This is entirely based on Event Organiser (a plug-in I've developed). The code is lifted almost straight from the source but with alterations. As such I've not tested the code as given.

Step 1: Create a feed

This is simple:

add_action('init','wpse63611_add_events_feed');
function wpse63611_add_events_feed(){
     add_feed('my-events','wpse63611_events_feed_output');
}

This adds a feed to your site: www.site.com?feed=my-events or www.site.com/feed/my-events if you have pretty permalinks. The wpse63611_events_feed_output() callback will output the contents of the ICAL file. But first...

Step 2: Alter the query

WordPress doesn't know what this feed is meant to contain. Here we use the pre_get_posts to tell WordPress, that for this feed we want posts of post type 'wr_event'. We could get events from a particular category, venue or between certain dates too.

By separating out the query from the output, you can have multiple feeds which use the same output function - but query different events (based on time, location, category etc).

add_action( 'pre_get_posts', 'wpse63611_event_feed_query' );
function wpse63611_event_feed_query( $query ) {

     $yesterday = current_time('timestamp') - 24*60*60;
     $compare = $latest ? '>' : '<';

     if( $query->is_feed('eo-events') ){
         $query->set('post_type', 'wr_event');
         $query->set('posts_per_page', -1);
         $query->set('meta_key', 'event_date');
         $query->set('orderby', 'meta_value_num');
         $query->set('order', 'ASC');
         $query->set('meta_compare', $compare);
         $query->set('meta_value', $value);
     }
}

Step 3: The contents of the ICS file

As mentioned earlier, wpse63611_events_feed_output() is responsible for printing the output of our feed.

 function wpse63611_events_feed_output(){
      //Let's give it a name;
      $filename = urlencode( 'my_events' . date('Y-m-d') . '.ics' );

      //Collect output 
      ob_start();

      // File header
      header( 'Content-Description: File Transfer' );
      header( 'Content-Disposition: attachment; filename=' . $filename );
      header('Content-type: text/calendar');
      header("Pragma: 0");
      header("Expires: 0");
?>
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//<?php  get_bloginfo('name'); ?>//NONSGML Events //EN
CALSCALE:GREGORIAN
X-WR-CALNAME:<?php echo get_bloginfo('name');?> - Events

<?php

    // Loop through events
    if ( have_posts() ):

         $now = new DateTime();
         $datestamp =$now->format('Ymd\THis\Z');

         while( have_posts() ): the_post();
              global $post;

              $uid = md5(uniqid(mt_rand(), true))."@mysite.com";

              $start = unixToiCal(get_event_date($post), get_event_time($post));
              $end = unixToiCal(get_event_end_date($post), get_event_end_time($post));

              $summary = wpse63611_escape_icalText(get_the_title())
              $description = apply_filters('the_excerpt_rss',  get_the_content());
              $description = wpse63611_escape_icalText($description);

BEGIN:VEVENT
UID:<?php echo $uid;?>

DTSTAMP:<?php echo $datestamp;?>

DTSTART:<?php echo $start; ?>

DTEND:<?php echo $end; ?>

SUMMARY:<?php echo wpse63611_esc_ical_text($summary);?>

DESCRIPTION:<?php echo wpse63611_esc_ical_text($description);?>

END:VEVENT

         endwhile;

    endif;
?>
END:VCALENDAR
<?php

    //Collect output and echo 
    $eventsical = ob_get_contents();
    ob_end_clean();
    echo $eventsical;
    exit();
}   

I've used the unixToiCal function you've defined in your question. I've also used the following to remove anything that might upset an ICAL parser:

 function wpse63611_esc_ical_text( $text='' ){

    $text = str_replace("\\", "\\\\", $text);
    $text = str_replace(",", "\,", $text);
    $text = str_replace(";", "\;", $text);
    $text = str_replace("\n", "\n ", $text);

    return $text;
 }
share|improve this answer
    
Hey Stephen. Thank you very much. I'm a really big step further. The file seems to be almost properly written. I updated my question with the output of the file. So the loop is correct and only the "outstanding/upcoming" events are in the calender - that is just what I want. However the file itself is still buggy. Can't be opened with any Calendar application. Do you see any lines that make the file not work? –  mathiregister Sep 3 '12 at 15:21
    
Thank you. everything is working fine now! –  mathiregister Sep 3 '12 at 15:57
    
I updated my question with my final two problems. Maybe you know how to do that? I need add a string 11:00 (time) to an existing timestamp of a date. And I need to find some way of passing a "placeholder" time like "00:00" if no time is set to an event. –  mathiregister Sep 3 '12 at 18:51
add comment

Try this:

First add this functions to your theme's functions.php

//this will call the download function if needed
function Ical_download() {
    global $wp;
    global $wp_query;
    if (isset($wp->query_vars["ical_download"])){
        event_list_iCal();
        exit();
    }
}

add_action('template_redirect', 'Ical_download');


//this will add ical_download to the list of query vars
function ical_download_query_val() {
    global $wp;
    $wp->add_query_var('ical_download');
}

add_filter('init', 'ical_download_query_val');
function event_list_iCal( $latest = true ) {

    $yesterday = time() - 24*60*60;
    $compare = $latest ? '>' : '<';

    $args = array(
        'post_type' => 'wr_event',
        'posts_per_page' => -1, // show all posts
        'meta_key' => 'event_date',
        'orderby' => 'meta_value_num',
        'order' => 'ASC',
        'meta_value' => $yesterday,
        'meta_compare' => $compare,
    );

    $loop = new WP_Query( $args );
    $ical = "BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//hacksw/handcal//NONSGML v1.0//EN
";
    while ( $loop->have_posts() ) : $loop->the_post();

    $ical .= "BEGIN:VEVENT
UID:" . md5(uniqid(mt_rand(), true)) . "example.com
DTSTAMP:" . gmdate('Ymd').'T'. gmdate('His') . "Z
DTSTART:".get_event_date($post)."00Z
DTEND:".get_event_end_date($post);."00Z
SUMMARY:".$post->title."
DESCRIPTION:".$post->content."
END:VEVENT
";

    endwhile;

    $ical .= "END:VCALENDAR";

    //set correct content-type-header
    header('Content-type: text/calendar; charset=utf-8');
    header('Content-Disposition: inline; filename=calendar.ics');
    echo $ical;
    exit;

}

So just update the summery and description fields as well as the start and end times to the right format and your download link should be something like:

<a href="<?php echo get_bloginfo('url').'?ical_download';?>">Download iCal</a>
share|improve this answer
    
Thank you. I updated my question with your code. It almost seems to work except for the fact that the calendar file can't be opened. So the download link seems to work fine, however the file can't be opened with iCal or any other Calendar Application. –  mathiregister Sep 2 '12 at 10:44
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.