/**
  * Handles attaching Message Templates to the Event on save.
  * @param  EE_Event $evtobj EE event object
  * @param  array       $data   The request data from the form
  * @return bool         success or fail
  */
 public function attach_evt_message_templates($evtobj, $data)
 {
     //first we remove all existing relations on the Event for message types.
     $evtobj->_remove_relations('Message_Template_Group');
     //now let's just loop throught the selected templates and add relations!
     foreach ($data['event_message_templates_relation'] as $grp_ID) {
         $evtobj->_add_relation_to($grp_ID, 'Message_Template_Group');
     }
     //now save
     return $evtobj->save();
 }
 /**
  * update event_datetimes
  * @param  EE_Event 	$evt_obj Event being updated
  * @param  array    	$data    the request data from the form
  * @return EE_Datetime[]
  */
 protected function _update_dtts($evt_obj, $data)
 {
     $timezone = isset($data['timezone_string']) ? $data['timezone_string'] : NULL;
     $saved_dtt_ids = array();
     $saved_dtt_objs = array();
     foreach ($data['edit_event_datetimes'] as $row => $dtt) {
         //trim all values to ensure any excess whitespace is removed.
         $dtt = array_map(function ($datetime_data) {
             return is_array($datetime_data) ? $datetime_data : trim($datetime_data);
         }, $dtt);
         $dtt['DTT_EVT_end'] = isset($dtt['DTT_EVT_end']) && !empty($dtt['DTT_EVT_end']) ? $dtt['DTT_EVT_end'] : $dtt['DTT_EVT_start'];
         $datetime_values = array('DTT_ID' => !empty($dtt['DTT_ID']) ? $dtt['DTT_ID'] : NULL, 'DTT_name' => !empty($dtt['DTT_name']) ? $dtt['DTT_name'] : '', 'DTT_description' => !empty($dtt['DTT_description']) ? $dtt['DTT_description'] : '', 'DTT_EVT_start' => $dtt['DTT_EVT_start'], 'DTT_EVT_end' => $dtt['DTT_EVT_end'], 'DTT_reg_limit' => empty($dtt['DTT_reg_limit']) ? EE_INF : $dtt['DTT_reg_limit'], 'DTT_order' => !isset($dtt['DTT_order']) ? $row : $dtt['DTT_order']);
         //if we have an id then let's get existing object first and then set the new values.  Otherwise we instantiate a new object for save.
         if (!empty($dtt['DTT_ID'])) {
             $DTM = EE_Registry::instance()->load_model('Datetime', array($timezone))->get_one_by_ID($dtt['DTT_ID']);
             //set date and time format according to what is set in this class.
             $DTM->set_date_format($this->_date_format_strings['date']);
             $DTM->set_time_format($this->_date_format_strings['time']);
             foreach ($datetime_values as $field => $value) {
                 $DTM->set($field, $value);
             }
             // make sure the $dtt_id here is saved just in case after the add_relation_to() the autosave replaces it.
             // We need to do this so we dont' TRASH the parent DTT.(save the ID for both key and value to avoid duplications)
             $saved_dtt_ids[$DTM->ID()] = $DTM->ID();
         } else {
             $DTM = EE_Registry::instance()->load_class('Datetime', array($datetime_values, $timezone), FALSE, FALSE);
             //reset date and times to match the format
             $DTM->set_date_format($this->_date_format_strings['date']);
             $DTM->set_time_format($this->_date_format_strings['time']);
             foreach ($datetime_values as $field => $value) {
                 $DTM->set($field, $value);
             }
         }
         $DTM->save();
         $DTM = $evt_obj->_add_relation_to($DTM, 'Datetime');
         $evt_obj->save();
         //before going any further make sure our dates are setup correctly so that the end date is always equal or greater than the start date.
         if ($DTM->get_raw('DTT_EVT_start') > $DTM->get_raw('DTT_EVT_end')) {
             $DTM->set('DTT_EVT_end', $DTM->get('DTT_EVT_start'));
             EE_Registry::instance()->load_helper('DTT_Helper');
             $DTM = EEH_DTT_Helper::date_time_add($DTM, 'DTT_EVT_end', 'days');
             $DTM->save();
         }
         //	now we have to make sure we add the new DTT_ID to the $saved_dtt_ids array
         // because it is possible there was a new one created for the autosave.
         // (save the ID for both key and value to avoid duplications)
         $saved_dtt_ids[$DTM->ID()] = $DTM->ID();
         $saved_dtt_objs[$row] = $DTM;
         //todo if ANY of these updates fail then we want the appropriate global error message.
     }
     //now we need to REMOVE any dtts that got deleted.  Keep in mind that this process will only kick in for DTT's that don't have any DTT_sold on them. So its safe to permanently delete at this point.
     $old_datetimes = explode(',', $data['datetime_IDs']);
     $old_datetimes = $old_datetimes[0] == '' ? array() : $old_datetimes;
     if (is_array($old_datetimes)) {
         $dtts_to_delete = array_diff($old_datetimes, $saved_dtt_ids);
         foreach ($dtts_to_delete as $id) {
             $id = absint($id);
             if (empty($id)) {
                 continue;
             }
             $dtt_to_remove = EE_Registry::instance()->load_model('Datetime')->get_one_by_ID($id);
             //remove tkt relationships.
             $related_tickets = $dtt_to_remove->get_many_related('Ticket');
             foreach ($related_tickets as $tkt) {
                 $dtt_to_remove->_remove_relation_to($tkt, 'Ticket');
             }
             $evt_obj->_remove_relation_to($id, 'Datetime');
             $dtt_to_remove->refresh_cache_of_related_objects();
         }
     }
     return $saved_dtt_objs;
 }
 /**
  * Handles saving everything related to Tickets (datetimes, tickets, prices)
  * @param  EE_Event $evtobj The Event object we're attaching data to
  * @param  array    $data   The request data from the form
  * @return bool             success or fail
  */
 protected function _default_tickets_update(EE_Event $evtobj, $data)
 {
     $success = true;
     $saved_dtt = null;
     $saved_tickets = array();
     $incoming_date_formats = array('Y-m-d', 'h:i a');
     foreach ($data['edit_event_datetimes'] as $row => $dtt) {
         //trim all values to ensure any excess whitespace is removed.
         $dtt = array_map('trim', $dtt);
         $dtt['DTT_EVT_end'] = isset($dtt['DTT_EVT_end']) && !empty($dtt['DTT_EVT_end']) ? $dtt['DTT_EVT_end'] : $dtt['DTT_EVT_start'];
         $datetime_values = array('DTT_ID' => !empty($dtt['DTT_ID']) ? $dtt['DTT_ID'] : NULL, 'DTT_EVT_start' => $dtt['DTT_EVT_start'], 'DTT_EVT_end' => $dtt['DTT_EVT_end'], 'DTT_reg_limit' => empty($dtt['DTT_reg_limit']) ? EE_INF : $dtt['DTT_reg_limit'], 'DTT_order' => $row);
         //if we have an id then let's get existing object first and then set the new values.  Otherwise we instantiate a new object for save.
         if (!empty($dtt['DTT_ID'])) {
             $DTM = EE_Registry::instance()->load_model('Datetime', array($evtobj->get_timezone()))->get_one_by_ID($dtt['DTT_ID']);
             $DTM->set_date_format($incoming_date_formats[0]);
             $DTM->set_time_format($incoming_date_formats[1]);
             foreach ($datetime_values as $field => $value) {
                 $DTM->set($field, $value);
             }
             //make sure the $dtt_id here is saved just in case after the add_relation_to() the autosave replaces it.  We need to do this so we dont' TRASH the parent DTT.
             $saved_dtts[$DTM->ID()] = $DTM;
         } else {
             $DTM = EE_Registry::instance()->load_class('Datetime', array($datetime_values), FALSE, FALSE);
             $DTM->set_date_format($incoming_date_formats[0]);
             $DTM->set_time_format($incoming_date_formats[1]);
             $DTM->set_timezone($evtobj->get_timezone());
             foreach ($datetime_values as $field => $value) {
                 $DTM->set($field, $value);
             }
         }
         $DTM->save();
         $DTT = $evtobj->_add_relation_to($DTM, 'Datetime');
         //load DTT helper
         //before going any further make sure our dates are setup correctly so that the end date is always equal or greater than the start date.
         if ($DTT->get_raw('DTT_EVT_start') > $DTT->get_raw('DTT_EVT_end')) {
             $DTT->set('DTT_EVT_end', $DTT->get('DTT_EVT_start'));
             $DTT = EEH_DTT_Helper::date_time_add($DTT, 'DTT_EVT_end', 'days');
             $DTT->save();
         }
         //now we got to make sure we add the new DTT_ID to the $saved_dtts array  because it is possible there was a new one created for the autosave.
         $saved_dtt = $DTT;
         $success = !$success ? $success : $DTT;
         //if ANY of these updates fail then we want the appropriate global error message. //todod this is actually sucky we need a better error message but this is what it is for now.
     }
     //no dtts get deleted so we don't do any of that logic here.
     //update tickets next
     $old_tickets = isset($data['ticket_IDs']) ? explode(',', $data['ticket_IDs']) : array();
     foreach ($data['edit_tickets'] as $row => $tkt) {
         $incoming_date_formats = array('Y-m-d', 'h:i a');
         $update_prices = false;
         $ticket_price = isset($data['edit_prices'][$row][1]['PRC_amount']) ? $data['edit_prices'][$row][1]['PRC_amount'] : 0;
         // trim inputs to ensure any excess whitespace is removed.
         $tkt = array_map('trim', $tkt);
         if (empty($tkt['TKT_start_date'])) {
             //let's use now in the set timezone.
             $now = new DateTime('now', new DateTimeZone($evtobj->get_timezone()));
             $tkt['TKT_start_date'] = $now->format($incoming_date_formats[0] . ' ' . $incoming_date_formats[1]);
         }
         if (empty($tkt['TKT_end_date'])) {
             //use the start date of the first datetime
             $dtt = $evtobj->first_datetime();
             $tkt['TKT_end_date'] = $dtt->start_date_and_time($incoming_date_formats[0], $incoming_date_formats[1]);
         }
         $TKT_values = array('TKT_ID' => !empty($tkt['TKT_ID']) ? $tkt['TKT_ID'] : NULL, 'TTM_ID' => !empty($tkt['TTM_ID']) ? $tkt['TTM_ID'] : 0, 'TKT_name' => !empty($tkt['TKT_name']) ? $tkt['TKT_name'] : '', 'TKT_description' => !empty($tkt['TKT_description']) ? $tkt['TKT_description'] : '', 'TKT_start_date' => $tkt['TKT_start_date'], 'TKT_end_date' => $tkt['TKT_end_date'], 'TKT_qty' => !isset($tkt['TKT_qty']) || $tkt['TKT_qty'] === '' ? EE_INF : $tkt['TKT_qty'], 'TKT_uses' => !isset($tkt['TKT_uses']) || $tkt['TKT_uses'] === '' ? EE_INF : $tkt['TKT_uses'], 'TKT_min' => empty($tkt['TKT_min']) ? 0 : $tkt['TKT_min'], 'TKT_max' => empty($tkt['TKT_max']) ? EE_INF : $tkt['TKT_max'], 'TKT_row' => $row, 'TKT_order' => isset($tkt['TKT_order']) ? $tkt['TKT_order'] : $row, 'TKT_price' => $ticket_price);
         //if this is a default TKT, then we need to set the TKT_ID to 0 and update accordingly, which means in turn that the prices will become new prices as well.
         if (isset($tkt['TKT_is_default']) && $tkt['TKT_is_default']) {
             $TKT_values['TKT_ID'] = 0;
             $TKT_values['TKT_is_default'] = 0;
             $TKT_values['TKT_price'] = $ticket_price;
             $update_prices = TRUE;
         }
         //if we have a TKT_ID then we need to get that existing TKT_obj and update it
         //we actually do our saves a head of doing any add_relations to because its entirely possible that this ticket didn't removed or added to any datetime in the session but DID have it's items modified.
         //keep in mind that if the TKT has been sold (and we have changed pricing information), then we won't be updating the tkt but instead a new tkt will be created and the old one archived.
         if (!empty($tkt['TKT_ID'])) {
             $TKT = EE_Registry::instance()->load_model('Ticket', array($evtobj->get_timezone()))->get_one_by_ID($tkt['TKT_ID']);
             if ($TKT instanceof EE_Ticket) {
                 $ticket_sold = $TKT->count_related('Registration', array(array('STS_ID' => array('NOT IN', array(EEM_Registration::status_id_incomplete))))) > 0 ? true : false;
                 //let's just check the total price for the existing ticket and determine if it matches the new total price.  if they are different then we create a new ticket (if tkts sold) if they aren't different then we go ahead and modify existing ticket.
                 $create_new_TKT = $ticket_sold && $ticket_price != $TKT->get('TKT_price') && !$TKT->get('TKT_deleted') ? true : false;
                 $TKT->set_date_format($incoming_date_formats[0]);
                 $TKT->set_time_format($incoming_date_formats[1]);
                 //set new values
                 foreach ($TKT_values as $field => $value) {
                     if ($field == 'TKT_qty') {
                         $TKT->set_qty($value);
                     } else {
                         $TKT->set($field, $value);
                     }
                 }
                 //if $create_new_TKT is false then we can safely update the existing ticket.  Otherwise we have to create a new ticket.
                 if ($create_new_TKT) {
                     //archive the old ticket first
                     $TKT->set('TKT_deleted', 1);
                     $TKT->save();
                     //make sure this ticket is still recorded in our saved_tkts so we don't run it through the regular trash routine.
                     $saved_tickets[$TKT->ID()] = $TKT;
                     //create new ticket that's a copy of the existing except a new id of course (and not archived) AND has the new TKT_price associated with it.
                     $TKT = clone $TKT;
                     $TKT->set('TKT_ID', 0);
                     $TKT->set('TKT_deleted', 0);
                     $TKT->set('TKT_price', $ticket_price);
                     $TKT->set('TKT_sold', 0);
                     //now we need to make sure that $new prices are created as well and attached to new ticket.
                     $update_prices = true;
                 }
                 //make sure price is set if it hasn't been already
                 $TKT->set('TKT_price', $ticket_price);
             }
         } else {
             //no TKT_id so a new TKT
             $TKT_values['TKT_price'] = $ticket_price;
             $TKT = EE_Registry::instance()->load_class('Ticket', array($TKT_values), FALSE, FALSE);
             if ($TKT instanceof EE_Ticket) {
                 //need to reset values to properly account for the date formats
                 $TKT->set_date_format($incoming_date_formats[0]);
                 $TKT->set_time_format($incoming_date_formats[1]);
                 $TKT->set_timezone($evtobj->get_timezone());
                 //set new values
                 foreach ($TKT_values as $field => $value) {
                     if ($field == 'TKT_qty') {
                         $TKT->set_qty($value);
                     } else {
                         $TKT->set($field, $value);
                     }
                 }
                 $update_prices = TRUE;
             }
         }
         // cap ticket qty by datetime reg limits
         $TKT->set_qty(min($TKT->qty(), $TKT->qty('reg_limit')));
         //update ticket.
         $TKT->save();
         //before going any further make sure our dates are setup correctly so that the end date is always equal or greater than the start date.
         if ($TKT->get_raw('TKT_start_date') > $TKT->get_raw('TKT_end_date')) {
             $TKT->set('TKT_end_date', $TKT->get('TKT_start_date'));
             $TKT = EEH_DTT_Helper::date_time_add($TKT, 'TKT_end_date', 'days');
             $TKT->save();
         }
         //initially let's add the ticket to the dtt
         $saved_dtt->_add_relation_to($TKT, 'Ticket');
         $saved_tickets[$TKT->ID()] = $TKT;
         //add prices to ticket
         $this->_add_prices_to_ticket($data['edit_prices'][$row], $TKT, $update_prices);
     }
     //however now we need to handle permanently deleting tickets via the ui.  Keep in mind that the ui does not allow deleting/archiving tickets that have ticket sold.  However, it does allow for deleting tickets that have no tickets sold, in which case we want to get rid of permanently because there is no need to save in db.
     $old_tickets = isset($old_tickets[0]) && $old_tickets[0] == '' ? array() : $old_tickets;
     $tickets_removed = array_diff($old_tickets, array_keys($saved_tickets));
     foreach ($tickets_removed as $id) {
         $id = absint($id);
         //get the ticket for this id
         $tkt_to_remove = EE_Registry::instance()->load_model('Ticket')->get_one_by_ID($id);
         //need to get all the related datetimes on this ticket and remove from every single one of them (remember this process can ONLY kick off if there are NO tkts_sold)
         $dtts = $tkt_to_remove->get_many_related('Datetime');
         foreach ($dtts as $dtt) {
             $tkt_to_remove->_remove_relation_to($dtt, 'Datetime');
         }
         //need to do the same for prices (except these prices can also be deleted because again, tickets can only be trashed if they don't have any TKTs sold (otherwise they are just archived))
         $tkt_to_remove->delete_related_permanently('Price');
         //finally let's delete this ticket (which should not be blocked at this point b/c we've removed all our relationships)
         $tkt_to_remove->delete_permanently();
     }
     return array($saved_dtt, $saved_tickets);
 }
 /**
  * update event_datetimes
  * @param  EE_Event 	$evt_obj Event being updated
  * @param  array    	$data    the request data from the form
  * @return EE_Datetime           array of EE_Datetime ids created/updated.
  */
 private function _update_dtts($evt_obj, $data)
 {
     $timezone = isset($data['timezone_string']) ? $data['timezone_string'] : NULL;
     $success = TRUE;
     foreach ($data['edit_event_datetimes'] as $row => $dtt) {
         $dtt['DTT_EVT_end'] = isset($dtt['DTT_EVT_end']) && !empty($dtt['DTT_EVT_end']) ? $dtt['DTT_EVT_end'] : $dtt['DTT_EVT_start'];
         $datetime_values = array('DTT_ID' => !empty($dtt['DTT_ID']) ? $dtt['DTT_ID'] : NULL, 'DTT_name' => !empty($dtt['DTT_name']) ? $dtt['DTT_name'] : '', 'DTT_description' => !empty($dtt['DTT_description']) ? $dtt['DTT_description'] : '', 'DTT_EVT_start' => $dtt['DTT_EVT_start'], 'DTT_EVT_end' => $dtt['DTT_EVT_end'], 'DTT_reg_limit' => empty($dtt['DTT_reg_limit']) ? INF : $dtt['DTT_reg_limit'], 'DTT_order' => !isset($dtt['DTT_order']) ? $row : $dtt['DTT_order']);
         //if we have an id then let's get existing object first and then set the new values.  Otherwise we instantiate a new object for save.
         if (!empty($dtt['DTT_ID'])) {
             $DTM = EE_Registry::instance()->load_model('Datetime', array($timezone))->get_one_by_ID($dtt['DTT_ID']);
             foreach ($datetime_values as $field => $value) {
                 $DTM->set($field, $value);
             }
             //make sure the $dtt_id here is saved just in case after the add_relation_to() the autosave replaces it.  We need to do this so we dont' TRASH the parent DTT.
             $saved_dtts[$DTM->ID()] = $DTM;
         } else {
             $DTM = EE_Registry::instance()->load_class('Datetime', array($datetime_values, $timezone), FALSE, FALSE);
         }
         $DTM->save();
         $DTT = $evt_obj->_add_relation_to($DTM, 'Datetime');
         //before going any further make sure our dates are setup correctly so that the end date is always equal or greater than the start date.
         if ($DTT->get_raw('DTT_EVT_start') > $DTT->get_raw('DTT_EVT_end')) {
             $DTT->set('DTT_EVT_end', $DTT->get('DTT_EVT_start'));
             EE_Registry::instance()->load_helper('DTT_Helper');
             EE_Registry::instance()->load_helper('DTT_Helper');
             $DTT = EEH_DTT_Helper::date_time_add($DTT, 'DTT_EVT_end', 'days');
             $DTT->save();
         }
         $datetimes_start_times[$DTT->start_date_and_time('Y-m-d', 'H:i:s')] = $DTT->ID();
         //now we got to make sure we add the new DTT_ID to the $saved_dtts array  because it is possible there was a new one created for the autosave.
         $saved_dtts[$DTT->ID()] = $DTT;
         $saved_dtt_objs[$row] = $DTT;
         $success = !$success ? $success : $DTT;
         //if ANY of these updates fail then we want the appropriate global error message. //todod this is actually sucky we need a better error message but this is what it is for now.
     }
     //now we need to REMOVE any dtts that got deleted.  Keep in mind that this process will only kick in for DTT's that don't have any DTT_sold on them. So its safe to permanently delete at this point.
     $old_datetimes = explode(',', $data['datetime_IDs']);
     $old_datetimes = $old_datetimes[0] == '' ? array() : $old_datetimes;
     if (is_array($old_datetimes)) {
         $dtts_to_delete = array_diff($old_datetimes, array_keys($saved_dtts));
         foreach ($dtts_to_delete as $id) {
             $id = absint($id);
             if (empty($id)) {
                 continue;
             }
             $dtt_to_remove = EE_Registry::instance()->load_model('Datetime')->get_one_by_ID($id);
             //remove tkt relationships.
             $related_tickets = $dtt_to_remove->get_many_related('Ticket');
             foreach ($related_tickets as $tkt) {
                 $dtt_to_remove->_remove_relation_to($tkt, 'Ticket');
             }
             $evt_obj->_remove_relation_to($id, 'Datetime');
         }
     }
     return $saved_dtt_objs;
 }