Completed
Branch BUG-9774-email-validation (c72797)
by
unknown
680:01 queued 665:37
created

EE_Export::instance()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 4
nc 2
nop 1
dl 0
loc 7
rs 9.2
c 0
b 0
f 0
1
<?php if (!defined('EVENT_ESPRESSO_VERSION')) exit('No direct script access allowed');
2
do_action( 'AHEE_log', __FILE__, __FUNCTION__, '' );
3
/**
4
 * EE_Export class
5
 * 
6
 * For creating csv file exports/reports in a single request.
7
 * Note that if you're creating a large csv file this is likely to timeout,
8
 * and so it would be better to use EventEspressoBatchRequest\BatchRequestProcessor
9
 * @package				Event Espresso
10
 * @subpackage			includes/functions
11
 * @author					Brent Christensen
12
 *
13
 * ------------------------------------------------------------------------
14
 */
15
 class EE_Export {
16
	 
17
	 const option_prefix = 'ee_report_job_';
18
19
20
  // instance of the EE_Export object
21
	private static $_instance = NULL;
22
23
  // instance of the EE_CSV object
24
	/**
25
	 *
26
	 * @var EE_CSV
27
	 */
28
	var $EE_CSV = NULL;
29
30
31
	private $_req_data = array();
32
33
34
35
	 /**
36
	  *        private constructor to prevent direct creation
37
	  *
38
	  * @Constructor
39
	  * @access private
40
	  * @param array $request_data
41
	  */
42
 	private function __construct( $request_data = array() ) {
43
		$this->_req_data = $request_data;
44
		$this->today = date("Y-m-d",time());
0 ignored issues
show
Bug introduced by
The property today does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
45
		require_once( EE_CLASSES . 'EE_CSV.class.php' );
46
		$this->EE_CSV= EE_CSV::instance();
47
	}
48
49
50
51
	 /**
52
	  *        @ singleton method used to instantiate class object
53
	  *        @ access public
54
	  *
55
	  * @param array $request_data
56
	  * @return \EE_Export
57
	  */
58
	public static function instance( $request_data = array() ) {
59
		// check if class object is instantiated
60
		if ( self::$_instance === NULL  or ! is_object( self::$_instance ) or ! ( self::$_instance instanceof EE_Export )) {
61
			self::$_instance = new self( $request_data );
62
		}
63
		return self::$_instance;
64
	}
65
66
67
	/**
68
	 *			@Export Event Espresso data - routes export requests
69
	 *		  @access public
70
	 *			@return void | bool
71
	 */
72
	public function export() {
73
74
		// in case of bulk exports, the "actual" action will be in action2, but first check regular action for "export" keyword
75
		if ( isset( $this->_req_data['action'] ) && strpos( $this->_req_data['action'], 'export' ) === FALSE ) {
76
			// check if action2 has export action
77
			if ( isset( $this->_req_data['action2'] ) && strpos( $this->_req_data['action2'], 'export' ) !== FALSE ) {
78
				// whoop! there it is!
79
				$this->_req_data['action'] = $this->_req_data['action2'];
80
			}
81
		}
82
83
		$this->_req_data['export'] = isset( $this->_req_data['export'] ) ? $this->_req_data['export'] : '';
84
85
		switch ($this->_req_data['export']) {
86
			case 'report':
87
				switch ($this->_req_data['action']) {
88
89
90
					case "event";
91
					case "export_events";
92
					case 'all_event_data' :
93
						$this->export_all_event_data();
94
					break;
95
96
					case 'registrations_report_for_event':
97
						$this->report_registrations_for_event( $this->_req_data['EVT_ID'] );
98
					break;
99
100
					case 'attendees':
101
						$this->export_attendees();
102
					break;
103
104
					case 'categories':
105
						$this->export_categories();
106
					break;
107
108
					default:
109
						EE_Error::add_error(__('An error occurred! The requested export report could not be found.','event_espresso'), __FILE__, __FUNCTION__, __LINE__ ) ;
110
						return FALSE;
111
					break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
112
113
				}
114
			break; // end of switch export : report
115
			default:
116
			break;
117
		} // end of switch export
118
119
		exit;
120
121
	}
122
123
	/**
124
	 * Downloads a CSV file with all the columns, but no data. This should be used for importing
125
	 * @return null kills execution
126
	 */
127
	function export_sample(){
128
		$event = EEM_Event::instance()->get_one();
129
		$this->_req_data['EVT_ID'] = $event->ID();
130
		$this->export_all_event_data();
131
	}
132
133
134
135
136
	/**
137
	 *			@Export data for ALL events
138
	 *		  @access public
139
	 *			@return void
140
	 */
141
	function export_all_event_data() {
142
		// are any Event IDs set?
143
		$event_query_params = array();
144
		$related_models_query_params = array();
145
		$related_through_reg_query_params = array();
146
		$datetime_ticket_query_params = array();
147
		$price_query_params = array();
148
		$price_type_query_params = array();
149
		$term_query_params  = array();
150
		$state_country_query_params = array();
151
		$question_group_query_params = array();
152
		$question_query_params = array();
153
		if ( isset( $this->_req_data['EVT_ID'] )) {
154
			// do we have an array of IDs ?
155
156
			if ( is_array( $this->_req_data['EVT_ID'] )) {
157
				$EVT_IDs =  array_map( 'sanitize_text_field', $this->_req_data['EVT_ID'] );
158
				$value_to_equal = array('IN',$EVT_IDs);
159
				$filename = 'events';
160
			} else {
161
				// generate regular where = clause
162
				$EVT_ID = absint( $this->_req_data['EVT_ID'] );
163
				$value_to_equal = $EVT_ID;
164
				$event = EE_Registry::instance()->load_model('Event')->get_one_by_ID($EVT_ID);
165
166
				$filename = 'event-' . ( $event instanceof EE_Event ? $event->slug() : __( 'unknown', 'event_espresso' ) );
167
168
			}
169
			$event_query_params[0]['EVT_ID'] =$value_to_equal;
170
			$related_models_query_params[0]['Event.EVT_ID'] = $value_to_equal;
171
			$related_through_reg_query_params[0]['Registration.EVT_ID'] = $value_to_equal;
172
			$datetime_ticket_query_params[0]['Datetime.EVT_ID'] = $value_to_equal;
173
			$price_query_params[0]['Ticket.Datetime.EVT_ID'] = $value_to_equal;
174
			$price_type_query_params[0]['Price.Ticket.Datetime.EVT_ID'] = $value_to_equal;
175
			$term_query_params[0]['Term_Taxonomy.Event.EVT_ID'] = $value_to_equal;
176
			$state_country_query_params[0]['Venue.Event.EVT_ID'] = $value_to_equal;
177
			$question_group_query_params[0]['Event.EVT_ID'] = $value_to_equal;
178
			$question_query_params[0]['Question_Group.Event.EVT_ID'] = $value_to_equal;
179
180
		} else {
181
			$filename = 'all-events';
182
		}
183
184
185
		// array in the format:  table name =>  query where clause
186
		$models_to_export = array(
187
				'Event'=>$event_query_params,
188
				'Datetime'=>$related_models_query_params,
189
				'Ticket_Template'=>$price_query_params,
190
				'Ticket'=>$datetime_ticket_query_params,
191
				'Datetime_Ticket'=>$datetime_ticket_query_params,
192
				'Price_Type'=>$price_type_query_params,
193
				'Price'=>$price_query_params,
194
				'Ticket_Price'=>$price_query_params,
195
				'Term'=>$term_query_params,
196
				'Term_Taxonomy'=>$related_models_query_params,
197
				'Term_Relationship'=>$related_models_query_params, //model has NO primary key...
198
				'Country'=>$state_country_query_params,
199
				'State'=>$state_country_query_params,
200
				'Venue'=>$related_models_query_params,
201
				'Event_Venue'=>$related_models_query_params,
202
				'Question_Group'=>$question_group_query_params,
203
				'Event_Question_Group'=>$question_group_query_params,
204
				'Question'=>$question_query_params,
205
				'Question_Group_Question'=>$question_query_params,
206
//				'Transaction'=>$related_through_reg_query_params,
207
//				'Registration'=>$related_models_query_params,
208
//				'Attendee'=>$related_through_reg_query_params,
209
//				'Line_Item'=>
210
211
			);
212
213
		$model_data = $this->_get_export_data_for_models( $models_to_export );
214
215
		$filename = $this->generate_filename ( $filename );
216
217 View Code Duplication
		if ( ! $this->EE_CSV->export_multiple_model_data_to_csv( $filename, $model_data )) {
0 ignored issues
show
Documentation introduced by
$model_data is of type boolean, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug Best Practice introduced by
The expression $this->EE_CSV->export_mu...$filename, $model_data) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
Security Bug introduced by
It seems like $filename defined by $this->generate_filename($filename) on line 215 can also be of type false; however, EE_CSV::export_multiple_model_data_to_csv() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
218
			EE_Error::add_error(__("'An error occurred and the Event details could not be exported from the database.'", "event_espresso"), __FILE__, __FUNCTION__, __LINE__ );
219
		}
220
	}
221
222
	function report_attendees(){
223
		$attendee_rows = EEM_Attendee::instance()->get_all_wpdb_results( 
224
			array( 
225
				'force_join' => array( 'State', 'Country' ), 
226
				'caps' => EEM_Base::caps_read_admin 
227
			) 
228
		);
229
		$csv_data = array();
230 View Code Duplication
		foreach( $attendee_rows as $attendee_row ){
231
			$csv_row = array();
232
			foreach( EEM_Attendee::instance()->field_settings() as $field_name => $field_obj ){
233
				if( $field_name == 'STA_ID' ){
234
					$state_name_field = EEM_State::instance()->field_settings_for( 'STA_name' );
235
					$csv_row[ __( 'State', 'event_espresso' ) ] = $attendee_row[ $state_name_field->get_qualified_column() ];
236
				}elseif( $field_name == 'CNT_ISO' ){
237
					$country_name_field = EEM_Country::instance()->field_settings_for( 'CNT_name' );
238
					$csv_row[ __( 'Country', 'event_espresso' ) ] = $attendee_row[ $country_name_field->get_qualified_column() ];
239
				}else{
240
					$csv_row[ $field_obj->get_nicename() ] = $attendee_row[ $field_obj->get_qualified_column() ];
241
				}
242
			}
243
			$csv_data[] = $csv_row;
244
		}
245
246
		$filename = $this->generate_filename ( 'contact-list-report' );
247
248
		$handle = $this->EE_CSV->begin_sending_csv( $filename);
249
		$this->EE_CSV->write_data_array_to_csv($handle, $csv_data);
250
		$this->EE_CSV->end_sending_csv($handle);
251
	}
252
253
254
	/**
255
	 *			@Export data for ALL attendees
256
	 *		  @access public
257
	 *			@return void
258
	 */
259
	function export_attendees() {
260
261
		$states_that_have_an_attendee = EEM_State::instance()->get_all(array(0=>array('Attendee.ATT_ID'=>array('IS NOT NULL'))));
262
		$countries_that_have_an_attendee = EEM_Country::instance()->get_all(array(0=>array('Attendee.ATT_ID'=>array('IS NOT NULL'))));
263
//		$states_to_export_query_params
264
		$models_to_export = array(
265
			'Country'=>array(array('CNT_ISO'=>array('IN',array_keys($countries_that_have_an_attendee)))),
266
			'State'=>array(array('STA_ID'=>array('IN',array_keys($states_that_have_an_attendee)))),
267
			'Attendee'=>array(),
268
		);
269
270
271
272
		$model_data = $this->_get_export_data_for_models( $models_to_export );
273
		$filename = $this->generate_filename ( 'all-attendees' );
274
275 View Code Duplication
		if ( ! $this->EE_CSV->export_multiple_model_data_to_csv( $filename, $model_data )) {
0 ignored issues
show
Security Bug introduced by
It seems like $filename defined by $this->generate_filename('all-attendees') on line 273 can also be of type false; however, EE_CSV::export_multiple_model_data_to_csv() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
Documentation introduced by
$model_data is of type boolean, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug Best Practice introduced by
The expression $this->EE_CSV->export_mu...$filename, $model_data) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
276
			EE_Error::add_error(__('An error occurred and the Attendee data could not be exported from the database.','event_espresso'), __FILE__, __FUNCTION__, __LINE__ );
277
		}
278
	}
279
280
	/**
281
	 * Shortcut for preparing a database result for display
282
	 * @param EEM_Base $model
283
	 * @param string $field_name
284
	 * @param string $raw_db_value
285
	 * @param boolean|string $pretty_schema true to display pretty, a string to use a specific "Schema", or false to NOT display pretty
286
	 * @return string
287
	 */
288 View Code Duplication
	protected function _prepare_value_from_db_for_display( $model, $field_name,  $raw_db_value, $pretty_schema = true ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
289
		$field_obj = $model->field_settings_for( $field_name );
290
		$value_on_model_obj = $field_obj->prepare_for_set_from_db( $raw_db_value );
291
		if( $field_obj instanceof EE_Datetime_Field ) {
292
			$field_obj->set_date_format( EE_CSV::instance()->get_date_format_for_csv( $field_obj->get_date_format( $pretty_schema ) ), $pretty_schema );
0 ignored issues
show
Bug introduced by
It seems like $pretty_schema defined by parameter $pretty_schema on line 288 can also be of type string; however, EE_Datetime_Field::get_date_format() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Bug introduced by
It seems like $pretty_schema defined by parameter $pretty_schema on line 288 can also be of type string; however, EE_Datetime_Field::set_date_format() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
293
			$field_obj->set_time_format( EE_CSV::instance()->get_time_format_for_csv( $field_obj->get_time_format( $pretty_schema ) ), $pretty_schema );
0 ignored issues
show
Bug introduced by
It seems like $pretty_schema defined by parameter $pretty_schema on line 288 can also be of type string; however, EE_Datetime_Field::get_time_format() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Bug introduced by
It seems like $pretty_schema defined by parameter $pretty_schema on line 288 can also be of type string; however, EE_Datetime_Field::set_time_format() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
294
		}
295
		if( $pretty_schema === true){
296
			return $field_obj->prepare_for_pretty_echoing( $value_on_model_obj );
297
		}elseif( is_string( $pretty_schema ) ) {
298
			return $field_obj->prepare_for_pretty_echoing($value_on_model_obj, $pretty_schema );
0 ignored issues
show
Unused Code introduced by
The call to EE_Model_Field_Base::prepare_for_pretty_echoing() has too many arguments starting with $pretty_schema.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
299
		}else{
300
			return $field_obj->prepare_for_get( $value_on_model_obj );
301
		}
302
	}
303
304
	/**
305
	 * Export a custom CSV of registration info including: A bunch of the reg fields, the time of the event, the price name,
306
	 * and the questions associated with the registrations
307
	 * @param int $event_id
308
	 */
309
	function report_registrations_for_event( $event_id = NULL ){
310
		$reg_fields_to_include = array(
311
				'TXN_ID',
312
				'ATT_ID',
313
				'REG_ID',
314
				'REG_date',
315
				'REG_code',
316
				'REG_count',
317
				'REG_final_price'
318
319
		);
320
		$att_fields_to_include = array(
321
			'ATT_fname',
322
			'ATT_lname',
323
			'ATT_email',
324
			'ATT_address',
325
			'ATT_address2',
326
			'ATT_city',
327
			'STA_ID',
328
			'CNT_ISO',
329
			'ATT_zip',
330
			'ATT_phone',
331
		);
332
333
		$registrations_csv_ready_array = array();
334
		$reg_model = EE_Registry::instance()->load_model('Registration');
335
		$query_params = apply_filters(
336
			'FHEE__EE_Export__report_registration_for_event',
337
			array(
338
				array(
339
					'OR' => array(
340
						//don't include registrations from failed or abandoned transactions...
341
						'Transaction.STS_ID' => array( 'NOT IN', array( EEM_Transaction::failed_status_code, EEM_Transaction::abandoned_status_code ) ),
342
						//unless the registration is approved, in which case include it regardless of transaction status
343
						'STS_ID' => EEM_Registration::status_id_approved
344
						),
345
					'Ticket.TKT_deleted' => array( 'IN', array( true, false ) )
346
					),
347
				'order_by' => array('Transaction.TXN_ID'=>'asc','REG_count'=>'asc'),
348
				'force_join' => array( 'Transaction', 'Ticket', 'Attendee' ),
349
				'caps' => EEM_Base::caps_read_admin
350
			),
351
			$event_id
352
		);
353 View Code Duplication
		if( $event_id ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $event_id of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
354
			$query_params[0]['EVT_ID'] =  $event_id;
355
		}else{
356
			$query_params[ 'force_join' ][] = 'Event';
357
		}
358
		$registration_rows = $reg_model->get_all_wpdb_results( $query_params );
359
		//get all questions which relate to someone in this group
360
		$registration_ids = array();
361
		foreach( $registration_rows as $reg_row ) {
362
			$registration_ids[] = intval( $reg_row[ 'Registration.REG_ID'] );
363
		}
364
//		EEM_Question::instance()->show_next_x_db_queries();
365
		$questions_for_these_regs_rows = EEM_Question::instance()->get_all_wpdb_results(array(array('Answer.REG_ID'=>array('IN',$registration_ids))));
366
		foreach($registration_rows as $reg_row){
367
			if ( is_array( $reg_row ) ) {
368
				$reg_csv_array = array();
369
				if( ! $event_id ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $event_id of type integer|null is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
370
					//get the event's name and Id
371
					$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' ] );
372
				}
373
				$is_primary_reg = $reg_row[ 'Registration.REG_count' ] == '1' ? true : false;
374
				/*@var $reg_row EE_Registration */
375
				foreach($reg_fields_to_include as $field_name){
376
					$field = $reg_model->field_settings_for($field_name);
377
					if($field_name == 'REG_final_price'){
378
						$value = $this->_prepare_value_from_db_for_display( $reg_model, $field_name, $reg_row[ 'Registration.REG_final_price'], 'localized_float' );
0 ignored issues
show
Bug introduced by
It seems like $reg_model defined by \EE_Registry::instance()...d_model('Registration') on line 334 can be null; however, EE_Export::_prepare_value_from_db_for_display() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
379
					}elseif( $field_name == 'REG_count' ){
380
						$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' ] ) );
0 ignored issues
show
Bug introduced by
It seems like $reg_model defined by \EE_Registry::instance()...d_model('Registration') on line 334 can be null; however, EE_Export::_prepare_value_from_db_for_display() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
381
					}elseif( $field_name == 'REG_date' ) {
382
						$value = $this->_prepare_value_from_db_for_display( $reg_model, $field_name, $reg_row[ 'Registration.REG_date'], 'no_html' );
0 ignored issues
show
Bug introduced by
It seems like $reg_model defined by \EE_Registry::instance()...d_model('Registration') on line 334 can be null; however, EE_Export::_prepare_value_from_db_for_display() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
383
					}else{
384
						$value = $this->_prepare_value_from_db_for_display( $reg_model, $field_name, $reg_row[ $field->get_qualified_column() ] );
0 ignored issues
show
Bug introduced by
It seems like $reg_model defined by \EE_Registry::instance()...d_model('Registration') on line 334 can be null; however, EE_Export::_prepare_value_from_db_for_display() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
385
					}
386
					$reg_csv_array[$this->_get_column_name_for_field($field)] = $value;
387
					if($field_name == 'REG_final_price'){
388
						//add a column named Currency after the final price
389
						$reg_csv_array[__("Currency", "event_espresso")] = EE_Config::instance()->currency->code;
390
					}
391
				}
392
				//get pretty status
393
				$stati = EEM_Status::instance()->localized_status( array(
394
					$reg_row[ 'Registration.STS_ID' ] => __( 'unknown', 'event_espresso' ),
395
					$reg_row[ 'TransactionTable.STS_ID' ] => __( 'unknown', 'event_espresso' ) ),
396
						FALSE,
397
						'sentence' );
398
				$reg_csv_array[__("Registration Status", 'event_espresso')] = $stati[ $reg_row[ 'Registration.STS_ID' ] ];
399
				//get pretty trnasaction status
400
				$reg_csv_array[__("Transaction Status", 'event_espresso')] = $stati[ $reg_row[ 'TransactionTable.STS_ID' ] ];
401
				$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';
402
				$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';
403
				$payment_methods = array();
404
				$gateway_txn_ids_etc = array();
405
				$payment_times = array();
406 View Code Duplication
				if( $is_primary_reg && $reg_row[ 'TransactionTable.TXN_ID' ] ){
407
					$payments_info = EEM_Payment::instance()->get_all_wpdb_results(
408
							array(
409
								array(
410
									'TXN_ID' => $reg_row[ 'TransactionTable.TXN_ID' ],
411
									'STS_ID' => EEM_Payment::status_id_approved
412
								),
413
								'force_join' => array( 'Payment_Method' ),
414
415
							),
416
							ARRAY_A,
417
							'Payment_Method.PMD_admin_name as name, Payment.PAY_txn_id_chq_nmbr as gateway_txn_id, Payment.PAY_timestamp as payment_time' );
418
419
					foreach( $payments_info as $payment_method_and_gateway_txn_id ){
420
						$payment_methods[] = isset( $payment_method_and_gateway_txn_id[ 'name' ] ) ? $payment_method_and_gateway_txn_id[ 'name' ] : __( 'Unknown', 'event_espresso' );
421
						$gateway_txn_ids_etc[] = isset( $payment_method_and_gateway_txn_id[ 'gateway_txn_id' ] ) ? $payment_method_and_gateway_txn_id[ 'gateway_txn_id' ] : '';
422
						$payment_times[] = isset( $payment_method_and_gateway_txn_id[ 'payment_time' ] ) ? $payment_method_and_gateway_txn_id[ 'payment_time' ] : '';
423
					}
424
425
				}
426
				$reg_csv_array[ __( 'Payment Date(s)', 'event_espresso' ) ] = implode( ',', $payment_times );
427
				$reg_csv_array[ __( 'Payment Method(s)', 'event_espresso' ) ] = implode( ",", $payment_methods );
428
				$reg_csv_array[ __( 'Gateway Transaction ID(s)', 'event_espresso' )] = implode( ',', $gateway_txn_ids_etc );
429
430
				//get whether or not the user has checked in
431
				$reg_csv_array[__("Check-Ins", "event_espresso")] = $reg_model->count_related( $reg_row[ 'Registration.REG_ID'] , 'Checkin' );
432
				//get ticket of registration and its price
433
				$ticket_model = EE_Registry::instance()->load_model('Ticket');
434
				if( $reg_row[ 'Ticket.TKT_ID'] ) {
435
					$ticket_name = $this->_prepare_value_from_db_for_display( $ticket_model, 'TKT_name', $reg_row[ 'Ticket.TKT_name' ] );
0 ignored issues
show
Bug introduced by
It seems like $ticket_model defined by \EE_Registry::instance()->load_model('Ticket') on line 433 can be null; however, EE_Export::_prepare_value_from_db_for_display() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
436
					$datetimes_strings = array();
437 View Code Duplication
					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){
438
						$datetimes_strings[] = $this->_prepare_value_from_db_for_display( EEM_Datetime::instance(), 'DTT_EVT_start', $datetime[ 'Datetime.DTT_EVT_start'] );
439
					}
440
441
				} else {
442
					$ticket_name = __( 'Unknown', 'event_espresso' );
443
					$datetimes_strings = array( __( 'Unknown', 'event_espresso' ) );
444
				}
445
				$reg_csv_array[$ticket_model->field_settings_for('TKT_name')->get_nicename()] = $ticket_name;
446
				$reg_csv_array[__("Datetimes of Ticket", "event_espresso")] = implode(", ", $datetimes_strings);
447
				//get datetime(s) of registration
448
449
				//add attendee columns
450
				foreach($att_fields_to_include as $att_field_name){
451
					$field_obj = EEM_Attendee::instance()->field_settings_for($att_field_name);
452 View Code Duplication
					if( $reg_row[ 'Attendee_CPT.ID' ]){
453
						if($att_field_name == 'STA_ID'){
454
							$value = EEM_State::instance()->get_var( array( array( 'STA_ID' => $reg_row[ 'Attendee_Meta.STA_ID' ] ) ), 'STA_name' );
455
						}elseif($att_field_name == 'CNT_ISO'){
456
							$value = EEM_Country::instance()->get_var( array( array( 'CNT_ISO' => $reg_row[ 'Attendee_Meta.CNT_ISO' ] ) ), 'CNT_name' );
457
						}else{
458
							$value = $this->_prepare_value_from_db_for_display( EEM_Attendee::instance(), $att_field_name, $reg_row[ $field_obj->get_qualified_column() ] );
459
						}
460
					}else{
461
						$value = '';
462
					}
463
464
					$reg_csv_array[$this->_get_column_name_for_field($field_obj)] = $value;
465
				}
466
467
				//make sure each registration has the same questions in the same order
468 View Code Duplication
				foreach($questions_for_these_regs_rows as $question_row){
469
					if( ! isset($reg_csv_array[$question_row[ 'Question.QST_admin_label']])){
470
						$reg_csv_array[$question_row[ 'Question.QST_admin_label' ] ] = null;
471
					}
472
				}
473
				//now fill out the questions THEY answered
474
				foreach( EEM_Answer::instance()->get_all_wpdb_results( array( array( 'REG_ID' => $reg_row[ 'Registration.REG_ID' ] ), 'force_join' => array( 'Question' ) ) ) as $answer_row){
475
					/* @var $answer EE_Answer */
476
					if( $answer_row[ 'Question.QST_ID' ] ){
477
						$question_label = $this->_prepare_value_from_db_for_display( EEM_Question::instance(), 'QST_admin_label', $answer_row[ 'Question.QST_admin_label' ] );
478
					}else{
479
						$question_label = sprintf( __( 'Question $s', 'event_espresso' ), $answer_row[ 'Answer.QST_ID' ] );
480
					}
481 View Code Duplication
                                        if( isset( $answer_row[ 'Question.QST_type'] ) && $answer_row[ 'Question.QST_type' ] == EEM_Question::QST_type_state ) {
482
                                            $reg_csv_array[ $question_label ] = EEM_State::instance()->get_state_name_by_ID( $answer_row[ 'Answer.ANS_value' ] );
483
                                        } else {
484
                                            $reg_csv_array[ $question_label ] = $this->_prepare_value_from_db_for_display( EEM_Answer::instance(), 'ANS_value', $answer_row[ 'Answer.ANS_value' ] );
485
                                        }
486
				}
487
				$registrations_csv_ready_array[] = apply_filters( 'FHEE__EE_Export__report_registrations__reg_csv_array', $reg_csv_array, $reg_row );
488
			}
489
		}
490
491
		//if we couldn't export anything, we want to at least show the column headers
492 View Code Duplication
		if(empty($registrations_csv_ready_array)){
493
			$reg_csv_array = array();
494
			$model_and_fields_to_include = array(
495
				'Registration' => $reg_fields_to_include,
496
				'Attendee' => $att_fields_to_include
497
			);
498
			foreach($model_and_fields_to_include as $model_name => $field_list){
499
				$model = EE_Registry::instance()->load_model($model_name);
500
				foreach($field_list as $field_name){
501
					$field = $model->field_settings_for($field_name);
502
					$reg_csv_array[$this->_get_column_name_for_field($field)] = null;//$registration->get($field->get_name());
503
				}
504
			}
505
			$registrations_csv_ready_array [] = $reg_csv_array;
506
		}
507 View Code Duplication
		if( $event_id ){
0 ignored issues
show
Bug Best Practice introduced by
The expression $event_id of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
508
			$event_slug =  EEM_Event::instance()->get_var( array( array( 'EVT_ID' => $event_id ) ), 'EVT_slug' );
509
			if( ! $event_slug ) {
510
				$event_slug = __( 'unknown', 'event_espresso' );
511
			}
512
		}else{
513
			$event_slug = __( 'all', 'event_espresso' );
514
		}
515
		$filename = sprintf( "registrations-for-%s", $event_slug );
516
517
		$handle = $this->EE_CSV->begin_sending_csv( $filename);
518
		$this->EE_CSV->write_data_array_to_csv($handle, $registrations_csv_ready_array);
519
		$this->EE_CSV->end_sending_csv($handle);
520
	}
521
522
	/**
523
	 * Gets the 'normal' column named for fields
524
	 * @param EE_Model_Field_Base $field
525
	 * @return string
526
	 */
527
	protected function _get_column_name_for_field(EE_Model_Field_Base $field){
528
		return $field->get_nicename()."[".$field->get_name()."]";
529
	}
530
531
532
	/**
533
	 *			@Export data for ALL events
534
	 *		  @access public
535
	 *			@return void
536
	 */
537
	function export_categories() {
538
		// are any Event IDs set?
539
		$query_params = array();
540
		if ( isset( $this->_req_data['EVT_CAT_ID'] )) {
541
			// do we have an array of IDs ?
542
			if ( is_array( $this->_req_data['EVT_CAT_ID'] )) {
543
				// generate an "IN (CSV)" where clause
544
				$EVT_CAT_IDs = array_map( 'sanitize_text_field', $this->_req_data['EVT_CAT_ID'] );
545
				$filename = 'event-categories';
546
				$query_params[0]['term_taxonomy_id'] = array('IN',$EVT_CAT_IDs);
547
			} else {
548
				// generate regular where = clause
549
				$EVT_CAT_ID = absint( $this->_req_data['EVT_CAT_ID'] );
550
				$filename = 'event-category#' . $EVT_CAT_ID;
551
				$query_params[0]['term_taxonomy_id'] = $EVT_CAT_ID;
552
			}
553
		} else {
554
			// no IDs means we will d/l the entire table
555
			$filename = 'all-categories';
556
		}
557
558
		$tables_to_export = array(
559
				'Term_Taxonomy' => $query_params
560
			);
561
562
		$table_data = $this->_get_export_data_for_models( $tables_to_export );
563
		$filename = $this->generate_filename ( $filename );
564
565 View Code Duplication
		if ( ! $this->EE_CSV->export_multiple_model_data_to_csv( $filename, $table_data )) {
0 ignored issues
show
Security Bug introduced by
It seems like $filename defined by $this->generate_filename($filename) on line 563 can also be of type false; however, EE_CSV::export_multiple_model_data_to_csv() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
Documentation introduced by
$table_data is of type boolean, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug Best Practice introduced by
The expression $this->EE_CSV->export_mu...$filename, $table_data) of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
566
			EE_Error::add_error(__('An error occurred and the Category details could not be exported from the database.','event_espresso'), __FILE__, __FUNCTION__, __LINE__ );
567
		}
568
	}
569
570
571
	/**
572
	 *			@process export name to create a suitable filename
573
	 *		  @access private
574
	 *		  @param string - export_name
575
	 *			@return string on success, FALSE on fail
576
	 */
577
	private function generate_filename ( $export_name = '' ) {
578 View Code Duplication
		if ( $export_name != '' ) {
579
			$filename = get_bloginfo('name') . '-' . $export_name;
580
			$filename = sanitize_key( $filename ) . '-' . $this->today;
581
			return $filename;
582
		}	 else {
583
			EE_Error::add_error(__("No filename was provided", "event_espresso"), __FILE__, __FUNCTION__, __LINE__ );
584
		}
585
		return false;
586
	}
587
588
589
590
	/**
591
	 *	@recursive function for exporting table data and merging the results with the next results
592
	 *	@access private
593
	 *	@param array $models_to_export keys are model names (eg 'Event', 'Attendee', etc.) and values are arrays of query params like on EEM_Base::get_all
594
	 *	@return array on success, FALSE on fail
595
	 */
596
	private function _get_export_data_for_models( $models_to_export = array() ) {
597
		$table_data = FALSE;
598
		if ( is_array( $models_to_export ) ) {
599
			foreach ( $models_to_export as $model_name => $query_params ) {
600
				//check for a numerically-indexed array. in that case, $model_name is the value!!
601
				if(is_int($model_name)){
602
					$model_name = $query_params;
603
					$query_params = array();
604
				}
605
				$model = EE_Registry::instance()->load_model($model_name);
606
				$model_objects = $model->get_all($query_params);
607
608
				$table_data[$model_name] = array();
609
				foreach($model_objects as $model_object){
610
					$model_data_array = array();
611
					$fields = $model->field_settings();
612
					foreach($fields as $field){
613
						$column_name = $field->get_nicename()."[".$field->get_name()."]";
614
						if($field instanceof EE_Datetime_Field){
615
//							$field->set_date_format('Y-m-d');
616
//							$field->set_time_format('H:i:s');
617
							$model_data_array[$column_name] = $model_object->get_datetime($field->get_name(),'Y-m-d','H:i:s');
618
						}
619
						else{
620
							$model_data_array[$column_name] = $model_object->get($field->get_name());
621
						}
622
					}
623
					$table_data[$model_name][] = $model_data_array;
624
				}
625
626
			}
627
		}
628
		return $table_data;
629
	}
630
}
631
/* End of file EE_Export.class.php */
632
/* Location: /includes/classes/EE_Export.class.php */
633