/**
  *		This function is a singleton method used to instantiate the EEM_Answer object
  *
  *		@access public
  *		@return \EEM_Answer
  */
 public static function instance()
 {
     // check if instance of EEM_Answer already exists
     if (!self::$_instance instanceof EEM_Answer) {
         // instantiate Espresso_model
         self::$_instance = new self();
     }
     return self::$_instance;
 }
 /**
  * @group 8237
  */
 public function test_get_ANS_value__with_unsafe_html_in_db()
 {
     $a = $this->new_model_obj_with_dependencies('Answer');
     global $wpdb;
     //manually insert bad stuff into the answer
     $success = $wpdb->update(EEM_Answer::instance()->table(), array('ANS_value' => '<img src=x onerror=prompt(document.cookie)><img src=x onerror=prompt(/XSS/)><img src=x onerror=prompt(1)>'), array('ANS_ID' => $a->ID()), array('%s'), array('%s'));
     $this->assertEquals(1, $success);
     $this->assertEquals('<img src=x onerror=prompt(document.cookie)><img src=x onerror=prompt(/XSS/)><img src=x onerror=prompt(1)>', EEM_Answer::instance()->get_var(array(array('ANS_ID' => $a->ID())), 'ANS_value'));
     //ok so it's definetely got dangerous stuff in the db, but when we fetch it using the models it should be safe again
     $a->refresh_from_db();
     $this->assertEquals('', $a->get_raw('ANS_value'));
 }
/**
 * @deprecated since 4.8.32.rc.000 because it has issues on https://events.codebasehq.com/projects/event-espresso/tickets/9165
 * it is preferred to instead use _update_attendee_registration_form_new() which
 * also better handles form validation. Exits
 * @param EE_Admin_Page $admin_page
 * @return void
 */
function ee_deprecated_update_attendee_registration_form_old($admin_page)
{
    //check if the old hooks are in use. If not, do the default
    if (!ee_deprecated_using_old_registration_admin_custom_questions_form_hooks() || !$admin_page instanceof EE_Admin_Page) {
        return;
    }
    $req_data = $admin_page->get_request_data();
    $qstns = isset($req_data['qstn']) ? $req_data['qstn'] : FALSE;
    $REG_ID = isset($req_data['_REG_ID']) ? absint($req_data['_REG_ID']) : FALSE;
    $qstns = apply_filters('FHEE__Registrations_Admin_Page___update_attendee_registration_form__qstns', $qstns);
    if (!$REG_ID || !$qstns) {
        EE_Error::add_error(__('An error occurred. No registration ID and/or registration questions were received.', 'event_espresso'), __FILE__, __FUNCTION__, __LINE__);
    }
    $success = TRUE;
    // allow others to get in on this awesome fun   :D
    do_action('AHEE__Registrations_Admin_Page___save_attendee_registration_form__after_reg_and_attendee_save', $REG_ID, $qstns);
    // loop thru questions... FINALLY!!!
    foreach ($qstns as $QST_ID => $qstn) {
        //if $qstn isn't an array then it doesn't already have an answer, so let's create the answer
        if (!is_array($qstn)) {
            $success = $this->_save_new_answer($REG_ID, $QST_ID, $qstn);
            continue;
        }
        foreach ($qstn as $ANS_ID => $ANS_value) {
            //get answer
            $query_params = array(0 => array('ANS_ID' => $ANS_ID, 'REG_ID' => $REG_ID, 'QST_ID' => $QST_ID));
            $answer = EEM_Answer::instance()->get_one($query_params);
            //this MAY be an array but NOT have an answer because its multi select.  If so then we need to create the answer
            if (!$answer instanceof EE_Answer) {
                $set_values = array('QST_ID' => $QST_ID, 'REG_ID' => $REG_ID, 'ANS_value' => $qstn);
                $success = EEM_Answer::instance()->insert($set_values);
                continue 2;
            }
            $answer->set('ANS_value', $ANS_value);
            $success = $answer->save();
        }
    }
    $what = __('Registration Form', 'event_espresso');
    $route = $REG_ID ? array('action' => 'view_registration', '_REG_ID' => $REG_ID) : array('action' => 'default');
    $admin_page->redirect_after_action($success, $what, __('updated', 'event_espresso'), $route);
    exit;
}
 /**
  *        _get_question_target_db_column
  *
  * @access 	public
  * @param 	EE_Registration $registration (so existing answers for registration are included)
  * @param 	int 	$EVT_ID 	so all question groups are included for event (not just answers from registration).
  * @throws EE_Error
  * @return 	array
  */
 public function assemble_array_of_groups_questions_and_options(EE_Registration $registration, $EVT_ID = 0)
 {
     if (empty($EVT_ID)) {
         throw new EE_Error(__('An error occurred. No EVT_ID is included.  Needed to know which question groups to retrieve.', 'event_espresso'));
     }
     $questions = array();
     // get all question groups for event
     $qgs = $this->get_question_groups_for_event($EVT_ID, $registration);
     if (!empty($qgs)) {
         foreach ($qgs as $qg) {
             $qsts = $qg->questions();
             $questions[$qg->ID()] = $qg->model_field_array();
             $questions[$qg->ID()]['QSG_questions'] = array();
             foreach ($qsts as $qst) {
                 if ($qst->is_system_question()) {
                     continue;
                 }
                 $answer = EEM_Answer::instance()->get_one(array(array('QST_ID' => $qst->ID(), 'REG_ID' => $registration->ID())));
                 $answer = $answer instanceof EE_Answer ? $answer : EEM_Answer::instance()->create_default_object();
                 $qst_name = $qstn_id = $qst->ID();
                 $ans_id = $answer->ID();
                 $qst_name = !empty($ans_id) ? '[' . $qst_name . '][' . $ans_id . ']' : '[' . $qst_name . ']';
                 $input_name = '';
                 $input_id = sanitize_key($qst->display_text());
                 $input_class = '';
                 $questions[$qg->ID()]['QSG_questions'][$qst->ID()] = $qst->model_field_array();
                 $questions[$qg->ID()]['QSG_questions'][$qst->ID()]['QST_input_name'] = 'qstn' . $input_name . $qst_name;
                 $questions[$qg->ID()]['QSG_questions'][$qst->ID()]['QST_input_id'] = $input_id . '-' . $qstn_id;
                 $questions[$qg->ID()]['QSG_questions'][$qst->ID()]['QST_input_class'] = $input_class;
                 $questions[$qg->ID()]['QSG_questions'][$qst->ID()]['QST_options'] = array();
                 $questions[$qg->ID()]['QSG_questions'][$qst->ID()]['qst_obj'] = $qst;
                 $questions[$qg->ID()]['QSG_questions'][$qst->ID()]['ans_obj'] = $answer;
                 if ($qst->type() == 'RADIO_BTN' || $qst->type() == 'CHECKBOX' || $qst->type() == 'DROPDOWN') {
                     $QSOs = $qst->options(TRUE, $answer->value());
                     if (is_array($QSOs)) {
                         foreach ($QSOs as $QSO_ID => $QSO) {
                             $questions[$qg->ID()]['QSG_questions'][$qst->ID()]['QST_options'][$QSO_ID] = $QSO->model_field_array();
                         }
                     }
                 }
             }
         }
     }
     return $questions;
 }
 /**
  * Export a custom CSV of registration info including: A bunch of the reg fields, the time of the event, the price name,
  * and the questions associated with the registrations
  * @param int $event_id
  */
 function report_registrations_for_event($event_id = NULL)
 {
     $reg_fields_to_include = array('TXN_ID', 'ATT_ID', 'REG_ID', 'REG_date', 'REG_code', 'REG_count', 'REG_final_price');
     $att_fields_to_include = array('ATT_fname', 'ATT_lname', 'ATT_email', 'ATT_address', 'ATT_address2', 'ATT_city', 'STA_ID', 'CNT_ISO', 'ATT_zip', 'ATT_phone');
     $registrations_csv_ready_array = array();
     $reg_model = EE_Registry::instance()->load_model('Registration');
     $query_params = apply_filters('FHEE__EE_Export__report_registration_for_event', array(array('OR' => array('Transaction.STS_ID' => array('NOT IN', array(EEM_Transaction::failed_status_code, EEM_Transaction::abandoned_status_code)), 'STS_ID' => EEM_Registration::status_id_approved), 'Ticket.TKT_deleted' => array('IN', array(true, false))), 'order_by' => array('Transaction.TXN_ID' => 'asc', 'REG_count' => 'asc'), 'force_join' => array('Transaction', 'Ticket', 'Attendee'), 'caps' => EEM_Base::caps_read_admin), $event_id);
     if ($event_id) {
         $query_params[0]['EVT_ID'] = $event_id;
     } else {
         $query_params['force_join'][] = 'Event';
     }
     $registration_rows = $reg_model->get_all_wpdb_results($query_params);
     //get all questions which relate to someone in this group
     $registration_ids = array();
     foreach ($registration_rows as $reg_row) {
         $registration_ids[] = intval($reg_row['Registration.REG_ID']);
     }
     //		EEM_Question::instance()->show_next_x_db_queries();
     $questions_for_these_regs_rows = EEM_Question::instance()->get_all_wpdb_results(array(array('Answer.REG_ID' => array('IN', $registration_ids))));
     foreach ($registration_rows as $reg_row) {
         if (is_array($reg_row)) {
             $reg_csv_array = array();
             if (!$event_id) {
                 //get the event's name and Id
                 $reg_csv_array[__('Event', 'event_espresso')] = sprintf(__('%1$s (%2$s)', 'event_espresso'), $this->_prepare_value_from_db_for_display(EEM_Event::instance(), 'EVT_name', $reg_row['Event_CPT.post_title']), $reg_row['Event_CPT.ID']);
             }
             $is_primary_reg = $reg_row['Registration.REG_count'] == '1' ? true : false;
             /*@var $reg_row EE_Registration */
             foreach ($reg_fields_to_include as $field_name) {
                 $field = $reg_model->field_settings_for($field_name);
                 if ($field_name == 'REG_final_price') {
                     $value = $this->_prepare_value_from_db_for_display($reg_model, $field_name, $reg_row['Registration.REG_final_price'], 'localized_float');
                 } elseif ($field_name == 'REG_count') {
                     $value = sprintf(__('%s of %s', 'event_espresso'), $this->_prepare_value_from_db_for_display($reg_model, 'REG_count', $reg_row['Registration.REG_count']), $this->_prepare_value_from_db_for_display($reg_model, 'REG_group_size', $reg_row['Registration.REG_group_size']));
                 } elseif ($field_name == 'REG_date') {
                     $value = $this->_prepare_value_from_db_for_display($reg_model, $field_name, $reg_row['Registration.REG_date'], 'no_html');
                 } else {
                     $value = $this->_prepare_value_from_db_for_display($reg_model, $field_name, $reg_row[$field->get_qualified_column()]);
                 }
                 $reg_csv_array[$this->_get_column_name_for_field($field)] = $value;
                 if ($field_name == 'REG_final_price') {
                     //add a column named Currency after the final price
                     $reg_csv_array[__("Currency", "event_espresso")] = EE_Config::instance()->currency->code;
                 }
             }
             //get pretty status
             $stati = EEM_Status::instance()->localized_status(array($reg_row['Registration.STS_ID'] => __('unknown', 'event_espresso'), $reg_row['TransactionTable.STS_ID'] => __('unknown', 'event_espresso')), FALSE, 'sentence');
             $reg_csv_array[__("Registration Status", 'event_espresso')] = $stati[$reg_row['Registration.STS_ID']];
             //get pretty trnasaction status
             $reg_csv_array[__("Transaction Status", 'event_espresso')] = $stati[$reg_row['TransactionTable.STS_ID']];
             $reg_csv_array[__('Transaction Amount Due', 'event_espresso')] = $is_primary_reg ? $this->_prepare_value_from_db_for_display(EEM_Transaction::instance(), 'TXN_total', $reg_row['TransactionTable.TXN_total'], 'localized_float') : '0.00';
             $reg_csv_array[__('Amount Paid', 'event_espresso')] = $is_primary_reg ? $this->_prepare_value_from_db_for_display(EEM_Transaction::instance(), 'TXN_paid', $reg_row['TransactionTable.TXN_paid'], 'localized_float') : '0.00';
             $payment_methods = array();
             $gateway_txn_ids_etc = array();
             $payment_times = array();
             if ($is_primary_reg && $reg_row['TransactionTable.TXN_ID']) {
                 $payments_info = EEM_Payment::instance()->get_all_wpdb_results(array(array('TXN_ID' => $reg_row['TransactionTable.TXN_ID'], 'STS_ID' => EEM_Payment::status_id_approved), 'force_join' => array('Payment_Method')), ARRAY_A, 'Payment_Method.PMD_admin_name as name, Payment.PAY_txn_id_chq_nmbr as gateway_txn_id, Payment.PAY_timestamp as payment_time');
                 foreach ($payments_info as $payment_method_and_gateway_txn_id) {
                     $payment_methods[] = isset($payment_method_and_gateway_txn_id['name']) ? $payment_method_and_gateway_txn_id['name'] : __('Unknown', 'event_espresso');
                     $gateway_txn_ids_etc[] = isset($payment_method_and_gateway_txn_id['gateway_txn_id']) ? $payment_method_and_gateway_txn_id['gateway_txn_id'] : '';
                     $payment_times[] = isset($payment_method_and_gateway_txn_id['payment_time']) ? $payment_method_and_gateway_txn_id['payment_time'] : '';
                 }
             }
             $reg_csv_array[__('Payment Date(s)', 'event_espresso')] = implode(',', $payment_times);
             $reg_csv_array[__('Payment Method(s)', 'event_espresso')] = implode(",", $payment_methods);
             $reg_csv_array[__('Gateway Transaction ID(s)', 'event_espresso')] = implode(',', $gateway_txn_ids_etc);
             //get whether or not the user has checked in
             $reg_csv_array[__("Check-Ins", "event_espresso")] = $reg_model->count_related($reg_row['Registration.REG_ID'], 'Checkin');
             //get ticket of registration and its price
             $ticket_model = EE_Registry::instance()->load_model('Ticket');
             if ($reg_row['Ticket.TKT_ID']) {
                 $ticket_name = $this->_prepare_value_from_db_for_display($ticket_model, 'TKT_name', $reg_row['Ticket.TKT_name']);
                 $datetimes_strings = array();
                 foreach (EEM_Datetime::instance()->get_all_wpdb_results(array(array('Ticket.TKT_ID' => $reg_row['Ticket.TKT_ID']), 'order_by' => array('DTT_EVT_start' => 'ASC'), 'default_where_conditions' => 'none')) as $datetime) {
                     $datetimes_strings[] = $this->_prepare_value_from_db_for_display(EEM_Datetime::instance(), 'DTT_EVT_start', $datetime['Datetime.DTT_EVT_start']);
                 }
             } else {
                 $ticket_name = __('Unknown', 'event_espresso');
                 $datetimes_strings = array(__('Unknown', 'event_espresso'));
             }
             $reg_csv_array[$ticket_model->field_settings_for('TKT_name')->get_nicename()] = $ticket_name;
             $reg_csv_array[__("Datetimes of Ticket", "event_espresso")] = implode(", ", $datetimes_strings);
             //get datetime(s) of registration
             //add attendee columns
             foreach ($att_fields_to_include as $att_field_name) {
                 $field_obj = EEM_Attendee::instance()->field_settings_for($att_field_name);
                 if ($reg_row['Attendee_CPT.ID']) {
                     if ($att_field_name == 'STA_ID') {
                         $value = EEM_State::instance()->get_var(array(array('STA_ID' => $reg_row['Attendee_Meta.STA_ID'])), 'STA_name');
                     } elseif ($att_field_name == 'CNT_ISO') {
                         $value = EEM_Country::instance()->get_var(array(array('CNT_ISO' => $reg_row['Attendee_Meta.CNT_ISO'])), 'CNT_name');
                     } else {
                         $value = $this->_prepare_value_from_db_for_display(EEM_Attendee::instance(), $att_field_name, $reg_row[$field_obj->get_qualified_column()]);
                     }
                 } else {
                     $value = '';
                 }
                 $reg_csv_array[$this->_get_column_name_for_field($field_obj)] = $value;
             }
             //make sure each registration has the same questions in the same order
             foreach ($questions_for_these_regs_rows as $question_row) {
                 if (!isset($reg_csv_array[$question_row['Question.QST_admin_label']])) {
                     $reg_csv_array[$question_row['Question.QST_admin_label']] = null;
                 }
             }
             //now fill out the questions THEY answered
             foreach (EEM_Answer::instance()->get_all_wpdb_results(array(array('REG_ID' => $reg_row['Registration.REG_ID']), 'force_join' => array('Question'))) as $answer_row) {
                 /* @var $answer EE_Answer */
                 if ($answer_row['Question.QST_ID']) {
                     $question_label = $this->_prepare_value_from_db_for_display(EEM_Question::instance(), 'QST_admin_label', $answer_row['Question.QST_admin_label']);
                 } else {
                     $question_label = sprintf(__('Question $s', 'event_espresso'), $answer_row['Answer.QST_ID']);
                 }
                 if (isset($answer_row['Question.QST_type']) && $answer_row['Question.QST_type'] == EEM_Question::QST_type_state) {
                     $reg_csv_array[$question_label] = EEM_State::instance()->get_state_name_by_ID($answer_row['Answer.ANS_value']);
                 } else {
                     $reg_csv_array[$question_label] = $this->_prepare_value_from_db_for_display(EEM_Answer::instance(), 'ANS_value', $answer_row['Answer.ANS_value']);
                 }
             }
             $registrations_csv_ready_array[] = apply_filters('FHEE__EE_Export__report_registrations__reg_csv_array', $reg_csv_array, $reg_row);
         }
     }
     //if we couldn't export anything, we want to at least show the column headers
     if (empty($registrations_csv_ready_array)) {
         $reg_csv_array = array();
         $model_and_fields_to_include = array('Registration' => $reg_fields_to_include, 'Attendee' => $att_fields_to_include);
         foreach ($model_and_fields_to_include as $model_name => $field_list) {
             $model = EE_Registry::instance()->load_model($model_name);
             foreach ($field_list as $field_name) {
                 $field = $model->field_settings_for($field_name);
                 $reg_csv_array[$this->_get_column_name_for_field($field)] = null;
                 //$registration->get($field->get_name());
             }
         }
         $registrations_csv_ready_array[] = $reg_csv_array;
     }
     if ($event_id) {
         $event_slug = EEM_Event::instance()->get_var(array(array('EVT_ID' => $event_id)), 'EVT_slug');
         if (!$event_slug) {
             $event_slug = __('unknown', 'event_espresso');
         }
     } else {
         $event_slug = __('all', 'event_espresso');
     }
     $filename = sprintf("registrations-for-%s", $event_slug);
     $handle = $this->EE_CSV->begin_sending_csv($filename);
     $this->EE_CSV->write_data_array_to_csv($handle, $registrations_csv_ready_array);
     $this->EE_CSV->end_sending_csv($handle);
 }
 /**
  * @access public
  * @param EE_Registration $registration
  * @param EE_Question     $question
  * @return 	EE_Form_Input_Base
  */
 public function reg_form_question(EE_Registration $registration, EE_Question $question)
 {
     // if this question was for an attendee detail, then check for that answer
     $answer_value = EEM_Answer::instance()->get_attendee_property_answer_value($registration, $question->ID());
     $answer = $answer_value === null ? EEM_Answer::instance()->get_one(array(array('QST_ID' => $question->ID(), 'REG_ID' => $registration->ID()))) : null;
     // if NOT returning to edit an existing registration OR if this question is for an attendee property OR we still don't have an EE_Answer object
     if (!$registration->reg_url_link() || $answer_value || !$answer instanceof EE_Answer) {
         // create an EE_Answer object for storing everything in
         $answer = EE_Answer::new_instance(array('QST_ID' => $question->ID(), 'REG_ID' => $registration->ID()));
     }
     // verify instance
     if ($answer instanceof EE_Answer) {
         if (!empty($answer_value)) {
             $answer->set('ANS_value', $answer_value);
         }
         $answer->cache('Question', $question);
         $answer_cache_id = $question->system_ID() != NULL ? $question->system_ID() . '-' . $registration->reg_url_link() : $question->ID() . '-' . $registration->reg_url_link();
         $registration->cache('Answer', $answer, $answer_cache_id);
     }
     return $this->_generate_question_input($registration, $question, $answer);
 }
 /**
  * Saves
  * @access private
  * @param bool $REG_ID
  * @return bool
  */
 private function _save_reg_custom_questions_form($REG_ID = FALSE)
 {
     if (!$REG_ID) {
         EE_Error::add_error(__('An error occurred. No registration ID was received.', 'event_espresso'), __FILE__, __FUNCTION__, __LINE__);
     }
     $form = $this->_get_reg_custom_questions_form($REG_ID);
     $form->receive_form_submission($this->_req_data);
     $success = false;
     if ($form->is_valid()) {
         foreach ($form->subforms() as $question_group_id => $question_group_form) {
             foreach ($question_group_form->inputs() as $question_id => $input) {
                 $where_conditions = array('QST_ID' => $question_id, 'REG_ID' => $REG_ID);
                 $possibly_new_values = array('ANS_value' => $input->normalized_value());
                 $answer = EEM_Answer::instance()->get_one(array($where_conditions));
                 if ($answer instanceof EE_Answer) {
                     $success = $answer->save($possibly_new_values);
                 } else {
                     //insert it then
                     $cols_n_vals = array_merge($where_conditions, $possibly_new_values);
                     $answer = EE_Answer::new_instance($cols_n_vals);
                     $success = $answer->save();
                 }
             }
         }
     } else {
         EE_Error::add_error($form->get_validation_error_string(), __FILE__, __FUNCTION__, __LINE__);
     }
     return $success;
 }
 private function _save_new_answer($REG_ID, $QST_ID, $ans)
 {
     $set_values = array('QST_ID' => $QST_ID, 'REG_ID' => $REG_ID, 'ANS_value' => $ans);
     $success = EEM_Answer::instance()->insert($set_values);
     return $success;
 }
 /**
  * Gets the registration's answer value to the specified question
  * (either the question's ID or a question object)
  * @param EE_Question|int $question
  * @param bool            $pretty_value
  * @return array|string if pretty_value= true, the result will always be a string
  * (because the answer might be an array of answer values, so passing pretty_value=true
  * will convert it into some kind of string)
  */
 public function answer_value_to_question($question, $pretty_value = true)
 {
     $question_id = EEM_Question::instance()->ensure_is_ID($question);
     return EEM_Answer::instance()->get_answer_value_to_question($this, $question_id, $pretty_value);
 }
 /**
  * set property values for question form input
  * @access public
  * @param    string $property
  * @param    mixed  $value
  * @return mixed
  */
 public function set($property = NULL, $value = NULL)
 {
     if (!empty($property)) {
         if (EEM_Question::instance()->has_field($property)) {
             $this->_QST->set($property, $value);
         } else {
             if (EEM_Answer::instance()->has_field($property)) {
                 $this->_ANS->set($property, $value);
             } else {
                 if ($this->_question_form_input_property_exists(__CLASS__, $property)) {
                     echo "<hr>{$property} is a prop of QFI";
                     $this->{$property} = $value;
                     return TRUE;
                 }
             }
         }
     }
     return NULL;
 }
 /**
  *        load and display the Registration Single Page Checkout
  *
  * @access 	public
  * @param 	bool $from_admin
  * @return 	string
  */
 public function registration_checkout($from_admin = FALSE)
 {
     do_action('AHEE_log', __FILE__, __FUNCTION__, '');
     //d($this->_cart);
     EE_Registry::instance()->load_helper('Form_Fields');
     EE_Registry::instance()->load_helper('Template');
     EE_Registry::instance()->load_class('Question_Form_Input', array(), FALSE, FALSE, TRUE);
     $event_queue = array();
     $total_items = 0;
     $ticket_count = array();
     $payment_required = FALSE;
     $sold_out_events = array();
     $events_requiring_pre_approval = array();
     $additional_event_attendees = array();
     //		$events_that_use_coupon_codes = array();
     //		$events_that_use_groupon_codes = array();
     $template_args = array('css_class' => '', 'confirmation_data' => '', 'reg_page_discounts_dv_class' => 'hidden', 'additional_attendee_reg_info' => NULL, 'whats_in_the_cart' => '', 'prmy_att_input_name' => NULL);
     $event_queue['title'] = __('Registrations', 'event_espresso');
     $additional_attendee_forms = FALSE;
     $registrations = $this->_transaction->registrations(array(), TRUE);
     // grab the saved registrations from the transaction
     if ($this->_transaction instanceof EE_Transaction && $registrations !== NULL) {
         //d( $this->_transaction );
         $event_queue['has_items'] = TRUE;
         $prev_event = NULL;
         foreach ($registrations as $registration) {
             if (($registration->event()->is_sold_out() || $registration->event()->is_sold_out(TRUE)) && !$this->_reg_url_link == $registration->reg_url_link()) {
                 // add event to list of events that are sold out
                 $sold_out_events[$registration->event()->ID()] = '<li><span class="dashicons dashicons-marker ee-icon-size-16 pink-text"></span>' . $registration->event()->name() . '</li>';
             }
             $payment_required = $registration->status_ID() == EEM_Registration::status_id_pending_payment || $registration->status_ID() == EEM_Registration::status_id_approved ? TRUE : $payment_required;
             if (!$payment_required && !$this->_reg_url_link == $registration->reg_url_link()) {
                 // add event to list of events with pre-approval reg status
                 $events_requiring_pre_approval[$registration->event()->ID()] = '<li><span class="dashicons dashicons-marker ee-icon-size-16 orange-text"></span>' . $registration->event()->name() . '</li>';
             }
             $total_items++;
             $ticket_count[$registration->ticket()->ID()] = isset($ticket_count[$registration->ticket()->ID()]) ? $ticket_count[$registration->ticket()->ID()] + 1 : 1;
             $line_item_ID = $registration->reg_url_link();
             $event_queue['items'][$line_item_ID]['ticket'] = $registration->ticket();
             $event_queue['items'][$line_item_ID]['event'] = $registration->event();
             $event_queue['items'][$line_item_ID]['reg_count'] = $registration->count();
             // do we need to generate data regarding attendees and reg form questions?
             if (strpos($this->_current_step, 'attendee_information') !== FALSE) {
                 $question_meta = array('EVT_ID' => $registration->event()->ID(), 'att_nmbr' => $registration->count(), 'ticket_id' => $registration->ticket()->ID(), 'input_name' => '[' . $line_item_ID . ']', 'input_id' => $line_item_ID, 'input_class' => 'ee-reg-page-questions' . $template_args['css_class']);
                 $Question_Groups = EE_Registry::instance()->load_model('Question_Group')->get_all(array(array('Event.EVT_ID' => $registration->event()->ID(), 'Event_Question_Group.EQG_primary' => $registration->count() == 1 ? TRUE : FALSE), 'order_by' => array('QSG_order' => 'ASC')));
                 foreach ($Question_Groups as $QSG_ID => $Question_Group) {
                     $where = array('QST_deleted' => 0);
                     if (!$from_admin) {
                         $where['QST_admin_only'] = 0;
                     }
                     $Questions = $Question_Group->get_many_related('Question', array($where, 'order_by' => array('Question_Group_Question.QGQ_order' => 'ASC')));
                     foreach ($Questions as $Question) {
                         if ($Question instanceof EE_Question) {
                             // if this question was for an attendee detail, then check for that answer
                             $answer_value = EEM_Answer::instance()->get_attendee_property_answer_value($registration, $Question->ID());
                             $answer = $this->_reg_url_link || !$answer_value ? EEM_Answer::instance()->get_one(array(array('QST_ID' => $Question->ID(), 'REG_ID' => $registration->ID()))) : NULL;
                             // if NOT returning to edit an existing registration OR if this question is for an attendee property OR we still don't have an EE_Answer object
                             if (!$this->_reg_url_link || $answer_value || !$answer instanceof EE_Answer) {
                                 // create an EE_Answer object for storing everything in
                                 $answer = EE_Answer::new_instance(array('QST_ID' => $Question->ID(), 'REG_ID' => $registration->ID()));
                             }
                             if ($answer instanceof EE_Answer) {
                                 if (!empty($answer_value)) {
                                     $answer->set('ANS_value', $answer_value);
                                 }
                                 $question_meta['attendee'][$Question->is_system_question() ? $Question->system_ID() : $Question->ID()] = $answer->value();
                                 $answer->cache('Question', $Question);
                                 $answer_cache_id = $Question->system_ID() != NULL ? $Question->system_ID() . '-' . $line_item_ID : $Question->ID() . '-' . $line_item_ID;
                                 //								echo '<h4>$answer_cache_id : ' . $answer_cache_id . '  <br /><span style="font-size:10px;font-weight:normal;">' . __FILE__ . '<br />line no: ' . __LINE__ . '</span></h4>';
                                 $registration->cache('Answer', $answer, $answer_cache_id);
                             }
                             $Question_Groups[$QSG_ID]->cache('Question', $Question);
                         }
                     }
                 }
                 //					printr( $registration, '$registration  <br /><span style="font-size:10px;font-weight:normal;">' . __FILE__ . '<br />line no: ' . __LINE__ . '</span>', 'auto' );
                 add_filter('FHEE__EEH_Form_Fields__label_html', array('EED_Single_Page_Checkout', 'reg_form_form_field_label_wrap'), 10, 2);
                 add_filter('FHEE__EEH_Form_Fields__input_html', array('EED_Single_Page_Checkout', 'reg_form_form_field_input__wrap'), 10, 2);
                 $attendee_questions = EEH_Form_Fields::generate_question_groups_html2($Question_Groups, $question_meta, $from_admin, 'div');
                 // show this attendee form?
                 if (empty($attendee_questions)) {
                     $event_queue['items'][$line_item_ID]['additional_attendee_reg_info'] = "\n" . '<input type="hidden" id="' . $line_item_ID . '-additional_attendee_reg_info" name="qstn[' . $line_item_ID . '][additional_attendee_reg_info]" value="0" />' . "\n";
                 } else {
                     $additional_attendee_forms = $registration->count() == 1 ? FALSE : TRUE;
                     $event_queue['items'][$line_item_ID]['additional_attendee_reg_info'] = '';
                 }
                 $event_queue['items'][$line_item_ID]['attendee_questions'] = $attendee_questions;
                 // is this the primary registrant ?
                 if ($registration->count() == 1) {
                     // grab line item from primary attendee
                     $template_args['prmy_att_input_name'] = $line_item_ID;
                 } else {
                     // for all  attendees other than the primary attendee
                     $additional_event_attendees[$registration->ticket()->ID()][$line_item_ID] = array('ticket' => $registration->ticket()->name(), 'att_nmbr' => $registration->count(), 'input_id' => $line_item_ID, 'input_name' => '[' . $line_item_ID . ']');
                     $item_name = $registration->ticket()->name();
                     $item_name .= $registration->ticket()->description() != '' ? ' - ' . $registration->ticket()->description() : '';
                     // if this is a new ticket OR if this is the very first additional attendee after the primary attendee
                     if ($registration->ticket()->ID() != $prev_event || $registration->count() == 2) {
                         $additional_event_attendees[$registration->ticket()->ID()][$line_item_ID]['event_hdr'] = $item_name;
                         $prev_event = $registration->ticket()->ID();
                     } else {
                         // no heading
                         $additional_event_attendees[$registration->ticket()->ID()][$line_item_ID]['event_hdr'] = FALSE;
                     }
                 }
             }
         }
         if (!$this->_reg_url_link) {
             EE_Registry::instance()->SSN->set_session_data(array('transaction' => $this->_transaction));
         }
         //				echo '<h3>'. __CLASS__ . '->' . __FUNCTION__ . ' <br /><span style="font-size:10px;font-weight:normal;">' . __FILE__ . '<br />line no: ' . __LINE__ . '</span></h3>';
         //				EE_Registry::instance()->SSN->update();
         //				d( $this->_transaction );
         //				d( $this->_cart );
     } else {
         // empty
         $event_queue['has_items'] = FALSE;
     }
     // sold_out_events
     $template_args['sold_out_events'] = implode($sold_out_events);
     $template_args['sold_out_events_msg'] = apply_filters('FHEE__Single_Page_Checkout__registration_checkout__sold_out_events_msg', __('It appears that the event you were about to make a payment for has sold out since you first registered. If you have already made a partial payment towards this event, please contact the event administrator for a refund.', 'event_espresso'));
     // events_requiring_pre_approval
     $template_args['events_requiring_pre_approval'] = implode($events_requiring_pre_approval);
     $template_args['events_requiring_pre_approval_msg'] = apply_filters('FHEE__Single_Page_Checkout__registration_checkout__sold_out_events_msg', __('The following events do not require payment at this time and will not be billed during this transaction. Billing will only occur after the attendee has been approved by the event organizer. You will be notified when your registration has been processed. If this is a free event, then no billing will occur.', 'event_espresso'));
     //  GOT COUPONS ?
     $template_args['events_that_use_coupon_codes'] = '';
     $template_args['use_coupon_codes'] = FALSE;
     // Groupons ?
     $template_args['events_that_use_groupon_codes'] = '';
     $template_args['use_groupon_codes'] = FALSE;
     $template_args['spco_reg_page_ajax_coupons_url'] = add_query_arg(array('ee' => 'apply_coupon'), $this->_reg_page_base_url);
     //		$template_args['print_copy_info'] = $additional_attendee_forms || $total_items > 2 ? TRUE : FALSE;
     $template_args['total_items'] = $total_items;
     $template_args['ticket_count'] = $ticket_count;
     $template_args['print_copy_info'] = $additional_attendee_forms;
     //		d($additional_event_attendees);
     $template_args['additional_event_attendees'] = $additional_event_attendees;
     // total monies paid to date
     $total_payments = 0;
     // the original total
     $cart_total_before_tax = $this->_cart->get_cart_total_before_tax();
     // get cart total
     $grand_total = $this->_cart->get_cart_grand_total();
     $template_args['grand_total'] = apply_filters('FHEE__EED_Single_Page_Checkout__registration_checkout__grand_total', $grand_total);
     // check if monies are potentially owing
     $template_args['payment_required'] = $cart_total_before_tax > 0 ? $payment_required : FALSE;
     // not a free event?
     if ($template_args['payment_required']) {
         //check for any previous payments
         if ($template_args['payments'] = $this->_transaction->approved_payments()) {
             foreach ($template_args['payments'] as $payment) {
                 if ($payment instanceof EE_Payment) {
                     // increment total payments
                     $total_payments += $payment->amount();
                 }
             }
             $template_args['pay_date_frmt'] = get_option('date_format') . ' ' . get_option('time_format');
         }
     } else {
         //unset( self::$_reg_steps['payment_options'] );
         EE_Registry::instance()->SSN->set_session_data(array('billing_info' => 'no payment required'));
         $template_args['payments'] = array();
     }
     $template_args['sub_total'] = $cart_total_before_tax;
     $template_args['taxes'] = $this->_cart->get_taxes_line_item()->children();
     // what's left to pay?
     $amount_owing = $grand_total - $total_payments;
     $template_args['amount_owing'] = apply_filters('FHEE__EED_Single_Page_Checkout__registration_checkout__amount_owing', $amount_owing);
     //$template_args['grand_total'] = $template_args['amount_owing'] !== FALSE ? $amount_owing : $grand_total;
     $template_args['total_items'] = $event_queue['total_items'] = $total_items;
     //	d( $event_queue );
     $template_args['event_queue'] = $event_queue;
     $template_args['images_dir_url'] = EE_GLOBAL_ASSETS_URL . 'images/';
     $template_args['reg_url_link'] = $this->_reg_url_link;
     $template_args['return_url'] = add_query_arg(array('ee' => 'event_queue'), $this->_reg_page_base_url);
     $template_args['update_url'] = add_query_arg(array('ee' => 'update_event_queue'), $this->_reg_page_base_url);
     $template_args['register_url'] = add_query_arg(array('ee' => '_register'), $this->_reg_page_base_url);
     $template_args['event_queue_url'] = add_query_arg(array('ee' => 'event_queue'), $this->_reg_page_base_url);
     $template_args['confirmation_data'] = $this->_current_step == 'registration_confirmation' ? $this->_registration_confirmation() : '';
     $step_or_revisit = __('Step ', 'event_espresso');
     if ($this->_revisit && $this->_current_step == 'attendee_information') {
         // Update Registration Details
         $confirmation_btn_text = sprintf(__('Update%1$sRegistration%1$sDetails', 'event_espresso'), '&nbsp;');
         $confirmation_btn_text = apply_filters('FHEE__EED_Single_Page_Checkout__registration_checkout__button_text__update_registration_details', $confirmation_btn_text);
         $step_or_revisit = __('Edit', 'event_espresso');
     } else {
         if ($this->_revisit && $this->_current_step == 'payment_options') {
             // Process Payment
             $confirmation_btn_text = sprintf(__('Process%1$sPayment', 'event_espresso'), '&nbsp;');
             $confirmation_btn_text = apply_filters('FHEE__EED_Single_Page_Checkout__registration_checkout__button_text__process_payment', $confirmation_btn_text);
             $step_or_revisit = '';
         } else {
             // Finalize Registration
             $confirmation_btn_text = sprintf(__('Finalize%1$sRegistration', 'event_espresso'), '&nbsp;');
             $confirmation_btn_text = apply_filters('FHEE__EED_Single_Page_Checkout__registration_checkout__button_text__finalize_registration', $confirmation_btn_text);
         }
     }
     // grand total less than paid but greater than zero ?
     if ($grand_total < $this->_transaction->paid() && $grand_total > 0 && $this->_next_step == 'payment_options') {
         // owing money
         $proceed_to_payment_btn_text = sprintf(__('%1$s%2$s%1$sProceed%1$sto%1$sPayment', 'event_espresso'), '&nbsp;', '&amp;');
         $confirmation_btn_text .= apply_filters('FHEE__EED_Single_Page_Checkout__registration_checkout__button_text__and_proceed_to_payment', $proceed_to_payment_btn_text);
     }
     add_action('AHEE__SPCO_after_reg_step_form', array($this, 'add_extra_finalize_registration_inputs'), 10, 2);
     $template_args['from_admin'] = $from_admin;
     //if in admin we exit at this point and display the questions template
     if ($from_admin) {
         //some custom template args
         $template_args['step_dv_class'] = '';
         $template_args['revisit'] = $this->_revisit;
         return EEH_Template::display_template($this->_templates['registration_page_attendee_information'], $template_args, TRUE);
     }
     $proceed_to_btn_text = sprintf(__('Proceed%1$sto%1$s', 'event_espresso'), '&nbsp;');
     $proceed_to_btn_text = apply_filters('FHEE__EED_Single_Page_Checkout__registration_checkout__button_text__proceed_to', $proceed_to_btn_text);
     $registration_steps = '';
     $step_nmbr = 1;
     // set pointer to first step
     reset(self::$_reg_steps);
     // loop through steps
     while ($reg_step_details = current(self::$_reg_steps)) {
         $reg_step = key(self::$_reg_steps);
         //			echo '<br/><h4>$reg_step : ' . $reg_step . '  <br /><span style="font-size:10px;font-weight:normal;">' . __FILE__ . '<br />line no: ' . __LINE__ . '</span></h4>';
         //			echo '<h4>$this->_current_step : ' . $this->_current_step . '  <br /><span style="font-size:10px;font-weight:normal;">' . __FILE__ . '<br />line no: ' . __LINE__ . '</span></h4>';
         $edit_lnk_class = $this->_current_step == $reg_step ? ' hidden' : '';
         $edit_lnk_url = add_query_arg(array('ee' => '_register', 'step' => $reg_step_details['display_func']), $this->_reg_page_base_url);
         $step_dv_class = $this->_current_step == $reg_step ? '' : ' hidden';
         $reg_step_form_url = add_query_arg(array('ee' => '_register', 'step' => $reg_step_details['process_func']), $this->_reg_page_base_url);
         $next = $this->_get_next_reg_step();
         //d( $next );
         $next_step = $next ? $next['display_func'] : 'finalize_registration';
         $next_step_text = $next ? $proceed_to_btn_text . $next['name'] : $confirmation_btn_text;
         $step_args = array_merge($template_args, array('step' => $reg_step, 'step_nmbr' => $this->_revisit !== FALSE ? $step_or_revisit : $step_or_revisit . $step_nmbr . ' - ', 'edit_lnk_class' => $edit_lnk_class, 'edit_lnk_url' => $edit_lnk_url, 'step_dv_class' => $step_dv_class, 'reg_step_form_url' => $reg_step_form_url, 'reg_step_ajax_action' => $reg_step_details['process_func'], 'next_step' => $next_step, 'next_step_text' => $next_step_text, 'revisit' => $this->_revisit));
         if ($reg_step == 'payment_options') {
             EE_Registry::instance()->load_model('Gateways');
             // has gateway been set by no-js user?
             if (EE_Registry::instance()->REQ->is_set('payment')) {
                 if ($payment = sanitize_text_field(EE_Registry::instance()->REQ->get('payment'))) {
                     //						d( $payment );
                     if (EE_Registry::instance()->LIB->EEM_Gateways->selected_gateway() != $payment) {
                         EE_Registry::instance()->LIB->EEM_Gateways->set_selected_gateway($payment);
                     } else {
                         EE_Registry::instance()->LIB->EEM_Gateways->unset_selected_gateway($payment);
                     }
                 }
             }
             $step_args['selected_gateway'] = EE_Registry::instance()->LIB->EEM_Gateways->selected_gateway();
         } else {
             $step_args['selected_gateway'] = '';
         }
         //			printr( $step_args, '$step_args  <br /><span style="font-size:10px;font-weight:normal;">' . __FILE__ . '<br />line no: ' . __LINE__ . '</span>', 'auto' );
         //			d( $step_args );
         $registration_steps .= EEH_Template::locate_template($this->_templates[$reg_step_details['template']], $step_args, TRUE, TRUE);
         // pass step info to js
         EE_Registry::$i18n_js_strings['reg_steps'][] = $reg_step_details['display_func'];
         next(self::$_reg_steps);
         $step_nmbr++;
     }
     EE_Registry::$i18n_js_strings['reg_steps'][] = 'finalize_registration';
     $wrapper_args = array('step' => $this->_current_step, 'empty_cart' => $total_items < 1 ? TRUE : FALSE, 'reg_steps' => self::$_reg_steps, 'registration_steps' => $registration_steps, 'revisit' => $this->_revisit, 'empty_msg' => apply_filters('FHEE__Single_Page_Checkout__registration_checkout__empty_msg', __('You need to select at least one event before you can proceed with the registration process.', 'event_espresso')));
     //		d( $wrapper_args );
     EE_Registry::instance()->REQ->add_output(EEH_Template::locate_template($this->_templates['registration_page_wrapper'], $wrapper_args, TRUE, TRUE));
 }
 /**
  * Gets the list of states for the form input
  *
  * @param array|null $states_list
  * @param EE_Question $question
  * @param EE_Registration $registration
  * @return array 2d keys are state IDs, values are their names
  */
 public function use_cached_states_for_form_input($states_list, $question, $registration, $answer)
 {
     $state_options = array('' => array('' => ''));
     $states = $this->checkout->action == 'process_reg_step' ? EEM_State::instance()->get_all_states() : EEM_State::instance()->get_all_active_states();
     if (!empty($states)) {
         foreach ($states as $state) {
             if ($state instanceof EE_State) {
                 $state_options[$state->country()->name()][$state->ID()] = $state->name();
             }
         }
     }
     if ($question instanceof EE_Question && $registration instanceof EE_Registration) {
         $answer = EEM_Answer::instance()->get_one(array(array('QST_ID' => $question->ID(), 'REG_ID' => $registration->ID())));
     } else {
         $answer = EE_Answer::new_instance();
     }
     $state_options = apply_filters('FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__state_options', $state_options, $this, $registration, $question, $answer);
     return $state_options;
 }
 /**
  * Creates a form input from this question which can be used in HTML forms
  * @param EE_Registration $registration
  * @param EE_Answer $answer
  * @param array $input_constructor_args
  * @return EE_Form_Input_Base
  */
 public function generate_form_input($registration = null, $answer = null, $input_constructor_args = array())
 {
     $identifier = $this->is_system_question() ? $this->system_ID() : $this->ID();
     $input_constructor_args = array_merge(array('required' => $this->required() ? true : false, 'html_label_text' => $this->display_text(), 'required_validation_error_message' => $this->required_text()), $input_constructor_args);
     if (!$answer instanceof EE_Answer && $registration instanceof EE_Registration) {
         $answer = EEM_Answer::instance()->get_registration_question_answer_object($registration, $this->ID());
     }
     // has this question been answered ?
     if ($answer instanceof EE_Answer) {
         //answer gets htmlspecialchars called on it, undo that please
         //because the form input's display strategy may call esc_attr too
         //which also does html special characters
         $values_with_html_special_chars = $answer->value();
         if (is_array($values_with_html_special_chars)) {
             $default_value = array_map('htmlspecialchars_decode', $values_with_html_special_chars);
         } else {
             $default_value = htmlspecialchars_decode($values_with_html_special_chars);
         }
         $input_constructor_args['default'] = $default_value;
     }
     $max_max_for_question = EEM_Question::instance()->absolute_max_for_system_question($this->system_ID());
     if (EEM_Question::instance()->question_type_is_in_category($this->type(), 'text')) {
         $input_constructor_args['validation_strategies'][] = new EE_Max_Length_Validation_Strategy(null, min($max_max_for_question, $this->max()));
     }
     $input_constructor_args = apply_filters('FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__input_constructor_args', $input_constructor_args, $registration, $this, $answer);
     $result = null;
     switch ($this->type()) {
         // Text
         case EEM_Question::QST_type_text:
             $result = new EE_Text_Input($input_constructor_args);
             break;
             // Textarea
         // Textarea
         case EEM_Question::QST_type_textarea:
             $result = new EE_Text_Area_Input($input_constructor_args);
             break;
             // Radio Buttons
         // Radio Buttons
         case EEM_Question::QST_type_radio:
             $result = new EE_Radio_Button_Input($this->options(), $input_constructor_args);
             break;
             // Dropdown
         // Dropdown
         case EEM_Question::QST_type_dropdown:
             $result = new EE_Select_Input($this->options(), $input_constructor_args);
             break;
             // State Dropdown
         // State Dropdown
         case EEM_Question::QST_type_state:
             $state_options = apply_filters('FHEE__EE_Question__generate_form_input__state_options', null, $this, $registration, $answer);
             $result = new EE_State_Select_Input($state_options, $input_constructor_args);
             break;
             // Country Dropdown
         // Country Dropdown
         case EEM_Question::QST_type_country:
             $country_options = apply_filters('FHEE__EE_Question__generate_form_input__country_options', null, $this, $registration, $answer);
             $result = new EE_Country_Select_Input($country_options, $input_constructor_args);
             break;
             // Checkboxes
         // Checkboxes
         case EEM_Question::QST_type_checkbox:
             $result = new EE_Checkbox_Multi_Input($this->options(), $input_constructor_args);
             break;
             // Date
         // Date
         case EEM_Question::QST_type_date:
             $result = new EE_Datepicker_Input($input_constructor_args);
             break;
         case EEM_Question::QST_type_html_textarea:
             $input_constructor_args['validation_strategies'][] = new EE_Simple_HTML_Validation_Strategy();
             $result = new EE_Text_Area_Input($input_constructor_args);
             $result->remove_validation_strategy('EE_Plaintext_Validation_Strategy');
             break;
         case EEM_Question::QST_type_email:
             $result = new EE_Email_Input($input_constructor_args);
             break;
         case EEM_Question::QST_type_us_phone:
             $result = new EE_Phone_Input($input_constructor_args);
             break;
         case EEM_Question::QST_type_int:
             $input_constructor_args['validation_strategies'][] = new EE_Int_Validation_Strategy();
             $result = new EE_Text_Input($input_constructor_args);
             break;
         case EEM_Question::QST_type_decimal:
             $input_constructor_args['validation_strategies'][] = new EE_Float_Validation_Strategy();
             $result = new EE_Text_Input($input_constructor_args);
             break;
         case EEM_Question::QST_type_url:
             $input_constructor_args['validation_strategies'][] = new EE_URL_Validation_Strategy();
             $result = new EE_Text_Input($input_constructor_args);
             break;
         case EEM_Question::QST_type_year:
             $result = new EE_Year_Input($input_constructor_args, apply_filters('FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__year_question__four_digit', true, $this), apply_filters('FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__year_question__early_range', 100, $this), apply_filters('FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__year_question__end_range', 100, $this));
             break;
         case EEM_Question::QST_type_multi_select:
             $result = new EE_Select_Multiple_Input($this->options(), $input_constructor_args);
             break;
             // fallback
         // fallback
         default:
             $default_input = apply_filters('FHEE__EE_SPCO_Reg_Step_Attendee_Information___generate_question_input__default', null, $this->type(), $this, $input_constructor_args);
             if (!$default_input) {
                 $default_input = new EE_Text_Input($input_constructor_args);
             }
             $result = $default_input;
     }
     return apply_filters('FHEE__EE_Question__generate_form_input__return', $result, $registration, $this, $answer);
 }