Custom Fields
Re-Envisioned
Support General How to Create Add To Calendar Buttons for Your Events

  • Creator
    Topic
  • #28543
    Resolved Ulysse
    Participant

    Hello,
    I try to follow your tuto but I experience some issues.
    First, I use elementor to create the template of my custom posts and fields so I try to adapt the code.

    • I created my custom post event and custom fields:
      ci_event_startdate
      ci_event_enddate
      ci_event_address (linked a google map to generate an address).

    Then I created the snippet (using a plugin to include snippet):

    <?php
    function justread_ics_download() {
    
            if ( is_singular( ‘event’ ) && isset( $_GET['ics'] ) ) {
    
                    include get_stylesheet_directory() . '/inc/ICS.php';
    
                    header('Content-Type: text/calendar; charset=utf-8');
    
                    header('Content-Disposition: attachment; filename=invite.ics');
    
                    $ics = new ICS(array(
    
                            'location' => $_POST['location'],
    
                            'dtstart' => $_POST['start_date'],
    
                            'dtend' => $_POST['end_date'],
    
                            'summary' => $_POST['summary'],
    
                    ));
    
                    echo $ics->to_string();
    
                    exit();
    
            }
    
    }
    
    add_action( 'template_redirect', 'justread_ics_download' );
    ?>

    Then I created this shortcode:

    <?php
    add_shortcode( 'calendardata', function() {
    $start_date = rwmb_get_value( ci_event_start_date, array( 'format' => 'Y-m-d g:iA' ) );
    
    $start_date = wp_date( 'Ymd\THis', $start_date );
    
    $end_date   = rwmb_get_value( ci_event_end_date, array( 'format' => 'Y-m-d g:iA' ) );
    
    $end_date   = wp_date( 'Ymd\THis', $end_date );
    }
                 );
    ?>

    Then, on template of the event custom post using elementor, I created a block, where I include the shortcode 'calendardata'
    And then the htmlc code:

    <form method="post" action="?ics=true">
    
            <input type="hidden" name="start_date" value="<?php echo $start_date; ?>">
    
            <input type="hidden" name="end_date" value="<?php echo $end_date; ?>">
    
            <input type="hidden" name="location" value="<?php echo rwmb_meta( 'ci_event_location_address' ); ?>">
    
            <input type="hidden" name="summary" value="<?php the_title(); ?>">
    
            <input type="submit" value="Add to Calendar">
    
    </form>

    I have 2 issues:
    - The first one, my firewall blocked the access and I get the message:
    Access Denied - https://mywebsite.com/event/test-event/?ics=true
    Error Code 5558
    This request has been blocked by Patchstack Web Application Firewall .
    If you are a legitimate user, contact the administrator of the site with above error code if this message persists.
    So why this is blocked?
    - The second one is when I deactivate the firewall, the page refreshes and redirects to https://mywebsite.com/event/test-event/?ics=true
    but not file is created.

    So, I don't know what's going on?

Viewing 10 replies - 1 through 10 (of 10 total)
  • Author
    Replies
  • #28569
    Long Nguyen
    Moderator

    Hi,

    1. When clicking the Add to Calendar button, there is a popup box to download the file .ics so the firewall might block this behavior. You can contact their support to get more information.
    2. Change the curry quote to the single quote on this code

    if ( is_singular( ‘event’ ) && isset( $_GET['ics'] ) ) {
    

    to

    if ( is_singular( 'event' ) && isset( $_GET['ics'] ) ) {
    

    And the important point in step 2 of the article is to use a theme template. If not, this function get_stylesheet_directory() will not work.

    You can copy content of the file ICS.php and add before the function justread_ics_download()

    class ICS {
    ...
    }
    function justread_ics_download() {
    ...
    }
    

    The shortcode should include all PHP and HTML form, please don't break it into two parts.

    add_shortcode( 'calendardata', function() {
        $start_date = rwmb_get_value( ci_event_start_date, array( 'format' => 'Y-m-d g:iA' ) );
        $start_date = wp_date( 'Ymd\THis', $start_date );
        $end_date   = rwmb_get_value( ci_event_end_date, array( 'format' => 'Y-m-d g:iA' ) );
        $end_date   = wp_date( 'Ymd\THis', $end_date );
        ?>
        <form method="post" action="?ics=true">
                <input type="hidden" name="start_date" value="<?php echo $start_date; ?>">
                <input type="hidden" name="end_date" value="<?php echo $end_date; ?>">
                <input type="hidden" name="location" value="<?php echo rwmb_meta( 'event_location' ); ?>">
                <input type="hidden" name="summary" value="<?php the_title(); ?>">
                <input type="submit" value="Add to Calendar">
        </form>
        <?php
    } );

    #28574
    Ulysse
    Participant

    Thank you for your answer. I tried to follow your guidance but I failed.
    In particular, I have included the part of the ics.php in my snippet as you said, but did not create a single-event.php

    <?php
    
    class ICS {
      const DT_FORMAT = 'Ymd\THis';
    
      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);
      }
    }
    
    function justread_ics_download() {
    
            if ( is_singular( 'event' ) && isset( $_GET['ics'] ) ) {
    
                    include get_stylesheet_directory() . '/inc/ICS.php';
    
                    header('Content-Type: text/calendar; charset=utf-8');
    
                    header('Content-Disposition: attachment; filename=invite.ics');
    
                    $ics = new ICS(array(
    
                            'location' => $_POST['location'],
    
                            'dtstart' => $_POST['start_date'],
    
                            'dtend' => $_POST['end_date'],
    
                            'summary' => $_POST['summary'],
    
                    ));
    
                    echo $ics->to_string();
    
                    exit();
    
            }
    
    }
    
    add_action( 'template_redirect', 'justread_ics_download' );
    ?>

    But it still doesn't work.

    #28586
    Long Nguyen
    Moderator

    Hi,

    I got the issue. The function is_singular( 'event' ) still check if the template available on the theme templates, please remove this code.

    if ( isset( $_GET['ics'] ) ) { ...}

    and remove unnecessary code

    include get_stylesheet_directory() . '/inc/ICS.php';

    wrap your field ID in the single quote

    $start_date = rwmb_get_value( 'ci_event_start_date', array( 'format' => 'Y-m-d g:iA' ) );
    $end_date   = rwmb_get_value( 'ci_event_end_date', array( 'format' => 'Y-m-d g:iA' ) );

    Let me know how it goes.

    #28602
    Ulysse
    Participant

    Hi,
    It's much better.
    Now, I can download the ics file. My remaining issue is that, it stays blocked on white page as it was waiting something to exit. Maybe I have to make a kind a "call back" to go back to the page?

    #28619
    Ulysse
    Participant

    Everytging seems to work. The ics file whiich is created is correct. My onlyissue is that I odn't know why at the end of the process, the visitor is blocked on a blanck page.

    Here are my codes:

    
    <?php
    
    class ICS {
      const DT_FORMAT = 'Ymd\THis';
    
      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);
      }
    }
    
    function justread_ics_download() {
    
            if ( isset( $_GET['ics'] ) ) {
    
                   // include get_stylesheet_directory() . '/inc/ICS.php';
    
                    header('Content-Type: text/calendar; charset=utf-8');
    
                    header('Content-Disposition: attachment; filename=invite.ics');
    
                    $ics = new ICS(array(
    
                           'location' => $_POST['location'],
    
                           'dtstart' => $_POST['start_date'],
    
                           'dtend' => $_POST['end_date'],
    
                           'summary' => $_POST['summary'],
    
                     ));
                    //return "It works";
    
                    echo $ics->to_string();
    
                    
                    exit();
    
                    return;
    
            }
    
    }
    
    add_action('template_redirect', 'justread_ics_download' );
    ?>
    

    And the shortcode:

    
    <?php
    add_shortcode( 'calendardata', function() {
        $start_date = rwmb_get_value( 'ci_event_start_date', array( 'format' => 'Y-m-d g:iA' ) );
        $start_date = wp_date( 'Ymd\THis', $start_date );
        $end_date = rwmb_get_value( 'ci_event_end_date', array( 'format' => 'Y-m-d g:iA' ) );
        $end_date = wp_date( 'Ymd\THis', $end_date );
            echo $end_date;
            echo $start_date;
    ?>
        <form method="post" action="?ics=true">
            <input type="hidden" name="start_date" value="<?php echo $start_date; ?>">
            <input type="hidden" name="end_date" value="<?php echo $end_date; ?>">
            <input type="hidden" name="location" value="<?php echo rwmb_meta( 'ci_event_location_address' ); ?>">
            <input type="hidden" name="summary" value="<?php the_title(); ?>">
            <input type="submit" value="Add to Calendar">
        </form>
    <?php
    return;
    }
    );
    ?>
    
    #28706
    Ulysse
    Participant

    Hello.
    I don't see how to solve that. Once the "form" is submitted, it stays on a blank page.

    #28721
    Long Nguyen
    Moderator

    Hi,

    Please try to deactivate all plugins except Meta Box, MB extensions and use a default theme of WordPress to re-check this issue.

    And please share your page URL, I will help you to check the issue.

    #28724
    Ulysse
    Participant

    Hello,
    I already tried to deactivate all my plugins and only keep what I needed : elementor pro / metabox / script organizer (the plugin I use to implement my codes). And I still has the same issue.

    I have been advised by Didou Schol to include target in my form, and it works.

    <?php
    add_shortcode( 'calendardata', function() {
        $start_date = rwmb_get_value( 'ci_event_start_date', array( 'format' => 'Y-m-d g:iA' ) );
        $start_date = wp_date( 'Ymd\THis', $start_date );
        $end_date = rwmb_get_value( 'ci_event_end_date', array( 'format' => 'Y-m-d g:iA' ) );
        $end_date = wp_date( 'Ymd\THis', $end_date );
            echo $end_date;
            echo $start_date;
    ?>
        <form method="post" action="?ics=true" target="_blank">
            <input type="hidden" name="start_date" value="<?php echo $start_date; ?>">
            <input type="hidden" name="end_date" value="<?php echo $end_date; ?>">
            <input type="hidden" name="location" value="<?php echo rwmb_meta( 'ci_event_location_address' ); ?>">
            <input type="hidden" name="summary" value="<?php the_title(); ?>">
            <input type="submit" value="Add to Calendar">
        </form>
    <?php
    return;
    }
    );
    ?>

    And so now, when the user clicks on "add to calendar", a new tab is opened, the ICS file is downloaded and the tab is closed. I think, it's good ? What do you think?

    #28728
    Long Nguyen
    Moderator

    Hi,

    Add attribute target="_blank" is a good way, thanks Didou. On my local site, both ways are worked. Screen record https://www.loom.com/share/956524cf111e4be186b9da75fd9d6e50

    Not sure what's wrong with your site but you can go on this way.

    #28731
    Ulysse
    Participant

    Thank you very much for your time. I made several test and I don't know why it does not work without "target". (maybe cache on the server, or on the cdn?).

    By the way, I created a button "add to google calendar" as well. It can be useful as well. I share the code below if it can help our community:

    First, include in the function.php (or in a php code snippet):

    function addToGoogleCalendar($title='', $startdate='', $enddate='', $location='', $details='')
    {
        $startdate = ($startdate ? $startdate : time());
        $startdate = (is_numeric($startdate) ? $startdate : strtotime($startdate));
        $enddate = ($enddate ? $enddate : $startdate + 3600);
        $enddate = (is_numeric($enddate) ? $enddate : strtotime($enddate));   
        $google_url = "https://www.google.com/calendar/event";
        $action = "?action=TEMPLATE";
        $title = ( $title ? ("&text=" . urlencode($title)) : "") ;
        $dates = "&dates=" . getIcalDate($startdate) . "Z/" . getIcalDate($enddate) . "Z";
        $location = ( $location ? ("&location=" . urlencode($location)) : "") ;
        $details = ( $details ? ("&details=" . urlencode($details)) : "") ;
        $out = $google_url . $action . $title . $dates . $location . $details;
    
        return $out;
    }
    function getIcalDate($time, $incl_time = true)
    {
        return $incl_time ? date('Ymd\THis', $time) : date('Ymd', $time);
    }

    Then create a shortcode [googlecalendar]

    <?php
    add_shortcode( 'googlecalendar', function() {
        $start_date = rwmb_get_value( 'ci_event_start_date', array( 'format' => 'Y-m-d g:iA' ) );
        $start_date = wp_date( 'Ymd\THis', $start_date );
        $end_date = rwmb_get_value( 'ci_event_end_date', array( 'format' => 'Y-m-d g:iA' ) );
        $end_date = wp_date( 'Ymd\THis', $end_date );
        $title = get_the_title() ;
        $location = rwmb_meta( 'ci_event_location_address' ) ;
            $urlgc = addToGoogleCalendar($title, $start_date, $end_date, $location, $title);
         
    return $urlgc;
    }
    );
    ?>

    The result of the shortcode is a link with all the data of my custom post "events) to create an event in google calendar.

Viewing 10 replies - 1 through 10 (of 10 total)
  • You must be logged in to reply to this topic.