0

I'm writing a custom plugin for reservations. I need to generate an .ics file that needs to be sent to a predefined email address.

I've tried some library like ZContent iCalendar and a PHP script that I found on github that I've included inside my plugin code. The script works well and I can obtain a valid output. The problem is I can't figure out how to generate the ics file and attach it to an email using the wp_mail function.

Here is my code:

<?php
/**
 * ICS.php
 * =======
 * Use this class to create an .ics file.
 *
 * Usage
 * -----
 * Basic usage - generate ics file contents (see below for available properties):
 *   $ics = new ICS($props);
 *   $ics_file_contents = $ics->to_string();
 *
 * Setting properties after instantiation
 *   $ics = new ICS();
 *   $ics->set('summary', 'My awesome event');
 *
 * You can also set multiple properties at the same time by using an array:
 *   $ics->set(array(
 *     'dtstart' => 'now + 30 minutes',
 *     'dtend' => 'now + 1 hour'
 *   ));
 *
 * Available properties
 * --------------------
 * description
 *   String description of the event.
 * dtend
 *   A date/time stamp designating the end of the event. You can use either a
 *   DateTime object or a PHP datetime format string (e.g. "now + 1 hour").
 * dtstart
 *   A date/time stamp designating the start of the event. You can use either a
 *   DateTime object or a PHP datetime format string (e.g. "now + 1 hour").
 * location
 *   String address or description of the location of the event.
 * summary
 *   String short summary of the event - usually used as the title.
 * url
 *   A url to attach to the the event. Make sure to add the protocol (http://
 *   or https://).
 */
class WP_ICS{
  const DT_FORMAT = 'Ymd\THis\Z';
  protected $properties = array();
  private $available_properties = array(
    'description',
    'dtend',
    'dtstart',
    'location',
    'summary',
    'url'
  );
  public function __construct($props) {
    $this->set($props);
  }
  public function set($key, $val = false) {
    if (is_array($key)) {
      foreach ($key as $k => $v) {
        $this->set($k, $v);
      }
    } else {
      if (in_array($key, $this->available_properties)) {
        $this->properties[$key] = $this->sanitize_val($val, $key);
      }
    }
  }
  public function to_string() {
    $rows = $this->build_props();
    return implode("\r\n", $rows);
  }
  private function build_props() {
    // Build ICS properties - add header
    $ics_props = array(
      'BEGIN:VCALENDAR',
      'VERSION:2.0',
      'PRODID:-//hacksw/handcal//NONSGML v1.0//EN',
      'CALSCALE:GREGORIAN',
      'BEGIN:VEVENT'
    );
    // Build ICS properties - add header
    $props = array();
    foreach($this->properties as $k => $v) {
      $props[strtoupper($k . ($k === 'url' ? ';VALUE=URI' : ''))] = $v;
    }
    // Set some default values
    $props['DTSTAMP'] = $this->format_timestamp('now');
    $props['UID'] = uniqid();
    // Append properties
    foreach ($props as $k => $v) {
      $ics_props[] = "$k:$v";
    }
    // Build ICS properties - add footer
    $ics_props[] = 'END:VEVENT';
    $ics_props[] = 'END:VCALENDAR';
    return $ics_props;
  }
  private function sanitize_val($val, $key = false) {
    switch($key) {
      case 'dtend':
      case 'dtstamp':
      case 'dtstart':
        $val = $this->format_timestamp($val);
        break;
      default:
        $val = $this->escape_string($val);
    }
    return $val;
  }
  private function format_timestamp($timestamp) {
    $dt = new DateTime($timestamp);
    return $dt->format(self::DT_FORMAT);
  }
  private function escape_string($str) {
    return preg_replace('/([\,;])/','\\\$1', $str);
  }
}


class iCalBooking{

  public function init(){
    add_action('admin_post_forward_booking_request', array($this, 'send_booking_request'));
    add_action('admin_post_nopriv_forward_booking_request', array($this, 'send_booking_request'));
    #add_action();
    add_shortcode('booking', array($this, 'display_booking_form'));
  }

  public function display_booking_form(){
    include_once 'booking-form.php';
  }

  private function has_valid_nonce() {
      if(!isset( $_POST['booking_nonce'] )){
        return false;
      }
      $field  = wp_unslash( $_POST['booking_nonce'] );
      $action = 'validate_booking_request';
      return wp_verify_nonce( $field, $action );
  }

  public function send_booking_request(){
    if(! ( $this->has_valid_nonce() ) ){
      return;
    }
    $fname = $_POST['client_fname'];
    $lname = $_POST['client_lname'];
    $phone = $_POS['client_phone'];
    $email = $_POST['client_email'];

    $title = 'Prenotazione';

    $description = $fname . $lname . $phone . $email;


    $ics = new WP_ICS(array(
      'location' => '',
      'description' => $description,
      'dtstart' => $_POST['client_checkin'],
      'dtend' => $_POST['client_checkout'],
      'summary' => '',
      'url' => ''
    ));

    file_put_contents('reservation.ics', $ics->to_string() );

    #header('Content-Type: text/calendar; charset=utf-8');
    #header('Content-Disposition: attachment; filename=reservation.ics');


  }

}

$booking = new iCalBooking;
$booking->init();
?> 
0

This is untested, but it's kind of the approach you need to take. I'm just dealing with the last method in your class.

Here's how I would approach it:

  1. You need to specify a file path when you save the file so you can use that later to get the file to attach.
  2. Set up the values for your email (the "to" address, subject, message, headers, and path to the attachment).
  3. Use the variables from #2 for wp_mail() to send your message.

Here's the function from your class with my additions:

public function send_booking_request(){
    if(! ( $this->has_valid_nonce() ) ){
      return;
    }
    $fname = $_POST['client_fname'];
    $lname = $_POST['client_lname'];
    $phone = $_POS['client_phone'];
    $email = $_POST['client_email'];

    $title = 'Prenotazione';

    $description = $fname . $lname . $phone . $email;


    $ics = new WP_ICS(array(
      'location' => '',
      'description' => $description,
      'dtstart' => $_POST['client_checkin'],
      'dtend' => $_POST['client_checkout'],
      'summary' => '',
      'url' => ''
    ));


    // You need to specify the path here so you can use it later to get the attachment
    $uploads = wp_upload_dir();
    $path = $uploads['path'];

    file_put_contents( $path . '/reservation.ics', $ics->to_string() );

    // Set up email
    $to = 'you@email.com';
    $subject = "reservation file";
    $message = "Please see attached reservation.";
    $headers = 'From: My Name <myname@mydomain.com>' . "\r\n";
    $attachments = array( $path . '/reservation.ics' );

    // Send the email.
    wp_mail( $to, $subject, $message, $headers, $attachments );

    #header('Content-Type: text/calendar; charset=utf-8');
    #header('Content-Disposition: attachment; filename=reservation.ics');

}

Assuming the rest of your code is OK and the file is created, this should be able to get it and attach it to an email.

  • I have found a dirt solution to avoid using the mail attachment. I've created two files inside a folder in the root of my wordpress installation, Inside this folder I put the php script that will generate the .ics file and a page calle download-ics.php that accept some querystring params that are respectively the start date and the end date. To avoid to pass some sensible information to the url I will give to the user only the possibility to add the reservation to the calendar, all the info like phone cc are passed inside the body of the mail. – ZWPDev Mar 20 at 18:19
  • The only problem with the querystring method I've adopted is that the last part of the url sometimes are truncated due to the spaces in the last param, i need to fix this. I will try also your suggestion. Thanks for the reply. – ZWPDev Mar 20 at 18:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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