Advertisement
Guest User

EM Recurring Tickets: classes/em-event.php

a guest
May 9th, 2013
295
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 98.05 KB | None | 0 0
  1. <?php
  2. /**
  3.  * Get an event in a db friendly way, by checking globals and passed variables to avoid extra class instantiations
  4.  * @param mixed $id
  5.  * @param mixed $search_by
  6.  * @return EM_Event
  7.  */
  8. function em_get_event($id = false, $search_by = 'event_id') {
  9.     global $EM_Event;
  10.     //check if it's not already global so we don't instantiate again
  11.     if( is_object($EM_Event) && get_class($EM_Event) == 'EM_Event' ){
  12.         if( is_object($id) && $EM_Event->post_id == $id->ID ){
  13.             return apply_filters('em_get_event', $EM_Event);
  14.         }elseif( !is_object($id) ){
  15.             if( $search_by == 'event_id' && $EM_Event->event_id == $id ){
  16.                 return apply_filters('em_get_event', $EM_Event);
  17.             }elseif( $search_by == 'post_id' && $EM_Event->post_id == $id ){
  18.                 return apply_filters('em_get_event', $EM_Event);
  19.             }
  20.         }
  21.     }
  22.     if( is_object($id) && get_class($id) == 'EM_Event' ){
  23.         return apply_filters('em_get_event', $id);
  24.     }else{
  25.         return apply_filters('em_get_event', new EM_Event($id,$search_by));
  26.     }
  27. }
  28. /**
  29.  * Event Object. This holds all the info pertaining to an event, including location and recurrence info.
  30.  * An event object can be one of three "types" a recurring event, recurrence of a recurring event, or a single event.
  31.  * The single event might be part of a set of recurring events, but if loaded by specific event id then any operations and saves are
  32.  * specifically done on this event. However, if you edit the recurring group, any changes made to single events are overwritten.
  33.  *
  34.  * @author marcus
  35.  */
  36. //TODO Can add more recurring functionality such as "also update all future recurring events" or "edit all events" like google calendar does.
  37. //TODO Integrate recurrences into events table
  38. //FIXME If you create a super long recurrence timespan, there could be thousands of events... need an upper limit here.
  39. class EM_Event extends EM_Object{
  40.     /* Field Names */
  41.     var $event_id;
  42.     var $post_id;
  43.     var $event_slug;
  44.     var $event_owner;
  45.     var $event_name;
  46.     var $event_start_time;
  47.     var $event_end_time;
  48.     var $event_all_day;
  49.     var $event_start_date;
  50.     var $event_end_date;
  51.     var $post_content;
  52.     var $event_rsvp;
  53.     var $event_rsvp_date;
  54.     var $event_rsvp_time = "00:00:00";
  55.     var $event_spaces;
  56.     var $location_id;
  57.     var $recurrence_id;
  58.     var $event_status;
  59.     var $event_date_created;
  60.     var $event_date_modified;
  61.     var $blog_id;
  62.     var $group_id; 
  63.     /**
  64.      * Populated with the non-hidden event post custom fields (i.e. not starting with _)
  65.      * @var array
  66.      */
  67.     var $event_attributes = array();
  68.     /* Recurring Specific Values */
  69.     var $recurrence;
  70.     var $recurrence_interval;
  71.     var $recurrence_freq;
  72.     var $recurrence_byday;
  73.     var $recurrence_days = 0;
  74.     var $recurrence_byweekno;
  75.     /* anonymous submission information */
  76.     var $event_owner_anonymous;
  77.     var $event_owner_name;
  78.     var $event_owner_email;
  79.     /**
  80.      * Previously used to give this object shorter property names for db values (each key has a name) but this is now depreciated, use the db field names as properties. This propertey provides extra info about the db fields.
  81.      * @var array
  82.      */
  83.     var $fields = array(
  84.         'event_id' => array( 'name'=>'id', 'type'=>'%d' ),
  85.         'post_id' => array( 'name'=>'post_id', 'type'=>'%d' ),
  86.         'event_slug' => array( 'name'=>'slug', 'type'=>'%s', 'null'=>true ),
  87.         'event_owner' => array( 'name'=>'owner', 'type'=>'%d', 'null'=>true ),
  88.         'event_name' => array( 'name'=>'name', 'type'=>'%s', 'null'=>true ),
  89.         'event_start_time' => array( 'name'=>'start_time', 'type'=>'%s', 'null'=>true ),
  90.         'event_end_time' => array( 'name'=>'end_time', 'type'=>'%s', 'null'=>true ),
  91.         'event_all_day' => array( 'name'=>'all_day', 'type'=>'%d', 'null'=>true ),
  92.         'event_start_date' => array( 'name'=>'start_date', 'type'=>'%s', 'null'=>true ),
  93.         'event_end_date' => array( 'name'=>'end_date', 'type'=>'%s', 'null'=>true ),
  94.         'post_content' => array( 'name'=>'notes', 'type'=>'%s', 'null'=>true ),
  95.         'event_rsvp' => array( 'name'=>'rsvp', 'type'=>'%d', 'null'=>true ), //has a default, so can be null/excluded
  96.         'event_rsvp_date' => array( 'name'=>'rsvp_date', 'type'=>'%s', 'null'=>true ),
  97.         'event_rsvp_time' => array( 'name'=>'rsvp_time', 'type'=>'%s', 'null'=>true ),
  98.         'event_spaces' => array( 'name'=>'spaces', 'type'=>'%d', 'null'=>true),
  99.         'location_id' => array( 'name'=>'location_id', 'type'=>'%d', 'null'=>true ),
  100.         'recurrence_id' => array( 'name'=>'recurrence_id', 'type'=>'%d', 'null'=>true ),
  101.         'event_status' => array( 'name'=>'status', 'type'=>'%d', 'null'=>true ),
  102.         'event_private' => array( 'name'=>'status', 'type'=>'%d', 'null'=>true ),
  103.         'event_date_created' => array( 'name'=>'date_created', 'type'=>'%s', 'null'=>true ),
  104.         'event_date_modified' => array( 'name'=>'date_modified', 'type'=>'%s', 'null'=>true ),
  105.         'event_attributes' => array( 'name'=>'attributes', 'type'=>'%s', 'null'=>true ),
  106.         'blog_id' => array( 'name'=>'blog_id', 'type'=>'%d', 'null'=>true ),
  107.         'group_id' => array( 'name'=>'group_id', 'type'=>'%d', 'null'=>true ),
  108.         'recurrence' => array( 'name'=>'recurrence', 'type'=>'%d', 'null'=>true ), //every x day(s)/week(s)/month(s)
  109.         'recurrence_interval' => array( 'name'=>'interval', 'type'=>'%d', 'null'=>true ), //every x day(s)/week(s)/month(s)
  110.         'recurrence_freq' => array( 'name'=>'freq', 'type'=>'%s', 'null'=>true ), //daily,weekly,monthly?
  111.         'recurrence_days' => array( 'name'=>'days', 'type'=>'%d', 'null'=>true ), //daily,weekly,monthly?
  112.         'recurrence_byday' => array( 'name'=>'byday', 'type'=>'%s', 'null'=>true ), //if weekly or monthly, what days of the week?
  113.         'recurrence_byweekno' => array( 'name'=>'byweekno', 'type'=>'%d', 'null'=>true ), //if monthly which week (-1 is last)
  114.     );
  115.     var $post_fields = array('event_slug','event_owner','event_name','event_attributes','post_id','post_content'); //fields that won't be taken from the em_events table anymore
  116.     var $recurrence_fields = array('recurrence_interval', 'recurrence_freq', 'recurrence_days', 'recurrence_byday', 'recurrence_byweekno');
  117.    
  118.     var $image_url = '';
  119.     /**
  120.      * Timestamp of start date/time
  121.      * @var int
  122.      */
  123.     var $start;
  124.     /**
  125.      * Timestamp of end date/time
  126.      * @var int
  127.      */
  128.     var $end;
  129.     /**
  130.      * Timestamp for booking cut-off date/time
  131.      * @var int
  132.      */
  133.     var $rsvp_end;
  134.     /**
  135.      * Created on timestamp, taken from DB, converted to TS
  136.      * @var int
  137.      */
  138.     var $created;
  139.     /**
  140.      * Created on timestamp, taken from DB, converted to TS
  141.      * @var int
  142.      */
  143.     var $modified;
  144.    
  145.     /**
  146.      * @var EM_Location
  147.      */
  148.     var $location;
  149.     /**
  150.      * @var EM_Bookings
  151.      */
  152.     var $bookings;
  153.     /**
  154.      * The contact person for this event
  155.      * @var WP_User
  156.      */
  157.     var $contact;
  158.     /**
  159.      * The category object
  160.      * @var EM_Category
  161.      */
  162.     var $category;
  163.     /**
  164.      * If there are any errors, they will be added here.
  165.      * @var array
  166.      */
  167.     var $errors = array(); 
  168.     /**
  169.      * If something was successful, a feedback message might be supplied here.
  170.      * @var string
  171.      */
  172.     var $feedback_message;
  173.     /**
  174.      * Any warnings about an event (e.g. bad data, recurrence, etc.)
  175.      * @var string
  176.      */
  177.     var $warnings;
  178.     /**
  179.      * Array of dbem_event field names required to create an event
  180.      * @var array
  181.      */
  182.     var $required_fields = array('event_name', 'event_start_date');
  183.     var $mime_types = array(1 => 'gif', 2 => 'jpg', 3 => 'png');
  184.     /**
  185.      * previous status of event when instantiated
  186.      * @access protected
  187.      * @var mixed
  188.      */
  189.     var $previous_status = 0;
  190.    
  191.     /* Post Variables - copied out of post object for easy IDE reference */
  192.     var $ID;
  193.     var $post_author;
  194.     var $post_date;
  195.     var $post_date_gmt;
  196.     var $post_title;
  197.     var $post_excerpt;
  198.     var $post_status;
  199.     var $comment_status;
  200.     var $ping_status;
  201.     var $post_password;
  202.     var $post_name;
  203.     var $to_ping;
  204.     var $pinged;
  205.     var $post_modified;
  206.     var $post_modified_gmt;
  207.     var $post_content_filtered;
  208.     var $post_parent;
  209.     var $guid;
  210.     var $menu_order;
  211.     var $post_type;
  212.     var $post_mime_type;
  213.     var $comment_count;
  214.     var $ancestors;
  215.     var $filter;
  216.    
  217.     /**
  218.      * Initialize an event. You can provide event data in an associative array (using database table field names), an id number, or false (default) to create empty event.
  219.      * @param mixed $event_data
  220.      * @param mixed $search_by default is post_id, otherwise it can be by event_id as well.
  221.      * @return null
  222.      */
  223.     function __construct($id = false, $search_by = 'event_id') {
  224.         global $wpdb;
  225.         if( is_array($id) ){
  226.             //deal with the old array style, but we can't supply arrays anymore
  227.             $id = (!empty($id['event_id'])) ? $id['event_id'] : $id['post_id'];
  228.             $search_by = (!empty($id['event_id'])) ? 'event_id':'post_id';
  229.         }
  230.         $is_post = !empty($id->ID) && ($id->post_type == EM_POST_TYPE_EVENT || $id->post_type == 'event-recurring');
  231.         if( is_numeric($id) || $is_post ){ //only load info if $id is a number
  232.             if($search_by == 'event_id' && !$is_post ){
  233.                 //search by event_id, get post_id and blog_id (if in ms mode) and load the post
  234.                 $results = $wpdb->get_row($wpdb->prepare("SELECT post_id, blog_id FROM ".EM_EVENTS_TABLE." WHERE event_id=%d",$id), ARRAY_A);
  235.                 if( is_multisite() && (is_numeric($results['blog_id']) || $results['blog_id']=='' ) ){
  236.                     if( $results['blog_id']=='' )  $results['blog_id'] = get_current_site()->blog_id;
  237.                     $event_post = get_blog_post($results['blog_id'], $results['post_id']);
  238.                     $search_by = $results['blog_id'];
  239.                 }else{
  240.                     $event_post = get_post($results['post_id']);   
  241.                 }
  242.             }else{
  243.                 if(!$is_post){
  244.                     if( is_multisite() && (is_numeric($search_by) || $search_by == '') ){
  245.                         if( $search_by == '' ) $search_by = get_current_site()->blog_id;
  246.                         //we've been given a blog_id, so we're searching for a post id
  247.                         $event_post = get_blog_post($search_by, $id);
  248.                     }else{
  249.                         //search for the post id only
  250.                         $event_post = get_post($id);
  251.                     }
  252.                 }else{
  253.                     $event_post = $id;
  254.                 }
  255.             }
  256.             $this->load_postdata($event_post, $search_by);
  257.         }
  258.         $this->recurrence = $this->is_recurring() ? 1:0;
  259.         //if(defined('trashtest')){ print_r($this); die("got here");}
  260.         //Do it here so things appear in the po file.
  261.         $this->status_array = array(
  262.             0 => __('Pending','dbem'),
  263.             1 => __('Approved','dbem')
  264.         );
  265.         do_action('em_event', $this, $id, $search_by);
  266.     }
  267.    
  268.     function load_postdata($event_post, $search_by = false){
  269.         if( is_object($event_post) ){
  270.             //load post data - regardless
  271.             $this->post_id = $event_post->ID;
  272.             $this->event_name = $event_post->post_title;
  273.             $this->event_owner = $event_post->post_author;
  274.             $this->post_content = $event_post->post_content;
  275.             $this->event_slug = $event_post->post_name;
  276.             $this->event_modified = $event_post->post_modified;
  277.             foreach( $event_post as $key => $value ){ //merge post object into this object
  278.                 $this->$key = $value;
  279.             }
  280.             $this->previous_status = $this->event_status; //so we know about updates
  281.             $this->recurrence = $this->is_recurring() ? 1:0;
  282.             //load meta data and other related information
  283.             if( $event_post->post_status != 'auto-draft' ){
  284.                 $event_meta = $this->get_event_meta($search_by);
  285.                 //Get custom fields and post meta
  286.                 foreach($event_meta as $event_meta_key => $event_meta_val){
  287.                     $field_name = substr($event_meta_key, 1);
  288.                     if($event_meta_key[0] != '_'){
  289.                         $this->event_attributes[$event_meta_key] = ( count($event_meta_val) > 1 ) ? $event_meta_val:$event_meta_val[0];                
  290.                     }elseif( is_string($field_name) && !in_array($field_name, $this->post_fields) ){
  291.                         if( array_key_exists($field_name, $this->fields) ){
  292.                             $this->$field_name = $event_meta_val[0];
  293.                         }elseif( in_array($field_name, array('event_owner_name','event_owner_anonymous','event_owner_email')) ){
  294.                             $this->$field_name = $event_meta_val[0];
  295.                         }
  296.                     }
  297.                 }
  298.                 //Start/End times should be available as timestamp
  299.                 $this->start = strtotime($this->event_start_date." ".$this->event_start_time);
  300.                 $this->end = strtotime($this->event_end_date." ".$this->event_end_time);
  301.                 if( !empty($this->event_rsvp_date ) ){
  302.                     $this->rsvp_end = strtotime($this->event_rsvp_date." ".$this->event_rsvp_time);
  303.                 }
  304.                 //quick compatability fix in case _event_id isn't loaded or somehow got erased in post meta
  305.                 if( empty($this->event_id) && !$this->is_recurring() ){
  306.                     global $wpdb;
  307.                     $event_array = $wpdb->get_row('SELECT * FROM '.EM_EVENTS_TABLE. ' WHERE post_id='.$event_post->ID, ARRAY_A);
  308.                     if( !empty($event_array['event_id']) ){
  309.                         foreach($event_array as $key => $value){
  310.                             if( !empty($value) && empty($this->$key) ){
  311.                                 update_post_meta($event_post->ID, '_'.$key, $value);
  312.                                 $this->$key = $value;
  313.                             }
  314.                         }
  315.                     }
  316.                 }
  317.             }
  318.             $this->get_status();
  319.             $this->compat_keys();
  320.         }
  321.     }
  322.    
  323.     function get_event_meta($blog_id = false){
  324.         if( is_numeric($blog_id) && $blog_id > 0 && is_multisite() ){
  325.             // if in multisite mode, switch blogs quickly to get the right post meta.
  326.             switch_to_blog($blog_id);
  327.             $event_meta = get_post_meta($this->post_id);
  328.             restore_current_blog();
  329.             $this->blog_id = $blog_id;
  330.         }else{
  331.             $event_meta = get_post_meta($this->post_id);
  332.         }
  333.         return $event_meta;
  334.     }
  335.    
  336.     /**
  337.      * Retrieve event information via POST (only used in situations where posts aren't submitted via WP)
  338.      * @return boolean
  339.      */
  340.     function get_post($validate = true){   
  341.         global $allowedposttags;
  342.         //we need to get the post/event name and content.... that's it.
  343.         $this->post_content = isset($_POST['content']) ? wp_kses( stripslashes($_POST['content']), $allowedposttags):'';
  344.         $this->event_name = !empty($_POST['event_name']) ? htmlspecialchars_decode(wp_kses_data(htmlspecialchars_decode(stripslashes($_POST['event_name'])))):'';
  345.         $this->post_type = ($this->is_recurring() || !empty($_POST['recurring'])) ? 'event-recurring':EM_POST_TYPE_EVENT;
  346.         //don't forget categories!
  347.         $this->get_categories()->get_post();
  348.         //anonymous submissions and guest basic info
  349.         if( !is_user_logged_in() && get_option('dbem_events_anonymous_submissions') && empty($this->event_id) ){
  350.             $this->event_owner_anonymous = 1;
  351.             $this->event_owner_name = !empty($_POST['event_owner_name']) ? wp_kses_data(stripslashes($_POST['event_owner_name'])):'';
  352.             $this->event_owner_email = !empty($_POST['event_owner_email']) ? wp_kses_data($_POST['event_owner_email']):'';
  353.         }
  354.         //get the rest and validate (optional)
  355.         $this->get_post_meta(false);
  356.         $result = $validate ? $this->validate():true; //validate both post and meta, otherwise return true
  357.         return apply_filters('em_event_get_post', $result, $this);     
  358.     }
  359.    
  360.     /**
  361.      * Retrieve event post meta information via POST, which should be always be called when saving the event custom post via WP.
  362.      * @param boolean $validate whether or not to run validation, default is true
  363.      * @return boolean
  364.      */
  365.     function get_post_meta($validate = true){
  366.         //Grab POST data   
  367.         $this->event_start_date = ( !empty($_POST['event_start_date']) ) ? wp_kses_data($_POST['event_start_date']) : '';
  368.         $this->event_end_date = ( !empty($_POST['event_end_date']) ) ? wp_kses_data($_POST['event_end_date']) : $this->event_start_date;
  369.         //check if this is recurring or not
  370.         if( !empty($_POST['recurring']) ){
  371.             $this->recurrence = 1;
  372.             $this->post_type = 'event-recurring';
  373.         }
  374.         //Get Location info
  375.         if( !get_option('dbem_locations_enabled') || (!empty($_POST['no_location']) && !get_option('dbem_require_location',true)) || (empty($_POST['location_id']) && !get_option('dbem_require_location',true) && get_option('dbem_use_select_for_locations')) ){
  376.             $this->location_id = 0;
  377.         }elseif( !empty($_POST['location_id']) && is_numeric($_POST['location_id']) ){
  378.             $this->location_id = $_POST['location_id'];
  379.         }else{
  380.             //we're adding a new location, so create an empty location and populate
  381.             $this->location_id = null;
  382.             $this->get_location()->get_post(false);
  383.             $this->get_location()->post_content = ''; //reset post content, as it'll grab the event description otherwise
  384.         }
  385.         //Sort out time
  386.         $this->event_all_day = ( !empty($_POST['event_all_day']) ) ? 1 : 0;
  387.         if( !$this->event_all_day ){
  388.             $match = array();
  389.             foreach( array('event_start_time','event_end_time', 'event_rsvp_time') as $timeName ){
  390.                 if( !empty($_POST[$timeName]) && preg_match ( '/^([01]\d|2[0-3]):([0-5]\d) ?(AM|PM)?$/', $_POST[$timeName], $match ) ){
  391.                     if( !empty($match[3]) && $match[3] == 'PM' && $match[1] != 12 ){
  392.                         $match[1] = 12+$match[1];
  393.                     }elseif( !empty($match[3]) && $match[3] == 'AM' && $match[1] == 12 ){
  394.                         $match[1] = '00';
  395.                     }
  396.                     $this->$timeName = $match[1].":".$match[2].":00";
  397.                 }else{
  398.                     $this->$timeName = ($timeName == 'event_start_time') ? "00:00:00":$this->event_start_time;
  399.                 }
  400.             }
  401.         }else{
  402.             $this->event_start_time = $this->event_end_time = '00:00:00';
  403.         }
  404.         //Start/End times should be available as timestamp
  405.         $this->start = strtotime($this->event_start_date." ".$this->event_start_time);
  406.         $this->end = strtotime($this->event_end_date." ".$this->event_end_time);
  407.         //Bookings
  408.         if( !empty($_POST['event_rsvp']) && $_POST['event_rsvp'] ){
  409.             $this->get_bookings()->get_tickets()->get_post();
  410.             $this->event_rsvp = 1;
  411.             //RSVP cuttoff TIME is set up above where start/end times are as well
  412.             if( !$this->is_recurring() ){
  413.                 $this->event_rsvp_date = ( isset($_POST['event_rsvp_date']) ) ? wp_kses_data($_POST['event_rsvp_date']) : $this->event_start_date;
  414.                 if( empty($this->event_rsvp_date) ){ $this->event_rsvp_time = '00:00:00'; }
  415.             }
  416.             $this->event_spaces = ( isset($_POST['event_spaces']) ) ? absint($_POST['event_spaces']):0;
  417.         }else{
  418.             $this->event_rsvp = 0;
  419.             $this->event_rsvp_time = '00:00:00';
  420.         }
  421.         //Sort out event attributes - note that custom post meta now also gets inserted here automatically (and is overwritten by these attributes)
  422.         if(get_option('dbem_attributes_enabled')){
  423.             global $allowedtags;
  424.             if( !is_array($this->event_attributes) ){ $this->event_attributes = array(); }
  425.             $event_available_attributes = em_get_attributes();
  426.             if( !empty($_POST['em_attributes']) && is_array($_POST['em_attributes']) ){
  427.                 foreach($_POST['em_attributes'] as $att_key => $att_value ){
  428.                     if( (in_array($att_key, $event_available_attributes['names']) || array_key_exists($att_key, $this->event_attributes) ) ){
  429.                         $this->event_attributes[$att_key] = '';
  430.                         $att_vals = count($event_available_attributes['values'][$att_key]);
  431.                         if( !empty($att_value) ){
  432.                             if( $att_vals <= 1 || ($att_vals > 1 && in_array($att_value, $event_available_attributes['values'][$att_key])) ){
  433.                                 $this->event_attributes[$att_key] = stripslashes($att_value);
  434.                             }
  435.                         }
  436.                         if( empty($att_value) && $att_vals > 1){
  437.                             $this->event_attributes[$att_key] = stripslashes(wp_kses($event_available_attributes['values'][$att_key][0], $allowedtags));
  438.                         }
  439.                     }
  440.                 }
  441.             }
  442.         }
  443.         //Set Blog ID
  444.         if( is_multisite() ){
  445.             $this->blog_id = get_current_blog_id();
  446.         }
  447.         //group id
  448.         $this->group_id = (!empty($_POST['group_id']) && is_numeric($_POST['group_id'])) ? $_POST['group_id']:0;
  449.         //Recurrence data
  450.         if( $this->is_recurring() ){
  451.             $this->recurrence = 1; //just in case
  452.             $this->recurrence_freq = ( !empty($_POST['recurrence_freq']) && in_array($_POST['recurrence_freq'], array('daily','weekly','monthly','yearly')) ) ? $_POST['recurrence_freq']:'daily';
  453.             if( !empty($_POST['recurrence_bydays']) && $this->recurrence_freq == 'weekly' && self::array_is_numeric($_POST['recurrence_bydays']) ){
  454.                 $this->recurrence_byday = implode( ",", $_POST['recurrence_bydays'] );
  455.             }elseif( !empty($_POST['recurrence_byday']) && $this->recurrence_freq == 'monthly' ){
  456.                 $this->recurrence_byday = wp_kses_data($_POST['recurrence_byday']);
  457.             }
  458.             $this->recurrence_interval = ( !empty($_POST['recurrence_interval']) && is_numeric($_POST['recurrence_interval']) ) ? $_POST['recurrence_interval']:1;
  459.             $this->recurrence_byweekno = ( !empty($_POST['recurrence_byweekno']) ) ? wp_kses_data($_POST['recurrence_byweekno']):'';
  460.             $this->recurrence_days = ( !empty($_POST['recurrence_days']) && is_numeric($_POST['recurrence_days']) ) ? (int) $_POST['recurrence_days']:0;
  461.         }
  462.         //categories in MS GLobal
  463.         if(EM_MS_GLOBAL && !is_main_site()){
  464.             $this->get_categories()->get_post(); //it'll know what to do
  465.         }
  466.         //validate (optional) and return result
  467.         $this->compat_keys(); //compatability
  468.         return apply_filters('em_event_get_post', count($this->errors) == 0, $this);
  469.     }
  470.    
  471.     function validate(){
  472.         $validate_post = true;
  473.         if( empty($this->event_name) ){
  474.             $validate_post = false;
  475.             $this->add_error( sprintf(__("%s is required.", "dbem"), __('Event name','dbem')) );
  476.         }
  477.         //anonymous submissions and guest basic info
  478.         if( !empty($this->event_owner_anonymous) ){
  479.             if( !is_email($this->event_owner_email) ){
  480.                 $this->add_error( sprintf(__("%s is required.", "dbem"), __('A valid email','dbem')) );
  481.             }
  482.             if( empty($this->event_owner_name) ){
  483.                 $this->add_error( sprintf(__("%s is required.", "dbem"), __('Your name','dbem')) );
  484.             }
  485.         }
  486.         $validate_tickets = $this->get_bookings()->get_tickets()->validate();
  487.         $validate_image = $this->image_validate();
  488.         $validate_meta = $this->validate_meta();
  489.         return apply_filters('em_event_validate', $validate_post && $validate_image && $validate_meta && $validate_tickets, $this );       
  490.     }
  491.     function validate_meta(){
  492.         $missing_fields = Array ();
  493.         foreach ( array('event_start_date') as $field ) {
  494.             if ( $this->$field == "") {
  495.                 $missing_fields[$field] = $field;
  496.             }
  497.         }
  498.         if( preg_match('/\d{4}-\d{2}-\d{2}/', $this->event_start_date) && preg_match('/\d{4}-\d{2}-\d{2}/', $this->event_end_date) ){
  499.             if( strtotime($this->event_start_date . $this->event_start_time) > strtotime($this->event_end_date . $this->event_end_time) ){
  500.                 $this->add_error(__('Events cannot start after they end.','dbem'));
  501.             }elseif( $this->is_recurring() && $this->recurrence_days == 0 && strtotime($this->event_start_date . $this->event_start_time) > strtotime($this->event_start_date . $this->event_end_time) ){
  502.                 $this->add_error(__('Events cannot start after they end.','dbem').' '.__('For recurring events that end the following day, ensure you make your event last 1 or more days.'));
  503.             }
  504.         }else{
  505.             if( !empty($missing_fields['event_start_date']) ) { unset($missing_fields['event_start_date']); }
  506.             if( !empty($missing_fields['event_end_date']) ) { unset($missing_fields['event_end_date']); }
  507.             $this->add_error(__('Dates must have correct formatting. Please use the date picker provided.','dbem'));
  508.         }
  509.         if( $this->event_rsvp ){
  510.             if( !$this->get_bookings()->get_tickets()->validate() ){
  511.                 $this->add_error($this->get_bookings()->get_tickets()->get_errors());
  512.             }
  513.             if( !empty($this->event_rsvp_date) && !preg_match('/\d{4}-\d{2}-\d{2}/', $this->event_rsvp_date) ){
  514.                 $this->add_error(__('Dates must have correct formatting. Please use the date picker provided.','dbem'));
  515.             }
  516.         }
  517.         if( get_option('dbem_locations_enabled') && empty($this->location_id) ){ //location ids don't need validating as we're not saving a location
  518.             if( get_option('dbem_require_location',true) || $this->location_id !== 0 ){
  519.                 if( !$this->get_location()->validate() ){
  520.                     $this->add_error($this->get_location()->get_errors());
  521.                 }
  522.             }
  523.         }
  524.         if ( count($missing_fields) > 0){
  525.             // TODO Create friendly equivelant names for missing fields notice in validation
  526.             $this->add_error( __( 'Missing fields: ', 'dbem') . implode ( ", ", $missing_fields ) . ". " );
  527.         }
  528.         if ( $this->is_recurring() && ($this->event_end_date == "" || $this->event_end_date == $this->event_start_date) ){
  529.             $this->add_error( __( 'Since the event is repeated, you must specify an event end date greater than the start date.', 'dbem' ));
  530.         }
  531.         return apply_filters('em_event_validate_meta', count($this->errors) == 0, $this );
  532.     }
  533.    
  534.     /**
  535.      * Will save the current instance into the database, along with location information if a new one was created and return true if successful, false if not.
  536.      * Will automatically detect whether it's a new or existing event.
  537.      * @return boolean
  538.      */
  539.     function save(){
  540.         global $wpdb, $current_user, $blog_id;
  541.         if( !$this->can_manage('edit_events', 'edit_others_events') && !( get_option('dbem_events_anonymous_submissions') && empty($this->event_id)) ){
  542.             //unless events can be submitted by an anonymous user (and this is a new event), user must have permissions.
  543.             return apply_filters('em_event_save', false, $this);
  544.         }
  545.         remove_action('save_post',array('EM_Event_Post_Admin','save_post'),10,1); //disable the default save post action, we'll do it manually this way
  546.         do_action('em_event_save_pre', $this);
  547.         $post_array = array();
  548.         //Deal with updates to an event
  549.         if( !empty($this->post_id) ){
  550.             //get the full array of post data so we don't overwrite anything.
  551.             if( !empty($this->blog_id) && is_multisite() ){
  552.                 $post_array = (array) get_blog_post($this->blog_id, $this->post_id);
  553.             }else{
  554.                 $post_array = (array) get_post($this->post_id);
  555.             }
  556.         }
  557.         //Overwrite new post info
  558.         $post_array['post_type'] = ($this->recurrence && get_option('dbem_recurrence_enabled')) ? 'event-recurring':EM_POST_TYPE_EVENT;
  559.         $post_array['post_title'] = $this->event_name;
  560.         $post_array['post_content'] = $this->post_content;
  561.         //decide on post status
  562.         if( empty($this->force_status) ){
  563.             if( count($this->errors) == 0 ){
  564.                 $post_array['post_status'] = ( $this->can_manage('publish_events','publish_events') ) ? 'publish':'pending';
  565.             }else{
  566.                 $post_array['post_status'] = 'draft';
  567.             }
  568.         }else{
  569.             $post_array['post_status'] = $this->force_status;
  570.         }
  571.         //anonymous submission only
  572.         if( !is_user_logged_in() && get_option('dbem_events_anonymous_submissions') && empty($this->event_id) ){
  573.             $post_array['post_author'] = get_option('dbem_events_anonymous_user');
  574.             if( !is_numeric($post_array['post_author']) ) $post_array['post_author'] = 0;
  575.         }
  576.         //Save post and continue with meta
  577.         $post_id = wp_insert_post($post_array);
  578.         $post_save = false;
  579.         $meta_save = false;
  580.         if( !is_wp_error($post_id) && !empty($post_id) ){
  581.             $post_save = true;
  582.             //refresh this event with wp post info we'll put into the db
  583.             $post_data = get_post($post_id);
  584.             $this->post_id = $post_id;
  585.             $this->event_slug = $post_data->post_name;
  586.             $this->event_owner = $post_data->post_author;
  587.             $this->post_status = $post_data->post_status;
  588.             $this->get_status();
  589.             //Categories? note that categories will soft-fail, so no errors
  590.             $this->get_categories()->event_id = $this->event_id;
  591.             $this->categories->post_id = $this->post_id;
  592.             $this->categories->save();
  593.             //anonymous submissions should save this information
  594.             if( !empty($this->event_owner_anonymous) ){
  595.                 update_post_meta($this->post_id, '_event_owner_anonymous', 1);
  596.                 update_post_meta($this->post_id, '_event_owner_name', $this->event_owner_name);
  597.                 update_post_meta($this->post_id, '_event_owner_email', $this->event_owner_email);
  598.             }
  599.             //save the image
  600.             $this->image_upload();
  601.             //now save the meta
  602.             $meta_save = $this->save_meta();
  603.             $image_save = (count($this->errors) == 0); //whilst it might not be an image save that fails, we can know something went wrong
  604.         }
  605.         $result = $meta_save && $post_save && $image_save;
  606.         $previous_status = $this->previous_status;
  607.         if($result) $this->load_postdata($post_data, $blog_id); //reload post info
  608.         $this->previous_status = $previous_status;
  609.         //do a dirty update for location too if it's not published
  610.         if( $this->is_published() && !empty($this->location_id) ){
  611.             $EM_Location = $this->get_location();
  612.             if( $EM_Location->location_status !== 1 ){
  613.                 //let's also publish the location
  614.                 $EM_Location->set_status(1, true);
  615.             }
  616.         }
  617.         return apply_filters('em_event_save', $result, $this);
  618.     }
  619.    
  620.     function save_meta(){
  621.         global $wpdb;
  622.         if( ( get_option('dbem_events_anonymous_submissions') && empty($this->event_id)) || $this->can_manage('edit_events', 'edit_others_events') ){
  623.             do_action('em_event_save_meta_pre', $this);
  624.             //first save location
  625.             if( empty($this->location_id) && !($this->location_id === 0 && !get_option('dbem_require_location',true)) ){
  626.                 if( !$this->get_location()->save() ){ //soft fail
  627.                     global $EM_Notices;
  628.                     if( !empty($this->get_location()->location_id) ){
  629.                         $EM_Notices->add_error( __('There were some errors saving your location.','dbem').' '.sprintf(__('It will not be displayed on the website listings, to correct this you must <a href="%s">edit your location</a> directly.'),$this->get_location()->output('#_LOCATIONEDITURL')), true);
  630.                     }else{
  631.                         $this->get_location()->set_status(null);
  632.                         $EM_Notices->add_error( __('There were some errors saving your location.'), true);
  633.                     }
  634.                 }
  635.                 if( !empty($this->location->location_id) ){ //only case we don't use get_location(), since it will fail as location has an id, whereas location_id isn't set in this object
  636.                     $this->location_id = $this->location->location_id;
  637.                 }
  638.             }
  639.             //Update Post Meta
  640.             foreach($this->fields as $key => $field_info){
  641.                 if( !in_array($key, $this->post_fields) && $key != 'event_attributes' ){
  642.                     update_post_meta($this->post_id, '_'.$key, $this->$key);
  643.                 }elseif($key == 'event_attributes'){
  644.                     //attributes get saved as individual keys
  645.                     foreach($this->event_attributes as $event_attribute_key => $event_attribute){
  646.                         if( !empty($event_attribute) ){
  647.                             update_post_meta($this->post_id, $event_attribute_key, $event_attribute);
  648.                         }else{
  649.                             delete_post_meta($this->post_id, $event_attribute_key);
  650.                         }
  651.                     }
  652.                 }
  653.             }
  654.             //update timestampes
  655.             update_post_meta($this->post_id, '_start_ts', str_pad($this->start, 10, 0, STR_PAD_LEFT));
  656.             update_post_meta($this->post_id, '_end_ts', str_pad($this->end, 10, 0, STR_PAD_LEFT));
  657.             //sort out event status        
  658.             $result = count($this->errors) == 0;
  659.             $this->get_status();
  660.             $this->event_status = ($result) ? $this->event_status:null; //set status at this point, it's either the current status, or if validation fails, null
  661.             //Save to em_event table
  662.             $event_array = $this->to_array(true);
  663.             unset($event_array['event_id']);
  664.             //decide whether or not event is private at this point
  665.             $event_array['event_private'] = ( $this->post_status == 'private' ) ? 1:0;
  666.             //save event_attributes just in case
  667.             $event_array['event_attributes'] = serialize($this->event_attributes);
  668.             //check if event truly exists, meaning the event_id is actually a valid event id
  669.             if( !empty($this->event_id) ){
  670.                 $blog_condition = '';
  671.                 if( EM_MS_GLOBAL ){
  672.                     if( is_main_site() ){
  673.                         $blog_condition = " AND (blog_id='".get_current_blog_id()."' OR blog_id IS NULL)";
  674.                     }else{
  675.                         $blog_condition = " AND blog_id='".get_current_blog_id()."' ";
  676.                     }
  677.                 }
  678.                 $event_truly_exists = $wpdb->get_var('SELECT post_id FROM '.EM_EVENTS_TABLE." WHERE event_id={$this->event_id}".$blog_condition) == $this->post_id;
  679.             }else{
  680.                 $event_truly_exists = false;
  681.             }
  682.             //save all the meta
  683.             if( empty($this->event_id) || !$event_truly_exists ){
  684.                 $this->previous_status = 0; //for sure this was previously status 0
  685.                 $this->event_date_created = current_time('mysql');
  686.                 if ( !$wpdb->insert(EM_EVENTS_TABLE, $event_array) ){
  687.                     $this->add_error( sprintf(__('Something went wrong saving your %s to the index table. Please inform a site administrator about this.','dbem'),__('event','dbem')));
  688.                 }else{
  689.                     //success, so link the event with the post via an event id meta value for easy retrieval
  690.                     $this->event_id = $wpdb->insert_id;
  691.                     update_post_meta($this->post_id, '_event_id', $this->event_id);
  692.                     $this->feedback_message = sprintf(__('Successfully saved %s','dbem'),__('Event','dbem'));
  693.                     $just_added_event = true; //make an easy hook
  694.                     do_action('em_event_save_new', $this);
  695.                 }
  696.             }else{
  697.                 $event_array['post_content'] = $this->post_content; //in case the content was removed, which is acceptable
  698.                 $this->previous_status = $this->get_previous_status();
  699.                 $this->event_date_modified = $event_array['event_date_modified'] = current_time('mysql');
  700.                 if ( $wpdb->update(EM_EVENTS_TABLE, $event_array, array('event_id'=>$this->event_id) ) === false ){
  701.                     $this->add_error( sprintf(__('Something went wrong updating your %s to the index table. Please inform a site administrator about this.','dbem'),__('event','dbem')));          
  702.                 }else{
  703.                     //Also set the status here if status != previous status
  704.                     if( $this->previous_status != $this->get_status()){
  705.                         $status_value = $this->get_status(true);
  706.                         $wpdb->query('UPDATE '.EM_EVENTS_TABLE." SET event_status=$status_value WHERE event_id=".$this->event_id);
  707.                     }
  708.                     $this->feedback_message = sprintf(__('Successfully saved %s','dbem'),__('Event','dbem'));
  709.                 }      
  710.             }
  711.             //Add/Delete Tickets
  712.             if($this->event_rsvp == 0){
  713.                 $this->get_bookings()->delete();
  714.             }else{
  715.                 if( !$this->get_bookings()->get_tickets()->save() ){
  716.                     $this->add_error( $this->get_bookings()->get_tickets()->get_errors() );
  717.                 }
  718.             }
  719.             $result = count($this->errors) == 0;
  720.             //If we're saving event categories in MS Global mode, we'll add them here, saving by term id (cat ids are gone now)
  721.             if( EM_MS_GLOBAL && !is_main_site() ){
  722.                 $this->get_categories()->save(); //it'll know what to do
  723.             }elseif( EM_MS_GLOBAL ){
  724.                 $this->get_categories()->save_index(); //just save to index, we assume cats are saved in $this->save();
  725.             }
  726.             //build recurrences if needed
  727.             if( $this->is_recurring() && $result && $this->is_published() ){ //only save events if recurring event validates and is published
  728.                 if( !$this->save_events() ){ //only save if post is 'published'
  729.                     $this->add_error(__ ( 'Something went wrong with the recurrence update...', 'dbem' ). __ ( 'There was a problem saving the recurring events.', 'dbem' ));
  730.                 }
  731.             }
  732.             if( !empty($just_added_event) ){
  733.                 do_action('em_event_added', $this);
  734.             }
  735.         }
  736.         $this->compat_keys();
  737.         return apply_filters('em_event_save_meta', count($this->errors) == 0, $this);
  738.     }
  739.    
  740.     /**
  741.      * Duplicates this event and returns the duplicated event. Will return false if there is a problem with duplication.
  742.      * @return EM_Event
  743.      */
  744.     function duplicate(){
  745.         global $wpdb, $EZSQL_ERROR;
  746.         //First, duplicate.
  747.         if( $this->can_manage('edit_events','edit_others_events') ){
  748.             $EM_Event = clone $this;
  749.             $EM_Event->get_categories(); //before we remove event/post ids
  750.             $EM_Event->get_bookings()->get_tickets(); //in case this wasn't loaded and before we reset ids
  751.             $EM_Event->event_id = null;
  752.             $EM_Event->post_id = null;
  753.             $EM_Event->ID = null;
  754.             $EM_Event->post_name = '';
  755.             $EM_Event->location_id = (empty($EM_Event->location_id)  && !get_option('dbem_require_location')) ? 0:$EM_Event->location_id;
  756.             $EM_Event->get_bookings()->event_id = null;
  757.             $EM_Event->get_bookings()->get_tickets()->event_id = null;
  758.             //if bookings reset ticket ids and duplicate tickets
  759.             foreach($EM_Event->get_bookings()->get_tickets()->tickets as $EM_Ticket){
  760.                 $EM_Ticket->ticket_id = null;
  761.                 $EM_Ticket->event_id = null;
  762.             }
  763.             do_action('em_event_duplicate_pre', $EM_Event);
  764.             $EM_Event->duplicated = true;
  765.             $EM_Event->force_status = 'draft';
  766.             if( $EM_Event->save() ){
  767.                 $EM_Event->feedback_message = sprintf(__("%s successfully duplicated.", 'dbem'), __('Event','dbem'));
  768.                 //other non-EM post meta inc. featured image
  769.                 $event_meta = $this->get_event_meta($this->blog_id);
  770.                 $event_meta['_event_approvals_count'] = 0; //reset this counter for new event
  771.                 $event_meta_inserts = array();
  772.                 //Get custom fields and post meta - adapted from $this->load_post_meta()
  773.                 foreach($event_meta as $event_meta_key => $event_meta_vals){
  774.                     if($event_meta_key[0] == '_' && is_array($event_meta_vals)){
  775.                         $field_name = substr($event_meta_key, 1);
  776.                         if($field_name != 'event_attributes' && !array_key_exists($field_name, $this->fields) && !in_array($field_name, array('edit_last', 'edit_lock', 'event_owner_name','event_owner_anonymous','event_owner_email')) ){
  777.                             foreach($event_meta_vals as $event_meta_val){
  778.                                 $event_meta_inserts[] = "({$EM_Event->post_id}, '{$event_meta_key}', '{$event_meta_val}')";
  779.                             }
  780.                         }
  781.                     }
  782.                 }
  783.                 //save in one SQL statement
  784.                 if( !empty($event_meta_inserts) ){
  785.                     $wpdb->query('INSERT INTO '.$wpdb->postmeta." (post_id, meta_key, meta_value) VALUES ".implode(', ', $event_meta_inserts));
  786.                 }
  787.                 //set event to draft status
  788.                 return apply_filters('em_event_duplicate', $EM_Event, $this);
  789.             }
  790.         }
  791.         //TODO add error notifications for duplication failures.
  792.         return apply_filters('em_event_duplicate', false, $this);;
  793.     }
  794.    
  795.     /**
  796.      * Delete whole event, including bookings, tickets, etc.
  797.      * @return boolean
  798.      */
  799.     function delete($force_delete = false){ //atm wp seems to force cp deletions anyway
  800.         global $wpdb;
  801.         if( $this->can_manage('delete_events', 'delete_others_events') ){
  802.             if( !is_admin() ){
  803.                 include_once('em-event-post-admin.php');
  804.                 if( !defined('EM_EVENT_DELETE_INCLUDE') ){
  805.                     EM_Event_Post_Admin::init();
  806.                     EM_Event_Recurring_Post_Admin::init();
  807.                     define('EM_EVENT_DELETE_INCLUDE',true);
  808.                 }
  809.             }
  810.             do_action('em_event_delete_pre', $this);
  811.             if( $force_delete ){
  812.                 $result = wp_delete_post($this->post_id,$force_delete);
  813.             }else{
  814.                 $result = wp_trash_post($this->post_id);
  815.             }
  816.         }else{
  817.             $result = false;
  818.         }
  819.         //print_r($result); echo "|"; print_r($result_meta); die('DELETING');
  820.         return apply_filters('em_event_delete', $result !== false, $this);
  821.     }
  822.    
  823.     function delete_meta(){
  824.         global $wpdb;
  825.         $result = false;
  826.         if( $this->can_manage('delete_events', 'delete_others_events') ){
  827.             do_action('em_event_delete_meta_event_pre', $this);
  828.             $result = $wpdb->query ( $wpdb->prepare("DELETE FROM ". EM_EVENTS_TABLE ." WHERE event_id=%d", $this->event_id) );
  829.             if( $result !== false ){
  830.                 $this->delete_bookings();
  831.                 $this->delete_tickets();
  832.                 //Delete the recurrences then this recurrence event
  833.                 if( $this->is_recurring() ){
  834.                     $result = $this->delete_events(); //was true at this point, so false if fails
  835.                 }
  836.                 //Delete categories from meta if in MS global mode
  837.                 if( EM_MS_GLOBAL ){
  838.                     $wpdb->query('DELETE FROM '.EM_META_TABLE.' WHERE object_id='.$this->event_id." AND meta_key='event-category'");
  839.                 }
  840.             }
  841.         }
  842.         return apply_filters('em_event_delete_meta', $result !== false, $this);
  843.     }
  844.    
  845.     /**
  846.      * Shortcut function for $this->get_bookings()->delete(), because using the EM_Bookings requires loading previous bookings, which isn't neceesary.
  847.      */
  848.     function delete_bookings(){
  849.         global $wpdb;
  850.         do_action('em_event_delete_bookings_pre', $this);
  851.         $result = false;
  852.         if( $this->can_manage('manage_bookings','manage_others_bookings') ){
  853.             $result_bt = $wpdb->query( $wpdb->prepare("DELETE FROM ".EM_TICKETS_BOOKINGS_TABLE." WHERE booking_id IN (SELECT booking_id FROM ".EM_BOOKINGS_TABLE." WHERE event_id=%d)", $this->event_id) );
  854.             $result = $wpdb->query( $wpdb->prepare("DELETE FROM ".EM_BOOKINGS_TABLE." WHERE event_id=%d", $this->event_id) );
  855.         }
  856.         return apply_filters('em_event_delete_bookings', $result !== false && $result_bt !== false, $this);
  857.     }
  858.    
  859.     /**
  860.      * Shortcut function for $this->get_bookings()->delete(), because using the EM_Bookings requires loading previous bookings, which isn't neceesary.
  861.      */
  862.     function delete_tickets(){
  863.         global $wpdb;
  864.         do_action('em_event_delete_tickets_pre', $this);
  865.         $result = false;
  866.         if( $this->can_manage('manage_bookings','manage_others_bookings') ){
  867.             $result_bt = $wpdb->query( $wpdb->prepare("DELETE FROM ".EM_TICKETS_BOOKINGS_TABLE." WHERE ticket_id IN (SELECT ticket_id FROM ".EM_TICKETS_TABLE." WHERE event_id=%d)", $this->event_id) );
  868.             $result = $wpdb->query( $wpdb->prepare("DELETE FROM ".EM_TICKETS_TABLE." WHERE event_id=%d", $this->event_id) );
  869.         }
  870.         return apply_filters('em_event_delete_tickets', $result, $this);
  871.     }
  872.    
  873.     /**
  874.      * Change the status of the event. This will save to the Database too.
  875.      * @param int $status
  876.      * @param boolean $set_post_status
  877.      * @return string
  878.      */
  879.     function set_status($status, $set_post_status = false){
  880.         global $wpdb;
  881.         if($status === null){
  882.             $set_status='NULL';
  883.             if($set_post_status){
  884.                 //if the post is trash, don't untrash it!
  885.                 $wpdb->update( $wpdb->posts, array( 'post_status' => 'draft' ), array( 'ID' => $this->post_id ) );
  886.                 $this->post_status = 'draft';
  887.             }
  888.         }else{
  889.             $set_status = $status ? 1:0;
  890.             if($set_post_status){
  891.                 if($this->post_status == 'pending'){
  892.                     $this->post_name = sanitize_title($this->post_title);
  893.                 }
  894.                 $this->post_status = $set_status ? 'publish':'pending';
  895.                 $wpdb->update( $wpdb->posts, array( 'post_status' => $this->post_status, 'post_name' => $this->post_name ), array( 'ID' => $this->post_id ) );
  896.             }      
  897.         }
  898.         $this->previous_status = $this->get_previous_status();
  899.         $result = $wpdb->query("UPDATE ".EM_EVENTS_TABLE." SET event_status=$set_status, event_slug='{$this->post_name}' WHERE event_id=".$this->event_id);
  900.         $this->get_status(); //reload status
  901.         return apply_filters('em_event_set_status', $result !== false, $status, $this);
  902.     }
  903.    
  904.     function is_published(){
  905.         return apply_filters('em_event_is_published', ($this->post_status == 'publish' || $this->post_status == 'private'), $this);
  906.     }
  907.    
  908.     function get_status($db = false){
  909.         switch( $this->post_status ){
  910.             case 'private':
  911.                 $this->event_private = 1;
  912.                 $this->event_status = $status = 1;
  913.                 break;
  914.             case 'publish':
  915.                 $this->event_private = 0;
  916.                 $this->event_status = $status = 1;
  917.                 break;
  918.             case 'pending':
  919.                 $this->event_private = 0;
  920.                 $this->event_status = $status = 0;
  921.                 break;
  922.             default: //draft or unknown
  923.                 $this->event_private = 0;
  924.                 $status = $db ? 'NULL':null;
  925.                 $this->event_status = null;
  926.                 break;
  927.         }
  928.         return $status;
  929.     }
  930.    
  931.     function get_previous_status(){
  932.         global $wpdb;
  933.         return $wpdb->get_var('SELECT event_status FROM '.EM_EVENTS_TABLE.' WHERE event_id='.$this->event_id); //get status from db, not post_status, as posts get saved quickly
  934.     }
  935.    
  936.     /**
  937.      * Returns an EM_Categories object of the EM_Event instance.
  938.      * @return EM_Categories
  939.      */
  940.     function get_categories() {
  941.         if( empty($this->categories) ){
  942.             $this->categories = new EM_Categories($this);
  943.         }elseif(empty($this->categories->event_id)){
  944.             $this->categories->event_id = $this->event_id;
  945.             $this->categories->post_id = $this->post_id;           
  946.         }
  947.         return apply_filters('em_event_get_categories', $this->categories, $this);
  948.     }
  949.    
  950.     /**
  951.      * Returns the location object this event belongs to.
  952.      * @return EM_Location
  953.      */
  954.     function get_location() {
  955.         global $EM_Location;
  956.         if( is_object($EM_Location) && $EM_Location->location_id == $this->location_id ){
  957.             $this->location = $EM_Location;
  958.         }else{
  959.             if( !is_object($this->location) || $this->location->location_id != $this->location_id ){
  960.                 $this->location = em_get_location($this->location_id);
  961.             }
  962.         }
  963.         return $this->location;
  964.     }  
  965.    
  966.     /**
  967.      * Returns the location object this event belongs to.
  968.      * @return EM_Person
  969.      */
  970.     function get_contact(){
  971.         if( !is_object($this->contact) ){
  972.             $this->contact = new EM_Person($this->event_owner);
  973.             //if this is anonymous submission, change contact email and name
  974.             if( $this->event_owner_anonymous ){
  975.                 $this->contact->user_email = $this->event_owner_email;
  976.                 $name = explode(' ',$this->event_owner_name);
  977.                 $first_name = array_shift($name);
  978.                 $last_name = (count($name) > 0) ? implode(' ',$name):'';
  979.                 $this->contact->user_firstname = $this->contact->first_name = $first_name;
  980.                 $this->contact->user_lastname = $this->contact->last_name = $last_name;
  981.             }
  982.         }
  983.         return $this->contact;
  984.     }
  985.    
  986.     /**
  987.      * Retrieve and save the bookings belonging to instance. If called again will return cached version, set $force_reload to true to create a new EM_Bookings object.
  988.      * @param boolean $force_reload
  989.      * @return EM_Bookings
  990.      */
  991.     function get_bookings( $force_reload = false ){
  992.         if( get_option('dbem_rsvp_enabled') ){
  993.             if( (!$this->bookings || $force_reload) ){
  994.                 $this->bookings = new EM_Bookings($this);
  995.             }
  996.             $this->bookings->event_id = $this->event_id; //always refresh event_id
  997.         }else{
  998.             return new EM_Bookings();
  999.         }
  1000.         //TODO for some reason this returned instance doesn't modify the original, e.g. try $this->get_bookings()->add($EM_Booking) and see how $this->bookings->feedback_message doesn't change
  1001.         return apply_filters('em_event_get_bookings', $this->bookings, $this);
  1002.     }
  1003.    
  1004.     /**
  1005.      * Get the tickets related to this event.
  1006.      * @param boolean $force_reload
  1007.      * @return EM_Tickets
  1008.      */
  1009.     function get_tickets( $force_reload = false ){
  1010.         return $this->get_bookings($force_reload)->get_tickets();
  1011.     }
  1012.    
  1013.     /**
  1014.      * Gets number of spaces in this event, dependent on ticket spaces or hard limit, whichever is smaller.
  1015.      * @param boolean $force_refresh
  1016.      * @return int
  1017.      */
  1018.     function get_spaces($force_refresh=false){
  1019.         return $this->get_bookings()->get_spaces($force_refresh);
  1020.     }
  1021.    
  1022.     /*
  1023.      * Extends the default EM_Object function by switching blogs as needed if in MS Global mode  
  1024.      * @param string $size
  1025.      * @return string
  1026.      * @see EM_Object::get_image_url()
  1027.      */
  1028.     function get_image_url($size = 'full'){
  1029.         if( EM_MS_GLOBAL && get_current_blog_id() != $this->blog_id ){
  1030.             switch_to_blog($this->blog_id);
  1031.             $switch_back = true;
  1032.         }
  1033.         $return = parent::get_image_url($size);
  1034.         if( !empty($switch_back) ){ restore_current_blog(); }
  1035.         return $return;
  1036.     }
  1037.    
  1038.     function get_edit_reschedule_url(){
  1039.         if( $this->is_recurrence() ){
  1040.             $EM_Event = em_get_event($this->recurrence_id);
  1041.             return $EM_Event->get_edit_url();
  1042.         }
  1043.     }
  1044.    
  1045.     function get_edit_url(){
  1046.         if( $this->can_manage('edit_events','edit_others_events') ){
  1047.             if( EM_MS_GLOBAL && get_site_option('dbem_ms_global_events_links') && !empty($this->blog_id) && is_main_site() && $this->blog_id != get_current_blog_id() ){
  1048.                 if( get_blog_option($this->blog_id, 'dbem_edit_events_page') ){
  1049.                     $link = em_add_get_params(get_permalink(get_blog_option($this->blog_id, 'dbem_edit_events_page')), array('action'=>'edit','event_id'=>$this->event_id), false);
  1050.                 }
  1051.                 if( empty($link))
  1052.                     $link = get_admin_url($this->blog_id, "post.php?post={$this->post_id}&action=edit");
  1053.             }else{
  1054.                 if( get_option('dbem_edit_events_page') && !is_admin() ){
  1055.                     $link = em_add_get_params(get_permalink(get_option('dbem_edit_events_page')), array('action'=>'edit','event_id'=>$this->event_id), false);
  1056.                 }
  1057.                 if( empty($link))
  1058.                     $link = admin_url()."post.php?post={$this->post_id}&action=edit";
  1059.             }
  1060.             return apply_filters('em_event_get_edit_url', $link, $this);
  1061.         }
  1062.     }
  1063.    
  1064.     function get_bookings_url(){
  1065.         if( get_option('dbem_edit_bookings_page') && (!is_admin() || !empty($_REQUEST['is_public'])) ){
  1066.             $my_bookings_page = get_permalink(get_option('dbem_edit_bookings_page'));
  1067.             $bookings_link = em_add_get_params($my_bookings_page, array('event_id'=>$this->event_id), false);
  1068.         }else{
  1069.             if( is_multisite() && $this->blog_id != get_current_blog_id() ){
  1070.                 $bookings_link = get_admin_url($this->blog_id, 'edit.php?post_type='.EM_POST_TYPE_EVENT."&page=events-manager-bookings&event_id=".$this->event_id);
  1071.             }else{
  1072.                 $bookings_link = EM_ADMIN_URL. "&page=events-manager-bookings&event_id=".$this->event_id;
  1073.             }
  1074.         }
  1075.         return apply_filters('em_event_get_bookings_url', $bookings_link, $this);
  1076.     }
  1077.    
  1078.     function get_permalink(){
  1079.         if( EM_MS_GLOBAL ){
  1080.             if( get_site_option('dbem_ms_global_events_links') && !empty($this->blog_id) && $this->blog_id != get_current_blog_id() ){
  1081.                 //linking directly to the blog
  1082.                 $event_link = get_blog_permalink( $this->blog_id, $this->post_id);
  1083.             }elseif( !empty($this->blog_id) && is_main_site() && $this->blog_id != get_current_blog_id() ){
  1084.                 if( get_option('dbem_events_page') ){
  1085.                     $event_link = trailingslashit(get_permalink(get_option('dbem_events_page')).get_site_option('dbem_ms_events_slug',EM_EVENT_SLUG).'/'.$this->event_slug.'-'.$this->event_id);
  1086.                 }else{
  1087.                     $event_link = trailingslashit(home_url()).EM_POST_TYPE_EVENT_SLUG.'/'.get_site_option('dbem_ms_events_slug',EM_EVENT_SLUG).'/'.$this->event_slug.'-'.$this->event_id;
  1088.                 }
  1089.             }
  1090.         }
  1091.         if( empty($event_link) ){
  1092.             $event_link = get_post_permalink($this->post_id);
  1093.         }
  1094.         return apply_filters('em_event_get_permalink', $event_link, $this);
  1095.     }
  1096.    
  1097.     function get_ical_url(){
  1098.         global $wp_rewrite;
  1099.         if( !empty($wp_rewrite) && $wp_rewrite->using_permalinks() ){
  1100.             return trailingslashit($this->get_permalink()).'ical/';
  1101.         }else{
  1102.             return em_add_get_params($this->get_permalink(), array('ical'=>1));
  1103.         }
  1104.     }
  1105.    
  1106.     function is_free( $now = false ){
  1107.         $free = true;
  1108.         foreach($this->get_tickets() as $EM_Ticket){
  1109.             /* @var $EM_Ticket EM_Ticket */
  1110.             if( $EM_Ticket->get_price() > 0 ){
  1111.                 if( !$now || $EM_Ticket->is_available() ){ 
  1112.                     $free = false;
  1113.                 }
  1114.             }
  1115.         }
  1116.         return apply_filters('em_event_is_free',$free,$this);
  1117.     }
  1118.    
  1119.     /**
  1120.      * Will output a single event format of this event.
  1121.      * Equivalent of calling EM_Event::output( get_option ( 'dbem_single_event_format' ) )
  1122.      * @param string $target
  1123.      * @return string
  1124.      */
  1125.     function output_single($target='html'){
  1126.         $format = get_option ( 'dbem_single_event_format' );
  1127.         return apply_filters('em_event_output_single', $this->output($format, $target), $this, $target);
  1128.     }
  1129.    
  1130.     /**
  1131.      * Will output a event in the format passed in $format by replacing placeholders within the format.
  1132.      * @param string $format
  1133.      * @param string $target
  1134.      * @return string
  1135.      */
  1136.     function output($format, $target="html") { 
  1137.         $event_string = $format;
  1138.         //Time place holder that doesn't show if empty.
  1139.         //TODO add filter here too
  1140.         preg_match_all('/#@?_\{[^}]+\}/', $format, $results);
  1141.         foreach($results[0] as $result) {
  1142.             if(substr($result, 0, 3 ) == "#@_"){
  1143.                 $date = 'end_date';
  1144.                 $offset = 4;
  1145.             }else{
  1146.                 $date = 'start_date';
  1147.                 $offset = 3;
  1148.             }
  1149.             if( $date == 'end_date' && $this->event_end_date == $this->event_start_date ){
  1150.                 $replace = __( apply_filters('em_event_output_placeholder', '', $this, $result, $target) );
  1151.             }else{
  1152.                 $replace = __( apply_filters('em_event_output_placeholder', mysql2date(substr($result, $offset, (strlen($result)-($offset+1)) ), $this->$date), $this, $result, $target) );
  1153.             }
  1154.             $event_string = str_replace($result,$replace,$event_string );
  1155.         }
  1156.         //This is for the custom attributes
  1157.         preg_match_all('/#_ATT\{([^}]+)\}(\{([^}]+)\})?/', $format, $results);
  1158.         $attributes = em_get_attributes();
  1159.         foreach($results[0] as $resultKey => $result) {
  1160.             //Strip string of placeholder and just leave the reference
  1161.             $attRef = substr( substr($result, 0, strpos($result, '}')), 6 );
  1162.             $attString = '';
  1163.             if( is_array($this->event_attributes) && array_key_exists($attRef, $this->event_attributes) ){
  1164.                 $attString = $this->event_attributes[$attRef];
  1165.             }elseif( !empty($results[3][$resultKey]) ){
  1166.                 //Check to see if we have a second set of braces;
  1167.                 $attString = $results[3][$resultKey];
  1168.             }elseif( !empty($attributes['values'][$attRef][0]) ){
  1169.                 $attString = $attributes['values'][$attRef][0];
  1170.             }
  1171.             $attString = apply_filters('em_event_output_placeholder', $attString, $this, $result, $target);
  1172.             $event_string = str_replace($result, $attString ,$event_string );
  1173.         }
  1174.         //First let's do some conditional placeholder removals
  1175.         for ($i = 0 ; $i < EM_CONDITIONAL_RECURSIONS; $i++){ //you can add nested recursions by modifying this setting in your wp_options table
  1176.             preg_match_all('/\{([a-zA-Z0-9_]+)\}(.+?)\{\/\1\}/s', $event_string, $conditionals);
  1177.             if( count($conditionals[0]) > 0 ){
  1178.                 //Check if the language we want exists, if not we take the first language there
  1179.                 foreach($conditionals[1] as $key => $condition){
  1180.                     $show_condition = false;
  1181.                     if ($condition == 'has_bookings') {
  1182.                         //check if there's a booking, if not, remove this section of code.
  1183.                         $show_condition = ($this->event_rsvp && get_option('dbem_rsvp_enabled'));
  1184.                     }elseif ($condition == 'no_bookings') {
  1185.                         //check if there's a booking, if not, remove this section of code.
  1186.                         $show_condition = (!$this->event_rsvp && get_option('dbem_rsvp_enabled'));
  1187.                     }elseif ($condition == 'no_location'){
  1188.                         //does this event have a valid location?
  1189.                         $show_condition = ( empty($this->location_id) || !$this->get_location()->location_status );
  1190.                     }elseif ($condition == 'has_location'){
  1191.                         //does this event have a valid location?
  1192.                         $show_condition = ( !empty($this->location_id) && $this->get_location()->location_status );
  1193.                     }elseif ($condition == 'has_image'){
  1194.                         //does this event have an image?
  1195.                         $show_condition = ( $this->get_image_url() != '' );
  1196.                     }elseif ($condition == 'no_image'){
  1197.                         //does this event have an image?
  1198.                         $show_condition = ( $this->get_image_url() == '' );
  1199.                     }elseif ($condition == 'has_time'){
  1200.                         //are the booking times different and not an all-day event
  1201.                         $show_condition = ( $this->event_start_time != $this->event_end_time && !$this->event_all_day );
  1202.                     }elseif ($condition == 'no_time'){
  1203.                         //are the booking times exactly the same and it's not an all-day event.
  1204.                         $show_condition = ( $this->event_start_time == $this->event_end_time && !$this->event_all_day );
  1205.                     }elseif ($condition == 'all_day'){
  1206.                         //is it an all day event
  1207.                         $show_condition = !empty($this->event_all_day);
  1208.                     }elseif ($condition == 'logged_in'){
  1209.                         //user is logged in
  1210.                         $show_condition = is_user_logged_in();
  1211.                     }elseif ($condition == 'not_logged_in'){
  1212.                         //not logged in
  1213.                         $show_condition = !is_user_logged_in();
  1214.                     }elseif ($condition == 'has_spaces'){
  1215.                         //is it an all day event
  1216.                         $show_condition = $this->event_rsvp && $this->get_bookings()->get_available_spaces() > 0;
  1217.                     }elseif ($condition == 'fully_booked'){
  1218.                         //is it an all day event
  1219.                         $show_condition = $this->event_rsvp && $this->get_bookings()->get_available_spaces() <= 0;
  1220.                     }elseif ($condition == 'is_free' || $condition == 'is_free_now'){
  1221.                         //is it a free day event, if _now then free right now
  1222.                         $show_condition = !$this->event_rsvp || $this->is_free( $condition == 'is_free_now' );
  1223.                     }elseif ($condition == 'not_free' || $condition == 'not_free_now'){
  1224.                         //is it a paid event, if _now then paid right now
  1225.                         $show_condition = $this->event_rsvp && !$this->is_free( $condition == 'not_free_now' );
  1226.                     }elseif ($condition == 'is_long'){
  1227.                         //is it an all day event
  1228.                         $show_condition = $this->event_start_date != $this->event_end_date;
  1229.                     }elseif ($condition == 'not_long'){
  1230.                         //is it an all day event
  1231.                         $show_condition = $this->event_start_date == $this->event_end_date;
  1232.                     }elseif ($condition == 'is_past'){
  1233.                         //if event is past
  1234.                         $show_condition = $this->start <= current_time('timestamp');
  1235.                     }elseif ($condition == 'is_future'){
  1236.                         //if event is upcoming
  1237.                         $show_condition = $this->start > current_time('timestamp');
  1238.                     }elseif ($condition == 'is_recurrence'){
  1239.                         //if event is a recurrence
  1240.                         $show_condition = $this->is_recurrence();
  1241.                     }elseif ($condition == 'not_recurrence'){
  1242.                         //if event is not a recurrence
  1243.                         $show_condition = !$this->is_recurrence();
  1244.                     }elseif ($condition == 'is_private'){
  1245.                         //if event is a recurrence
  1246.                         $show_condition = $this->event_private == 1;
  1247.                     }elseif ($condition == 'not_private'){
  1248.                         //if event is not a recurrence
  1249.                         $show_condition = $this->event_private == 0;
  1250.                     }elseif ( preg_match('/^has_category_([a-zA-Z0-9_\-]+)$/', $condition, $category_match)){
  1251.                         //event is in this category
  1252.                         $show_condition = has_term($category_match[1], EM_TAXONOMY_CATEGORY, $this->post_id);
  1253.                     }elseif ( preg_match('/^no_category_([a-zA-Z0-9_\-]+)$/', $condition, $category_match)){
  1254.                         //event is NOT in this category
  1255.                         $show_condition = !has_term($category_match[1], EM_TAXONOMY_CATEGORY, $this->post_id);
  1256.                     }elseif ( preg_match('/^has_tag_([a-zA-Z0-9_\-]+)$/', $condition, $tag_match)){
  1257.                         //event has this tag
  1258.                         $show_condition = has_term($tag_match[1], EM_TAXONOMY_TAG, $this->post_id);
  1259.                     }elseif ( preg_match('/^no_tag_([a-zA-Z0-9_\-]+)$/', $condition, $tag_match)){
  1260.                        //event doesn't have this tag
  1261.                         $show_condition = !has_term($tag_match[1], EM_TAXONOMY_TAG, $this->post_id);
  1262.                     }
  1263.                     //other potential ones - has_attribute_... no_attribute_... has_categories_...
  1264.                     $show_condition = apply_filters('em_event_output_show_condition', $show_condition, $condition, $conditionals[0][$key], $this);
  1265.                     if($show_condition){
  1266.                         //calculate lengths to delete placeholders
  1267.                         $placeholder_length = strlen($condition)+2;
  1268.                         $replacement = substr($conditionals[0][$key], $placeholder_length, strlen($conditionals[0][$key])-($placeholder_length *2 +1));
  1269.                     }else{
  1270.                         $replacement = '';
  1271.                     }
  1272.                     $event_string = str_replace($conditionals[0][$key], apply_filters('em_event_output_condition', $replacement, $condition, $conditionals[0][$key], $this), $event_string);
  1273.                 }
  1274.             }
  1275.         }
  1276.         //Now let's check out the placeholders.
  1277.         preg_match_all("/(#@?_?[A-Za-z0-9]+)({([a-zA-Z0-9_,]+)})?/", $format, $placeholders);
  1278.         $replaces = array();
  1279.         foreach($placeholders[1] as $key => $result) {
  1280.             $match = true;
  1281.             $replace = '';
  1282.             $full_result = $placeholders[0][$key];
  1283.             switch( $result ){
  1284.                 //Event Details
  1285.                 case '#_EVENTID':
  1286.                     $replace = $this->event_id;
  1287.                     break;
  1288.                 case '#_EVENTPOSTID':
  1289.                     $replace = $this->post_id;
  1290.                     break;
  1291.                 case '#_NAME': //depreciated
  1292.                 case '#_EVENTNAME':
  1293.                     $replace = $this->event_name;
  1294.                     break;
  1295.                 case '#_NOTES': //depreciated
  1296.                 case '#_EXCERPT': //depreciated
  1297.                 case '#_EVENTNOTES':
  1298.                 case '#_EVENTEXCERPT':
  1299.                     $replace = $this->post_content;
  1300.                     if($result == "#_EXCERPT" || $result == "#_EVENTEXCERPT"){
  1301.                         if( !empty($this->post_excerpt) ){
  1302.                             $replace = $this->post_excerpt;
  1303.                         }else{
  1304.                             $matches = explode('<!--more', $this->post_content);
  1305.                             $replace = $matches[0];
  1306.                         }
  1307.                     }
  1308.                     break;
  1309.                 case '#_EVENTIMAGEURL':
  1310.                 case '#_EVENTIMAGE':
  1311.                     if($this->get_image_url() != ''){
  1312.                         if($result == '#_EVENTIMAGEURL'){
  1313.                             $replace =  esc_url($this->image_url);
  1314.                         }else{
  1315.                             if( empty($placeholders[3][$key]) ){
  1316.                                 $replace = "<img src='".esc_url($this->image_url)."' alt='".esc_attr($this->event_name)."'/>";
  1317.                             }else{
  1318.                                 $image_size = explode(',', $placeholders[3][$key]);
  1319.                                 $image_src = $this->image_url;
  1320.                                 if( $this->array_is_numeric($image_size) && count($image_size) > 1 ){
  1321.                                     //get a thumbnail
  1322.                                     if( get_option('dbem_disable_timthumb') ){
  1323.                                         if( EM_MS_GLOBAL && get_current_blog_id() != $this->blog_id ){
  1324.                                             switch_to_blog($this->blog_id);
  1325.                                             $switch_back = true;
  1326.                                         }
  1327.                                         $replace = get_the_post_thumbnail($this->ID, $image_size);
  1328.                                         if( !empty($switch_back) ){ restore_current_blog(); }
  1329.                                     }else{
  1330.                                         if ( is_multisite() ) { //get the direct url as timthumb doesn't support redirect urls
  1331.                                             global $blog_id;
  1332.                                             $imageParts = explode('/blogs.dir/', $image_src);
  1333.                                             if (isset($imageParts[1])) {
  1334.                                                 $image_src = network_site_url('/wp-content/blogs.dir/'. $imageParts[1]);
  1335.                                             }
  1336.                                         }
  1337.                                         $width = ($image_size[0]) ? 'width="'.esc_attr($image_size[0]).'"':'';
  1338.                                         $height = ($image_size[1]) ? 'height="'.esc_attr($image_size[1]).'"':'';
  1339.                                         $replace = "<img src='".esc_url(em_get_thumbnail_url($image_src, $image_size[0], $image_size[1]))."' alt='".esc_attr($this->event_name)."' $width $height />";
  1340.                                     }
  1341.                                 }else{
  1342.                                     $replace = "<img src='".esc_url($image_src)."' alt='".esc_attr($this->event_name)."'/>";
  1343.                                 }
  1344.                             }
  1345.                         }
  1346.                     }
  1347.                     break;
  1348.                 //Times & Dates
  1349.                 case '#_24HSTARTTIME':
  1350.                 case '#_24HENDTIME':
  1351.                     $time = ($result == '#_24HSTARTTIME') ? $this->event_start_time:$this->event_end_time;
  1352.                     $replace = substr($time, 0,5);
  1353.                     break;
  1354.                 case '#_12HSTARTTIME':
  1355.                 case '#_12HENDTIME':
  1356.                     $time = ($result == '#_12HSTARTTIME') ? $this->event_start_time:$this->event_end_time;
  1357.                     $replace = date('g:i A', strtotime($time));
  1358.                     break;
  1359.                 case '#_EVENTTIMES':
  1360.                     //get format of time to show
  1361.                     if( !$this->event_all_day ){
  1362.                         $time_format = ( get_option('dbem_time_format') ) ? get_option('dbem_time_format'):get_option('time_format');
  1363.                         if($this->event_start_time != $this->event_end_time ){
  1364.                             $replace = date_i18n($time_format, $this->start). get_option('dbem_times_separator') . date_i18n($time_format, $this->end);
  1365.                         }else{
  1366.                             $replace = date_i18n($time_format, $this->start);
  1367.                         }
  1368.                     }else{
  1369.                         $replace = get_option('dbem_event_all_day_message');
  1370.                     }
  1371.                     break;
  1372.                 case '#_EVENTDATES':
  1373.                     //get format of time to show
  1374.                     $date_format = ( get_option('dbem_date_format') ) ? get_option('dbem_date_format'):get_option('date_format');
  1375.                     if( $this->event_start_date != $this->event_end_date){
  1376.                         $replace = date_i18n($date_format, $this->start). get_option('dbem_dates_separator') . date_i18n($date_format, $this->end);
  1377.                     }else{
  1378.                         $replace = date_i18n($date_format, $this->start);
  1379.                     }
  1380.                     break;
  1381.                 //Links
  1382.                 case '#_EVENTPAGEURL': //Depreciated   
  1383.                 case '#_LINKEDNAME': //Depreciated
  1384.                 case '#_EVENTURL': //Just the URL
  1385.                 case '#_EVENTLINK': //HTML Link
  1386.                     $event_link = esc_url($this->get_permalink());
  1387.                     if($result == '#_LINKEDNAME' || $result == '#_EVENTLINK'){
  1388.                         $replace = '<a href="'.$event_link.'" title="'.esc_attr($this->event_name).'">'.esc_attr($this->event_name).'</a>';
  1389.                     }else{
  1390.                         $replace = $event_link;
  1391.                     }
  1392.                     break;
  1393.                 case '#_EDITEVENTURL':
  1394.                 case '#_EDITEVENTLINK':
  1395.                     if( $this->can_manage('edit_events','edit_others_events') ){
  1396.                         $link = esc_url($this->get_edit_url());
  1397.                         if( $result == '#_EDITEVENTLINK'){
  1398.                             $replace = '<a href="'.$link.'">'.esc_html(sprintf(__('Edit Event','dbem'))).'</a>';
  1399.                         }else{
  1400.                             $replace = $link;
  1401.                         }
  1402.                     }    
  1403.                     break;
  1404.                 //Bookings
  1405.                 case '#_ADDBOOKINGFORM': //Depreciated
  1406.                 case '#_REMOVEBOOKINGFORM': //Depreciated
  1407.                 case '#_BOOKINGFORM':
  1408.                     if( get_option('dbem_rsvp_enabled')){
  1409.                         ob_start();
  1410.                         $template = em_locate_template('placeholders/bookingform.php', true, array('EM_Event'=>$this));
  1411.                         EM_Bookings::enqueue_js();
  1412.                         $replace = ob_get_clean();
  1413.                     }
  1414.                     break;
  1415.                 case '#_BOOKINGBUTTON':
  1416.                     if( get_option('dbem_rsvp_enabled') && $this->event_rsvp ){
  1417.                         ob_start();
  1418.                         $template = em_locate_template('placeholders/bookingbutton.php', true, array('EM_Event'=>$this));
  1419.                         $replace = ob_get_clean();
  1420.                     }
  1421.                     break;
  1422.                 case '#_EVENTPRICERANGEALL':                   
  1423.                     $show_all_ticket_prices = true; //continues below
  1424.                 case '#_EVENTPRICERANGE':
  1425.                     //get the range of prices
  1426.                     $min = false;
  1427.                     $max = 0;
  1428.                     if( $this->get_bookings()->is_open() || !empty($show_all_ticket_prices) ){
  1429.                         foreach( $this->get_tickets()->tickets as $EM_Ticket ){
  1430.                             /* @var $EM_Ticket EM_Ticket */
  1431.                             if( $EM_Ticket->is_available() || get_option('dbem_bookings_tickets_show_unavailable') || !empty($show_all_ticket_prices) ){
  1432.                                 if($EM_Ticket->get_price() > $max ){
  1433.                                     $max = $EM_Ticket->get_price();
  1434.                                 }
  1435.                                 if($EM_Ticket->get_price() < $min || $min === false){
  1436.                                     $min = $EM_Ticket->get_price();
  1437.                                 }                      
  1438.                             }
  1439.                         }
  1440.                     }
  1441.                     if( $min === false ) $min = 0;
  1442.                     if( $min != $max ){
  1443.                         $replace = em_get_currency_formatted($min).' - '.em_get_currency_formatted($max);
  1444.                     }else{
  1445.                         $replace = em_get_currency_formatted($min);
  1446.                     }
  1447.                     break;
  1448.                 case '#_EVENTPRICEMIN':
  1449.                     //get the range of prices
  1450.                     $min = false;
  1451.                     foreach( $this->get_tickets()->tickets as $EM_Ticket ){
  1452.                         /* @var $EM_Ticket EM_Ticket */
  1453.                         if( $EM_Ticket->is_available()|| get_option('dbem_bookings_tickets_show_unavailable') ){
  1454.                             if( $EM_Ticket->get_price() < $min || $min === false){
  1455.                                 $min = $EM_Ticket->get_price();
  1456.                             }
  1457.                         }
  1458.                     }
  1459.                     if( $min === false ) $min = 0;
  1460.                     $replace = em_get_currency_formatted($min);
  1461.                     break;
  1462.                 case '#_EVENTPRICEMAX':
  1463.                     //get the range of prices
  1464.                     $max = 0;
  1465.                     foreach( $this->get_tickets()->tickets as $EM_Ticket ){
  1466.                         /* @var $EM_Ticket EM_Ticket */
  1467.                         if( $EM_Ticket->is_available()|| get_option('dbem_bookings_tickets_show_unavailable') ){
  1468.                             if( $EM_Ticket->get_price() > $max ){
  1469.                                 $max = $EM_Ticket->get_price();
  1470.                             }
  1471.                         }          
  1472.                     }
  1473.                     $replace = em_get_currency_formatted($max);
  1474.                     break;
  1475.                 case '#_AVAILABLESEATS': //Depreciated
  1476.                 case '#_AVAILABLESPACES':
  1477.                     if ($this->event_rsvp && get_option('dbem_rsvp_enabled')) {
  1478.                        $replace = $this->get_bookings()->get_available_spaces();
  1479.                     } else {
  1480.                         $replace = "0";
  1481.                     }
  1482.                     break;
  1483.                 case '#_BOOKEDSEATS': //Depreciated
  1484.                 case '#_BOOKEDSPACES':
  1485.                     //This placeholder is actually a little misleading, as it'll consider reserved (i.e. pending) bookings as 'booked'
  1486.                     if ($this->event_rsvp && get_option('dbem_rsvp_enabled')) {
  1487.                         $replace = $this->get_bookings()->get_booked_spaces();
  1488.                         if( get_option('dbem_bookings_approval_reserved') ){
  1489.                             $replace += $this->get_bookings()->get_pending_spaces();
  1490.                         }
  1491.                     } else {
  1492.                         $replace = "0";
  1493.                     }
  1494.                     break;
  1495.                 case '#_PENDINGSPACES':
  1496.                     if ($this->event_rsvp && get_option('dbem_rsvp_enabled')) {
  1497.                        $replace = $this->get_bookings()->get_pending_spaces();
  1498.                     } else {
  1499.                         $replace = "0";
  1500.                     }
  1501.                     break;
  1502.                 case '#_SEATS': //Depreciated
  1503.                 case '#_SPACES':
  1504.                     $replace = $this->get_spaces();
  1505.                     break;
  1506.                 case '#_BOOKINGSURL':
  1507.                 case '#_BOOKINGSLINK':
  1508.                     if( $this->can_manage('manage_bookings','manage_others_bookings') ){
  1509.                         $bookings_link = esc_url($this->get_bookings_url());
  1510.                         if($result == '#_BOOKINGSLINK'){
  1511.                             $replace = '<a href="'.$bookings_link.'" title="'.esc_attr($this->event_name).'">'.esc_html($this->event_name).'</a>';
  1512.                         }else{
  1513.                             $replace = $bookings_link; 
  1514.                         }
  1515.                     }
  1516.                     break;
  1517.                 //Contact Person
  1518.                 case '#_CONTACTNAME':
  1519.                 case '#_CONTACTPERSON': //Depreciated (your call, I think name is better)
  1520.                     $replace = $this->get_contact()->display_name;
  1521.                     break;
  1522.                 case '#_CONTACTUSERNAME':
  1523.                     $replace = $this->get_contact()->user_login;
  1524.                     break;
  1525.                 case '#_CONTACTEMAIL':
  1526.                 case '#_CONTACTMAIL': //Depreciated
  1527.                     $replace = $this->get_contact()->user_email;
  1528.                     break;
  1529.                 case '#_CONTACTURL':
  1530.                     $replace = $this->get_contact()->user_url;
  1531.                     break;
  1532.                 case '#_CONTACTID':
  1533.                     $replace = $this->get_contact()->ID;
  1534.                     break;
  1535.                 case '#_CONTACTPHONE':
  1536.                     $replace = ( $this->get_contact()->phone != '') ? $this->get_contact()->phone : __('N/A', 'dbem');
  1537.                     break;
  1538.                 case '#_CONTACTAVATAR':
  1539.                     $replace = get_avatar( $this->get_contact()->ID, $size = '50' );
  1540.                     break;
  1541.                 case '#_CONTACTPROFILELINK':
  1542.                 case '#_CONTACTPROFILEURL':
  1543.                     if( function_exists('bp_core_get_user_domain') ){
  1544.                         $replace = bp_core_get_user_domain($this->get_contact()->ID);
  1545.                         if( $result == '#_CONTACTPROFILELINK' ){
  1546.                             $replace = '<a href="'.esc_url($replace).'">'.__('Profile', 'dbem').'</a>';
  1547.                         }
  1548.                     }
  1549.                     break;
  1550.                 case '#_CONTACTMETA':
  1551.                     if( !empty($placeholders[3][$key]) ){
  1552.                         $replace = get_user_meta($this->event_owner, $placeholders[3][$key], true);
  1553.                     }
  1554.                     break;
  1555.                 case '#_ATTENDEES':
  1556.                     ob_start();
  1557.                     $template = em_locate_template('placeholders/attendees.php', true, array('EM_Event'=>$this));
  1558.                     $replace = ob_get_clean();
  1559.                     break;
  1560.                 case '#_ATTENDEESLIST':
  1561.                     ob_start();
  1562.                     $template = em_locate_template('placeholders/attendeeslist.php', true, array('EM_Event'=>$this));
  1563.                     $replace = ob_get_clean();
  1564.                     break;
  1565.                 case '#_ATTENDEESPENDINGLIST':
  1566.                     ob_start();
  1567.                     $template = em_locate_template('placeholders/attendeespendinglist.php', true, array('EM_Event'=>$this));
  1568.                     $replace = ob_get_clean();
  1569.                     break;
  1570.                 //Categories and Tags
  1571.                 case '#_EVENTCATEGORIESIMAGES':
  1572.                     ob_start();
  1573.                     $template = em_locate_template('placeholders/eventcategoriesimages.php', true, array('EM_Event'=>$this));
  1574.                     $replace = ob_get_clean();
  1575.                     break;
  1576.                 case '#_EVENTTAGS':
  1577.                     ob_start();
  1578.                     $template = em_locate_template('placeholders/eventtags.php', true, array('EM_Event'=>$this));
  1579.                     $replace = ob_get_clean();
  1580.                     break;
  1581.                 case '#_CATEGORIES': //depreciated
  1582.                 case '#_EVENTCATEGORIES':
  1583.                     ob_start();
  1584.                     $template = em_locate_template('placeholders/categories.php', true, array('EM_Event'=>$this));
  1585.                     $replace = ob_get_clean();
  1586.                     break;
  1587.                 //Ical Stuff
  1588.                 case '#_EVENTICALURL':
  1589.                 case '#_EVENTICALLINK':
  1590.                     $replace = $this->get_ical_url();
  1591.                     if( $result == '#_EVENTICALLINK' ){
  1592.                         $replace = '<a href="'.esc_url($replace).'">iCal</a>';
  1593.                     }
  1594.                     break;
  1595.                 case '#_EVENTGCALURL':
  1596.                 case '#_EVENTGCALLINK':
  1597.                     //get dates
  1598.                     if($this->event_all_day && $this->event_start_date == $this->event_end_date){
  1599.                         $dateStart  = date('Ymd',$this->start - (60*60*get_option('gmt_offset')));
  1600.                         $dateEnd    = date('Ymd',$this->start + 60*60*24 - (60*60*get_option('gmt_offset')));
  1601.                     }else{
  1602.                         $dateStart  = date('Ymd\THis\Z',$this->start - (60*60*get_option('gmt_offset')));
  1603.                         $dateEnd = date('Ymd\THis\Z',$this->end - (60*60*get_option('gmt_offset')));
  1604.                     }
  1605.                     //build url
  1606.                     $gcal_url = 'http://www.google.com/calendar/event?action=TEMPLATE&text=event_name&dates=start_date/end_date&details=post_content&location=location_name&trp=false&sprop=event_url&sprop=name:blog_name';
  1607.                     $gcal_url = str_replace('event_name', urlencode($this->event_name), $gcal_url);
  1608.                     $gcal_url = str_replace('start_date', urlencode($dateStart), $gcal_url);
  1609.                     $gcal_url = str_replace('end_date', urlencode($dateEnd), $gcal_url);
  1610.                     $gcal_url = str_replace('location_name', urlencode($this->output('#_LOCATION')), $gcal_url);
  1611.                     $gcal_url = str_replace('blog_name', urlencode(get_bloginfo()), $gcal_url);
  1612.                     $gcal_url = str_replace('event_url', urlencode($this->get_permalink()), $gcal_url);
  1613.                     //calculate URL length so we know how much we can work with to make a description.
  1614.                     if( !empty($this->post_excerpt) ){
  1615.                         $gcal_url_description = $this->post_excerpt;
  1616.                     }else{
  1617.                         $matches = explode('<!--more', $this->post_content);
  1618.                         $gcal_url_description = wp_kses_data($matches[0]);
  1619.                     }
  1620.                     $gcal_url_length = strlen($gcal_url) - 9;
  1621.                     if( strlen($gcal_url_description) + $gcal_url_length > 1350 ){
  1622.                         $gcal_url_description = substr($gcal_url_description, 0, 1380 - $gcal_url_length - 3 ).'...';
  1623.                     }
  1624.                     $gcal_url = str_replace('post_content', urlencode($gcal_url_description), $gcal_url);
  1625.                     //get the final url
  1626.                     $replace = $gcal_url;
  1627.                     if( $result == '#_EVENTGCALLINK' ){
  1628.                         $img_url = 'www.google.com/calendar/images/ext/gc_button2.gif';
  1629.                         $img_url = is_ssl() ? 'https://'.$img_url:'http://'.$img_url;
  1630.                         $replace = '<a href="'.esc_url($replace).'" target="_blank"><img src="'.esc_url($img_url).'" alt="0" border="0"></a>';
  1631.                     }
  1632.                     break;
  1633.                 default:
  1634.                     $replace = $full_result;
  1635.                     break;
  1636.             }
  1637.             $replaces[$full_result] = apply_filters('em_event_output_placeholder', $replace, $this, $full_result, $target);
  1638.         }
  1639.         //sort out replacements so that during replacements shorter placeholders don't overwrite longer varieties.
  1640.         krsort($replaces);
  1641.         foreach($replaces as $full_result => $replacement){
  1642.             if( !in_array($full_result, array('#_NOTES','#_EVENTNOTES')) ){
  1643.                 $event_string = str_replace($full_result, $replacement , $event_string );
  1644.             }else{
  1645.                 $new_placeholder = str_replace('#_', '__#', $full_result); //this will avoid repeated filters when locations/categories are parsed
  1646.                 $event_string = str_replace($full_result, $new_placeholder , $event_string );
  1647.                 $desc_replace[$new_placeholder] = $replacement;
  1648.             }
  1649.         }
  1650.         //Time placeholders
  1651.         foreach($placeholders[1] as $result) {
  1652.             // matches all PHP START date and time placeholders
  1653.             if (preg_match('/^#[dDjlNSwzWFmMntLoYyaABgGhHisueIOPTZcrU]$/', $result)) {
  1654.                 $replace = date_i18n(ltrim($result, "#"), $this->start);
  1655.                 $replace = apply_filters('em_event_output_placeholder', $replace, $this, $result, $target);
  1656.                 $event_string = str_replace($result, $replace, $event_string );
  1657.             }
  1658.             // matches all PHP END time placeholders for endtime
  1659.             if (preg_match('/^#@[dDjlNSwzWFmMntLoYyaABgGhHisueIOPTZcrU]$/', $result)) {
  1660.                 $replace = date_i18n(ltrim($result, "#@"), $this->end);
  1661.                 $replace = apply_filters('em_event_output_placeholder', $replace, $this, $result, $target);
  1662.                 $event_string = str_replace($result, $replace, $event_string );
  1663.             }
  1664.         }
  1665.         //Now do dependent objects
  1666.         if( !empty($this->location_id) && $this->get_location()->location_status ){
  1667.             $event_string = $this->get_location()->output($event_string, $target);
  1668.         }else{
  1669.             $EM_Location = new EM_Location();
  1670.             $event_string = $EM_Location->output($event_string, $target);
  1671.         }
  1672.        
  1673.         //for backwards compat and easy use, take over the individual category placeholders with the frirst cat in th elist.
  1674.         $EM_Categories = $this->get_categories();
  1675.         if( count($EM_Categories->categories) > 0 ){
  1676.             $EM_Category = $EM_Categories->get_first();
  1677.         }  
  1678.        
  1679.         if( empty($EM_Category) ) $EM_Category = new EM_Category();
  1680.         $event_string = $EM_Category->output($event_string, $target);
  1681.        
  1682.         //Finally, do the event notes, so that previous placeholders don't get replaced within the content, which may use shortcodes
  1683.         if( !empty($desc_replace) ){
  1684.             foreach($desc_replace as $full_result => $replacement){
  1685.                 $event_string = str_replace($full_result, $replacement , $event_string );
  1686.             }
  1687.         }
  1688.        
  1689.         return apply_filters('em_event_output', $event_string, $this, $format, $target);
  1690.     }  
  1691.    
  1692.     /**********************************************************
  1693.      * RECURRENCE METHODS
  1694.      ***********************************************************/
  1695.    
  1696.     /**
  1697.      * Returns true if this is a recurring event.
  1698.      * @return boolean
  1699.      */
  1700.     function is_recurring(){
  1701.         return $this->post_type == 'event-recurring' && get_option('dbem_recurrence_enabled');
  1702.     }  
  1703.     /**
  1704.      * Will return true if this individual event is part of a set of events that recur
  1705.      * @return boolean
  1706.      */
  1707.     function is_recurrence(){
  1708.         return ( $this->event_id > 0 && $this->recurrence_id > 0 && get_option('dbem_recurrence_enabled') );
  1709.     }
  1710.     /**
  1711.      * Returns if this is an individual event and is not a recurrence
  1712.      * @return boolean
  1713.      */
  1714.     function is_individual(){
  1715.         return ( !$this->is_recurring() && !$this->is_recurrence() );
  1716.     }
  1717.    
  1718.     /**
  1719.      * Gets the event recurrence template, which is an EM_Event_Recurrence object (based off an event-recurring post)
  1720.      * @return EM_Event_Recurrence
  1721.      */
  1722.     function get_event_recurrence(){
  1723.         if(!$this->is_recurring()){
  1724.             return new EM_Event($this->recurrence_id); //remember, recurrence_id is a post!
  1725.         }else{
  1726.             return $this;
  1727.         }
  1728.     }
  1729.    
  1730.     function get_detach_url(){
  1731.         return admin_url().'admin.php?event_id='.$this->event_id.'&amp;action=event_detach&amp;_wpnonce='.wp_create_nonce('event_detach_'.get_current_user_id().'_'.$this->event_id);
  1732.     }
  1733.    
  1734.     function get_attach_url($recurrence_id){
  1735.         return admin_url().'admin.php?undo_id='.$recurrence_id.'&amp;event_id='.$this->event_id.'&amp;action=event_attach&amp;_wpnonce='.wp_create_nonce('event_attach_'.get_current_user_id().'_'.$this->event_id);
  1736.     }
  1737.    
  1738.     /**
  1739.      * Returns if this is an individual event and is not recurring or a recurrence
  1740.      * @return boolean
  1741.      */
  1742.     function detach(){
  1743.         global $wpdb;
  1744.         if( $this->is_recurrence() && !$this->is_recurring() && $this->can_manage('edit_recurring_events','edit_others_recurring_events') ){
  1745.             //remove recurrence id from post meta and index table
  1746.             $url = $this->get_attach_url($this->recurrence_id);
  1747.             $wpdb->update(EM_EVENTS_TABLE, array('recurrence_id'=>0), array('event_id' => $this->event_id));
  1748.             update_post_meta($this->post_id, '_recurrence_id', 0);
  1749.             $this->feedback_message = __('Event detached.','dbem') . ' <a href="'.$url.'">'.__('Undo','dbem').'</a>';
  1750.             $this->recurrence_id = 0;
  1751.             return true;
  1752.         }
  1753.         $this->add_error(__('Event could not be detached.','dbem'));
  1754.         return false;
  1755.     }
  1756.    
  1757.     /**
  1758.      * Returns if this is an individual event and is not recurring or a recurrence
  1759.      * @return boolean
  1760.      */
  1761.     function attach($recurrence_id){
  1762.         global $wpdb;
  1763.         if( !$this->is_recurrence() && !$this->is_recurring() && is_numeric($recurrence_id) && $this->can_manage('edit_recurring_events','edit_others_recurring_events') ){
  1764.             //add recurrence id to post meta and index table
  1765.             $wpdb->update(EM_EVENTS_TABLE, array('recurrence_id'=>$recurrence_id), array('event_id' => $this->event_id));
  1766.             update_post_meta($this->post_id, '_recurrence_id', $recurrence_id);
  1767.             $this->feedback_message = __('Event re-attached to recurrence.','dbem');
  1768.             return true;
  1769.         }
  1770.         $this->add_error(__('Event could not be attached.','dbem'));
  1771.         return false;
  1772.     }
  1773.    
  1774.     /**
  1775.      * Saves events and replaces old ones. Returns true if sucecssful or false if not.
  1776.      * @return boolean
  1777.      */
  1778.     function save_events() {
  1779.         global $wpdb;
  1780.         if( $this->can_manage('edit_events','edit_others_events') && $this->is_published() ){
  1781.             do_action('em_event_save_events_pre', $this); //actions/filters only run if event is recurring
  1782.             //Make template event index, post, and meta (and we just change event dates)
  1783.             $event = $this->to_array(true); //event template - for index
  1784.             $event['event_attributes'] = serialize($event['event_attributes']);
  1785.             $post_fields = $wpdb->get_row('SELECT * FROM '.$wpdb->posts.' WHERE ID='.$this->post_id, ARRAY_A); //post to copy
  1786.             $post_name = $post_fields['post_name']; //save post slug since we'll be using this
  1787.             $post_fields['post_type'] = 'event'; //make sure we'll save events, not recurrence templates
  1788.             $meta_fields_map = $wpdb->get_results('SELECT meta_key,meta_value FROM '.$wpdb->postmeta.' WHERE post_id='.$this->post_id, ARRAY_A);
  1789.             $meta_fields = array();
  1790.             //convert meta_fields into a cleaner array
  1791.             foreach($meta_fields_map as $meta_data){
  1792.                 $meta_fields[$meta_data['meta_key']] = $meta_data['meta_value'];
  1793.             }
  1794.             //remove id and we have a event template to feed to wpdb insert
  1795.             unset($event['event_id']);
  1796.             unset($post_fields['ID']);
  1797.             //remove recurrence meta info we won't need in events
  1798.             foreach( array_keys($this->recurrence_fields) as $recurrence_field){
  1799.                 unset($event[$recurrence_field]);
  1800.                 unset($meta_fields['_'.$recurrence_field]);
  1801.             }      
  1802.             $event['event_date_created'] = current_time('mysql'); //since the recurrences are recreated
  1803.             unset($event['event_date_modified']);
  1804.             //Set the recurrence ID
  1805.             $event['recurrence_id'] = $meta_fields['_recurrence_id'] = $this->event_id;
  1806.             $event['recurrence'] = $meta_fields['_recurrence'] = 0;
  1807.             //Let's start saving!
  1808.             $this->delete_events(); //Delete old events beforehand, this will change soon
  1809.             $event_saves = array();
  1810.             $event_ids = array();
  1811.             $event_start = array();
  1812.             $post_ids = array();
  1813.             $matching_days = $this->get_recurrence_days(); //Get days where events recur
  1814.             if( count($matching_days) > 0 ){
  1815.                 //first save event post data
  1816.                 $recurring_date_format = apply_filters('em_event_save_events_format', 'Y-m-d');
  1817.                 foreach( $matching_days as $day ) {
  1818.                     //rewrite post fields if needed
  1819.                     $post_fields['post_name'] = $event['event_slug'] = $meta_fields['_event_slug'] = apply_filters('em_event_save_events_slug', $post_name.'-'.date($recurring_date_format, $day), $post_fields, $day, $matching_days, $this);
  1820.                     //adjust certain meta information
  1821.                     $event['event_start_date'] = $meta_fields['_event_start_date'] = date("Y-m-d", $day);
  1822.                     $meta_fields['_start_ts'] = strtotime($event['event_start_date'].' '.$event['event_start_time']);
  1823.                     $event['event_rsvp_date'] = $meta_fields['_event_rsvp_date'] = $event['event_start_date'];
  1824.                     $event['event_rsvp_time'] = $meta_fields['_event_rsvp_time'] = $event['event_start_time'];
  1825.                     if($this->recurrence_days > 0){
  1826.                         $event['event_end_date'] = $meta_fields['_event_end_date'] = date("Y-m-d", $meta_fields['_start_ts'] + ($this->recurrence_days * 60*60*24));
  1827.                     }else{
  1828.                         $event['event_end_date'] = $meta_fields['_event_end_date'] = $event['event_start_date'];
  1829.                     }  
  1830.                     $meta_fields['_end_ts'] = strtotime($event['event_end_date'].' '.$event['event_end_time']);
  1831.                     //create the event
  1832.                     if( $wpdb->insert($wpdb->posts, $post_fields ) ){
  1833.                         $event['post_id'] = $meta_fields['_post_id'] = $post_id = $post_ids[] = $wpdb->insert_id; //post id saved into event and also as a var for later user
  1834.                         // Set GUID and event slug as per wp_insert_post
  1835.                         $wpdb->update( $wpdb->posts, array( 'guid' => get_permalink( $post_id ) ), array('ID'=>$post_id) );
  1836.                         //insert into events index table
  1837.                         $event_saves[] = $wpdb->insert(EM_EVENTS_TABLE, $event);
  1838.                         $event_ids[] = $event_id = $wpdb->insert_id;
  1839.                         //create the meta inserts for each event
  1840.                         $meta_fields['_event_id'] = $event_id;
  1841.                         foreach($meta_fields as $meta_key => $meta_val){
  1842.                             $meta_inserts[] = $wpdb->prepare("(%d, '%s', '%s')", array($post_id, $meta_key, $meta_val));
  1843.                         }
  1844.                         $event_start[$event_id] = $day;
  1845.                     }else{
  1846.                         $event_saves[] = false;
  1847.                     }
  1848.                     //if( EM_DEBUG ){ echo "Entering recurrence " . date("D d M Y", $day)."<br/>"; }
  1849.                 }
  1850.                 //insert the metas in one go, faster than one by one
  1851.                 if( count($meta_inserts) > 0 ){
  1852.                     $result = $wpdb->query("INSERT INTO ".$wpdb->postmeta." (post_id,meta_key,meta_value) VALUES ".implode(',',$meta_inserts));
  1853.                     if($result === false){
  1854.                         $this->add_error('There was a problem adding custom fields to your recurring events.','dbem');
  1855.                     }
  1856.                 }
  1857.                 //copy the event tags and categories
  1858.                 $categories = $this->get_categories()->categories;
  1859.                 foreach( $categories as $category){
  1860.                     if( !empty($category->slug) ) $cat_slugs[] = $category->slug; //save of category will soft-fail if slug is empty
  1861.                 }
  1862.                 $cat_slugs_count = count($categories);
  1863.                 $tags = get_the_terms( $this->post_id, EM_TAXONOMY_TAG);
  1864.                 $tax_slugs = array();
  1865.                 if( is_array($tags) ){
  1866.                     foreach($tags as $tag){
  1867.                         if( !empty($tag->slug) ) $tax_slugs[] = $tag->slug; //save of category will soft-fail if slug is empty
  1868.                     }
  1869.                 }
  1870.                 $tax_slugs_count = count($tags);
  1871.                 foreach($post_ids as $post_id){
  1872.                     if( $cat_slugs_count > 0 && !EM_MS_GLOBAL ){
  1873.                         wp_set_object_terms($post_id, $cat_slugs, EM_TAXONOMY_CATEGORY);
  1874.                     }
  1875.                     if( $tax_slugs_count > 0 ){
  1876.                         wp_set_object_terms($post_id, $tax_slugs, EM_TAXONOMY_TAG);
  1877.                     }
  1878.                 }
  1879.                 //featured images
  1880.                 if( !empty($this->attributes['thumbnail_id']) ){
  1881.                     $image_inserts = array();
  1882.                     foreach($post_ids as $post_ids){
  1883.                         $image_inserts[] = "({$this->post_id}, '_thumbnail_id', {$this->attributes['thumbnail_id']})";
  1884.                     }
  1885.                     if( count($image_inserts) > 0 ){
  1886.                         $wpdb->query('INSERT INTO '.$wpdb->postmeta.' (post_id, meta_key, meta_value) VALUES '.implode(', ', $image_inserts));
  1887.                     }
  1888.                 }
  1889.                 //MS Global Categories
  1890.                 if( $cat_slugs_count > 0 && EM_MS_GLOBAL ){
  1891.                     foreach($categories as $EM_Category){
  1892.                         foreach($event_ids as $event_id){
  1893.                             $wpdb->insert(EM_META_TABLE, array('meta_value'=>$EM_Category->term_id,'object_id'=>$event_id,'meta_key'=>'event-category'));
  1894.                         }
  1895.                     }
  1896.                 }
  1897.                 //now, save booking info for each event
  1898.                 if( $this->event_rsvp ){
  1899.                     $meta_inserts = array();
  1900.                     $base_event_start_ts = strtotime( $this->event_start_date );
  1901.                    
  1902.                     foreach($this->get_tickets() as $EM_Ticket){
  1903.                         /* @var $EM_Ticket EM_Ticket */
  1904.                         //get array, modify event id and insert
  1905.                         $ticket = $EM_Ticket->to_array();
  1906.                         unset($ticket['ticket_id']);
  1907.                         //clean up ticket values
  1908.                         foreach($ticket as $k => $v){
  1909.                             if( empty($v) && $k != 'ticket_name' ){
  1910.                                 $ticket[$k] = 'NULL';
  1911.                             }else{
  1912.                                 $data_type = !empty($EM_Ticket->fields[$k]['type']) ? $EM_Ticket->fields[$k]['type']:'%s';
  1913.                                 $ticket[$k] = $wpdb->prepare($data_type,$v);
  1914.                             }
  1915.                         }
  1916.                        
  1917.                         $base_ticket_start_ts = strtotime( str_replace( "'", '', $ticket['ticket_start'] ) );
  1918.                         $base_ticket_end_ts = strtotime( str_replace( "'", '', $ticket['ticket_end'] ) );
  1919.                        
  1920.                         foreach($event_ids as $event_id){
  1921.                             if( $base_ticket_start_ts )
  1922.                                 $ticket['ticket_start'] = "'" . date("Y-m-d", $base_ticket_start_ts + ( $event_start[ $event_id ] - $base_event_start_ts ) ) . "'";
  1923.  
  1924.                             if( $base_ticket_end_ts )
  1925.                                 $ticket['ticket_end'] = "'" . date("Y-m-d", $base_ticket_end_ts + ( $event_start[ $event_id ] - $base_event_start_ts ) ) . "'";
  1926.                        
  1927.                             $ticket['event_id'] = $event_id;
  1928.                             $meta_inserts[] = "(".implode(",",$ticket).")";
  1929.                         }
  1930.                     }
  1931.                     $keys = "(".implode(",",array_keys($ticket)).")";
  1932.                     $values = implode(',',$meta_inserts);
  1933.                     $sql = "INSERT INTO ".EM_TICKETS_TABLE." $keys VALUES $values";
  1934.                     $result = $wpdb->query($sql);
  1935.                 }
  1936.             }else{
  1937.                 $this->add_error('You have not defined a date range long enough to create a recurrence.','dbem');
  1938.                 $result = false;
  1939.             }
  1940.             return apply_filters('em_event_save_events', !in_array(false, $event_saves) && $result !== false, $this, $event_ids, $post_ids);
  1941.         }
  1942.         return apply_filters('em_event_save_events', false, $this, $event_ids, $post_ids);
  1943.     }
  1944.    
  1945.     /**
  1946.      * Removes all reoccurring events.
  1947.      * @param $recurrence_id
  1948.      * @return null
  1949.      */
  1950.     function delete_events(){
  1951.         global $wpdb;
  1952.         do_action('em_event_delete_events_pre', $this);
  1953.         //So we don't do something we'll regret later, we could just supply the get directly into the delete, but this is safer
  1954.         $result = false;
  1955.         if( $this->can_manage('delete_events', 'delete_others_events') ){
  1956.             //delete events from em_events table
  1957.             $events_array = EM_Events::get( array('recurrence_id'=>$this->event_id, 'scope'=>'all', 'status'=>false ) );
  1958.             foreach($events_array as $EM_Event){
  1959.                 /* @var $EM_Event EM_Event */
  1960.                 if($EM_Event->recurrence_id == $this->event_id){
  1961.                     $EM_Event->delete(true);
  1962.                 }
  1963.             }          
  1964.         }
  1965.         return apply_filters('delete_events', $result, $this, $events_array);
  1966.     }
  1967.    
  1968.     /**
  1969.      * Returns the days that match the recurrance array passed (unix timestamps)
  1970.      * @param array $recurrence
  1971.      * @return array
  1972.      */
  1973.     function get_recurrence_days(){        
  1974.         $start_date = strtotime($this->event_start_date);
  1975.         $end_date = strtotime($this->event_end_date);
  1976.                
  1977.         $weekdays = explode(",", $this->recurrence_byday); //what days of the week (or if monthly, one value at index 0)
  1978.          
  1979.         $matching_days = array();
  1980.         $aDay = 86400;  // a day in seconds
  1981.         $aWeek = $aDay * 7;      
  1982.            
  1983.         //TODO can this be optimized?
  1984.         switch ( $this->recurrence_freq ){
  1985.             case 'daily':
  1986.                 //If daily, it's simple. Get start date, add interval timestamps to that and create matching day for each interval until end date.
  1987.                 $current_date = $start_date;
  1988.                 while( $current_date <= $end_date ){
  1989.                     $matching_days[] = $current_date;
  1990.                     $current_date = $current_date + ($aDay * $this->recurrence_interval);
  1991.                 }
  1992.                 break;
  1993.             case 'weekly':
  1994.                 //sort out week one, get starting days and then days that match time span of event (i.e. remove past events in week 1)
  1995.                 $start_of_week = get_option('start_of_week'); //Start of week depends on WordPress
  1996.                 //first, get the start of this week as timestamp
  1997.                 $event_start_day = date('w', $start_date);
  1998.                 //then get the timestamps of weekdays during this first week, regardless if within event range
  1999.                 $start_weekday_dates = array(); //Days in week 1 where there would events, regardless of event date range
  2000.                 for($i = 0; $i < 7; $i++){
  2001.                     $weekday_date = $start_date+($aDay*$i); //the date of the weekday we're currently checking
  2002.                     $weekday_day = date('w',$weekday_date); //the day of the week we're checking, taking into account wp start of week setting
  2003.  
  2004.                     if( in_array( $weekday_day, $weekdays) ){
  2005.                         $start_weekday_dates[] = $weekday_date; //it's in our starting week day, so add it
  2006.                     }
  2007.                 }                  
  2008.                 //for each day of eventful days in week 1, add 7 days * weekly intervals
  2009.                 foreach ($start_weekday_dates as $weekday_date){
  2010.                     //Loop weeks by interval until we reach or surpass end date
  2011.                     while($weekday_date <= $end_date){
  2012.                         if( $weekday_date >= $start_date && $weekday_date <= $end_date ){
  2013.                             $matching_days[] = $weekday_date;
  2014.                         }
  2015.                         $weekday_date = $weekday_date + ($aWeek *  $this->recurrence_interval);
  2016.                     }
  2017.                 }//done!
  2018.                 break;  
  2019.             case 'monthly':
  2020.                 //loop months starting this month by intervals
  2021.                 $current_arr = getdate($start_date);
  2022.                 $end_arr = getdate($end_date);
  2023.                 $end_month_date = strtotime( date('Y-m-t', $end_date) ); //End date on last day of month
  2024.                 $current_date = strtotime( date('Y-m-1', $start_date) ); //Start date on first day of month
  2025.                 while( $current_date <= $end_month_date ){
  2026.                     $last_day_of_month = date('t', $current_date);
  2027.                     //Now find which day we're talking about
  2028.                     $current_week_day = date('w',$current_date);
  2029.                     $matching_month_days = array();
  2030.                     //Loop through days of this years month and save matching days to temp array
  2031.                     for($day = 1; $day <= $last_day_of_month; $day++){
  2032.                         if((int) $current_week_day == $this->recurrence_byday){
  2033.                             $matching_month_days[] = $day;
  2034.                         }
  2035.                         $current_week_day = ($current_week_day < 6) ? $current_week_day+1 : 0;                         
  2036.                     }
  2037.                     //Now grab from the array the x day of the month
  2038.                     $matching_day = ($this->recurrence_byweekno > 0) ? $matching_month_days[$this->recurrence_byweekno-1] : array_pop($matching_month_days);
  2039.                     $matching_date = strtotime(date('Y-m',$current_date).'-'.$matching_day);
  2040.                     if($matching_date >= $start_date && $matching_date <= $end_date){
  2041.                         $matching_days[] = $matching_date;
  2042.                     }
  2043.                     //add the number of days in this month to make start of next month
  2044.                     $current_arr['mon'] += $this->recurrence_interval;
  2045.                     if($current_arr['mon'] > 12){
  2046.                         //FIXME this won't work if interval is more than 12
  2047.                         $current_arr['mon'] = $current_arr['mon'] - 12;
  2048.                         $current_arr['year']++;
  2049.                     }
  2050.                     $current_date = strtotime("{$current_arr['year']}-{$current_arr['mon']}-1");
  2051.                 }
  2052.                 break;
  2053.             case 'yearly':
  2054.                 //If yearly, it's simple. Get start date, add interval timestamps to that and create matching day for each interval until end date.
  2055.                 $month = date('m', $this->start);
  2056.                 $day = date('d',$this->start);
  2057.                 $year = date('Y',$this->start);
  2058.                 $end_year = date('Y',$this->end);
  2059.                 if( @mktime(0,0,0,$day,$month,$end_year) < $this->end ) $end_year--;
  2060.                 while( $year <= $end_year ){
  2061.                     $matching_days[] = mktime(0,0,0,$month,$day,$year);
  2062.                     $year++;
  2063.                 }          
  2064.                 break;
  2065.         }
  2066.         sort($matching_days);
  2067.         return apply_filters('em_events_get_recurrence_days', $matching_days, $this);
  2068.     }
  2069.    
  2070.     /**
  2071.      * If event is recurring, set recurrences to same status as template
  2072.      * @param $status
  2073.      */
  2074.     function set_status_events($status){
  2075.         //give sub events same status
  2076.         $events_array = EM_Events::get( array('recurrence_id'=>$this->post_id, 'scope'=>'all', 'status'=>false ) );
  2077.         foreach($events_array as $EM_Event){
  2078.             /* @var $EM_Event EM_Event */
  2079.             if($EM_Event->recurrence_id == $this->event_id){
  2080.                 $EM_Event->set_status($status);
  2081.             }
  2082.         }
  2083.     }
  2084.    
  2085.     /**
  2086.      * Returns a string representation of this recurrence. Will return false if not a recurrence
  2087.      * @return string
  2088.      */
  2089.     function get_recurrence_description() {
  2090.         $EM_Event_Recurring = $this->get_event_recurrence();
  2091.         $recurrence = $this->to_array();
  2092.         $weekdays_name = array(__('Sunday', 'dbem'),__('Monday', 'dbem'),__('Tuesday', 'dbem'),__('Wednesday', 'dbem'),__('Thursday', 'dbem'),__('Friday', 'dbem'),__('Saturday', 'dbem'));
  2093.         $monthweek_name = array('1' => __('the first %s of the month', 'dbem'),'2' => __('the second %s of the month', 'dbem'), '3' => __('the third %s of the month', 'dbem'), '4' => __('the fourth %s of the month', 'dbem'), '-1' => __('the last %s of the month', 'dbem'));
  2094.         $output = sprintf (__('From %1$s to %2$s', 'dbem'),  $EM_Event_Recurring->event_start_date, $EM_Event_Recurring->event_end_date).", ";
  2095.         if ($EM_Event_Recurring->recurrence_freq == 'daily')  {
  2096.             $freq_desc =__('everyday', 'dbem');
  2097.             if ($EM_Event_Recurring->recurrence_interval > 1 ) {
  2098.                 $freq_desc = sprintf (__("every %s days", 'dbem'), $EM_Event_Recurring->recurrence_interval);
  2099.             }
  2100.         }elseif ($EM_Event_Recurring->recurrence_freq == 'weekly')  {
  2101.             $weekday_array = explode(",", $EM_Event_Recurring->recurrence_byday);
  2102.             $natural_days = array();
  2103.             foreach($weekday_array as $day){
  2104.                 array_push($natural_days, $weekdays_name[$day]);
  2105.             }
  2106.             $output .= implode(", ", $natural_days);
  2107.             $freq_desc = " " . __("every week", 'dbem');
  2108.             if ($EM_Event_Recurring->recurrence_interval > 1 ) {
  2109.                 $freq_desc = " ".sprintf (__("every %s weeks", 'dbem'), $EM_Event_Recurring->recurrence_interval);
  2110.             }
  2111.            
  2112.         }elseif ($EM_Event_Recurring->recurrence_freq == 'monthly')  {
  2113.             $weekday_array = explode(",", $EM_Event_Recurring->recurrence_byday);
  2114.             $natural_days = array();
  2115.             foreach($weekday_array as $day){
  2116.                 array_push($natural_days, $weekdays_name[$day]);
  2117.             }
  2118.             $freq_desc = sprintf (($monthweek_name[$EM_Event_Recurring->recurrence_byweekno]), implode(" and ", $natural_days));
  2119.             if ($EM_Event_Recurring->recurrence_interval > 1 ) {
  2120.                 $freq_desc .= ", ".sprintf (__("every %s months",'dbem'), $EM_Event_Recurring->recurrence_interval);
  2121.             }
  2122.         }elseif ($EM_Event_Recurring->recurrence_freq == 'yearly')  {
  2123.             $freq_desc .= __("every year", 'dbem');
  2124.             if ($EM_Event_Recurring->recurrence_interval > 1 ) {
  2125.                 $freq_desc .= sprintf (__("every %s years",'dbem'), $EM_Event_Recurring->recurrence_interval);
  2126.             }
  2127.         }else{
  2128.             $freq_desc = "[ERROR: corrupted database record]";
  2129.         }
  2130.         $output .= $freq_desc;
  2131.         return  $output;
  2132.     }  
  2133.    
  2134.     /**********************************************************
  2135.      * UTILITIES
  2136.      ***********************************************************/
  2137.    
  2138.     /**
  2139.      * Can the user manage this?
  2140.      */
  2141.     function can_manage( $owner_capability = false, $admin_capability = false, $user_to_check = false ){
  2142.         if( $this->event_id == '' && !is_user_logged_in() && get_option('dbem_events_anonymous_submissions') ){
  2143.             $user_to_check = get_option('dbem_events_anonymous_user');
  2144.         }
  2145.         return apply_filters('em_event_can_manage', parent::can_manage($owner_capability, $admin_capability, $user_to_check), $this, $owner_capability, $admin_capability, $user_to_check);
  2146.     }
  2147. }
  2148.  
  2149. //TODO placeholder targets filtering could be streamlined better
  2150. /**
  2151.  * This is a temporary filter function which mimicks the old filters in the old 2.x placeholders function
  2152.  * @param string $result
  2153.  * @param EM_Event $event
  2154.  * @param string $placeholder
  2155.  * @param string $target
  2156.  * @return mixed
  2157.  */
  2158. function em_event_output_placeholder($result,$event,$placeholder,$target='html'){
  2159.     if( $target == 'raw' ) return $result;
  2160.     if( in_array($placeholder, array("#_EXCERPT",'#_EVENTEXCERPT', "#_LOCATIONEXCERPT")) && $target == 'html' ){
  2161.         $result = apply_filters('dbem_notes_excerpt', $result);
  2162.     }elseif( $placeholder == '#_CONTACTEMAIL' && $target == 'html' ){
  2163.         $result = em_ascii_encode($event->get_contact()->user_email);
  2164.     }elseif( in_array($placeholder, array('#_EVENTNOTES','#_NOTES','#_DESCRIPTION','#_LOCATIONNOTES','#_CATEGORYNOTES','#_CATEGORYDESCRIPTION')) ){
  2165.         if($target == 'rss'){
  2166.             $result = apply_filters('dbem_notes_rss', $result);
  2167.             $result = apply_filters('the_content_rss', $result);
  2168.         }elseif($target == 'map'){
  2169.             $result = apply_filters('dbem_notes_map', $result);
  2170.         }elseif($target == 'ical'){
  2171.             $result = apply_filters('dbem_notes_ical', $result);
  2172.         }elseif ($target == "email"){    
  2173.             $result = apply_filters('dbem_notes_email', $result);
  2174.         }else{ //html
  2175.             $result = apply_filters('dbem_notes', $result);
  2176.         }
  2177.     }elseif( in_array($placeholder, array("#_NAME",'#_LOCATION','#_TOWN','#_ADDRESS','#_LOCATIONNAME',"#_EVENTNAME","#_LOCATIONNAME",'#_CATEGORY')) ){
  2178.         if ($target == "rss"){
  2179.             $result = apply_filters('dbem_general_rss', $result);
  2180.         }elseif ($target == "ical"){    
  2181.             $result = apply_filters('dbem_general_ical', $result);
  2182.         }elseif ($target == "email"){    
  2183.             $result = apply_filters('dbem_general_email', $result);
  2184.         }else{ //html
  2185.             $result = apply_filters('dbem_general', $result);
  2186.         }              
  2187.     }
  2188.     return $result;
  2189. }
  2190. add_filter('em_category_output_placeholder','em_event_output_placeholder',1,4);
  2191. add_filter('em_event_output_placeholder','em_event_output_placeholder',1,4);
  2192. add_filter('em_location_output_placeholder','em_event_output_placeholder',1,4);
  2193. // FILTERS
  2194. // filters for general events field (corresponding to those of  "the _title")
  2195. add_filter('dbem_general', 'wptexturize');
  2196. add_filter('dbem_general', 'convert_chars');
  2197. add_filter('dbem_general', 'trim');
  2198. // filters for the notes field in html (corresponding to those of  "the _content")
  2199. add_filter('dbem_notes', 'wptexturize');
  2200. add_filter('dbem_notes', 'convert_smilies');
  2201. add_filter('dbem_notes', 'convert_chars');
  2202. add_filter('dbem_notes', 'wpautop');
  2203. add_filter('dbem_notes', 'prepend_attachment');
  2204. add_filter('dbem_notes', 'do_shortcode');
  2205. // RSS content filter
  2206. add_filter('dbem_notes_rss', 'convert_chars', 8);
  2207. add_filter('dbem_general_rss', 'esc_html', 8);
  2208. // Notes map filters
  2209. add_filter('dbem_notes_map', 'convert_chars', 8);
  2210. add_filter('dbem_notes_map', 'js_escape');
  2211. //embeds support if using placeholders
  2212. if ( is_object($GLOBALS['wp_embed']) ){
  2213.     add_filter( 'dbem_notes', array( $GLOBALS['wp_embed'], 'run_shortcode' ), 8 );
  2214.     add_filter( 'dbem_notes', array( $GLOBALS['wp_embed'], 'autoembed' ), 8 );
  2215. }
  2216.  
  2217. /**
  2218.  * This function replaces the default gallery shortcode, so it can check if this is a recurring event recurrence and pass on the parent post id as the default post.
  2219.  * @param array $attr
  2220.  */
  2221. function em_event_gallery_override( $attr = array() ){
  2222.     global $post;
  2223.     if( $post->post_type == EM_POST_TYPE_EVENT && empty($attr['id']) && empty($attr['ids']) ){
  2224.         //no id specified, so check if it's recurring and override id with recurrence template post id
  2225.         $EM_Event = em_get_event($post->ID, 'post_id');
  2226.         if( $EM_Event->is_recurrence() ){
  2227.             $attr['id'] = $EM_Event->get_event_recurrence()->post_id;
  2228.         }
  2229.     }
  2230.     return gallery_shortcode($attr);
  2231. }
  2232. function em_event_gallery_override_init(){
  2233.     remove_shortcode('gallery');
  2234.     add_shortcode('gallery', 'em_event_gallery_override');
  2235. }
  2236. add_action('init','em_event_gallery_override_init', 1000); //so that plugins like JetPack don't think we're overriding gallery, we're not i swear!
  2237. ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement