Completed
Branch TASK-9118-extensions-page (04eaec)
by
unknown
856:31 queued 840:33
created

espresso_events_Pricing_Hooks   F

Complexity

Total Complexity 356

Size/Duplication

Total Lines 1351
Duplicated Lines 5.92 %

Coupling/Cohesion

Components 1
Dependencies 18

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 80
loc 1351
rs 0.5217
wmc 356
lcom 1
cbo 18

23 Methods

Rating   Name   Duplication   Size   Complexity  
F _update_dtts() 23 101 20
F _update_tkts() 17 251 52
B _set_hooks_properties() 0 111 6
A caf_updates() 0 9 3
A dtt_and_tickets_caf_update() 0 6 1
C _update_ticket_datetimes() 30 46 15
B _duplicate_ticket() 0 39 2
F _add_prices_to_ticket() 10 69 19
A autosave_handling() 0 19 1
F pricing_metabox() 0 112 15
A _get_datetime_row() 0 10 2
F _get_dtt_edit_row() 0 29 18
C _get_dtt_attached_tickets_row() 0 27 8
C _get_datetime_tickets_list_item() 0 20 15
F _get_ticket_row() 0 131 81
B _get_tax_rows() 0 23 5
A _get_tax_added() 0 4 2
F _get_ticket_price_row() 0 28 40
A _get_price_type_selector() 0 8 2
B _get_base_price_template() 0 15 6
F _get_price_modifier_template() 0 45 21
C _get_ticket_datetime_list_item() 0 17 12
C _get_ticket_js_structure() 0 51 10

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like espresso_events_Pricing_Hooks often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use espresso_events_Pricing_Hooks, and based on these observations, apply Extract Interface, too.

1
<?php
2
if (!defined('EVENT_ESPRESSO_VERSION') )
3
	exit('NO direct script access allowed');
4
5
/**
6
 * Event Espresso
7
 *
8
 * Event Registration and Management Plugin for Wordpress
9
 *
10
 * @package		Event Espresso
11
 * @author		Seth Shoultes
12
 * @copyright	(c)2009-2012 Event Espresso All Rights Reserved.
13
 * @license		http://eventespresso.com/support/terms-conditions/  ** see Plugin Licensing **
14
 * @link		http://www.eventespresso.com
15
 * @version		4.0
16
 *
17
 * ------------------------------------------------------------------------
18
 *
19
 * espresso_events_Pricing_Hooks
20
 * Hooks various messages logic so that it runs on indicated Events Admin Pages.
21
 * Commenting/docs common to all children classes is found in the EE_Admin_Hooks parent.
22
 *
23
 *
24
 * @package		espresso_events_Pricing_Hooks
25
 * @subpackage	caffeinated/admin/new/pricing/espresso_events_Pricing_Hooks.class.php
26
 * @author		Darren Ethier
27
 *
28
 * ------------------------------------------------------------------------
29
 */
30
class espresso_events_Pricing_Hooks extends EE_Admin_Hooks {
31
32
	/**
33
	 * This property is just used to hold the status of whether an event is currently being
34
	 * created (true) or edited (false)
35
	 * @access protected
36
	 * @var bool
37
	 */
38
	protected $_is_creating_event;
39
40
41
	/**
42
	 * Used to contain the format strings for date and time that will be used for php date and
43
	 * time.
44
	 *
45
	 * Is set in the _set_hooks_properties() method.
46
	 *
47
	 * @var array
48
	 */
49
	protected $_date_format_strings;
50
51
52
53
	protected function _set_hooks_properties() {
54
		$this->_name = 'pricing';
55
56
		//capability check
57
		if ( ! EE_Registry::instance()->CAP->current_user_can( 'ee_read_default_prices', 'advanced_ticket_datetime_metabox' ) ) {
58
			return;
59
		}
60
61
		EE_Registry::instance()->load_helper( 'DTT_Helper' );
62
63
		//if we were going to add our own metaboxes we'd use the below.
64
		$this->_metaboxes = array(
65
			0 => array(
66
				'page_route' => array('edit','create_new'),
67
				'func' => 'pricing_metabox',
68
				'label' => __('Event Tickets & Datetimes', 'event_espresso'),
69
				'priority' => 'high',
70
				'context' => 'normal'
71
				),
72
73
			);/**/
74
75
		$this->_remove_metaboxes = array(
76
			0 => array(
77
				'page_route' => array('edit', 'create_new'),
78
				'id' => 'espresso_event_editor_tickets',
79
				'context' => 'normal'
80
				)
81
			);
82
83
		/**
84
		 * Format strings for date and time.  Defaults are existing behaviour from 4.1.
85
		 * Note, that if you return null as the value for 'date', and 'time' in the array, then
86
		 * EE will automatically use the set wp_options, 'date_format', and 'time_format'.
87
		 *
88
		 * @since 4.6.7
89
		 *
90
		 * @var array  Expected an array returned with 'date' and 'time' keys.
91
		 */
92
		$this->_date_format_strings = apply_filters( 'FHEE__espresso_events_Pricing_Hooks___set_hooks_properties__date_format_strings', array(
93
				'date' => 'Y-m-d',
94
				'time' => 'h:i a'
95
			));
96
97
		//validate
98
		$this->_date_format_strings['date'] = isset( $this->_date_format_strings['date'] ) ? $this->_date_format_strings['date'] : null;
99
		$this->_date_format_strings['time'] = isset( $this->_date_format_strings['time'] ) ? $this->_date_format_strings['time'] : null;
100
101
		//validate format strings
102
		$format_validation = EEH_DTT_Helper::validate_format_string( $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'] );
103
		if ( is_array( $format_validation ) ) {
104
			$msg = '<p>' . sprintf( __( 'The format "%s" was likely added via a filter and is invalid for the following reasons:', 'event_espresso' ), $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'] ) . '</p><ul>';
105
			foreach ( $format_validation as $error ) {
106
				$msg .= '<li>' . $error . '</li>';
107
			}
108
			$msg .= '</ul></p><p>' . sprintf( __( '%sPlease note that your date and time formats have been reset to "Y-m-d" and "h:i a" respectively.%s', 'event_espresso' ), '<span style="color:#D54E21;">', '</span>' ) . '</p>';
109
			EE_Error::add_attention( $msg, __FILE__, __FUNCTION__, __LINE__ );
110
			$this->_date_format_strings = array(
111
				'date' => 'Y-m-d',
112
				'time' => 'h:i a'
113
				);
114
		}
115
116
117
		$this->_scripts_styles = array(
118
			'registers' => array(
119
				'ee-tickets-datetimes-css' => array(
120
					'url' => PRICING_ASSETS_URL . 'event-tickets-datetimes.css',
121
					'type' => 'css'
122
					),
123
				'ee-dtt-ticket-metabox' => array(
124
					'url' => PRICING_ASSETS_URL . 'ee-datetime-ticket-metabox.js',
125
					'depends' => array('ee-datepicker', 'ee-dialog', 'underscore')
126
					)
127
				),
128
			'deregisters' => array(
129
				'event-editor-css' => array('type' => 'css' ),
130
				'event-datetime-metabox' => array('type' => 'js')
131
				),
132
			'enqueues' => array(
133
				'ee-tickets-datetimes-css' => array( 'edit', 'create_new' ),
134
				'ee-dtt-ticket-metabox' => array( 'edit', 'create_new' )
135
				),
136
			'localize' => array(
137
				'ee-dtt-ticket-metabox' => array(
138
					'DTT_TRASH_BLOCK' => array(
139
						'main_warning' => __('The Datetime you are attempting to trash is the only datetime selected for the following ticket(s):', 'event_espresso'),
140
						'after_warning' => __('In order to trash this datetime you must first make sure the above ticket(s) are assigned to other datetimes.', 'event_espresso'),
141
						'cancel_button' => '<button class="button-secondary ee-modal-cancel">' . __('Cancel', 'event_espresso') . '</button>',
142
						'single_warning_from_tkt' => __('The Datetime you are attempting to unassign from this ticket is the only remaining datetime for this ticket. Tickets must always have at least one datetime assigned to them.', 'event_espresso'),
143
						'single_warning_from_dtt' => __('The ticket you are attempting to unassign from this datetime cannot be unassigned because the datetime is the only remaining datetime for the ticket.  Tickets must always have at least one datetime assigned to them.', 'event_espresso'),
144
						'dismiss_button' => '<button class="button-secondary ee-modal-cancel">' . __('Dismiss', 'event_espresso') . '</button>'
145
						),
146
					'DTT_ERROR_MSG' => array(
147
						'no_ticket_name' => __('General Admission', 'event_espresso'),
148
						'dismiss_button' => '<div class="save-cancel-button-container"><button class="button-secondary ee-modal-cancel">' . __('Dismiss', 'event_espresso') . '</button></div>'
149
						),
150
					'DTT_OVERSELL_WARNING' => array(
151
						'datetime_ticket' => __('You cannot add this ticket to this datetime because it has a sold amount that is greater than the amount of spots remaining for this datetime.', 'event_espresso'),
152
						'ticket_datetime' => __('You cannot add this datetime to this ticket because the ticket has a sold amount that is greater than the amount of spots remaining on the datetime.', 'event_espresso')
153
						),
154
					'DTT_CONVERTED_FORMATS' => EEH_DTT_Helper::convert_php_to_js_and_moment_date_formats( $this->_date_format_strings['date'], $this->_date_format_strings['time'] ),
155
					'DTT_START_OF_WEEK' => array( 'dayValue' => (int) get_option( 'start_of_week' ) )
156
					)
157
				)
158
			);
159
160
161
		add_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_Extend_Events_Admin_Page', array( $this, 'autosave_handling' ), 10 );
162
		add_filter('FHEE__Events_Admin_Page___insert_update_cpt_item__event_update_callbacks', array( $this, 'caf_updates' ), 10 );
163
	}
164
165
166
167
	public function caf_updates( $update_callbacks ) {
168
		foreach ( $update_callbacks as $key => $callback ) {
169
			if ( $callback[1] == '_default_tickets_update' )
170
				unset( $update_callbacks[$key] );
171
		}
172
173
		$update_callbacks[] = array( $this, 'dtt_and_tickets_caf_update' );
174
		return $update_callbacks;
175
	}
176
177
178
179
180
	/**
181
	 * Handles saving everything related to Tickets (datetimes, tickets, prices)
182
	 * @param  EE_Event $evtobj The Event object we're attaching data to
183
	 * @param  array    $data   The request data from the form
184
	 * @return bool             success or fail
185
	 */
186
	public function dtt_and_tickets_caf_update( $evtobj, $data ) {
187
		//first we need to start with datetimes cause they are the "root" items attached to events.
188
		$saved_dtts = $this->_update_dtts( $evtobj, $data );
189
		//next tackle the tickets (and prices?)
190
		$this->_update_tkts( $evtobj, $saved_dtts, $data );
191
	}
192
193
194
195
	/**
196
	 * update event_datetimes
197
	 * @param  EE_Event 	$evt_obj Event being updated
198
	 * @param  array    	$data    the request data from the form
199
	 * @return EE_Datetime[]
200
	 */
201
	protected function _update_dtts( $evt_obj, $data ) {
202
		$timezone = isset( $data['timezone_string'] ) ? $data['timezone_string'] : NULL;
203
		$saved_dtt_ids = array();
204
		$saved_dtt_objs = array();
205
206
		foreach ( $data['edit_event_datetimes'] as $row => $dtt ) {
207
			//trim all values to ensure any excess whitespace is removed.
208
			$dtt = array_map(
209
				function( $datetime_data ) {
210
					return is_array( $datetime_data ) ? $datetime_data : trim( $datetime_data );
211
				},
212
				$dtt
213
			);
214
			$dtt['DTT_EVT_end'] = isset($dtt['DTT_EVT_end']) && ! empty( $dtt['DTT_EVT_end'] ) ? $dtt['DTT_EVT_end'] : $dtt['DTT_EVT_start'];
215
			$datetime_values = array(
216
				'DTT_ID' 			=> ! empty( $dtt['DTT_ID'] ) ? $dtt['DTT_ID'] : NULL,
217
				'DTT_name' 			=> ! empty( $dtt['DTT_name'] ) ? $dtt['DTT_name'] : '',
218
				'DTT_description' 	=> ! empty( $dtt['DTT_description'] ) ? $dtt['DTT_description'] : '',
219
				'DTT_EVT_start' 	=> $dtt['DTT_EVT_start'],
220
				'DTT_EVT_end' 		=> $dtt['DTT_EVT_end'],
221
				'DTT_reg_limit' 	=> empty( $dtt['DTT_reg_limit'] ) ? EE_INF : $dtt[ 'DTT_reg_limit' ],
222
				'DTT_order' 		=> ! isset( $dtt['DTT_order'] ) ? $row : $dtt['DTT_order'],
223
			);
224
225
			//if we have an id then let's get existing object first and then set the new values.  Otherwise we instantiate a new object for save.
226
227
			if ( !empty( $dtt['DTT_ID'] ) ) {
228
				$DTM = EE_Registry::instance()->load_model('Datetime', array($timezone) )->get_one_by_ID($dtt['DTT_ID'] );
0 ignored issues
show
Bug introduced by
The method get_one_by_ID cannot be called on \EE_Registry::instance()...ime', array($timezone)) (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
229
230
				//set date and time format according to what is set in this class.
231
				$DTM->set_date_format( $this->_date_format_strings['date'] );
232
				$DTM->set_time_format( $this->_date_format_strings['time'] );
233
234
				foreach ( $datetime_values as $field => $value ) {
235
					$DTM->set( $field, $value );
236
				}
237
238
				// make sure the $dtt_id here is saved just in case after the add_relation_to() the autosave replaces it.
239
				// We need to do this so we dont' TRASH the parent DTT.(save the ID for both key and value to avoid duplications)
240
				$saved_dtt_ids[$DTM->ID()] = $DTM->ID();
241
242
			} else {
243
				$DTM = EE_Registry::instance()->load_class('Datetime', array( $datetime_values, $timezone ), FALSE, FALSE );
244
245
				//reset date and times to match the format
246
				$DTM->set_date_format( $this->_date_format_strings['date'] );
0 ignored issues
show
Bug introduced by
The method set_date_format cannot be called on $DTM (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
247
				$DTM->set_time_format( $this->_date_format_strings['time'] );
0 ignored issues
show
Bug introduced by
The method set_time_format cannot be called on $DTM (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
248
				foreach( $datetime_values as $field => $value ) {
249
					$DTM->set( $field, $value );
0 ignored issues
show
Bug introduced by
The method set cannot be called on $DTM (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
250
				}
251
			}
252
253
254
			$DTM->save();
255
			$DTM = $evt_obj->_add_relation_to( $DTM, 'Datetime' );
256
			$evt_obj->save();
257
258
			//before going any further make sure our dates are setup correctly so that the end date is always equal or greater than the start date.
259 View Code Duplication
			if( $DTM->get_raw('DTT_EVT_start') > $DTM->get_raw('DTT_EVT_end') ) {
260
				$DTM->set('DTT_EVT_end', $DTM->get('DTT_EVT_start') );
261
				EE_Registry::instance()->load_helper('DTT_Helper');
262
				$DTM = EEH_DTT_Helper::date_time_add($DTM, 'DTT_EVT_end', 'days');
263
				$DTM->save();
264
			}
265
266
			//	now we have to make sure we add the new DTT_ID to the $saved_dtt_ids array
267
			// because it is possible there was a new one created for the autosave.
268
			// (save the ID for both key and value to avoid duplications)
269
			$saved_dtt_ids[$DTM->ID()] = $DTM->ID();
270
			$saved_dtt_objs[$row] = $DTM;
271
272
			//todo if ANY of these updates fail then we want the appropriate global error message.
273
		}
274
275
		//now we need to REMOVE any dtts that got deleted.  Keep in mind that this process will only kick in for DTT's that don't have any DTT_sold on them. So its safe to permanently delete at this point.
276
		$old_datetimes = explode(',', $data['datetime_IDs'] );
277
		$old_datetimes = $old_datetimes[0] == '' ? array() : $old_datetimes;
278
279
		if ( is_array( $old_datetimes ) ) {
280
			$dtts_to_delete = array_diff( $old_datetimes, $saved_dtt_ids );
281 View Code Duplication
			foreach ( $dtts_to_delete as $id ) {
282
				$id = absint( $id );
283
				if ( empty( $id ) )
284
					continue;
285
286
				$dtt_to_remove = EE_Registry::instance()->load_model('Datetime')->get_one_by_ID($id);
0 ignored issues
show
Bug introduced by
The method get_one_by_ID cannot be called on \EE_Registry::instance()->load_model('Datetime') (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
287
288
				//remove tkt relationships.
289
				$related_tickets = $dtt_to_remove->get_many_related('Ticket');
290
				foreach ( $related_tickets as $tkt ) {
291
					$dtt_to_remove->_remove_relation_to($tkt, 'Ticket');
292
				}
293
294
				$evt_obj->_remove_relation_to( $id, 'Datetime' );
295
				$dtt_to_remove->refresh_cache_of_related_objects();
296
297
			}
298
		}
299
300
		return $saved_dtt_objs;
301
	}
302
303
304
305
306
307
308
	/**
309
	 * update tickets
310
	 * @param  EE_Event         $evtobj     Event object being updated
311
	 * @param  EE_Datetime[]    $saved_dtts an array of datetime ids being updated
312
	 * @param  array            $data       incoming request data
313
	 * @return EE_Ticket[]
314
	 */
315
	protected function _update_tkts( $evtobj, $saved_dtts, $data ) {
316
317
		$new_tkt = null;
318
		$new_default = null;
0 ignored issues
show
Unused Code introduced by
$new_default is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
319
		//stripslashes because WP filtered the $_POST ($data) array to add slashes
320
		$data = stripslashes_deep($data);
321
		$timezone = isset( $data['timezone_string'] ) ? $data['timezone_string'] : NULL;
322
		$saved_tickets = $dtts_on_existing = array();
0 ignored issues
show
Unused Code introduced by
$dtts_on_existing is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
323
		$old_tickets = isset( $data['ticket_IDs'] ) ? explode(',', $data['ticket_IDs'] ) : array();
324
325
		//load money helper
326
		EE_Registry::instance()->load_helper( 'Money' );
327
328
		foreach ( $data['edit_tickets'] as $row => $tkt ) {
329
330
			$update_prices = $create_new_TKT = FALSE;
331
332
			//figure out what dtts were added to the ticket and what dtts were removed from the ticket in the session.
333
334
			$starting_tkt_dtt_rows = explode(',',$data['starting_ticket_datetime_rows'][$row]);
335
			$tkt_dtt_rows = explode(',', $data['ticket_datetime_rows'][$row] );
336
			$dtts_added = array_diff($tkt_dtt_rows, $starting_tkt_dtt_rows);
337
			$dtts_removed = array_diff($starting_tkt_dtt_rows, $tkt_dtt_rows);
338
339
			// trim inputs to ensure any excess whitespace is removed.
340
			$tkt = array_map(
341
				function( $ticket_data ) {
342
					return is_array( $ticket_data ) ? $ticket_data : trim( $ticket_data );
343
				},
344
				$tkt
345
			);
346
347
			//note we are doing conversions to floats here instead of allowing EE_Money_Field to handle because we're doing calcs prior to using the models.
348
			//note incoming ['TKT_price'] value is already in standard notation (via js).
349
			$ticket_price = isset( $tkt['TKT_price'] ) ?  round ( (float) $tkt['TKT_price'], 3 ) : 0;
350
351
			//note incoming base price needs converted from localized value.
352
			$base_price = isset( $tkt['TKT_base_price'] ) ? EEH_Money::convert_to_float_from_localized_money( $tkt['TKT_base_price'] ) : 0;
353
			//if ticket price == 0 and $base_price != 0 then ticket price == base_price
354
			$ticket_price = $ticket_price === 0 && $base_price !== 0 ? $base_price : $ticket_price;
355
			$base_price_id = isset( $tkt['TKT_base_price_ID'] ) ? $tkt['TKT_base_price_ID'] : 0;
356
357
			$price_rows = is_array($data['edit_prices']) && isset($data['edit_prices'][$row]) ? $data['edit_prices'][$row] : array();
358
359
			$now = null;
0 ignored issues
show
Unused Code introduced by
$now is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
360
			if ( empty( $tkt['TKT_start_date'] ) ) {
361
				//lets' use now in the set timezone.
362
				$now = new DateTime( 'now', new DateTimeZone( $evtobj->get_timezone() ) );
363
				$tkt['TKT_start_date'] = $now->format( $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'] );
364
			}
365
366
			if ( empty( $tkt['TKT_end_date'] ) ) {
367
				/**
368
				 * set the TKT_end_date to the first datetime attached to the ticket.
369
				 */
370
				$first_dtt = $saved_dtts[reset( $tkt_dtt_rows )];
371
				$tkt['TKT_end_date'] = $first_dtt->start_date_and_time( $this->_date_format_strings['date'] . ' ' . $this->_date_format_string['time'] );
0 ignored issues
show
Bug introduced by
The property _date_format_string does not seem to exist. Did you mean _date_format_strings?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
372
			}
373
374
			$TKT_values = array(
375
				'TKT_ID' 			=> ! empty( $tkt['TKT_ID'] ) ? $tkt['TKT_ID'] : NULL,
376
				'TTM_ID' 			=> ! empty( $tkt['TTM_ID'] ) ? $tkt['TTM_ID'] : 0,
377
				'TKT_name' 			=> ! empty( $tkt['TKT_name'] ) ? $tkt['TKT_name'] : '',
378
				'TKT_description' 	=> ! empty( $tkt['TKT_description'] ) && $tkt['TKT_description'] != __('You can modify this description', 'event_espresso') ? $tkt['TKT_description'] : '',
379
				'TKT_start_date' 	=> $tkt['TKT_start_date'],
380
				'TKT_end_date' 		=> $tkt['TKT_end_date'],
381
				'TKT_qty' 			=> ! isset( $tkt[ 'TKT_qty' ] ) || $tkt[ 'TKT_qty' ] === '' ? EE_INF : $tkt[ 'TKT_qty' ],
382
				'TKT_uses' 			=> ! isset( $tkt[ 'TKT_uses' ] ) || $tkt[ 'TKT_uses' ] === '' ? EE_INF : $tkt['TKT_uses'],
383
				'TKT_min' 			=> empty( $tkt['TKT_min'] ) ? 0 : $tkt['TKT_min'],
384
				'TKT_max' 			=> empty( $tkt['TKT_max'] ) ? EE_INF : $tkt['TKT_max'],
385
				'TKT_row' 			=> $row,
386
				'TKT_order' 		=> isset( $tkt['TKT_order'] ) ? $tkt['TKT_order'] : 0,
387
				'TKT_taxable' 		=> ! empty( $tkt['TKT_taxable'] ) ? 1 : 0,
388
				'TKT_required' 		=> ! empty( $tkt['TKT_required'] ) ? 1 : 0,
389
				'TKT_price' 		=> $ticket_price
390
			);
391
392
393
			//if this is a default TKT, then we need to set the TKT_ID to 0 and update accordingly, which means in turn that the prices will become new prices as well.
394 View Code Duplication
			if ( isset( $tkt['TKT_is_default'] ) && $tkt['TKT_is_default'] ) {
395
				$TKT_values['TKT_ID'] = 0;
396
				$TKT_values['TKT_is_default'] = 0;
397
				$update_prices = TRUE;
398
			}
399
400
			// if we have a TKT_ID then we need to get that existing TKT_obj and update it
401
			// we actually do our saves ahead of doing any add_relations to
402
			// because its entirely possible that this ticket wasn't removed or added to any datetime in the session
403
			// but DID have it's items modified.
404
			// keep in mind that if the TKT has been sold (and we have changed pricing information),
405
			// then we won't be updating the tkt but instead a new tkt will be created and the old one archived.
406
			if ( absint( $TKT_values['TKT_ID'] ) ) {
407
				$TKT = EE_Registry::instance()->load_model( 'Ticket', array( $timezone ) )->get_one_by_ID( $tkt['TKT_ID'] );
0 ignored issues
show
Bug introduced by
The method get_one_by_ID cannot be called on \EE_Registry::instance()...ket', array($timezone)) (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
408
				if ( $TKT instanceof EE_Ticket ) {
409
410
					$TKT = $this->_update_ticket_datetimes( $TKT, $saved_dtts, $dtts_added, $dtts_removed );
411
					// are there any registrations using this ticket ?
412
					$tickets_sold = $TKT->count_related(
413
						'Registration',
414
						array( array(
415
								'STS_ID' => array( 'NOT IN', array( EEM_Registration::status_id_incomplete ) )
416
						) )
417
					);
418
					//set ticket formats
419
					$TKT->set_date_format( $this->_date_format_strings['date'] );
420
					$TKT->set_time_format( $this->_date_format_strings['time'] );
421
422
					// let's just check the total price for the existing ticket
423
					// and determine if it matches the new total price.
424
					// if they are different then we create a new ticket (if tkts sold)
425
					// if they aren't different then we go ahead and modify existing ticket.
426
					$create_new_TKT = $tickets_sold > 0 && $ticket_price != $TKT->price() && ! $TKT->deleted()
427
							? TRUE : FALSE;
428
429
					//set new values
430 View Code Duplication
					foreach ( $TKT_values as $field => $value ) {
431
						if ( $field === 'TKT_qty' ) {
432
							$TKT->set_qty( $value );
433
						} else {
434
							$TKT->set( $field, $value );
435
						}
436
					}
437
438
					//if $create_new_TKT is false then we can safely update the existing ticket.  Otherwise we have to create a new ticket.
439
					if ( $create_new_TKT ) {
440
						$new_tkt = $this->_duplicate_ticket( $TKT, $price_rows, $ticket_price, $base_price, $base_price_id );
441
					}
442
				}
443
444
			} else {
445
				// no TKT_id so a new TKT
446
				$TKT = EE_Ticket::new_instance(
447
					$TKT_values,
448
					$timezone,
449
					array( $this->_date_format_strings[ 'date' ], $this->_date_format_strings[ 'time' ]  )
450
				);
451
				if ( $TKT instanceof EE_Ticket ) {
452
					// make sure ticket has an ID of setting relations won't work
453
					$TKT->save();
454
					$TKT = $this->_update_ticket_datetimes( $TKT, $saved_dtts, $dtts_added, $dtts_removed );
455
					$update_prices = TRUE;
456
				}
457
			}
458
			//make sure any current values have been saved.
459
			//$TKT->save();
460
461
			//before going any further make sure our dates are setup correctly so that the end date is always equal or greater than the start date.
462 View Code Duplication
			if( $TKT->get_raw('TKT_start_date') > $TKT->get_raw('TKT_end_date') ) {
463
				$TKT->set('TKT_end_date', $TKT->get('TKT_start_date') );
464
				EE_Registry::instance()->load_helper('DTT_Helper');
465
				$TKT = EEH_DTT_Helper::date_time_add($TKT, 'TKT_end_date', 'days');
466
			}
467
468
			//let's make sure the base price is handled
469
			$TKT = ! $create_new_TKT ? $this->_add_prices_to_ticket( array(), $TKT, $update_prices, $base_price, $base_price_id ) : $TKT;
470
471
			//add/update price_modifiers
472
			$TKT = ! $create_new_TKT ? $this->_add_prices_to_ticket( $price_rows, $TKT, $update_prices ) : $TKT;
473
474
			//need to make sue that the TKT_price is accurate after saving the prices.
475
			$TKT->ensure_TKT_Price_correct();
476
477
			//handle CREATING a default tkt from the incoming tkt but ONLY if this isn't an autosave.
478
			if ( ! defined('DOING_AUTOSAVE' ) ) {
479
				if ( !empty($tkt['TKT_is_default_selector'] ) ) {
480
					$update_prices = TRUE;
481
					$new_default = clone $TKT;
482
					$new_default->set( 'TKT_ID', 0 );
483
					$new_default->set( 'TKT_is_default', 1 );
484
					$new_default->set( 'TKT_row', 1 );
485
					$new_default->set( 'TKT_price', $ticket_price );
486
					//remove any dtt relations cause we DON'T want dtt relations attached (note this is just removing the cached relations in the object)
487
					$new_default->_remove_relations('Datetime');
488
					//todo we need to add the current attached prices as new prices to the new default ticket.
489
					$new_default = $this->_add_prices_to_ticket( $price_rows, $new_default, $update_prices );
490
					//don't forget the base price!
491
					$new_default = $this->_add_prices_to_ticket( array(), $new_default, $update_prices, $base_price, $base_price_id );
492
					$new_default->save();
493
					do_action( 'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_default_ticket', $new_default, $row, $TKT, $data );
494
				}
495
			}
496
497
498
			//DO ALL dtt relationships for both current tickets and any archived tickets for the given dtt that are related to the current ticket. TODO... not sure exactly how we're going to do this considering we don't know what current ticket the archived tickets are related to (and TKT_parent is used for autosaves so that's not a field we can reliably use).
499
500
501
			//let's assign any tickets that have been setup to the saved_tickets tracker
502
			//save existing TKT
503
			$TKT->save();
504
			if ( $create_new_TKT && $new_tkt instanceof EE_Ticket ) {
505
				//save new TKT
506
				$new_tkt->save();
507
				//add new ticket to array
508
				$saved_tickets[ $new_tkt->ID() ] = $new_tkt;
509
510
				do_action( 'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_ticket', $new_tkt, $row, $tkt, $data );
511
512
			} else {
513
				//add tkt to saved tkts
514
				$saved_tickets[ $TKT->ID() ] = $TKT;
515
516
				do_action( 'AHEE__espresso_events_Pricing_Hooks___update_tkts_update_ticket', $TKT, $row, $tkt, $data );
517
			}
518
519
		}
520
521
		// now we need to handle tickets actually "deleted permanently".
522
		// There are cases where we'd want this to happen
523
		// (i.e. autosaves are happening and then in between autosaves the user trashes a ticket).
524
		// Or a draft event was saved and in the process of editing a ticket is trashed.
525
		// No sense in keeping all the related data in the db!
526
		$old_tickets = isset( $old_tickets[0] ) && $old_tickets[0] == '' ? array() : $old_tickets;
527
		$tickets_removed = array_diff( $old_tickets, array_keys($saved_tickets) );
528
529
		foreach ( $tickets_removed as $id ) {
530
			$id = absint( $id );
531
532
			//get the ticket for this id
533
			$tkt_to_remove = EE_Registry::instance()->load_model('Ticket')->get_one_by_ID($id);
0 ignored issues
show
Bug introduced by
The method get_one_by_ID cannot be called on \EE_Registry::instance()->load_model('Ticket') (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
534
535
			//if this tkt is a default tkt we leave it alone cause it won't be attached to the datetime
536
			if ( $tkt_to_remove->get('TKT_is_default') )
537
				continue;
538
539
			// if this tkt has any registrations attached so then we just ARCHIVE
540
			// because we don't actually permanently delete these tickets.
541
			if ( $tkt_to_remove->count_related('Registration') > 0 ) {
542
				$tkt_to_remove->delete();
543
				continue;
544
			}
545
546
			// need to get all the related datetimes on this ticket and remove from every single one of them
547
			// (remember this process can ONLY kick off if there are NO tkts_sold)
548
			$dtts = $tkt_to_remove->get_many_related('Datetime');
549
550
			foreach( $dtts as $dtt ) {
551
				$tkt_to_remove->_remove_relation_to($dtt, 'Datetime');
552
			}
553
554
			// need to do the same for prices (except these prices can also be deleted because again,
555
			// tickets can only be trashed if they don't have any TKTs sold (otherwise they are just archived))
556
			$tkt_to_remove->delete_related_permanently('Price');
557
558
			do_action( 'AHEE__espresso_events_Pricing_Hooks___update_tkts_delete_ticket', $tkt_to_remove );
559
560
			// finally let's delete this ticket
561
			// (which should not be blocked at this point b/c we've removed all our relationships)
562
			$tkt_to_remove->delete_permanently();
563
		}
564
		return $saved_tickets;
565
	}
566
567
568
569
	/**
570
	 *
571
	 * @access  protected
572
	 * @param \EE_Ticket $ticket
573
	 * @param \EE_Datetime[] $saved_datetimes
574
	 * @param \EE_Datetime[] $added_datetimes
575
	 * @param \EE_Datetime[] $removed_datetimes
576
	 * @return \EE_Ticket
577
	 * @throws \EE_Error
578
	 */
579
	protected function  _update_ticket_datetimes(
580
		EE_Ticket $ticket,
581
		$saved_datetimes = array(),
582
		$added_datetimes = array(),
583
		$removed_datetimes = array()
584
	) {
585
586
		// to start we have to add the ticket to all the datetimes its supposed to be with,
587
		// and removing the ticket from datetimes it got removed from.
588
589
		// first let's add datetimes
590 View Code Duplication
		if ( ! empty( $added_datetimes ) && is_array( $added_datetimes ) ) {
591
			foreach ( $added_datetimes as $row_id ) {
592
				$row_id = (int)$row_id;
593
				if ( isset( $saved_datetimes[ $row_id ] ) && $saved_datetimes[ $row_id ] instanceof EE_Datetime ) {
594
					$ticket->_add_relation_to( $saved_datetimes[ $row_id ], 'Datetime' );
595
					// Is this an existing ticket (has an ID) and does it have any sold?
596
					// If so, then we need to add that to the DTT sold because this DTT is getting added.
597
					if ( $ticket->ID() && $ticket->sold() > 0 ) {
598
						$saved_datetimes[ $row_id ]->increase_sold( $ticket->sold() );
599
						$saved_datetimes[ $row_id ]->save();
600
					}
601
				}
602
			}
603
		}
604
		// then remove datetimes
605 View Code Duplication
		if ( ! empty( $removed_datetimes ) && is_array( $removed_datetimes ) ) {
606
			foreach ( $removed_datetimes as $row_id ) {
607
				$row_id = (int)$row_id;
608
				// its entirely possible that a datetime got deleted (instead of just removed from relationship.
609
				// So make sure we skip over this if the dtt isn't in the $saved_datetimes array)
610
				if ( isset( $saved_datetimes[ $row_id ] ) && $saved_datetimes[ $row_id ] instanceof EE_Datetime ) {
611
					$ticket->_remove_relation_to( $saved_datetimes[ $row_id ], 'Datetime' );
612
					// Is this an existing ticket (has an ID) and does it have any sold?
613
					// If so, then we need to remove it's sold from the DTT_sold.
614
					if ( $ticket->ID() && $ticket->sold() > 0 ) {
615
						$saved_datetimes[ $row_id ]->decrease_sold( $ticket->sold() );
616
						$saved_datetimes[ $row_id ]->save();
617
					}
618
				}
619
			}
620
		}
621
		// cap ticket qty by datetime reg limits
622
		$ticket->set_qty( min( $ticket->qty(), $ticket->qty( 'reg_limit' ) ) );
623
		return $ticket;
624
	}
625
626
627
628
	/**
629
	 *
630
	 * @access  protected
631
	 * @param \EE_Ticket $ticket
632
	 * @param array $price_rows
633
	 * @param int $ticket_price
634
	 * @param int $base_price
635
	 * @param int $base_price_id
636
	 * @return \EE_Ticket
637
	 * @throws \EE_Error
638
	 */
639
	protected function  _duplicate_ticket(
640
		EE_Ticket $ticket,
641
		$price_rows = array(),
642
		$ticket_price = 0,
643
		$base_price = 0 ,
644
		$base_price_id = 0
645
	) {
646
647
		// create new ticket that's a copy of the existing
648
		// except a new id of course (and not archived)
649
		// AND has the new TKT_price associated with it.
650
		$new_ticket = clone( $ticket );
651
		$new_ticket->set( 'TKT_ID', 0 );
652
		$new_ticket->set( 'TKT_deleted', 0 );
653
		$new_ticket->set( 'TKT_price', $ticket_price );
654
		$new_ticket->set( 'TKT_sold', 0 );
655
		// let's get a new ID for this ticket
656
		$new_ticket->save();
657
		// we also need to make sure this new ticket gets the same datetime attachments as the archived ticket
658
		$datetimes_on_existing = $ticket->get_many_related( 'Datetime' );
659
		$new_ticket = $this->_update_ticket_datetimes(
660
			$new_ticket,
661
			$datetimes_on_existing,
0 ignored issues
show
Documentation introduced by
$datetimes_on_existing is of type array<integer,object<EE_Base_Class>>, but the function expects a array<integer,object<EE_Datetime>>.

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...
662
			array_keys( $datetimes_on_existing )
0 ignored issues
show
Documentation introduced by
array_keys($datetimes_on_existing) is of type array<integer,integer>, but the function expects a array<integer,object<EE_Datetime>>.

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...
663
		);
664
665
		// $ticket will get archived later b/c we are NOT adding it to the saved_tickets array.
666
		// if existing $ticket has sold amount, then we need to adjust the qty for the new TKT to = the remaining
667
		// available.
668
		if ( $ticket->sold() > 0 ) {
669
			$new_qty = $ticket->qty() - $ticket->sold();
670
			$new_ticket->set_qty( $new_qty );
671
		}
672
		//now we update the prices just for this ticket
673
		$new_ticket = $this->_add_prices_to_ticket( $price_rows, $new_ticket, true );
674
		//and we update the base price
675
		$new_ticket = $this->_add_prices_to_ticket( array(), $new_ticket, true, $base_price, $base_price_id );
676
		return $new_ticket;
677
	}
678
679
680
681
682
683
	/**
684
	 * This attaches a list of given prices to a ticket.
685
	 * Note we dont' have to worry about ever removing relationships (or archiving prices) because if there is a change in price information on a ticket, a new ticket is created anyways so the archived ticket will retain the old price info and prices are automatically "archived" via the ticket.
686
	 *
687
	 * @access  private
688
	 * @param array  	$prices  	Array of prices from the form.
689
	 * @param EE_Ticket $ticket  	EE_Ticket object that prices are being attached to.
690
	 * @param bool 		$new_prices Whether attach existing incoming prices or create new ones.
691
	 * @param int|bool 		$base_price if FALSE then NOT doing a base price add.
692
	 * @param int|bool 		$base_price_id  if present then this is the base_price_id being updated.
693
	 * @return EE_Ticket
694
	 */
695
	protected function  _add_prices_to_ticket( $prices = array(), EE_Ticket $ticket, $new_prices = FALSE, $base_price = FALSE, $base_price_id = FALSE ) {
696
697
		//let's just get any current prices that may exist on the given ticket so we can remove any prices that got trashed in this session.
698
		$current_prices_on_ticket = $base_price !== FALSE ? $ticket->base_price(TRUE) : $ticket->price_modifiers();
699
700
		$updated_prices = array();
701
702
		// if $base_price ! FALSE then updating a base price.
703
		if ( $base_price !== FALSE ) {
704
			$prices[1] = array(
705
				'PRC_ID' => $new_prices || $base_price_id === 1 ? NULL : $base_price_id,
706
				'PRT_ID' => 1,
707
				'PRC_amount' => $base_price,
708
				'PRC_name' => $ticket->get('TKT_name'),
709
				'PRC_desc' => $ticket->get('TKT_description')
710
				);
711
		}
712
713
		//possibly need to save tkt
714
		if ( ! $ticket->ID() )
715
			$ticket->save();
716
717
		foreach ( $prices as $row => $prc ) {
718
			$prt_id = !empty( $prc['PRT_ID'] ) ? $prc['PRT_ID'] : NULL;
719
			if ( empty($prt_id) )
720
				continue; //prices MUST have a price type id.
721
			$PRC_values = array(
722
				'PRC_ID' => !empty( $prc['PRC_ID'] ) ? $prc['PRC_ID'] : NULL,
723
				'PRT_ID' => $prt_id,
724
				'PRC_amount' => !empty( $prc['PRC_amount'] ) ? $prc['PRC_amount'] : 0,
725
				'PRC_name' => !empty( $prc['PRC_name'] ) ? $prc['PRC_name'] : '',
726
				'PRC_desc' => !empty( $prc['PRC_desc'] ) ? $prc['PRC_desc'] : '',
727
				'PRC_is_default' => false, //make sure we set PRC_is_default to false for all ticket saves from event_editor
728
				'PRC_order' => $row
729
				);
730 View Code Duplication
			if ( $new_prices || empty( $PRC_values['PRC_ID'] ) ) {
731
				$PRC_values['PRC_ID'] = 0;
732
				$PRC = EE_Registry::instance()->load_class('Price', array( $PRC_values ), FALSE, FALSE);
733
			} else {
734
				$PRC = EE_Registry::instance()->load_model( 'Price' )->get_one_by_ID( $prc['PRC_ID'] );
0 ignored issues
show
Bug introduced by
The method get_one_by_ID cannot be called on \EE_Registry::instance()->load_model('Price') (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
735
				//update this price with new values
736
				foreach ( $PRC_values as $field => $newprc ) {
737
					$PRC->set( $field, $newprc );
738
				}
739
			}
740
			$PRC->save();
741
			$prcid = $PRC->ID();
742
			$updated_prices[$prcid] = $PRC;
743
			$ticket->_add_relation_to( $PRC, 'Price' );
744
		}
745
746
		//now let's remove any prices that got removed from the ticket
747
		if ( !empty ( $current_prices_on_ticket ) ) {
748
			$current = array_keys($current_prices_on_ticket);
749
			$updated = array_keys($updated_prices);
750
			$prices_to_remove = array_diff($current, $updated);
751
			if ( !empty( $prices_to_remove ) ) {
752
				foreach ( $prices_to_remove as $prc_id ) {
753
					$p = $current_prices_on_ticket[$prc_id];
754
					$ticket->_remove_relation_to( $p, 'Price' );
755
756
					//delete permanently the price
757
					$p->delete_permanently();
758
				}
759
			}
760
		}
761
762
		return $ticket;
763
	}
764
765
766
767
	public function autosave_handling( $event_admin_obj ) {
768
		return $event_admin_obj; //doing nothing for the moment.
769
		//todo when I get to this remember that I need to set the template args on the $event_admin_obj (use the set_template_args() method)
770
771
		/**
772
		 * need to remember to handle TICKET DEFAULT saves correctly:  I've got two input fields in the dom:
773
		 *
774
		 * 1. TKT_is_default_selector (visible)
775
		 * 2. TKT_is_default (hidden)
776
		 *
777
		 * I think we'll use the TKT_is_default for recording whether the ticket displayed IS a default ticket (on new event creations). Whereas the TKT_is_default_selector is for the user to indicate they want this ticket to be saved as a default.
778
		 *
779
		 * The tricky part is, on an initial display on create or edit (or after manually updating), the TKT_is_default_selector will always be unselected and the TKT_is_default will only be true if this is a create.  However, after an autosave, users will want some sort of indicator that the TKT HAS been saved as a default.. in other words we don't want to remove the check on TKT_is_default_selector. So here's what I'm thinking.
780
		 * On Autosave:
781
		 * 1. If TKT_is_default is true: we create a new TKT, send back the new id and add id to related elements, then set the TKT_is_default to false.
782
		 * 2. If TKT_is_default_selector is true: we create/edit existing ticket (following conditions above as well).  We do NOT create a new default ticket.  The checkbox stays selected after autosave.
783
		 * 3. only on MANUAL update do we check for the selection and if selected create the new default ticket.
784
		 */
785
	}
786
787
788
789
790
	public function pricing_metabox() {
791
		$existing_datetime_ids = $existing_ticket_ids = $datetime_tickets = $ticket_datetimes = array();
792
793
		$evtobj = $this->_adminpage_obj->get_cpt_model_obj();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class EE_Admin_Page as the method get_cpt_model_obj() does only exist in the following sub-classes of EE_Admin_Page: EE_Admin_Page_CPT, Events_Admin_Page, Extend_Events_Admin_Page, Extend_Registrations_Admin_Page, Registrations_Admin_Page, Venues_Admin_Page. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
794
795
		//set is_creating_event property.
796
		$evtID = $evtobj->ID();
797
		$this->_is_creating_event = absint($evtID) != 0 ? FALSE : TRUE;
798
799
		//default main template args
800
		$main_template_args = array(
801
			'event_datetime_help_link' => EEH_Template::get_help_tab_link('event_editor_event_datetimes_help_tab', $this->_adminpage_obj->page_slug, $this->_adminpage_obj->get_req_action(), FALSE, FALSE ), //todo need to add a filter to the template for the help text in the Events_Admin_Page core file so we can add further help
802
			'existing_datetime_ids' => '',
803
			'total_dtt_rows' => 1,
804
			'add_new_dtt_help_link' => EEH_Template::get_help_tab_link('add_new_dtt_info', $this->_adminpage_obj->page_slug, $this->_adminpage_obj->get_req_action(), FALSE, FALSE ), //todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
805
			'datetime_rows' => '',
806
			'show_tickets_container' => '',//$this->_adminpage_obj->get_cpt_model_obj()->ID() > 1 ? ' style="display:none;"' : '',
807
			'ticket_rows' => '',
808
			'existing_ticket_ids' => '',
809
			'total_ticket_rows' => 1,
810
			'ticket_js_structure' => '',
811
			'ee_collapsible_status' => ' ee-collapsible-open'//$this->_adminpage_obj->get_cpt_model_obj()->ID() > 0 ? ' ee-collapsible-closed' : ' ee-collapsible-open'
812
			);
813
814
		$timezone = $evtobj instanceof EE_Event ? $evtobj->timezone_string() : NULL;
815
816
		do_action( 'AHEE_log', __FILE__, __FUNCTION__, '' );
817
818
		/**
819
		 * 1. Start with retrieving Datetimes
820
		 * 2. For each datetime get related tickets
821
		 * 3. For each ticket get related prices
822
		 */
823
824
		$DTM = EE_Registry::instance()->load_model('Datetime', array($timezone) );
825
		$times = $DTM->get_all_event_dates( $evtID );
0 ignored issues
show
Bug introduced by
The method get_all_event_dates cannot be called on $DTM (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
826
827
828
829
		$main_template_args['total_dtt_rows'] = count($times);
830
		foreach ( $times as $time ) {
831
			$dttid = $time->get('DTT_ID');
832
			$dttrow = $time->get('DTT_order');
833
			$existing_datetime_ids[] = $dttid;
834
835
			//tickets attached
836
			$related_tickets = $time->ID() > 0 ? $time->get_many_related('Ticket', array( array( 'OR' => array( 'TKT_deleted' => 1, 'TKT_deleted*' => 0 ) ), 'default_where_conditions' => 'none', 'order_by' => array('TKT_order' => 'ASC' ) ) ) : array();
837
838
			//if there are no related tickets this is likely a new event OR autodraft
839
			// event so we need to generate the default tickets because dtts
840
			// ALWAYS have at least one related ticket!!.  EXCEPT, we dont' do this if there is already more than one
841
			// datetime on the event.
842
			if ( empty ( $related_tickets ) && count( $times ) < 2 ) {
843
				$related_tickets = EE_Registry::instance()->load_model('Ticket')->get_all_default_tickets();
0 ignored issues
show
Bug introduced by
The method get_all_default_tickets cannot be called on \EE_Registry::instance()->load_model('Ticket') (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
844
			}
845
846
847
			//we can't actually setup rows in this loop yet cause we don't know all the unique tickets for this event yet (tickets are linked through all datetimes). So we're going to temporarily cache some of that information.
848
849
			//loop through and setup the ticket rows and make sure the order is set.
850
			$order = 0;
0 ignored issues
show
Unused Code introduced by
$order is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
851
			foreach ( $related_tickets as $ticket ) {
852
				$tktid = $ticket->get('TKT_ID');
853
				$tktrow = $ticket->get('TKT_row');
854
				//we only want unique tickets in our final display!!
855
				if ( !in_array( $tktid, $existing_ticket_ids ) ) {
856
					$existing_ticket_ids[] = $tktid;
857
					$all_tickets[] = $ticket;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$all_tickets was never initialized. Although not strictly required by PHP, it is generally a good practice to add $all_tickets = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
858
				}
859
860
				//temporary cache of this ticket info for this datetime for later processing of datetime rows.
861
				$datetime_tickets[$dttid][] = $tktrow;
862
863
				//temporary cache of this datetime info for this ticket for later processing of ticket rows.
864
				if ( !isset( $ticket_datetimes[$tktid] ) || ! in_array( $dttrow, $ticket_datetimes[$tktid] ) )
865
					$ticket_datetimes[$tktid][] = $dttrow;
866
			}
867
		}
868
869
		$main_template_args['total_ticket_rows'] = count( $existing_ticket_ids );
870
		$main_template_args['existing_ticket_ids'] = implode( ',', $existing_ticket_ids );
871
		$main_template_args['existing_datetime_ids'] = implode( ',', $existing_datetime_ids );
872
873
		//sort $all_tickets by order
874
		usort( $all_tickets, function( $a, $b ) {
875
			$a_order = (int) $a->get('TKT_order');
876
			$b_order = (int) $b->get('TKT_order');
877
			if ( $a_order == $b_order ) {
878
				return 0;
879
			}
880
			return ( $a_order < $b_order ) ? -1 : 1;
881
		});
882
883
		//k NOW we have all the data we need for setting up the dtt rows and ticket rows so we start our dtt loop again.
884
		$dttrow = 1;
885
		foreach ( $times as $time ) {
886
			$main_template_args['datetime_rows'] .= $this->_get_datetime_row( $dttrow, $time, $datetime_tickets, $all_tickets, FALSE, $times );
887
			$dttrow++;
888
		}
889
890
		//then loop through all tickets for the ticket rows.
891
		$tktrow = 1;
892
		foreach ( $all_tickets as $ticket ) {
893
			$main_template_args['ticket_rows'] .= $this->_get_ticket_row( $tktrow, $ticket, $ticket_datetimes, $times, FALSE, $all_tickets );
894
			$tktrow++;
895
		}
896
897
		$main_template_args['ticket_js_structure'] = $this->_get_ticket_js_structure($times, $all_tickets);
898
		$template = PRICING_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php';
899
		EEH_Template::display_template( $template, $main_template_args );
900
		return;
901
	}
902
903
904
905
	protected function _get_datetime_row( $dttrow, EE_Datetime $dtt, $datetime_tickets, $all_tickets, $default = FALSE, $all_dtts = array() ) {
906
907
		$dtt_display_template_args = array(
908
			'dtt_edit_row' => $this->_get_dtt_edit_row( $dttrow, $dtt, $default, $all_dtts ),
909
			'dtt_attached_tickets_row' => $this->_get_dtt_attached_tickets_row( $dttrow, $dtt, $datetime_tickets, $all_tickets, $default ),
910
			'dtt_row' => $default ? 'DTTNUM' : $dttrow
911
			);
912
		$template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_row_wrapper.template.php';
913
		return EEH_Template::display_template( $template, $dtt_display_template_args, TRUE);
914
	}
915
916
917
918
	/**
919
	 * This method is used to generate a dtt fields  edit row.
920
	 * The same row is used to generate a row with valid DTT objects and the default row that is used as the
921
	 * skeleton by the js.
922
	 *
923
	 * @param int     $dttrow                               The row number for the row being generated.
924
	 * @param mixed EE_Datetime|null $dtt      If not default row being generated, this must be a EE_Datetime
925
	 *                               				object.
926
	 * @param bool   $default  		         Whether a default row is being generated or not.
927
	 * @param EE_Datetime[] $all_dtts             This is the array of all datetimes used in the editor.
928
	 *
929
	 * @return string Generated edit row.
930
	 */
931
	protected function _get_dtt_edit_row( $dttrow, $dtt, $default, $all_dtts ) {
0 ignored issues
show
Unused Code introduced by
The parameter $default is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
932
933
		// if the incomign $dtt object is NOT an instance of EE_Datetime then force default to true.
934
		$default = ! $dtt instanceof EE_Datetime ? true : false;
935
936
		$template_args = array(
937
			'dtt_row' => $default ? 'DTTNUM' : $dttrow,
938
			'event_datetimes_name' => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
939
			'edit_dtt_expanded' => '',//$this->_adminpage_obj->get_cpt_model_obj()->ID() > 0 ? '' : ' ee-edit-editing',
940
			'DTT_ID' => $default ? '' : $dtt->ID(),
941
			'DTT_name' => $default ? '' : $dtt->name(),
942
			'DTT_description' => $default ? '' : $dtt->description(),
943
			'DTT_EVT_start' => $default ? '' : $dtt->start_date( $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'] ),
944
			'DTT_EVT_end' => $default ? '' : $dtt->end_date( $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'] ),
945
			'DTT_reg_limit' => $default ? '' : $dtt->get_pretty('DTT_reg_limit','input'),
946
			'DTT_order' => $default ? 'DTTNUM' : $dttrow,
947
			'dtt_sold' => $default ? '0' : $dtt->get('DTT_sold'),
948
			'clone_icon' => !empty( $dtt ) && $dtt->get('DTT_sold') > 0 ? '' : 'clone-icon ee-icon ee-icon-clone clickable',
949
			'trash_icon' => !empty( $dtt ) && $dtt->get('DTT_sold') > 0  ? 'ee-lock-icon' : 'trash-icon dashicons dashicons-post-trash clickable'
950
			);
951
952
		$template_args['show_trash'] = count( $all_dtts ) === 1 && $template_args['trash_icon'] !== 'ee-lock-icon' ? ' style="display:none"' : '';
953
954
		//allow filtering of template args at this point.
955
		$template_args = apply_filters( 'FHEE__espresso_events_Pricing_Hooks___get_dtt_edit_row__template_args', $template_args, $dttrow, $dtt, $default, $all_dtts, $this->_is_creating_event );
956
957
		$template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_edit_row.template.php';
958
		return EEH_Template::display_template( $template, $template_args, TRUE );
959
	}
960
961
962
	protected function _get_dtt_attached_tickets_row( $dttrow, $dtt, $datetime_tickets, $all_tickets, $default ) {
963
964
		$template_args = array(
965
			'dtt_row' => $default ? 'DTTNUM' : $dttrow,
966
			'event_datetimes_name' => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
967
			'DTT_description' => $default ? '' : $dtt->description(),
968
			'datetime_tickets_list' => $default ? '<li class="hidden"></li>' : '',
969
			'show_tickets_row' => ' style="display:none;"', //$default || $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0 ? ' style="display:none;"' : '',
970
			'add_new_datetime_ticket_help_link' => EEH_Template::get_help_tab_link('add_new_ticket_via_datetime', $this->_adminpage_obj->page_slug, $this->_adminpage_obj->get_req_action(), FALSE, FALSE ), //todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
971
			'DTT_ID' => $default ? '' : $dtt->ID()
972
			);
973
974
		//need to setup the list items (but only if this isnt' a default skeleton setup)
975
		if ( !$default ) {
976
			$tktrow = 1;
977
			foreach ( $all_tickets as $ticket ) {
978
				$template_args['datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item( $dttrow, $tktrow, $dtt, $ticket, $datetime_tickets, $default );
979
				$tktrow++;
980
			}
981
		}
982
983
		//filter template args at this point
984
		$template_args = apply_filters( 'FHEE__espresso_events_Pricing_Hooks___get_dtt_attached_ticket_row__template_args', $template_args, $dttrow, $dtt, $datetime_tickets, $all_tickets, $default, $this->_is_creating_event );
985
986
		$template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_attached_tickets_row.template.php';
987
		return EEH_Template::display_template( $template, $template_args, TRUE );
988
	}
989
990
991
992
	protected function _get_datetime_tickets_list_item( $dttrow, $tktrow, $dtt, $ticket, $datetime_tickets, $default ) {
993
		$tktid = !empty( $ticket ) ? $ticket->ID() : 0;
0 ignored issues
show
Unused Code introduced by
$tktid is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
994
		$dtt_tkts = $dtt instanceof EE_Datetime && isset( $datetime_tickets[$dtt->ID()] ) ? $datetime_tickets[$dtt->ID()] : array();
995
996
		$displayrow = !empty( $ticket ) ? $ticket->get('TKT_row') : 0;
997
		$template_args = array(
998
			'dtt_row' => $default ? 'DTTNUM' : $dttrow,
999
			'tkt_row' => $default && empty( $ticket ) ? 'TICKETNUM' : $tktrow,
1000
			'datetime_ticket_checked' => in_array($displayrow, $dtt_tkts) ? ' checked="checked"' : '',
1001
			'ticket_selected' => in_array($displayrow, $dtt_tkts) ? ' ticket-selected' : '',
1002
			'TKT_name' => $default && empty( $ticket ) ? 'TKTNAME' : $ticket->get('TKT_name'),
1003
			'tkt_status_class' => ( $default && empty( $ticket ) ) || $this->_is_creating_event ? ' tkt-status-' . EE_Ticket::onsale : ' tkt-status-' . $ticket->ticket_status(),
1004
			);
1005
1006
		//filter template args
1007
		$template_args = apply_filters( 'FHEE__espresso_events_Pricing_Hooks___get_datetime_tickets_list_item__template_args', $template_args, $dttrow, $tktrow, $dtt, $ticket, $datetime_tickets, $default, $this->_is_creating_event );
1008
1009
		$template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_dtt_tickets_list.template.php';
1010
		return EEH_Template::display_template( $template, $template_args, TRUE );
1011
	}
1012
1013
1014
1015
1016
	/**
1017
	 * This generates the ticket row for tickets.
1018
	 * This same method is used to generate both the actual rows and the js skeleton row (when default ==
1019
	 * true)
1020
	 *
1021
	 * @param int     $tktrow           Represents the row number being generated.
1022
	 * @param mixed null|EE_Ticket $ticket           If default then this will be null.
1023
	 * @param EE_Datetime[] $ticket_datetimes    Either an array of all datetimes on all tickets indexed by
1024
	 *                                           			   each ticket or empty for  default
1025
	 * @param EE_Datetime[] $all_dtts                   All Datetimes on the event or empty for default.
1026
	 * @param bool   $default          Whether default row being generated or not.
1027
	 * @param EE_Ticket[]  $all_tickets      This is an array of all tickets attached to the event (or empty in the
1028
	 *                                       		   case of defaults)
1029
	 *
1030
	 * @return [type] [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
1031
	 */
1032
	protected function _get_ticket_row( $tktrow, $ticket, $ticket_datetimes, $all_dtts, $default = FALSE, $all_tickets = array() ) {
0 ignored issues
show
Unused Code introduced by
The parameter $default is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1033
1034
		//if $ticket is not an instance of EE_Ticket then force default to true.
1035
		$default =  ! $ticket instanceof EE_Ticket ? true : false;
1036
1037
1038
		$prices = !empty($ticket) && !$default ? $ticket->get_many_related('Price', array('default_where_conditions' => 'none', 'order_by' => array('PRC_order' => 'ASC') ) ) : array();
1039
1040
		// check if we're dealing with a default ticket in which case we don't want any starting_ticket_datetime_row values set (otherwise there won't be any new relationships created for tickets based off of the default ticket).  This will future proof in case there is ever any behaviour change between what the primary_key defaults to.
1041
		$default_dtt = $default || ($ticket instanceof EE_Ticket && $ticket->get('TKT_is_default') ) ? TRUE : FALSE;
1042
1043
		$tkt_dtts = $ticket instanceof EE_Ticket && isset( $ticket_datetimes[$ticket->ID()] ) ? $ticket_datetimes[$ticket->ID()] : array();
1044
1045
		$ticket_subtotal = $default ? 0 : $ticket->get_ticket_subtotal();
1046
		$base_price = $default ? NULL :  $ticket->base_price();
1047
		$count_price_mods = EEM_Price::instance()->get_all_default_prices(TRUE);
1048
1049
		//breaking out complicated condition for ticket_status
1050
		if ( $default ) {
1051
			$ticket_status_class = ' tkt-status-' . EE_Ticket::onsale;
1052
		} else {
1053
			$ticket_status_class =  $ticket->is_default() ? ' tkt-status-' . EE_Ticket::onsale : ' tkt-status-' . $ticket->ticket_status();
1054
		}
1055
1056
		//breaking out complicated condition for TKT_taxable
1057
		if ( $default ) {
1058
			$TKT_taxable = '';
1059
		} else {
1060
			$TKT_taxable = $ticket->get('TKT_taxable') ? ' checked="checked"' : '';
1061
		}
1062
1063
1064
		$template_args = array(
1065
			'tkt_row' => $default ? 'TICKETNUM' : $tktrow,
1066
			'TKT_order' => $default ? 'TICKETNUM' : $tktrow, //on initial page load this will always be the correct order.
1067
			'tkt_status_class' => $ticket_status_class,
1068
			'display_edit_tkt_row' => ' style="display:none;"',
1069
			'edit_tkt_expanded' => '',
1070
			'edit_tickets_name' => $default ? 'TICKETNAMEATTR' : 'edit_tickets',
1071
			'TKT_name' => $default ? '' : $ticket->get('TKT_name'),
1072
			'TKT_start_date' => $default ? '' : $ticket->get_date('TKT_start_date', $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'] ),
1073
			'TKT_end_date' => $default ? '' : $ticket->get_date('TKT_end_date', $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time']  ),
1074
			'TKT_status' => $default ? EEH_Template::pretty_status(EE_Ticket::onsale, FALSE, 'sentence') : $ticket->is_default() ? EEH_Template::pretty_status( EE_Ticket::onsale, FALSE, 'sentence') : $ticket->ticket_status(TRUE),
1075
			'TKT_price' => $default ? '' : EEH_Template::format_currency($ticket->get_ticket_total_with_taxes(), FALSE, FALSE),
1076
			'TKT_price_code' => EE_Registry::instance()->CFG->currency->code,
1077
			'TKT_price_amount' => $default ? 0 : $ticket_subtotal,
1078
			'TKT_qty' => $default ? '' : $ticket->get_pretty('TKT_qty','symbol'),
1079
			'TKT_qty_for_input'=> $default ? '' : $ticket->get_pretty('TKT_qty','input'),
1080
			'TKT_uses' => $default ? '' : $ticket->get_pretty('TKT_uses','input'),
1081
			'TKT_min' => $default ? '' : ( $ticket->get('TKT_min') === -1 || $ticket->get('TKT_min') === 0 ? '' : $ticket->get('TKT_min') ),
1082
			'TKT_max' => $default ? '' :  $ticket->get_pretty('TKT_max','input'),
1083
			'TKT_sold' => $default ? 0 : $ticket->tickets_sold('ticket'),
1084
			'TKT_registrations' => $default ? 0 : $ticket->count_registrations( array( array( 'STS_ID' => array( '!=', EEM_Registration::status_id_incomplete ) ) ) ),
1085
			'TKT_ID' => $default ? 0 : $ticket->get('TKT_ID'),
1086
			'TKT_description' => $default ? '' : $ticket->get('TKT_description'),
1087
			'TKT_is_default' => $default ? 0 : $ticket->get('TKT_is_default'),
1088
			'TKT_required' => $default ? 0 : $ticket->required(),
1089
			'TKT_is_default_selector' => '',
1090
			'ticket_price_rows' => '',
1091
			'TKT_base_price' => $default || ! $base_price instanceof EE_Price ? '' : $base_price->get_pretty('PRC_amount', 'localized_float'),
1092
			'TKT_base_price_ID' => $default || ! $base_price instanceof EE_Price ? 0 : $base_price->ID(),
1093
			'show_price_modifier' => count($prices) > 1 || ( $default && $count_price_mods > 0 ) ? '' : ' style="display:none;"',
1094
			'show_price_mod_button' => count($prices) > 1 || ( $default && $count_price_mods > 0 ) || ( !$default && $ticket->get('TKT_deleted') ) ? ' style="display:none;"' : '',
1095
			'total_price_rows' => count($prices) > 1 ? count($prices) : 1,
1096
			'ticket_datetimes_list' => $default ? '<li class="hidden"></li>' : '',
1097
			'starting_ticket_datetime_rows' => $default || $default_dtt ? '' : implode(',', $tkt_dtts),
1098
			'ticket_datetime_rows' => $default ? '' : implode(',', $tkt_dtts),
1099
			'existing_ticket_price_ids' => $default, '', implode(',', array_keys( $prices) ),
1100
			'ticket_template_id' => $default ? 0 : $ticket->get('TTM_ID'),
1101
			'TKT_taxable' => $TKT_taxable,
1102
			'display_subtotal' => $ticket instanceof EE_Ticket && $ticket->get('TKT_taxable') ? '' : ' style="display:none"',
1103
			'price_currency_symbol' => EE_Registry::instance()->CFG->currency->sign,
1104
			'TKT_subtotal_amount_display' => EEH_Template::format_currency($ticket_subtotal, FALSE, FALSE ),
1105
			'TKT_subtotal_amount' => $ticket_subtotal,
1106
			'tax_rows' => $this->_get_tax_rows( $tktrow, $ticket ),
1107
			'disabled' => $ticket instanceof EE_Ticket && $ticket->get('TKT_deleted') ? TRUE: FALSE,
1108
			'ticket_archive_class' => $ticket instanceof EE_Ticket && $ticket->get('TKT_deleted') ? ' ticket-archived' : '',
1109
			'trash_icon' => $ticket instanceof EE_Ticket && $ticket->get('TKT_deleted') ? 'ee-lock-icon ' : 'trash-icon dashicons dashicons-post-trash clickable',
1110
			'clone_icon' => $ticket instanceof EE_Ticket && $ticket->get('TKT_deleted') ? '' : 'clone-icon ee-icon ee-icon-clone clickable'
1111
			);
1112
1113
		$template_args['trash_hidden'] = count( $all_tickets ) === 1 && $template_args['trash_icon'] != 'ee-lock-icon' ? ' style="display:none"' : '';
1114
1115
		//handle rows that should NOT be empty
1116
		if ( empty( $template_args['TKT_start_date'] ) ) {
1117
			//if empty then the start date will be now.
1118
			$template_args['TKT_start_date'] = date( $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'] , current_time('timestamp'));
1119
			$template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1120
		}
1121
1122
		if ( empty( $template_args['TKT_end_date'] ) ) {
1123
1124
			//get the earliest datetime (if present);
1125
			$earliest_dtt = $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0 ? $this->_adminpage_obj->get_cpt_model_obj()->get_first_related('Datetime', array('order_by'=> array('DTT_EVT_start' => 'ASC' ) ) ) : NULL;
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class EE_Admin_Page as the method get_cpt_model_obj() does only exist in the following sub-classes of EE_Admin_Page: EE_Admin_Page_CPT, Events_Admin_Page, Extend_Events_Admin_Page, Extend_Registrations_Admin_Page, Registrations_Admin_Page, Venues_Admin_Page. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
1126
1127
			if ( !empty( $earliest_dtt ) ) {
1128
				$template_args['TKT_end_date'] = $earliest_dtt->get_datetime('DTT_EVT_start', $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'] );
1129
			} else {
1130
				//default so let's just use what's been set for the default date-time which is 30 days from now.
1131
				$template_args['TKT_end_date'] = date( $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'] , mktime(24, 0, 0, date("m"), date("d") + 29, date("Y") )  );
1132
			}
1133
			$template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1134
		}
1135
1136
		//generate ticket_datetime items
1137
		if ( ! $default ) {
1138
			$dttrow = 1;
1139
			foreach ( $all_dtts as $dtt ) {
1140
				$template_args['ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item( $dttrow, $tktrow, $dtt, $ticket, $ticket_datetimes, $default );
1141
				$dttrow++;
1142
			}
1143
		}
1144
1145
		$prcrow = 1;
1146
		foreach ( $prices as $price ) {
1147
			if ( $price->is_base_price() ) {
1148
				$prcrow++;
1149
				continue;
1150
			}
1151
			$show_trash = ( count( $prices ) > 1 && $prcrow === 1 ) || count( $prices ) === 1  ? FALSE : TRUE;
1152
			$show_create = count( $prices ) > 1 && count( $prices ) !== $prcrow ? FALSE : TRUE;
1153
			$template_args['ticket_price_rows'] .= $this->_get_ticket_price_row( $tktrow, $prcrow, $price, $default, $ticket, $show_trash, $show_create );
1154
			$prcrow++;
1155
		}
1156
1157
		//filter $template_args
1158
		$template_args = apply_filters( 'FHEE__espresso_events_Pricing_Hooks___get_ticket_row__template_args', $template_args, $tktrow, $ticket, $ticket_datetimes, $all_dtts, $default, $all_tickets, $this->_is_creating_event );
1159
1160
		$template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_row.template.php';
1161
		return EEH_Template::display_template( $template, $template_args, TRUE );
1162
	}
1163
1164
1165
1166
1167
1168
	protected function _get_tax_rows( $tktrow, $ticket ) {
1169
		$tax_rows = '';
1170
		$template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_tax_row.template.php';
1171
		$template_args = array();
0 ignored issues
show
Unused Code introduced by
$template_args is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1172
		$taxes = empty( $ticket ) ? EE_Taxes::get_taxes_for_admin() : $ticket->get_ticket_taxes_for_admin();
1173
		foreach ( $taxes as $tax ) {
1174
			$tax_added = $this->_get_tax_added( $tax, $ticket );
1175
			$template_args = array(
1176
				'display_tax' => !empty( $ticket ) && $ticket->get('TKT_taxable') ? '' : ' style="display:none;"',
1177
				'tax_id' => $tax->ID(),
1178
				'tkt_row' => $tktrow,
1179
				'tax_label' => $tax->get('PRC_name'),
1180
				'tax_added' => $tax_added,
1181
				'tax_added_display' => EEH_Template::format_currency($tax_added, FALSE, FALSE ),
1182
				'tax_amount' => $tax->get('PRC_amount')
1183
				);
1184
			$template_args = apply_filters( 'FHEE__espresso_events_Pricing_Hooks___get_tax_rows__template_args', $template_args, $tktrow, $ticket, $this->_is_creating_event  );
1185
			$tax_rows .= EEH_Template::display_template( $template, $template_args, TRUE );
1186
		}
1187
1188
1189
		return $tax_rows;
1190
	}
1191
1192
1193
	protected function _get_tax_added( EE_Price $tax, $ticket ) {
1194
		$subtotal = empty( $ticket ) ? 0 : $ticket->get_ticket_subtotal();
1195
		return $subtotal * $tax->get('PRC_amount') / 100;
1196
	}
1197
1198
1199
1200
1201
	protected function _get_ticket_price_row( $tktrow, $prcrow, $price, $default, $ticket, $show_trash = TRUE, $show_create = TRUE ) {
1202
		$send_disabled = !empty( $ticket ) && $ticket->get('TKT_deleted') ? TRUE : FALSE;
1203
		$template_args = array(
1204
			'tkt_row' => $default && empty($ticket) ? 'TICKETNUM' : $tktrow,
1205
			'PRC_order' => $default && empty($price) ? 'PRICENUM' : $prcrow,
1206
			'edit_prices_name' => $default && empty($price) ? 'PRICENAMEATTR' : 'edit_prices',
1207
			'price_type_selector' => $default && empty( $price ) ? $this->_get_base_price_template( $tktrow, $prcrow, $price, $default ) : $this->_get_price_type_selector( $tktrow, $prcrow, $price, $default, $send_disabled ),
1208
			'PRC_ID' => $default && empty($price) ? 0 : $price->ID(),
1209
			'PRC_is_default' => $default && empty($price) ? 0 : $price->get('PRC_is_default'),
1210
			'PRC_name' => $default && empty($price) ? '' : $price->get('PRC_name'),
1211
			'price_currency_symbol' => EE_Registry::instance()->CFG->currency->sign,
1212
			'show_plus_or_minus' => $default && empty($price) ? '' : ' style="display:none;"',
1213
			'show_plus' => $default && empty( $price ) ? ' style="display:none;"' : ( $price->is_discount() || $price->is_base_price() ? ' style="display:none;"' : ''),
1214
			'show_minus' => $default && empty( $price ) ? ' style="display:none;"' : ($price->is_discount() ? '' : ' style="display:none;"'),
1215
			'show_currency_symbol' => $default && empty( $price ) ? ' style="display:none"' : ($price->is_percent() ? ' style="display:none"' : '' ),
1216
			'PRC_amount' => $default && empty( $price ) ? 0 : $price->get_pretty('PRC_amount', 'localized_float'),
1217
			'show_percentage' => $default && empty( $price ) ? ' style="display:none;"' : ( $price->is_percent() ? '' : ' style="display:none;"' ),
1218
			'show_trash_icon' => $show_trash ? '' : ' style="display:none;"',
1219
			'show_create_button' => $show_create ? '' : ' style="display:none;"',
1220
			'PRC_desc' => $default && empty( $price ) ? '' : $price->get('PRC_desc'),
1221
			'disabled' => !empty( $ticket ) && $ticket->get('TKT_deleted') ? TRUE : FALSE
1222
			);
1223
1224
	$template_args = apply_filters( 'FHEE__espresso_events_Pricing_Hooks___get_ticket_price_row__template_args', $template_args, $tktrow, $prcrow, $price, $default, $ticket, $show_trash, $show_create, $this->_is_creating_event );
1225
1226
		$template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_price_row.template.php';
1227
		return EEH_Template::display_template( $template, $template_args, TRUE );
1228
	}
1229
1230
1231
	protected function _get_price_type_selector( $tktrow, $prcrow, $price, $default, $disabled = FALSE ) {
1232
		if ( $price->is_base_price() ) {
1233
			return $this->_get_base_price_template( $tktrow, $prcrow, $price, $default );
1234
		} else {
1235
			return $this->_get_price_modifier_template( $tktrow, $prcrow, $price, $default, $disabled );
1236
		}
1237
1238
	}
1239
1240
1241
	protected function _get_base_price_template( $tktrow, $prcrow, $price, $default ) {
1242
		$template_args = array(
1243
				'tkt_row' => $default ? 'TICKETNUM' : $tktrow,
1244
				'PRC_order' => $default && empty( $price ) ? 'PRICENUM' : $prcrow,
1245
				'PRT_ID' => $default && empty( $price ) ? 1 : $price->get('PRT_ID'),
1246
				'PRT_name' => __('Price', 'event_espresso'),
1247
				'price_selected_operator' => '+',
1248
				'price_selected_is_percent' => 0
1249
			);
1250
		$template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_type_base.template.php';
1251
1252
		$template_args = apply_filters( 'FHEE__espresso_events_Pricing_Hooks___get_base_price_template__template_args', $template_args, $tktrow, $prcrow, $price, $default, $this->_is_creating_event );
1253
1254
		return EEH_Template::display_template( $template, $template_args, TRUE );
1255
	}
1256
1257
1258
1259
	protected function _get_price_modifier_template( $tktrow, $prcrow, $price, $default, $disabled = FALSE ) {
1260
		$select_name = $default && empty( $price ) ? 'edit_prices[TICKETNUM][PRICENUM][PRT_ID]' : 'edit_prices[' . $tktrow . '][' . $prcrow . '][PRT_ID]';
1261
		$price_types = EE_Registry::instance()->load_model('Price_Type')->get_all(array( array('OR' => array('PBT_ID' => '2', 'PBT_ID*' => '3' ) ) ) );
0 ignored issues
show
Bug introduced by
The method get_all cannot be called on \EE_Registry::instance()...oad_model('Price_Type') (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
1262
		$price_option_span_template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_option_span.template.php';
1263
		$all_price_types = $default && empty( $price ) ? array(array('id' => 0, 'text' => __('Select Modifier', 'event_espresso')) ) : array();
1264
		$selected_price_type_id = $default && empty( $price ) ? 0 : $price->type();
1265
		$price_option_spans = '';
1266
		//setup pricetypes for selector
1267
		foreach ( $price_types as $price_type ) {
1268
			$all_price_types[] = array(
1269
				'id' => $price_type->ID(),
1270
				'text' => $price_type->get('PRT_name'),
1271
				);
1272
1273
			//while we're in the loop let's setup the option spans used by js
1274
			$spanargs = array(
1275
				'PRT_ID' => $price_type->ID(),
1276
				'PRT_operator' => $price_type->is_discount() ? '-' : '+',
1277
				'PRT_is_percent' => $price_type->get('PRT_is_percent') ? 1 : 0
1278
				);
1279
			$price_option_spans .= EEH_Template::display_template($price_option_span_template, $spanargs, TRUE );
1280
		}
1281
1282
		$select_params = $disabled ? 'style="width:auto;" disabled'  : 'style="width:auto;"';
1283
		$main_name = $select_name;
1284
		$select_name = $disabled ? 'archive_price[' . $tktrow . '][' . $prcrow . '][PRT_ID]' : $main_name;
1285
1286
		$template_args = array(
1287
			'tkt_row' => $default ? 'TICKETNUM' : $tktrow,
1288
			'PRC_order' => $default && empty( $price ) ? 'PRICENUM' : $prcrow,
1289
			'price_modifier_selector' => EEH_Form_Fields::select_input( $select_name, $all_price_types, $selected_price_type_id, $select_params, 'edit-price-PRT_ID' ),
1290
			'main_name' => $main_name,
1291
			'selected_price_type_id' => $selected_price_type_id,
1292
			'price_option_spans' => $price_option_spans,
1293
			'price_selected_operator' => $default && empty( $price ) ? '' : ( $price->is_discount() ? '-' : '+' ),
1294
			'price_selected_is_percent' => $default && empty( $price ) ? '' : ( $price->is_percent() ? 1 : 0 ),
1295
			'disabled' => $disabled
1296
			);
1297
1298
		$template_args = apply_filters( 'FHEE__espresso_events_Pricing_Hooks___get_price_modifier_template__template_args', $template_args, $tktrow, $prcrow, $price, $default, $disabled, $this->_is_creating_event );
1299
1300
		$template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_modifier_selector.template.php';
1301
1302
		return EEH_Template::display_template( $template, $template_args, TRUE );
1303
	}
1304
1305
1306
1307
	protected function _get_ticket_datetime_list_item( $dttrow, $tktrow, $dtt, $ticket, $ticket_datetimes, $default ) {
1308
		$dttid = !empty($dtt) ? $dtt->ID() : 0;
0 ignored issues
show
Unused Code introduced by
$dttid is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1309
		$displayrow = !empty($dtt) ? $dtt->get('DTT_order') : 0;
1310
		$tkt_dtts = $ticket instanceof EE_Ticket && isset( $ticket_datetimes[$ticket->ID()] ) ? $ticket_datetimes[$ticket->ID()] : array();
1311
		$template_args = array(
1312
			'dtt_row' => $default && empty( $dtt ) ? 'DTTNUM' : $dttrow,
1313
			'tkt_row' => $default ? 'TICKETNUM' : $tktrow,
1314
			'ticket_datetime_selected' => in_array( $displayrow, $tkt_dtts ) ? ' ticket-selected' : '',
1315
			'ticket_datetime_checked' => in_array( $displayrow, $tkt_dtts ) ? ' checked="checked"' : '',
1316
			'DTT_name' => $default && empty( $dtt ) ? 'DTTNAME' : $dtt->get_dtt_display_name( TRUE ),
1317
			'tkt_status_class' => '',
1318
			);
1319
1320
		$template_args = apply_filters( 'FHEE__espresso_events_Pricing_Hooks___get_ticket_datetime_list_item__template_args', $template_args, $dttrow, $tktrow, $dtt, $ticket, $ticket_datetimes, $default, $this->_is_creating_event );
1321
		$template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_datetimes_list_item.template.php';
1322
		return EEH_Template::display_template( $template, $template_args, TRUE );
1323
	}
1324
1325
1326
1327
	protected function _get_ticket_js_structure($all_dtts, $all_tickets) {
1328
		$template_args = array(
1329
			'default_datetime_edit_row' => $this->_get_dtt_edit_row('DTTNUM', NULL, TRUE, $all_dtts),
1330
			'default_ticket_row' => $this->_get_ticket_row( 'TICKETNUM', NULL, array(), array(), TRUE),
1331
			'default_price_row' => $this->_get_ticket_price_row( 'TICKETNUM', 'PRICENUM', NULL, TRUE, NULL ),
1332
			'default_price_rows' => '',
1333
			'default_base_price_amount' => 0,
1334
			'default_base_price_name' => '',
1335
			'default_base_price_description' => '',
1336
			'default_price_modifier_selector_row' => $this->_get_price_modifier_template( 'TICKETNUM', 'PRICENUM', NULL, TRUE ),
1337
			'default_available_tickets_for_datetime' => $this->_get_dtt_attached_tickets_row( 'DTTNUM', NULL, array(), array(), TRUE ),
1338
			'existing_available_datetime_tickets_list' => '',
1339
			'existing_available_ticket_datetimes_list' => '',
1340
			'new_available_datetime_ticket_list_item' => $this->_get_datetime_tickets_list_item( 'DTTNUM', 'TICKETNUM', NULL, NULL, array(), TRUE ),
1341
			'new_available_ticket_datetime_list_item' => $this->_get_ticket_datetime_list_item( 'DTTNUM', 'TICKETNUM', NULL, NULL, array(), TRUE )
1342
			);
1343
1344
		$tktrow = 1;
1345
		foreach ( $all_tickets as $ticket ) {
1346
			$template_args['existing_available_datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item( 'DTTNUM', $tktrow, NULL, $ticket, array(), TRUE );
1347
			$tktrow++;
1348
		}
1349
1350
1351
		$dttrow = 1;
1352
		foreach ( $all_dtts as $dtt ) {
1353
			$template_args['existing_available_ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item( $dttrow, 'TICKETNUM', $dtt, NULL, array(), TRUE );
1354
			$dttrow++;
1355
		}
1356
1357
		$default_prices = EE_Registry::instance()->load_model('Price')->get_all_default_prices();
0 ignored issues
show
Bug introduced by
The method get_all_default_prices cannot be called on \EE_Registry::instance()->load_model('Price') (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
1358
		$prcrow = 1;
1359
		foreach ( $default_prices as $price ) {
1360
			if ( $price->is_base_price() ) {
1361
				$template_args['default_base_price_amount'] = $price->get_pretty('PRC_amount', 'localized_float');
1362
				$template_args['default_base_price_name'] = $price->get('PRC_name');
1363
				$template_args['default_base_price_description'] = $price->get('PRC_desc');
1364
				$prcrow++;
1365
				continue;
1366
			}
1367
			$show_trash = ( count( $default_prices ) > 1 && $prcrow === 1 ) || count( $default_prices ) === 1  ? FALSE : TRUE;
1368
			$show_create = count( $default_prices ) > 1 && count( $default_prices ) !== $prcrow ? FALSE : TRUE;
1369
			$template_args['default_price_rows'] .= $this->_get_ticket_price_row( 'TICKETNUM', $prcrow, $price, TRUE, NULL, $show_trash, $show_create );
1370
			$prcrow++;
1371
		}
1372
1373
		$template_args = apply_filters( 'FHEE__espresso_events_Pricing_Hooks___get_ticket_js_structure__template_args', $template_args, $all_dtts, $all_tickets, $this->_is_creating_event );
1374
1375
		$template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_js_structure.template.php';
1376
		return EEH_Template::display_template( $template, $template_args, TRUE );
1377
	}
1378
1379
1380
} //end class espresso_events_Pricing_Hooks
1381