Completed
Branch CASC/preview-page (4af2ef)
by
unknown
40:30 queued 18:13
created
core/libraries/batch/JobHandlers/PreviewEventDeletion.php 1 patch
Indentation   +122 added lines, -122 removed lines patch added patch discarded remove patch
@@ -26,130 +26,130 @@
 block discarded – undo
26 26
 class PreviewEventDeletion extends JobHandler
27 27
 {
28 28
 
29
-    // phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
30
-    /**
31
-     *
32
-     * @param JobParameters $job_parameters
33
-     * @throws BatchRequestException
34
-     * @return JobStepResponse
35
-     */
36
-    public function create_job(JobParameters $job_parameters)
37
-    {
38
-        // Set the "root" model objects we will want to delete (record their ID and model)
39
-        $event_ids = $job_parameters->request_datum('EVT_IDs', array());
40
-        // Find all the root nodes to delete (this isn't just events, because there's other data, like related tickets,
41
-        // prices, message templates, etc, whose model definition doesn't make them dependent on events. But,
42
-        // we have no UI to access them independent of events, so they may as well get deleted too.)
43
-        $model_objects_to_delete = [];
44
-        foreach ($event_ids as $event_id) {
45
-            $event = EEM_Event::instance()->get_one_by_ID($event_id);
46
-            // Also, we want to delete their related, non-global, tickets, prices and message templates
47
-            $related_non_global_tickets = EEM_Ticket::instance()->get_all_deleted_and_undeleted(
48
-                [
49
-                    [
50
-                        'TKT_is_default' => false,
51
-                        'Datetime.EVT_ID' => $event_id
52
-                    ]
53
-                ]
54
-            );
55
-            $related_non_global_prices = EEM_Price::instance()->get_all_deleted_and_undeleted(
56
-                [
57
-                    [
58
-                        'PRC_is_default' => false,
59
-                        'Ticket.Datetime.EVT_ID' => $event_id
60
-                    ]
61
-                ]
62
-            );
63
-            $model_objects_to_delete = array_merge(
64
-                $model_objects_to_delete,
65
-                [$event],
66
-                $related_non_global_tickets,
67
-                $related_non_global_prices
68
-            );
69
-        }
70
-        $roots = [];
71
-        foreach ($model_objects_to_delete as $model_object) {
72
-            $roots[] = new ModelObjNode($model_object->ID(), $model_object->get_model());
73
-        }
74
-        $job_parameters->add_extra_data('roots', $roots);
75
-        // Set an estimate of how long this will take (we're discovering as we go, so it seems impossible to give
76
-        // an accurate count.)
77
-        $estimated_work_per_model_obj = 100;
78
-        $job_parameters->set_job_size(count($roots) * $estimated_work_per_model_obj);
79
-        return new JobStepResponse(
80
-            $job_parameters,
81
-            esc_html__('Generating preview of data to be deleted...', 'event_espresso')
82
-        );
83
-    }
29
+	// phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
30
+	/**
31
+	 *
32
+	 * @param JobParameters $job_parameters
33
+	 * @throws BatchRequestException
34
+	 * @return JobStepResponse
35
+	 */
36
+	public function create_job(JobParameters $job_parameters)
37
+	{
38
+		// Set the "root" model objects we will want to delete (record their ID and model)
39
+		$event_ids = $job_parameters->request_datum('EVT_IDs', array());
40
+		// Find all the root nodes to delete (this isn't just events, because there's other data, like related tickets,
41
+		// prices, message templates, etc, whose model definition doesn't make them dependent on events. But,
42
+		// we have no UI to access them independent of events, so they may as well get deleted too.)
43
+		$model_objects_to_delete = [];
44
+		foreach ($event_ids as $event_id) {
45
+			$event = EEM_Event::instance()->get_one_by_ID($event_id);
46
+			// Also, we want to delete their related, non-global, tickets, prices and message templates
47
+			$related_non_global_tickets = EEM_Ticket::instance()->get_all_deleted_and_undeleted(
48
+				[
49
+					[
50
+						'TKT_is_default' => false,
51
+						'Datetime.EVT_ID' => $event_id
52
+					]
53
+				]
54
+			);
55
+			$related_non_global_prices = EEM_Price::instance()->get_all_deleted_and_undeleted(
56
+				[
57
+					[
58
+						'PRC_is_default' => false,
59
+						'Ticket.Datetime.EVT_ID' => $event_id
60
+					]
61
+				]
62
+			);
63
+			$model_objects_to_delete = array_merge(
64
+				$model_objects_to_delete,
65
+				[$event],
66
+				$related_non_global_tickets,
67
+				$related_non_global_prices
68
+			);
69
+		}
70
+		$roots = [];
71
+		foreach ($model_objects_to_delete as $model_object) {
72
+			$roots[] = new ModelObjNode($model_object->ID(), $model_object->get_model());
73
+		}
74
+		$job_parameters->add_extra_data('roots', $roots);
75
+		// Set an estimate of how long this will take (we're discovering as we go, so it seems impossible to give
76
+		// an accurate count.)
77
+		$estimated_work_per_model_obj = 100;
78
+		$job_parameters->set_job_size(count($roots) * $estimated_work_per_model_obj);
79
+		return new JobStepResponse(
80
+			$job_parameters,
81
+			esc_html__('Generating preview of data to be deleted...', 'event_espresso')
82
+		);
83
+	}
84 84
 
85
-    /**
86
-     * Performs another step of the job
87
-     * @param JobParameters $job_parameters
88
-     * @param int $batch_size
89
-     * @return JobStepResponse
90
-     * @throws BatchRequestException
91
-     */
92
-    public function continue_job(JobParameters $job_parameters, $batch_size = 50)
93
-    {
94
-        // Serializing and unserializing is what really makes this drag on (eg on localhost, the ajax requests took
95
-        // about 4 seconds when the batch size was 250, but 3 seconds when the batch size was 50. So like
96
-        // 50% of the request is just serializing and unserializing.) So, make the batches much bigger.
97
-        $batch_size *= 3;
98
-        $units_processed = 0;
99
-        foreach ($job_parameters->extra_datum('roots', array()) as $root_node) {
100
-            if ($units_processed >= $batch_size) {
101
-                break;
102
-            }
103
-            if (! $root_node instanceof ModelObjNode) {
104
-                throw new InvalidClassException('ModelObjNode');
105
-            }
106
-            if ($root_node->isComplete()) {
107
-                continue;
108
-            }
109
-            $units_processed += $root_node->visit($batch_size - $units_processed);
110
-        }
111
-        $job_parameters->mark_processed($units_processed);
112
-        // If the most-recently processed root node is complete, we must be all done because we're doing them
113
-        // sequentially.
114
-        if (isset($root_node) && $root_node instanceof ModelObjNode && $root_node->isComplete()) {
115
-            $job_parameters->set_status(JobParameters::status_complete);
116
-            // Show a full progress bar.
117
-            $job_parameters->set_units_processed($job_parameters->job_size());
118
-            $deletion_job_code = $job_parameters->request_datum('deletion_job_code');
119
-            add_option('ee_deletion_' . $deletion_job_code, $job_parameters->extra_datum('roots'), null, 'no');
120
-            return new JobStepResponse(
121
-                $job_parameters,
122
-                esc_html__('Finished identifying items for deletion.', 'event_espresso'),
123
-                [
124
-                    'deletion_job_code' => $deletion_job_code
125
-                ]
126
-            );
127
-        } else {
128
-            // Because the job size was a guess, it may have likely been provden wrong. We don't want to show more work
129
-            // done than we originally said there would be. So adjust the estimate.
130
-            if (($job_parameters->units_processed() / $job_parameters->job_size()) > .8) {
131
-                $job_parameters->set_job_size($job_parameters->job_size() * 2);
132
-            }
133
-            return new JobStepResponse(
134
-                $job_parameters,
135
-                sprintf(
136
-                    esc_html__('Identified %d items for deletion.', 'event_espresso'),
137
-                    $units_processed
138
-                )
139
-            );
140
-        }
141
-    }
85
+	/**
86
+	 * Performs another step of the job
87
+	 * @param JobParameters $job_parameters
88
+	 * @param int $batch_size
89
+	 * @return JobStepResponse
90
+	 * @throws BatchRequestException
91
+	 */
92
+	public function continue_job(JobParameters $job_parameters, $batch_size = 50)
93
+	{
94
+		// Serializing and unserializing is what really makes this drag on (eg on localhost, the ajax requests took
95
+		// about 4 seconds when the batch size was 250, but 3 seconds when the batch size was 50. So like
96
+		// 50% of the request is just serializing and unserializing.) So, make the batches much bigger.
97
+		$batch_size *= 3;
98
+		$units_processed = 0;
99
+		foreach ($job_parameters->extra_datum('roots', array()) as $root_node) {
100
+			if ($units_processed >= $batch_size) {
101
+				break;
102
+			}
103
+			if (! $root_node instanceof ModelObjNode) {
104
+				throw new InvalidClassException('ModelObjNode');
105
+			}
106
+			if ($root_node->isComplete()) {
107
+				continue;
108
+			}
109
+			$units_processed += $root_node->visit($batch_size - $units_processed);
110
+		}
111
+		$job_parameters->mark_processed($units_processed);
112
+		// If the most-recently processed root node is complete, we must be all done because we're doing them
113
+		// sequentially.
114
+		if (isset($root_node) && $root_node instanceof ModelObjNode && $root_node->isComplete()) {
115
+			$job_parameters->set_status(JobParameters::status_complete);
116
+			// Show a full progress bar.
117
+			$job_parameters->set_units_processed($job_parameters->job_size());
118
+			$deletion_job_code = $job_parameters->request_datum('deletion_job_code');
119
+			add_option('ee_deletion_' . $deletion_job_code, $job_parameters->extra_datum('roots'), null, 'no');
120
+			return new JobStepResponse(
121
+				$job_parameters,
122
+				esc_html__('Finished identifying items for deletion.', 'event_espresso'),
123
+				[
124
+					'deletion_job_code' => $deletion_job_code
125
+				]
126
+			);
127
+		} else {
128
+			// Because the job size was a guess, it may have likely been provden wrong. We don't want to show more work
129
+			// done than we originally said there would be. So adjust the estimate.
130
+			if (($job_parameters->units_processed() / $job_parameters->job_size()) > .8) {
131
+				$job_parameters->set_job_size($job_parameters->job_size() * 2);
132
+			}
133
+			return new JobStepResponse(
134
+				$job_parameters,
135
+				sprintf(
136
+					esc_html__('Identified %d items for deletion.', 'event_espresso'),
137
+					$units_processed
138
+				)
139
+			);
140
+		}
141
+	}
142 142
 
143
-    /**
144
-     * Performs any clean-up logic when we know the job is completed
145
-     * @param JobParameters $job_parameters
146
-     * @return JobStepResponse
147
-     * @throws BatchRequestException
148
-     */
149
-    public function cleanup_job(JobParameters $job_parameters)
150
-    {
151
-        // Nothing much to do. We can't delete the option with the built tree because we may need it in a moment for the deletion
152
-    }
143
+	/**
144
+	 * Performs any clean-up logic when we know the job is completed
145
+	 * @param JobParameters $job_parameters
146
+	 * @return JobStepResponse
147
+	 * @throws BatchRequestException
148
+	 */
149
+	public function cleanup_job(JobParameters $job_parameters)
150
+	{
151
+		// Nothing much to do. We can't delete the option with the built tree because we may need it in a moment for the deletion
152
+	}
153 153
 }
154 154
 // End of file EventDeletion.php
155 155
 // Location: EventEspressoBatchRequest\JobHandlers/EventDeletion.php
Please login to merge, or discard this patch.
core/libraries/batch/JobHandlers/ExecuteBatchDeletion.php 1 patch
Indentation   +121 added lines, -121 removed lines patch added patch discarded remove patch
@@ -27,129 +27,129 @@
 block discarded – undo
27 27
 class ExecuteBatchDeletion extends JobHandler
28 28
 {
29 29
 
30
-    // phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
31
-    /**
32
-     *
33
-     * @param JobParameters $job_parameters
34
-     * @throws BatchRequestException
35
-     * @return JobStepResponse
36
-     */
37
-    public function create_job(JobParameters $job_parameters)
38
-    {
39
-        $deletion_job_code = $job_parameters->request_datum('deletion_job_code', null);
40
-        $roots = get_option('ee_deletion_'  . $deletion_job_code, null);
41
-        if ($roots === null) {
42
-            throw new UnexpectedEntityException($roots, 'array', esc_html__('The job seems to be stale. Please press the back button in your browser twice.', 'event_espresso'));
43
-        }
44
-        $models_and_ids_to_delete = [];
45
-        foreach ($roots as $root) {
46
-            if (! $root instanceof ModelObjNode) {
47
-                throw new UnexpectedEntityException($root, 'ModelObjNode');
48
-            }
49
-            $models_and_ids_to_delete = array_replace_recursive($models_and_ids_to_delete, $root->getIds());
50
-        }
51
-        $job_parameters->set_extra_data(
52
-            [
53
-                'models_and_ids_to_delete' => $models_and_ids_to_delete
54
-            ]
55
-        );
56
-        // Find the job's actual size.
57
-        $job_size = 0;
58
-        foreach ($models_and_ids_to_delete as $model_name => $ids) {
59
-            $job_size += count($ids);
60
-        }
61
-        $job_parameters->set_job_size($job_size);
62
-        return new JobStepResponse(
63
-            $job_parameters,
64
-            esc_html__('Beginning to delete items...', 'event_espresso')
65
-        );
66
-    }
30
+	// phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
31
+	/**
32
+	 *
33
+	 * @param JobParameters $job_parameters
34
+	 * @throws BatchRequestException
35
+	 * @return JobStepResponse
36
+	 */
37
+	public function create_job(JobParameters $job_parameters)
38
+	{
39
+		$deletion_job_code = $job_parameters->request_datum('deletion_job_code', null);
40
+		$roots = get_option('ee_deletion_'  . $deletion_job_code, null);
41
+		if ($roots === null) {
42
+			throw new UnexpectedEntityException($roots, 'array', esc_html__('The job seems to be stale. Please press the back button in your browser twice.', 'event_espresso'));
43
+		}
44
+		$models_and_ids_to_delete = [];
45
+		foreach ($roots as $root) {
46
+			if (! $root instanceof ModelObjNode) {
47
+				throw new UnexpectedEntityException($root, 'ModelObjNode');
48
+			}
49
+			$models_and_ids_to_delete = array_replace_recursive($models_and_ids_to_delete, $root->getIds());
50
+		}
51
+		$job_parameters->set_extra_data(
52
+			[
53
+				'models_and_ids_to_delete' => $models_and_ids_to_delete
54
+			]
55
+		);
56
+		// Find the job's actual size.
57
+		$job_size = 0;
58
+		foreach ($models_and_ids_to_delete as $model_name => $ids) {
59
+			$job_size += count($ids);
60
+		}
61
+		$job_parameters->set_job_size($job_size);
62
+		return new JobStepResponse(
63
+			$job_parameters,
64
+			esc_html__('Beginning to delete items...', 'event_espresso')
65
+		);
66
+	}
67 67
 
68
-    /**
69
-     * Performs another step of the job
70
-     * @param JobParameters $job_parameters
71
-     * @param int $batch_size
72
-     * @return JobStepResponse
73
-     * @throws BatchRequestException
74
-     */
75
-    public function continue_job(JobParameters $job_parameters, $batch_size = 50)
76
-    {
77
-        // We already have the items IDs. So deleting is really fast. Let's speed it up.
78
-        $batch_size *= 10;
79
-        $units_processed = 0;
80
-        $models_and_ids_to_delete = $job_parameters->extra_datum('models_and_ids_to_delete', []);
81
-        // Build a new list of everything leftover after this request's of deletions.
82
-        $models_and_ids_remaining = [];
83
-        foreach ($models_and_ids_to_delete as $model_name => $ids_to_delete) {
84
-            if ($units_processed < $batch_size) {
85
-                $model = EE_Registry::instance()->load_model($model_name);
86
-                $ids_to_delete_this_query = array_slice($ids_to_delete, 0, $batch_size - $units_processed, true);
87
-                if ($model->has_primary_key_field()) {
88
-                    $where_conditions = [
89
-                        $model->primary_key_name() => [
90
-                            'IN',
91
-                            $ids_to_delete_this_query
92
-                        ]
93
-                    ];
94
-                } else {
95
-                    $where_conditions = [
96
-                        'OR' => []
97
-                    ];
98
-                    foreach ($ids_to_delete_this_query as $index_primary_key_string) {
99
-                        $keys_n_values = $model->parse_index_primary_key_string($index_primary_key_string);
100
-                        $where_conditions['OR'][ 'AND*' . $index_primary_key_string ] = $keys_n_values;
101
-                    }
102
-                }
103
-                // Deleting time!
104
-                // The model's deletion method reports every ROW deleted, and in the case of CPT models that will be
105
-                // two rows deleted for event CPT item. So don't rely on it for the count of items deleted.
106
-                $model->delete_permanently(
107
-                    [
108
-                        $where_conditions
109
-                    ],
110
-                    false
111
-                );
112
-                $units_processed += count($ids_to_delete_this_query);
113
-                $remaining_ids = array_diff_key($ids_to_delete, $ids_to_delete_this_query);
114
-                // If there's any more from this model, we'll do them next time.
115
-                if (count($remaining_ids) > 0) {
116
-                    $models_and_ids_remaining[ $model_name ] = $remaining_ids;
117
-                }
118
-            } else {
119
-                $models_and_ids_remaining[ $model_name ] = $models_and_ids_to_delete[ $model_name ];
120
-            }
121
-        }
122
-        $job_parameters->mark_processed($units_processed);
123
-        // All done deleting for this request. Is there anything to do next time?
124
-        if (empty($models_and_ids_remaining)) {
125
-            $job_parameters->set_status(JobParameters::status_complete);
126
-            return new JobStepResponse(
127
-                $job_parameters,
128
-                esc_html__('Deletion complete.', 'event_espresso')
129
-            );
130
-        }
131
-        $job_parameters->add_extra_data('models_and_ids_to_delete', $models_and_ids_remaining);
132
-        return new JobStepResponse(
133
-            $job_parameters,
134
-            sprintf(
135
-                esc_html__('Deleted %d items.', 'event_espresso'),
136
-                $units_processed
137
-            )
138
-        );
139
-    }
68
+	/**
69
+	 * Performs another step of the job
70
+	 * @param JobParameters $job_parameters
71
+	 * @param int $batch_size
72
+	 * @return JobStepResponse
73
+	 * @throws BatchRequestException
74
+	 */
75
+	public function continue_job(JobParameters $job_parameters, $batch_size = 50)
76
+	{
77
+		// We already have the items IDs. So deleting is really fast. Let's speed it up.
78
+		$batch_size *= 10;
79
+		$units_processed = 0;
80
+		$models_and_ids_to_delete = $job_parameters->extra_datum('models_and_ids_to_delete', []);
81
+		// Build a new list of everything leftover after this request's of deletions.
82
+		$models_and_ids_remaining = [];
83
+		foreach ($models_and_ids_to_delete as $model_name => $ids_to_delete) {
84
+			if ($units_processed < $batch_size) {
85
+				$model = EE_Registry::instance()->load_model($model_name);
86
+				$ids_to_delete_this_query = array_slice($ids_to_delete, 0, $batch_size - $units_processed, true);
87
+				if ($model->has_primary_key_field()) {
88
+					$where_conditions = [
89
+						$model->primary_key_name() => [
90
+							'IN',
91
+							$ids_to_delete_this_query
92
+						]
93
+					];
94
+				} else {
95
+					$where_conditions = [
96
+						'OR' => []
97
+					];
98
+					foreach ($ids_to_delete_this_query as $index_primary_key_string) {
99
+						$keys_n_values = $model->parse_index_primary_key_string($index_primary_key_string);
100
+						$where_conditions['OR'][ 'AND*' . $index_primary_key_string ] = $keys_n_values;
101
+					}
102
+				}
103
+				// Deleting time!
104
+				// The model's deletion method reports every ROW deleted, and in the case of CPT models that will be
105
+				// two rows deleted for event CPT item. So don't rely on it for the count of items deleted.
106
+				$model->delete_permanently(
107
+					[
108
+						$where_conditions
109
+					],
110
+					false
111
+				);
112
+				$units_processed += count($ids_to_delete_this_query);
113
+				$remaining_ids = array_diff_key($ids_to_delete, $ids_to_delete_this_query);
114
+				// If there's any more from this model, we'll do them next time.
115
+				if (count($remaining_ids) > 0) {
116
+					$models_and_ids_remaining[ $model_name ] = $remaining_ids;
117
+				}
118
+			} else {
119
+				$models_and_ids_remaining[ $model_name ] = $models_and_ids_to_delete[ $model_name ];
120
+			}
121
+		}
122
+		$job_parameters->mark_processed($units_processed);
123
+		// All done deleting for this request. Is there anything to do next time?
124
+		if (empty($models_and_ids_remaining)) {
125
+			$job_parameters->set_status(JobParameters::status_complete);
126
+			return new JobStepResponse(
127
+				$job_parameters,
128
+				esc_html__('Deletion complete.', 'event_espresso')
129
+			);
130
+		}
131
+		$job_parameters->add_extra_data('models_and_ids_to_delete', $models_and_ids_remaining);
132
+		return new JobStepResponse(
133
+			$job_parameters,
134
+			sprintf(
135
+				esc_html__('Deleted %d items.', 'event_espresso'),
136
+				$units_processed
137
+			)
138
+		);
139
+	}
140 140
 
141
-    /**
142
-     * Performs any clean-up logic when we know the job is completed
143
-     * @param JobParameters $job_parameters
144
-     * @return JobStepResponse
145
-     * @throws BatchRequestException
146
-     */
147
-    public function cleanup_job(JobParameters $job_parameters)
148
-    {
149
-        delete_option(
150
-            'EEBatchDeletion' . $job_parameters->request_datum('deletion_job_code')
151
-        );
152
-    }
141
+	/**
142
+	 * Performs any clean-up logic when we know the job is completed
143
+	 * @param JobParameters $job_parameters
144
+	 * @return JobStepResponse
145
+	 * @throws BatchRequestException
146
+	 */
147
+	public function cleanup_job(JobParameters $job_parameters)
148
+	{
149
+		delete_option(
150
+			'EEBatchDeletion' . $job_parameters->request_datum('deletion_job_code')
151
+		);
152
+	}
153 153
 }
154 154
 // End of file EventDeletion.php
155 155
 // Location: EventEspressoBatchRequest\JobHandlers/EventDeletion.php
Please login to merge, or discard this patch.
admin_pages/events/templates/event_preview_deletion.template.php 1 patch
Indentation   +49 added lines, -49 removed lines patch added patch discarded remove patch
@@ -1,90 +1,90 @@
 block discarded – undo
1 1
 <h2><?php esc_html_e('Please Confirm You Want to Permanently Delete the Following Data', 'event_espresso'); ?></h2>
2 2
 <h3>
3 3
     <?php
4
-    printf(
5
-        esc_html(
6
-            // translators: 1: number of events
7
-            _n('%1$d Event', '%1$d Events', count($events), 'event_espresso')
8
-        ),
9
-        count($events)
10
-    );
11
-    ?>
4
+	printf(
5
+		esc_html(
6
+			// translators: 1: number of events
7
+			_n('%1$d Event', '%1$d Events', count($events), 'event_espresso')
8
+		),
9
+		count($events)
10
+	);
11
+	?>
12 12
 </h3>
13 13
 <ul>
14 14
     <?php
15
-    foreach ($events as $event) {
16
-        ?>
15
+	foreach ($events as $event) {
16
+		?>
17 17
         <li>
18 18
             <?php echo $event->name(); ?>
19 19
         </li>
20 20
         <?php
21
-    }
22
-    ?>
21
+	}
22
+	?>
23 23
 </ul>
24 24
 <h3>
25 25
     <?php
26
-    printf(
27
-        esc_html(
28
-            // translators: 1: number of datetimes
29
-            _n('%1$d Datetime', '%1$d Datetimes', count($datetimes), 'event_espresso')
30
-        ),
31
-        count($datetimes)
32
-    );
33
-    ?>
26
+	printf(
27
+		esc_html(
28
+			// translators: 1: number of datetimes
29
+			_n('%1$d Datetime', '%1$d Datetimes', count($datetimes), 'event_espresso')
30
+		),
31
+		count($datetimes)
32
+	);
33
+	?>
34 34
 </h3>
35 35
 <ul>
36 36
     <?php
37
-    foreach ($datetimes as $datetime) {
38
-        ?>
37
+	foreach ($datetimes as $datetime) {
38
+		?>
39 39
         <li>
40 40
             <?php echo $datetime->get_dtt_display_name(true); ?>
41 41
         </li>
42 42
         <?php
43
-    }
44
-    ?>
43
+	}
44
+	?>
45 45
 </ul>
46 46
 <h3>
47 47
     <?php
48
-    printf(
49
-        esc_html(
50
-            _n('$1%d Registration', '%1$d Registrations', $reg_count, 'event_espresso')
51
-        ),
52
-        $reg_count
53
-    );
54
-    ?>
48
+	printf(
49
+		esc_html(
50
+			_n('$1%d Registration', '%1$d Registrations', $reg_count, 'event_espresso')
51
+		),
52
+		$reg_count
53
+	);
54
+	?>
55 55
 </h3>
56 56
 <?php
57 57
 if ($reg_count > count($registrations)) {
58
-    ?>
58
+	?>
59 59
     <p>
60 60
         <?php
61
-        printf(
62
-            esc_html__('Only showing first %1$d.', 'event_espresso'),
63
-            count($registrations)
64
-        );
65
-        ?>
61
+		printf(
62
+			esc_html__('Only showing first %1$d.', 'event_espresso'),
63
+			count($registrations)
64
+		);
65
+		?>
66 66
     </p>
67 67
     <?php
68 68
 }
69 69
 ?>
70 70
 <ul>
71 71
     <?php
72
-    foreach ($registrations as $registration) {
73
-        ?>
72
+	foreach ($registrations as $registration) {
73
+		?>
74 74
         <li>
75 75
             <?php
76
-            printf(
77
-                esc_html(
78
-                    _x('%1$s (%2$d of %3$d)', 'Registration name (number of count)', 'event_espresso')
79
-                ),
80
-                $registration->attendeeName(true),
81
-                $registration->count(),
82
-                $registration->group_size()
83
-            ); ?>
76
+			printf(
77
+				esc_html(
78
+					_x('%1$s (%2$d of %3$d)', 'Registration name (number of count)', 'event_espresso')
79
+				),
80
+				$registration->attendeeName(true),
81
+				$registration->count(),
82
+				$registration->group_size()
83
+			); ?>
84 84
         </li>
85 85
         <?php
86
-    }
87
-    ?>
86
+	}
87
+	?>
88 88
 </ul>
89 89
 <form action="<?php echo $form_url; ?>" method="POST">
90 90
     <?php echo $form->get_html_and_js(); ?>
Please login to merge, or discard this patch.
admin_pages/events/Events_Admin_Page.core.php 2 patches
Indentation   +2824 added lines, -2824 removed lines patch added patch discarded remove patch
@@ -18,2828 +18,2828 @@
 block discarded – undo
18 18
 class Events_Admin_Page extends EE_Admin_Page_CPT
19 19
 {
20 20
 
21
-    /**
22
-     * This will hold the event object for event_details screen.
23
-     *
24
-     * @access protected
25
-     * @var EE_Event $_event
26
-     */
27
-    protected $_event;
28
-
29
-
30
-    /**
31
-     * This will hold the category object for category_details screen.
32
-     *
33
-     * @var stdClass $_category
34
-     */
35
-    protected $_category;
36
-
37
-
38
-    /**
39
-     * This will hold the event model instance
40
-     *
41
-     * @var EEM_Event $_event_model
42
-     */
43
-    protected $_event_model;
44
-
45
-
46
-    /**
47
-     * @var EE_Event
48
-     */
49
-    protected $_cpt_model_obj = false;
50
-
51
-    /**
52
-     * Initialize page props for this admin page group.
53
-     */
54
-    protected function _init_page_props()
55
-    {
56
-        $this->page_slug = EVENTS_PG_SLUG;
57
-        $this->page_label = EVENTS_LABEL;
58
-        $this->_admin_base_url = EVENTS_ADMIN_URL;
59
-        $this->_admin_base_path = EVENTS_ADMIN;
60
-        $this->_cpt_model_names = array(
61
-            'create_new' => 'EEM_Event',
62
-            'edit'       => 'EEM_Event',
63
-        );
64
-        $this->_cpt_edit_routes = array(
65
-            'espresso_events' => 'edit',
66
-        );
67
-        add_action(
68
-            'AHEE__EE_Admin_Page_CPT__set_model_object__after_set_object',
69
-            array($this, 'verify_event_edit'),
70
-            10,
71
-            2
72
-        );
73
-    }
74
-
75
-
76
-    /**
77
-     * Sets the ajax hooks used for this admin page group.
78
-     */
79
-    protected function _ajax_hooks()
80
-    {
81
-        add_action('wp_ajax_ee_save_timezone_setting', array($this, 'save_timezonestring_setting'));
82
-    }
83
-
84
-
85
-    /**
86
-     * Sets the page properties for this admin page group.
87
-     */
88
-    protected function _define_page_props()
89
-    {
90
-        $this->_admin_page_title = EVENTS_LABEL;
91
-        $this->_labels = array(
92
-            'buttons'      => array(
93
-                'add'             => esc_html__('Add New Event', 'event_espresso'),
94
-                'edit'            => esc_html__('Edit Event', 'event_espresso'),
95
-                'delete'          => esc_html__('Delete Event', 'event_espresso'),
96
-                'add_category'    => esc_html__('Add New Category', 'event_espresso'),
97
-                'edit_category'   => esc_html__('Edit Category', 'event_espresso'),
98
-                'delete_category' => esc_html__('Delete Category', 'event_espresso'),
99
-            ),
100
-            'editor_title' => array(
101
-                'espresso_events' => esc_html__('Enter event title here', 'event_espresso'),
102
-            ),
103
-            'publishbox'   => array(
104
-                'create_new'        => esc_html__('Save New Event', 'event_espresso'),
105
-                'edit'              => esc_html__('Update Event', 'event_espresso'),
106
-                'add_category'      => esc_html__('Save New Category', 'event_espresso'),
107
-                'edit_category'     => esc_html__('Update Category', 'event_espresso'),
108
-                'template_settings' => esc_html__('Update Settings', 'event_espresso'),
109
-            ),
110
-        );
111
-    }
112
-
113
-
114
-    /**
115
-     * Sets the page routes property for this admin page group.
116
-     */
117
-    protected function _set_page_routes()
118
-    {
119
-        // load formatter helper
120
-        // load field generator helper
121
-        // is there a evt_id in the request?
122
-        $evt_id = ! empty($this->_req_data['EVT_ID']) && ! is_array($this->_req_data['EVT_ID'])
123
-            ? $this->_req_data['EVT_ID']
124
-            : 0;
125
-        $evt_id = ! empty($this->_req_data['post']) ? $this->_req_data['post'] : $evt_id;
126
-        $this->_page_routes = array(
127
-            'default'                       => array(
128
-                'func'       => '_events_overview_list_table',
129
-                'capability' => 'ee_read_events',
130
-            ),
131
-            'create_new'                    => array(
132
-                'func'       => '_create_new_cpt_item',
133
-                'capability' => 'ee_edit_events',
134
-            ),
135
-            'edit'                          => array(
136
-                'func'       => '_edit_cpt_item',
137
-                'capability' => 'ee_edit_event',
138
-                'obj_id'     => $evt_id,
139
-            ),
140
-            'copy_event'                    => array(
141
-                'func'       => '_copy_events',
142
-                'capability' => 'ee_edit_event',
143
-                'obj_id'     => $evt_id,
144
-                'noheader'   => true,
145
-            ),
146
-            'trash_event'                   => array(
147
-                'func'       => '_trash_or_restore_event',
148
-                'args'       => array('event_status' => 'trash'),
149
-                'capability' => 'ee_delete_event',
150
-                'obj_id'     => $evt_id,
151
-                'noheader'   => true,
152
-            ),
153
-            'trash_events'                  => array(
154
-                'func'       => '_trash_or_restore_events',
155
-                'args'       => array('event_status' => 'trash'),
156
-                'capability' => 'ee_delete_events',
157
-                'noheader'   => true,
158
-            ),
159
-            'restore_event'                 => array(
160
-                'func'       => '_trash_or_restore_event',
161
-                'args'       => array('event_status' => 'draft'),
162
-                'capability' => 'ee_delete_event',
163
-                'obj_id'     => $evt_id,
164
-                'noheader'   => true,
165
-            ),
166
-            'restore_events'                => array(
167
-                'func'       => '_trash_or_restore_events',
168
-                'args'       => array('event_status' => 'draft'),
169
-                'capability' => 'ee_delete_events',
170
-                'noheader'   => true,
171
-            ),
172
-            'delete_event'                  => array(
173
-                'func'       => '_delete_event',
174
-                'capability' => 'ee_delete_event',
175
-                'obj_id'     => $evt_id,
176
-                'noheader'   => true,
177
-            ),
178
-            'delete_events'                 => array(
179
-                'func'       => '_delete_events',
180
-                'capability' => 'ee_delete_events',
181
-                'noheader'   => true,
182
-            ),
183
-            'view_report'                   => array(
184
-                'func'      => '_view_report',
185
-                'capablity' => 'ee_edit_events',
186
-            ),
187
-            'default_event_settings'        => array(
188
-                'func'       => '_default_event_settings',
189
-                'capability' => 'manage_options',
190
-            ),
191
-            'update_default_event_settings' => array(
192
-                'func'       => '_update_default_event_settings',
193
-                'capability' => 'manage_options',
194
-                'noheader'   => true,
195
-            ),
196
-            'template_settings'             => array(
197
-                'func'       => '_template_settings',
198
-                'capability' => 'manage_options',
199
-            ),
200
-            // event category tab related
201
-            'add_category'                  => array(
202
-                'func'       => '_category_details',
203
-                'capability' => 'ee_edit_event_category',
204
-                'args'       => array('add'),
205
-            ),
206
-            'edit_category'                 => array(
207
-                'func'       => '_category_details',
208
-                'capability' => 'ee_edit_event_category',
209
-                'args'       => array('edit'),
210
-            ),
211
-            'delete_categories'             => array(
212
-                'func'       => '_delete_categories',
213
-                'capability' => 'ee_delete_event_category',
214
-                'noheader'   => true,
215
-            ),
216
-            'delete_category'               => array(
217
-                'func'       => '_delete_categories',
218
-                'capability' => 'ee_delete_event_category',
219
-                'noheader'   => true,
220
-            ),
221
-            'insert_category'               => array(
222
-                'func'       => '_insert_or_update_category',
223
-                'args'       => array('new_category' => true),
224
-                'capability' => 'ee_edit_event_category',
225
-                'noheader'   => true,
226
-            ),
227
-            'update_category'               => array(
228
-                'func'       => '_insert_or_update_category',
229
-                'args'       => array('new_category' => false),
230
-                'capability' => 'ee_edit_event_category',
231
-                'noheader'   => true,
232
-            ),
233
-            'category_list'                 => array(
234
-                'func'       => '_category_list_table',
235
-                'capability' => 'ee_manage_event_categories',
236
-            ),
237
-            'preview_deletion' => [
238
-                'func' => 'previewDeletion',
239
-                'capability' => 'ee_delete_events',
240
-            ],
241
-            'confirm_deletion' => [
242
-                'func' => 'confirmDeletion',
243
-                'capability' => 'ee_delete_events',
244
-                'noheader' => true,
245
-            ]
246
-        );
247
-    }
248
-
249
-
250
-    /**
251
-     * Set the _page_config property for this admin page group.
252
-     */
253
-    protected function _set_page_config()
254
-    {
255
-        $this->_page_config = array(
256
-            'default'                => array(
257
-                'nav'           => array(
258
-                    'label' => esc_html__('Overview', 'event_espresso'),
259
-                    'order' => 10,
260
-                ),
261
-                'list_table'    => 'Events_Admin_List_Table',
262
-                'help_tabs'     => array(
263
-                    'events_overview_help_tab'                       => array(
264
-                        'title'    => esc_html__('Events Overview', 'event_espresso'),
265
-                        'filename' => 'events_overview',
266
-                    ),
267
-                    'events_overview_table_column_headings_help_tab' => array(
268
-                        'title'    => esc_html__('Events Overview Table Column Headings', 'event_espresso'),
269
-                        'filename' => 'events_overview_table_column_headings',
270
-                    ),
271
-                    'events_overview_filters_help_tab'               => array(
272
-                        'title'    => esc_html__('Events Overview Filters', 'event_espresso'),
273
-                        'filename' => 'events_overview_filters',
274
-                    ),
275
-                    'events_overview_view_help_tab'                  => array(
276
-                        'title'    => esc_html__('Events Overview Views', 'event_espresso'),
277
-                        'filename' => 'events_overview_views',
278
-                    ),
279
-                    'events_overview_other_help_tab'                 => array(
280
-                        'title'    => esc_html__('Events Overview Other', 'event_espresso'),
281
-                        'filename' => 'events_overview_other',
282
-                    ),
283
-                ),
284
-                'help_tour'     => array(
285
-                    'Event_Overview_Help_Tour',
286
-                    // 'New_Features_Test_Help_Tour' for testing multiple help tour
287
-                ),
288
-                'qtips'         => array(
289
-                    'EE_Event_List_Table_Tips',
290
-                ),
291
-                'require_nonce' => false,
292
-            ),
293
-            'create_new'             => array(
294
-                'nav'           => array(
295
-                    'label'      => esc_html__('Add Event', 'event_espresso'),
296
-                    'order'      => 5,
297
-                    'persistent' => false,
298
-                ),
299
-                'metaboxes'     => array('_register_event_editor_meta_boxes'),
300
-                'help_tabs'     => array(
301
-                    'event_editor_help_tab'                            => array(
302
-                        'title'    => esc_html__('Event Editor', 'event_espresso'),
303
-                        'filename' => 'event_editor',
304
-                    ),
305
-                    'event_editor_title_richtexteditor_help_tab'       => array(
306
-                        'title'    => esc_html__('Event Title & Rich Text Editor', 'event_espresso'),
307
-                        'filename' => 'event_editor_title_richtexteditor',
308
-                    ),
309
-                    'event_editor_venue_details_help_tab'              => array(
310
-                        'title'    => esc_html__('Event Venue Details', 'event_espresso'),
311
-                        'filename' => 'event_editor_venue_details',
312
-                    ),
313
-                    'event_editor_event_datetimes_help_tab'            => array(
314
-                        'title'    => esc_html__('Event Datetimes', 'event_espresso'),
315
-                        'filename' => 'event_editor_event_datetimes',
316
-                    ),
317
-                    'event_editor_event_tickets_help_tab'              => array(
318
-                        'title'    => esc_html__('Event Tickets', 'event_espresso'),
319
-                        'filename' => 'event_editor_event_tickets',
320
-                    ),
321
-                    'event_editor_event_registration_options_help_tab' => array(
322
-                        'title'    => esc_html__('Event Registration Options', 'event_espresso'),
323
-                        'filename' => 'event_editor_event_registration_options',
324
-                    ),
325
-                    'event_editor_tags_categories_help_tab'            => array(
326
-                        'title'    => esc_html__('Event Tags & Categories', 'event_espresso'),
327
-                        'filename' => 'event_editor_tags_categories',
328
-                    ),
329
-                    'event_editor_questions_registrants_help_tab'      => array(
330
-                        'title'    => esc_html__('Questions for Registrants', 'event_espresso'),
331
-                        'filename' => 'event_editor_questions_registrants',
332
-                    ),
333
-                    'event_editor_save_new_event_help_tab'             => array(
334
-                        'title'    => esc_html__('Save New Event', 'event_espresso'),
335
-                        'filename' => 'event_editor_save_new_event',
336
-                    ),
337
-                    'event_editor_other_help_tab'                      => array(
338
-                        'title'    => esc_html__('Event Other', 'event_espresso'),
339
-                        'filename' => 'event_editor_other',
340
-                    ),
341
-                ),
342
-                'help_tour'     => array(
343
-                    'Event_Editor_Help_Tour',
344
-                ),
345
-                'qtips'         => array('EE_Event_Editor_Decaf_Tips'),
346
-                'require_nonce' => false,
347
-            ),
348
-            'edit'                   => array(
349
-                'nav'           => array(
350
-                    'label'      => esc_html__('Edit Event', 'event_espresso'),
351
-                    'order'      => 5,
352
-                    'persistent' => false,
353
-                    'url'        => isset($this->_req_data['post'])
354
-                        ? EE_Admin_Page::add_query_args_and_nonce(
355
-                            array('post' => $this->_req_data['post'], 'action' => 'edit'),
356
-                            $this->_current_page_view_url
357
-                        )
358
-                        : $this->_admin_base_url,
359
-                ),
360
-                'metaboxes'     => array('_register_event_editor_meta_boxes'),
361
-                'help_tabs'     => array(
362
-                    'event_editor_help_tab'                            => array(
363
-                        'title'    => esc_html__('Event Editor', 'event_espresso'),
364
-                        'filename' => 'event_editor',
365
-                    ),
366
-                    'event_editor_title_richtexteditor_help_tab'       => array(
367
-                        'title'    => esc_html__('Event Title & Rich Text Editor', 'event_espresso'),
368
-                        'filename' => 'event_editor_title_richtexteditor',
369
-                    ),
370
-                    'event_editor_venue_details_help_tab'              => array(
371
-                        'title'    => esc_html__('Event Venue Details', 'event_espresso'),
372
-                        'filename' => 'event_editor_venue_details',
373
-                    ),
374
-                    'event_editor_event_datetimes_help_tab'            => array(
375
-                        'title'    => esc_html__('Event Datetimes', 'event_espresso'),
376
-                        'filename' => 'event_editor_event_datetimes',
377
-                    ),
378
-                    'event_editor_event_tickets_help_tab'              => array(
379
-                        'title'    => esc_html__('Event Tickets', 'event_espresso'),
380
-                        'filename' => 'event_editor_event_tickets',
381
-                    ),
382
-                    'event_editor_event_registration_options_help_tab' => array(
383
-                        'title'    => esc_html__('Event Registration Options', 'event_espresso'),
384
-                        'filename' => 'event_editor_event_registration_options',
385
-                    ),
386
-                    'event_editor_tags_categories_help_tab'            => array(
387
-                        'title'    => esc_html__('Event Tags & Categories', 'event_espresso'),
388
-                        'filename' => 'event_editor_tags_categories',
389
-                    ),
390
-                    'event_editor_questions_registrants_help_tab'      => array(
391
-                        'title'    => esc_html__('Questions for Registrants', 'event_espresso'),
392
-                        'filename' => 'event_editor_questions_registrants',
393
-                    ),
394
-                    'event_editor_save_new_event_help_tab'             => array(
395
-                        'title'    => esc_html__('Save New Event', 'event_espresso'),
396
-                        'filename' => 'event_editor_save_new_event',
397
-                    ),
398
-                    'event_editor_other_help_tab'                      => array(
399
-                        'title'    => esc_html__('Event Other', 'event_espresso'),
400
-                        'filename' => 'event_editor_other',
401
-                    ),
402
-                ),
403
-                'qtips'         => array('EE_Event_Editor_Decaf_Tips'),
404
-                'require_nonce' => false,
405
-            ),
406
-            'default_event_settings' => array(
407
-                'nav'           => array(
408
-                    'label' => esc_html__('Default Settings', 'event_espresso'),
409
-                    'order' => 40,
410
-                ),
411
-                'metaboxes'     => array_merge($this->_default_espresso_metaboxes, array('_publish_post_box')),
412
-                'labels'        => array(
413
-                    'publishbox' => esc_html__('Update Settings', 'event_espresso'),
414
-                ),
415
-                'help_tabs'     => array(
416
-                    'default_settings_help_tab'        => array(
417
-                        'title'    => esc_html__('Default Event Settings', 'event_espresso'),
418
-                        'filename' => 'events_default_settings',
419
-                    ),
420
-                    'default_settings_status_help_tab' => array(
421
-                        'title'    => esc_html__('Default Registration Status', 'event_espresso'),
422
-                        'filename' => 'events_default_settings_status',
423
-                    ),
424
-                    'default_maximum_tickets_help_tab' => array(
425
-                        'title'    => esc_html__('Default Maximum Tickets Per Order', 'event_espresso'),
426
-                        'filename' => 'events_default_settings_max_tickets',
427
-                    ),
428
-                ),
429
-                'help_tour'     => array('Event_Default_Settings_Help_Tour'),
430
-                'require_nonce' => false,
431
-            ),
432
-            // template settings
433
-            'template_settings'      => array(
434
-                'nav'           => array(
435
-                    'label' => esc_html__('Templates', 'event_espresso'),
436
-                    'order' => 30,
437
-                ),
438
-                'metaboxes'     => $this->_default_espresso_metaboxes,
439
-                'help_tabs'     => array(
440
-                    'general_settings_templates_help_tab' => array(
441
-                        'title'    => esc_html__('Templates', 'event_espresso'),
442
-                        'filename' => 'general_settings_templates',
443
-                    ),
444
-                ),
445
-                'help_tour'     => array('Templates_Help_Tour'),
446
-                'require_nonce' => false,
447
-            ),
448
-            // event category stuff
449
-            'add_category'           => array(
450
-                'nav'           => array(
451
-                    'label'      => esc_html__('Add Category', 'event_espresso'),
452
-                    'order'      => 15,
453
-                    'persistent' => false,
454
-                ),
455
-                'help_tabs'     => array(
456
-                    'add_category_help_tab' => array(
457
-                        'title'    => esc_html__('Add New Event Category', 'event_espresso'),
458
-                        'filename' => 'events_add_category',
459
-                    ),
460
-                ),
461
-                'help_tour'     => array('Event_Add_Category_Help_Tour'),
462
-                'metaboxes'     => array('_publish_post_box'),
463
-                'require_nonce' => false,
464
-            ),
465
-            'edit_category'          => array(
466
-                'nav'           => array(
467
-                    'label'      => esc_html__('Edit Category', 'event_espresso'),
468
-                    'order'      => 15,
469
-                    'persistent' => false,
470
-                    'url'        => isset($this->_req_data['EVT_CAT_ID'])
471
-                        ? add_query_arg(
472
-                            array('EVT_CAT_ID' => $this->_req_data['EVT_CAT_ID']),
473
-                            $this->_current_page_view_url
474
-                        )
475
-                        : $this->_admin_base_url,
476
-                ),
477
-                'help_tabs'     => array(
478
-                    'edit_category_help_tab' => array(
479
-                        'title'    => esc_html__('Edit Event Category', 'event_espresso'),
480
-                        'filename' => 'events_edit_category',
481
-                    ),
482
-                ),
483
-                /*'help_tour' => array('Event_Edit_Category_Help_Tour'),*/
484
-                'metaboxes'     => array('_publish_post_box'),
485
-                'require_nonce' => false,
486
-            ),
487
-            'category_list'          => array(
488
-                'nav'           => array(
489
-                    'label' => esc_html__('Categories', 'event_espresso'),
490
-                    'order' => 20,
491
-                ),
492
-                'list_table'    => 'Event_Categories_Admin_List_Table',
493
-                'help_tabs'     => array(
494
-                    'events_categories_help_tab'                       => array(
495
-                        'title'    => esc_html__('Event Categories', 'event_espresso'),
496
-                        'filename' => 'events_categories',
497
-                    ),
498
-                    'events_categories_table_column_headings_help_tab' => array(
499
-                        'title'    => esc_html__('Event Categories Table Column Headings', 'event_espresso'),
500
-                        'filename' => 'events_categories_table_column_headings',
501
-                    ),
502
-                    'events_categories_view_help_tab'                  => array(
503
-                        'title'    => esc_html__('Event Categories Views', 'event_espresso'),
504
-                        'filename' => 'events_categories_views',
505
-                    ),
506
-                    'events_categories_other_help_tab'                 => array(
507
-                        'title'    => esc_html__('Event Categories Other', 'event_espresso'),
508
-                        'filename' => 'events_categories_other',
509
-                    ),
510
-                ),
511
-                'help_tour'     => array(
512
-                    'Event_Categories_Help_Tour',
513
-                ),
514
-                'metaboxes'     => $this->_default_espresso_metaboxes,
515
-                'require_nonce' => false,
516
-            ),
517
-            'preview_deletion'           => array(
518
-                'nav'           => array(
519
-                    'label'      => esc_html__('Preview Deletion', 'event_espresso'),
520
-                    'order'      => 15,
521
-                    'persistent' => false,
522
-                ),
523
-                'require_nonce' => false
524
-            )
525
-        );
526
-    }
527
-
528
-
529
-    /**
530
-     * Used to register any global screen options if necessary for every route in this admin page group.
531
-     */
532
-    protected function _add_screen_options()
533
-    {
534
-    }
535
-
536
-
537
-    /**
538
-     * Implementing the screen options for the 'default' route.
539
-     */
540
-    protected function _add_screen_options_default()
541
-    {
542
-        $this->_per_page_screen_option();
543
-    }
544
-
545
-
546
-    /**
547
-     * Implementing screen options for the category list route.
548
-     */
549
-    protected function _add_screen_options_category_list()
550
-    {
551
-        $page_title = $this->_admin_page_title;
552
-        $this->_admin_page_title = esc_html__('Categories', 'event_espresso');
553
-        $this->_per_page_screen_option();
554
-        $this->_admin_page_title = $page_title;
555
-    }
556
-
557
-
558
-    /**
559
-     * Used to register any global feature pointers for the admin page group.
560
-     */
561
-    protected function _add_feature_pointers()
562
-    {
563
-    }
564
-
565
-
566
-    /**
567
-     * Registers and enqueues any global scripts and styles for the entire admin page group.
568
-     */
569
-    public function load_scripts_styles()
570
-    {
571
-        wp_register_style(
572
-            'events-admin-css',
573
-            EVENTS_ASSETS_URL . 'events-admin-page.css',
574
-            array(),
575
-            EVENT_ESPRESSO_VERSION
576
-        );
577
-        wp_register_style('ee-cat-admin', EVENTS_ASSETS_URL . 'ee-cat-admin.css', array(), EVENT_ESPRESSO_VERSION);
578
-        wp_enqueue_style('events-admin-css');
579
-        wp_enqueue_style('ee-cat-admin');
580
-        // todo note: we also need to load_scripts_styles per view (i.e. default/view_report/event_details
581
-        // registers for all views
582
-        // scripts
583
-        wp_register_script(
584
-            'event_editor_js',
585
-            EVENTS_ASSETS_URL . 'event_editor.js',
586
-            array('ee_admin_js', 'jquery-ui-slider', 'jquery-ui-timepicker-addon'),
587
-            EVENT_ESPRESSO_VERSION,
588
-            true
589
-        );
590
-    }
591
-
592
-
593
-    /**
594
-     * Enqueuing scripts and styles specific to this view
595
-     */
596
-    public function load_scripts_styles_create_new()
597
-    {
598
-        $this->load_scripts_styles_edit();
599
-    }
600
-
601
-
602
-    /**
603
-     * Enqueuing scripts and styles specific to this view
604
-     */
605
-    public function load_scripts_styles_edit()
606
-    {
607
-        // styles
608
-        wp_enqueue_style('espresso-ui-theme');
609
-        wp_register_style(
610
-            'event-editor-css',
611
-            EVENTS_ASSETS_URL . 'event-editor.css',
612
-            array('ee-admin-css'),
613
-            EVENT_ESPRESSO_VERSION
614
-        );
615
-        wp_enqueue_style('event-editor-css');
616
-        // scripts
617
-        wp_register_script(
618
-            'event-datetime-metabox',
619
-            EVENTS_ASSETS_URL . 'event-datetime-metabox.js',
620
-            array('event_editor_js', 'ee-datepicker'),
621
-            EVENT_ESPRESSO_VERSION
622
-        );
623
-        wp_enqueue_script('event-datetime-metabox');
624
-    }
625
-
626
-
627
-    /**
628
-     * Populating the _views property for the category list table view.
629
-     */
630
-    protected function _set_list_table_views_category_list()
631
-    {
632
-        $this->_views = array(
633
-            'all' => array(
634
-                'slug'        => 'all',
635
-                'label'       => esc_html__('All', 'event_espresso'),
636
-                'count'       => 0,
637
-                'bulk_action' => array(
638
-                    'delete_categories' => esc_html__('Delete Permanently', 'event_espresso'),
639
-                ),
640
-            ),
641
-        );
642
-    }
643
-
644
-
645
-    /**
646
-     * For adding anything that fires on the admin_init hook for any route within this admin page group.
647
-     */
648
-    public function admin_init()
649
-    {
650
-        EE_Registry::$i18n_js_strings['image_confirm'] = esc_html__(
651
-            'Do you really want to delete this image? Please remember to update your event to complete the removal.',
652
-            'event_espresso'
653
-        );
654
-    }
655
-
656
-
657
-    /**
658
-     * For adding anything that should be triggered on the admin_notices hook for any route within this admin page
659
-     * group.
660
-     */
661
-    public function admin_notices()
662
-    {
663
-    }
664
-
665
-
666
-    /**
667
-     * For adding anything that should be triggered on the `admin_print_footer_scripts` hook for any route within
668
-     * this admin page group.
669
-     */
670
-    public function admin_footer_scripts()
671
-    {
672
-    }
673
-
674
-
675
-    /**
676
-     * Call this function to verify if an event is public and has tickets for sale.  If it does, then we need to show a
677
-     * warning (via EE_Error::add_error());
678
-     *
679
-     * @param  EE_Event $event Event object
680
-     * @param string    $req_type
681
-     * @return void
682
-     * @throws EE_Error
683
-     * @access public
684
-     */
685
-    public function verify_event_edit($event = null, $req_type = '')
686
-    {
687
-        // don't need to do this when processing
688
-        if (! empty($req_type)) {
689
-            return;
690
-        }
691
-        // no event?
692
-        if (empty($event)) {
693
-            // set event
694
-            $event = $this->_cpt_model_obj;
695
-        }
696
-        // STILL no event?
697
-        if (! $event instanceof EE_Event) {
698
-            return;
699
-        }
700
-        $orig_status = $event->status();
701
-        // first check if event is active.
702
-        if ($orig_status === EEM_Event::cancelled
703
-            || $orig_status === EEM_Event::postponed
704
-            || $event->is_expired()
705
-            || $event->is_inactive()
706
-        ) {
707
-            return;
708
-        }
709
-        // made it here so it IS active... next check that any of the tickets are sold.
710
-        if ($event->is_sold_out(true)) {
711
-            if ($orig_status !== EEM_Event::sold_out && $event->status() !== $orig_status) {
712
-                EE_Error::add_attention(
713
-                    sprintf(
714
-                        esc_html__(
715
-                            'Please note that the Event Status has automatically been changed to %s because there are no more spaces available for this event.  However, this change is not permanent until you update the event.  You can change the status back to something else before updating if you wish.',
716
-                            'event_espresso'
717
-                        ),
718
-                        EEH_Template::pretty_status(EEM_Event::sold_out, false, 'sentence')
719
-                    )
720
-                );
721
-            }
722
-            return;
723
-        } elseif ($orig_status === EEM_Event::sold_out) {
724
-            EE_Error::add_attention(
725
-                sprintf(
726
-                    esc_html__(
727
-                        'Please note that the Event Status has automatically been changed to %s because more spaces have become available for this event, most likely due to abandoned transactions freeing up reserved tickets.  However, this change is not permanent until you update the event. If you wish, you can change the status back to something else before updating.',
728
-                        'event_espresso'
729
-                    ),
730
-                    EEH_Template::pretty_status($event->status(), false, 'sentence')
731
-                )
732
-            );
733
-        }
734
-        // now we need to determine if the event has any tickets on sale.  If not then we dont' show the error
735
-        if (! $event->tickets_on_sale()) {
736
-            return;
737
-        }
738
-        // made it here so show warning
739
-        $this->_edit_event_warning();
740
-    }
741
-
742
-
743
-    /**
744
-     * This is the text used for when an event is being edited that is public and has tickets for sale.
745
-     * When needed, hook this into a EE_Error::add_error() notice.
746
-     *
747
-     * @access protected
748
-     * @return void
749
-     */
750
-    protected function _edit_event_warning()
751
-    {
752
-        // we don't want to add warnings during these requests
753
-        if (isset($this->_req_data['action']) && $this->_req_data['action'] === 'editpost') {
754
-            return;
755
-        }
756
-        EE_Error::add_attention(
757
-            sprintf(
758
-                esc_html__(
759
-                    'Your event is open for registration. Making changes may disrupt any transactions in progress. %sLearn more%s',
760
-                    'event_espresso'
761
-                ),
762
-                '<a class="espresso-help-tab-lnk">',
763
-                '</a>'
764
-            )
765
-        );
766
-    }
767
-
768
-
769
-    /**
770
-     * When a user is creating a new event, notify them if they haven't set their timezone.
771
-     * Otherwise, do the normal logic
772
-     *
773
-     * @return string
774
-     * @throws \EE_Error
775
-     */
776
-    protected function _create_new_cpt_item()
777
-    {
778
-        $has_timezone_string = get_option('timezone_string');
779
-        // only nag them about setting their timezone if it's their first event, and they haven't already done it
780
-        if (! $has_timezone_string && ! EEM_Event::instance()->exists(array())) {
781
-            EE_Error::add_attention(
782
-                sprintf(
783
-                    __(
784
-                        'Your website\'s timezone is currently set to a UTC offset. We recommend updating your timezone to a city or region near you before you create an event. Change your timezone now:%1$s%2$s%3$sChange Timezone%4$s',
785
-                        'event_espresso'
786
-                    ),
787
-                    '<br>',
788
-                    '<select id="timezone_string" name="timezone_string" aria-describedby="timezone-description">'
789
-                    . EEH_DTT_Helper::wp_timezone_choice('', EEH_DTT_Helper::get_user_locale())
790
-                    . '</select>',
791
-                    '<button class="button button-secondary timezone-submit">',
792
-                    '</button><span class="spinner"></span>'
793
-                ),
794
-                __FILE__,
795
-                __FUNCTION__,
796
-                __LINE__
797
-            );
798
-        }
799
-        return parent::_create_new_cpt_item();
800
-    }
801
-
802
-
803
-    /**
804
-     * Sets the _views property for the default route in this admin page group.
805
-     */
806
-    protected function _set_list_table_views_default()
807
-    {
808
-        $this->_views = array(
809
-            'all'   => array(
810
-                'slug'        => 'all',
811
-                'label'       => esc_html__('View All Events', 'event_espresso'),
812
-                'count'       => 0,
813
-                'bulk_action' => array(
814
-                    'trash_events' => esc_html__('Move to Trash', 'event_espresso'),
815
-                ),
816
-            ),
817
-            'draft' => array(
818
-                'slug'        => 'draft',
819
-                'label'       => esc_html__('Draft', 'event_espresso'),
820
-                'count'       => 0,
821
-                'bulk_action' => array(
822
-                    'trash_events' => esc_html__('Move to Trash', 'event_espresso'),
823
-                ),
824
-            ),
825
-        );
826
-        if (EE_Registry::instance()->CAP->current_user_can('ee_delete_events', 'espresso_events_trash_events')) {
827
-            $this->_views['trash'] = array(
828
-                'slug'        => 'trash',
829
-                'label'       => esc_html__('Trash', 'event_espresso'),
830
-                'count'       => 0,
831
-                'bulk_action' => array(
832
-                    'restore_events' => esc_html__('Restore From Trash', 'event_espresso'),
833
-                    'delete_events'  => esc_html__('Delete Permanently', 'event_espresso'),
834
-                ),
835
-            );
836
-        }
837
-    }
838
-
839
-
840
-    /**
841
-     * Provides the legend item array for the default list table view.
842
-     *
843
-     * @return array
844
-     */
845
-    protected function _event_legend_items()
846
-    {
847
-        $items = array(
848
-            'view_details'   => array(
849
-                'class' => 'dashicons dashicons-search',
850
-                'desc'  => esc_html__('View Event', 'event_espresso'),
851
-            ),
852
-            'edit_event'     => array(
853
-                'class' => 'ee-icon ee-icon-calendar-edit',
854
-                'desc'  => esc_html__('Edit Event Details', 'event_espresso'),
855
-            ),
856
-            'view_attendees' => array(
857
-                'class' => 'dashicons dashicons-groups',
858
-                'desc'  => esc_html__('View Registrations for Event', 'event_espresso'),
859
-            ),
860
-        );
861
-        $items = apply_filters('FHEE__Events_Admin_Page___event_legend_items__items', $items);
862
-        $statuses = array(
863
-            'sold_out_status'  => array(
864
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::sold_out,
865
-                'desc'  => EEH_Template::pretty_status(EE_Datetime::sold_out, false, 'sentence'),
866
-            ),
867
-            'active_status'    => array(
868
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::active,
869
-                'desc'  => EEH_Template::pretty_status(EE_Datetime::active, false, 'sentence'),
870
-            ),
871
-            'upcoming_status'  => array(
872
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::upcoming,
873
-                'desc'  => EEH_Template::pretty_status(EE_Datetime::upcoming, false, 'sentence'),
874
-            ),
875
-            'postponed_status' => array(
876
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::postponed,
877
-                'desc'  => EEH_Template::pretty_status(EE_Datetime::postponed, false, 'sentence'),
878
-            ),
879
-            'cancelled_status' => array(
880
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::cancelled,
881
-                'desc'  => EEH_Template::pretty_status(EE_Datetime::cancelled, false, 'sentence'),
882
-            ),
883
-            'expired_status'   => array(
884
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::expired,
885
-                'desc'  => EEH_Template::pretty_status(EE_Datetime::expired, false, 'sentence'),
886
-            ),
887
-            'inactive_status'  => array(
888
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::inactive,
889
-                'desc'  => EEH_Template::pretty_status(EE_Datetime::inactive, false, 'sentence'),
890
-            ),
891
-        );
892
-        $statuses = apply_filters('FHEE__Events_Admin_Page__event_legend_items__statuses', $statuses);
893
-        return array_merge($items, $statuses);
894
-    }
895
-
896
-
897
-    /**
898
-     * @return EEM_Event
899
-     */
900
-    private function _event_model()
901
-    {
902
-        if (! $this->_event_model instanceof EEM_Event) {
903
-            $this->_event_model = EE_Registry::instance()->load_model('Event');
904
-        }
905
-        return $this->_event_model;
906
-    }
907
-
908
-
909
-    /**
910
-     * Adds extra buttons to the WP CPT permalink field row.
911
-     * Method is called from parent and is hooked into the wp 'get_sample_permalink_html' filter.
912
-     *
913
-     * @param  string $return    the current html
914
-     * @param  int    $id        the post id for the page
915
-     * @param  string $new_title What the title is
916
-     * @param  string $new_slug  what the slug is
917
-     * @return string            The new html string for the permalink area
918
-     */
919
-    public function extra_permalink_field_buttons($return, $id, $new_title, $new_slug)
920
-    {
921
-        // make sure this is only when editing
922
-        if (! empty($id)) {
923
-            $post = get_post($id);
924
-            $return .= '<a class="button button-small" onclick="prompt(\'Shortcode:\', jQuery(\'#shortcode\').val()); return false;" href="#"  tabindex="-1">'
925
-                       . esc_html__('Shortcode', 'event_espresso')
926
-                       . '</a> ';
927
-            $return .= '<input id="shortcode" type="hidden" value="[ESPRESSO_TICKET_SELECTOR event_id='
928
-                       . $post->ID
929
-                       . ']">';
930
-        }
931
-        return $return;
932
-    }
933
-
934
-
935
-    /**
936
-     * _events_overview_list_table
937
-     * This contains the logic for showing the events_overview list
938
-     *
939
-     * @access protected
940
-     * @return void
941
-     * @throws \EE_Error
942
-     */
943
-    protected function _events_overview_list_table()
944
-    {
945
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
946
-        $this->_template_args['after_list_table'] = ! empty($this->_template_args['after_list_table'])
947
-            ? (array) $this->_template_args['after_list_table']
948
-            : array();
949
-        $this->_template_args['after_list_table']['view_event_list_button'] = EEH_HTML::br()
950
-                . EEH_Template::get_button_or_link(
951
-                    get_post_type_archive_link('espresso_events'),
952
-                    esc_html__("View Event Archive Page", "event_espresso"),
953
-                    'button'
954
-                );
955
-        $this->_template_args['after_list_table']['legend'] = $this->_display_legend($this->_event_legend_items());
956
-        $this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
957
-            'create_new',
958
-            'add',
959
-            array(),
960
-            'add-new-h2'
961
-        );
962
-        $this->display_admin_list_table_page_with_no_sidebar();
963
-    }
964
-
965
-
966
-    /**
967
-     * this allows for extra misc actions in the default WP publish box
968
-     *
969
-     * @return void
970
-     */
971
-    public function extra_misc_actions_publish_box()
972
-    {
973
-        $this->_generate_publish_box_extra_content();
974
-    }
975
-
976
-
977
-    /**
978
-     * This is hooked into the WordPress do_action('save_post') hook and runs after the custom post type has been
979
-     * saved.
980
-     * Typically you would use this to save any additional data.
981
-     * Keep in mind also that "save_post" runs on EVERY post update to the database.
982
-     * ALSO very important.  When a post transitions from scheduled to published,
983
-     * the save_post action is fired but you will NOT have any _POST data containing any extra info you may have from
984
-     * other meta saves. So MAKE sure that you handle this accordingly.
985
-     *
986
-     * @access protected
987
-     * @abstract
988
-     * @param  string $post_id The ID of the cpt that was saved (so you can link relationally)
989
-     * @param  object $post    The post object of the cpt that was saved.
990
-     * @return void
991
-     * @throws \EE_Error
992
-     */
993
-    protected function _insert_update_cpt_item($post_id, $post)
994
-    {
995
-        if ($post instanceof WP_Post && $post->post_type !== 'espresso_events') {
996
-            // get out we're not processing an event save.
997
-            return;
998
-        }
999
-        $event_values = array(
1000
-            'EVT_display_desc'                => ! empty($this->_req_data['display_desc']) ? 1 : 0,
1001
-            'EVT_display_ticket_selector'     => ! empty($this->_req_data['display_ticket_selector']) ? 1 : 0,
1002
-            'EVT_additional_limit'            => min(
1003
-                apply_filters('FHEE__EE_Events_Admin__insert_update_cpt_item__EVT_additional_limit_max', 255),
1004
-                ! empty($this->_req_data['additional_limit']) ? $this->_req_data['additional_limit'] : null
1005
-            ),
1006
-            'EVT_default_registration_status' => ! empty($this->_req_data['EVT_default_registration_status'])
1007
-                ? $this->_req_data['EVT_default_registration_status']
1008
-                : EE_Registry::instance()->CFG->registration->default_STS_ID,
1009
-            'EVT_member_only'                 => ! empty($this->_req_data['member_only']) ? 1 : 0,
1010
-            'EVT_allow_overflow'              => ! empty($this->_req_data['EVT_allow_overflow']) ? 1 : 0,
1011
-            'EVT_timezone_string'             => ! empty($this->_req_data['timezone_string'])
1012
-                ? $this->_req_data['timezone_string'] : null,
1013
-            'EVT_external_URL'                => ! empty($this->_req_data['externalURL'])
1014
-                ? $this->_req_data['externalURL'] : null,
1015
-            'EVT_phone'                       => ! empty($this->_req_data['event_phone'])
1016
-                ? $this->_req_data['event_phone'] : null,
1017
-        );
1018
-        // update event
1019
-        $success = $this->_event_model()->update_by_ID($event_values, $post_id);
1020
-        // get event_object for other metaboxes... though it would seem to make sense to just use $this->_event_model()->get_one_by_ID( $post_id ).. i have to setup where conditions to override the filters in the model that filter out autodraft and inherit statuses so we GET the inherit id!
1021
-        $get_one_where = array(
1022
-            $this->_event_model()->primary_key_name() => $post_id,
1023
-            'OR'                                      => array(
1024
-                'status'   => $post->post_status,
1025
-                // if trying to "Publish" a sold out event, it's status will get switched back to "sold_out" in the db,
1026
-                // but the returned object here has a status of "publish", so use the original post status as well
1027
-                'status*1' => $this->_req_data['original_post_status'],
1028
-            ),
1029
-        );
1030
-        $event = $this->_event_model()->get_one(array($get_one_where));
1031
-        // the following are default callbacks for event attachment updates that can be overridden by caffeinated functionality and/or addons.
1032
-        $event_update_callbacks = apply_filters(
1033
-            'FHEE__Events_Admin_Page___insert_update_cpt_item__event_update_callbacks',
1034
-            array(
1035
-                array($this, '_default_venue_update'),
1036
-                array($this, '_default_tickets_update'),
1037
-            )
1038
-        );
1039
-        $att_success = true;
1040
-        foreach ($event_update_callbacks as $e_callback) {
1041
-            $_success = is_callable($e_callback)
1042
-                ? call_user_func($e_callback, $event, $this->_req_data)
1043
-                : false;
1044
-            // if ANY of these updates fail then we want the appropriate global error message
1045
-            $att_success = ! $att_success ? $att_success : $_success;
1046
-        }
1047
-        // any errors?
1048
-        if ($success && false === $att_success) {
1049
-            EE_Error::add_error(
1050
-                esc_html__(
1051
-                    'Event Details saved successfully but something went wrong with saving attachments.',
1052
-                    'event_espresso'
1053
-                ),
1054
-                __FILE__,
1055
-                __FUNCTION__,
1056
-                __LINE__
1057
-            );
1058
-        } elseif ($success === false) {
1059
-            EE_Error::add_error(
1060
-                esc_html__('Event Details did not save successfully.', 'event_espresso'),
1061
-                __FILE__,
1062
-                __FUNCTION__,
1063
-                __LINE__
1064
-            );
1065
-        }
1066
-    }
1067
-
1068
-
1069
-    /**
1070
-     * @see parent::restore_item()
1071
-     * @param int $post_id
1072
-     * @param int $revision_id
1073
-     */
1074
-    protected function _restore_cpt_item($post_id, $revision_id)
1075
-    {
1076
-        // copy existing event meta to new post
1077
-        $post_evt = $this->_event_model()->get_one_by_ID($post_id);
1078
-        if ($post_evt instanceof EE_Event) {
1079
-            // meta revision restore
1080
-            $post_evt->restore_revision($revision_id);
1081
-            // related objs restore
1082
-            $post_evt->restore_revision($revision_id, array('Venue', 'Datetime', 'Price'));
1083
-        }
1084
-    }
1085
-
1086
-
1087
-    /**
1088
-     * Attach the venue to the Event
1089
-     *
1090
-     * @param  \EE_Event $evtobj Event Object to add the venue to
1091
-     * @param  array     $data   The request data from the form
1092
-     * @return bool           Success or fail.
1093
-     */
1094
-    protected function _default_venue_update(\EE_Event $evtobj, $data)
1095
-    {
1096
-        require_once(EE_MODELS . 'EEM_Venue.model.php');
1097
-        $venue_model = EE_Registry::instance()->load_model('Venue');
1098
-        $rows_affected = null;
1099
-        $venue_id = ! empty($data['venue_id']) ? $data['venue_id'] : null;
1100
-        // very important.  If we don't have a venue name...
1101
-        // then we'll get out because not necessary to create empty venue
1102
-        if (empty($data['venue_title'])) {
1103
-            return false;
1104
-        }
1105
-        $venue_array = array(
1106
-            'VNU_wp_user'         => $evtobj->get('EVT_wp_user'),
1107
-            'VNU_name'            => ! empty($data['venue_title']) ? $data['venue_title'] : null,
1108
-            'VNU_desc'            => ! empty($data['venue_description']) ? $data['venue_description'] : null,
1109
-            'VNU_identifier'      => ! empty($data['venue_identifier']) ? $data['venue_identifier'] : null,
1110
-            'VNU_short_desc'      => ! empty($data['venue_short_description']) ? $data['venue_short_description']
1111
-                : null,
1112
-            'VNU_address'         => ! empty($data['address']) ? $data['address'] : null,
1113
-            'VNU_address2'        => ! empty($data['address2']) ? $data['address2'] : null,
1114
-            'VNU_city'            => ! empty($data['city']) ? $data['city'] : null,
1115
-            'STA_ID'              => ! empty($data['state']) ? $data['state'] : null,
1116
-            'CNT_ISO'             => ! empty($data['countries']) ? $data['countries'] : null,
1117
-            'VNU_zip'             => ! empty($data['zip']) ? $data['zip'] : null,
1118
-            'VNU_phone'           => ! empty($data['venue_phone']) ? $data['venue_phone'] : null,
1119
-            'VNU_capacity'        => ! empty($data['venue_capacity']) ? $data['venue_capacity'] : null,
1120
-            'VNU_url'             => ! empty($data['venue_url']) ? $data['venue_url'] : null,
1121
-            'VNU_virtual_phone'   => ! empty($data['virtual_phone']) ? $data['virtual_phone'] : null,
1122
-            'VNU_virtual_url'     => ! empty($data['virtual_url']) ? $data['virtual_url'] : null,
1123
-            'VNU_enable_for_gmap' => isset($data['enable_for_gmap']) ? 1 : 0,
1124
-            'status'              => 'publish',
1125
-        );
1126
-        // if we've got the venue_id then we're just updating the existing venue so let's do that and then get out.
1127
-        if (! empty($venue_id)) {
1128
-            $update_where = array($venue_model->primary_key_name() => $venue_id);
1129
-            $rows_affected = $venue_model->update($venue_array, array($update_where));
1130
-            // we've gotta make sure that the venue is always attached to a revision.. add_relation_to should take care of making sure that the relation is already present.
1131
-            $evtobj->_add_relation_to($venue_id, 'Venue');
1132
-            return $rows_affected > 0 ? true : false;
1133
-        } else {
1134
-            // we insert the venue
1135
-            $venue_id = $venue_model->insert($venue_array);
1136
-            $evtobj->_add_relation_to($venue_id, 'Venue');
1137
-            return ! empty($venue_id) ? true : false;
1138
-        }
1139
-        // when we have the ancestor come in it's already been handled by the revision save.
1140
-    }
1141
-
1142
-
1143
-    /**
1144
-     * Handles saving everything related to Tickets (datetimes, tickets, prices)
1145
-     *
1146
-     * @param  EE_Event $evtobj The Event object we're attaching data to
1147
-     * @param  array    $data   The request data from the form
1148
-     * @return array
1149
-     */
1150
-    protected function _default_tickets_update(EE_Event $evtobj, $data)
1151
-    {
1152
-        $success = true;
1153
-        $saved_dtt = null;
1154
-        $saved_tickets = array();
1155
-        $incoming_date_formats = array('Y-m-d', 'h:i a');
1156
-        foreach ($data['edit_event_datetimes'] as $row => $dtt) {
1157
-            // trim all values to ensure any excess whitespace is removed.
1158
-            $dtt = array_map('trim', $dtt);
1159
-            $dtt['DTT_EVT_end'] = isset($dtt['DTT_EVT_end']) && ! empty($dtt['DTT_EVT_end']) ? $dtt['DTT_EVT_end']
1160
-                : $dtt['DTT_EVT_start'];
1161
-            $datetime_values = array(
1162
-                'DTT_ID'        => ! empty($dtt['DTT_ID']) ? $dtt['DTT_ID'] : null,
1163
-                'DTT_EVT_start' => $dtt['DTT_EVT_start'],
1164
-                'DTT_EVT_end'   => $dtt['DTT_EVT_end'],
1165
-                'DTT_reg_limit' => empty($dtt['DTT_reg_limit']) ? EE_INF : $dtt['DTT_reg_limit'],
1166
-                'DTT_order'     => $row,
1167
-            );
1168
-            // 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.
1169
-            if (! empty($dtt['DTT_ID'])) {
1170
-                $DTM = EE_Registry::instance()
1171
-                                  ->load_model('Datetime', array($evtobj->get_timezone()))
1172
-                                  ->get_one_by_ID($dtt['DTT_ID']);
1173
-                $DTM->set_date_format($incoming_date_formats[0]);
1174
-                $DTM->set_time_format($incoming_date_formats[1]);
1175
-                foreach ($datetime_values as $field => $value) {
1176
-                    $DTM->set($field, $value);
1177
-                }
1178
-                // make sure the $dtt_id here is saved just in case after the add_relation_to() the autosave replaces it.  We need to do this so we dont' TRASH the parent DTT.
1179
-                $saved_dtts[ $DTM->ID() ] = $DTM;
1180
-            } else {
1181
-                $DTM = EE_Registry::instance()->load_class(
1182
-                    'Datetime',
1183
-                    array($datetime_values, $evtobj->get_timezone(), $incoming_date_formats),
1184
-                    false,
1185
-                    false
1186
-                );
1187
-                foreach ($datetime_values as $field => $value) {
1188
-                    $DTM->set($field, $value);
1189
-                }
1190
-            }
1191
-            $DTM->save();
1192
-            $DTT = $evtobj->_add_relation_to($DTM, 'Datetime');
1193
-            // load DTT helper
1194
-            // 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.
1195
-            if ($DTT->get_raw('DTT_EVT_start') > $DTT->get_raw('DTT_EVT_end')) {
1196
-                $DTT->set('DTT_EVT_end', $DTT->get('DTT_EVT_start'));
1197
-                $DTT = EEH_DTT_Helper::date_time_add($DTT, 'DTT_EVT_end', 'days');
1198
-                $DTT->save();
1199
-            }
1200
-            // now we got to make sure we add the new DTT_ID to the $saved_dtts array  because it is possible there was a new one created for the autosave.
1201
-            $saved_dtt = $DTT;
1202
-            $success = ! $success ? $success : $DTT;
1203
-            // if ANY of these updates fail then we want the appropriate global error message.
1204
-            // //todo this is actually sucky we need a better error message but this is what it is for now.
1205
-        }
1206
-        // no dtts get deleted so we don't do any of that logic here.
1207
-        // update tickets next
1208
-        $old_tickets = isset($data['ticket_IDs']) ? explode(',', $data['ticket_IDs']) : array();
1209
-        foreach ($data['edit_tickets'] as $row => $tkt) {
1210
-            $incoming_date_formats = array('Y-m-d', 'h:i a');
1211
-            $update_prices = false;
1212
-            $ticket_price = isset($data['edit_prices'][ $row ][1]['PRC_amount'])
1213
-                ? $data['edit_prices'][ $row ][1]['PRC_amount'] : 0;
1214
-            // trim inputs to ensure any excess whitespace is removed.
1215
-            $tkt = array_map('trim', $tkt);
1216
-            if (empty($tkt['TKT_start_date'])) {
1217
-                // let's use now in the set timezone.
1218
-                $now = new DateTime('now', new DateTimeZone($evtobj->get_timezone()));
1219
-                $tkt['TKT_start_date'] = $now->format($incoming_date_formats[0] . ' ' . $incoming_date_formats[1]);
1220
-            }
1221
-            if (empty($tkt['TKT_end_date'])) {
1222
-                // use the start date of the first datetime
1223
-                $dtt = $evtobj->first_datetime();
1224
-                $tkt['TKT_end_date'] = $dtt->start_date_and_time(
1225
-                    $incoming_date_formats[0],
1226
-                    $incoming_date_formats[1]
1227
-                );
1228
-            }
1229
-            $TKT_values = array(
1230
-                'TKT_ID'          => ! empty($tkt['TKT_ID']) ? $tkt['TKT_ID'] : null,
1231
-                'TTM_ID'          => ! empty($tkt['TTM_ID']) ? $tkt['TTM_ID'] : 0,
1232
-                'TKT_name'        => ! empty($tkt['TKT_name']) ? $tkt['TKT_name'] : '',
1233
-                'TKT_description' => ! empty($tkt['TKT_description']) ? $tkt['TKT_description'] : '',
1234
-                'TKT_start_date'  => $tkt['TKT_start_date'],
1235
-                'TKT_end_date'    => $tkt['TKT_end_date'],
1236
-                'TKT_qty'         => ! isset($tkt['TKT_qty']) || $tkt['TKT_qty'] === '' ? EE_INF : $tkt['TKT_qty'],
1237
-                'TKT_uses'        => ! isset($tkt['TKT_uses']) || $tkt['TKT_uses'] === '' ? EE_INF : $tkt['TKT_uses'],
1238
-                'TKT_min'         => empty($tkt['TKT_min']) ? 0 : $tkt['TKT_min'],
1239
-                'TKT_max'         => empty($tkt['TKT_max']) ? EE_INF : $tkt['TKT_max'],
1240
-                'TKT_row'         => $row,
1241
-                'TKT_order'       => isset($tkt['TKT_order']) ? $tkt['TKT_order'] : $row,
1242
-                'TKT_price'       => $ticket_price,
1243
-            );
1244
-            // 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.
1245
-            if (isset($tkt['TKT_is_default']) && $tkt['TKT_is_default']) {
1246
-                $TKT_values['TKT_ID'] = 0;
1247
-                $TKT_values['TKT_is_default'] = 0;
1248
-                $TKT_values['TKT_price'] = $ticket_price;
1249
-                $update_prices = true;
1250
-            }
1251
-            // if we have a TKT_ID then we need to get that existing TKT_obj and update it
1252
-            // we actually do our saves a head of doing any add_relations to because its entirely possible that this ticket didn't removed or added to any datetime in the session but DID have it's items modified.
1253
-            // keep in mind that if the TKT has been sold (and we have changed pricing information), then we won't be updating the tkt but instead a new tkt will be created and the old one archived.
1254
-            if (! empty($tkt['TKT_ID'])) {
1255
-                $TKT = EE_Registry::instance()
1256
-                                  ->load_model('Ticket', array($evtobj->get_timezone()))
1257
-                                  ->get_one_by_ID($tkt['TKT_ID']);
1258
-                if ($TKT instanceof EE_Ticket) {
1259
-                    $ticket_sold = $TKT->count_related(
1260
-                        'Registration',
1261
-                        array(
1262
-                            array(
1263
-                                'STS_ID' => array(
1264
-                                    'NOT IN',
1265
-                                    array(EEM_Registration::status_id_incomplete),
1266
-                                ),
1267
-                            ),
1268
-                        )
1269
-                    ) > 0 ? true : false;
1270
-                    // let's just check the total price for the existing ticket and determine if it matches the new total price.  if they are different then we create a new ticket (if tkts sold) if they aren't different then we go ahead and modify existing ticket.
1271
-                    $create_new_TKT = $ticket_sold && $ticket_price != $TKT->get('TKT_price')
1272
-                                      && ! $TKT->get('TKT_deleted');
1273
-                    $TKT->set_date_format($incoming_date_formats[0]);
1274
-                    $TKT->set_time_format($incoming_date_formats[1]);
1275
-                    // set new values
1276
-                    foreach ($TKT_values as $field => $value) {
1277
-                        if ($field == 'TKT_qty') {
1278
-                            $TKT->set_qty($value);
1279
-                        } else {
1280
-                            $TKT->set($field, $value);
1281
-                        }
1282
-                    }
1283
-                    // if $create_new_TKT is false then we can safely update the existing ticket.  Otherwise we have to create a new ticket.
1284
-                    if ($create_new_TKT) {
1285
-                        // archive the old ticket first
1286
-                        $TKT->set('TKT_deleted', 1);
1287
-                        $TKT->save();
1288
-                        // make sure this ticket is still recorded in our saved_tkts so we don't run it through the regular trash routine.
1289
-                        $saved_tickets[ $TKT->ID() ] = $TKT;
1290
-                        // create new ticket that's a copy of the existing except a new id of course (and not archived) AND has the new TKT_price associated with it.
1291
-                        $TKT = clone $TKT;
1292
-                        $TKT->set('TKT_ID', 0);
1293
-                        $TKT->set('TKT_deleted', 0);
1294
-                        $TKT->set('TKT_price', $ticket_price);
1295
-                        $TKT->set('TKT_sold', 0);
1296
-                        // now we need to make sure that $new prices are created as well and attached to new ticket.
1297
-                        $update_prices = true;
1298
-                    }
1299
-                    // make sure price is set if it hasn't been already
1300
-                    $TKT->set('TKT_price', $ticket_price);
1301
-                }
1302
-            } else {
1303
-                // no TKT_id so a new TKT
1304
-                $TKT_values['TKT_price'] = $ticket_price;
1305
-                $TKT = EE_Registry::instance()->load_class('Ticket', array($TKT_values), false, false);
1306
-                if ($TKT instanceof EE_Ticket) {
1307
-                    // need to reset values to properly account for the date formats
1308
-                    $TKT->set_date_format($incoming_date_formats[0]);
1309
-                    $TKT->set_time_format($incoming_date_formats[1]);
1310
-                    $TKT->set_timezone($evtobj->get_timezone());
1311
-                    // set new values
1312
-                    foreach ($TKT_values as $field => $value) {
1313
-                        if ($field == 'TKT_qty') {
1314
-                            $TKT->set_qty($value);
1315
-                        } else {
1316
-                            $TKT->set($field, $value);
1317
-                        }
1318
-                    }
1319
-                    $update_prices = true;
1320
-                }
1321
-            }
1322
-            // cap ticket qty by datetime reg limits
1323
-            $TKT->set_qty(min($TKT->qty(), $TKT->qty('reg_limit')));
1324
-            // update ticket.
1325
-            $TKT->save();
1326
-            // 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.
1327
-            if ($TKT->get_raw('TKT_start_date') > $TKT->get_raw('TKT_end_date')) {
1328
-                $TKT->set('TKT_end_date', $TKT->get('TKT_start_date'));
1329
-                $TKT = EEH_DTT_Helper::date_time_add($TKT, 'TKT_end_date', 'days');
1330
-                $TKT->save();
1331
-            }
1332
-            // initially let's add the ticket to the dtt
1333
-            $saved_dtt->_add_relation_to($TKT, 'Ticket');
1334
-            $saved_tickets[ $TKT->ID() ] = $TKT;
1335
-            // add prices to ticket
1336
-            $this->_add_prices_to_ticket($data['edit_prices'][ $row ], $TKT, $update_prices);
1337
-        }
1338
-        // however now we need to handle permanently deleting tickets via the ui.  Keep in mind that the ui does not allow deleting/archiving tickets that have ticket sold.  However, it does allow for deleting tickets that have no tickets sold, in which case we want to get rid of permanently because there is no need to save in db.
1339
-        $old_tickets = isset($old_tickets[0]) && $old_tickets[0] == '' ? array() : $old_tickets;
1340
-        $tickets_removed = array_diff($old_tickets, array_keys($saved_tickets));
1341
-        foreach ($tickets_removed as $id) {
1342
-            $id = absint($id);
1343
-            // get the ticket for this id
1344
-            $tkt_to_remove = EE_Registry::instance()->load_model('Ticket')->get_one_by_ID($id);
1345
-            // need to get all the related datetimes on this ticket and remove from every single one of them (remember this process can ONLY kick off if there are NO tkts_sold)
1346
-            $dtts = $tkt_to_remove->get_many_related('Datetime');
1347
-            foreach ($dtts as $dtt) {
1348
-                $tkt_to_remove->_remove_relation_to($dtt, 'Datetime');
1349
-            }
1350
-            // need to do the same for prices (except these prices can also be deleted because again, tickets can only be trashed if they don't have any TKTs sold (otherwise they are just archived))
1351
-            $tkt_to_remove->delete_related_permanently('Price');
1352
-            // finally let's delete this ticket (which should not be blocked at this point b/c we've removed all our relationships)
1353
-            $tkt_to_remove->delete_permanently();
1354
-        }
1355
-        return array($saved_dtt, $saved_tickets);
1356
-    }
1357
-
1358
-
1359
-    /**
1360
-     * This attaches a list of given prices to a ticket.
1361
-     * Note we dont' have to worry about ever removing relationships (or archiving prices) because if there is a change
1362
-     * in price information on a ticket, a new ticket is created anyways so the archived ticket will retain the old
1363
-     * price info and prices are automatically "archived" via the ticket.
1364
-     *
1365
-     * @access  private
1366
-     * @param array     $prices     Array of prices from the form.
1367
-     * @param EE_Ticket $ticket     EE_Ticket object that prices are being attached to.
1368
-     * @param bool      $new_prices Whether attach existing incoming prices or create new ones.
1369
-     * @return  void
1370
-     */
1371
-    private function _add_prices_to_ticket($prices, EE_Ticket $ticket, $new_prices = false)
1372
-    {
1373
-        foreach ($prices as $row => $prc) {
1374
-            $PRC_values = array(
1375
-                'PRC_ID'         => ! empty($prc['PRC_ID']) ? $prc['PRC_ID'] : null,
1376
-                'PRT_ID'         => ! empty($prc['PRT_ID']) ? $prc['PRT_ID'] : null,
1377
-                'PRC_amount'     => ! empty($prc['PRC_amount']) ? $prc['PRC_amount'] : 0,
1378
-                'PRC_name'       => ! empty($prc['PRC_name']) ? $prc['PRC_name'] : '',
1379
-                'PRC_desc'       => ! empty($prc['PRC_desc']) ? $prc['PRC_desc'] : '',
1380
-                'PRC_is_default' => 0, // make sure prices are NOT set as default from this context
1381
-                'PRC_order'      => $row,
1382
-            );
1383
-            if ($new_prices || empty($PRC_values['PRC_ID'])) {
1384
-                $PRC_values['PRC_ID'] = 0;
1385
-                $PRC = EE_Registry::instance()->load_class('Price', array($PRC_values), false, false);
1386
-            } else {
1387
-                $PRC = EE_Registry::instance()->load_model('Price')->get_one_by_ID($prc['PRC_ID']);
1388
-                // update this price with new values
1389
-                foreach ($PRC_values as $field => $newprc) {
1390
-                    $PRC->set($field, $newprc);
1391
-                }
1392
-                $PRC->save();
1393
-            }
1394
-            $ticket->_add_relation_to($PRC, 'Price');
1395
-        }
1396
-    }
1397
-
1398
-
1399
-    /**
1400
-     * Add in our autosave ajax handlers
1401
-     *
1402
-     */
1403
-    protected function _ee_autosave_create_new()
1404
-    {
1405
-    }
1406
-
1407
-
1408
-    /**
1409
-     * More autosave handlers.
1410
-     */
1411
-    protected function _ee_autosave_edit()
1412
-    {
1413
-        return; // TEMPORARILY EXITING CAUSE THIS IS A TODO
1414
-    }
1415
-
1416
-
1417
-    /**
1418
-     *    _generate_publish_box_extra_content
1419
-     */
1420
-    private function _generate_publish_box_extra_content()
1421
-    {
1422
-        // load formatter helper
1423
-        // args for getting related registrations
1424
-        $approved_query_args = array(
1425
-            array(
1426
-                'REG_deleted' => 0,
1427
-                'STS_ID'      => EEM_Registration::status_id_approved,
1428
-            ),
1429
-        );
1430
-        $not_approved_query_args = array(
1431
-            array(
1432
-                'REG_deleted' => 0,
1433
-                'STS_ID'      => EEM_Registration::status_id_not_approved,
1434
-            ),
1435
-        );
1436
-        $pending_payment_query_args = array(
1437
-            array(
1438
-                'REG_deleted' => 0,
1439
-                'STS_ID'      => EEM_Registration::status_id_pending_payment,
1440
-            ),
1441
-        );
1442
-        // publish box
1443
-        $publish_box_extra_args = array(
1444
-            'view_approved_reg_url'        => add_query_arg(
1445
-                array(
1446
-                    'action'      => 'default',
1447
-                    'event_id'    => $this->_cpt_model_obj->ID(),
1448
-                    '_reg_status' => EEM_Registration::status_id_approved,
1449
-                ),
1450
-                REG_ADMIN_URL
1451
-            ),
1452
-            'view_not_approved_reg_url'    => add_query_arg(
1453
-                array(
1454
-                    'action'      => 'default',
1455
-                    'event_id'    => $this->_cpt_model_obj->ID(),
1456
-                    '_reg_status' => EEM_Registration::status_id_not_approved,
1457
-                ),
1458
-                REG_ADMIN_URL
1459
-            ),
1460
-            'view_pending_payment_reg_url' => add_query_arg(
1461
-                array(
1462
-                    'action'      => 'default',
1463
-                    'event_id'    => $this->_cpt_model_obj->ID(),
1464
-                    '_reg_status' => EEM_Registration::status_id_pending_payment,
1465
-                ),
1466
-                REG_ADMIN_URL
1467
-            ),
1468
-            'approved_regs'                => $this->_cpt_model_obj->count_related(
1469
-                'Registration',
1470
-                $approved_query_args
1471
-            ),
1472
-            'not_approved_regs'            => $this->_cpt_model_obj->count_related(
1473
-                'Registration',
1474
-                $not_approved_query_args
1475
-            ),
1476
-            'pending_payment_regs'         => $this->_cpt_model_obj->count_related(
1477
-                'Registration',
1478
-                $pending_payment_query_args
1479
-            ),
1480
-            'misc_pub_section_class'       => apply_filters(
1481
-                'FHEE_Events_Admin_Page___generate_publish_box_extra_content__misc_pub_section_class',
1482
-                'misc-pub-section'
1483
-            ),
1484
-        );
1485
-        ob_start();
1486
-        do_action(
1487
-            'AHEE__Events_Admin_Page___generate_publish_box_extra_content__event_editor_overview_add',
1488
-            $this->_cpt_model_obj
1489
-        );
1490
-        $publish_box_extra_args['event_editor_overview_add'] = ob_get_clean();
1491
-        // load template
1492
-        EEH_Template::display_template(
1493
-            EVENTS_TEMPLATE_PATH . 'event_publish_box_extras.template.php',
1494
-            $publish_box_extra_args
1495
-        );
1496
-    }
1497
-
1498
-
1499
-    /**
1500
-     * @return EE_Event
1501
-     */
1502
-    public function get_event_object()
1503
-    {
1504
-        return $this->_cpt_model_obj;
1505
-    }
1506
-
1507
-
1508
-
1509
-
1510
-    /** METABOXES * */
1511
-    /**
1512
-     * _register_event_editor_meta_boxes
1513
-     * add all metaboxes related to the event_editor
1514
-     *
1515
-     * @return void
1516
-     */
1517
-    protected function _register_event_editor_meta_boxes()
1518
-    {
1519
-        $this->verify_cpt_object();
1520
-        add_meta_box(
1521
-            'espresso_event_editor_tickets',
1522
-            esc_html__('Event Datetime & Ticket', 'event_espresso'),
1523
-            array($this, 'ticket_metabox'),
1524
-            $this->page_slug,
1525
-            'normal',
1526
-            'high'
1527
-        );
1528
-        add_meta_box(
1529
-            'espresso_event_editor_event_options',
1530
-            esc_html__('Event Registration Options', 'event_espresso'),
1531
-            array($this, 'registration_options_meta_box'),
1532
-            $this->page_slug,
1533
-            'side',
1534
-            'default'
1535
-        );
1536
-        // NOTE: if you're looking for other metaboxes in here,
1537
-        // where a metabox has a related management page in the admin
1538
-        // you will find it setup in the related management page's "_Hooks" file.
1539
-        // i.e. messages metabox is found in "espresso_events_Messages_Hooks.class.php".
1540
-    }
1541
-
1542
-
1543
-    /**
1544
-     * @throws DomainException
1545
-     * @throws EE_Error
1546
-     */
1547
-    public function ticket_metabox()
1548
-    {
1549
-        $existing_datetime_ids = $existing_ticket_ids = array();
1550
-        // defaults for template args
1551
-        $template_args = array(
1552
-            'existing_datetime_ids'    => '',
1553
-            'event_datetime_help_link' => '',
1554
-            'ticket_options_help_link' => '',
1555
-            'time'                     => null,
1556
-            'ticket_rows'              => '',
1557
-            'existing_ticket_ids'      => '',
1558
-            'total_ticket_rows'        => 1,
1559
-            'ticket_js_structure'      => '',
1560
-            'trash_icon'               => 'ee-lock-icon',
1561
-            'disabled'                 => '',
1562
-        );
1563
-        $event_id = is_object($this->_cpt_model_obj) ? $this->_cpt_model_obj->ID() : null;
1564
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1565
-        /**
1566
-         * 1. Start with retrieving Datetimes
1567
-         * 2. Fore each datetime get related tickets
1568
-         * 3. For each ticket get related prices
1569
-         */
1570
-        $times = EE_Registry::instance()->load_model('Datetime')->get_all_event_dates($event_id);
1571
-        /** @type EE_Datetime $first_datetime */
1572
-        $first_datetime = reset($times);
1573
-        // do we get related tickets?
1574
-        if ($first_datetime instanceof EE_Datetime
1575
-            && $first_datetime->ID() !== 0
1576
-        ) {
1577
-            $existing_datetime_ids[] = $first_datetime->get('DTT_ID');
1578
-            $template_args['time'] = $first_datetime;
1579
-            $related_tickets = $first_datetime->tickets(
1580
-                array(
1581
-                    array('OR' => array('TKT_deleted' => 1, 'TKT_deleted*' => 0)),
1582
-                    'default_where_conditions' => 'none',
1583
-                )
1584
-            );
1585
-            if (! empty($related_tickets)) {
1586
-                $template_args['total_ticket_rows'] = count($related_tickets);
1587
-                $row = 0;
1588
-                foreach ($related_tickets as $ticket) {
1589
-                    $existing_ticket_ids[] = $ticket->get('TKT_ID');
1590
-                    $template_args['ticket_rows'] .= $this->_get_ticket_row($ticket, false, $row);
1591
-                    $row++;
1592
-                }
1593
-            } else {
1594
-                $template_args['total_ticket_rows'] = 1;
1595
-                /** @type EE_Ticket $ticket */
1596
-                $ticket = EE_Registry::instance()->load_model('Ticket')->create_default_object();
1597
-                $template_args['ticket_rows'] .= $this->_get_ticket_row($ticket);
1598
-            }
1599
-        } else {
1600
-            $template_args['time'] = $times[0];
1601
-            /** @type EE_Ticket $ticket */
1602
-            $ticket = EE_Registry::instance()->load_model('Ticket')->get_all_default_tickets();
1603
-            $template_args['ticket_rows'] .= $this->_get_ticket_row($ticket[1]);
1604
-            // NOTE: we're just sending the first default row
1605
-            // (decaf can't manage default tickets so this should be sufficient);
1606
-        }
1607
-        $template_args['event_datetime_help_link'] = $this->_get_help_tab_link(
1608
-            'event_editor_event_datetimes_help_tab'
1609
-        );
1610
-        $template_args['ticket_options_help_link'] = $this->_get_help_tab_link('ticket_options_info');
1611
-        $template_args['existing_datetime_ids'] = implode(',', $existing_datetime_ids);
1612
-        $template_args['existing_ticket_ids'] = implode(',', $existing_ticket_ids);
1613
-        $template_args['ticket_js_structure'] = $this->_get_ticket_row(
1614
-            EE_Registry::instance()->load_model('Ticket')->create_default_object(),
1615
-            true
1616
-        );
1617
-        $template = apply_filters(
1618
-            'FHEE__Events_Admin_Page__ticket_metabox__template',
1619
-            EVENTS_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php'
1620
-        );
1621
-        EEH_Template::display_template($template, $template_args);
1622
-    }
1623
-
1624
-
1625
-    /**
1626
-     * Setup an individual ticket form for the decaf event editor page
1627
-     *
1628
-     * @access private
1629
-     * @param  EE_Ticket $ticket   the ticket object
1630
-     * @param  boolean   $skeleton whether we're generating a skeleton for js manipulation
1631
-     * @param int        $row
1632
-     * @return string generated html for the ticket row.
1633
-     */
1634
-    private function _get_ticket_row($ticket, $skeleton = false, $row = 0)
1635
-    {
1636
-        $template_args = array(
1637
-            'tkt_status_class'    => ' tkt-status-' . $ticket->ticket_status(),
1638
-            'tkt_archive_class'   => $ticket->ticket_status() === EE_Ticket::archived && ! $skeleton ? ' tkt-archived'
1639
-                : '',
1640
-            'ticketrow'           => $skeleton ? 'TICKETNUM' : $row,
1641
-            'TKT_ID'              => $ticket->get('TKT_ID'),
1642
-            'TKT_name'            => $ticket->get('TKT_name'),
1643
-            'TKT_start_date'      => $skeleton ? '' : $ticket->get_date('TKT_start_date', 'Y-m-d h:i a'),
1644
-            'TKT_end_date'        => $skeleton ? '' : $ticket->get_date('TKT_end_date', 'Y-m-d h:i a'),
1645
-            'TKT_is_default'      => $ticket->get('TKT_is_default'),
1646
-            'TKT_qty'             => $ticket->get_pretty('TKT_qty', 'input'),
1647
-            'edit_ticketrow_name' => $skeleton ? 'TICKETNAMEATTR' : 'edit_tickets',
1648
-            'TKT_sold'            => $skeleton ? 0 : $ticket->get('TKT_sold'),
1649
-            'trash_icon'          => ($skeleton || (! empty($ticket) && ! $ticket->get('TKT_deleted')))
1650
-                                     && (! empty($ticket) && $ticket->get('TKT_sold') === 0)
1651
-                ? 'trash-icon dashicons dashicons-post-trash clickable' : 'ee-lock-icon',
1652
-            'disabled'            => $skeleton || (! empty($ticket) && ! $ticket->get('TKT_deleted')) ? ''
1653
-                : ' disabled=disabled',
1654
-        );
1655
-        $price = $ticket->ID() !== 0
1656
-            ? $ticket->get_first_related('Price', array('default_where_conditions' => 'none'))
1657
-            : EE_Registry::instance()->load_model('Price')->create_default_object();
1658
-        $price_args = array(
1659
-            'price_currency_symbol' => EE_Registry::instance()->CFG->currency->sign,
1660
-            'PRC_amount'            => $price->get('PRC_amount'),
1661
-            'PRT_ID'                => $price->get('PRT_ID'),
1662
-            'PRC_ID'                => $price->get('PRC_ID'),
1663
-            'PRC_is_default'        => $price->get('PRC_is_default'),
1664
-        );
1665
-        // make sure we have default start and end dates if skeleton
1666
-        // handle rows that should NOT be empty
1667
-        if (empty($template_args['TKT_start_date'])) {
1668
-            // if empty then the start date will be now.
1669
-            $template_args['TKT_start_date'] = date('Y-m-d h:i a', current_time('timestamp'));
1670
-        }
1671
-        if (empty($template_args['TKT_end_date'])) {
1672
-            // get the earliest datetime (if present);
1673
-            $earliest_dtt = $this->_cpt_model_obj->ID() > 0
1674
-                ? $this->_cpt_model_obj->get_first_related(
1675
-                    'Datetime',
1676
-                    array('order_by' => array('DTT_EVT_start' => 'ASC'))
1677
-                )
1678
-                : null;
1679
-            if (! empty($earliest_dtt)) {
1680
-                $template_args['TKT_end_date'] = $earliest_dtt->get_datetime('DTT_EVT_start', 'Y-m-d', 'h:i a');
1681
-            } else {
1682
-                $template_args['TKT_end_date'] = date(
1683
-                    'Y-m-d h:i a',
1684
-                    mktime(0, 0, 0, date("m"), date("d") + 7, date("Y"))
1685
-                );
1686
-            }
1687
-        }
1688
-        $template_args = array_merge($template_args, $price_args);
1689
-        $template = apply_filters(
1690
-            'FHEE__Events_Admin_Page__get_ticket_row__template',
1691
-            EVENTS_TEMPLATE_PATH . 'event_tickets_metabox_ticket_row.template.php',
1692
-            $ticket
1693
-        );
1694
-        return EEH_Template::display_template($template, $template_args, true);
1695
-    }
1696
-
1697
-
1698
-    /**
1699
-     * @throws DomainException
1700
-     */
1701
-    public function registration_options_meta_box()
1702
-    {
1703
-        $yes_no_values = array(
1704
-            array('id' => true, 'text' => esc_html__('Yes', 'event_espresso')),
1705
-            array('id' => false, 'text' => esc_html__('No', 'event_espresso')),
1706
-        );
1707
-        $default_reg_status_values = EEM_Registration::reg_status_array(
1708
-            array(
1709
-                EEM_Registration::status_id_cancelled,
1710
-                EEM_Registration::status_id_declined,
1711
-                EEM_Registration::status_id_incomplete,
1712
-            ),
1713
-            true
1714
-        );
1715
-        // $template_args['is_active_select'] = EEH_Form_Fields::select_input('is_active', $yes_no_values, $this->_cpt_model_obj->is_active());
1716
-        $template_args['_event'] = $this->_cpt_model_obj;
1717
-        $template_args['active_status'] = $this->_cpt_model_obj->pretty_active_status(false);
1718
-        $template_args['additional_limit'] = $this->_cpt_model_obj->additional_limit();
1719
-        $template_args['default_registration_status'] = EEH_Form_Fields::select_input(
1720
-            'default_reg_status',
1721
-            $default_reg_status_values,
1722
-            $this->_cpt_model_obj->default_registration_status()
1723
-        );
1724
-        $template_args['display_description'] = EEH_Form_Fields::select_input(
1725
-            'display_desc',
1726
-            $yes_no_values,
1727
-            $this->_cpt_model_obj->display_description()
1728
-        );
1729
-        $template_args['display_ticket_selector'] = EEH_Form_Fields::select_input(
1730
-            'display_ticket_selector',
1731
-            $yes_no_values,
1732
-            $this->_cpt_model_obj->display_ticket_selector(),
1733
-            '',
1734
-            '',
1735
-            false
1736
-        );
1737
-        $template_args['additional_registration_options'] = apply_filters(
1738
-            'FHEE__Events_Admin_Page__registration_options_meta_box__additional_registration_options',
1739
-            '',
1740
-            $template_args,
1741
-            $yes_no_values,
1742
-            $default_reg_status_values
1743
-        );
1744
-        EEH_Template::display_template(
1745
-            EVENTS_TEMPLATE_PATH . 'event_registration_options.template.php',
1746
-            $template_args
1747
-        );
1748
-    }
1749
-
1750
-
1751
-    /**
1752
-     * _get_events()
1753
-     * This method simply returns all the events (for the given _view and paging)
1754
-     *
1755
-     * @access public
1756
-     * @param int  $per_page     count of items per page (20 default);
1757
-     * @param int  $current_page what is the current page being viewed.
1758
-     * @param bool $count        if TRUE then we just return a count of ALL events matching the given _view.
1759
-     *                           If FALSE then we return an array of event objects
1760
-     *                           that match the given _view and paging parameters.
1761
-     * @return array an array of event objects.
1762
-     */
1763
-    public function get_events($per_page = 10, $current_page = 1, $count = false)
1764
-    {
1765
-        $EEME = $this->_event_model();
1766
-        $offset = ($current_page - 1) * $per_page;
1767
-        $limit = $count ? null : $offset . ',' . $per_page;
1768
-        $orderby = isset($this->_req_data['orderby']) ? $this->_req_data['orderby'] : 'EVT_ID';
1769
-        $order = isset($this->_req_data['order']) ? $this->_req_data['order'] : "DESC";
1770
-        if (isset($this->_req_data['month_range'])) {
1771
-            $pieces = explode(' ', $this->_req_data['month_range'], 3);
1772
-            // simulate the FIRST day of the month, that fixes issues for months like February
1773
-            // where PHP doesn't know what to assume for date.
1774
-            // @see https://events.codebasehq.com/projects/event-espresso/tickets/10437
1775
-            $month_r = ! empty($pieces[0]) ? date('m', \EEH_DTT_Helper::first_of_month_timestamp($pieces[0])) : '';
1776
-            $year_r = ! empty($pieces[1]) ? $pieces[1] : '';
1777
-        }
1778
-        $where = array();
1779
-        $status = isset($this->_req_data['status']) ? $this->_req_data['status'] : null;
1780
-        // determine what post_status our condition will have for the query.
1781
-        switch ($status) {
1782
-            case 'month':
1783
-            case 'today':
1784
-            case null:
1785
-            case 'all':
1786
-                break;
1787
-            case 'draft':
1788
-                $where['status'] = array('IN', array('draft', 'auto-draft'));
1789
-                break;
1790
-            default:
1791
-                $where['status'] = $status;
1792
-        }
1793
-        // categories?
1794
-        $category = isset($this->_req_data['EVT_CAT']) && $this->_req_data['EVT_CAT'] > 0
1795
-            ? $this->_req_data['EVT_CAT'] : null;
1796
-        if (! empty($category)) {
1797
-            $where['Term_Taxonomy.taxonomy'] = EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY;
1798
-            $where['Term_Taxonomy.term_id'] = $category;
1799
-        }
1800
-        // date where conditions
1801
-        $start_formats = EEM_Datetime::instance()->get_formats_for('DTT_EVT_start');
1802
-        if (isset($this->_req_data['month_range']) && $this->_req_data['month_range'] != '') {
1803
-            $DateTime = new DateTime(
1804
-                $year_r . '-' . $month_r . '-01 00:00:00',
1805
-                new DateTimeZone(EEM_Datetime::instance()->get_timezone())
1806
-            );
1807
-            $start = $DateTime->format(implode(' ', $start_formats));
1808
-            $end = $DateTime->setDate(
1809
-                $year_r,
1810
-                $month_r,
1811
-                $DateTime
1812
-                    ->format('t')
1813
-            )->setTime(23, 59, 59)
1814
-                            ->format(implode(' ', $start_formats));
1815
-            $where['Datetime.DTT_EVT_start'] = array('BETWEEN', array($start, $end));
1816
-        } elseif (isset($this->_req_data['status']) && $this->_req_data['status'] == 'today') {
1817
-            $DateTime = new DateTime('now', new DateTimeZone(EEM_Event::instance()->get_timezone()));
1818
-            $start = $DateTime->setTime(0, 0, 0)->format(implode(' ', $start_formats));
1819
-            $end = $DateTime->setTime(23, 59, 59)->format(implode(' ', $start_formats));
1820
-            $where['Datetime.DTT_EVT_start'] = array('BETWEEN', array($start, $end));
1821
-        } elseif (isset($this->_req_data['status']) && $this->_req_data['status'] == 'month') {
1822
-            $now = date('Y-m-01');
1823
-            $DateTime = new DateTime($now, new DateTimeZone(EEM_Event::instance()->get_timezone()));
1824
-            $start = $DateTime->setTime(0, 0, 0)->format(implode(' ', $start_formats));
1825
-            $end = $DateTime->setDate(date('Y'), date('m'), $DateTime->format('t'))
1826
-                            ->setTime(23, 59, 59)
1827
-                            ->format(implode(' ', $start_formats));
1828
-            $where['Datetime.DTT_EVT_start'] = array('BETWEEN', array($start, $end));
1829
-        }
1830
-        if (! EE_Registry::instance()->CAP->current_user_can('ee_read_others_events', 'get_events')) {
1831
-            $where['EVT_wp_user'] = get_current_user_id();
1832
-        } else {
1833
-            if (! isset($where['status'])) {
1834
-                if (! EE_Registry::instance()->CAP->current_user_can('ee_read_private_events', 'get_events')) {
1835
-                    $where['OR'] = array(
1836
-                        'status*restrict_private' => array('!=', 'private'),
1837
-                        'AND'                     => array(
1838
-                            'status*inclusive' => array('=', 'private'),
1839
-                            'EVT_wp_user'      => get_current_user_id(),
1840
-                        ),
1841
-                    );
1842
-                }
1843
-            }
1844
-        }
1845
-        if (isset($this->_req_data['EVT_wp_user'])) {
1846
-            if ($this->_req_data['EVT_wp_user'] != get_current_user_id()
1847
-                && EE_Registry::instance()->CAP->current_user_can('ee_read_others_events', 'get_events')
1848
-            ) {
1849
-                $where['EVT_wp_user'] = $this->_req_data['EVT_wp_user'];
1850
-            }
1851
-        }
1852
-        // search query handling
1853
-        if (isset($this->_req_data['s'])) {
1854
-            $search_string = '%' . $this->_req_data['s'] . '%';
1855
-            $where['OR'] = array(
1856
-                'EVT_name'       => array('LIKE', $search_string),
1857
-                'EVT_desc'       => array('LIKE', $search_string),
1858
-                'EVT_short_desc' => array('LIKE', $search_string),
1859
-            );
1860
-        }
1861
-        // filter events by venue.
1862
-        if (isset($this->_req_data['venue']) && ! empty($this->_req_data['venue'])) {
1863
-            $where['Venue.VNU_ID'] = absint($this->_req_data['venue']);
1864
-        }
1865
-        $where = apply_filters('FHEE__Events_Admin_Page__get_events__where', $where, $this->_req_data);
1866
-        $query_params = apply_filters(
1867
-            'FHEE__Events_Admin_Page__get_events__query_params',
1868
-            array(
1869
-                $where,
1870
-                'limit'    => $limit,
1871
-                'order_by' => $orderby,
1872
-                'order'    => $order,
1873
-                'group_by' => 'EVT_ID',
1874
-            ),
1875
-            $this->_req_data
1876
-        );
1877
-        // let's first check if we have special requests coming in.
1878
-        if (isset($this->_req_data['active_status'])) {
1879
-            switch ($this->_req_data['active_status']) {
1880
-                case 'upcoming':
1881
-                    return $EEME->get_upcoming_events($query_params, $count);
1882
-                    break;
1883
-                case 'expired':
1884
-                    return $EEME->get_expired_events($query_params, $count);
1885
-                    break;
1886
-                case 'active':
1887
-                    return $EEME->get_active_events($query_params, $count);
1888
-                    break;
1889
-                case 'inactive':
1890
-                    return $EEME->get_inactive_events($query_params, $count);
1891
-                    break;
1892
-            }
1893
-        }
1894
-
1895
-        $events = $count ? $EEME->count(array($where), 'EVT_ID', true) : $EEME->get_all($query_params);
1896
-        return $events;
1897
-    }
1898
-
1899
-
1900
-    /**
1901
-     * handling for WordPress CPT actions (trash, restore, delete)
1902
-     *
1903
-     * @param string $post_id
1904
-     */
1905
-    public function trash_cpt_item($post_id)
1906
-    {
1907
-        $this->_req_data['EVT_ID'] = $post_id;
1908
-        $this->_trash_or_restore_event('trash', false);
1909
-    }
1910
-
1911
-
1912
-    /**
1913
-     * @param string $post_id
1914
-     */
1915
-    public function restore_cpt_item($post_id)
1916
-    {
1917
-        $this->_req_data['EVT_ID'] = $post_id;
1918
-        $this->_trash_or_restore_event('draft', false);
1919
-    }
1920
-
1921
-
1922
-    /**
1923
-     * @param string $post_id
1924
-     */
1925
-    public function delete_cpt_item($post_id)
1926
-    {
1927
-        throw new EE_Error(esc_html__('Please contact Event Espresso support with the details of the steps taken to produce this error.', 'event_espresso'));
1928
-        $this->_req_data['EVT_ID'] = $post_id;
1929
-        $this->_delete_event();
1930
-    }
1931
-
1932
-
1933
-    /**
1934
-     * _trash_or_restore_event
1935
-     *
1936
-     * @access protected
1937
-     * @param  string $event_status
1938
-     * @param bool    $redirect_after
1939
-     */
1940
-    protected function _trash_or_restore_event($event_status = 'trash', $redirect_after = true)
1941
-    {
1942
-        // determine the event id and set to array.
1943
-        $EVT_ID = isset($this->_req_data['EVT_ID']) ? absint($this->_req_data['EVT_ID']) : false;
1944
-        // loop thru events
1945
-        if ($EVT_ID) {
1946
-            // clean status
1947
-            $event_status = sanitize_key($event_status);
1948
-            // grab status
1949
-            if (! empty($event_status)) {
1950
-                $success = $this->_change_event_status($EVT_ID, $event_status);
1951
-            } else {
1952
-                $success = false;
1953
-                $msg = esc_html__(
1954
-                    'An error occurred. The event could not be moved to the trash because a valid event status was not not supplied.',
1955
-                    'event_espresso'
1956
-                );
1957
-                EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1958
-            }
1959
-        } else {
1960
-            $success = false;
1961
-            $msg = esc_html__(
1962
-                'An error occurred. The event could not be moved to the trash because a valid event ID was not not supplied.',
1963
-                'event_espresso'
1964
-            );
1965
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1966
-        }
1967
-        $action = $event_status == 'trash' ? 'moved to the trash' : 'restored from the trash';
1968
-        if ($redirect_after) {
1969
-            $this->_redirect_after_action($success, 'Event', $action, array('action' => 'default'));
1970
-        }
1971
-    }
1972
-
1973
-
1974
-    /**
1975
-     * _trash_or_restore_events
1976
-     *
1977
-     * @access protected
1978
-     * @param  string $event_status
1979
-     * @return void
1980
-     */
1981
-    protected function _trash_or_restore_events($event_status = 'trash')
1982
-    {
1983
-        // clean status
1984
-        $event_status = sanitize_key($event_status);
1985
-        // grab status
1986
-        if (! empty($event_status)) {
1987
-            $success = true;
1988
-            // determine the event id and set to array.
1989
-            $EVT_IDs = isset($this->_req_data['EVT_IDs']) ? (array) $this->_req_data['EVT_IDs'] : array();
1990
-            // loop thru events
1991
-            foreach ($EVT_IDs as $EVT_ID) {
1992
-                if ($EVT_ID = absint($EVT_ID)) {
1993
-                    $results = $this->_change_event_status($EVT_ID, $event_status);
1994
-                    $success = $results !== false ? $success : false;
1995
-                } else {
1996
-                    $msg = sprintf(
1997
-                        esc_html__(
1998
-                            'An error occurred. Event #%d could not be moved to the trash because a valid event ID was not not supplied.',
1999
-                            'event_espresso'
2000
-                        ),
2001
-                        $EVT_ID
2002
-                    );
2003
-                    EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2004
-                    $success = false;
2005
-                }
2006
-            }
2007
-        } else {
2008
-            $success = false;
2009
-            $msg = esc_html__(
2010
-                'An error occurred. The event could not be moved to the trash because a valid event status was not not supplied.',
2011
-                'event_espresso'
2012
-            );
2013
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2014
-        }
2015
-        // in order to force a pluralized result message we need to send back a success status greater than 1
2016
-        $success = $success ? 2 : false;
2017
-        $action = $event_status == 'trash' ? 'moved to the trash' : 'restored from the trash';
2018
-        $this->_redirect_after_action($success, 'Events', $action, array('action' => 'default'));
2019
-    }
2020
-
2021
-
2022
-    /**
2023
-     * _trash_or_restore_events
2024
-     *
2025
-     * @access  private
2026
-     * @param  int    $EVT_ID
2027
-     * @param  string $event_status
2028
-     * @return bool
2029
-     */
2030
-    private function _change_event_status($EVT_ID = 0, $event_status = '')
2031
-    {
2032
-        // grab event id
2033
-        if (! $EVT_ID) {
2034
-            $msg = esc_html__(
2035
-                'An error occurred. No Event ID or an invalid Event ID was received.',
2036
-                'event_espresso'
2037
-            );
2038
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2039
-            return false;
2040
-        }
2041
-        $this->_cpt_model_obj = EEM_Event::instance()->get_one_by_ID($EVT_ID);
2042
-        // clean status
2043
-        $event_status = sanitize_key($event_status);
2044
-        // grab status
2045
-        if (empty($event_status)) {
2046
-            $msg = esc_html__(
2047
-                'An error occurred. No Event Status or an invalid Event Status was received.',
2048
-                'event_espresso'
2049
-            );
2050
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2051
-            return false;
2052
-        }
2053
-        // was event trashed or restored ?
2054
-        switch ($event_status) {
2055
-            case 'draft':
2056
-                $action = 'restored from the trash';
2057
-                $hook = 'AHEE_event_restored_from_trash';
2058
-                break;
2059
-            case 'trash':
2060
-                $action = 'moved to the trash';
2061
-                $hook = 'AHEE_event_moved_to_trash';
2062
-                break;
2063
-            default:
2064
-                $action = 'updated';
2065
-                $hook = false;
2066
-        }
2067
-        // use class to change status
2068
-        $this->_cpt_model_obj->set_status($event_status);
2069
-        $success = $this->_cpt_model_obj->save();
2070
-        if ($success === false) {
2071
-            $msg = sprintf(esc_html__('An error occurred. The event could not be %s.', 'event_espresso'), $action);
2072
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2073
-            return false;
2074
-        }
2075
-        if ($hook) {
2076
-            do_action($hook);
2077
-        }
2078
-        return true;
2079
-    }
2080
-
2081
-
2082
-    /**
2083
-     * _delete_event
2084
-     *
2085
-     * @access protected
2086
-     * @param bool $redirect_after
2087
-     */
2088
-    protected function _delete_event()
2089
-    {
2090
-        $this->generateDeletionPreview(isset($this->_req_data['EVT_ID']) ? $this->_req_data['EVT_ID'] : array());
2091
-    }
2092
-
2093
-
2094
-    /**
2095
-     * _delete_events
2096
-     *
2097
-     * @access protected
2098
-     * @return void
2099
-     */
2100
-    protected function _delete_events()
2101
-    {
2102
-        $this->generateDeletionPreview(isset($this->_req_data['EVT_IDs']) ? (array) $this->_req_data['EVT_IDs'] : array());
2103
-    }
2104
-
2105
-    protected function generateDeletionPreview($event_ids)
2106
-    {
2107
-        $event_ids = (array) $event_ids;
2108
-        // Set a code we can use to reference this deletion task in the batch jobs and preview page.
2109
-        $deletion_job_code = wp_generate_password(6, false);
2110
-        $return_url = EE_Admin_Page::add_query_args_and_nonce(
2111
-            [
2112
-                'action' => 'preview_deletion',
2113
-                'deletion_job_code' => $deletion_job_code,
2114
-            ],
2115
-            $this->_admin_base_url
2116
-        );
2117
-        $event_ids = array_map(
2118
-            'intval',
2119
-            $event_ids
2120
-        );
2121
-
2122
-        EEH_URL::safeRedirectAndExit(
2123
-            EE_Admin_Page::add_query_args_and_nonce(
2124
-                array(
2125
-                    'page'        => 'espresso_batch',
2126
-                    'batch'       => EED_Batch::batch_job,
2127
-                    'EVT_IDs'      => $event_ids,
2128
-                    'deletion_job_code' => $deletion_job_code,
2129
-                    'job_handler' => urlencode('EventEspressoBatchRequest\JobHandlers\PreviewEventDeletion'),
2130
-                    'return_url'  => urlencode($return_url),
2131
-                ),
2132
-                admin_url()
2133
-            )
2134
-        );
2135
-    }
2136
-
2137
-    /**
2138
-     * Checks for a POST submission
2139
-     * @since $VID:$
2140
-     */
2141
-    protected function confirmDeletion()
2142
-    {
2143
-        $deletion_job_code = isset($this->_req_data['deletion_job_code']) ? $this->_req_data['deletion_job_code'] : '';
2144
-        $models_and_ids_to_delete = $this->getModelsAndIdsToDelete($deletion_job_code);
2145
-        $form = new ConfirmEventDeletionForm($models_and_ids_to_delete['Event']);
2146
-        // Initialize the form from the request, and check if its valid.
2147
-        $form->receive_form_submission($this->_req_data);
2148
-        if ($form->is_valid()) {
2149
-            // Redirect the user to the deletion batch job.
2150
-            EEH_URL::safeRedirectAndExit(
2151
-                EE_Admin_Page::add_query_args_and_nonce(
2152
-                    array(
2153
-                        'page'        => 'espresso_batch',
2154
-                        'batch'       => EED_Batch::batch_job,
2155
-                        'deletion_job_code' => $deletion_job_code,
2156
-                        'job_handler' => urlencode('EventEspressoBatchRequest\JobHandlers\ExecuteBatchDeletion'),
2157
-                        'return_url'  => urlencode(
2158
-                            add_query_arg(
2159
-                                [
2160
-                                    'status' => 'trash'
2161
-                                ],
2162
-                                EVENTS_ADMIN_URL
2163
-                            )
2164
-                        )
2165
-                    ),
2166
-                    admin_url()
2167
-                )
2168
-            );
2169
-        } else {
2170
-            // Dont' use $form->submission_error_message() because it adds the form input's label in front
2171
-            // of each validation error which ends up looking quite confusing.
2172
-            $validation_errors = $form->get_validation_errors_accumulated();
2173
-            foreach ($validation_errors as $validation_error) {
2174
-                 EE_Error::add_error(
2175
-                     $validation_error->getMessage(),
2176
-                     __FILE__,
2177
-                     __FUNCTION__,
2178
-                     __LINE__
2179
-                 );
2180
-            }
2181
-
2182
-            EEH_URL::safeRedirectAndExit(
2183
-                EE_Admin_Page::add_query_args_and_nonce(
2184
-                    [
2185
-                        'action' => 'preview_deletion',
2186
-                        'deletion_job_code' => $deletion_job_code
2187
-                    ],
2188
-                    $this->admin_base_url()
2189
-                )
2190
-            );
2191
-        }
2192
-    }
2193
-
2194
-    /**
2195
-     * Uses the querystring and job option to figure out what we intend to delete.
2196
-     * @since $VID:$
2197
-     * @return array top-level keys are model names, and their values are arrays of IDs.
2198
-     * @throws EE_Error
2199
-     * @throws InvalidArgumentException
2200
-     * @throws InvalidDataTypeException
2201
-     * @throws InvalidInterfaceException
2202
-     * @throws ReflectionException
2203
-     * @throws UnexpectedEntityException
2204
-     */
2205
-    protected function getModelsAndIdsToDelete($deletion_job_code)
2206
-    {
2207
-        if (! $deletion_job_code) {
2208
-            throw new Exception(esc_html__('We aren’t sure which job you are performing. Please press back in your browser and try again.', 'event_espresso'));
2209
-        }
2210
-        $deletion_data = get_option('ee_deletion_' . $deletion_job_code, []);
2211
-
2212
-        $models_and_ids_to_delete = [];
2213
-        foreach ($deletion_data as $root) {
2214
-            if (! $root instanceof ModelObjNode) {
2215
-                throw new UnexpectedEntityException($root, 'ModelObjNode');
2216
-            }
2217
-            $models_and_ids_to_delete = array_replace_recursive($models_and_ids_to_delete, $root->getIds());
2218
-        }
2219
-        return $models_and_ids_to_delete;
2220
-    }
2221
-
2222
-    /**
2223
-     * A page for users to preview what exactly will be deleted, and confirm they want to delete it.
2224
-     * @since $VID:$
2225
-     * @throws EE_Error
2226
-     */
2227
-    protected function previewDeletion()
2228
-    {
2229
-        $deletion_job_code = isset($this->_req_data['deletion_job_code']) ? $this->_req_data['deletion_job_code'] : '';
2230
-        $models_and_ids_to_delete = $this->getModelsAndIdsToDelete($deletion_job_code);
2231
-        $event_ids = isset($models_and_ids_to_delete['Event']) ? $models_and_ids_to_delete['Event'] : array();
2232
-        if (empty($event_ids) || ! is_array($event_ids)) {
2233
-            throw new EE_Error(
2234
-                esc_html__('No Events were found to delete.', 'event_espresso')
2235
-            );
2236
-        }
2237
-        $datetime_ids = isset($models_and_ids_to_delete['Datetime']) ? $models_and_ids_to_delete['Datetime'] : array();
2238
-        if (! is_array($datetime_ids)) {
2239
-            throw new UnexpectedEntityException($datetime_ids, 'array');
2240
-        }
2241
-        $registration_ids = isset($models_and_ids_to_delete['Registration']) ? $models_and_ids_to_delete['Registration'] : array();
2242
-        if (! is_array($registration_ids)) {
2243
-            throw new UnexpectedEntityException($registration_ids, 'array');
2244
-        }
2245
-        $num_registrations_to_show = 10;
2246
-        $reg_count = count($registration_ids);
2247
-        if ($reg_count > $num_registrations_to_show) {
2248
-            $registration_ids = array_slice($registration_ids, 0, $num_registrations_to_show);
2249
-        }
2250
-        $form = new ConfirmEventDeletionForm($event_ids);
2251
-        $events = EEM_Event::instance()->get_all_deleted_and_undeleted(
2252
-            [
2253
-                [
2254
-                    'EVT_ID' => ['IN', $event_ids]
2255
-                ]
2256
-            ]
2257
-        );
2258
-        $datetimes = EEM_Datetime::instance()->get_all_deleted_and_undeleted(
2259
-            [
2260
-                [
2261
-                    'DTT_ID' => ['IN', $datetime_ids]
2262
-                ]
2263
-            ]
2264
-        );
2265
-        $registrations = EEM_Registration::instance()->get_all_deleted_and_undeleted(
2266
-            [
2267
-                [
2268
-                    'REG_ID' => ['IN', $registration_ids]
2269
-                ]
2270
-            ]
2271
-        );
2272
-        $confirm_deletion_args = [
2273
-            'action' => 'confirm_deletion',
2274
-            'deletion_job_code' => $deletion_job_code
2275
-        ];
2276
-
2277
-        $this->_template_args['admin_page_content'] = EEH_Template::display_template(
2278
-            EVENTS_TEMPLATE_PATH . 'event_preview_deletion.template.php',
2279
-            [
2280
-                'form_url' => EE_Admin_Page::add_query_args_and_nonce(
2281
-                    $confirm_deletion_args,
2282
-                    $this->admin_base_url()
2283
-                ),
2284
-                'form' => $form,
2285
-                'events' => $events,
2286
-                'datetimes' => $datetimes,
2287
-                'registrations' => $registrations,
2288
-                'reg_count' => $reg_count,
2289
-                'num_registrations_to_show' => $num_registrations_to_show
2290
-            ],
2291
-            true
2292
-        );
2293
-        $this->display_admin_page_with_no_sidebar();
2294
-    }
2295
-
2296
-    /**
2297
-     * _permanently_delete_event
2298
-     *
2299
-     * @access  private
2300
-     * @param  int $EVT_ID
2301
-     * @return bool
2302
-     */
2303
-    private function _permanently_delete_event($EVT_ID = 0)
2304
-    {
2305
-        // grab event id
2306
-        if (! $EVT_ID) {
2307
-            $msg = esc_html__(
2308
-                'An error occurred. No Event ID or an invalid Event ID was received.',
2309
-                'event_espresso'
2310
-            );
2311
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2312
-            return false;
2313
-        }
2314
-        if (! $this->_cpt_model_obj instanceof EE_Event
2315
-            || $this->_cpt_model_obj->ID() !== $EVT_ID
2316
-        ) {
2317
-            $this->_cpt_model_obj = EEM_Event::instance()->get_one_by_ID($EVT_ID);
2318
-        }
2319
-        if (! $this->_cpt_model_obj instanceof EE_Event) {
2320
-            return false;
2321
-        }
2322
-        // need to delete related tickets and prices first.
2323
-        $datetimes = $this->_cpt_model_obj->get_many_related('Datetime');
2324
-        foreach ($datetimes as $datetime) {
2325
-            $this->_cpt_model_obj->_remove_relation_to($datetime, 'Datetime');
2326
-            $tickets = $datetime->get_many_related('Ticket');
2327
-            foreach ($tickets as $ticket) {
2328
-                $ticket->_remove_relation_to($datetime, 'Datetime');
2329
-                $ticket->delete_related_permanently('Price');
2330
-                $ticket->delete_permanently();
2331
-            }
2332
-            $datetime->delete();
2333
-        }
2334
-        // what about related venues or terms?
2335
-        $venues = $this->_cpt_model_obj->get_many_related('Venue');
2336
-        foreach ($venues as $venue) {
2337
-            $this->_cpt_model_obj->_remove_relation_to($venue, 'Venue');
2338
-        }
2339
-        // any attached question groups?
2340
-        $question_groups = $this->_cpt_model_obj->get_many_related('Question_Group');
2341
-        if (! empty($question_groups)) {
2342
-            foreach ($question_groups as $question_group) {
2343
-                $this->_cpt_model_obj->_remove_relation_to($question_group, 'Question_Group');
2344
-            }
2345
-        }
2346
-        // Message Template Groups
2347
-        $this->_cpt_model_obj->_remove_relations('Message_Template_Group');
2348
-        /** @type EE_Term_Taxonomy[] $term_taxonomies */
2349
-        $term_taxonomies = $this->_cpt_model_obj->term_taxonomies();
2350
-        foreach ($term_taxonomies as $term_taxonomy) {
2351
-            $this->_cpt_model_obj->remove_relation_to_term_taxonomy($term_taxonomy);
2352
-        }
2353
-        $success = $this->_cpt_model_obj->delete_permanently();
2354
-        // did it all go as planned ?
2355
-        if ($success) {
2356
-            $msg = sprintf(esc_html__('Event ID # %d has been deleted.', 'event_espresso'), $EVT_ID);
2357
-            EE_Error::add_success($msg);
2358
-        } else {
2359
-            $msg = sprintf(
2360
-                esc_html__('An error occurred. Event ID # %d could not be deleted.', 'event_espresso'),
2361
-                $EVT_ID
2362
-            );
2363
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2364
-            return false;
2365
-        }
2366
-        do_action('AHEE__Events_Admin_Page___permanently_delete_event__after_event_deleted', $EVT_ID);
2367
-        return true;
2368
-    }
2369
-
2370
-
2371
-    /**
2372
-     * get total number of events
2373
-     *
2374
-     * @access public
2375
-     * @return int
2376
-     */
2377
-    public function total_events()
2378
-    {
2379
-        $count = EEM_Event::instance()->count(array('caps' => 'read_admin'), 'EVT_ID', true);
2380
-        return $count;
2381
-    }
2382
-
2383
-
2384
-    /**
2385
-     * get total number of draft events
2386
-     *
2387
-     * @access public
2388
-     * @return int
2389
-     */
2390
-    public function total_events_draft()
2391
-    {
2392
-        $where = array(
2393
-            'status' => array('IN', array('draft', 'auto-draft')),
2394
-        );
2395
-        $count = EEM_Event::instance()->count(array($where, 'caps' => 'read_admin'), 'EVT_ID', true);
2396
-        return $count;
2397
-    }
2398
-
2399
-
2400
-    /**
2401
-     * get total number of trashed events
2402
-     *
2403
-     * @access public
2404
-     * @return int
2405
-     */
2406
-    public function total_trashed_events()
2407
-    {
2408
-        $where = array(
2409
-            'status' => 'trash',
2410
-        );
2411
-        $count = EEM_Event::instance()->count(array($where, 'caps' => 'read_admin'), 'EVT_ID', true);
2412
-        return $count;
2413
-    }
2414
-
2415
-
2416
-    /**
2417
-     *    _default_event_settings
2418
-     *    This generates the Default Settings Tab
2419
-     *
2420
-     * @return void
2421
-     * @throws EE_Error
2422
-     */
2423
-    protected function _default_event_settings()
2424
-    {
2425
-        $this->_set_add_edit_form_tags('update_default_event_settings');
2426
-        $this->_set_publish_post_box_vars(null, false, false, null, false);
2427
-        $this->_template_args['admin_page_content'] = $this->_default_event_settings_form()->get_html();
2428
-        $this->display_admin_page_with_sidebar();
2429
-    }
2430
-
2431
-
2432
-    /**
2433
-     * Return the form for event settings.
2434
-     *
2435
-     * @return EE_Form_Section_Proper
2436
-     * @throws EE_Error
2437
-     */
2438
-    protected function _default_event_settings_form()
2439
-    {
2440
-        $registration_config = EE_Registry::instance()->CFG->registration;
2441
-        $registration_stati_for_selection = EEM_Registration::reg_status_array(
2442
-            // exclude
2443
-            array(
2444
-                EEM_Registration::status_id_cancelled,
2445
-                EEM_Registration::status_id_declined,
2446
-                EEM_Registration::status_id_incomplete,
2447
-                EEM_Registration::status_id_wait_list,
2448
-            ),
2449
-            true
2450
-        );
2451
-        return new EE_Form_Section_Proper(
2452
-            array(
2453
-                'name'            => 'update_default_event_settings',
2454
-                'html_id'         => 'update_default_event_settings',
2455
-                'html_class'      => 'form-table',
2456
-                'layout_strategy' => new EE_Admin_Two_Column_Layout(),
2457
-                'subsections'     => apply_filters(
2458
-                    'FHEE__Events_Admin_Page___default_event_settings_form__form_subsections',
2459
-                    array(
2460
-                        'default_reg_status'  => new EE_Select_Input(
2461
-                            $registration_stati_for_selection,
2462
-                            array(
2463
-                                'default'         => isset($registration_config->default_STS_ID)
2464
-                                                     && array_key_exists(
2465
-                                                         $registration_config->default_STS_ID,
2466
-                                                         $registration_stati_for_selection
2467
-                                                     )
2468
-                                    ? sanitize_text_field($registration_config->default_STS_ID)
2469
-                                    : EEM_Registration::status_id_pending_payment,
2470
-                                'html_label_text' => esc_html__('Default Registration Status', 'event_espresso')
2471
-                                                     . EEH_Template::get_help_tab_link(
2472
-                                                         'default_settings_status_help_tab'
2473
-                                                     ),
2474
-                                'html_help_text'  => esc_html__(
2475
-                                    'This setting allows you to preselect what the default registration status setting is when creating an event.  Note that changing this setting does NOT retroactively apply it to existing events.',
2476
-                                    'event_espresso'
2477
-                                ),
2478
-                            )
2479
-                        ),
2480
-                        'default_max_tickets' => new EE_Integer_Input(
2481
-                            array(
2482
-                                'default'         => isset($registration_config->default_maximum_number_of_tickets)
2483
-                                    ? $registration_config->default_maximum_number_of_tickets
2484
-                                    : EEM_Event::get_default_additional_limit(),
2485
-                                'html_label_text' => esc_html__(
2486
-                                    'Default Maximum Tickets Allowed Per Order:',
2487
-                                    'event_espresso'
2488
-                                )
2489
-                                                     . EEH_Template::get_help_tab_link(
2490
-                                                         'default_maximum_tickets_help_tab"'
2491
-                                                     ),
2492
-                                'html_help_text'  => esc_html__(
2493
-                                    'This setting allows you to indicate what will be the default for the maximum number of tickets per order when creating new events.',
2494
-                                    'event_espresso'
2495
-                                ),
2496
-                            )
2497
-                        ),
2498
-                    )
2499
-                ),
2500
-            )
2501
-        );
2502
-    }
2503
-
2504
-
2505
-    /**
2506
-     * _update_default_event_settings
2507
-     *
2508
-     * @access protected
2509
-     * @return void
2510
-     * @throws EE_Error
2511
-     */
2512
-    protected function _update_default_event_settings()
2513
-    {
2514
-        $registration_config = EE_Registry::instance()->CFG->registration;
2515
-        $form = $this->_default_event_settings_form();
2516
-        if ($form->was_submitted()) {
2517
-            $form->receive_form_submission();
2518
-            if ($form->is_valid()) {
2519
-                $valid_data = $form->valid_data();
2520
-                if (isset($valid_data['default_reg_status'])) {
2521
-                    $registration_config->default_STS_ID = $valid_data['default_reg_status'];
2522
-                }
2523
-                if (isset($valid_data['default_max_tickets'])) {
2524
-                    $registration_config->default_maximum_number_of_tickets = $valid_data['default_max_tickets'];
2525
-                }
2526
-                // update because data was valid!
2527
-                EE_Registry::instance()->CFG->update_espresso_config();
2528
-                EE_Error::overwrite_success();
2529
-                EE_Error::add_success(
2530
-                    __('Default Event Settings were updated', 'event_espresso')
2531
-                );
2532
-            }
2533
-        }
2534
-        $this->_redirect_after_action(0, '', '', array('action' => 'default_event_settings'), true);
2535
-    }
2536
-
2537
-
2538
-    /*************        Templates        *************/
2539
-    protected function _template_settings()
2540
-    {
2541
-        $this->_admin_page_title = esc_html__('Template Settings (Preview)', 'event_espresso');
2542
-        $this->_template_args['preview_img'] = '<img src="'
2543
-                                               . EVENTS_ASSETS_URL
2544
-                                               . '/images/'
2545
-                                               . 'caffeinated_template_features.jpg" alt="'
2546
-                                               . esc_attr__('Template Settings Preview screenshot', 'event_espresso')
2547
-                                               . '" />';
2548
-        $this->_template_args['preview_text'] = '<strong>'
2549
-                                                . esc_html__(
2550
-                                                    'Template Settings is a feature that is only available in the premium version of Event Espresso 4 which is available with a support license purchase on EventEspresso.com. Template Settings allow you to configure some of the appearance options for both the Event List and Event Details pages.',
2551
-                                                    'event_espresso'
2552
-                                                ) . '</strong>';
2553
-        $this->display_admin_caf_preview_page('template_settings_tab');
2554
-    }
2555
-
2556
-
2557
-    /** Event Category Stuff **/
2558
-    /**
2559
-     * set the _category property with the category object for the loaded page.
2560
-     *
2561
-     * @access private
2562
-     * @return void
2563
-     */
2564
-    private function _set_category_object()
2565
-    {
2566
-        if (isset($this->_category->id) && ! empty($this->_category->id)) {
2567
-            return;
2568
-        } //already have the category object so get out.
2569
-        // set default category object
2570
-        $this->_set_empty_category_object();
2571
-        // only set if we've got an id
2572
-        if (! isset($this->_req_data['EVT_CAT_ID'])) {
2573
-            return;
2574
-        }
2575
-        $category_id = absint($this->_req_data['EVT_CAT_ID']);
2576
-        $term = get_term($category_id, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY);
2577
-        if (! empty($term)) {
2578
-            $this->_category->category_name = $term->name;
2579
-            $this->_category->category_identifier = $term->slug;
2580
-            $this->_category->category_desc = $term->description;
2581
-            $this->_category->id = $term->term_id;
2582
-            $this->_category->parent = $term->parent;
2583
-        }
2584
-    }
2585
-
2586
-
2587
-    /**
2588
-     * Clears out category properties.
2589
-     */
2590
-    private function _set_empty_category_object()
2591
-    {
2592
-        $this->_category = new stdClass();
2593
-        $this->_category->category_name = $this->_category->category_identifier = $this->_category->category_desc = '';
2594
-        $this->_category->id = $this->_category->parent = 0;
2595
-    }
2596
-
2597
-
2598
-    /**
2599
-     * @throws EE_Error
2600
-     */
2601
-    protected function _category_list_table()
2602
-    {
2603
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2604
-        $this->_search_btn_label = esc_html__('Categories', 'event_espresso');
2605
-        $this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
2606
-            'add_category',
2607
-            'add_category',
2608
-            array(),
2609
-            'add-new-h2'
2610
-        );
2611
-        $this->display_admin_list_table_page_with_sidebar();
2612
-    }
2613
-
2614
-
2615
-    /**
2616
-     * Output category details view.
2617
-     */
2618
-    protected function _category_details($view)
2619
-    {
2620
-        // load formatter helper
2621
-        // load field generator helper
2622
-        $route = $view == 'edit' ? 'update_category' : 'insert_category';
2623
-        $this->_set_add_edit_form_tags($route);
2624
-        $this->_set_category_object();
2625
-        $id = ! empty($this->_category->id) ? $this->_category->id : '';
2626
-        $delete_action = 'delete_category';
2627
-        // custom redirect
2628
-        $redirect = EE_Admin_Page::add_query_args_and_nonce(
2629
-            array('action' => 'category_list'),
2630
-            $this->_admin_base_url
2631
-        );
2632
-        $this->_set_publish_post_box_vars('EVT_CAT_ID', $id, $delete_action, $redirect);
2633
-        // take care of contents
2634
-        $this->_template_args['admin_page_content'] = $this->_category_details_content();
2635
-        $this->display_admin_page_with_sidebar();
2636
-    }
2637
-
2638
-
2639
-    /**
2640
-     * Output category details content.
2641
-     */
2642
-    protected function _category_details_content()
2643
-    {
2644
-        $editor_args['category_desc'] = array(
2645
-            'type'          => 'wp_editor',
2646
-            'value'         => EEH_Formatter::admin_format_content($this->_category->category_desc),
2647
-            'class'         => 'my_editor_custom',
2648
-            'wpeditor_args' => array('media_buttons' => false),
2649
-        );
2650
-        $_wp_editor = $this->_generate_admin_form_fields($editor_args, 'array');
2651
-        $all_terms = get_terms(
2652
-            array(EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY),
2653
-            array('hide_empty' => 0, 'exclude' => array($this->_category->id))
2654
-        );
2655
-        // setup category select for term parents.
2656
-        $category_select_values[] = array(
2657
-            'text' => esc_html__('No Parent', 'event_espresso'),
2658
-            'id'   => 0,
2659
-        );
2660
-        foreach ($all_terms as $term) {
2661
-            $category_select_values[] = array(
2662
-                'text' => $term->name,
2663
-                'id'   => $term->term_id,
2664
-            );
2665
-        }
2666
-        $category_select = EEH_Form_Fields::select_input(
2667
-            'category_parent',
2668
-            $category_select_values,
2669
-            $this->_category->parent
2670
-        );
2671
-        $template_args = array(
2672
-            'category'                 => $this->_category,
2673
-            'category_select'          => $category_select,
2674
-            'unique_id_info_help_link' => $this->_get_help_tab_link('unique_id_info'),
2675
-            'category_desc_editor'     => $_wp_editor['category_desc']['field'],
2676
-            'disable'                  => '',
2677
-            'disabled_message'         => false,
2678
-        );
2679
-        $template = EVENTS_TEMPLATE_PATH . 'event_category_details.template.php';
2680
-        return EEH_Template::display_template($template, $template_args, true);
2681
-    }
2682
-
2683
-
2684
-    /**
2685
-     * Handles deleting categories.
2686
-     */
2687
-    protected function _delete_categories()
2688
-    {
2689
-        $cat_ids = isset($this->_req_data['EVT_CAT_ID']) ? (array) $this->_req_data['EVT_CAT_ID']
2690
-            : (array) $this->_req_data['category_id'];
2691
-        foreach ($cat_ids as $cat_id) {
2692
-            $this->_delete_category($cat_id);
2693
-        }
2694
-        // doesn't matter what page we're coming from... we're going to the same place after delete.
2695
-        $query_args = array(
2696
-            'action' => 'category_list',
2697
-        );
2698
-        $this->_redirect_after_action(0, '', '', $query_args);
2699
-    }
2700
-
2701
-
2702
-    /**
2703
-     * Handles deleting specific category.
2704
-     *
2705
-     * @param int $cat_id
2706
-     */
2707
-    protected function _delete_category($cat_id)
2708
-    {
2709
-        $cat_id = absint($cat_id);
2710
-        wp_delete_term($cat_id, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY);
2711
-    }
2712
-
2713
-
2714
-    /**
2715
-     * Handles triggering the update or insertion of a new category.
2716
-     *
2717
-     * @param bool $new_category true means we're triggering the insert of a new category.
2718
-     */
2719
-    protected function _insert_or_update_category($new_category)
2720
-    {
2721
-        $cat_id = $new_category ? $this->_insert_category() : $this->_insert_category(true);
2722
-        $success = 0; // we already have a success message so lets not send another.
2723
-        if ($cat_id) {
2724
-            $query_args = array(
2725
-                'action'     => 'edit_category',
2726
-                'EVT_CAT_ID' => $cat_id,
2727
-            );
2728
-        } else {
2729
-            $query_args = array('action' => 'add_category');
2730
-        }
2731
-        $this->_redirect_after_action($success, '', '', $query_args, true);
2732
-    }
2733
-
2734
-
2735
-    /**
2736
-     * Inserts or updates category
2737
-     *
2738
-     * @param bool $update (true indicates we're updating a category).
2739
-     * @return bool|mixed|string
2740
-     */
2741
-    private function _insert_category($update = false)
2742
-    {
2743
-        $cat_id = $update ? $this->_req_data['EVT_CAT_ID'] : '';
2744
-        $category_name = isset($this->_req_data['category_name']) ? $this->_req_data['category_name'] : '';
2745
-        $category_desc = isset($this->_req_data['category_desc']) ? $this->_req_data['category_desc'] : '';
2746
-        $category_parent = isset($this->_req_data['category_parent']) ? $this->_req_data['category_parent'] : 0;
2747
-        if (empty($category_name)) {
2748
-            $msg = esc_html__('You must add a name for the category.', 'event_espresso');
2749
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2750
-            return false;
2751
-        }
2752
-        $term_args = array(
2753
-            'name'        => $category_name,
2754
-            'description' => $category_desc,
2755
-            'parent'      => $category_parent,
2756
-        );
2757
-        // was the category_identifier input disabled?
2758
-        if (isset($this->_req_data['category_identifier'])) {
2759
-            $term_args['slug'] = $this->_req_data['category_identifier'];
2760
-        }
2761
-        $insert_ids = $update
2762
-            ? wp_update_term($cat_id, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY, $term_args)
2763
-            : wp_insert_term($category_name, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY, $term_args);
2764
-        if (! is_array($insert_ids)) {
2765
-            $msg = esc_html__(
2766
-                'An error occurred and the category has not been saved to the database.',
2767
-                'event_espresso'
2768
-            );
2769
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2770
-        } else {
2771
-            $cat_id = $insert_ids['term_id'];
2772
-            $msg = sprintf(esc_html__('The category %s was successfully saved', 'event_espresso'), $category_name);
2773
-            EE_Error::add_success($msg);
2774
-        }
2775
-        return $cat_id;
2776
-    }
2777
-
2778
-
2779
-    /**
2780
-     * Gets categories or count of categories matching the arguments in the request.
2781
-     *
2782
-     * @param int  $per_page
2783
-     * @param int  $current_page
2784
-     * @param bool $count
2785
-     * @return EE_Base_Class[]|EE_Term_Taxonomy[]|int
2786
-     */
2787
-    public function get_categories($per_page = 10, $current_page = 1, $count = false)
2788
-    {
2789
-        // testing term stuff
2790
-        $orderby = isset($this->_req_data['orderby']) ? $this->_req_data['orderby'] : 'Term.term_id';
2791
-        $order = isset($this->_req_data['order']) ? $this->_req_data['order'] : 'DESC';
2792
-        $limit = ($current_page - 1) * $per_page;
2793
-        $where = array('taxonomy' => EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY);
2794
-        if (isset($this->_req_data['s'])) {
2795
-            $sstr = '%' . $this->_req_data['s'] . '%';
2796
-            $where['OR'] = array(
2797
-                'Term.name'   => array('LIKE', $sstr),
2798
-                'description' => array('LIKE', $sstr),
2799
-            );
2800
-        }
2801
-        $query_params = array(
2802
-            $where,
2803
-            'order_by'   => array($orderby => $order),
2804
-            'limit'      => $limit . ',' . $per_page,
2805
-            'force_join' => array('Term'),
2806
-        );
2807
-        $categories = $count
2808
-            ? EEM_Term_Taxonomy::instance()->count($query_params, 'term_id')
2809
-            : EEM_Term_Taxonomy::instance()->get_all($query_params);
2810
-        return $categories;
2811
-    }
2812
-
2813
-    /* end category stuff */
2814
-    /**************/
2815
-
2816
-
2817
-    /**
2818
-     * Callback for the `ee_save_timezone_setting` ajax action.
2819
-     *
2820
-     * @throws EE_Error
2821
-     */
2822
-    public function save_timezonestring_setting()
2823
-    {
2824
-        $timezone_string = isset($this->_req_data['timezone_selected'])
2825
-            ? $this->_req_data['timezone_selected']
2826
-            : '';
2827
-        if (empty($timezone_string) || ! EEH_DTT_Helper::validate_timezone($timezone_string, false)) {
2828
-            EE_Error::add_error(
2829
-                esc_html__('An invalid timezone string submitted.', 'event_espresso'),
2830
-                __FILE__,
2831
-                __FUNCTION__,
2832
-                __LINE__
2833
-            );
2834
-            $this->_template_args['error'] = true;
2835
-            $this->_return_json();
2836
-        }
2837
-
2838
-        update_option('timezone_string', $timezone_string);
2839
-        EE_Error::add_success(
2840
-            esc_html__('Your timezone string was updated.', 'event_espresso')
2841
-        );
2842
-        $this->_template_args['success'] = true;
2843
-        $this->_return_json(true, array('action' => 'create_new'));
2844
-    }
21
+	/**
22
+	 * This will hold the event object for event_details screen.
23
+	 *
24
+	 * @access protected
25
+	 * @var EE_Event $_event
26
+	 */
27
+	protected $_event;
28
+
29
+
30
+	/**
31
+	 * This will hold the category object for category_details screen.
32
+	 *
33
+	 * @var stdClass $_category
34
+	 */
35
+	protected $_category;
36
+
37
+
38
+	/**
39
+	 * This will hold the event model instance
40
+	 *
41
+	 * @var EEM_Event $_event_model
42
+	 */
43
+	protected $_event_model;
44
+
45
+
46
+	/**
47
+	 * @var EE_Event
48
+	 */
49
+	protected $_cpt_model_obj = false;
50
+
51
+	/**
52
+	 * Initialize page props for this admin page group.
53
+	 */
54
+	protected function _init_page_props()
55
+	{
56
+		$this->page_slug = EVENTS_PG_SLUG;
57
+		$this->page_label = EVENTS_LABEL;
58
+		$this->_admin_base_url = EVENTS_ADMIN_URL;
59
+		$this->_admin_base_path = EVENTS_ADMIN;
60
+		$this->_cpt_model_names = array(
61
+			'create_new' => 'EEM_Event',
62
+			'edit'       => 'EEM_Event',
63
+		);
64
+		$this->_cpt_edit_routes = array(
65
+			'espresso_events' => 'edit',
66
+		);
67
+		add_action(
68
+			'AHEE__EE_Admin_Page_CPT__set_model_object__after_set_object',
69
+			array($this, 'verify_event_edit'),
70
+			10,
71
+			2
72
+		);
73
+	}
74
+
75
+
76
+	/**
77
+	 * Sets the ajax hooks used for this admin page group.
78
+	 */
79
+	protected function _ajax_hooks()
80
+	{
81
+		add_action('wp_ajax_ee_save_timezone_setting', array($this, 'save_timezonestring_setting'));
82
+	}
83
+
84
+
85
+	/**
86
+	 * Sets the page properties for this admin page group.
87
+	 */
88
+	protected function _define_page_props()
89
+	{
90
+		$this->_admin_page_title = EVENTS_LABEL;
91
+		$this->_labels = array(
92
+			'buttons'      => array(
93
+				'add'             => esc_html__('Add New Event', 'event_espresso'),
94
+				'edit'            => esc_html__('Edit Event', 'event_espresso'),
95
+				'delete'          => esc_html__('Delete Event', 'event_espresso'),
96
+				'add_category'    => esc_html__('Add New Category', 'event_espresso'),
97
+				'edit_category'   => esc_html__('Edit Category', 'event_espresso'),
98
+				'delete_category' => esc_html__('Delete Category', 'event_espresso'),
99
+			),
100
+			'editor_title' => array(
101
+				'espresso_events' => esc_html__('Enter event title here', 'event_espresso'),
102
+			),
103
+			'publishbox'   => array(
104
+				'create_new'        => esc_html__('Save New Event', 'event_espresso'),
105
+				'edit'              => esc_html__('Update Event', 'event_espresso'),
106
+				'add_category'      => esc_html__('Save New Category', 'event_espresso'),
107
+				'edit_category'     => esc_html__('Update Category', 'event_espresso'),
108
+				'template_settings' => esc_html__('Update Settings', 'event_espresso'),
109
+			),
110
+		);
111
+	}
112
+
113
+
114
+	/**
115
+	 * Sets the page routes property for this admin page group.
116
+	 */
117
+	protected function _set_page_routes()
118
+	{
119
+		// load formatter helper
120
+		// load field generator helper
121
+		// is there a evt_id in the request?
122
+		$evt_id = ! empty($this->_req_data['EVT_ID']) && ! is_array($this->_req_data['EVT_ID'])
123
+			? $this->_req_data['EVT_ID']
124
+			: 0;
125
+		$evt_id = ! empty($this->_req_data['post']) ? $this->_req_data['post'] : $evt_id;
126
+		$this->_page_routes = array(
127
+			'default'                       => array(
128
+				'func'       => '_events_overview_list_table',
129
+				'capability' => 'ee_read_events',
130
+			),
131
+			'create_new'                    => array(
132
+				'func'       => '_create_new_cpt_item',
133
+				'capability' => 'ee_edit_events',
134
+			),
135
+			'edit'                          => array(
136
+				'func'       => '_edit_cpt_item',
137
+				'capability' => 'ee_edit_event',
138
+				'obj_id'     => $evt_id,
139
+			),
140
+			'copy_event'                    => array(
141
+				'func'       => '_copy_events',
142
+				'capability' => 'ee_edit_event',
143
+				'obj_id'     => $evt_id,
144
+				'noheader'   => true,
145
+			),
146
+			'trash_event'                   => array(
147
+				'func'       => '_trash_or_restore_event',
148
+				'args'       => array('event_status' => 'trash'),
149
+				'capability' => 'ee_delete_event',
150
+				'obj_id'     => $evt_id,
151
+				'noheader'   => true,
152
+			),
153
+			'trash_events'                  => array(
154
+				'func'       => '_trash_or_restore_events',
155
+				'args'       => array('event_status' => 'trash'),
156
+				'capability' => 'ee_delete_events',
157
+				'noheader'   => true,
158
+			),
159
+			'restore_event'                 => array(
160
+				'func'       => '_trash_or_restore_event',
161
+				'args'       => array('event_status' => 'draft'),
162
+				'capability' => 'ee_delete_event',
163
+				'obj_id'     => $evt_id,
164
+				'noheader'   => true,
165
+			),
166
+			'restore_events'                => array(
167
+				'func'       => '_trash_or_restore_events',
168
+				'args'       => array('event_status' => 'draft'),
169
+				'capability' => 'ee_delete_events',
170
+				'noheader'   => true,
171
+			),
172
+			'delete_event'                  => array(
173
+				'func'       => '_delete_event',
174
+				'capability' => 'ee_delete_event',
175
+				'obj_id'     => $evt_id,
176
+				'noheader'   => true,
177
+			),
178
+			'delete_events'                 => array(
179
+				'func'       => '_delete_events',
180
+				'capability' => 'ee_delete_events',
181
+				'noheader'   => true,
182
+			),
183
+			'view_report'                   => array(
184
+				'func'      => '_view_report',
185
+				'capablity' => 'ee_edit_events',
186
+			),
187
+			'default_event_settings'        => array(
188
+				'func'       => '_default_event_settings',
189
+				'capability' => 'manage_options',
190
+			),
191
+			'update_default_event_settings' => array(
192
+				'func'       => '_update_default_event_settings',
193
+				'capability' => 'manage_options',
194
+				'noheader'   => true,
195
+			),
196
+			'template_settings'             => array(
197
+				'func'       => '_template_settings',
198
+				'capability' => 'manage_options',
199
+			),
200
+			// event category tab related
201
+			'add_category'                  => array(
202
+				'func'       => '_category_details',
203
+				'capability' => 'ee_edit_event_category',
204
+				'args'       => array('add'),
205
+			),
206
+			'edit_category'                 => array(
207
+				'func'       => '_category_details',
208
+				'capability' => 'ee_edit_event_category',
209
+				'args'       => array('edit'),
210
+			),
211
+			'delete_categories'             => array(
212
+				'func'       => '_delete_categories',
213
+				'capability' => 'ee_delete_event_category',
214
+				'noheader'   => true,
215
+			),
216
+			'delete_category'               => array(
217
+				'func'       => '_delete_categories',
218
+				'capability' => 'ee_delete_event_category',
219
+				'noheader'   => true,
220
+			),
221
+			'insert_category'               => array(
222
+				'func'       => '_insert_or_update_category',
223
+				'args'       => array('new_category' => true),
224
+				'capability' => 'ee_edit_event_category',
225
+				'noheader'   => true,
226
+			),
227
+			'update_category'               => array(
228
+				'func'       => '_insert_or_update_category',
229
+				'args'       => array('new_category' => false),
230
+				'capability' => 'ee_edit_event_category',
231
+				'noheader'   => true,
232
+			),
233
+			'category_list'                 => array(
234
+				'func'       => '_category_list_table',
235
+				'capability' => 'ee_manage_event_categories',
236
+			),
237
+			'preview_deletion' => [
238
+				'func' => 'previewDeletion',
239
+				'capability' => 'ee_delete_events',
240
+			],
241
+			'confirm_deletion' => [
242
+				'func' => 'confirmDeletion',
243
+				'capability' => 'ee_delete_events',
244
+				'noheader' => true,
245
+			]
246
+		);
247
+	}
248
+
249
+
250
+	/**
251
+	 * Set the _page_config property for this admin page group.
252
+	 */
253
+	protected function _set_page_config()
254
+	{
255
+		$this->_page_config = array(
256
+			'default'                => array(
257
+				'nav'           => array(
258
+					'label' => esc_html__('Overview', 'event_espresso'),
259
+					'order' => 10,
260
+				),
261
+				'list_table'    => 'Events_Admin_List_Table',
262
+				'help_tabs'     => array(
263
+					'events_overview_help_tab'                       => array(
264
+						'title'    => esc_html__('Events Overview', 'event_espresso'),
265
+						'filename' => 'events_overview',
266
+					),
267
+					'events_overview_table_column_headings_help_tab' => array(
268
+						'title'    => esc_html__('Events Overview Table Column Headings', 'event_espresso'),
269
+						'filename' => 'events_overview_table_column_headings',
270
+					),
271
+					'events_overview_filters_help_tab'               => array(
272
+						'title'    => esc_html__('Events Overview Filters', 'event_espresso'),
273
+						'filename' => 'events_overview_filters',
274
+					),
275
+					'events_overview_view_help_tab'                  => array(
276
+						'title'    => esc_html__('Events Overview Views', 'event_espresso'),
277
+						'filename' => 'events_overview_views',
278
+					),
279
+					'events_overview_other_help_tab'                 => array(
280
+						'title'    => esc_html__('Events Overview Other', 'event_espresso'),
281
+						'filename' => 'events_overview_other',
282
+					),
283
+				),
284
+				'help_tour'     => array(
285
+					'Event_Overview_Help_Tour',
286
+					// 'New_Features_Test_Help_Tour' for testing multiple help tour
287
+				),
288
+				'qtips'         => array(
289
+					'EE_Event_List_Table_Tips',
290
+				),
291
+				'require_nonce' => false,
292
+			),
293
+			'create_new'             => array(
294
+				'nav'           => array(
295
+					'label'      => esc_html__('Add Event', 'event_espresso'),
296
+					'order'      => 5,
297
+					'persistent' => false,
298
+				),
299
+				'metaboxes'     => array('_register_event_editor_meta_boxes'),
300
+				'help_tabs'     => array(
301
+					'event_editor_help_tab'                            => array(
302
+						'title'    => esc_html__('Event Editor', 'event_espresso'),
303
+						'filename' => 'event_editor',
304
+					),
305
+					'event_editor_title_richtexteditor_help_tab'       => array(
306
+						'title'    => esc_html__('Event Title & Rich Text Editor', 'event_espresso'),
307
+						'filename' => 'event_editor_title_richtexteditor',
308
+					),
309
+					'event_editor_venue_details_help_tab'              => array(
310
+						'title'    => esc_html__('Event Venue Details', 'event_espresso'),
311
+						'filename' => 'event_editor_venue_details',
312
+					),
313
+					'event_editor_event_datetimes_help_tab'            => array(
314
+						'title'    => esc_html__('Event Datetimes', 'event_espresso'),
315
+						'filename' => 'event_editor_event_datetimes',
316
+					),
317
+					'event_editor_event_tickets_help_tab'              => array(
318
+						'title'    => esc_html__('Event Tickets', 'event_espresso'),
319
+						'filename' => 'event_editor_event_tickets',
320
+					),
321
+					'event_editor_event_registration_options_help_tab' => array(
322
+						'title'    => esc_html__('Event Registration Options', 'event_espresso'),
323
+						'filename' => 'event_editor_event_registration_options',
324
+					),
325
+					'event_editor_tags_categories_help_tab'            => array(
326
+						'title'    => esc_html__('Event Tags & Categories', 'event_espresso'),
327
+						'filename' => 'event_editor_tags_categories',
328
+					),
329
+					'event_editor_questions_registrants_help_tab'      => array(
330
+						'title'    => esc_html__('Questions for Registrants', 'event_espresso'),
331
+						'filename' => 'event_editor_questions_registrants',
332
+					),
333
+					'event_editor_save_new_event_help_tab'             => array(
334
+						'title'    => esc_html__('Save New Event', 'event_espresso'),
335
+						'filename' => 'event_editor_save_new_event',
336
+					),
337
+					'event_editor_other_help_tab'                      => array(
338
+						'title'    => esc_html__('Event Other', 'event_espresso'),
339
+						'filename' => 'event_editor_other',
340
+					),
341
+				),
342
+				'help_tour'     => array(
343
+					'Event_Editor_Help_Tour',
344
+				),
345
+				'qtips'         => array('EE_Event_Editor_Decaf_Tips'),
346
+				'require_nonce' => false,
347
+			),
348
+			'edit'                   => array(
349
+				'nav'           => array(
350
+					'label'      => esc_html__('Edit Event', 'event_espresso'),
351
+					'order'      => 5,
352
+					'persistent' => false,
353
+					'url'        => isset($this->_req_data['post'])
354
+						? EE_Admin_Page::add_query_args_and_nonce(
355
+							array('post' => $this->_req_data['post'], 'action' => 'edit'),
356
+							$this->_current_page_view_url
357
+						)
358
+						: $this->_admin_base_url,
359
+				),
360
+				'metaboxes'     => array('_register_event_editor_meta_boxes'),
361
+				'help_tabs'     => array(
362
+					'event_editor_help_tab'                            => array(
363
+						'title'    => esc_html__('Event Editor', 'event_espresso'),
364
+						'filename' => 'event_editor',
365
+					),
366
+					'event_editor_title_richtexteditor_help_tab'       => array(
367
+						'title'    => esc_html__('Event Title & Rich Text Editor', 'event_espresso'),
368
+						'filename' => 'event_editor_title_richtexteditor',
369
+					),
370
+					'event_editor_venue_details_help_tab'              => array(
371
+						'title'    => esc_html__('Event Venue Details', 'event_espresso'),
372
+						'filename' => 'event_editor_venue_details',
373
+					),
374
+					'event_editor_event_datetimes_help_tab'            => array(
375
+						'title'    => esc_html__('Event Datetimes', 'event_espresso'),
376
+						'filename' => 'event_editor_event_datetimes',
377
+					),
378
+					'event_editor_event_tickets_help_tab'              => array(
379
+						'title'    => esc_html__('Event Tickets', 'event_espresso'),
380
+						'filename' => 'event_editor_event_tickets',
381
+					),
382
+					'event_editor_event_registration_options_help_tab' => array(
383
+						'title'    => esc_html__('Event Registration Options', 'event_espresso'),
384
+						'filename' => 'event_editor_event_registration_options',
385
+					),
386
+					'event_editor_tags_categories_help_tab'            => array(
387
+						'title'    => esc_html__('Event Tags & Categories', 'event_espresso'),
388
+						'filename' => 'event_editor_tags_categories',
389
+					),
390
+					'event_editor_questions_registrants_help_tab'      => array(
391
+						'title'    => esc_html__('Questions for Registrants', 'event_espresso'),
392
+						'filename' => 'event_editor_questions_registrants',
393
+					),
394
+					'event_editor_save_new_event_help_tab'             => array(
395
+						'title'    => esc_html__('Save New Event', 'event_espresso'),
396
+						'filename' => 'event_editor_save_new_event',
397
+					),
398
+					'event_editor_other_help_tab'                      => array(
399
+						'title'    => esc_html__('Event Other', 'event_espresso'),
400
+						'filename' => 'event_editor_other',
401
+					),
402
+				),
403
+				'qtips'         => array('EE_Event_Editor_Decaf_Tips'),
404
+				'require_nonce' => false,
405
+			),
406
+			'default_event_settings' => array(
407
+				'nav'           => array(
408
+					'label' => esc_html__('Default Settings', 'event_espresso'),
409
+					'order' => 40,
410
+				),
411
+				'metaboxes'     => array_merge($this->_default_espresso_metaboxes, array('_publish_post_box')),
412
+				'labels'        => array(
413
+					'publishbox' => esc_html__('Update Settings', 'event_espresso'),
414
+				),
415
+				'help_tabs'     => array(
416
+					'default_settings_help_tab'        => array(
417
+						'title'    => esc_html__('Default Event Settings', 'event_espresso'),
418
+						'filename' => 'events_default_settings',
419
+					),
420
+					'default_settings_status_help_tab' => array(
421
+						'title'    => esc_html__('Default Registration Status', 'event_espresso'),
422
+						'filename' => 'events_default_settings_status',
423
+					),
424
+					'default_maximum_tickets_help_tab' => array(
425
+						'title'    => esc_html__('Default Maximum Tickets Per Order', 'event_espresso'),
426
+						'filename' => 'events_default_settings_max_tickets',
427
+					),
428
+				),
429
+				'help_tour'     => array('Event_Default_Settings_Help_Tour'),
430
+				'require_nonce' => false,
431
+			),
432
+			// template settings
433
+			'template_settings'      => array(
434
+				'nav'           => array(
435
+					'label' => esc_html__('Templates', 'event_espresso'),
436
+					'order' => 30,
437
+				),
438
+				'metaboxes'     => $this->_default_espresso_metaboxes,
439
+				'help_tabs'     => array(
440
+					'general_settings_templates_help_tab' => array(
441
+						'title'    => esc_html__('Templates', 'event_espresso'),
442
+						'filename' => 'general_settings_templates',
443
+					),
444
+				),
445
+				'help_tour'     => array('Templates_Help_Tour'),
446
+				'require_nonce' => false,
447
+			),
448
+			// event category stuff
449
+			'add_category'           => array(
450
+				'nav'           => array(
451
+					'label'      => esc_html__('Add Category', 'event_espresso'),
452
+					'order'      => 15,
453
+					'persistent' => false,
454
+				),
455
+				'help_tabs'     => array(
456
+					'add_category_help_tab' => array(
457
+						'title'    => esc_html__('Add New Event Category', 'event_espresso'),
458
+						'filename' => 'events_add_category',
459
+					),
460
+				),
461
+				'help_tour'     => array('Event_Add_Category_Help_Tour'),
462
+				'metaboxes'     => array('_publish_post_box'),
463
+				'require_nonce' => false,
464
+			),
465
+			'edit_category'          => array(
466
+				'nav'           => array(
467
+					'label'      => esc_html__('Edit Category', 'event_espresso'),
468
+					'order'      => 15,
469
+					'persistent' => false,
470
+					'url'        => isset($this->_req_data['EVT_CAT_ID'])
471
+						? add_query_arg(
472
+							array('EVT_CAT_ID' => $this->_req_data['EVT_CAT_ID']),
473
+							$this->_current_page_view_url
474
+						)
475
+						: $this->_admin_base_url,
476
+				),
477
+				'help_tabs'     => array(
478
+					'edit_category_help_tab' => array(
479
+						'title'    => esc_html__('Edit Event Category', 'event_espresso'),
480
+						'filename' => 'events_edit_category',
481
+					),
482
+				),
483
+				/*'help_tour' => array('Event_Edit_Category_Help_Tour'),*/
484
+				'metaboxes'     => array('_publish_post_box'),
485
+				'require_nonce' => false,
486
+			),
487
+			'category_list'          => array(
488
+				'nav'           => array(
489
+					'label' => esc_html__('Categories', 'event_espresso'),
490
+					'order' => 20,
491
+				),
492
+				'list_table'    => 'Event_Categories_Admin_List_Table',
493
+				'help_tabs'     => array(
494
+					'events_categories_help_tab'                       => array(
495
+						'title'    => esc_html__('Event Categories', 'event_espresso'),
496
+						'filename' => 'events_categories',
497
+					),
498
+					'events_categories_table_column_headings_help_tab' => array(
499
+						'title'    => esc_html__('Event Categories Table Column Headings', 'event_espresso'),
500
+						'filename' => 'events_categories_table_column_headings',
501
+					),
502
+					'events_categories_view_help_tab'                  => array(
503
+						'title'    => esc_html__('Event Categories Views', 'event_espresso'),
504
+						'filename' => 'events_categories_views',
505
+					),
506
+					'events_categories_other_help_tab'                 => array(
507
+						'title'    => esc_html__('Event Categories Other', 'event_espresso'),
508
+						'filename' => 'events_categories_other',
509
+					),
510
+				),
511
+				'help_tour'     => array(
512
+					'Event_Categories_Help_Tour',
513
+				),
514
+				'metaboxes'     => $this->_default_espresso_metaboxes,
515
+				'require_nonce' => false,
516
+			),
517
+			'preview_deletion'           => array(
518
+				'nav'           => array(
519
+					'label'      => esc_html__('Preview Deletion', 'event_espresso'),
520
+					'order'      => 15,
521
+					'persistent' => false,
522
+				),
523
+				'require_nonce' => false
524
+			)
525
+		);
526
+	}
527
+
528
+
529
+	/**
530
+	 * Used to register any global screen options if necessary for every route in this admin page group.
531
+	 */
532
+	protected function _add_screen_options()
533
+	{
534
+	}
535
+
536
+
537
+	/**
538
+	 * Implementing the screen options for the 'default' route.
539
+	 */
540
+	protected function _add_screen_options_default()
541
+	{
542
+		$this->_per_page_screen_option();
543
+	}
544
+
545
+
546
+	/**
547
+	 * Implementing screen options for the category list route.
548
+	 */
549
+	protected function _add_screen_options_category_list()
550
+	{
551
+		$page_title = $this->_admin_page_title;
552
+		$this->_admin_page_title = esc_html__('Categories', 'event_espresso');
553
+		$this->_per_page_screen_option();
554
+		$this->_admin_page_title = $page_title;
555
+	}
556
+
557
+
558
+	/**
559
+	 * Used to register any global feature pointers for the admin page group.
560
+	 */
561
+	protected function _add_feature_pointers()
562
+	{
563
+	}
564
+
565
+
566
+	/**
567
+	 * Registers and enqueues any global scripts and styles for the entire admin page group.
568
+	 */
569
+	public function load_scripts_styles()
570
+	{
571
+		wp_register_style(
572
+			'events-admin-css',
573
+			EVENTS_ASSETS_URL . 'events-admin-page.css',
574
+			array(),
575
+			EVENT_ESPRESSO_VERSION
576
+		);
577
+		wp_register_style('ee-cat-admin', EVENTS_ASSETS_URL . 'ee-cat-admin.css', array(), EVENT_ESPRESSO_VERSION);
578
+		wp_enqueue_style('events-admin-css');
579
+		wp_enqueue_style('ee-cat-admin');
580
+		// todo note: we also need to load_scripts_styles per view (i.e. default/view_report/event_details
581
+		// registers for all views
582
+		// scripts
583
+		wp_register_script(
584
+			'event_editor_js',
585
+			EVENTS_ASSETS_URL . 'event_editor.js',
586
+			array('ee_admin_js', 'jquery-ui-slider', 'jquery-ui-timepicker-addon'),
587
+			EVENT_ESPRESSO_VERSION,
588
+			true
589
+		);
590
+	}
591
+
592
+
593
+	/**
594
+	 * Enqueuing scripts and styles specific to this view
595
+	 */
596
+	public function load_scripts_styles_create_new()
597
+	{
598
+		$this->load_scripts_styles_edit();
599
+	}
600
+
601
+
602
+	/**
603
+	 * Enqueuing scripts and styles specific to this view
604
+	 */
605
+	public function load_scripts_styles_edit()
606
+	{
607
+		// styles
608
+		wp_enqueue_style('espresso-ui-theme');
609
+		wp_register_style(
610
+			'event-editor-css',
611
+			EVENTS_ASSETS_URL . 'event-editor.css',
612
+			array('ee-admin-css'),
613
+			EVENT_ESPRESSO_VERSION
614
+		);
615
+		wp_enqueue_style('event-editor-css');
616
+		// scripts
617
+		wp_register_script(
618
+			'event-datetime-metabox',
619
+			EVENTS_ASSETS_URL . 'event-datetime-metabox.js',
620
+			array('event_editor_js', 'ee-datepicker'),
621
+			EVENT_ESPRESSO_VERSION
622
+		);
623
+		wp_enqueue_script('event-datetime-metabox');
624
+	}
625
+
626
+
627
+	/**
628
+	 * Populating the _views property for the category list table view.
629
+	 */
630
+	protected function _set_list_table_views_category_list()
631
+	{
632
+		$this->_views = array(
633
+			'all' => array(
634
+				'slug'        => 'all',
635
+				'label'       => esc_html__('All', 'event_espresso'),
636
+				'count'       => 0,
637
+				'bulk_action' => array(
638
+					'delete_categories' => esc_html__('Delete Permanently', 'event_espresso'),
639
+				),
640
+			),
641
+		);
642
+	}
643
+
644
+
645
+	/**
646
+	 * For adding anything that fires on the admin_init hook for any route within this admin page group.
647
+	 */
648
+	public function admin_init()
649
+	{
650
+		EE_Registry::$i18n_js_strings['image_confirm'] = esc_html__(
651
+			'Do you really want to delete this image? Please remember to update your event to complete the removal.',
652
+			'event_espresso'
653
+		);
654
+	}
655
+
656
+
657
+	/**
658
+	 * For adding anything that should be triggered on the admin_notices hook for any route within this admin page
659
+	 * group.
660
+	 */
661
+	public function admin_notices()
662
+	{
663
+	}
664
+
665
+
666
+	/**
667
+	 * For adding anything that should be triggered on the `admin_print_footer_scripts` hook for any route within
668
+	 * this admin page group.
669
+	 */
670
+	public function admin_footer_scripts()
671
+	{
672
+	}
673
+
674
+
675
+	/**
676
+	 * Call this function to verify if an event is public and has tickets for sale.  If it does, then we need to show a
677
+	 * warning (via EE_Error::add_error());
678
+	 *
679
+	 * @param  EE_Event $event Event object
680
+	 * @param string    $req_type
681
+	 * @return void
682
+	 * @throws EE_Error
683
+	 * @access public
684
+	 */
685
+	public function verify_event_edit($event = null, $req_type = '')
686
+	{
687
+		// don't need to do this when processing
688
+		if (! empty($req_type)) {
689
+			return;
690
+		}
691
+		// no event?
692
+		if (empty($event)) {
693
+			// set event
694
+			$event = $this->_cpt_model_obj;
695
+		}
696
+		// STILL no event?
697
+		if (! $event instanceof EE_Event) {
698
+			return;
699
+		}
700
+		$orig_status = $event->status();
701
+		// first check if event is active.
702
+		if ($orig_status === EEM_Event::cancelled
703
+			|| $orig_status === EEM_Event::postponed
704
+			|| $event->is_expired()
705
+			|| $event->is_inactive()
706
+		) {
707
+			return;
708
+		}
709
+		// made it here so it IS active... next check that any of the tickets are sold.
710
+		if ($event->is_sold_out(true)) {
711
+			if ($orig_status !== EEM_Event::sold_out && $event->status() !== $orig_status) {
712
+				EE_Error::add_attention(
713
+					sprintf(
714
+						esc_html__(
715
+							'Please note that the Event Status has automatically been changed to %s because there are no more spaces available for this event.  However, this change is not permanent until you update the event.  You can change the status back to something else before updating if you wish.',
716
+							'event_espresso'
717
+						),
718
+						EEH_Template::pretty_status(EEM_Event::sold_out, false, 'sentence')
719
+					)
720
+				);
721
+			}
722
+			return;
723
+		} elseif ($orig_status === EEM_Event::sold_out) {
724
+			EE_Error::add_attention(
725
+				sprintf(
726
+					esc_html__(
727
+						'Please note that the Event Status has automatically been changed to %s because more spaces have become available for this event, most likely due to abandoned transactions freeing up reserved tickets.  However, this change is not permanent until you update the event. If you wish, you can change the status back to something else before updating.',
728
+						'event_espresso'
729
+					),
730
+					EEH_Template::pretty_status($event->status(), false, 'sentence')
731
+				)
732
+			);
733
+		}
734
+		// now we need to determine if the event has any tickets on sale.  If not then we dont' show the error
735
+		if (! $event->tickets_on_sale()) {
736
+			return;
737
+		}
738
+		// made it here so show warning
739
+		$this->_edit_event_warning();
740
+	}
741
+
742
+
743
+	/**
744
+	 * This is the text used for when an event is being edited that is public and has tickets for sale.
745
+	 * When needed, hook this into a EE_Error::add_error() notice.
746
+	 *
747
+	 * @access protected
748
+	 * @return void
749
+	 */
750
+	protected function _edit_event_warning()
751
+	{
752
+		// we don't want to add warnings during these requests
753
+		if (isset($this->_req_data['action']) && $this->_req_data['action'] === 'editpost') {
754
+			return;
755
+		}
756
+		EE_Error::add_attention(
757
+			sprintf(
758
+				esc_html__(
759
+					'Your event is open for registration. Making changes may disrupt any transactions in progress. %sLearn more%s',
760
+					'event_espresso'
761
+				),
762
+				'<a class="espresso-help-tab-lnk">',
763
+				'</a>'
764
+			)
765
+		);
766
+	}
767
+
768
+
769
+	/**
770
+	 * When a user is creating a new event, notify them if they haven't set their timezone.
771
+	 * Otherwise, do the normal logic
772
+	 *
773
+	 * @return string
774
+	 * @throws \EE_Error
775
+	 */
776
+	protected function _create_new_cpt_item()
777
+	{
778
+		$has_timezone_string = get_option('timezone_string');
779
+		// only nag them about setting their timezone if it's their first event, and they haven't already done it
780
+		if (! $has_timezone_string && ! EEM_Event::instance()->exists(array())) {
781
+			EE_Error::add_attention(
782
+				sprintf(
783
+					__(
784
+						'Your website\'s timezone is currently set to a UTC offset. We recommend updating your timezone to a city or region near you before you create an event. Change your timezone now:%1$s%2$s%3$sChange Timezone%4$s',
785
+						'event_espresso'
786
+					),
787
+					'<br>',
788
+					'<select id="timezone_string" name="timezone_string" aria-describedby="timezone-description">'
789
+					. EEH_DTT_Helper::wp_timezone_choice('', EEH_DTT_Helper::get_user_locale())
790
+					. '</select>',
791
+					'<button class="button button-secondary timezone-submit">',
792
+					'</button><span class="spinner"></span>'
793
+				),
794
+				__FILE__,
795
+				__FUNCTION__,
796
+				__LINE__
797
+			);
798
+		}
799
+		return parent::_create_new_cpt_item();
800
+	}
801
+
802
+
803
+	/**
804
+	 * Sets the _views property for the default route in this admin page group.
805
+	 */
806
+	protected function _set_list_table_views_default()
807
+	{
808
+		$this->_views = array(
809
+			'all'   => array(
810
+				'slug'        => 'all',
811
+				'label'       => esc_html__('View All Events', 'event_espresso'),
812
+				'count'       => 0,
813
+				'bulk_action' => array(
814
+					'trash_events' => esc_html__('Move to Trash', 'event_espresso'),
815
+				),
816
+			),
817
+			'draft' => array(
818
+				'slug'        => 'draft',
819
+				'label'       => esc_html__('Draft', 'event_espresso'),
820
+				'count'       => 0,
821
+				'bulk_action' => array(
822
+					'trash_events' => esc_html__('Move to Trash', 'event_espresso'),
823
+				),
824
+			),
825
+		);
826
+		if (EE_Registry::instance()->CAP->current_user_can('ee_delete_events', 'espresso_events_trash_events')) {
827
+			$this->_views['trash'] = array(
828
+				'slug'        => 'trash',
829
+				'label'       => esc_html__('Trash', 'event_espresso'),
830
+				'count'       => 0,
831
+				'bulk_action' => array(
832
+					'restore_events' => esc_html__('Restore From Trash', 'event_espresso'),
833
+					'delete_events'  => esc_html__('Delete Permanently', 'event_espresso'),
834
+				),
835
+			);
836
+		}
837
+	}
838
+
839
+
840
+	/**
841
+	 * Provides the legend item array for the default list table view.
842
+	 *
843
+	 * @return array
844
+	 */
845
+	protected function _event_legend_items()
846
+	{
847
+		$items = array(
848
+			'view_details'   => array(
849
+				'class' => 'dashicons dashicons-search',
850
+				'desc'  => esc_html__('View Event', 'event_espresso'),
851
+			),
852
+			'edit_event'     => array(
853
+				'class' => 'ee-icon ee-icon-calendar-edit',
854
+				'desc'  => esc_html__('Edit Event Details', 'event_espresso'),
855
+			),
856
+			'view_attendees' => array(
857
+				'class' => 'dashicons dashicons-groups',
858
+				'desc'  => esc_html__('View Registrations for Event', 'event_espresso'),
859
+			),
860
+		);
861
+		$items = apply_filters('FHEE__Events_Admin_Page___event_legend_items__items', $items);
862
+		$statuses = array(
863
+			'sold_out_status'  => array(
864
+				'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::sold_out,
865
+				'desc'  => EEH_Template::pretty_status(EE_Datetime::sold_out, false, 'sentence'),
866
+			),
867
+			'active_status'    => array(
868
+				'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::active,
869
+				'desc'  => EEH_Template::pretty_status(EE_Datetime::active, false, 'sentence'),
870
+			),
871
+			'upcoming_status'  => array(
872
+				'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::upcoming,
873
+				'desc'  => EEH_Template::pretty_status(EE_Datetime::upcoming, false, 'sentence'),
874
+			),
875
+			'postponed_status' => array(
876
+				'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::postponed,
877
+				'desc'  => EEH_Template::pretty_status(EE_Datetime::postponed, false, 'sentence'),
878
+			),
879
+			'cancelled_status' => array(
880
+				'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::cancelled,
881
+				'desc'  => EEH_Template::pretty_status(EE_Datetime::cancelled, false, 'sentence'),
882
+			),
883
+			'expired_status'   => array(
884
+				'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::expired,
885
+				'desc'  => EEH_Template::pretty_status(EE_Datetime::expired, false, 'sentence'),
886
+			),
887
+			'inactive_status'  => array(
888
+				'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::inactive,
889
+				'desc'  => EEH_Template::pretty_status(EE_Datetime::inactive, false, 'sentence'),
890
+			),
891
+		);
892
+		$statuses = apply_filters('FHEE__Events_Admin_Page__event_legend_items__statuses', $statuses);
893
+		return array_merge($items, $statuses);
894
+	}
895
+
896
+
897
+	/**
898
+	 * @return EEM_Event
899
+	 */
900
+	private function _event_model()
901
+	{
902
+		if (! $this->_event_model instanceof EEM_Event) {
903
+			$this->_event_model = EE_Registry::instance()->load_model('Event');
904
+		}
905
+		return $this->_event_model;
906
+	}
907
+
908
+
909
+	/**
910
+	 * Adds extra buttons to the WP CPT permalink field row.
911
+	 * Method is called from parent and is hooked into the wp 'get_sample_permalink_html' filter.
912
+	 *
913
+	 * @param  string $return    the current html
914
+	 * @param  int    $id        the post id for the page
915
+	 * @param  string $new_title What the title is
916
+	 * @param  string $new_slug  what the slug is
917
+	 * @return string            The new html string for the permalink area
918
+	 */
919
+	public function extra_permalink_field_buttons($return, $id, $new_title, $new_slug)
920
+	{
921
+		// make sure this is only when editing
922
+		if (! empty($id)) {
923
+			$post = get_post($id);
924
+			$return .= '<a class="button button-small" onclick="prompt(\'Shortcode:\', jQuery(\'#shortcode\').val()); return false;" href="#"  tabindex="-1">'
925
+					   . esc_html__('Shortcode', 'event_espresso')
926
+					   . '</a> ';
927
+			$return .= '<input id="shortcode" type="hidden" value="[ESPRESSO_TICKET_SELECTOR event_id='
928
+					   . $post->ID
929
+					   . ']">';
930
+		}
931
+		return $return;
932
+	}
933
+
934
+
935
+	/**
936
+	 * _events_overview_list_table
937
+	 * This contains the logic for showing the events_overview list
938
+	 *
939
+	 * @access protected
940
+	 * @return void
941
+	 * @throws \EE_Error
942
+	 */
943
+	protected function _events_overview_list_table()
944
+	{
945
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
946
+		$this->_template_args['after_list_table'] = ! empty($this->_template_args['after_list_table'])
947
+			? (array) $this->_template_args['after_list_table']
948
+			: array();
949
+		$this->_template_args['after_list_table']['view_event_list_button'] = EEH_HTML::br()
950
+				. EEH_Template::get_button_or_link(
951
+					get_post_type_archive_link('espresso_events'),
952
+					esc_html__("View Event Archive Page", "event_espresso"),
953
+					'button'
954
+				);
955
+		$this->_template_args['after_list_table']['legend'] = $this->_display_legend($this->_event_legend_items());
956
+		$this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
957
+			'create_new',
958
+			'add',
959
+			array(),
960
+			'add-new-h2'
961
+		);
962
+		$this->display_admin_list_table_page_with_no_sidebar();
963
+	}
964
+
965
+
966
+	/**
967
+	 * this allows for extra misc actions in the default WP publish box
968
+	 *
969
+	 * @return void
970
+	 */
971
+	public function extra_misc_actions_publish_box()
972
+	{
973
+		$this->_generate_publish_box_extra_content();
974
+	}
975
+
976
+
977
+	/**
978
+	 * This is hooked into the WordPress do_action('save_post') hook and runs after the custom post type has been
979
+	 * saved.
980
+	 * Typically you would use this to save any additional data.
981
+	 * Keep in mind also that "save_post" runs on EVERY post update to the database.
982
+	 * ALSO very important.  When a post transitions from scheduled to published,
983
+	 * the save_post action is fired but you will NOT have any _POST data containing any extra info you may have from
984
+	 * other meta saves. So MAKE sure that you handle this accordingly.
985
+	 *
986
+	 * @access protected
987
+	 * @abstract
988
+	 * @param  string $post_id The ID of the cpt that was saved (so you can link relationally)
989
+	 * @param  object $post    The post object of the cpt that was saved.
990
+	 * @return void
991
+	 * @throws \EE_Error
992
+	 */
993
+	protected function _insert_update_cpt_item($post_id, $post)
994
+	{
995
+		if ($post instanceof WP_Post && $post->post_type !== 'espresso_events') {
996
+			// get out we're not processing an event save.
997
+			return;
998
+		}
999
+		$event_values = array(
1000
+			'EVT_display_desc'                => ! empty($this->_req_data['display_desc']) ? 1 : 0,
1001
+			'EVT_display_ticket_selector'     => ! empty($this->_req_data['display_ticket_selector']) ? 1 : 0,
1002
+			'EVT_additional_limit'            => min(
1003
+				apply_filters('FHEE__EE_Events_Admin__insert_update_cpt_item__EVT_additional_limit_max', 255),
1004
+				! empty($this->_req_data['additional_limit']) ? $this->_req_data['additional_limit'] : null
1005
+			),
1006
+			'EVT_default_registration_status' => ! empty($this->_req_data['EVT_default_registration_status'])
1007
+				? $this->_req_data['EVT_default_registration_status']
1008
+				: EE_Registry::instance()->CFG->registration->default_STS_ID,
1009
+			'EVT_member_only'                 => ! empty($this->_req_data['member_only']) ? 1 : 0,
1010
+			'EVT_allow_overflow'              => ! empty($this->_req_data['EVT_allow_overflow']) ? 1 : 0,
1011
+			'EVT_timezone_string'             => ! empty($this->_req_data['timezone_string'])
1012
+				? $this->_req_data['timezone_string'] : null,
1013
+			'EVT_external_URL'                => ! empty($this->_req_data['externalURL'])
1014
+				? $this->_req_data['externalURL'] : null,
1015
+			'EVT_phone'                       => ! empty($this->_req_data['event_phone'])
1016
+				? $this->_req_data['event_phone'] : null,
1017
+		);
1018
+		// update event
1019
+		$success = $this->_event_model()->update_by_ID($event_values, $post_id);
1020
+		// get event_object for other metaboxes... though it would seem to make sense to just use $this->_event_model()->get_one_by_ID( $post_id ).. i have to setup where conditions to override the filters in the model that filter out autodraft and inherit statuses so we GET the inherit id!
1021
+		$get_one_where = array(
1022
+			$this->_event_model()->primary_key_name() => $post_id,
1023
+			'OR'                                      => array(
1024
+				'status'   => $post->post_status,
1025
+				// if trying to "Publish" a sold out event, it's status will get switched back to "sold_out" in the db,
1026
+				// but the returned object here has a status of "publish", so use the original post status as well
1027
+				'status*1' => $this->_req_data['original_post_status'],
1028
+			),
1029
+		);
1030
+		$event = $this->_event_model()->get_one(array($get_one_where));
1031
+		// the following are default callbacks for event attachment updates that can be overridden by caffeinated functionality and/or addons.
1032
+		$event_update_callbacks = apply_filters(
1033
+			'FHEE__Events_Admin_Page___insert_update_cpt_item__event_update_callbacks',
1034
+			array(
1035
+				array($this, '_default_venue_update'),
1036
+				array($this, '_default_tickets_update'),
1037
+			)
1038
+		);
1039
+		$att_success = true;
1040
+		foreach ($event_update_callbacks as $e_callback) {
1041
+			$_success = is_callable($e_callback)
1042
+				? call_user_func($e_callback, $event, $this->_req_data)
1043
+				: false;
1044
+			// if ANY of these updates fail then we want the appropriate global error message
1045
+			$att_success = ! $att_success ? $att_success : $_success;
1046
+		}
1047
+		// any errors?
1048
+		if ($success && false === $att_success) {
1049
+			EE_Error::add_error(
1050
+				esc_html__(
1051
+					'Event Details saved successfully but something went wrong with saving attachments.',
1052
+					'event_espresso'
1053
+				),
1054
+				__FILE__,
1055
+				__FUNCTION__,
1056
+				__LINE__
1057
+			);
1058
+		} elseif ($success === false) {
1059
+			EE_Error::add_error(
1060
+				esc_html__('Event Details did not save successfully.', 'event_espresso'),
1061
+				__FILE__,
1062
+				__FUNCTION__,
1063
+				__LINE__
1064
+			);
1065
+		}
1066
+	}
1067
+
1068
+
1069
+	/**
1070
+	 * @see parent::restore_item()
1071
+	 * @param int $post_id
1072
+	 * @param int $revision_id
1073
+	 */
1074
+	protected function _restore_cpt_item($post_id, $revision_id)
1075
+	{
1076
+		// copy existing event meta to new post
1077
+		$post_evt = $this->_event_model()->get_one_by_ID($post_id);
1078
+		if ($post_evt instanceof EE_Event) {
1079
+			// meta revision restore
1080
+			$post_evt->restore_revision($revision_id);
1081
+			// related objs restore
1082
+			$post_evt->restore_revision($revision_id, array('Venue', 'Datetime', 'Price'));
1083
+		}
1084
+	}
1085
+
1086
+
1087
+	/**
1088
+	 * Attach the venue to the Event
1089
+	 *
1090
+	 * @param  \EE_Event $evtobj Event Object to add the venue to
1091
+	 * @param  array     $data   The request data from the form
1092
+	 * @return bool           Success or fail.
1093
+	 */
1094
+	protected function _default_venue_update(\EE_Event $evtobj, $data)
1095
+	{
1096
+		require_once(EE_MODELS . 'EEM_Venue.model.php');
1097
+		$venue_model = EE_Registry::instance()->load_model('Venue');
1098
+		$rows_affected = null;
1099
+		$venue_id = ! empty($data['venue_id']) ? $data['venue_id'] : null;
1100
+		// very important.  If we don't have a venue name...
1101
+		// then we'll get out because not necessary to create empty venue
1102
+		if (empty($data['venue_title'])) {
1103
+			return false;
1104
+		}
1105
+		$venue_array = array(
1106
+			'VNU_wp_user'         => $evtobj->get('EVT_wp_user'),
1107
+			'VNU_name'            => ! empty($data['venue_title']) ? $data['venue_title'] : null,
1108
+			'VNU_desc'            => ! empty($data['venue_description']) ? $data['venue_description'] : null,
1109
+			'VNU_identifier'      => ! empty($data['venue_identifier']) ? $data['venue_identifier'] : null,
1110
+			'VNU_short_desc'      => ! empty($data['venue_short_description']) ? $data['venue_short_description']
1111
+				: null,
1112
+			'VNU_address'         => ! empty($data['address']) ? $data['address'] : null,
1113
+			'VNU_address2'        => ! empty($data['address2']) ? $data['address2'] : null,
1114
+			'VNU_city'            => ! empty($data['city']) ? $data['city'] : null,
1115
+			'STA_ID'              => ! empty($data['state']) ? $data['state'] : null,
1116
+			'CNT_ISO'             => ! empty($data['countries']) ? $data['countries'] : null,
1117
+			'VNU_zip'             => ! empty($data['zip']) ? $data['zip'] : null,
1118
+			'VNU_phone'           => ! empty($data['venue_phone']) ? $data['venue_phone'] : null,
1119
+			'VNU_capacity'        => ! empty($data['venue_capacity']) ? $data['venue_capacity'] : null,
1120
+			'VNU_url'             => ! empty($data['venue_url']) ? $data['venue_url'] : null,
1121
+			'VNU_virtual_phone'   => ! empty($data['virtual_phone']) ? $data['virtual_phone'] : null,
1122
+			'VNU_virtual_url'     => ! empty($data['virtual_url']) ? $data['virtual_url'] : null,
1123
+			'VNU_enable_for_gmap' => isset($data['enable_for_gmap']) ? 1 : 0,
1124
+			'status'              => 'publish',
1125
+		);
1126
+		// if we've got the venue_id then we're just updating the existing venue so let's do that and then get out.
1127
+		if (! empty($venue_id)) {
1128
+			$update_where = array($venue_model->primary_key_name() => $venue_id);
1129
+			$rows_affected = $venue_model->update($venue_array, array($update_where));
1130
+			// we've gotta make sure that the venue is always attached to a revision.. add_relation_to should take care of making sure that the relation is already present.
1131
+			$evtobj->_add_relation_to($venue_id, 'Venue');
1132
+			return $rows_affected > 0 ? true : false;
1133
+		} else {
1134
+			// we insert the venue
1135
+			$venue_id = $venue_model->insert($venue_array);
1136
+			$evtobj->_add_relation_to($venue_id, 'Venue');
1137
+			return ! empty($venue_id) ? true : false;
1138
+		}
1139
+		// when we have the ancestor come in it's already been handled by the revision save.
1140
+	}
1141
+
1142
+
1143
+	/**
1144
+	 * Handles saving everything related to Tickets (datetimes, tickets, prices)
1145
+	 *
1146
+	 * @param  EE_Event $evtobj The Event object we're attaching data to
1147
+	 * @param  array    $data   The request data from the form
1148
+	 * @return array
1149
+	 */
1150
+	protected function _default_tickets_update(EE_Event $evtobj, $data)
1151
+	{
1152
+		$success = true;
1153
+		$saved_dtt = null;
1154
+		$saved_tickets = array();
1155
+		$incoming_date_formats = array('Y-m-d', 'h:i a');
1156
+		foreach ($data['edit_event_datetimes'] as $row => $dtt) {
1157
+			// trim all values to ensure any excess whitespace is removed.
1158
+			$dtt = array_map('trim', $dtt);
1159
+			$dtt['DTT_EVT_end'] = isset($dtt['DTT_EVT_end']) && ! empty($dtt['DTT_EVT_end']) ? $dtt['DTT_EVT_end']
1160
+				: $dtt['DTT_EVT_start'];
1161
+			$datetime_values = array(
1162
+				'DTT_ID'        => ! empty($dtt['DTT_ID']) ? $dtt['DTT_ID'] : null,
1163
+				'DTT_EVT_start' => $dtt['DTT_EVT_start'],
1164
+				'DTT_EVT_end'   => $dtt['DTT_EVT_end'],
1165
+				'DTT_reg_limit' => empty($dtt['DTT_reg_limit']) ? EE_INF : $dtt['DTT_reg_limit'],
1166
+				'DTT_order'     => $row,
1167
+			);
1168
+			// 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.
1169
+			if (! empty($dtt['DTT_ID'])) {
1170
+				$DTM = EE_Registry::instance()
1171
+								  ->load_model('Datetime', array($evtobj->get_timezone()))
1172
+								  ->get_one_by_ID($dtt['DTT_ID']);
1173
+				$DTM->set_date_format($incoming_date_formats[0]);
1174
+				$DTM->set_time_format($incoming_date_formats[1]);
1175
+				foreach ($datetime_values as $field => $value) {
1176
+					$DTM->set($field, $value);
1177
+				}
1178
+				// make sure the $dtt_id here is saved just in case after the add_relation_to() the autosave replaces it.  We need to do this so we dont' TRASH the parent DTT.
1179
+				$saved_dtts[ $DTM->ID() ] = $DTM;
1180
+			} else {
1181
+				$DTM = EE_Registry::instance()->load_class(
1182
+					'Datetime',
1183
+					array($datetime_values, $evtobj->get_timezone(), $incoming_date_formats),
1184
+					false,
1185
+					false
1186
+				);
1187
+				foreach ($datetime_values as $field => $value) {
1188
+					$DTM->set($field, $value);
1189
+				}
1190
+			}
1191
+			$DTM->save();
1192
+			$DTT = $evtobj->_add_relation_to($DTM, 'Datetime');
1193
+			// load DTT helper
1194
+			// 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.
1195
+			if ($DTT->get_raw('DTT_EVT_start') > $DTT->get_raw('DTT_EVT_end')) {
1196
+				$DTT->set('DTT_EVT_end', $DTT->get('DTT_EVT_start'));
1197
+				$DTT = EEH_DTT_Helper::date_time_add($DTT, 'DTT_EVT_end', 'days');
1198
+				$DTT->save();
1199
+			}
1200
+			// now we got to make sure we add the new DTT_ID to the $saved_dtts array  because it is possible there was a new one created for the autosave.
1201
+			$saved_dtt = $DTT;
1202
+			$success = ! $success ? $success : $DTT;
1203
+			// if ANY of these updates fail then we want the appropriate global error message.
1204
+			// //todo this is actually sucky we need a better error message but this is what it is for now.
1205
+		}
1206
+		// no dtts get deleted so we don't do any of that logic here.
1207
+		// update tickets next
1208
+		$old_tickets = isset($data['ticket_IDs']) ? explode(',', $data['ticket_IDs']) : array();
1209
+		foreach ($data['edit_tickets'] as $row => $tkt) {
1210
+			$incoming_date_formats = array('Y-m-d', 'h:i a');
1211
+			$update_prices = false;
1212
+			$ticket_price = isset($data['edit_prices'][ $row ][1]['PRC_amount'])
1213
+				? $data['edit_prices'][ $row ][1]['PRC_amount'] : 0;
1214
+			// trim inputs to ensure any excess whitespace is removed.
1215
+			$tkt = array_map('trim', $tkt);
1216
+			if (empty($tkt['TKT_start_date'])) {
1217
+				// let's use now in the set timezone.
1218
+				$now = new DateTime('now', new DateTimeZone($evtobj->get_timezone()));
1219
+				$tkt['TKT_start_date'] = $now->format($incoming_date_formats[0] . ' ' . $incoming_date_formats[1]);
1220
+			}
1221
+			if (empty($tkt['TKT_end_date'])) {
1222
+				// use the start date of the first datetime
1223
+				$dtt = $evtobj->first_datetime();
1224
+				$tkt['TKT_end_date'] = $dtt->start_date_and_time(
1225
+					$incoming_date_formats[0],
1226
+					$incoming_date_formats[1]
1227
+				);
1228
+			}
1229
+			$TKT_values = array(
1230
+				'TKT_ID'          => ! empty($tkt['TKT_ID']) ? $tkt['TKT_ID'] : null,
1231
+				'TTM_ID'          => ! empty($tkt['TTM_ID']) ? $tkt['TTM_ID'] : 0,
1232
+				'TKT_name'        => ! empty($tkt['TKT_name']) ? $tkt['TKT_name'] : '',
1233
+				'TKT_description' => ! empty($tkt['TKT_description']) ? $tkt['TKT_description'] : '',
1234
+				'TKT_start_date'  => $tkt['TKT_start_date'],
1235
+				'TKT_end_date'    => $tkt['TKT_end_date'],
1236
+				'TKT_qty'         => ! isset($tkt['TKT_qty']) || $tkt['TKT_qty'] === '' ? EE_INF : $tkt['TKT_qty'],
1237
+				'TKT_uses'        => ! isset($tkt['TKT_uses']) || $tkt['TKT_uses'] === '' ? EE_INF : $tkt['TKT_uses'],
1238
+				'TKT_min'         => empty($tkt['TKT_min']) ? 0 : $tkt['TKT_min'],
1239
+				'TKT_max'         => empty($tkt['TKT_max']) ? EE_INF : $tkt['TKT_max'],
1240
+				'TKT_row'         => $row,
1241
+				'TKT_order'       => isset($tkt['TKT_order']) ? $tkt['TKT_order'] : $row,
1242
+				'TKT_price'       => $ticket_price,
1243
+			);
1244
+			// 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.
1245
+			if (isset($tkt['TKT_is_default']) && $tkt['TKT_is_default']) {
1246
+				$TKT_values['TKT_ID'] = 0;
1247
+				$TKT_values['TKT_is_default'] = 0;
1248
+				$TKT_values['TKT_price'] = $ticket_price;
1249
+				$update_prices = true;
1250
+			}
1251
+			// if we have a TKT_ID then we need to get that existing TKT_obj and update it
1252
+			// we actually do our saves a head of doing any add_relations to because its entirely possible that this ticket didn't removed or added to any datetime in the session but DID have it's items modified.
1253
+			// keep in mind that if the TKT has been sold (and we have changed pricing information), then we won't be updating the tkt but instead a new tkt will be created and the old one archived.
1254
+			if (! empty($tkt['TKT_ID'])) {
1255
+				$TKT = EE_Registry::instance()
1256
+								  ->load_model('Ticket', array($evtobj->get_timezone()))
1257
+								  ->get_one_by_ID($tkt['TKT_ID']);
1258
+				if ($TKT instanceof EE_Ticket) {
1259
+					$ticket_sold = $TKT->count_related(
1260
+						'Registration',
1261
+						array(
1262
+							array(
1263
+								'STS_ID' => array(
1264
+									'NOT IN',
1265
+									array(EEM_Registration::status_id_incomplete),
1266
+								),
1267
+							),
1268
+						)
1269
+					) > 0 ? true : false;
1270
+					// let's just check the total price for the existing ticket and determine if it matches the new total price.  if they are different then we create a new ticket (if tkts sold) if they aren't different then we go ahead and modify existing ticket.
1271
+					$create_new_TKT = $ticket_sold && $ticket_price != $TKT->get('TKT_price')
1272
+									  && ! $TKT->get('TKT_deleted');
1273
+					$TKT->set_date_format($incoming_date_formats[0]);
1274
+					$TKT->set_time_format($incoming_date_formats[1]);
1275
+					// set new values
1276
+					foreach ($TKT_values as $field => $value) {
1277
+						if ($field == 'TKT_qty') {
1278
+							$TKT->set_qty($value);
1279
+						} else {
1280
+							$TKT->set($field, $value);
1281
+						}
1282
+					}
1283
+					// if $create_new_TKT is false then we can safely update the existing ticket.  Otherwise we have to create a new ticket.
1284
+					if ($create_new_TKT) {
1285
+						// archive the old ticket first
1286
+						$TKT->set('TKT_deleted', 1);
1287
+						$TKT->save();
1288
+						// make sure this ticket is still recorded in our saved_tkts so we don't run it through the regular trash routine.
1289
+						$saved_tickets[ $TKT->ID() ] = $TKT;
1290
+						// create new ticket that's a copy of the existing except a new id of course (and not archived) AND has the new TKT_price associated with it.
1291
+						$TKT = clone $TKT;
1292
+						$TKT->set('TKT_ID', 0);
1293
+						$TKT->set('TKT_deleted', 0);
1294
+						$TKT->set('TKT_price', $ticket_price);
1295
+						$TKT->set('TKT_sold', 0);
1296
+						// now we need to make sure that $new prices are created as well and attached to new ticket.
1297
+						$update_prices = true;
1298
+					}
1299
+					// make sure price is set if it hasn't been already
1300
+					$TKT->set('TKT_price', $ticket_price);
1301
+				}
1302
+			} else {
1303
+				// no TKT_id so a new TKT
1304
+				$TKT_values['TKT_price'] = $ticket_price;
1305
+				$TKT = EE_Registry::instance()->load_class('Ticket', array($TKT_values), false, false);
1306
+				if ($TKT instanceof EE_Ticket) {
1307
+					// need to reset values to properly account for the date formats
1308
+					$TKT->set_date_format($incoming_date_formats[0]);
1309
+					$TKT->set_time_format($incoming_date_formats[1]);
1310
+					$TKT->set_timezone($evtobj->get_timezone());
1311
+					// set new values
1312
+					foreach ($TKT_values as $field => $value) {
1313
+						if ($field == 'TKT_qty') {
1314
+							$TKT->set_qty($value);
1315
+						} else {
1316
+							$TKT->set($field, $value);
1317
+						}
1318
+					}
1319
+					$update_prices = true;
1320
+				}
1321
+			}
1322
+			// cap ticket qty by datetime reg limits
1323
+			$TKT->set_qty(min($TKT->qty(), $TKT->qty('reg_limit')));
1324
+			// update ticket.
1325
+			$TKT->save();
1326
+			// 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.
1327
+			if ($TKT->get_raw('TKT_start_date') > $TKT->get_raw('TKT_end_date')) {
1328
+				$TKT->set('TKT_end_date', $TKT->get('TKT_start_date'));
1329
+				$TKT = EEH_DTT_Helper::date_time_add($TKT, 'TKT_end_date', 'days');
1330
+				$TKT->save();
1331
+			}
1332
+			// initially let's add the ticket to the dtt
1333
+			$saved_dtt->_add_relation_to($TKT, 'Ticket');
1334
+			$saved_tickets[ $TKT->ID() ] = $TKT;
1335
+			// add prices to ticket
1336
+			$this->_add_prices_to_ticket($data['edit_prices'][ $row ], $TKT, $update_prices);
1337
+		}
1338
+		// however now we need to handle permanently deleting tickets via the ui.  Keep in mind that the ui does not allow deleting/archiving tickets that have ticket sold.  However, it does allow for deleting tickets that have no tickets sold, in which case we want to get rid of permanently because there is no need to save in db.
1339
+		$old_tickets = isset($old_tickets[0]) && $old_tickets[0] == '' ? array() : $old_tickets;
1340
+		$tickets_removed = array_diff($old_tickets, array_keys($saved_tickets));
1341
+		foreach ($tickets_removed as $id) {
1342
+			$id = absint($id);
1343
+			// get the ticket for this id
1344
+			$tkt_to_remove = EE_Registry::instance()->load_model('Ticket')->get_one_by_ID($id);
1345
+			// need to get all the related datetimes on this ticket and remove from every single one of them (remember this process can ONLY kick off if there are NO tkts_sold)
1346
+			$dtts = $tkt_to_remove->get_many_related('Datetime');
1347
+			foreach ($dtts as $dtt) {
1348
+				$tkt_to_remove->_remove_relation_to($dtt, 'Datetime');
1349
+			}
1350
+			// need to do the same for prices (except these prices can also be deleted because again, tickets can only be trashed if they don't have any TKTs sold (otherwise they are just archived))
1351
+			$tkt_to_remove->delete_related_permanently('Price');
1352
+			// finally let's delete this ticket (which should not be blocked at this point b/c we've removed all our relationships)
1353
+			$tkt_to_remove->delete_permanently();
1354
+		}
1355
+		return array($saved_dtt, $saved_tickets);
1356
+	}
1357
+
1358
+
1359
+	/**
1360
+	 * This attaches a list of given prices to a ticket.
1361
+	 * Note we dont' have to worry about ever removing relationships (or archiving prices) because if there is a change
1362
+	 * in price information on a ticket, a new ticket is created anyways so the archived ticket will retain the old
1363
+	 * price info and prices are automatically "archived" via the ticket.
1364
+	 *
1365
+	 * @access  private
1366
+	 * @param array     $prices     Array of prices from the form.
1367
+	 * @param EE_Ticket $ticket     EE_Ticket object that prices are being attached to.
1368
+	 * @param bool      $new_prices Whether attach existing incoming prices or create new ones.
1369
+	 * @return  void
1370
+	 */
1371
+	private function _add_prices_to_ticket($prices, EE_Ticket $ticket, $new_prices = false)
1372
+	{
1373
+		foreach ($prices as $row => $prc) {
1374
+			$PRC_values = array(
1375
+				'PRC_ID'         => ! empty($prc['PRC_ID']) ? $prc['PRC_ID'] : null,
1376
+				'PRT_ID'         => ! empty($prc['PRT_ID']) ? $prc['PRT_ID'] : null,
1377
+				'PRC_amount'     => ! empty($prc['PRC_amount']) ? $prc['PRC_amount'] : 0,
1378
+				'PRC_name'       => ! empty($prc['PRC_name']) ? $prc['PRC_name'] : '',
1379
+				'PRC_desc'       => ! empty($prc['PRC_desc']) ? $prc['PRC_desc'] : '',
1380
+				'PRC_is_default' => 0, // make sure prices are NOT set as default from this context
1381
+				'PRC_order'      => $row,
1382
+			);
1383
+			if ($new_prices || empty($PRC_values['PRC_ID'])) {
1384
+				$PRC_values['PRC_ID'] = 0;
1385
+				$PRC = EE_Registry::instance()->load_class('Price', array($PRC_values), false, false);
1386
+			} else {
1387
+				$PRC = EE_Registry::instance()->load_model('Price')->get_one_by_ID($prc['PRC_ID']);
1388
+				// update this price with new values
1389
+				foreach ($PRC_values as $field => $newprc) {
1390
+					$PRC->set($field, $newprc);
1391
+				}
1392
+				$PRC->save();
1393
+			}
1394
+			$ticket->_add_relation_to($PRC, 'Price');
1395
+		}
1396
+	}
1397
+
1398
+
1399
+	/**
1400
+	 * Add in our autosave ajax handlers
1401
+	 *
1402
+	 */
1403
+	protected function _ee_autosave_create_new()
1404
+	{
1405
+	}
1406
+
1407
+
1408
+	/**
1409
+	 * More autosave handlers.
1410
+	 */
1411
+	protected function _ee_autosave_edit()
1412
+	{
1413
+		return; // TEMPORARILY EXITING CAUSE THIS IS A TODO
1414
+	}
1415
+
1416
+
1417
+	/**
1418
+	 *    _generate_publish_box_extra_content
1419
+	 */
1420
+	private function _generate_publish_box_extra_content()
1421
+	{
1422
+		// load formatter helper
1423
+		// args for getting related registrations
1424
+		$approved_query_args = array(
1425
+			array(
1426
+				'REG_deleted' => 0,
1427
+				'STS_ID'      => EEM_Registration::status_id_approved,
1428
+			),
1429
+		);
1430
+		$not_approved_query_args = array(
1431
+			array(
1432
+				'REG_deleted' => 0,
1433
+				'STS_ID'      => EEM_Registration::status_id_not_approved,
1434
+			),
1435
+		);
1436
+		$pending_payment_query_args = array(
1437
+			array(
1438
+				'REG_deleted' => 0,
1439
+				'STS_ID'      => EEM_Registration::status_id_pending_payment,
1440
+			),
1441
+		);
1442
+		// publish box
1443
+		$publish_box_extra_args = array(
1444
+			'view_approved_reg_url'        => add_query_arg(
1445
+				array(
1446
+					'action'      => 'default',
1447
+					'event_id'    => $this->_cpt_model_obj->ID(),
1448
+					'_reg_status' => EEM_Registration::status_id_approved,
1449
+				),
1450
+				REG_ADMIN_URL
1451
+			),
1452
+			'view_not_approved_reg_url'    => add_query_arg(
1453
+				array(
1454
+					'action'      => 'default',
1455
+					'event_id'    => $this->_cpt_model_obj->ID(),
1456
+					'_reg_status' => EEM_Registration::status_id_not_approved,
1457
+				),
1458
+				REG_ADMIN_URL
1459
+			),
1460
+			'view_pending_payment_reg_url' => add_query_arg(
1461
+				array(
1462
+					'action'      => 'default',
1463
+					'event_id'    => $this->_cpt_model_obj->ID(),
1464
+					'_reg_status' => EEM_Registration::status_id_pending_payment,
1465
+				),
1466
+				REG_ADMIN_URL
1467
+			),
1468
+			'approved_regs'                => $this->_cpt_model_obj->count_related(
1469
+				'Registration',
1470
+				$approved_query_args
1471
+			),
1472
+			'not_approved_regs'            => $this->_cpt_model_obj->count_related(
1473
+				'Registration',
1474
+				$not_approved_query_args
1475
+			),
1476
+			'pending_payment_regs'         => $this->_cpt_model_obj->count_related(
1477
+				'Registration',
1478
+				$pending_payment_query_args
1479
+			),
1480
+			'misc_pub_section_class'       => apply_filters(
1481
+				'FHEE_Events_Admin_Page___generate_publish_box_extra_content__misc_pub_section_class',
1482
+				'misc-pub-section'
1483
+			),
1484
+		);
1485
+		ob_start();
1486
+		do_action(
1487
+			'AHEE__Events_Admin_Page___generate_publish_box_extra_content__event_editor_overview_add',
1488
+			$this->_cpt_model_obj
1489
+		);
1490
+		$publish_box_extra_args['event_editor_overview_add'] = ob_get_clean();
1491
+		// load template
1492
+		EEH_Template::display_template(
1493
+			EVENTS_TEMPLATE_PATH . 'event_publish_box_extras.template.php',
1494
+			$publish_box_extra_args
1495
+		);
1496
+	}
1497
+
1498
+
1499
+	/**
1500
+	 * @return EE_Event
1501
+	 */
1502
+	public function get_event_object()
1503
+	{
1504
+		return $this->_cpt_model_obj;
1505
+	}
1506
+
1507
+
1508
+
1509
+
1510
+	/** METABOXES * */
1511
+	/**
1512
+	 * _register_event_editor_meta_boxes
1513
+	 * add all metaboxes related to the event_editor
1514
+	 *
1515
+	 * @return void
1516
+	 */
1517
+	protected function _register_event_editor_meta_boxes()
1518
+	{
1519
+		$this->verify_cpt_object();
1520
+		add_meta_box(
1521
+			'espresso_event_editor_tickets',
1522
+			esc_html__('Event Datetime & Ticket', 'event_espresso'),
1523
+			array($this, 'ticket_metabox'),
1524
+			$this->page_slug,
1525
+			'normal',
1526
+			'high'
1527
+		);
1528
+		add_meta_box(
1529
+			'espresso_event_editor_event_options',
1530
+			esc_html__('Event Registration Options', 'event_espresso'),
1531
+			array($this, 'registration_options_meta_box'),
1532
+			$this->page_slug,
1533
+			'side',
1534
+			'default'
1535
+		);
1536
+		// NOTE: if you're looking for other metaboxes in here,
1537
+		// where a metabox has a related management page in the admin
1538
+		// you will find it setup in the related management page's "_Hooks" file.
1539
+		// i.e. messages metabox is found in "espresso_events_Messages_Hooks.class.php".
1540
+	}
1541
+
1542
+
1543
+	/**
1544
+	 * @throws DomainException
1545
+	 * @throws EE_Error
1546
+	 */
1547
+	public function ticket_metabox()
1548
+	{
1549
+		$existing_datetime_ids = $existing_ticket_ids = array();
1550
+		// defaults for template args
1551
+		$template_args = array(
1552
+			'existing_datetime_ids'    => '',
1553
+			'event_datetime_help_link' => '',
1554
+			'ticket_options_help_link' => '',
1555
+			'time'                     => null,
1556
+			'ticket_rows'              => '',
1557
+			'existing_ticket_ids'      => '',
1558
+			'total_ticket_rows'        => 1,
1559
+			'ticket_js_structure'      => '',
1560
+			'trash_icon'               => 'ee-lock-icon',
1561
+			'disabled'                 => '',
1562
+		);
1563
+		$event_id = is_object($this->_cpt_model_obj) ? $this->_cpt_model_obj->ID() : null;
1564
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1565
+		/**
1566
+		 * 1. Start with retrieving Datetimes
1567
+		 * 2. Fore each datetime get related tickets
1568
+		 * 3. For each ticket get related prices
1569
+		 */
1570
+		$times = EE_Registry::instance()->load_model('Datetime')->get_all_event_dates($event_id);
1571
+		/** @type EE_Datetime $first_datetime */
1572
+		$first_datetime = reset($times);
1573
+		// do we get related tickets?
1574
+		if ($first_datetime instanceof EE_Datetime
1575
+			&& $first_datetime->ID() !== 0
1576
+		) {
1577
+			$existing_datetime_ids[] = $first_datetime->get('DTT_ID');
1578
+			$template_args['time'] = $first_datetime;
1579
+			$related_tickets = $first_datetime->tickets(
1580
+				array(
1581
+					array('OR' => array('TKT_deleted' => 1, 'TKT_deleted*' => 0)),
1582
+					'default_where_conditions' => 'none',
1583
+				)
1584
+			);
1585
+			if (! empty($related_tickets)) {
1586
+				$template_args['total_ticket_rows'] = count($related_tickets);
1587
+				$row = 0;
1588
+				foreach ($related_tickets as $ticket) {
1589
+					$existing_ticket_ids[] = $ticket->get('TKT_ID');
1590
+					$template_args['ticket_rows'] .= $this->_get_ticket_row($ticket, false, $row);
1591
+					$row++;
1592
+				}
1593
+			} else {
1594
+				$template_args['total_ticket_rows'] = 1;
1595
+				/** @type EE_Ticket $ticket */
1596
+				$ticket = EE_Registry::instance()->load_model('Ticket')->create_default_object();
1597
+				$template_args['ticket_rows'] .= $this->_get_ticket_row($ticket);
1598
+			}
1599
+		} else {
1600
+			$template_args['time'] = $times[0];
1601
+			/** @type EE_Ticket $ticket */
1602
+			$ticket = EE_Registry::instance()->load_model('Ticket')->get_all_default_tickets();
1603
+			$template_args['ticket_rows'] .= $this->_get_ticket_row($ticket[1]);
1604
+			// NOTE: we're just sending the first default row
1605
+			// (decaf can't manage default tickets so this should be sufficient);
1606
+		}
1607
+		$template_args['event_datetime_help_link'] = $this->_get_help_tab_link(
1608
+			'event_editor_event_datetimes_help_tab'
1609
+		);
1610
+		$template_args['ticket_options_help_link'] = $this->_get_help_tab_link('ticket_options_info');
1611
+		$template_args['existing_datetime_ids'] = implode(',', $existing_datetime_ids);
1612
+		$template_args['existing_ticket_ids'] = implode(',', $existing_ticket_ids);
1613
+		$template_args['ticket_js_structure'] = $this->_get_ticket_row(
1614
+			EE_Registry::instance()->load_model('Ticket')->create_default_object(),
1615
+			true
1616
+		);
1617
+		$template = apply_filters(
1618
+			'FHEE__Events_Admin_Page__ticket_metabox__template',
1619
+			EVENTS_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php'
1620
+		);
1621
+		EEH_Template::display_template($template, $template_args);
1622
+	}
1623
+
1624
+
1625
+	/**
1626
+	 * Setup an individual ticket form for the decaf event editor page
1627
+	 *
1628
+	 * @access private
1629
+	 * @param  EE_Ticket $ticket   the ticket object
1630
+	 * @param  boolean   $skeleton whether we're generating a skeleton for js manipulation
1631
+	 * @param int        $row
1632
+	 * @return string generated html for the ticket row.
1633
+	 */
1634
+	private function _get_ticket_row($ticket, $skeleton = false, $row = 0)
1635
+	{
1636
+		$template_args = array(
1637
+			'tkt_status_class'    => ' tkt-status-' . $ticket->ticket_status(),
1638
+			'tkt_archive_class'   => $ticket->ticket_status() === EE_Ticket::archived && ! $skeleton ? ' tkt-archived'
1639
+				: '',
1640
+			'ticketrow'           => $skeleton ? 'TICKETNUM' : $row,
1641
+			'TKT_ID'              => $ticket->get('TKT_ID'),
1642
+			'TKT_name'            => $ticket->get('TKT_name'),
1643
+			'TKT_start_date'      => $skeleton ? '' : $ticket->get_date('TKT_start_date', 'Y-m-d h:i a'),
1644
+			'TKT_end_date'        => $skeleton ? '' : $ticket->get_date('TKT_end_date', 'Y-m-d h:i a'),
1645
+			'TKT_is_default'      => $ticket->get('TKT_is_default'),
1646
+			'TKT_qty'             => $ticket->get_pretty('TKT_qty', 'input'),
1647
+			'edit_ticketrow_name' => $skeleton ? 'TICKETNAMEATTR' : 'edit_tickets',
1648
+			'TKT_sold'            => $skeleton ? 0 : $ticket->get('TKT_sold'),
1649
+			'trash_icon'          => ($skeleton || (! empty($ticket) && ! $ticket->get('TKT_deleted')))
1650
+									 && (! empty($ticket) && $ticket->get('TKT_sold') === 0)
1651
+				? 'trash-icon dashicons dashicons-post-trash clickable' : 'ee-lock-icon',
1652
+			'disabled'            => $skeleton || (! empty($ticket) && ! $ticket->get('TKT_deleted')) ? ''
1653
+				: ' disabled=disabled',
1654
+		);
1655
+		$price = $ticket->ID() !== 0
1656
+			? $ticket->get_first_related('Price', array('default_where_conditions' => 'none'))
1657
+			: EE_Registry::instance()->load_model('Price')->create_default_object();
1658
+		$price_args = array(
1659
+			'price_currency_symbol' => EE_Registry::instance()->CFG->currency->sign,
1660
+			'PRC_amount'            => $price->get('PRC_amount'),
1661
+			'PRT_ID'                => $price->get('PRT_ID'),
1662
+			'PRC_ID'                => $price->get('PRC_ID'),
1663
+			'PRC_is_default'        => $price->get('PRC_is_default'),
1664
+		);
1665
+		// make sure we have default start and end dates if skeleton
1666
+		// handle rows that should NOT be empty
1667
+		if (empty($template_args['TKT_start_date'])) {
1668
+			// if empty then the start date will be now.
1669
+			$template_args['TKT_start_date'] = date('Y-m-d h:i a', current_time('timestamp'));
1670
+		}
1671
+		if (empty($template_args['TKT_end_date'])) {
1672
+			// get the earliest datetime (if present);
1673
+			$earliest_dtt = $this->_cpt_model_obj->ID() > 0
1674
+				? $this->_cpt_model_obj->get_first_related(
1675
+					'Datetime',
1676
+					array('order_by' => array('DTT_EVT_start' => 'ASC'))
1677
+				)
1678
+				: null;
1679
+			if (! empty($earliest_dtt)) {
1680
+				$template_args['TKT_end_date'] = $earliest_dtt->get_datetime('DTT_EVT_start', 'Y-m-d', 'h:i a');
1681
+			} else {
1682
+				$template_args['TKT_end_date'] = date(
1683
+					'Y-m-d h:i a',
1684
+					mktime(0, 0, 0, date("m"), date("d") + 7, date("Y"))
1685
+				);
1686
+			}
1687
+		}
1688
+		$template_args = array_merge($template_args, $price_args);
1689
+		$template = apply_filters(
1690
+			'FHEE__Events_Admin_Page__get_ticket_row__template',
1691
+			EVENTS_TEMPLATE_PATH . 'event_tickets_metabox_ticket_row.template.php',
1692
+			$ticket
1693
+		);
1694
+		return EEH_Template::display_template($template, $template_args, true);
1695
+	}
1696
+
1697
+
1698
+	/**
1699
+	 * @throws DomainException
1700
+	 */
1701
+	public function registration_options_meta_box()
1702
+	{
1703
+		$yes_no_values = array(
1704
+			array('id' => true, 'text' => esc_html__('Yes', 'event_espresso')),
1705
+			array('id' => false, 'text' => esc_html__('No', 'event_espresso')),
1706
+		);
1707
+		$default_reg_status_values = EEM_Registration::reg_status_array(
1708
+			array(
1709
+				EEM_Registration::status_id_cancelled,
1710
+				EEM_Registration::status_id_declined,
1711
+				EEM_Registration::status_id_incomplete,
1712
+			),
1713
+			true
1714
+		);
1715
+		// $template_args['is_active_select'] = EEH_Form_Fields::select_input('is_active', $yes_no_values, $this->_cpt_model_obj->is_active());
1716
+		$template_args['_event'] = $this->_cpt_model_obj;
1717
+		$template_args['active_status'] = $this->_cpt_model_obj->pretty_active_status(false);
1718
+		$template_args['additional_limit'] = $this->_cpt_model_obj->additional_limit();
1719
+		$template_args['default_registration_status'] = EEH_Form_Fields::select_input(
1720
+			'default_reg_status',
1721
+			$default_reg_status_values,
1722
+			$this->_cpt_model_obj->default_registration_status()
1723
+		);
1724
+		$template_args['display_description'] = EEH_Form_Fields::select_input(
1725
+			'display_desc',
1726
+			$yes_no_values,
1727
+			$this->_cpt_model_obj->display_description()
1728
+		);
1729
+		$template_args['display_ticket_selector'] = EEH_Form_Fields::select_input(
1730
+			'display_ticket_selector',
1731
+			$yes_no_values,
1732
+			$this->_cpt_model_obj->display_ticket_selector(),
1733
+			'',
1734
+			'',
1735
+			false
1736
+		);
1737
+		$template_args['additional_registration_options'] = apply_filters(
1738
+			'FHEE__Events_Admin_Page__registration_options_meta_box__additional_registration_options',
1739
+			'',
1740
+			$template_args,
1741
+			$yes_no_values,
1742
+			$default_reg_status_values
1743
+		);
1744
+		EEH_Template::display_template(
1745
+			EVENTS_TEMPLATE_PATH . 'event_registration_options.template.php',
1746
+			$template_args
1747
+		);
1748
+	}
1749
+
1750
+
1751
+	/**
1752
+	 * _get_events()
1753
+	 * This method simply returns all the events (for the given _view and paging)
1754
+	 *
1755
+	 * @access public
1756
+	 * @param int  $per_page     count of items per page (20 default);
1757
+	 * @param int  $current_page what is the current page being viewed.
1758
+	 * @param bool $count        if TRUE then we just return a count of ALL events matching the given _view.
1759
+	 *                           If FALSE then we return an array of event objects
1760
+	 *                           that match the given _view and paging parameters.
1761
+	 * @return array an array of event objects.
1762
+	 */
1763
+	public function get_events($per_page = 10, $current_page = 1, $count = false)
1764
+	{
1765
+		$EEME = $this->_event_model();
1766
+		$offset = ($current_page - 1) * $per_page;
1767
+		$limit = $count ? null : $offset . ',' . $per_page;
1768
+		$orderby = isset($this->_req_data['orderby']) ? $this->_req_data['orderby'] : 'EVT_ID';
1769
+		$order = isset($this->_req_data['order']) ? $this->_req_data['order'] : "DESC";
1770
+		if (isset($this->_req_data['month_range'])) {
1771
+			$pieces = explode(' ', $this->_req_data['month_range'], 3);
1772
+			// simulate the FIRST day of the month, that fixes issues for months like February
1773
+			// where PHP doesn't know what to assume for date.
1774
+			// @see https://events.codebasehq.com/projects/event-espresso/tickets/10437
1775
+			$month_r = ! empty($pieces[0]) ? date('m', \EEH_DTT_Helper::first_of_month_timestamp($pieces[0])) : '';
1776
+			$year_r = ! empty($pieces[1]) ? $pieces[1] : '';
1777
+		}
1778
+		$where = array();
1779
+		$status = isset($this->_req_data['status']) ? $this->_req_data['status'] : null;
1780
+		// determine what post_status our condition will have for the query.
1781
+		switch ($status) {
1782
+			case 'month':
1783
+			case 'today':
1784
+			case null:
1785
+			case 'all':
1786
+				break;
1787
+			case 'draft':
1788
+				$where['status'] = array('IN', array('draft', 'auto-draft'));
1789
+				break;
1790
+			default:
1791
+				$where['status'] = $status;
1792
+		}
1793
+		// categories?
1794
+		$category = isset($this->_req_data['EVT_CAT']) && $this->_req_data['EVT_CAT'] > 0
1795
+			? $this->_req_data['EVT_CAT'] : null;
1796
+		if (! empty($category)) {
1797
+			$where['Term_Taxonomy.taxonomy'] = EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY;
1798
+			$where['Term_Taxonomy.term_id'] = $category;
1799
+		}
1800
+		// date where conditions
1801
+		$start_formats = EEM_Datetime::instance()->get_formats_for('DTT_EVT_start');
1802
+		if (isset($this->_req_data['month_range']) && $this->_req_data['month_range'] != '') {
1803
+			$DateTime = new DateTime(
1804
+				$year_r . '-' . $month_r . '-01 00:00:00',
1805
+				new DateTimeZone(EEM_Datetime::instance()->get_timezone())
1806
+			);
1807
+			$start = $DateTime->format(implode(' ', $start_formats));
1808
+			$end = $DateTime->setDate(
1809
+				$year_r,
1810
+				$month_r,
1811
+				$DateTime
1812
+					->format('t')
1813
+			)->setTime(23, 59, 59)
1814
+							->format(implode(' ', $start_formats));
1815
+			$where['Datetime.DTT_EVT_start'] = array('BETWEEN', array($start, $end));
1816
+		} elseif (isset($this->_req_data['status']) && $this->_req_data['status'] == 'today') {
1817
+			$DateTime = new DateTime('now', new DateTimeZone(EEM_Event::instance()->get_timezone()));
1818
+			$start = $DateTime->setTime(0, 0, 0)->format(implode(' ', $start_formats));
1819
+			$end = $DateTime->setTime(23, 59, 59)->format(implode(' ', $start_formats));
1820
+			$where['Datetime.DTT_EVT_start'] = array('BETWEEN', array($start, $end));
1821
+		} elseif (isset($this->_req_data['status']) && $this->_req_data['status'] == 'month') {
1822
+			$now = date('Y-m-01');
1823
+			$DateTime = new DateTime($now, new DateTimeZone(EEM_Event::instance()->get_timezone()));
1824
+			$start = $DateTime->setTime(0, 0, 0)->format(implode(' ', $start_formats));
1825
+			$end = $DateTime->setDate(date('Y'), date('m'), $DateTime->format('t'))
1826
+							->setTime(23, 59, 59)
1827
+							->format(implode(' ', $start_formats));
1828
+			$where['Datetime.DTT_EVT_start'] = array('BETWEEN', array($start, $end));
1829
+		}
1830
+		if (! EE_Registry::instance()->CAP->current_user_can('ee_read_others_events', 'get_events')) {
1831
+			$where['EVT_wp_user'] = get_current_user_id();
1832
+		} else {
1833
+			if (! isset($where['status'])) {
1834
+				if (! EE_Registry::instance()->CAP->current_user_can('ee_read_private_events', 'get_events')) {
1835
+					$where['OR'] = array(
1836
+						'status*restrict_private' => array('!=', 'private'),
1837
+						'AND'                     => array(
1838
+							'status*inclusive' => array('=', 'private'),
1839
+							'EVT_wp_user'      => get_current_user_id(),
1840
+						),
1841
+					);
1842
+				}
1843
+			}
1844
+		}
1845
+		if (isset($this->_req_data['EVT_wp_user'])) {
1846
+			if ($this->_req_data['EVT_wp_user'] != get_current_user_id()
1847
+				&& EE_Registry::instance()->CAP->current_user_can('ee_read_others_events', 'get_events')
1848
+			) {
1849
+				$where['EVT_wp_user'] = $this->_req_data['EVT_wp_user'];
1850
+			}
1851
+		}
1852
+		// search query handling
1853
+		if (isset($this->_req_data['s'])) {
1854
+			$search_string = '%' . $this->_req_data['s'] . '%';
1855
+			$where['OR'] = array(
1856
+				'EVT_name'       => array('LIKE', $search_string),
1857
+				'EVT_desc'       => array('LIKE', $search_string),
1858
+				'EVT_short_desc' => array('LIKE', $search_string),
1859
+			);
1860
+		}
1861
+		// filter events by venue.
1862
+		if (isset($this->_req_data['venue']) && ! empty($this->_req_data['venue'])) {
1863
+			$where['Venue.VNU_ID'] = absint($this->_req_data['venue']);
1864
+		}
1865
+		$where = apply_filters('FHEE__Events_Admin_Page__get_events__where', $where, $this->_req_data);
1866
+		$query_params = apply_filters(
1867
+			'FHEE__Events_Admin_Page__get_events__query_params',
1868
+			array(
1869
+				$where,
1870
+				'limit'    => $limit,
1871
+				'order_by' => $orderby,
1872
+				'order'    => $order,
1873
+				'group_by' => 'EVT_ID',
1874
+			),
1875
+			$this->_req_data
1876
+		);
1877
+		// let's first check if we have special requests coming in.
1878
+		if (isset($this->_req_data['active_status'])) {
1879
+			switch ($this->_req_data['active_status']) {
1880
+				case 'upcoming':
1881
+					return $EEME->get_upcoming_events($query_params, $count);
1882
+					break;
1883
+				case 'expired':
1884
+					return $EEME->get_expired_events($query_params, $count);
1885
+					break;
1886
+				case 'active':
1887
+					return $EEME->get_active_events($query_params, $count);
1888
+					break;
1889
+				case 'inactive':
1890
+					return $EEME->get_inactive_events($query_params, $count);
1891
+					break;
1892
+			}
1893
+		}
1894
+
1895
+		$events = $count ? $EEME->count(array($where), 'EVT_ID', true) : $EEME->get_all($query_params);
1896
+		return $events;
1897
+	}
1898
+
1899
+
1900
+	/**
1901
+	 * handling for WordPress CPT actions (trash, restore, delete)
1902
+	 *
1903
+	 * @param string $post_id
1904
+	 */
1905
+	public function trash_cpt_item($post_id)
1906
+	{
1907
+		$this->_req_data['EVT_ID'] = $post_id;
1908
+		$this->_trash_or_restore_event('trash', false);
1909
+	}
1910
+
1911
+
1912
+	/**
1913
+	 * @param string $post_id
1914
+	 */
1915
+	public function restore_cpt_item($post_id)
1916
+	{
1917
+		$this->_req_data['EVT_ID'] = $post_id;
1918
+		$this->_trash_or_restore_event('draft', false);
1919
+	}
1920
+
1921
+
1922
+	/**
1923
+	 * @param string $post_id
1924
+	 */
1925
+	public function delete_cpt_item($post_id)
1926
+	{
1927
+		throw new EE_Error(esc_html__('Please contact Event Espresso support with the details of the steps taken to produce this error.', 'event_espresso'));
1928
+		$this->_req_data['EVT_ID'] = $post_id;
1929
+		$this->_delete_event();
1930
+	}
1931
+
1932
+
1933
+	/**
1934
+	 * _trash_or_restore_event
1935
+	 *
1936
+	 * @access protected
1937
+	 * @param  string $event_status
1938
+	 * @param bool    $redirect_after
1939
+	 */
1940
+	protected function _trash_or_restore_event($event_status = 'trash', $redirect_after = true)
1941
+	{
1942
+		// determine the event id and set to array.
1943
+		$EVT_ID = isset($this->_req_data['EVT_ID']) ? absint($this->_req_data['EVT_ID']) : false;
1944
+		// loop thru events
1945
+		if ($EVT_ID) {
1946
+			// clean status
1947
+			$event_status = sanitize_key($event_status);
1948
+			// grab status
1949
+			if (! empty($event_status)) {
1950
+				$success = $this->_change_event_status($EVT_ID, $event_status);
1951
+			} else {
1952
+				$success = false;
1953
+				$msg = esc_html__(
1954
+					'An error occurred. The event could not be moved to the trash because a valid event status was not not supplied.',
1955
+					'event_espresso'
1956
+				);
1957
+				EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1958
+			}
1959
+		} else {
1960
+			$success = false;
1961
+			$msg = esc_html__(
1962
+				'An error occurred. The event could not be moved to the trash because a valid event ID was not not supplied.',
1963
+				'event_espresso'
1964
+			);
1965
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1966
+		}
1967
+		$action = $event_status == 'trash' ? 'moved to the trash' : 'restored from the trash';
1968
+		if ($redirect_after) {
1969
+			$this->_redirect_after_action($success, 'Event', $action, array('action' => 'default'));
1970
+		}
1971
+	}
1972
+
1973
+
1974
+	/**
1975
+	 * _trash_or_restore_events
1976
+	 *
1977
+	 * @access protected
1978
+	 * @param  string $event_status
1979
+	 * @return void
1980
+	 */
1981
+	protected function _trash_or_restore_events($event_status = 'trash')
1982
+	{
1983
+		// clean status
1984
+		$event_status = sanitize_key($event_status);
1985
+		// grab status
1986
+		if (! empty($event_status)) {
1987
+			$success = true;
1988
+			// determine the event id and set to array.
1989
+			$EVT_IDs = isset($this->_req_data['EVT_IDs']) ? (array) $this->_req_data['EVT_IDs'] : array();
1990
+			// loop thru events
1991
+			foreach ($EVT_IDs as $EVT_ID) {
1992
+				if ($EVT_ID = absint($EVT_ID)) {
1993
+					$results = $this->_change_event_status($EVT_ID, $event_status);
1994
+					$success = $results !== false ? $success : false;
1995
+				} else {
1996
+					$msg = sprintf(
1997
+						esc_html__(
1998
+							'An error occurred. Event #%d could not be moved to the trash because a valid event ID was not not supplied.',
1999
+							'event_espresso'
2000
+						),
2001
+						$EVT_ID
2002
+					);
2003
+					EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2004
+					$success = false;
2005
+				}
2006
+			}
2007
+		} else {
2008
+			$success = false;
2009
+			$msg = esc_html__(
2010
+				'An error occurred. The event could not be moved to the trash because a valid event status was not not supplied.',
2011
+				'event_espresso'
2012
+			);
2013
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2014
+		}
2015
+		// in order to force a pluralized result message we need to send back a success status greater than 1
2016
+		$success = $success ? 2 : false;
2017
+		$action = $event_status == 'trash' ? 'moved to the trash' : 'restored from the trash';
2018
+		$this->_redirect_after_action($success, 'Events', $action, array('action' => 'default'));
2019
+	}
2020
+
2021
+
2022
+	/**
2023
+	 * _trash_or_restore_events
2024
+	 *
2025
+	 * @access  private
2026
+	 * @param  int    $EVT_ID
2027
+	 * @param  string $event_status
2028
+	 * @return bool
2029
+	 */
2030
+	private function _change_event_status($EVT_ID = 0, $event_status = '')
2031
+	{
2032
+		// grab event id
2033
+		if (! $EVT_ID) {
2034
+			$msg = esc_html__(
2035
+				'An error occurred. No Event ID or an invalid Event ID was received.',
2036
+				'event_espresso'
2037
+			);
2038
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2039
+			return false;
2040
+		}
2041
+		$this->_cpt_model_obj = EEM_Event::instance()->get_one_by_ID($EVT_ID);
2042
+		// clean status
2043
+		$event_status = sanitize_key($event_status);
2044
+		// grab status
2045
+		if (empty($event_status)) {
2046
+			$msg = esc_html__(
2047
+				'An error occurred. No Event Status or an invalid Event Status was received.',
2048
+				'event_espresso'
2049
+			);
2050
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2051
+			return false;
2052
+		}
2053
+		// was event trashed or restored ?
2054
+		switch ($event_status) {
2055
+			case 'draft':
2056
+				$action = 'restored from the trash';
2057
+				$hook = 'AHEE_event_restored_from_trash';
2058
+				break;
2059
+			case 'trash':
2060
+				$action = 'moved to the trash';
2061
+				$hook = 'AHEE_event_moved_to_trash';
2062
+				break;
2063
+			default:
2064
+				$action = 'updated';
2065
+				$hook = false;
2066
+		}
2067
+		// use class to change status
2068
+		$this->_cpt_model_obj->set_status($event_status);
2069
+		$success = $this->_cpt_model_obj->save();
2070
+		if ($success === false) {
2071
+			$msg = sprintf(esc_html__('An error occurred. The event could not be %s.', 'event_espresso'), $action);
2072
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2073
+			return false;
2074
+		}
2075
+		if ($hook) {
2076
+			do_action($hook);
2077
+		}
2078
+		return true;
2079
+	}
2080
+
2081
+
2082
+	/**
2083
+	 * _delete_event
2084
+	 *
2085
+	 * @access protected
2086
+	 * @param bool $redirect_after
2087
+	 */
2088
+	protected function _delete_event()
2089
+	{
2090
+		$this->generateDeletionPreview(isset($this->_req_data['EVT_ID']) ? $this->_req_data['EVT_ID'] : array());
2091
+	}
2092
+
2093
+
2094
+	/**
2095
+	 * _delete_events
2096
+	 *
2097
+	 * @access protected
2098
+	 * @return void
2099
+	 */
2100
+	protected function _delete_events()
2101
+	{
2102
+		$this->generateDeletionPreview(isset($this->_req_data['EVT_IDs']) ? (array) $this->_req_data['EVT_IDs'] : array());
2103
+	}
2104
+
2105
+	protected function generateDeletionPreview($event_ids)
2106
+	{
2107
+		$event_ids = (array) $event_ids;
2108
+		// Set a code we can use to reference this deletion task in the batch jobs and preview page.
2109
+		$deletion_job_code = wp_generate_password(6, false);
2110
+		$return_url = EE_Admin_Page::add_query_args_and_nonce(
2111
+			[
2112
+				'action' => 'preview_deletion',
2113
+				'deletion_job_code' => $deletion_job_code,
2114
+			],
2115
+			$this->_admin_base_url
2116
+		);
2117
+		$event_ids = array_map(
2118
+			'intval',
2119
+			$event_ids
2120
+		);
2121
+
2122
+		EEH_URL::safeRedirectAndExit(
2123
+			EE_Admin_Page::add_query_args_and_nonce(
2124
+				array(
2125
+					'page'        => 'espresso_batch',
2126
+					'batch'       => EED_Batch::batch_job,
2127
+					'EVT_IDs'      => $event_ids,
2128
+					'deletion_job_code' => $deletion_job_code,
2129
+					'job_handler' => urlencode('EventEspressoBatchRequest\JobHandlers\PreviewEventDeletion'),
2130
+					'return_url'  => urlencode($return_url),
2131
+				),
2132
+				admin_url()
2133
+			)
2134
+		);
2135
+	}
2136
+
2137
+	/**
2138
+	 * Checks for a POST submission
2139
+	 * @since $VID:$
2140
+	 */
2141
+	protected function confirmDeletion()
2142
+	{
2143
+		$deletion_job_code = isset($this->_req_data['deletion_job_code']) ? $this->_req_data['deletion_job_code'] : '';
2144
+		$models_and_ids_to_delete = $this->getModelsAndIdsToDelete($deletion_job_code);
2145
+		$form = new ConfirmEventDeletionForm($models_and_ids_to_delete['Event']);
2146
+		// Initialize the form from the request, and check if its valid.
2147
+		$form->receive_form_submission($this->_req_data);
2148
+		if ($form->is_valid()) {
2149
+			// Redirect the user to the deletion batch job.
2150
+			EEH_URL::safeRedirectAndExit(
2151
+				EE_Admin_Page::add_query_args_and_nonce(
2152
+					array(
2153
+						'page'        => 'espresso_batch',
2154
+						'batch'       => EED_Batch::batch_job,
2155
+						'deletion_job_code' => $deletion_job_code,
2156
+						'job_handler' => urlencode('EventEspressoBatchRequest\JobHandlers\ExecuteBatchDeletion'),
2157
+						'return_url'  => urlencode(
2158
+							add_query_arg(
2159
+								[
2160
+									'status' => 'trash'
2161
+								],
2162
+								EVENTS_ADMIN_URL
2163
+							)
2164
+						)
2165
+					),
2166
+					admin_url()
2167
+				)
2168
+			);
2169
+		} else {
2170
+			// Dont' use $form->submission_error_message() because it adds the form input's label in front
2171
+			// of each validation error which ends up looking quite confusing.
2172
+			$validation_errors = $form->get_validation_errors_accumulated();
2173
+			foreach ($validation_errors as $validation_error) {
2174
+				 EE_Error::add_error(
2175
+					 $validation_error->getMessage(),
2176
+					 __FILE__,
2177
+					 __FUNCTION__,
2178
+					 __LINE__
2179
+				 );
2180
+			}
2181
+
2182
+			EEH_URL::safeRedirectAndExit(
2183
+				EE_Admin_Page::add_query_args_and_nonce(
2184
+					[
2185
+						'action' => 'preview_deletion',
2186
+						'deletion_job_code' => $deletion_job_code
2187
+					],
2188
+					$this->admin_base_url()
2189
+				)
2190
+			);
2191
+		}
2192
+	}
2193
+
2194
+	/**
2195
+	 * Uses the querystring and job option to figure out what we intend to delete.
2196
+	 * @since $VID:$
2197
+	 * @return array top-level keys are model names, and their values are arrays of IDs.
2198
+	 * @throws EE_Error
2199
+	 * @throws InvalidArgumentException
2200
+	 * @throws InvalidDataTypeException
2201
+	 * @throws InvalidInterfaceException
2202
+	 * @throws ReflectionException
2203
+	 * @throws UnexpectedEntityException
2204
+	 */
2205
+	protected function getModelsAndIdsToDelete($deletion_job_code)
2206
+	{
2207
+		if (! $deletion_job_code) {
2208
+			throw new Exception(esc_html__('We aren’t sure which job you are performing. Please press back in your browser and try again.', 'event_espresso'));
2209
+		}
2210
+		$deletion_data = get_option('ee_deletion_' . $deletion_job_code, []);
2211
+
2212
+		$models_and_ids_to_delete = [];
2213
+		foreach ($deletion_data as $root) {
2214
+			if (! $root instanceof ModelObjNode) {
2215
+				throw new UnexpectedEntityException($root, 'ModelObjNode');
2216
+			}
2217
+			$models_and_ids_to_delete = array_replace_recursive($models_and_ids_to_delete, $root->getIds());
2218
+		}
2219
+		return $models_and_ids_to_delete;
2220
+	}
2221
+
2222
+	/**
2223
+	 * A page for users to preview what exactly will be deleted, and confirm they want to delete it.
2224
+	 * @since $VID:$
2225
+	 * @throws EE_Error
2226
+	 */
2227
+	protected function previewDeletion()
2228
+	{
2229
+		$deletion_job_code = isset($this->_req_data['deletion_job_code']) ? $this->_req_data['deletion_job_code'] : '';
2230
+		$models_and_ids_to_delete = $this->getModelsAndIdsToDelete($deletion_job_code);
2231
+		$event_ids = isset($models_and_ids_to_delete['Event']) ? $models_and_ids_to_delete['Event'] : array();
2232
+		if (empty($event_ids) || ! is_array($event_ids)) {
2233
+			throw new EE_Error(
2234
+				esc_html__('No Events were found to delete.', 'event_espresso')
2235
+			);
2236
+		}
2237
+		$datetime_ids = isset($models_and_ids_to_delete['Datetime']) ? $models_and_ids_to_delete['Datetime'] : array();
2238
+		if (! is_array($datetime_ids)) {
2239
+			throw new UnexpectedEntityException($datetime_ids, 'array');
2240
+		}
2241
+		$registration_ids = isset($models_and_ids_to_delete['Registration']) ? $models_and_ids_to_delete['Registration'] : array();
2242
+		if (! is_array($registration_ids)) {
2243
+			throw new UnexpectedEntityException($registration_ids, 'array');
2244
+		}
2245
+		$num_registrations_to_show = 10;
2246
+		$reg_count = count($registration_ids);
2247
+		if ($reg_count > $num_registrations_to_show) {
2248
+			$registration_ids = array_slice($registration_ids, 0, $num_registrations_to_show);
2249
+		}
2250
+		$form = new ConfirmEventDeletionForm($event_ids);
2251
+		$events = EEM_Event::instance()->get_all_deleted_and_undeleted(
2252
+			[
2253
+				[
2254
+					'EVT_ID' => ['IN', $event_ids]
2255
+				]
2256
+			]
2257
+		);
2258
+		$datetimes = EEM_Datetime::instance()->get_all_deleted_and_undeleted(
2259
+			[
2260
+				[
2261
+					'DTT_ID' => ['IN', $datetime_ids]
2262
+				]
2263
+			]
2264
+		);
2265
+		$registrations = EEM_Registration::instance()->get_all_deleted_and_undeleted(
2266
+			[
2267
+				[
2268
+					'REG_ID' => ['IN', $registration_ids]
2269
+				]
2270
+			]
2271
+		);
2272
+		$confirm_deletion_args = [
2273
+			'action' => 'confirm_deletion',
2274
+			'deletion_job_code' => $deletion_job_code
2275
+		];
2276
+
2277
+		$this->_template_args['admin_page_content'] = EEH_Template::display_template(
2278
+			EVENTS_TEMPLATE_PATH . 'event_preview_deletion.template.php',
2279
+			[
2280
+				'form_url' => EE_Admin_Page::add_query_args_and_nonce(
2281
+					$confirm_deletion_args,
2282
+					$this->admin_base_url()
2283
+				),
2284
+				'form' => $form,
2285
+				'events' => $events,
2286
+				'datetimes' => $datetimes,
2287
+				'registrations' => $registrations,
2288
+				'reg_count' => $reg_count,
2289
+				'num_registrations_to_show' => $num_registrations_to_show
2290
+			],
2291
+			true
2292
+		);
2293
+		$this->display_admin_page_with_no_sidebar();
2294
+	}
2295
+
2296
+	/**
2297
+	 * _permanently_delete_event
2298
+	 *
2299
+	 * @access  private
2300
+	 * @param  int $EVT_ID
2301
+	 * @return bool
2302
+	 */
2303
+	private function _permanently_delete_event($EVT_ID = 0)
2304
+	{
2305
+		// grab event id
2306
+		if (! $EVT_ID) {
2307
+			$msg = esc_html__(
2308
+				'An error occurred. No Event ID or an invalid Event ID was received.',
2309
+				'event_espresso'
2310
+			);
2311
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2312
+			return false;
2313
+		}
2314
+		if (! $this->_cpt_model_obj instanceof EE_Event
2315
+			|| $this->_cpt_model_obj->ID() !== $EVT_ID
2316
+		) {
2317
+			$this->_cpt_model_obj = EEM_Event::instance()->get_one_by_ID($EVT_ID);
2318
+		}
2319
+		if (! $this->_cpt_model_obj instanceof EE_Event) {
2320
+			return false;
2321
+		}
2322
+		// need to delete related tickets and prices first.
2323
+		$datetimes = $this->_cpt_model_obj->get_many_related('Datetime');
2324
+		foreach ($datetimes as $datetime) {
2325
+			$this->_cpt_model_obj->_remove_relation_to($datetime, 'Datetime');
2326
+			$tickets = $datetime->get_many_related('Ticket');
2327
+			foreach ($tickets as $ticket) {
2328
+				$ticket->_remove_relation_to($datetime, 'Datetime');
2329
+				$ticket->delete_related_permanently('Price');
2330
+				$ticket->delete_permanently();
2331
+			}
2332
+			$datetime->delete();
2333
+		}
2334
+		// what about related venues or terms?
2335
+		$venues = $this->_cpt_model_obj->get_many_related('Venue');
2336
+		foreach ($venues as $venue) {
2337
+			$this->_cpt_model_obj->_remove_relation_to($venue, 'Venue');
2338
+		}
2339
+		// any attached question groups?
2340
+		$question_groups = $this->_cpt_model_obj->get_many_related('Question_Group');
2341
+		if (! empty($question_groups)) {
2342
+			foreach ($question_groups as $question_group) {
2343
+				$this->_cpt_model_obj->_remove_relation_to($question_group, 'Question_Group');
2344
+			}
2345
+		}
2346
+		// Message Template Groups
2347
+		$this->_cpt_model_obj->_remove_relations('Message_Template_Group');
2348
+		/** @type EE_Term_Taxonomy[] $term_taxonomies */
2349
+		$term_taxonomies = $this->_cpt_model_obj->term_taxonomies();
2350
+		foreach ($term_taxonomies as $term_taxonomy) {
2351
+			$this->_cpt_model_obj->remove_relation_to_term_taxonomy($term_taxonomy);
2352
+		}
2353
+		$success = $this->_cpt_model_obj->delete_permanently();
2354
+		// did it all go as planned ?
2355
+		if ($success) {
2356
+			$msg = sprintf(esc_html__('Event ID # %d has been deleted.', 'event_espresso'), $EVT_ID);
2357
+			EE_Error::add_success($msg);
2358
+		} else {
2359
+			$msg = sprintf(
2360
+				esc_html__('An error occurred. Event ID # %d could not be deleted.', 'event_espresso'),
2361
+				$EVT_ID
2362
+			);
2363
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2364
+			return false;
2365
+		}
2366
+		do_action('AHEE__Events_Admin_Page___permanently_delete_event__after_event_deleted', $EVT_ID);
2367
+		return true;
2368
+	}
2369
+
2370
+
2371
+	/**
2372
+	 * get total number of events
2373
+	 *
2374
+	 * @access public
2375
+	 * @return int
2376
+	 */
2377
+	public function total_events()
2378
+	{
2379
+		$count = EEM_Event::instance()->count(array('caps' => 'read_admin'), 'EVT_ID', true);
2380
+		return $count;
2381
+	}
2382
+
2383
+
2384
+	/**
2385
+	 * get total number of draft events
2386
+	 *
2387
+	 * @access public
2388
+	 * @return int
2389
+	 */
2390
+	public function total_events_draft()
2391
+	{
2392
+		$where = array(
2393
+			'status' => array('IN', array('draft', 'auto-draft')),
2394
+		);
2395
+		$count = EEM_Event::instance()->count(array($where, 'caps' => 'read_admin'), 'EVT_ID', true);
2396
+		return $count;
2397
+	}
2398
+
2399
+
2400
+	/**
2401
+	 * get total number of trashed events
2402
+	 *
2403
+	 * @access public
2404
+	 * @return int
2405
+	 */
2406
+	public function total_trashed_events()
2407
+	{
2408
+		$where = array(
2409
+			'status' => 'trash',
2410
+		);
2411
+		$count = EEM_Event::instance()->count(array($where, 'caps' => 'read_admin'), 'EVT_ID', true);
2412
+		return $count;
2413
+	}
2414
+
2415
+
2416
+	/**
2417
+	 *    _default_event_settings
2418
+	 *    This generates the Default Settings Tab
2419
+	 *
2420
+	 * @return void
2421
+	 * @throws EE_Error
2422
+	 */
2423
+	protected function _default_event_settings()
2424
+	{
2425
+		$this->_set_add_edit_form_tags('update_default_event_settings');
2426
+		$this->_set_publish_post_box_vars(null, false, false, null, false);
2427
+		$this->_template_args['admin_page_content'] = $this->_default_event_settings_form()->get_html();
2428
+		$this->display_admin_page_with_sidebar();
2429
+	}
2430
+
2431
+
2432
+	/**
2433
+	 * Return the form for event settings.
2434
+	 *
2435
+	 * @return EE_Form_Section_Proper
2436
+	 * @throws EE_Error
2437
+	 */
2438
+	protected function _default_event_settings_form()
2439
+	{
2440
+		$registration_config = EE_Registry::instance()->CFG->registration;
2441
+		$registration_stati_for_selection = EEM_Registration::reg_status_array(
2442
+			// exclude
2443
+			array(
2444
+				EEM_Registration::status_id_cancelled,
2445
+				EEM_Registration::status_id_declined,
2446
+				EEM_Registration::status_id_incomplete,
2447
+				EEM_Registration::status_id_wait_list,
2448
+			),
2449
+			true
2450
+		);
2451
+		return new EE_Form_Section_Proper(
2452
+			array(
2453
+				'name'            => 'update_default_event_settings',
2454
+				'html_id'         => 'update_default_event_settings',
2455
+				'html_class'      => 'form-table',
2456
+				'layout_strategy' => new EE_Admin_Two_Column_Layout(),
2457
+				'subsections'     => apply_filters(
2458
+					'FHEE__Events_Admin_Page___default_event_settings_form__form_subsections',
2459
+					array(
2460
+						'default_reg_status'  => new EE_Select_Input(
2461
+							$registration_stati_for_selection,
2462
+							array(
2463
+								'default'         => isset($registration_config->default_STS_ID)
2464
+													 && array_key_exists(
2465
+														 $registration_config->default_STS_ID,
2466
+														 $registration_stati_for_selection
2467
+													 )
2468
+									? sanitize_text_field($registration_config->default_STS_ID)
2469
+									: EEM_Registration::status_id_pending_payment,
2470
+								'html_label_text' => esc_html__('Default Registration Status', 'event_espresso')
2471
+													 . EEH_Template::get_help_tab_link(
2472
+														 'default_settings_status_help_tab'
2473
+													 ),
2474
+								'html_help_text'  => esc_html__(
2475
+									'This setting allows you to preselect what the default registration status setting is when creating an event.  Note that changing this setting does NOT retroactively apply it to existing events.',
2476
+									'event_espresso'
2477
+								),
2478
+							)
2479
+						),
2480
+						'default_max_tickets' => new EE_Integer_Input(
2481
+							array(
2482
+								'default'         => isset($registration_config->default_maximum_number_of_tickets)
2483
+									? $registration_config->default_maximum_number_of_tickets
2484
+									: EEM_Event::get_default_additional_limit(),
2485
+								'html_label_text' => esc_html__(
2486
+									'Default Maximum Tickets Allowed Per Order:',
2487
+									'event_espresso'
2488
+								)
2489
+													 . EEH_Template::get_help_tab_link(
2490
+														 'default_maximum_tickets_help_tab"'
2491
+													 ),
2492
+								'html_help_text'  => esc_html__(
2493
+									'This setting allows you to indicate what will be the default for the maximum number of tickets per order when creating new events.',
2494
+									'event_espresso'
2495
+								),
2496
+							)
2497
+						),
2498
+					)
2499
+				),
2500
+			)
2501
+		);
2502
+	}
2503
+
2504
+
2505
+	/**
2506
+	 * _update_default_event_settings
2507
+	 *
2508
+	 * @access protected
2509
+	 * @return void
2510
+	 * @throws EE_Error
2511
+	 */
2512
+	protected function _update_default_event_settings()
2513
+	{
2514
+		$registration_config = EE_Registry::instance()->CFG->registration;
2515
+		$form = $this->_default_event_settings_form();
2516
+		if ($form->was_submitted()) {
2517
+			$form->receive_form_submission();
2518
+			if ($form->is_valid()) {
2519
+				$valid_data = $form->valid_data();
2520
+				if (isset($valid_data['default_reg_status'])) {
2521
+					$registration_config->default_STS_ID = $valid_data['default_reg_status'];
2522
+				}
2523
+				if (isset($valid_data['default_max_tickets'])) {
2524
+					$registration_config->default_maximum_number_of_tickets = $valid_data['default_max_tickets'];
2525
+				}
2526
+				// update because data was valid!
2527
+				EE_Registry::instance()->CFG->update_espresso_config();
2528
+				EE_Error::overwrite_success();
2529
+				EE_Error::add_success(
2530
+					__('Default Event Settings were updated', 'event_espresso')
2531
+				);
2532
+			}
2533
+		}
2534
+		$this->_redirect_after_action(0, '', '', array('action' => 'default_event_settings'), true);
2535
+	}
2536
+
2537
+
2538
+	/*************        Templates        *************/
2539
+	protected function _template_settings()
2540
+	{
2541
+		$this->_admin_page_title = esc_html__('Template Settings (Preview)', 'event_espresso');
2542
+		$this->_template_args['preview_img'] = '<img src="'
2543
+											   . EVENTS_ASSETS_URL
2544
+											   . '/images/'
2545
+											   . 'caffeinated_template_features.jpg" alt="'
2546
+											   . esc_attr__('Template Settings Preview screenshot', 'event_espresso')
2547
+											   . '" />';
2548
+		$this->_template_args['preview_text'] = '<strong>'
2549
+												. esc_html__(
2550
+													'Template Settings is a feature that is only available in the premium version of Event Espresso 4 which is available with a support license purchase on EventEspresso.com. Template Settings allow you to configure some of the appearance options for both the Event List and Event Details pages.',
2551
+													'event_espresso'
2552
+												) . '</strong>';
2553
+		$this->display_admin_caf_preview_page('template_settings_tab');
2554
+	}
2555
+
2556
+
2557
+	/** Event Category Stuff **/
2558
+	/**
2559
+	 * set the _category property with the category object for the loaded page.
2560
+	 *
2561
+	 * @access private
2562
+	 * @return void
2563
+	 */
2564
+	private function _set_category_object()
2565
+	{
2566
+		if (isset($this->_category->id) && ! empty($this->_category->id)) {
2567
+			return;
2568
+		} //already have the category object so get out.
2569
+		// set default category object
2570
+		$this->_set_empty_category_object();
2571
+		// only set if we've got an id
2572
+		if (! isset($this->_req_data['EVT_CAT_ID'])) {
2573
+			return;
2574
+		}
2575
+		$category_id = absint($this->_req_data['EVT_CAT_ID']);
2576
+		$term = get_term($category_id, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY);
2577
+		if (! empty($term)) {
2578
+			$this->_category->category_name = $term->name;
2579
+			$this->_category->category_identifier = $term->slug;
2580
+			$this->_category->category_desc = $term->description;
2581
+			$this->_category->id = $term->term_id;
2582
+			$this->_category->parent = $term->parent;
2583
+		}
2584
+	}
2585
+
2586
+
2587
+	/**
2588
+	 * Clears out category properties.
2589
+	 */
2590
+	private function _set_empty_category_object()
2591
+	{
2592
+		$this->_category = new stdClass();
2593
+		$this->_category->category_name = $this->_category->category_identifier = $this->_category->category_desc = '';
2594
+		$this->_category->id = $this->_category->parent = 0;
2595
+	}
2596
+
2597
+
2598
+	/**
2599
+	 * @throws EE_Error
2600
+	 */
2601
+	protected function _category_list_table()
2602
+	{
2603
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2604
+		$this->_search_btn_label = esc_html__('Categories', 'event_espresso');
2605
+		$this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
2606
+			'add_category',
2607
+			'add_category',
2608
+			array(),
2609
+			'add-new-h2'
2610
+		);
2611
+		$this->display_admin_list_table_page_with_sidebar();
2612
+	}
2613
+
2614
+
2615
+	/**
2616
+	 * Output category details view.
2617
+	 */
2618
+	protected function _category_details($view)
2619
+	{
2620
+		// load formatter helper
2621
+		// load field generator helper
2622
+		$route = $view == 'edit' ? 'update_category' : 'insert_category';
2623
+		$this->_set_add_edit_form_tags($route);
2624
+		$this->_set_category_object();
2625
+		$id = ! empty($this->_category->id) ? $this->_category->id : '';
2626
+		$delete_action = 'delete_category';
2627
+		// custom redirect
2628
+		$redirect = EE_Admin_Page::add_query_args_and_nonce(
2629
+			array('action' => 'category_list'),
2630
+			$this->_admin_base_url
2631
+		);
2632
+		$this->_set_publish_post_box_vars('EVT_CAT_ID', $id, $delete_action, $redirect);
2633
+		// take care of contents
2634
+		$this->_template_args['admin_page_content'] = $this->_category_details_content();
2635
+		$this->display_admin_page_with_sidebar();
2636
+	}
2637
+
2638
+
2639
+	/**
2640
+	 * Output category details content.
2641
+	 */
2642
+	protected function _category_details_content()
2643
+	{
2644
+		$editor_args['category_desc'] = array(
2645
+			'type'          => 'wp_editor',
2646
+			'value'         => EEH_Formatter::admin_format_content($this->_category->category_desc),
2647
+			'class'         => 'my_editor_custom',
2648
+			'wpeditor_args' => array('media_buttons' => false),
2649
+		);
2650
+		$_wp_editor = $this->_generate_admin_form_fields($editor_args, 'array');
2651
+		$all_terms = get_terms(
2652
+			array(EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY),
2653
+			array('hide_empty' => 0, 'exclude' => array($this->_category->id))
2654
+		);
2655
+		// setup category select for term parents.
2656
+		$category_select_values[] = array(
2657
+			'text' => esc_html__('No Parent', 'event_espresso'),
2658
+			'id'   => 0,
2659
+		);
2660
+		foreach ($all_terms as $term) {
2661
+			$category_select_values[] = array(
2662
+				'text' => $term->name,
2663
+				'id'   => $term->term_id,
2664
+			);
2665
+		}
2666
+		$category_select = EEH_Form_Fields::select_input(
2667
+			'category_parent',
2668
+			$category_select_values,
2669
+			$this->_category->parent
2670
+		);
2671
+		$template_args = array(
2672
+			'category'                 => $this->_category,
2673
+			'category_select'          => $category_select,
2674
+			'unique_id_info_help_link' => $this->_get_help_tab_link('unique_id_info'),
2675
+			'category_desc_editor'     => $_wp_editor['category_desc']['field'],
2676
+			'disable'                  => '',
2677
+			'disabled_message'         => false,
2678
+		);
2679
+		$template = EVENTS_TEMPLATE_PATH . 'event_category_details.template.php';
2680
+		return EEH_Template::display_template($template, $template_args, true);
2681
+	}
2682
+
2683
+
2684
+	/**
2685
+	 * Handles deleting categories.
2686
+	 */
2687
+	protected function _delete_categories()
2688
+	{
2689
+		$cat_ids = isset($this->_req_data['EVT_CAT_ID']) ? (array) $this->_req_data['EVT_CAT_ID']
2690
+			: (array) $this->_req_data['category_id'];
2691
+		foreach ($cat_ids as $cat_id) {
2692
+			$this->_delete_category($cat_id);
2693
+		}
2694
+		// doesn't matter what page we're coming from... we're going to the same place after delete.
2695
+		$query_args = array(
2696
+			'action' => 'category_list',
2697
+		);
2698
+		$this->_redirect_after_action(0, '', '', $query_args);
2699
+	}
2700
+
2701
+
2702
+	/**
2703
+	 * Handles deleting specific category.
2704
+	 *
2705
+	 * @param int $cat_id
2706
+	 */
2707
+	protected function _delete_category($cat_id)
2708
+	{
2709
+		$cat_id = absint($cat_id);
2710
+		wp_delete_term($cat_id, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY);
2711
+	}
2712
+
2713
+
2714
+	/**
2715
+	 * Handles triggering the update or insertion of a new category.
2716
+	 *
2717
+	 * @param bool $new_category true means we're triggering the insert of a new category.
2718
+	 */
2719
+	protected function _insert_or_update_category($new_category)
2720
+	{
2721
+		$cat_id = $new_category ? $this->_insert_category() : $this->_insert_category(true);
2722
+		$success = 0; // we already have a success message so lets not send another.
2723
+		if ($cat_id) {
2724
+			$query_args = array(
2725
+				'action'     => 'edit_category',
2726
+				'EVT_CAT_ID' => $cat_id,
2727
+			);
2728
+		} else {
2729
+			$query_args = array('action' => 'add_category');
2730
+		}
2731
+		$this->_redirect_after_action($success, '', '', $query_args, true);
2732
+	}
2733
+
2734
+
2735
+	/**
2736
+	 * Inserts or updates category
2737
+	 *
2738
+	 * @param bool $update (true indicates we're updating a category).
2739
+	 * @return bool|mixed|string
2740
+	 */
2741
+	private function _insert_category($update = false)
2742
+	{
2743
+		$cat_id = $update ? $this->_req_data['EVT_CAT_ID'] : '';
2744
+		$category_name = isset($this->_req_data['category_name']) ? $this->_req_data['category_name'] : '';
2745
+		$category_desc = isset($this->_req_data['category_desc']) ? $this->_req_data['category_desc'] : '';
2746
+		$category_parent = isset($this->_req_data['category_parent']) ? $this->_req_data['category_parent'] : 0;
2747
+		if (empty($category_name)) {
2748
+			$msg = esc_html__('You must add a name for the category.', 'event_espresso');
2749
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2750
+			return false;
2751
+		}
2752
+		$term_args = array(
2753
+			'name'        => $category_name,
2754
+			'description' => $category_desc,
2755
+			'parent'      => $category_parent,
2756
+		);
2757
+		// was the category_identifier input disabled?
2758
+		if (isset($this->_req_data['category_identifier'])) {
2759
+			$term_args['slug'] = $this->_req_data['category_identifier'];
2760
+		}
2761
+		$insert_ids = $update
2762
+			? wp_update_term($cat_id, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY, $term_args)
2763
+			: wp_insert_term($category_name, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY, $term_args);
2764
+		if (! is_array($insert_ids)) {
2765
+			$msg = esc_html__(
2766
+				'An error occurred and the category has not been saved to the database.',
2767
+				'event_espresso'
2768
+			);
2769
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2770
+		} else {
2771
+			$cat_id = $insert_ids['term_id'];
2772
+			$msg = sprintf(esc_html__('The category %s was successfully saved', 'event_espresso'), $category_name);
2773
+			EE_Error::add_success($msg);
2774
+		}
2775
+		return $cat_id;
2776
+	}
2777
+
2778
+
2779
+	/**
2780
+	 * Gets categories or count of categories matching the arguments in the request.
2781
+	 *
2782
+	 * @param int  $per_page
2783
+	 * @param int  $current_page
2784
+	 * @param bool $count
2785
+	 * @return EE_Base_Class[]|EE_Term_Taxonomy[]|int
2786
+	 */
2787
+	public function get_categories($per_page = 10, $current_page = 1, $count = false)
2788
+	{
2789
+		// testing term stuff
2790
+		$orderby = isset($this->_req_data['orderby']) ? $this->_req_data['orderby'] : 'Term.term_id';
2791
+		$order = isset($this->_req_data['order']) ? $this->_req_data['order'] : 'DESC';
2792
+		$limit = ($current_page - 1) * $per_page;
2793
+		$where = array('taxonomy' => EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY);
2794
+		if (isset($this->_req_data['s'])) {
2795
+			$sstr = '%' . $this->_req_data['s'] . '%';
2796
+			$where['OR'] = array(
2797
+				'Term.name'   => array('LIKE', $sstr),
2798
+				'description' => array('LIKE', $sstr),
2799
+			);
2800
+		}
2801
+		$query_params = array(
2802
+			$where,
2803
+			'order_by'   => array($orderby => $order),
2804
+			'limit'      => $limit . ',' . $per_page,
2805
+			'force_join' => array('Term'),
2806
+		);
2807
+		$categories = $count
2808
+			? EEM_Term_Taxonomy::instance()->count($query_params, 'term_id')
2809
+			: EEM_Term_Taxonomy::instance()->get_all($query_params);
2810
+		return $categories;
2811
+	}
2812
+
2813
+	/* end category stuff */
2814
+	/**************/
2815
+
2816
+
2817
+	/**
2818
+	 * Callback for the `ee_save_timezone_setting` ajax action.
2819
+	 *
2820
+	 * @throws EE_Error
2821
+	 */
2822
+	public function save_timezonestring_setting()
2823
+	{
2824
+		$timezone_string = isset($this->_req_data['timezone_selected'])
2825
+			? $this->_req_data['timezone_selected']
2826
+			: '';
2827
+		if (empty($timezone_string) || ! EEH_DTT_Helper::validate_timezone($timezone_string, false)) {
2828
+			EE_Error::add_error(
2829
+				esc_html__('An invalid timezone string submitted.', 'event_espresso'),
2830
+				__FILE__,
2831
+				__FUNCTION__,
2832
+				__LINE__
2833
+			);
2834
+			$this->_template_args['error'] = true;
2835
+			$this->_return_json();
2836
+		}
2837
+
2838
+		update_option('timezone_string', $timezone_string);
2839
+		EE_Error::add_success(
2840
+			esc_html__('Your timezone string was updated.', 'event_espresso')
2841
+		);
2842
+		$this->_template_args['success'] = true;
2843
+		$this->_return_json(true, array('action' => 'create_new'));
2844
+	}
2845 2845
 }
Please login to merge, or discard this patch.
Spacing   +68 added lines, -68 removed lines patch added patch discarded remove patch
@@ -570,11 +570,11 @@  discard block
 block discarded – undo
570 570
     {
571 571
         wp_register_style(
572 572
             'events-admin-css',
573
-            EVENTS_ASSETS_URL . 'events-admin-page.css',
573
+            EVENTS_ASSETS_URL.'events-admin-page.css',
574 574
             array(),
575 575
             EVENT_ESPRESSO_VERSION
576 576
         );
577
-        wp_register_style('ee-cat-admin', EVENTS_ASSETS_URL . 'ee-cat-admin.css', array(), EVENT_ESPRESSO_VERSION);
577
+        wp_register_style('ee-cat-admin', EVENTS_ASSETS_URL.'ee-cat-admin.css', array(), EVENT_ESPRESSO_VERSION);
578 578
         wp_enqueue_style('events-admin-css');
579 579
         wp_enqueue_style('ee-cat-admin');
580 580
         // todo note: we also need to load_scripts_styles per view (i.e. default/view_report/event_details
@@ -582,7 +582,7 @@  discard block
 block discarded – undo
582 582
         // scripts
583 583
         wp_register_script(
584 584
             'event_editor_js',
585
-            EVENTS_ASSETS_URL . 'event_editor.js',
585
+            EVENTS_ASSETS_URL.'event_editor.js',
586 586
             array('ee_admin_js', 'jquery-ui-slider', 'jquery-ui-timepicker-addon'),
587 587
             EVENT_ESPRESSO_VERSION,
588 588
             true
@@ -608,7 +608,7 @@  discard block
 block discarded – undo
608 608
         wp_enqueue_style('espresso-ui-theme');
609 609
         wp_register_style(
610 610
             'event-editor-css',
611
-            EVENTS_ASSETS_URL . 'event-editor.css',
611
+            EVENTS_ASSETS_URL.'event-editor.css',
612 612
             array('ee-admin-css'),
613 613
             EVENT_ESPRESSO_VERSION
614 614
         );
@@ -616,7 +616,7 @@  discard block
 block discarded – undo
616 616
         // scripts
617 617
         wp_register_script(
618 618
             'event-datetime-metabox',
619
-            EVENTS_ASSETS_URL . 'event-datetime-metabox.js',
619
+            EVENTS_ASSETS_URL.'event-datetime-metabox.js',
620 620
             array('event_editor_js', 'ee-datepicker'),
621 621
             EVENT_ESPRESSO_VERSION
622 622
         );
@@ -685,7 +685,7 @@  discard block
 block discarded – undo
685 685
     public function verify_event_edit($event = null, $req_type = '')
686 686
     {
687 687
         // don't need to do this when processing
688
-        if (! empty($req_type)) {
688
+        if ( ! empty($req_type)) {
689 689
             return;
690 690
         }
691 691
         // no event?
@@ -694,7 +694,7 @@  discard block
 block discarded – undo
694 694
             $event = $this->_cpt_model_obj;
695 695
         }
696 696
         // STILL no event?
697
-        if (! $event instanceof EE_Event) {
697
+        if ( ! $event instanceof EE_Event) {
698 698
             return;
699 699
         }
700 700
         $orig_status = $event->status();
@@ -732,7 +732,7 @@  discard block
 block discarded – undo
732 732
             );
733 733
         }
734 734
         // now we need to determine if the event has any tickets on sale.  If not then we dont' show the error
735
-        if (! $event->tickets_on_sale()) {
735
+        if ( ! $event->tickets_on_sale()) {
736 736
             return;
737 737
         }
738 738
         // made it here so show warning
@@ -777,7 +777,7 @@  discard block
 block discarded – undo
777 777
     {
778 778
         $has_timezone_string = get_option('timezone_string');
779 779
         // only nag them about setting their timezone if it's their first event, and they haven't already done it
780
-        if (! $has_timezone_string && ! EEM_Event::instance()->exists(array())) {
780
+        if ( ! $has_timezone_string && ! EEM_Event::instance()->exists(array())) {
781 781
             EE_Error::add_attention(
782 782
                 sprintf(
783 783
                     __(
@@ -861,31 +861,31 @@  discard block
 block discarded – undo
861 861
         $items = apply_filters('FHEE__Events_Admin_Page___event_legend_items__items', $items);
862 862
         $statuses = array(
863 863
             'sold_out_status'  => array(
864
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::sold_out,
864
+                'class' => 'ee-status-legend ee-status-legend-'.EE_Datetime::sold_out,
865 865
                 'desc'  => EEH_Template::pretty_status(EE_Datetime::sold_out, false, 'sentence'),
866 866
             ),
867 867
             'active_status'    => array(
868
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::active,
868
+                'class' => 'ee-status-legend ee-status-legend-'.EE_Datetime::active,
869 869
                 'desc'  => EEH_Template::pretty_status(EE_Datetime::active, false, 'sentence'),
870 870
             ),
871 871
             'upcoming_status'  => array(
872
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::upcoming,
872
+                'class' => 'ee-status-legend ee-status-legend-'.EE_Datetime::upcoming,
873 873
                 'desc'  => EEH_Template::pretty_status(EE_Datetime::upcoming, false, 'sentence'),
874 874
             ),
875 875
             'postponed_status' => array(
876
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::postponed,
876
+                'class' => 'ee-status-legend ee-status-legend-'.EE_Datetime::postponed,
877 877
                 'desc'  => EEH_Template::pretty_status(EE_Datetime::postponed, false, 'sentence'),
878 878
             ),
879 879
             'cancelled_status' => array(
880
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::cancelled,
880
+                'class' => 'ee-status-legend ee-status-legend-'.EE_Datetime::cancelled,
881 881
                 'desc'  => EEH_Template::pretty_status(EE_Datetime::cancelled, false, 'sentence'),
882 882
             ),
883 883
             'expired_status'   => array(
884
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::expired,
884
+                'class' => 'ee-status-legend ee-status-legend-'.EE_Datetime::expired,
885 885
                 'desc'  => EEH_Template::pretty_status(EE_Datetime::expired, false, 'sentence'),
886 886
             ),
887 887
             'inactive_status'  => array(
888
-                'class' => 'ee-status-legend ee-status-legend-' . EE_Datetime::inactive,
888
+                'class' => 'ee-status-legend ee-status-legend-'.EE_Datetime::inactive,
889 889
                 'desc'  => EEH_Template::pretty_status(EE_Datetime::inactive, false, 'sentence'),
890 890
             ),
891 891
         );
@@ -899,7 +899,7 @@  discard block
 block discarded – undo
899 899
      */
900 900
     private function _event_model()
901 901
     {
902
-        if (! $this->_event_model instanceof EEM_Event) {
902
+        if ( ! $this->_event_model instanceof EEM_Event) {
903 903
             $this->_event_model = EE_Registry::instance()->load_model('Event');
904 904
         }
905 905
         return $this->_event_model;
@@ -919,7 +919,7 @@  discard block
 block discarded – undo
919 919
     public function extra_permalink_field_buttons($return, $id, $new_title, $new_slug)
920 920
     {
921 921
         // make sure this is only when editing
922
-        if (! empty($id)) {
922
+        if ( ! empty($id)) {
923 923
             $post = get_post($id);
924 924
             $return .= '<a class="button button-small" onclick="prompt(\'Shortcode:\', jQuery(\'#shortcode\').val()); return false;" href="#"  tabindex="-1">'
925 925
                        . esc_html__('Shortcode', 'event_espresso')
@@ -953,7 +953,7 @@  discard block
 block discarded – undo
953 953
                     'button'
954 954
                 );
955 955
         $this->_template_args['after_list_table']['legend'] = $this->_display_legend($this->_event_legend_items());
956
-        $this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
956
+        $this->_admin_page_title .= ' '.$this->get_action_link_or_button(
957 957
             'create_new',
958 958
             'add',
959 959
             array(),
@@ -1093,7 +1093,7 @@  discard block
 block discarded – undo
1093 1093
      */
1094 1094
     protected function _default_venue_update(\EE_Event $evtobj, $data)
1095 1095
     {
1096
-        require_once(EE_MODELS . 'EEM_Venue.model.php');
1096
+        require_once(EE_MODELS.'EEM_Venue.model.php');
1097 1097
         $venue_model = EE_Registry::instance()->load_model('Venue');
1098 1098
         $rows_affected = null;
1099 1099
         $venue_id = ! empty($data['venue_id']) ? $data['venue_id'] : null;
@@ -1124,7 +1124,7 @@  discard block
 block discarded – undo
1124 1124
             'status'              => 'publish',
1125 1125
         );
1126 1126
         // if we've got the venue_id then we're just updating the existing venue so let's do that and then get out.
1127
-        if (! empty($venue_id)) {
1127
+        if ( ! empty($venue_id)) {
1128 1128
             $update_where = array($venue_model->primary_key_name() => $venue_id);
1129 1129
             $rows_affected = $venue_model->update($venue_array, array($update_where));
1130 1130
             // we've gotta make sure that the venue is always attached to a revision.. add_relation_to should take care of making sure that the relation is already present.
@@ -1166,7 +1166,7 @@  discard block
 block discarded – undo
1166 1166
                 'DTT_order'     => $row,
1167 1167
             );
1168 1168
             // 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.
1169
-            if (! empty($dtt['DTT_ID'])) {
1169
+            if ( ! empty($dtt['DTT_ID'])) {
1170 1170
                 $DTM = EE_Registry::instance()
1171 1171
                                   ->load_model('Datetime', array($evtobj->get_timezone()))
1172 1172
                                   ->get_one_by_ID($dtt['DTT_ID']);
@@ -1176,7 +1176,7 @@  discard block
 block discarded – undo
1176 1176
                     $DTM->set($field, $value);
1177 1177
                 }
1178 1178
                 // make sure the $dtt_id here is saved just in case after the add_relation_to() the autosave replaces it.  We need to do this so we dont' TRASH the parent DTT.
1179
-                $saved_dtts[ $DTM->ID() ] = $DTM;
1179
+                $saved_dtts[$DTM->ID()] = $DTM;
1180 1180
             } else {
1181 1181
                 $DTM = EE_Registry::instance()->load_class(
1182 1182
                     'Datetime',
@@ -1209,14 +1209,14 @@  discard block
 block discarded – undo
1209 1209
         foreach ($data['edit_tickets'] as $row => $tkt) {
1210 1210
             $incoming_date_formats = array('Y-m-d', 'h:i a');
1211 1211
             $update_prices = false;
1212
-            $ticket_price = isset($data['edit_prices'][ $row ][1]['PRC_amount'])
1213
-                ? $data['edit_prices'][ $row ][1]['PRC_amount'] : 0;
1212
+            $ticket_price = isset($data['edit_prices'][$row][1]['PRC_amount'])
1213
+                ? $data['edit_prices'][$row][1]['PRC_amount'] : 0;
1214 1214
             // trim inputs to ensure any excess whitespace is removed.
1215 1215
             $tkt = array_map('trim', $tkt);
1216 1216
             if (empty($tkt['TKT_start_date'])) {
1217 1217
                 // let's use now in the set timezone.
1218 1218
                 $now = new DateTime('now', new DateTimeZone($evtobj->get_timezone()));
1219
-                $tkt['TKT_start_date'] = $now->format($incoming_date_formats[0] . ' ' . $incoming_date_formats[1]);
1219
+                $tkt['TKT_start_date'] = $now->format($incoming_date_formats[0].' '.$incoming_date_formats[1]);
1220 1220
             }
1221 1221
             if (empty($tkt['TKT_end_date'])) {
1222 1222
                 // use the start date of the first datetime
@@ -1251,7 +1251,7 @@  discard block
 block discarded – undo
1251 1251
             // if we have a TKT_ID then we need to get that existing TKT_obj and update it
1252 1252
             // we actually do our saves a head of doing any add_relations to because its entirely possible that this ticket didn't removed or added to any datetime in the session but DID have it's items modified.
1253 1253
             // keep in mind that if the TKT has been sold (and we have changed pricing information), then we won't be updating the tkt but instead a new tkt will be created and the old one archived.
1254
-            if (! empty($tkt['TKT_ID'])) {
1254
+            if ( ! empty($tkt['TKT_ID'])) {
1255 1255
                 $TKT = EE_Registry::instance()
1256 1256
                                   ->load_model('Ticket', array($evtobj->get_timezone()))
1257 1257
                                   ->get_one_by_ID($tkt['TKT_ID']);
@@ -1286,7 +1286,7 @@  discard block
 block discarded – undo
1286 1286
                         $TKT->set('TKT_deleted', 1);
1287 1287
                         $TKT->save();
1288 1288
                         // make sure this ticket is still recorded in our saved_tkts so we don't run it through the regular trash routine.
1289
-                        $saved_tickets[ $TKT->ID() ] = $TKT;
1289
+                        $saved_tickets[$TKT->ID()] = $TKT;
1290 1290
                         // create new ticket that's a copy of the existing except a new id of course (and not archived) AND has the new TKT_price associated with it.
1291 1291
                         $TKT = clone $TKT;
1292 1292
                         $TKT->set('TKT_ID', 0);
@@ -1331,9 +1331,9 @@  discard block
 block discarded – undo
1331 1331
             }
1332 1332
             // initially let's add the ticket to the dtt
1333 1333
             $saved_dtt->_add_relation_to($TKT, 'Ticket');
1334
-            $saved_tickets[ $TKT->ID() ] = $TKT;
1334
+            $saved_tickets[$TKT->ID()] = $TKT;
1335 1335
             // add prices to ticket
1336
-            $this->_add_prices_to_ticket($data['edit_prices'][ $row ], $TKT, $update_prices);
1336
+            $this->_add_prices_to_ticket($data['edit_prices'][$row], $TKT, $update_prices);
1337 1337
         }
1338 1338
         // however now we need to handle permanently deleting tickets via the ui.  Keep in mind that the ui does not allow deleting/archiving tickets that have ticket sold.  However, it does allow for deleting tickets that have no tickets sold, in which case we want to get rid of permanently because there is no need to save in db.
1339 1339
         $old_tickets = isset($old_tickets[0]) && $old_tickets[0] == '' ? array() : $old_tickets;
@@ -1490,7 +1490,7 @@  discard block
 block discarded – undo
1490 1490
         $publish_box_extra_args['event_editor_overview_add'] = ob_get_clean();
1491 1491
         // load template
1492 1492
         EEH_Template::display_template(
1493
-            EVENTS_TEMPLATE_PATH . 'event_publish_box_extras.template.php',
1493
+            EVENTS_TEMPLATE_PATH.'event_publish_box_extras.template.php',
1494 1494
             $publish_box_extra_args
1495 1495
         );
1496 1496
     }
@@ -1582,7 +1582,7 @@  discard block
 block discarded – undo
1582 1582
                     'default_where_conditions' => 'none',
1583 1583
                 )
1584 1584
             );
1585
-            if (! empty($related_tickets)) {
1585
+            if ( ! empty($related_tickets)) {
1586 1586
                 $template_args['total_ticket_rows'] = count($related_tickets);
1587 1587
                 $row = 0;
1588 1588
                 foreach ($related_tickets as $ticket) {
@@ -1616,7 +1616,7 @@  discard block
 block discarded – undo
1616 1616
         );
1617 1617
         $template = apply_filters(
1618 1618
             'FHEE__Events_Admin_Page__ticket_metabox__template',
1619
-            EVENTS_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php'
1619
+            EVENTS_TEMPLATE_PATH.'event_tickets_metabox_main.template.php'
1620 1620
         );
1621 1621
         EEH_Template::display_template($template, $template_args);
1622 1622
     }
@@ -1634,7 +1634,7 @@  discard block
 block discarded – undo
1634 1634
     private function _get_ticket_row($ticket, $skeleton = false, $row = 0)
1635 1635
     {
1636 1636
         $template_args = array(
1637
-            'tkt_status_class'    => ' tkt-status-' . $ticket->ticket_status(),
1637
+            'tkt_status_class'    => ' tkt-status-'.$ticket->ticket_status(),
1638 1638
             'tkt_archive_class'   => $ticket->ticket_status() === EE_Ticket::archived && ! $skeleton ? ' tkt-archived'
1639 1639
                 : '',
1640 1640
             'ticketrow'           => $skeleton ? 'TICKETNUM' : $row,
@@ -1646,10 +1646,10 @@  discard block
 block discarded – undo
1646 1646
             'TKT_qty'             => $ticket->get_pretty('TKT_qty', 'input'),
1647 1647
             'edit_ticketrow_name' => $skeleton ? 'TICKETNAMEATTR' : 'edit_tickets',
1648 1648
             'TKT_sold'            => $skeleton ? 0 : $ticket->get('TKT_sold'),
1649
-            'trash_icon'          => ($skeleton || (! empty($ticket) && ! $ticket->get('TKT_deleted')))
1650
-                                     && (! empty($ticket) && $ticket->get('TKT_sold') === 0)
1649
+            'trash_icon'          => ($skeleton || ( ! empty($ticket) && ! $ticket->get('TKT_deleted')))
1650
+                                     && ( ! empty($ticket) && $ticket->get('TKT_sold') === 0)
1651 1651
                 ? 'trash-icon dashicons dashicons-post-trash clickable' : 'ee-lock-icon',
1652
-            'disabled'            => $skeleton || (! empty($ticket) && ! $ticket->get('TKT_deleted')) ? ''
1652
+            'disabled'            => $skeleton || ( ! empty($ticket) && ! $ticket->get('TKT_deleted')) ? ''
1653 1653
                 : ' disabled=disabled',
1654 1654
         );
1655 1655
         $price = $ticket->ID() !== 0
@@ -1676,7 +1676,7 @@  discard block
 block discarded – undo
1676 1676
                     array('order_by' => array('DTT_EVT_start' => 'ASC'))
1677 1677
                 )
1678 1678
                 : null;
1679
-            if (! empty($earliest_dtt)) {
1679
+            if ( ! empty($earliest_dtt)) {
1680 1680
                 $template_args['TKT_end_date'] = $earliest_dtt->get_datetime('DTT_EVT_start', 'Y-m-d', 'h:i a');
1681 1681
             } else {
1682 1682
                 $template_args['TKT_end_date'] = date(
@@ -1688,7 +1688,7 @@  discard block
 block discarded – undo
1688 1688
         $template_args = array_merge($template_args, $price_args);
1689 1689
         $template = apply_filters(
1690 1690
             'FHEE__Events_Admin_Page__get_ticket_row__template',
1691
-            EVENTS_TEMPLATE_PATH . 'event_tickets_metabox_ticket_row.template.php',
1691
+            EVENTS_TEMPLATE_PATH.'event_tickets_metabox_ticket_row.template.php',
1692 1692
             $ticket
1693 1693
         );
1694 1694
         return EEH_Template::display_template($template, $template_args, true);
@@ -1742,7 +1742,7 @@  discard block
 block discarded – undo
1742 1742
             $default_reg_status_values
1743 1743
         );
1744 1744
         EEH_Template::display_template(
1745
-            EVENTS_TEMPLATE_PATH . 'event_registration_options.template.php',
1745
+            EVENTS_TEMPLATE_PATH.'event_registration_options.template.php',
1746 1746
             $template_args
1747 1747
         );
1748 1748
     }
@@ -1764,7 +1764,7 @@  discard block
 block discarded – undo
1764 1764
     {
1765 1765
         $EEME = $this->_event_model();
1766 1766
         $offset = ($current_page - 1) * $per_page;
1767
-        $limit = $count ? null : $offset . ',' . $per_page;
1767
+        $limit = $count ? null : $offset.','.$per_page;
1768 1768
         $orderby = isset($this->_req_data['orderby']) ? $this->_req_data['orderby'] : 'EVT_ID';
1769 1769
         $order = isset($this->_req_data['order']) ? $this->_req_data['order'] : "DESC";
1770 1770
         if (isset($this->_req_data['month_range'])) {
@@ -1793,7 +1793,7 @@  discard block
 block discarded – undo
1793 1793
         // categories?
1794 1794
         $category = isset($this->_req_data['EVT_CAT']) && $this->_req_data['EVT_CAT'] > 0
1795 1795
             ? $this->_req_data['EVT_CAT'] : null;
1796
-        if (! empty($category)) {
1796
+        if ( ! empty($category)) {
1797 1797
             $where['Term_Taxonomy.taxonomy'] = EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY;
1798 1798
             $where['Term_Taxonomy.term_id'] = $category;
1799 1799
         }
@@ -1801,7 +1801,7 @@  discard block
 block discarded – undo
1801 1801
         $start_formats = EEM_Datetime::instance()->get_formats_for('DTT_EVT_start');
1802 1802
         if (isset($this->_req_data['month_range']) && $this->_req_data['month_range'] != '') {
1803 1803
             $DateTime = new DateTime(
1804
-                $year_r . '-' . $month_r . '-01 00:00:00',
1804
+                $year_r.'-'.$month_r.'-01 00:00:00',
1805 1805
                 new DateTimeZone(EEM_Datetime::instance()->get_timezone())
1806 1806
             );
1807 1807
             $start = $DateTime->format(implode(' ', $start_formats));
@@ -1827,11 +1827,11 @@  discard block
 block discarded – undo
1827 1827
                             ->format(implode(' ', $start_formats));
1828 1828
             $where['Datetime.DTT_EVT_start'] = array('BETWEEN', array($start, $end));
1829 1829
         }
1830
-        if (! EE_Registry::instance()->CAP->current_user_can('ee_read_others_events', 'get_events')) {
1830
+        if ( ! EE_Registry::instance()->CAP->current_user_can('ee_read_others_events', 'get_events')) {
1831 1831
             $where['EVT_wp_user'] = get_current_user_id();
1832 1832
         } else {
1833
-            if (! isset($where['status'])) {
1834
-                if (! EE_Registry::instance()->CAP->current_user_can('ee_read_private_events', 'get_events')) {
1833
+            if ( ! isset($where['status'])) {
1834
+                if ( ! EE_Registry::instance()->CAP->current_user_can('ee_read_private_events', 'get_events')) {
1835 1835
                     $where['OR'] = array(
1836 1836
                         'status*restrict_private' => array('!=', 'private'),
1837 1837
                         'AND'                     => array(
@@ -1851,7 +1851,7 @@  discard block
 block discarded – undo
1851 1851
         }
1852 1852
         // search query handling
1853 1853
         if (isset($this->_req_data['s'])) {
1854
-            $search_string = '%' . $this->_req_data['s'] . '%';
1854
+            $search_string = '%'.$this->_req_data['s'].'%';
1855 1855
             $where['OR'] = array(
1856 1856
                 'EVT_name'       => array('LIKE', $search_string),
1857 1857
                 'EVT_desc'       => array('LIKE', $search_string),
@@ -1946,7 +1946,7 @@  discard block
 block discarded – undo
1946 1946
             // clean status
1947 1947
             $event_status = sanitize_key($event_status);
1948 1948
             // grab status
1949
-            if (! empty($event_status)) {
1949
+            if ( ! empty($event_status)) {
1950 1950
                 $success = $this->_change_event_status($EVT_ID, $event_status);
1951 1951
             } else {
1952 1952
                 $success = false;
@@ -1983,7 +1983,7 @@  discard block
 block discarded – undo
1983 1983
         // clean status
1984 1984
         $event_status = sanitize_key($event_status);
1985 1985
         // grab status
1986
-        if (! empty($event_status)) {
1986
+        if ( ! empty($event_status)) {
1987 1987
             $success = true;
1988 1988
             // determine the event id and set to array.
1989 1989
             $EVT_IDs = isset($this->_req_data['EVT_IDs']) ? (array) $this->_req_data['EVT_IDs'] : array();
@@ -2030,7 +2030,7 @@  discard block
 block discarded – undo
2030 2030
     private function _change_event_status($EVT_ID = 0, $event_status = '')
2031 2031
     {
2032 2032
         // grab event id
2033
-        if (! $EVT_ID) {
2033
+        if ( ! $EVT_ID) {
2034 2034
             $msg = esc_html__(
2035 2035
                 'An error occurred. No Event ID or an invalid Event ID was received.',
2036 2036
                 'event_espresso'
@@ -2204,14 +2204,14 @@  discard block
 block discarded – undo
2204 2204
      */
2205 2205
     protected function getModelsAndIdsToDelete($deletion_job_code)
2206 2206
     {
2207
-        if (! $deletion_job_code) {
2207
+        if ( ! $deletion_job_code) {
2208 2208
             throw new Exception(esc_html__('We aren’t sure which job you are performing. Please press back in your browser and try again.', 'event_espresso'));
2209 2209
         }
2210
-        $deletion_data = get_option('ee_deletion_' . $deletion_job_code, []);
2210
+        $deletion_data = get_option('ee_deletion_'.$deletion_job_code, []);
2211 2211
 
2212 2212
         $models_and_ids_to_delete = [];
2213 2213
         foreach ($deletion_data as $root) {
2214
-            if (! $root instanceof ModelObjNode) {
2214
+            if ( ! $root instanceof ModelObjNode) {
2215 2215
                 throw new UnexpectedEntityException($root, 'ModelObjNode');
2216 2216
             }
2217 2217
             $models_and_ids_to_delete = array_replace_recursive($models_and_ids_to_delete, $root->getIds());
@@ -2235,11 +2235,11 @@  discard block
 block discarded – undo
2235 2235
             );
2236 2236
         }
2237 2237
         $datetime_ids = isset($models_and_ids_to_delete['Datetime']) ? $models_and_ids_to_delete['Datetime'] : array();
2238
-        if (! is_array($datetime_ids)) {
2238
+        if ( ! is_array($datetime_ids)) {
2239 2239
             throw new UnexpectedEntityException($datetime_ids, 'array');
2240 2240
         }
2241 2241
         $registration_ids = isset($models_and_ids_to_delete['Registration']) ? $models_and_ids_to_delete['Registration'] : array();
2242
-        if (! is_array($registration_ids)) {
2242
+        if ( ! is_array($registration_ids)) {
2243 2243
             throw new UnexpectedEntityException($registration_ids, 'array');
2244 2244
         }
2245 2245
         $num_registrations_to_show = 10;
@@ -2275,7 +2275,7 @@  discard block
 block discarded – undo
2275 2275
         ];
2276 2276
 
2277 2277
         $this->_template_args['admin_page_content'] = EEH_Template::display_template(
2278
-            EVENTS_TEMPLATE_PATH . 'event_preview_deletion.template.php',
2278
+            EVENTS_TEMPLATE_PATH.'event_preview_deletion.template.php',
2279 2279
             [
2280 2280
                 'form_url' => EE_Admin_Page::add_query_args_and_nonce(
2281 2281
                     $confirm_deletion_args,
@@ -2303,7 +2303,7 @@  discard block
 block discarded – undo
2303 2303
     private function _permanently_delete_event($EVT_ID = 0)
2304 2304
     {
2305 2305
         // grab event id
2306
-        if (! $EVT_ID) {
2306
+        if ( ! $EVT_ID) {
2307 2307
             $msg = esc_html__(
2308 2308
                 'An error occurred. No Event ID or an invalid Event ID was received.',
2309 2309
                 'event_espresso'
@@ -2311,12 +2311,12 @@  discard block
 block discarded – undo
2311 2311
             EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
2312 2312
             return false;
2313 2313
         }
2314
-        if (! $this->_cpt_model_obj instanceof EE_Event
2314
+        if ( ! $this->_cpt_model_obj instanceof EE_Event
2315 2315
             || $this->_cpt_model_obj->ID() !== $EVT_ID
2316 2316
         ) {
2317 2317
             $this->_cpt_model_obj = EEM_Event::instance()->get_one_by_ID($EVT_ID);
2318 2318
         }
2319
-        if (! $this->_cpt_model_obj instanceof EE_Event) {
2319
+        if ( ! $this->_cpt_model_obj instanceof EE_Event) {
2320 2320
             return false;
2321 2321
         }
2322 2322
         // need to delete related tickets and prices first.
@@ -2338,7 +2338,7 @@  discard block
 block discarded – undo
2338 2338
         }
2339 2339
         // any attached question groups?
2340 2340
         $question_groups = $this->_cpt_model_obj->get_many_related('Question_Group');
2341
-        if (! empty($question_groups)) {
2341
+        if ( ! empty($question_groups)) {
2342 2342
             foreach ($question_groups as $question_group) {
2343 2343
                 $this->_cpt_model_obj->_remove_relation_to($question_group, 'Question_Group');
2344 2344
             }
@@ -2549,7 +2549,7 @@  discard block
 block discarded – undo
2549 2549
                                                 . esc_html__(
2550 2550
                                                     'Template Settings is a feature that is only available in the premium version of Event Espresso 4 which is available with a support license purchase on EventEspresso.com. Template Settings allow you to configure some of the appearance options for both the Event List and Event Details pages.',
2551 2551
                                                     'event_espresso'
2552
-                                                ) . '</strong>';
2552
+                                                ).'</strong>';
2553 2553
         $this->display_admin_caf_preview_page('template_settings_tab');
2554 2554
     }
2555 2555
 
@@ -2569,12 +2569,12 @@  discard block
 block discarded – undo
2569 2569
         // set default category object
2570 2570
         $this->_set_empty_category_object();
2571 2571
         // only set if we've got an id
2572
-        if (! isset($this->_req_data['EVT_CAT_ID'])) {
2572
+        if ( ! isset($this->_req_data['EVT_CAT_ID'])) {
2573 2573
             return;
2574 2574
         }
2575 2575
         $category_id = absint($this->_req_data['EVT_CAT_ID']);
2576 2576
         $term = get_term($category_id, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY);
2577
-        if (! empty($term)) {
2577
+        if ( ! empty($term)) {
2578 2578
             $this->_category->category_name = $term->name;
2579 2579
             $this->_category->category_identifier = $term->slug;
2580 2580
             $this->_category->category_desc = $term->description;
@@ -2602,7 +2602,7 @@  discard block
 block discarded – undo
2602 2602
     {
2603 2603
         do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2604 2604
         $this->_search_btn_label = esc_html__('Categories', 'event_espresso');
2605
-        $this->_admin_page_title .= ' ' . $this->get_action_link_or_button(
2605
+        $this->_admin_page_title .= ' '.$this->get_action_link_or_button(
2606 2606
             'add_category',
2607 2607
             'add_category',
2608 2608
             array(),
@@ -2676,7 +2676,7 @@  discard block
 block discarded – undo
2676 2676
             'disable'                  => '',
2677 2677
             'disabled_message'         => false,
2678 2678
         );
2679
-        $template = EVENTS_TEMPLATE_PATH . 'event_category_details.template.php';
2679
+        $template = EVENTS_TEMPLATE_PATH.'event_category_details.template.php';
2680 2680
         return EEH_Template::display_template($template, $template_args, true);
2681 2681
     }
2682 2682
 
@@ -2761,7 +2761,7 @@  discard block
 block discarded – undo
2761 2761
         $insert_ids = $update
2762 2762
             ? wp_update_term($cat_id, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY, $term_args)
2763 2763
             : wp_insert_term($category_name, EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY, $term_args);
2764
-        if (! is_array($insert_ids)) {
2764
+        if ( ! is_array($insert_ids)) {
2765 2765
             $msg = esc_html__(
2766 2766
                 'An error occurred and the category has not been saved to the database.',
2767 2767
                 'event_espresso'
@@ -2792,7 +2792,7 @@  discard block
 block discarded – undo
2792 2792
         $limit = ($current_page - 1) * $per_page;
2793 2793
         $where = array('taxonomy' => EEM_CPT_Base::EVENT_CATEGORY_TAXONOMY);
2794 2794
         if (isset($this->_req_data['s'])) {
2795
-            $sstr = '%' . $this->_req_data['s'] . '%';
2795
+            $sstr = '%'.$this->_req_data['s'].'%';
2796 2796
             $where['OR'] = array(
2797 2797
                 'Term.name'   => array('LIKE', $sstr),
2798 2798
                 'description' => array('LIKE', $sstr),
@@ -2801,7 +2801,7 @@  discard block
 block discarded – undo
2801 2801
         $query_params = array(
2802 2802
             $where,
2803 2803
             'order_by'   => array($orderby => $order),
2804
-            'limit'      => $limit . ',' . $per_page,
2804
+            'limit'      => $limit.','.$per_page,
2805 2805
             'force_join' => array('Term'),
2806 2806
         );
2807 2807
         $categories = $count
Please login to merge, or discard this patch.
admin_pages/events/form_sections/ConfirmEventDeletionForm.php 2 patches
Indentation   +72 added lines, -72 removed lines patch added patch discarded remove patch
@@ -21,79 +21,79 @@
 block discarded – undo
21 21
  */
22 22
 class ConfirmEventDeletionForm extends \EE_Form_Section_Proper
23 23
 {
24
-    /**
25
-     * @var EE_Event[]
26
-     */
27
-    protected $events;
28
-    public function __construct($event_ids, $options_array = array())
29
-    {
30
-        if (! isset($options_array['subsections'])) {
31
-            $options_array['subsections'] = [];
32
-        }
33
-        if (! isset($options_array['subsections']['events'])) {
34
-            $events_subsection = new \EE_Form_Section_Proper();
35
-            $options_array['subsections']['events'] = $events_subsection;
36
-        }
37
-        $events = EEM_Event::instance()->get_all_deleted_and_undeleted(
38
-            [
39
-                [
40
-                    'EVT_ID' => ['IN',$event_ids]
41
-                ]
42
-            ]
43
-        );
44
-        if (! is_array($events)) {
45
-            throw new UnexpectedEntityException($event_ids, 'array');
46
-        }
47
-        $this->events = $events;
48
-        $events_inputs = [
49
-            'intro' => new EE_Form_Section_HTML(
50
-                EEH_HTML::h2(esc_html__('In order to prevent accidentally deleting the wrong events, please enter the unique URL slug of each event.', 'event_espresso'))
51
-            )
52
-        ];
53
-        foreach ($events as $event) {
54
-             $events_inputs[ $event->ID() ] = new \EE_Text_Input(
55
-                 [
56
-                    'html_label_text' => esc_html(
57
-                        sprintf(
58
-                            __('Please enter the URL slug of "%1$s" (hint: it’s "%2$s")', 'event_espresso'),
59
-                            $event->name(),
60
-                            $event->slug()
61
-                        )
62
-                    ),
63
-                    'required' => false
64
-                 ]
65
-             );
66
-        }
67
-        $events_subsection->add_subsections($events_inputs);
68
-        $options_array['subsections']['backup'] = new EE_Checkbox_Multi_Input(
69
-            [
70
-                'yes' => esc_html__('I have backed up my database.', 'event_espresso')
71
-            ],
72
-            [
73
-                'html_label_text' => esc_html__('Deleting this data cannot be undone. Please confirm you have a usable database backup.', 'event_espresso'),
74
-                'required' => true
75
-            ]
76
-        );
77
-        parent::__construct($options_array);
78
-    }
24
+	/**
25
+	 * @var EE_Event[]
26
+	 */
27
+	protected $events;
28
+	public function __construct($event_ids, $options_array = array())
29
+	{
30
+		if (! isset($options_array['subsections'])) {
31
+			$options_array['subsections'] = [];
32
+		}
33
+		if (! isset($options_array['subsections']['events'])) {
34
+			$events_subsection = new \EE_Form_Section_Proper();
35
+			$options_array['subsections']['events'] = $events_subsection;
36
+		}
37
+		$events = EEM_Event::instance()->get_all_deleted_and_undeleted(
38
+			[
39
+				[
40
+					'EVT_ID' => ['IN',$event_ids]
41
+				]
42
+			]
43
+		);
44
+		if (! is_array($events)) {
45
+			throw new UnexpectedEntityException($event_ids, 'array');
46
+		}
47
+		$this->events = $events;
48
+		$events_inputs = [
49
+			'intro' => new EE_Form_Section_HTML(
50
+				EEH_HTML::h2(esc_html__('In order to prevent accidentally deleting the wrong events, please enter the unique URL slug of each event.', 'event_espresso'))
51
+			)
52
+		];
53
+		foreach ($events as $event) {
54
+			 $events_inputs[ $event->ID() ] = new \EE_Text_Input(
55
+				 [
56
+					'html_label_text' => esc_html(
57
+						sprintf(
58
+							__('Please enter the URL slug of "%1$s" (hint: it’s "%2$s")', 'event_espresso'),
59
+							$event->name(),
60
+							$event->slug()
61
+						)
62
+					),
63
+					'required' => false
64
+				 ]
65
+			 );
66
+		}
67
+		$events_subsection->add_subsections($events_inputs);
68
+		$options_array['subsections']['backup'] = new EE_Checkbox_Multi_Input(
69
+			[
70
+				'yes' => esc_html__('I have backed up my database.', 'event_espresso')
71
+			],
72
+			[
73
+				'html_label_text' => esc_html__('Deleting this data cannot be undone. Please confirm you have a usable database backup.', 'event_espresso'),
74
+				'required' => true
75
+			]
76
+		);
77
+		parent::__construct($options_array);
78
+	}
79 79
 
80
-    public function _validate()
81
-    {
82
-        parent::_validate();
83
-        $events_subsection = $this->get_proper_subsection('events');
84
-        foreach ($this->events as $event) {
85
-            $event_input = $events_subsection->get_input($event->ID());
86
-            if ((string) $event_input->normalized_value() !== $event->slug()) {
87
-                $event_input->add_validation_error(
88
-                    sprintf(
89
-                        esc_html__('You entered the incorrect URL slug for the event "%1$s". Please enter it again (use "%2$s") to confirm you are deleting the correct event.', 'event_espresso'),
90
-                        $event->name(),
91
-                        $event->slug()
92
-                    )
93
-                );
94
-            }
95
-        }
96
-    }
80
+	public function _validate()
81
+	{
82
+		parent::_validate();
83
+		$events_subsection = $this->get_proper_subsection('events');
84
+		foreach ($this->events as $event) {
85
+			$event_input = $events_subsection->get_input($event->ID());
86
+			if ((string) $event_input->normalized_value() !== $event->slug()) {
87
+				$event_input->add_validation_error(
88
+					sprintf(
89
+						esc_html__('You entered the incorrect URL slug for the event "%1$s". Please enter it again (use "%2$s") to confirm you are deleting the correct event.', 'event_espresso'),
90
+						$event->name(),
91
+						$event->slug()
92
+					)
93
+				);
94
+			}
95
+		}
96
+	}
97 97
 }
98 98
 // End of file ConfirmEventDeletionForm.php
99 99
 // Location: EventEspresso\admin_pages\events\form_sections/ConfirmEventDeletionForm.php
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -27,21 +27,21 @@  discard block
 block discarded – undo
27 27
     protected $events;
28 28
     public function __construct($event_ids, $options_array = array())
29 29
     {
30
-        if (! isset($options_array['subsections'])) {
30
+        if ( ! isset($options_array['subsections'])) {
31 31
             $options_array['subsections'] = [];
32 32
         }
33
-        if (! isset($options_array['subsections']['events'])) {
33
+        if ( ! isset($options_array['subsections']['events'])) {
34 34
             $events_subsection = new \EE_Form_Section_Proper();
35 35
             $options_array['subsections']['events'] = $events_subsection;
36 36
         }
37 37
         $events = EEM_Event::instance()->get_all_deleted_and_undeleted(
38 38
             [
39 39
                 [
40
-                    'EVT_ID' => ['IN',$event_ids]
40
+                    'EVT_ID' => ['IN', $event_ids]
41 41
                 ]
42 42
             ]
43 43
         );
44
-        if (! is_array($events)) {
44
+        if ( ! is_array($events)) {
45 45
             throw new UnexpectedEntityException($event_ids, 'array');
46 46
         }
47 47
         $this->events = $events;
@@ -51,7 +51,7 @@  discard block
 block discarded – undo
51 51
             )
52 52
         ];
53 53
         foreach ($events as $event) {
54
-             $events_inputs[ $event->ID() ] = new \EE_Text_Input(
54
+             $events_inputs[$event->ID()] = new \EE_Text_Input(
55 55
                  [
56 56
                     'html_label_text' => esc_html(
57 57
                         sprintf(
Please login to merge, or discard this patch.
core/libraries/form_sections/base/EE_Form_Section_Proper.form.php 1 patch
Indentation   +1524 added lines, -1524 removed lines patch added patch discarded remove patch
@@ -14,1528 +14,1528 @@
 block discarded – undo
14 14
 class EE_Form_Section_Proper extends EE_Form_Section_Validatable
15 15
 {
16 16
 
17
-    const SUBMITTED_FORM_DATA_SSN_KEY = 'submitted_form_data';
18
-
19
-    /**
20
-     * Subsections
21
-     *
22
-     * @var EE_Form_Section_Validatable[]
23
-     */
24
-    protected $_subsections = array();
25
-
26
-    /**
27
-     * Strategy for laying out the form
28
-     *
29
-     * @var EE_Form_Section_Layout_Base
30
-     */
31
-    protected $_layout_strategy;
32
-
33
-    /**
34
-     * Whether or not this form has received and validated a form submission yet
35
-     *
36
-     * @var boolean
37
-     */
38
-    protected $_received_submission = false;
39
-
40
-    /**
41
-     * message displayed to users upon successful form submission
42
-     *
43
-     * @var string
44
-     */
45
-    protected $_form_submission_success_message = '';
46
-
47
-    /**
48
-     * message displayed to users upon unsuccessful form submission
49
-     *
50
-     * @var string
51
-     */
52
-    protected $_form_submission_error_message = '';
53
-
54
-    /**
55
-     * @var array like $_REQUEST
56
-     */
57
-    protected $cached_request_data;
58
-
59
-    /**
60
-     * Stores whether this form (and its sub-sections) were found to be valid or not.
61
-     * Starts off as null, but once the form is validated, it set to either true or false
62
-     * @var boolean|null
63
-     */
64
-    protected $is_valid;
65
-
66
-    /**
67
-     * Stores all the data that will localized for form validation
68
-     *
69
-     * @var array
70
-     */
71
-    static protected $_js_localization = array();
72
-
73
-    /**
74
-     * whether or not the form's localized validation JS vars have been set
75
-     *
76
-     * @type boolean
77
-     */
78
-    static protected $_scripts_localized = false;
79
-
80
-
81
-    /**
82
-     * when constructing a proper form section, calls _construct_finalize on children
83
-     * so that they know who their parent is, and what name they've been given.
84
-     *
85
-     * @param array[] $options_array   {
86
-     * @type          $subsections     EE_Form_Section_Validatable[] where keys are the section's name
87
-     * @type          $include         string[] numerically-indexed where values are section names to be included,
88
-     *                                 and in that order. This is handy if you want
89
-     *                                 the subsections to be ordered differently than the default, and if you override
90
-     *                                 which fields are shown
91
-     * @type          $exclude         string[] values are subsections to be excluded. This is handy if you want
92
-     *                                 to remove certain default subsections (note: if you specify BOTH 'include' AND
93
-     *                                 'exclude', the inclusions will be applied first, and the exclusions will exclude
94
-     *                                 items from that list of inclusions)
95
-     * @type          $layout_strategy EE_Form_Section_Layout_Base strategy for laying out the form
96
-     *                                 } @see EE_Form_Section_Validatable::__construct()
97
-     * @throws EE_Error
98
-     */
99
-    public function __construct($options_array = array())
100
-    {
101
-        $options_array = (array) apply_filters(
102
-            'FHEE__EE_Form_Section_Proper___construct__options_array',
103
-            $options_array,
104
-            $this
105
-        );
106
-        // call parent first, as it may be setting the name
107
-        parent::__construct($options_array);
108
-        // if they've included subsections in the constructor, add them now
109
-        if (isset($options_array['include'])) {
110
-            // we are going to make sure we ONLY have those subsections to include
111
-            // AND we are going to make sure they're in that specified order
112
-            $reordered_subsections = array();
113
-            foreach ($options_array['include'] as $input_name) {
114
-                if (isset($this->_subsections[ $input_name ])) {
115
-                    $reordered_subsections[ $input_name ] = $this->_subsections[ $input_name ];
116
-                }
117
-            }
118
-            $this->_subsections = $reordered_subsections;
119
-        }
120
-        if (isset($options_array['exclude'])) {
121
-            $exclude            = $options_array['exclude'];
122
-            $this->_subsections = array_diff_key($this->_subsections, array_flip($exclude));
123
-        }
124
-        if (isset($options_array['layout_strategy'])) {
125
-            $this->_layout_strategy = $options_array['layout_strategy'];
126
-        }
127
-        if (! $this->_layout_strategy) {
128
-            $this->_layout_strategy = is_admin() ? new EE_Admin_Two_Column_Layout() : new EE_Two_Column_Layout();
129
-        }
130
-        $this->_layout_strategy->_construct_finalize($this);
131
-        // ok so we are definitely going to want the forms JS,
132
-        // so enqueue it or remember to enqueue it during wp_enqueue_scripts
133
-        if (did_action('wp_enqueue_scripts') || did_action('admin_enqueue_scripts')) {
134
-            // ok so they've constructed this object after when they should have.
135
-            // just enqueue the generic form scripts and initialize the form immediately in the JS
136
-            EE_Form_Section_Proper::wp_enqueue_scripts(true);
137
-        } else {
138
-            add_action('wp_enqueue_scripts', array('EE_Form_Section_Proper', 'wp_enqueue_scripts'));
139
-            add_action('admin_enqueue_scripts', array('EE_Form_Section_Proper', 'wp_enqueue_scripts'));
140
-        }
141
-        add_action('wp_footer', array($this, 'ensure_scripts_localized'), 1);
142
-        /**
143
-         * Gives other plugins a chance to hook in before construct finalize is called.
144
-         * The form probably doesn't yet have a parent form section.
145
-         * Since 4.9.32, when this action was introduced, this is the best place to add a subsection onto a form,
146
-         * assuming you don't care what the form section's name, HTML ID, or HTML name etc are.
147
-         * Also see AHEE__EE_Form_Section_Proper___construct_finalize__end
148
-         *
149
-         * @since 4.9.32
150
-         * @param EE_Form_Section_Proper $this          before __construct is done, but all of its logic,
151
-         *                                              except maybe calling _construct_finalize has been done
152
-         * @param array                  $options_array options passed into the constructor
153
-         */
154
-        do_action(
155
-            'AHEE__EE_Form_Input_Base___construct__before_construct_finalize_called',
156
-            $this,
157
-            $options_array
158
-        );
159
-        if (isset($options_array['name'])) {
160
-            $this->_construct_finalize(null, $options_array['name']);
161
-        }
162
-    }
163
-
164
-
165
-    /**
166
-     * Finishes construction given the parent form section and this form section's name
167
-     *
168
-     * @param EE_Form_Section_Proper $parent_form_section
169
-     * @param string                 $name
170
-     * @throws EE_Error
171
-     */
172
-    public function _construct_finalize($parent_form_section, $name)
173
-    {
174
-        parent::_construct_finalize($parent_form_section, $name);
175
-        $this->_set_default_name_if_empty();
176
-        $this->_set_default_html_id_if_empty();
177
-        foreach ($this->_subsections as $subsection_name => $subsection) {
178
-            if ($subsection instanceof EE_Form_Section_Base) {
179
-                $subsection->_construct_finalize($this, $subsection_name);
180
-            } else {
181
-                throw new EE_Error(
182
-                    sprintf(
183
-                        esc_html__(
184
-                            'Subsection "%s" is not an instanceof EE_Form_Section_Base on form "%s". It is a "%s"',
185
-                            'event_espresso'
186
-                        ),
187
-                        $subsection_name,
188
-                        get_class($this),
189
-                        $subsection ? get_class($subsection) : esc_html__('NULL', 'event_espresso')
190
-                    )
191
-                );
192
-            }
193
-        }
194
-        /**
195
-         * Action performed just after form has been given a name (and HTML ID etc) and is fully constructed.
196
-         * If you have code that should modify the form and needs it and its subsections to have a name, HTML ID
197
-         * (or other attributes derived from the name like the HTML label id, etc), this is where it should be done.
198
-         * This might only happen just before displaying the form, or just before it receives form submission data.
199
-         * If you need to modify the form or its subsections before _construct_finalize is called on it (and we've
200
-         * ensured it has a name, HTML IDs, etc
201
-         *
202
-         * @param EE_Form_Section_Proper      $this
203
-         * @param EE_Form_Section_Proper|null $parent_form_section
204
-         * @param string                      $name
205
-         */
206
-        do_action(
207
-            'AHEE__EE_Form_Section_Proper___construct_finalize__end',
208
-            $this,
209
-            $parent_form_section,
210
-            $name
211
-        );
212
-    }
213
-
214
-
215
-    /**
216
-     * Gets the layout strategy for this form section
217
-     *
218
-     * @return EE_Form_Section_Layout_Base
219
-     */
220
-    public function get_layout_strategy()
221
-    {
222
-        return $this->_layout_strategy;
223
-    }
224
-
225
-
226
-    /**
227
-     * Gets the HTML for a single input for this form section according
228
-     * to the layout strategy
229
-     *
230
-     * @param EE_Form_Input_Base $input
231
-     * @return string
232
-     */
233
-    public function get_html_for_input($input)
234
-    {
235
-        return $this->_layout_strategy->layout_input($input);
236
-    }
237
-
238
-
239
-    /**
240
-     * was_submitted - checks if form inputs are present in request data
241
-     * Basically an alias for form_data_present_in() (which is used by both
242
-     * proper form sections and form inputs)
243
-     *
244
-     * @param null $form_data
245
-     * @return boolean
246
-     * @throws EE_Error
247
-     */
248
-    public function was_submitted($form_data = null)
249
-    {
250
-        return $this->form_data_present_in($form_data);
251
-    }
252
-
253
-    /**
254
-     * Gets the cached request data; but if there is none, or $req_data was set with
255
-     * something different, refresh the cache, and then return it
256
-     * @param null $req_data
257
-     * @return array
258
-     */
259
-    protected function getCachedRequest($req_data = null)
260
-    {
261
-        if ($this->cached_request_data === null
262
-            || (
263
-                $req_data !== null &&
264
-                $req_data !== $this->cached_request_data
265
-            )
266
-        ) {
267
-            $req_data = apply_filters(
268
-                'FHEE__EE_Form_Section_Proper__receive_form_submission__req_data',
269
-                $req_data,
270
-                $this
271
-            );
272
-            if ($req_data === null) {
273
-                $req_data = array_merge($_GET, $_POST);
274
-            }
275
-            $req_data = apply_filters(
276
-                'FHEE__EE_Form_Section_Proper__receive_form_submission__request_data',
277
-                $req_data,
278
-                $this
279
-            );
280
-            $this->cached_request_data = (array) $req_data;
281
-        }
282
-        return $this->cached_request_data;
283
-    }
284
-
285
-
286
-    /**
287
-     * After the form section is initially created, call this to sanitize the data in the submission
288
-     * which relates to this form section, validate it, and set it as properties on the form.
289
-     *
290
-     * @param array|null $req_data should usually be $_POST (the default).
291
-     *                             However, you CAN supply a different array.
292
-     *                             Consider using set_defaults() instead however.
293
-     *                             (If you rendered the form in the page using echo $form_x->get_html()
294
-     *                             the inputs will have the correct name in the request data for this function
295
-     *                             to find them and populate the form with them.
296
-     *                             If you have a flat form (with only input subsections),
297
-     *                             you can supply a flat array where keys
298
-     *                             are the form input names and values are their values)
299
-     * @param boolean    $validate whether or not to perform validation on this data. Default is,
300
-     *                             of course, to validate that data, and set errors on the invalid values.
301
-     *                             But if the data has already been validated
302
-     *                             (eg you validated the data then stored it in the DB)
303
-     *                             you may want to skip this step.
304
-     * @throws InvalidArgumentException
305
-     * @throws InvalidInterfaceException
306
-     * @throws InvalidDataTypeException
307
-     * @throws EE_Error
308
-     */
309
-    public function receive_form_submission($req_data = null, $validate = true)
310
-    {
311
-        $req_data = $this->getCachedRequest($req_data);
312
-        $this->_normalize($req_data);
313
-        if ($validate) {
314
-            $this->_validate();
315
-            // if it's invalid, we're going to want to re-display so remember what they submitted
316
-            if (! $this->is_valid()) {
317
-                $this->store_submitted_form_data_in_session();
318
-            }
319
-        }
320
-        if ($this->submission_error_message() === '' && ! $this->is_valid()) {
321
-            $this->set_submission_error_message();
322
-        }
323
-        do_action(
324
-            'AHEE__EE_Form_Section_Proper__receive_form_submission__end',
325
-            $req_data,
326
-            $this,
327
-            $validate
328
-        );
329
-    }
330
-
331
-
332
-    /**
333
-     * caches the originally submitted input values in the session
334
-     * so that they can be used to repopulate the form if it failed validation
335
-     *
336
-     * @return boolean whether or not the data was successfully stored in the session
337
-     * @throws InvalidArgumentException
338
-     * @throws InvalidInterfaceException
339
-     * @throws InvalidDataTypeException
340
-     * @throws EE_Error
341
-     */
342
-    protected function store_submitted_form_data_in_session()
343
-    {
344
-        return EE_Registry::instance()->SSN->set_session_data(
345
-            array(
346
-                EE_Form_Section_Proper::SUBMITTED_FORM_DATA_SSN_KEY => $this->submitted_values(true),
347
-            )
348
-        );
349
-    }
350
-
351
-
352
-    /**
353
-     * retrieves the originally submitted input values in the session
354
-     * so that they can be used to repopulate the form if it failed validation
355
-     *
356
-     * @return array
357
-     * @throws InvalidArgumentException
358
-     * @throws InvalidInterfaceException
359
-     * @throws InvalidDataTypeException
360
-     */
361
-    protected function get_submitted_form_data_from_session()
362
-    {
363
-        $session = EE_Registry::instance()->SSN;
364
-        if ($session instanceof EE_Session) {
365
-            return $session->get_session_data(
366
-                EE_Form_Section_Proper::SUBMITTED_FORM_DATA_SSN_KEY
367
-            );
368
-        }
369
-        return array();
370
-    }
371
-
372
-
373
-    /**
374
-     * flushed the originally submitted input values from the session
375
-     *
376
-     * @return boolean whether or not the data was successfully removed from the session
377
-     * @throws InvalidArgumentException
378
-     * @throws InvalidInterfaceException
379
-     * @throws InvalidDataTypeException
380
-     */
381
-    public static function flush_submitted_form_data_from_session()
382
-    {
383
-        return EE_Registry::instance()->SSN->reset_data(
384
-            array(EE_Form_Section_Proper::SUBMITTED_FORM_DATA_SSN_KEY)
385
-        );
386
-    }
387
-
388
-
389
-    /**
390
-     * Populates this form and its subsections with data from the session.
391
-     * (Wrapper for EE_Form_Section_Proper::receive_form_submission, so it shows
392
-     * validation errors when displaying too)
393
-     * Returns true if the form was populated from the session, false otherwise
394
-     *
395
-     * @return boolean
396
-     * @throws InvalidArgumentException
397
-     * @throws InvalidInterfaceException
398
-     * @throws InvalidDataTypeException
399
-     * @throws EE_Error
400
-     */
401
-    public function populate_from_session()
402
-    {
403
-        $form_data_in_session = $this->get_submitted_form_data_from_session();
404
-        if (empty($form_data_in_session)) {
405
-            return false;
406
-        }
407
-        $this->receive_form_submission($form_data_in_session);
408
-        add_action('shutdown', array('EE_Form_Section_Proper', 'flush_submitted_form_data_from_session'));
409
-        if ($this->form_data_present_in($form_data_in_session)) {
410
-            return true;
411
-        }
412
-        return false;
413
-    }
414
-
415
-
416
-    /**
417
-     * Populates the default data for the form, given an array where keys are
418
-     * the input names, and values are their values (preferably normalized to be their
419
-     * proper PHP types, not all strings... although that should be ok too).
420
-     * Proper subsections are sub-arrays, the key being the subsection's name, and
421
-     * the value being an array formatted in teh same way
422
-     *
423
-     * @param array $default_data
424
-     * @throws EE_Error
425
-     */
426
-    public function populate_defaults($default_data)
427
-    {
428
-        foreach ($this->subsections(false) as $subsection_name => $subsection) {
429
-            if (isset($default_data[ $subsection_name ])) {
430
-                if ($subsection instanceof EE_Form_Input_Base) {
431
-                    $subsection->set_default($default_data[ $subsection_name ]);
432
-                } elseif ($subsection instanceof EE_Form_Section_Proper) {
433
-                    $subsection->populate_defaults($default_data[ $subsection_name ]);
434
-                }
435
-            }
436
-        }
437
-    }
438
-
439
-
440
-    /**
441
-     * returns true if subsection exists
442
-     *
443
-     * @param string $name
444
-     * @return boolean
445
-     */
446
-    public function subsection_exists($name)
447
-    {
448
-        return isset($this->_subsections[ $name ]) ? true : false;
449
-    }
450
-
451
-
452
-    /**
453
-     * Gets the subsection specified by its name
454
-     *
455
-     * @param string  $name
456
-     * @param boolean $require_construction_to_be_finalized most client code should leave this as TRUE
457
-     *                                                      so that the inputs will be properly configured.
458
-     *                                                      However, some client code may be ok
459
-     *                                                      with construction finalize being called later
460
-     *                                                      (realizing that the subsections' html names
461
-     *                                                      might not be set yet, etc.)
462
-     * @return EE_Form_Section_Base
463
-     * @throws EE_Error
464
-     */
465
-    public function get_subsection($name, $require_construction_to_be_finalized = true)
466
-    {
467
-        if ($require_construction_to_be_finalized) {
468
-            $this->ensure_construct_finalized_called();
469
-        }
470
-        return $this->subsection_exists($name) ? $this->_subsections[ $name ] : null;
471
-    }
472
-
473
-
474
-    /**
475
-     * Gets all the validatable subsections of this form section
476
-     *
477
-     * @return EE_Form_Section_Validatable[]
478
-     * @throws EE_Error
479
-     */
480
-    public function get_validatable_subsections()
481
-    {
482
-        $validatable_subsections = array();
483
-        foreach ($this->subsections() as $name => $obj) {
484
-            if ($obj instanceof EE_Form_Section_Validatable) {
485
-                $validatable_subsections[ $name ] = $obj;
486
-            }
487
-        }
488
-        return $validatable_subsections;
489
-    }
490
-
491
-
492
-    /**
493
-     * Gets an input by the given name. If not found, or if its not an EE_FOrm_Input_Base child,
494
-     * throw an EE_Error.
495
-     *
496
-     * @param string  $name
497
-     * @param boolean $require_construction_to_be_finalized most client code should
498
-     *                                                      leave this as TRUE so that the inputs will be properly
499
-     *                                                      configured. However, some client code may be ok with
500
-     *                                                      construction finalize being called later
501
-     *                                                      (realizing that the subsections' html names might not be
502
-     *                                                      set yet, etc.)
503
-     * @return EE_Form_Input_Base
504
-     * @throws EE_Error
505
-     */
506
-    public function get_input($name, $require_construction_to_be_finalized = true)
507
-    {
508
-        $subsection = $this->get_subsection(
509
-            $name,
510
-            $require_construction_to_be_finalized
511
-        );
512
-        if (! $subsection instanceof EE_Form_Input_Base) {
513
-            throw new EE_Error(
514
-                sprintf(
515
-                    esc_html__(
516
-                        "Subsection '%s' is not an instanceof EE_Form_Input_Base on form '%s'. It is a '%s'",
517
-                        'event_espresso'
518
-                    ),
519
-                    $name,
520
-                    get_class($this),
521
-                    $subsection ? get_class($subsection) : esc_html__('NULL', 'event_espresso')
522
-                )
523
-            );
524
-        }
525
-        return $subsection;
526
-    }
527
-
528
-
529
-    /**
530
-     * Like get_input(), gets the proper subsection of the form given the name,
531
-     * otherwise throws an EE_Error
532
-     *
533
-     * @param string  $name
534
-     * @param boolean $require_construction_to_be_finalized most client code should
535
-     *                                                      leave this as TRUE so that the inputs will be properly
536
-     *                                                      configured. However, some client code may be ok with
537
-     *                                                      construction finalize being called later
538
-     *                                                      (realizing that the subsections' html names might not be
539
-     *                                                      set yet, etc.)
540
-     * @return EE_Form_Section_Proper
541
-     * @throws EE_Error
542
-     */
543
-    public function get_proper_subsection($name, $require_construction_to_be_finalized = true)
544
-    {
545
-        $subsection = $this->get_subsection(
546
-            $name,
547
-            $require_construction_to_be_finalized
548
-        );
549
-        if (! $subsection instanceof EE_Form_Section_Proper) {
550
-            throw new EE_Error(
551
-                sprintf(
552
-                    esc_html__(
553
-                        "Subsection '%'s is not an instanceof EE_Form_Section_Proper on form '%s'",
554
-                        'event_espresso'
555
-                    ),
556
-                    $name,
557
-                    get_class($this)
558
-                )
559
-            );
560
-        }
561
-        return $subsection;
562
-    }
563
-
564
-
565
-    /**
566
-     * Gets the value of the specified input. Should be called after receive_form_submission()
567
-     * or populate_defaults() on the form, where the normalized value on the input is set.
568
-     *
569
-     * @param string $name
570
-     * @return mixed depending on the input's type and its normalization strategy
571
-     * @throws EE_Error
572
-     */
573
-    public function get_input_value($name)
574
-    {
575
-        $input = $this->get_input($name);
576
-        return $input->normalized_value();
577
-    }
578
-
579
-
580
-    /**
581
-     * Checks if this form section itself is valid, and then checks its subsections
582
-     *
583
-     * @throws EE_Error
584
-     * @return boolean
585
-     */
586
-    public function is_valid()
587
-    {
588
-        if ($this->is_valid === null) {
589
-            if (! $this->has_received_submission()) {
590
-                throw new EE_Error(
591
-                    sprintf(
592
-                        esc_html__(
593
-                            'You cannot check if a form is valid before receiving the form submission using receive_form_submission',
594
-                            'event_espresso'
595
-                        )
596
-                    )
597
-                );
598
-            }
599
-            if (! parent::is_valid()) {
600
-                $this->is_valid = false;
601
-            } else {
602
-                // ok so no general errors to this entire form section.
603
-                // so let's check the subsections, but only set errors if that hasn't been done yet
604
-                $this->is_valid = true;
605
-                foreach ($this->get_validatable_subsections() as $subsection) {
606
-                    if (! $subsection->is_valid()) {
607
-                        $this->is_valid = false;
608
-                    }
609
-                }
610
-            }
611
-        }
612
-        return $this->is_valid;
613
-    }
614
-
615
-
616
-    /**
617
-     * gets the default name of this form section if none is specified
618
-     *
619
-     * @return void
620
-     */
621
-    protected function _set_default_name_if_empty()
622
-    {
623
-        if (! $this->_name) {
624
-            $classname    = get_class($this);
625
-            $default_name = str_replace('EE_', '', $classname);
626
-            $this->_name  = $default_name;
627
-        }
628
-    }
629
-
630
-
631
-    /**
632
-     * Returns the HTML for the form, except for the form opening and closing tags
633
-     * (as the form section doesn't know where you necessarily want to send the information to),
634
-     * and except for a submit button. Enqueues JS and CSS; if called early enough we will
635
-     * try to enqueue them in the header, otherwise they'll be enqueued in the footer.
636
-     * Not doing_it_wrong because theoretically this CAN be used properly,
637
-     * provided its used during "wp_enqueue_scripts", or it doesn't need to enqueue
638
-     * any CSS.
639
-     *
640
-     * @throws InvalidArgumentException
641
-     * @throws InvalidInterfaceException
642
-     * @throws InvalidDataTypeException
643
-     * @throws EE_Error
644
-     */
645
-    public function get_html_and_js()
646
-    {
647
-        $this->enqueue_js();
648
-        return $this->get_html();
649
-    }
650
-
651
-
652
-    /**
653
-     * returns HTML for displaying this form section. recursively calls display_section() on all subsections
654
-     *
655
-     * @param bool $display_previously_submitted_data
656
-     * @return string
657
-     * @throws InvalidArgumentException
658
-     * @throws InvalidInterfaceException
659
-     * @throws InvalidDataTypeException
660
-     * @throws EE_Error
661
-     * @throws EE_Error
662
-     * @throws EE_Error
663
-     */
664
-    public function get_html($display_previously_submitted_data = true)
665
-    {
666
-        $this->ensure_construct_finalized_called();
667
-        if ($display_previously_submitted_data) {
668
-            $this->populate_from_session();
669
-        }
670
-        return $this->_form_html_filter
671
-            ? $this->_form_html_filter->filterHtml($this->_layout_strategy->layout_form(), $this)
672
-            : $this->_layout_strategy->layout_form();
673
-    }
674
-
675
-
676
-    /**
677
-     * enqueues JS and CSS for the form.
678
-     * It is preferred to call this before wp_enqueue_scripts so the
679
-     * scripts and styles can be put in the header, but if called later
680
-     * they will be put in the footer (which is OK for JS, but in HTML4 CSS should
681
-     * only be in the header; but in HTML5 its ok in the body.
682
-     * See http://stackoverflow.com/questions/4957446/load-external-css-file-in-body-tag.
683
-     * So if your form enqueues CSS, it's preferred to call this before wp_enqueue_scripts.)
684
-     *
685
-     * @return void
686
-     * @throws EE_Error
687
-     */
688
-    public function enqueue_js()
689
-    {
690
-        $this->_enqueue_and_localize_form_js();
691
-        foreach ($this->subsections() as $subsection) {
692
-            $subsection->enqueue_js();
693
-        }
694
-    }
695
-
696
-
697
-    /**
698
-     * adds a filter so that jquery validate gets enqueued in EE_System::wp_enqueue_scripts().
699
-     * This must be done BEFORE wp_enqueue_scripts() gets called, which is on
700
-     * the wp_enqueue_scripts hook.
701
-     * However, registering the form js and localizing it can happen when we
702
-     * actually output the form (which is preferred, seeing how teh form's fields
703
-     * could change until it's actually outputted)
704
-     *
705
-     * @param boolean $init_form_validation_automatically whether or not we want the form validation
706
-     *                                                    to be triggered automatically or not
707
-     * @return void
708
-     */
709
-    public static function wp_enqueue_scripts($init_form_validation_automatically = true)
710
-    {
711
-        wp_register_script(
712
-            'ee_form_section_validation',
713
-            EE_GLOBAL_ASSETS_URL . 'scripts' . '/form_section_validation.js',
714
-            array('jquery-validate', 'jquery-ui-datepicker', 'jquery-validate-extra-methods'),
715
-            EVENT_ESPRESSO_VERSION,
716
-            true
717
-        );
718
-        wp_localize_script(
719
-            'ee_form_section_validation',
720
-            'ee_form_section_validation_init',
721
-            array('init' => $init_form_validation_automatically ? '1' : '0')
722
-        );
723
-    }
724
-
725
-
726
-    /**
727
-     * gets the variables used by form_section_validation.js.
728
-     * This needs to be called AFTER we've called $this->_enqueue_jquery_validate_script,
729
-     * but before the wordpress hook wp_loaded
730
-     *
731
-     * @throws EE_Error
732
-     */
733
-    public function _enqueue_and_localize_form_js()
734
-    {
735
-        $this->ensure_construct_finalized_called();
736
-        // actually, we don't want to localize just yet. There may be other forms on the page.
737
-        // so we need to add our form section data to a static variable accessible by all form sections
738
-        // and localize it just before the footer
739
-        $this->localize_validation_rules();
740
-        add_action('wp_footer', array('EE_Form_Section_Proper', 'localize_script_for_all_forms'), 2);
741
-        add_action('admin_footer', array('EE_Form_Section_Proper', 'localize_script_for_all_forms'));
742
-    }
743
-
744
-
745
-    /**
746
-     * add our form section data to a static variable accessible by all form sections
747
-     *
748
-     * @param bool $return_for_subsection
749
-     * @return void
750
-     * @throws EE_Error
751
-     */
752
-    public function localize_validation_rules($return_for_subsection = false)
753
-    {
754
-        // we only want to localize vars ONCE for the entire form,
755
-        // so if the form section doesn't have a parent, then it must be the top dog
756
-        if ($return_for_subsection || ! $this->parent_section()) {
757
-            EE_Form_Section_Proper::$_js_localization['form_data'][ $this->html_id() ] = array(
758
-                'form_section_id'  => $this->html_id(true),
759
-                'validation_rules' => $this->get_jquery_validation_rules(),
760
-                'other_data'       => $this->get_other_js_data(),
761
-                'errors'           => $this->subsection_validation_errors_by_html_name(),
762
-            );
763
-            EE_Form_Section_Proper::$_scripts_localized                                = true;
764
-        }
765
-    }
766
-
767
-
768
-    /**
769
-     * Gets an array of extra data that will be useful for client-side javascript.
770
-     * This is primarily data added by inputs and forms in addition to any
771
-     * scripts they might enqueue
772
-     *
773
-     * @param array $form_other_js_data
774
-     * @return array
775
-     * @throws EE_Error
776
-     */
777
-    public function get_other_js_data($form_other_js_data = array())
778
-    {
779
-        foreach ($this->subsections() as $subsection) {
780
-            $form_other_js_data = $subsection->get_other_js_data($form_other_js_data);
781
-        }
782
-        return $form_other_js_data;
783
-    }
784
-
785
-
786
-    /**
787
-     * Gets a flat array of inputs for this form section and its subsections.
788
-     * Keys are their form names, and values are the inputs themselves
789
-     *
790
-     * @return EE_Form_Input_Base
791
-     * @throws EE_Error
792
-     */
793
-    public function inputs_in_subsections()
794
-    {
795
-        $inputs = array();
796
-        foreach ($this->subsections() as $subsection) {
797
-            if ($subsection instanceof EE_Form_Input_Base) {
798
-                $inputs[ $subsection->html_name() ] = $subsection;
799
-            } elseif ($subsection instanceof EE_Form_Section_Proper) {
800
-                $inputs += $subsection->inputs_in_subsections();
801
-            }
802
-        }
803
-        return $inputs;
804
-    }
805
-
806
-
807
-    /**
808
-     * Gets a flat array of all the validation errors.
809
-     * Keys are html names (because those should be unique)
810
-     * and values are a string of all their validation errors
811
-     *
812
-     * @return string[]
813
-     * @throws EE_Error
814
-     */
815
-    public function subsection_validation_errors_by_html_name()
816
-    {
817
-        $inputs = $this->inputs();
818
-        $errors = array();
819
-        foreach ($inputs as $form_input) {
820
-            if ($form_input instanceof EE_Form_Input_Base && $form_input->get_validation_errors()) {
821
-                $errors[ $form_input->html_name() ] = $form_input->get_validation_error_string();
822
-            }
823
-        }
824
-        return $errors;
825
-    }
826
-
827
-
828
-    /**
829
-     * passes all the form data required by the JS to the JS, and enqueues the few required JS files.
830
-     * Should be setup by each form during the _enqueues_and_localize_form_js
831
-     *
832
-     * @throws InvalidArgumentException
833
-     * @throws InvalidInterfaceException
834
-     * @throws InvalidDataTypeException
835
-     */
836
-    public static function localize_script_for_all_forms()
837
-    {
838
-        // allow inputs and stuff to hook in their JS and stuff here
839
-        do_action('AHEE__EE_Form_Section_Proper__localize_script_for_all_forms__begin');
840
-        EE_Form_Section_Proper::$_js_localization['localized_error_messages'] = EE_Form_Section_Proper::_get_localized_error_messages();
841
-        $email_validation_level = isset(EE_Registry::instance()->CFG->registration->email_validation_level)
842
-            ? EE_Registry::instance()->CFG->registration->email_validation_level
843
-            : 'wp_default';
844
-        EE_Form_Section_Proper::$_js_localization['email_validation_level']   = $email_validation_level;
845
-        wp_enqueue_script('ee_form_section_validation');
846
-        wp_localize_script(
847
-            'ee_form_section_validation',
848
-            'ee_form_section_vars',
849
-            EE_Form_Section_Proper::$_js_localization
850
-        );
851
-    }
852
-
853
-
854
-    /**
855
-     * ensure_scripts_localized
856
-     *
857
-     * @throws EE_Error
858
-     */
859
-    public function ensure_scripts_localized()
860
-    {
861
-        if (! EE_Form_Section_Proper::$_scripts_localized) {
862
-            $this->_enqueue_and_localize_form_js();
863
-        }
864
-    }
865
-
866
-
867
-    /**
868
-     * Gets the hard-coded validation error messages to be used in the JS. The convention
869
-     * is that the key here should be the same as the custom validation rule put in the JS file
870
-     *
871
-     * @return array keys are custom validation rules, and values are internationalized strings
872
-     */
873
-    private static function _get_localized_error_messages()
874
-    {
875
-        return array(
876
-            'validUrl' => esc_html__('This is not a valid absolute URL. Eg, http://domain.com/monkey.jpg', 'event_espresso'),
877
-            'regex'    => esc_html__('Please check your input', 'event_espresso'),
878
-        );
879
-    }
880
-
881
-
882
-    /**
883
-     * @return array
884
-     */
885
-    public static function js_localization()
886
-    {
887
-        return self::$_js_localization;
888
-    }
889
-
890
-
891
-    /**
892
-     * @return void
893
-     */
894
-    public static function reset_js_localization()
895
-    {
896
-        self::$_js_localization = array();
897
-    }
898
-
899
-
900
-    /**
901
-     * Gets the JS to put inside the jquery validation rules for subsection of this form section.
902
-     * See parent function for more...
903
-     *
904
-     * @return array
905
-     * @throws EE_Error
906
-     */
907
-    public function get_jquery_validation_rules()
908
-    {
909
-        $jquery_validation_rules = array();
910
-        foreach ($this->get_validatable_subsections() as $subsection) {
911
-            $jquery_validation_rules = array_merge(
912
-                $jquery_validation_rules,
913
-                $subsection->get_jquery_validation_rules()
914
-            );
915
-        }
916
-        return $jquery_validation_rules;
917
-    }
918
-
919
-
920
-    /**
921
-     * Sanitizes all the data and sets the sanitized value of each field
922
-     *
923
-     * @param array $req_data like $_POST
924
-     * @return void
925
-     * @throws EE_Error
926
-     */
927
-    protected function _normalize($req_data)
928
-    {
929
-        $this->_received_submission = true;
930
-        $this->_validation_errors   = array();
931
-        foreach ($this->get_validatable_subsections() as $subsection) {
932
-            try {
933
-                $subsection->_normalize($req_data);
934
-            } catch (EE_Validation_Error $e) {
935
-                $subsection->add_validation_error($e);
936
-            }
937
-        }
938
-    }
939
-
940
-
941
-    /**
942
-     * Performs validation on this form section and its subsections.
943
-     * For each subsection,
944
-     * calls _validate_{subsection_name} on THIS form (if the function exists)
945
-     * and passes it the subsection, then calls _validate on that subsection.
946
-     * If you need to perform validation on the form as a whole (considering multiple)
947
-     * you would be best to override this _validate method,
948
-     * calling parent::_validate() first.
949
-     *
950
-     * @throws EE_Error
951
-     */
952
-    protected function _validate()
953
-    {
954
-        // reset the cache of whether this form is valid or not- we're re-validating it now
955
-        $this->is_valid = null;
956
-        foreach ($this->get_validatable_subsections() as $subsection_name => $subsection) {
957
-            if (method_exists($this, '_validate_' . $subsection_name)) {
958
-                call_user_func_array(array($this, '_validate_' . $subsection_name), array($subsection));
959
-            }
960
-            $subsection->_validate();
961
-        }
962
-    }
963
-
964
-
965
-    /**
966
-     * Gets all the validated inputs for the form section
967
-     *
968
-     * @return array
969
-     * @throws EE_Error
970
-     */
971
-    public function valid_data()
972
-    {
973
-        $inputs = array();
974
-        foreach ($this->subsections() as $subsection_name => $subsection) {
975
-            if ($subsection instanceof EE_Form_Section_Proper) {
976
-                $inputs[ $subsection_name ] = $subsection->valid_data();
977
-            } elseif ($subsection instanceof EE_Form_Input_Base) {
978
-                $inputs[ $subsection_name ] = $subsection->normalized_value();
979
-            }
980
-        }
981
-        return $inputs;
982
-    }
983
-
984
-
985
-    /**
986
-     * Gets all the inputs on this form section
987
-     *
988
-     * @return EE_Form_Input_Base[]
989
-     * @throws EE_Error
990
-     */
991
-    public function inputs()
992
-    {
993
-        $inputs = array();
994
-        foreach ($this->subsections() as $subsection_name => $subsection) {
995
-            if ($subsection instanceof EE_Form_Input_Base) {
996
-                $inputs[ $subsection_name ] = $subsection;
997
-            }
998
-        }
999
-        return $inputs;
1000
-    }
1001
-
1002
-
1003
-    /**
1004
-     * Gets all the subsections which are a proper form
1005
-     *
1006
-     * @return EE_Form_Section_Proper[]
1007
-     * @throws EE_Error
1008
-     */
1009
-    public function subforms()
1010
-    {
1011
-        $form_sections = array();
1012
-        foreach ($this->subsections() as $name => $obj) {
1013
-            if ($obj instanceof EE_Form_Section_Proper) {
1014
-                $form_sections[ $name ] = $obj;
1015
-            }
1016
-        }
1017
-        return $form_sections;
1018
-    }
1019
-
1020
-
1021
-    /**
1022
-     * Gets all the subsections (inputs, proper subsections, or html-only sections).
1023
-     * Consider using inputs() or subforms()
1024
-     * if you only want form inputs or proper form sections.
1025
-     *
1026
-     * @param boolean $require_construction_to_be_finalized most client code should
1027
-     *                                                      leave this as TRUE so that the inputs will be properly
1028
-     *                                                      configured. However, some client code may be ok with
1029
-     *                                                      construction finalize being called later
1030
-     *                                                      (realizing that the subsections' html names might not be
1031
-     *                                                      set yet, etc.)
1032
-     * @return EE_Form_Section_Proper[]
1033
-     * @throws EE_Error
1034
-     */
1035
-    public function subsections($require_construction_to_be_finalized = true)
1036
-    {
1037
-        if ($require_construction_to_be_finalized) {
1038
-            $this->ensure_construct_finalized_called();
1039
-        }
1040
-        return $this->_subsections;
1041
-    }
1042
-
1043
-
1044
-    /**
1045
-     * Returns whether this form has any subforms or inputs
1046
-     * @return bool
1047
-     */
1048
-    public function hasSubsections()
1049
-    {
1050
-        return ! empty($this->_subsections);
1051
-    }
1052
-
1053
-
1054
-    /**
1055
-     * Returns a simple array where keys are input names, and values are their normalized
1056
-     * values. (Similar to calling get_input_value on inputs)
1057
-     *
1058
-     * @param boolean $include_subform_inputs Whether to include inputs from subforms,
1059
-     *                                        or just this forms' direct children inputs
1060
-     * @param boolean $flatten                Whether to force the results into 1-dimensional array,
1061
-     *                                        or allow multidimensional array
1062
-     * @return array if $flatten is TRUE it will always be a 1-dimensional array
1063
-     *                                        with array keys being input names
1064
-     *                                        (regardless of whether they are from a subsection or not),
1065
-     *                                        and if $flatten is FALSE it can be a multidimensional array
1066
-     *                                        where keys are always subsection names and values are either
1067
-     *                                        the input's normalized value, or an array like the top-level array
1068
-     * @throws EE_Error
1069
-     */
1070
-    public function input_values($include_subform_inputs = false, $flatten = false)
1071
-    {
1072
-        return $this->_input_values(false, $include_subform_inputs, $flatten);
1073
-    }
1074
-
1075
-
1076
-    /**
1077
-     * Similar to EE_Form_Section_Proper::input_values(), except this returns the 'display_value'
1078
-     * of each input. On some inputs (especially radio boxes or checkboxes), the value stored
1079
-     * is not necessarily the value we want to display to users. This creates an array
1080
-     * where keys are the input names, and values are their display values
1081
-     *
1082
-     * @param boolean $include_subform_inputs Whether to include inputs from subforms,
1083
-     *                                        or just this forms' direct children inputs
1084
-     * @param boolean $flatten                Whether to force the results into 1-dimensional array,
1085
-     *                                        or allow multidimensional array
1086
-     * @return array if $flatten is TRUE it will always be a 1-dimensional array
1087
-     *                                        with array keys being input names
1088
-     *                                        (regardless of whether they are from a subsection or not),
1089
-     *                                        and if $flatten is FALSE it can be a multidimensional array
1090
-     *                                        where keys are always subsection names and values are either
1091
-     *                                        the input's normalized value, or an array like the top-level array
1092
-     * @throws EE_Error
1093
-     */
1094
-    public function input_pretty_values($include_subform_inputs = false, $flatten = false)
1095
-    {
1096
-        return $this->_input_values(true, $include_subform_inputs, $flatten);
1097
-    }
1098
-
1099
-
1100
-    /**
1101
-     * Gets the input values from the form
1102
-     *
1103
-     * @param boolean $pretty                 Whether to retrieve the pretty value,
1104
-     *                                        or just the normalized value
1105
-     * @param boolean $include_subform_inputs Whether to include inputs from subforms,
1106
-     *                                        or just this forms' direct children inputs
1107
-     * @param boolean $flatten                Whether to force the results into 1-dimensional array,
1108
-     *                                        or allow multidimensional array
1109
-     * @return array if $flatten is TRUE it will always be a 1-dimensional array with array keys being
1110
-     *                                        input names (regardless of whether they are from a subsection or not),
1111
-     *                                        and if $flatten is FALSE it can be a multidimensional array
1112
-     *                                        where keys are always subsection names and values are either
1113
-     *                                        the input's normalized value, or an array like the top-level array
1114
-     * @throws EE_Error
1115
-     */
1116
-    public function _input_values($pretty = false, $include_subform_inputs = false, $flatten = false)
1117
-    {
1118
-        $input_values = array();
1119
-        foreach ($this->subsections() as $subsection_name => $subsection) {
1120
-            if ($subsection instanceof EE_Form_Input_Base) {
1121
-                $input_values[ $subsection_name ] = $pretty
1122
-                    ? $subsection->pretty_value()
1123
-                    : $subsection->normalized_value();
1124
-            } elseif ($subsection instanceof EE_Form_Section_Proper && $include_subform_inputs) {
1125
-                $subform_input_values = $subsection->_input_values(
1126
-                    $pretty,
1127
-                    $include_subform_inputs,
1128
-                    $flatten
1129
-                );
1130
-                if ($flatten) {
1131
-                    $input_values = array_merge($input_values, $subform_input_values);
1132
-                } else {
1133
-                    $input_values[ $subsection_name ] = $subform_input_values;
1134
-                }
1135
-            }
1136
-        }
1137
-        return $input_values;
1138
-    }
1139
-
1140
-
1141
-    /**
1142
-     * Gets the originally submitted input values from the form
1143
-     *
1144
-     * @param boolean $include_subforms  Whether to include inputs from subforms,
1145
-     *                                   or just this forms' direct children inputs
1146
-     * @return array                     if $flatten is TRUE it will always be a 1-dimensional array
1147
-     *                                   with array keys being input names
1148
-     *                                   (regardless of whether they are from a subsection or not),
1149
-     *                                   and if $flatten is FALSE it can be a multidimensional array
1150
-     *                                   where keys are always subsection names and values are either
1151
-     *                                   the input's normalized value, or an array like the top-level array
1152
-     * @throws EE_Error
1153
-     */
1154
-    public function submitted_values($include_subforms = false)
1155
-    {
1156
-        $submitted_values = array();
1157
-        foreach ($this->subsections() as $subsection) {
1158
-            if ($subsection instanceof EE_Form_Input_Base) {
1159
-                // is this input part of an array of inputs?
1160
-                if (strpos($subsection->html_name(), '[') !== false) {
1161
-                    $full_input_name  = EEH_Array::convert_array_values_to_keys(
1162
-                        explode(
1163
-                            '[',
1164
-                            str_replace(']', '', $subsection->html_name())
1165
-                        ),
1166
-                        $subsection->raw_value()
1167
-                    );
1168
-                    $submitted_values = array_replace_recursive($submitted_values, $full_input_name);
1169
-                } else {
1170
-                    $submitted_values[ $subsection->html_name() ] = $subsection->raw_value();
1171
-                }
1172
-            } elseif ($subsection instanceof EE_Form_Section_Proper && $include_subforms) {
1173
-                $subform_input_values = $subsection->submitted_values($include_subforms);
1174
-                $submitted_values     = array_replace_recursive($submitted_values, $subform_input_values);
1175
-            }
1176
-        }
1177
-        return $submitted_values;
1178
-    }
1179
-
1180
-
1181
-    /**
1182
-     * Indicates whether or not this form has received a submission yet
1183
-     * (ie, had receive_form_submission called on it yet)
1184
-     *
1185
-     * @return boolean
1186
-     * @throws EE_Error
1187
-     */
1188
-    public function has_received_submission()
1189
-    {
1190
-        $this->ensure_construct_finalized_called();
1191
-        return $this->_received_submission;
1192
-    }
1193
-
1194
-
1195
-    /**
1196
-     * Equivalent to passing 'exclude' in the constructor's options array.
1197
-     * Removes the listed inputs from the form
1198
-     *
1199
-     * @param array $inputs_to_exclude values are the input names
1200
-     * @return void
1201
-     */
1202
-    public function exclude(array $inputs_to_exclude = array())
1203
-    {
1204
-        foreach ($inputs_to_exclude as $input_to_exclude_name) {
1205
-            unset($this->_subsections[ $input_to_exclude_name ]);
1206
-        }
1207
-    }
1208
-
1209
-
1210
-    /**
1211
-     * Changes these inputs' display strategy to be EE_Hidden_Display_Strategy.
1212
-     * @param array $inputs_to_hide
1213
-     * @throws EE_Error
1214
-     */
1215
-    public function hide(array $inputs_to_hide = array())
1216
-    {
1217
-        foreach ($inputs_to_hide as $input_to_hide) {
1218
-            $input = $this->get_input($input_to_hide);
1219
-            $input->set_display_strategy(new EE_Hidden_Display_Strategy());
1220
-        }
1221
-    }
1222
-
1223
-
1224
-    /**
1225
-     * add_subsections
1226
-     * Adds the listed subsections to the form section.
1227
-     * If $subsection_name_to_target is provided,
1228
-     * then new subsections are added before or after that subsection,
1229
-     * otherwise to the start or end of the entire subsections array.
1230
-     *
1231
-     * @param EE_Form_Section_Base[] $new_subsections           array of new form subsections
1232
-     *                                                          where keys are their names
1233
-     * @param string                 $subsection_name_to_target an existing for section that $new_subsections
1234
-     *                                                          should be added before or after
1235
-     *                                                          IF $subsection_name_to_target is null,
1236
-     *                                                          then $new_subsections will be added to
1237
-     *                                                          the beginning or end of the entire subsections array
1238
-     * @param boolean                $add_before                whether to add $new_subsections, before or after
1239
-     *                                                          $subsection_name_to_target,
1240
-     *                                                          or if $subsection_name_to_target is null,
1241
-     *                                                          before or after entire subsections array
1242
-     * @return void
1243
-     * @throws EE_Error
1244
-     */
1245
-    public function add_subsections($new_subsections, $subsection_name_to_target = null, $add_before = true)
1246
-    {
1247
-        foreach ($new_subsections as $subsection_name => $subsection) {
1248
-            if (! $subsection instanceof EE_Form_Section_Base) {
1249
-                EE_Error::add_error(
1250
-                    sprintf(
1251
-                        esc_html__(
1252
-                            "Trying to add a %s as a subsection (it was named '%s') to the form section '%s'. It was removed.",
1253
-                            'event_espresso'
1254
-                        ),
1255
-                        get_class($subsection),
1256
-                        $subsection_name,
1257
-                        $this->name()
1258
-                    )
1259
-                );
1260
-                unset($new_subsections[ $subsection_name ]);
1261
-            }
1262
-        }
1263
-        $this->_subsections = EEH_Array::insert_into_array(
1264
-            $this->_subsections,
1265
-            $new_subsections,
1266
-            $subsection_name_to_target,
1267
-            $add_before
1268
-        );
1269
-        if ($this->_construction_finalized) {
1270
-            foreach ($this->_subsections as $name => $subsection) {
1271
-                $subsection->_construct_finalize($this, $name);
1272
-            }
1273
-        }
1274
-    }
1275
-
1276
-
1277
-    /**
1278
-     * @param string $subsection_name
1279
-     * @param bool   $recursive
1280
-     * @return bool
1281
-     */
1282
-    public function has_subsection($subsection_name, $recursive = false)
1283
-    {
1284
-        foreach ($this->_subsections as $name => $subsection) {
1285
-            if ($name === $subsection_name
1286
-                || (
1287
-                    $recursive
1288
-                    && $subsection instanceof EE_Form_Section_Proper
1289
-                    && $subsection->has_subsection($subsection_name, $recursive)
1290
-                )
1291
-            ) {
1292
-                return true;
1293
-            }
1294
-        }
1295
-        return false;
1296
-    }
1297
-
1298
-
1299
-
1300
-    /**
1301
-     * Just gets all validatable subsections to clean their sensitive data
1302
-     *
1303
-     * @throws EE_Error
1304
-     */
1305
-    public function clean_sensitive_data()
1306
-    {
1307
-        foreach ($this->get_validatable_subsections() as $subsection) {
1308
-            $subsection->clean_sensitive_data();
1309
-        }
1310
-    }
1311
-
1312
-
1313
-    /**
1314
-     * Sets the submission error message (aka validation error message for this form section and all sub-sections)
1315
-     * @param string                           $form_submission_error_message
1316
-     * @param EE_Form_Section_Validatable $form_section unused
1317
-     * @throws EE_Error
1318
-     */
1319
-    public function set_submission_error_message(
1320
-        $form_submission_error_message = ''
1321
-    ) {
1322
-        $this->_form_submission_error_message = ! empty($form_submission_error_message)
1323
-            ? $form_submission_error_message
1324
-            : $this->getAllValidationErrorsString();
1325
-    }
1326
-
1327
-
1328
-    /**
1329
-     * Returns the cached error message. A default value is set for this during _validate(),
1330
-     * (called during receive_form_submission) but it can be explicitly set using
1331
-     * set_submission_error_message
1332
-     *
1333
-     * @return string
1334
-     */
1335
-    public function submission_error_message()
1336
-    {
1337
-        return $this->_form_submission_error_message;
1338
-    }
1339
-
1340
-
1341
-    /**
1342
-     * Sets a message to display if the data submitted to the form was valid.
1343
-     * @param string $form_submission_success_message
1344
-     */
1345
-    public function set_submission_success_message($form_submission_success_message = '')
1346
-    {
1347
-        $this->_form_submission_success_message = ! empty($form_submission_success_message)
1348
-            ? $form_submission_success_message
1349
-            : esc_html__('Form submitted successfully', 'event_espresso');
1350
-    }
1351
-
1352
-
1353
-    /**
1354
-     * Gets a message appropriate for display when the form is correctly submitted
1355
-     * @return string
1356
-     */
1357
-    public function submission_success_message()
1358
-    {
1359
-        return $this->_form_submission_success_message;
1360
-    }
1361
-
1362
-
1363
-    /**
1364
-     * Returns the prefix that should be used on child of this form section for
1365
-     * their html names. If this form section itself has a parent, prepends ITS
1366
-     * prefix onto this form section's prefix. Used primarily by
1367
-     * EE_Form_Input_Base::_set_default_html_name_if_empty
1368
-     *
1369
-     * @return string
1370
-     * @throws EE_Error
1371
-     */
1372
-    public function html_name_prefix()
1373
-    {
1374
-        if ($this->parent_section() instanceof EE_Form_Section_Proper) {
1375
-            return $this->parent_section()->html_name_prefix() . '[' . $this->name() . ']';
1376
-        }
1377
-        return $this->name();
1378
-    }
1379
-
1380
-
1381
-    /**
1382
-     * Gets the name, but first checks _construct_finalize has been called. If not,
1383
-     * calls it (assumes there is no parent and that we want the name to be whatever
1384
-     * was set, which is probably nothing, or the classname)
1385
-     *
1386
-     * @return string
1387
-     * @throws EE_Error
1388
-     */
1389
-    public function name()
1390
-    {
1391
-        $this->ensure_construct_finalized_called();
1392
-        return parent::name();
1393
-    }
1394
-
1395
-
1396
-    /**
1397
-     * @return EE_Form_Section_Proper
1398
-     * @throws EE_Error
1399
-     */
1400
-    public function parent_section()
1401
-    {
1402
-        $this->ensure_construct_finalized_called();
1403
-        return parent::parent_section();
1404
-    }
1405
-
1406
-
1407
-    /**
1408
-     * make sure construction finalized was called, otherwise children might not be ready
1409
-     *
1410
-     * @return void
1411
-     * @throws EE_Error
1412
-     */
1413
-    public function ensure_construct_finalized_called()
1414
-    {
1415
-        if (! $this->_construction_finalized) {
1416
-            $this->_construct_finalize($this->_parent_section, $this->_name);
1417
-        }
1418
-    }
1419
-
1420
-
1421
-    /**
1422
-     * Checks if any of this form section's inputs, or any of its children's inputs,
1423
-     * are in teh form data. If any are found, returns true. Else false
1424
-     *
1425
-     * @param array $req_data
1426
-     * @return boolean
1427
-     * @throws EE_Error
1428
-     */
1429
-    public function form_data_present_in($req_data = null)
1430
-    {
1431
-        $req_data = $this->getCachedRequest($req_data);
1432
-        foreach ($this->subsections() as $subsection) {
1433
-            if ($subsection instanceof EE_Form_Input_Base) {
1434
-                if ($subsection->form_data_present_in($req_data)) {
1435
-                    return true;
1436
-                }
1437
-            } elseif ($subsection instanceof EE_Form_Section_Proper) {
1438
-                if ($subsection->form_data_present_in($req_data)) {
1439
-                    return true;
1440
-                }
1441
-            }
1442
-        }
1443
-        return false;
1444
-    }
1445
-
1446
-
1447
-    /**
1448
-     * Gets validation errors for this form section and subsections
1449
-     * Similar to EE_Form_Section_Validatable::get_validation_errors() except this
1450
-     * gets the validation errors for ALL subsection
1451
-     *
1452
-     * @return EE_Validation_Error[]
1453
-     * @throws EE_Error
1454
-     */
1455
-    public function get_validation_errors_accumulated()
1456
-    {
1457
-        $validation_errors = $this->get_validation_errors();
1458
-        foreach ($this->get_validatable_subsections() as $subsection) {
1459
-            if ($subsection instanceof EE_Form_Section_Proper) {
1460
-                $validation_errors_on_this_subsection = $subsection->get_validation_errors_accumulated();
1461
-            } else {
1462
-                $validation_errors_on_this_subsection = $subsection->get_validation_errors();
1463
-            }
1464
-            if ($validation_errors_on_this_subsection) {
1465
-                $validation_errors = array_merge($validation_errors, $validation_errors_on_this_subsection);
1466
-            }
1467
-        }
1468
-        return $validation_errors;
1469
-    }
1470
-
1471
-    /**
1472
-     * Fetch validation errors from children and grandchildren and puts them in a single string.
1473
-     * This traverses the form section tree to generate this, but you probably want to instead use
1474
-     * get_form_submission_error_message() which is usually this message cached (or a custom validation error message)
1475
-     *
1476
-     * @return string
1477
-     * @since 4.9.59.p
1478
-     */
1479
-    protected function getAllValidationErrorsString()
1480
-    {
1481
-        $submission_error_messages = array();
1482
-        // bad, bad, bad registrant
1483
-        foreach ($this->get_validation_errors_accumulated() as $validation_error) {
1484
-            if ($validation_error instanceof EE_Validation_Error) {
1485
-                $form_section = $validation_error->get_form_section();
1486
-                if ($form_section instanceof EE_Form_Input_Base) {
1487
-                    $label = $validation_error->get_form_section()->html_label_text();
1488
-                } elseif ($form_section instanceof EE_Form_Section_Validatable) {
1489
-                    $label = $validation_error->get_form_section()->name();
1490
-                } else {
1491
-                    $label = esc_html__('Unknown', 'event_espresso');
1492
-                }
1493
-                $submission_error_messages[] = sprintf(
1494
-                    __('%s : %s', 'event_espresso'),
1495
-                    $label,
1496
-                    $validation_error->getMessage()
1497
-                );
1498
-            }
1499
-        }
1500
-        return implode('<br>', $submission_error_messages);
1501
-    }
1502
-
1503
-
1504
-    /**
1505
-     * This isn't just the name of an input, it's a path pointing to an input. The
1506
-     * path is similar to a folder path: slash (/) means to descend into a subsection,
1507
-     * dot-dot-slash (../) means to ascend into the parent section.
1508
-     * After a series of slashes and dot-dot-slashes, there should be the name of an input,
1509
-     * which will be returned.
1510
-     * Eg, if you want the related input to be conditional on a sibling input name 'foobar'
1511
-     * just use 'foobar'. If you want it to be conditional on an aunt/uncle input name
1512
-     * 'baz', use '../baz'. If you want it to be conditional on a cousin input,
1513
-     * the child of 'baz_section' named 'baz_child', use '../baz_section/baz_child'.
1514
-     * Etc
1515
-     *
1516
-     * @param string|false $form_section_path we accept false also because substr( '../', '../' ) = false
1517
-     * @return EE_Form_Section_Base
1518
-     * @throws EE_Error
1519
-     */
1520
-    public function find_section_from_path($form_section_path)
1521
-    {
1522
-        // check if we can find the input from purely going straight up the tree
1523
-        $input = parent::find_section_from_path($form_section_path);
1524
-        if ($input instanceof EE_Form_Section_Base) {
1525
-            return $input;
1526
-        }
1527
-        $next_slash_pos = strpos($form_section_path, '/');
1528
-        if ($next_slash_pos !== false) {
1529
-            $child_section_name = substr($form_section_path, 0, $next_slash_pos);
1530
-            $subpath            = substr($form_section_path, $next_slash_pos + 1);
1531
-        } else {
1532
-            $child_section_name = $form_section_path;
1533
-            $subpath            = '';
1534
-        }
1535
-        $child_section = $this->get_subsection($child_section_name);
1536
-        if ($child_section instanceof EE_Form_Section_Base) {
1537
-            return $child_section->find_section_from_path($subpath);
1538
-        }
1539
-        return null;
1540
-    }
17
+	const SUBMITTED_FORM_DATA_SSN_KEY = 'submitted_form_data';
18
+
19
+	/**
20
+	 * Subsections
21
+	 *
22
+	 * @var EE_Form_Section_Validatable[]
23
+	 */
24
+	protected $_subsections = array();
25
+
26
+	/**
27
+	 * Strategy for laying out the form
28
+	 *
29
+	 * @var EE_Form_Section_Layout_Base
30
+	 */
31
+	protected $_layout_strategy;
32
+
33
+	/**
34
+	 * Whether or not this form has received and validated a form submission yet
35
+	 *
36
+	 * @var boolean
37
+	 */
38
+	protected $_received_submission = false;
39
+
40
+	/**
41
+	 * message displayed to users upon successful form submission
42
+	 *
43
+	 * @var string
44
+	 */
45
+	protected $_form_submission_success_message = '';
46
+
47
+	/**
48
+	 * message displayed to users upon unsuccessful form submission
49
+	 *
50
+	 * @var string
51
+	 */
52
+	protected $_form_submission_error_message = '';
53
+
54
+	/**
55
+	 * @var array like $_REQUEST
56
+	 */
57
+	protected $cached_request_data;
58
+
59
+	/**
60
+	 * Stores whether this form (and its sub-sections) were found to be valid or not.
61
+	 * Starts off as null, but once the form is validated, it set to either true or false
62
+	 * @var boolean|null
63
+	 */
64
+	protected $is_valid;
65
+
66
+	/**
67
+	 * Stores all the data that will localized for form validation
68
+	 *
69
+	 * @var array
70
+	 */
71
+	static protected $_js_localization = array();
72
+
73
+	/**
74
+	 * whether or not the form's localized validation JS vars have been set
75
+	 *
76
+	 * @type boolean
77
+	 */
78
+	static protected $_scripts_localized = false;
79
+
80
+
81
+	/**
82
+	 * when constructing a proper form section, calls _construct_finalize on children
83
+	 * so that they know who their parent is, and what name they've been given.
84
+	 *
85
+	 * @param array[] $options_array   {
86
+	 * @type          $subsections     EE_Form_Section_Validatable[] where keys are the section's name
87
+	 * @type          $include         string[] numerically-indexed where values are section names to be included,
88
+	 *                                 and in that order. This is handy if you want
89
+	 *                                 the subsections to be ordered differently than the default, and if you override
90
+	 *                                 which fields are shown
91
+	 * @type          $exclude         string[] values are subsections to be excluded. This is handy if you want
92
+	 *                                 to remove certain default subsections (note: if you specify BOTH 'include' AND
93
+	 *                                 'exclude', the inclusions will be applied first, and the exclusions will exclude
94
+	 *                                 items from that list of inclusions)
95
+	 * @type          $layout_strategy EE_Form_Section_Layout_Base strategy for laying out the form
96
+	 *                                 } @see EE_Form_Section_Validatable::__construct()
97
+	 * @throws EE_Error
98
+	 */
99
+	public function __construct($options_array = array())
100
+	{
101
+		$options_array = (array) apply_filters(
102
+			'FHEE__EE_Form_Section_Proper___construct__options_array',
103
+			$options_array,
104
+			$this
105
+		);
106
+		// call parent first, as it may be setting the name
107
+		parent::__construct($options_array);
108
+		// if they've included subsections in the constructor, add them now
109
+		if (isset($options_array['include'])) {
110
+			// we are going to make sure we ONLY have those subsections to include
111
+			// AND we are going to make sure they're in that specified order
112
+			$reordered_subsections = array();
113
+			foreach ($options_array['include'] as $input_name) {
114
+				if (isset($this->_subsections[ $input_name ])) {
115
+					$reordered_subsections[ $input_name ] = $this->_subsections[ $input_name ];
116
+				}
117
+			}
118
+			$this->_subsections = $reordered_subsections;
119
+		}
120
+		if (isset($options_array['exclude'])) {
121
+			$exclude            = $options_array['exclude'];
122
+			$this->_subsections = array_diff_key($this->_subsections, array_flip($exclude));
123
+		}
124
+		if (isset($options_array['layout_strategy'])) {
125
+			$this->_layout_strategy = $options_array['layout_strategy'];
126
+		}
127
+		if (! $this->_layout_strategy) {
128
+			$this->_layout_strategy = is_admin() ? new EE_Admin_Two_Column_Layout() : new EE_Two_Column_Layout();
129
+		}
130
+		$this->_layout_strategy->_construct_finalize($this);
131
+		// ok so we are definitely going to want the forms JS,
132
+		// so enqueue it or remember to enqueue it during wp_enqueue_scripts
133
+		if (did_action('wp_enqueue_scripts') || did_action('admin_enqueue_scripts')) {
134
+			// ok so they've constructed this object after when they should have.
135
+			// just enqueue the generic form scripts and initialize the form immediately in the JS
136
+			EE_Form_Section_Proper::wp_enqueue_scripts(true);
137
+		} else {
138
+			add_action('wp_enqueue_scripts', array('EE_Form_Section_Proper', 'wp_enqueue_scripts'));
139
+			add_action('admin_enqueue_scripts', array('EE_Form_Section_Proper', 'wp_enqueue_scripts'));
140
+		}
141
+		add_action('wp_footer', array($this, 'ensure_scripts_localized'), 1);
142
+		/**
143
+		 * Gives other plugins a chance to hook in before construct finalize is called.
144
+		 * The form probably doesn't yet have a parent form section.
145
+		 * Since 4.9.32, when this action was introduced, this is the best place to add a subsection onto a form,
146
+		 * assuming you don't care what the form section's name, HTML ID, or HTML name etc are.
147
+		 * Also see AHEE__EE_Form_Section_Proper___construct_finalize__end
148
+		 *
149
+		 * @since 4.9.32
150
+		 * @param EE_Form_Section_Proper $this          before __construct is done, but all of its logic,
151
+		 *                                              except maybe calling _construct_finalize has been done
152
+		 * @param array                  $options_array options passed into the constructor
153
+		 */
154
+		do_action(
155
+			'AHEE__EE_Form_Input_Base___construct__before_construct_finalize_called',
156
+			$this,
157
+			$options_array
158
+		);
159
+		if (isset($options_array['name'])) {
160
+			$this->_construct_finalize(null, $options_array['name']);
161
+		}
162
+	}
163
+
164
+
165
+	/**
166
+	 * Finishes construction given the parent form section and this form section's name
167
+	 *
168
+	 * @param EE_Form_Section_Proper $parent_form_section
169
+	 * @param string                 $name
170
+	 * @throws EE_Error
171
+	 */
172
+	public function _construct_finalize($parent_form_section, $name)
173
+	{
174
+		parent::_construct_finalize($parent_form_section, $name);
175
+		$this->_set_default_name_if_empty();
176
+		$this->_set_default_html_id_if_empty();
177
+		foreach ($this->_subsections as $subsection_name => $subsection) {
178
+			if ($subsection instanceof EE_Form_Section_Base) {
179
+				$subsection->_construct_finalize($this, $subsection_name);
180
+			} else {
181
+				throw new EE_Error(
182
+					sprintf(
183
+						esc_html__(
184
+							'Subsection "%s" is not an instanceof EE_Form_Section_Base on form "%s". It is a "%s"',
185
+							'event_espresso'
186
+						),
187
+						$subsection_name,
188
+						get_class($this),
189
+						$subsection ? get_class($subsection) : esc_html__('NULL', 'event_espresso')
190
+					)
191
+				);
192
+			}
193
+		}
194
+		/**
195
+		 * Action performed just after form has been given a name (and HTML ID etc) and is fully constructed.
196
+		 * If you have code that should modify the form and needs it and its subsections to have a name, HTML ID
197
+		 * (or other attributes derived from the name like the HTML label id, etc), this is where it should be done.
198
+		 * This might only happen just before displaying the form, or just before it receives form submission data.
199
+		 * If you need to modify the form or its subsections before _construct_finalize is called on it (and we've
200
+		 * ensured it has a name, HTML IDs, etc
201
+		 *
202
+		 * @param EE_Form_Section_Proper      $this
203
+		 * @param EE_Form_Section_Proper|null $parent_form_section
204
+		 * @param string                      $name
205
+		 */
206
+		do_action(
207
+			'AHEE__EE_Form_Section_Proper___construct_finalize__end',
208
+			$this,
209
+			$parent_form_section,
210
+			$name
211
+		);
212
+	}
213
+
214
+
215
+	/**
216
+	 * Gets the layout strategy for this form section
217
+	 *
218
+	 * @return EE_Form_Section_Layout_Base
219
+	 */
220
+	public function get_layout_strategy()
221
+	{
222
+		return $this->_layout_strategy;
223
+	}
224
+
225
+
226
+	/**
227
+	 * Gets the HTML for a single input for this form section according
228
+	 * to the layout strategy
229
+	 *
230
+	 * @param EE_Form_Input_Base $input
231
+	 * @return string
232
+	 */
233
+	public function get_html_for_input($input)
234
+	{
235
+		return $this->_layout_strategy->layout_input($input);
236
+	}
237
+
238
+
239
+	/**
240
+	 * was_submitted - checks if form inputs are present in request data
241
+	 * Basically an alias for form_data_present_in() (which is used by both
242
+	 * proper form sections and form inputs)
243
+	 *
244
+	 * @param null $form_data
245
+	 * @return boolean
246
+	 * @throws EE_Error
247
+	 */
248
+	public function was_submitted($form_data = null)
249
+	{
250
+		return $this->form_data_present_in($form_data);
251
+	}
252
+
253
+	/**
254
+	 * Gets the cached request data; but if there is none, or $req_data was set with
255
+	 * something different, refresh the cache, and then return it
256
+	 * @param null $req_data
257
+	 * @return array
258
+	 */
259
+	protected function getCachedRequest($req_data = null)
260
+	{
261
+		if ($this->cached_request_data === null
262
+			|| (
263
+				$req_data !== null &&
264
+				$req_data !== $this->cached_request_data
265
+			)
266
+		) {
267
+			$req_data = apply_filters(
268
+				'FHEE__EE_Form_Section_Proper__receive_form_submission__req_data',
269
+				$req_data,
270
+				$this
271
+			);
272
+			if ($req_data === null) {
273
+				$req_data = array_merge($_GET, $_POST);
274
+			}
275
+			$req_data = apply_filters(
276
+				'FHEE__EE_Form_Section_Proper__receive_form_submission__request_data',
277
+				$req_data,
278
+				$this
279
+			);
280
+			$this->cached_request_data = (array) $req_data;
281
+		}
282
+		return $this->cached_request_data;
283
+	}
284
+
285
+
286
+	/**
287
+	 * After the form section is initially created, call this to sanitize the data in the submission
288
+	 * which relates to this form section, validate it, and set it as properties on the form.
289
+	 *
290
+	 * @param array|null $req_data should usually be $_POST (the default).
291
+	 *                             However, you CAN supply a different array.
292
+	 *                             Consider using set_defaults() instead however.
293
+	 *                             (If you rendered the form in the page using echo $form_x->get_html()
294
+	 *                             the inputs will have the correct name in the request data for this function
295
+	 *                             to find them and populate the form with them.
296
+	 *                             If you have a flat form (with only input subsections),
297
+	 *                             you can supply a flat array where keys
298
+	 *                             are the form input names and values are their values)
299
+	 * @param boolean    $validate whether or not to perform validation on this data. Default is,
300
+	 *                             of course, to validate that data, and set errors on the invalid values.
301
+	 *                             But if the data has already been validated
302
+	 *                             (eg you validated the data then stored it in the DB)
303
+	 *                             you may want to skip this step.
304
+	 * @throws InvalidArgumentException
305
+	 * @throws InvalidInterfaceException
306
+	 * @throws InvalidDataTypeException
307
+	 * @throws EE_Error
308
+	 */
309
+	public function receive_form_submission($req_data = null, $validate = true)
310
+	{
311
+		$req_data = $this->getCachedRequest($req_data);
312
+		$this->_normalize($req_data);
313
+		if ($validate) {
314
+			$this->_validate();
315
+			// if it's invalid, we're going to want to re-display so remember what they submitted
316
+			if (! $this->is_valid()) {
317
+				$this->store_submitted_form_data_in_session();
318
+			}
319
+		}
320
+		if ($this->submission_error_message() === '' && ! $this->is_valid()) {
321
+			$this->set_submission_error_message();
322
+		}
323
+		do_action(
324
+			'AHEE__EE_Form_Section_Proper__receive_form_submission__end',
325
+			$req_data,
326
+			$this,
327
+			$validate
328
+		);
329
+	}
330
+
331
+
332
+	/**
333
+	 * caches the originally submitted input values in the session
334
+	 * so that they can be used to repopulate the form if it failed validation
335
+	 *
336
+	 * @return boolean whether or not the data was successfully stored in the session
337
+	 * @throws InvalidArgumentException
338
+	 * @throws InvalidInterfaceException
339
+	 * @throws InvalidDataTypeException
340
+	 * @throws EE_Error
341
+	 */
342
+	protected function store_submitted_form_data_in_session()
343
+	{
344
+		return EE_Registry::instance()->SSN->set_session_data(
345
+			array(
346
+				EE_Form_Section_Proper::SUBMITTED_FORM_DATA_SSN_KEY => $this->submitted_values(true),
347
+			)
348
+		);
349
+	}
350
+
351
+
352
+	/**
353
+	 * retrieves the originally submitted input values in the session
354
+	 * so that they can be used to repopulate the form if it failed validation
355
+	 *
356
+	 * @return array
357
+	 * @throws InvalidArgumentException
358
+	 * @throws InvalidInterfaceException
359
+	 * @throws InvalidDataTypeException
360
+	 */
361
+	protected function get_submitted_form_data_from_session()
362
+	{
363
+		$session = EE_Registry::instance()->SSN;
364
+		if ($session instanceof EE_Session) {
365
+			return $session->get_session_data(
366
+				EE_Form_Section_Proper::SUBMITTED_FORM_DATA_SSN_KEY
367
+			);
368
+		}
369
+		return array();
370
+	}
371
+
372
+
373
+	/**
374
+	 * flushed the originally submitted input values from the session
375
+	 *
376
+	 * @return boolean whether or not the data was successfully removed from the session
377
+	 * @throws InvalidArgumentException
378
+	 * @throws InvalidInterfaceException
379
+	 * @throws InvalidDataTypeException
380
+	 */
381
+	public static function flush_submitted_form_data_from_session()
382
+	{
383
+		return EE_Registry::instance()->SSN->reset_data(
384
+			array(EE_Form_Section_Proper::SUBMITTED_FORM_DATA_SSN_KEY)
385
+		);
386
+	}
387
+
388
+
389
+	/**
390
+	 * Populates this form and its subsections with data from the session.
391
+	 * (Wrapper for EE_Form_Section_Proper::receive_form_submission, so it shows
392
+	 * validation errors when displaying too)
393
+	 * Returns true if the form was populated from the session, false otherwise
394
+	 *
395
+	 * @return boolean
396
+	 * @throws InvalidArgumentException
397
+	 * @throws InvalidInterfaceException
398
+	 * @throws InvalidDataTypeException
399
+	 * @throws EE_Error
400
+	 */
401
+	public function populate_from_session()
402
+	{
403
+		$form_data_in_session = $this->get_submitted_form_data_from_session();
404
+		if (empty($form_data_in_session)) {
405
+			return false;
406
+		}
407
+		$this->receive_form_submission($form_data_in_session);
408
+		add_action('shutdown', array('EE_Form_Section_Proper', 'flush_submitted_form_data_from_session'));
409
+		if ($this->form_data_present_in($form_data_in_session)) {
410
+			return true;
411
+		}
412
+		return false;
413
+	}
414
+
415
+
416
+	/**
417
+	 * Populates the default data for the form, given an array where keys are
418
+	 * the input names, and values are their values (preferably normalized to be their
419
+	 * proper PHP types, not all strings... although that should be ok too).
420
+	 * Proper subsections are sub-arrays, the key being the subsection's name, and
421
+	 * the value being an array formatted in teh same way
422
+	 *
423
+	 * @param array $default_data
424
+	 * @throws EE_Error
425
+	 */
426
+	public function populate_defaults($default_data)
427
+	{
428
+		foreach ($this->subsections(false) as $subsection_name => $subsection) {
429
+			if (isset($default_data[ $subsection_name ])) {
430
+				if ($subsection instanceof EE_Form_Input_Base) {
431
+					$subsection->set_default($default_data[ $subsection_name ]);
432
+				} elseif ($subsection instanceof EE_Form_Section_Proper) {
433
+					$subsection->populate_defaults($default_data[ $subsection_name ]);
434
+				}
435
+			}
436
+		}
437
+	}
438
+
439
+
440
+	/**
441
+	 * returns true if subsection exists
442
+	 *
443
+	 * @param string $name
444
+	 * @return boolean
445
+	 */
446
+	public function subsection_exists($name)
447
+	{
448
+		return isset($this->_subsections[ $name ]) ? true : false;
449
+	}
450
+
451
+
452
+	/**
453
+	 * Gets the subsection specified by its name
454
+	 *
455
+	 * @param string  $name
456
+	 * @param boolean $require_construction_to_be_finalized most client code should leave this as TRUE
457
+	 *                                                      so that the inputs will be properly configured.
458
+	 *                                                      However, some client code may be ok
459
+	 *                                                      with construction finalize being called later
460
+	 *                                                      (realizing that the subsections' html names
461
+	 *                                                      might not be set yet, etc.)
462
+	 * @return EE_Form_Section_Base
463
+	 * @throws EE_Error
464
+	 */
465
+	public function get_subsection($name, $require_construction_to_be_finalized = true)
466
+	{
467
+		if ($require_construction_to_be_finalized) {
468
+			$this->ensure_construct_finalized_called();
469
+		}
470
+		return $this->subsection_exists($name) ? $this->_subsections[ $name ] : null;
471
+	}
472
+
473
+
474
+	/**
475
+	 * Gets all the validatable subsections of this form section
476
+	 *
477
+	 * @return EE_Form_Section_Validatable[]
478
+	 * @throws EE_Error
479
+	 */
480
+	public function get_validatable_subsections()
481
+	{
482
+		$validatable_subsections = array();
483
+		foreach ($this->subsections() as $name => $obj) {
484
+			if ($obj instanceof EE_Form_Section_Validatable) {
485
+				$validatable_subsections[ $name ] = $obj;
486
+			}
487
+		}
488
+		return $validatable_subsections;
489
+	}
490
+
491
+
492
+	/**
493
+	 * Gets an input by the given name. If not found, or if its not an EE_FOrm_Input_Base child,
494
+	 * throw an EE_Error.
495
+	 *
496
+	 * @param string  $name
497
+	 * @param boolean $require_construction_to_be_finalized most client code should
498
+	 *                                                      leave this as TRUE so that the inputs will be properly
499
+	 *                                                      configured. However, some client code may be ok with
500
+	 *                                                      construction finalize being called later
501
+	 *                                                      (realizing that the subsections' html names might not be
502
+	 *                                                      set yet, etc.)
503
+	 * @return EE_Form_Input_Base
504
+	 * @throws EE_Error
505
+	 */
506
+	public function get_input($name, $require_construction_to_be_finalized = true)
507
+	{
508
+		$subsection = $this->get_subsection(
509
+			$name,
510
+			$require_construction_to_be_finalized
511
+		);
512
+		if (! $subsection instanceof EE_Form_Input_Base) {
513
+			throw new EE_Error(
514
+				sprintf(
515
+					esc_html__(
516
+						"Subsection '%s' is not an instanceof EE_Form_Input_Base on form '%s'. It is a '%s'",
517
+						'event_espresso'
518
+					),
519
+					$name,
520
+					get_class($this),
521
+					$subsection ? get_class($subsection) : esc_html__('NULL', 'event_espresso')
522
+				)
523
+			);
524
+		}
525
+		return $subsection;
526
+	}
527
+
528
+
529
+	/**
530
+	 * Like get_input(), gets the proper subsection of the form given the name,
531
+	 * otherwise throws an EE_Error
532
+	 *
533
+	 * @param string  $name
534
+	 * @param boolean $require_construction_to_be_finalized most client code should
535
+	 *                                                      leave this as TRUE so that the inputs will be properly
536
+	 *                                                      configured. However, some client code may be ok with
537
+	 *                                                      construction finalize being called later
538
+	 *                                                      (realizing that the subsections' html names might not be
539
+	 *                                                      set yet, etc.)
540
+	 * @return EE_Form_Section_Proper
541
+	 * @throws EE_Error
542
+	 */
543
+	public function get_proper_subsection($name, $require_construction_to_be_finalized = true)
544
+	{
545
+		$subsection = $this->get_subsection(
546
+			$name,
547
+			$require_construction_to_be_finalized
548
+		);
549
+		if (! $subsection instanceof EE_Form_Section_Proper) {
550
+			throw new EE_Error(
551
+				sprintf(
552
+					esc_html__(
553
+						"Subsection '%'s is not an instanceof EE_Form_Section_Proper on form '%s'",
554
+						'event_espresso'
555
+					),
556
+					$name,
557
+					get_class($this)
558
+				)
559
+			);
560
+		}
561
+		return $subsection;
562
+	}
563
+
564
+
565
+	/**
566
+	 * Gets the value of the specified input. Should be called after receive_form_submission()
567
+	 * or populate_defaults() on the form, where the normalized value on the input is set.
568
+	 *
569
+	 * @param string $name
570
+	 * @return mixed depending on the input's type and its normalization strategy
571
+	 * @throws EE_Error
572
+	 */
573
+	public function get_input_value($name)
574
+	{
575
+		$input = $this->get_input($name);
576
+		return $input->normalized_value();
577
+	}
578
+
579
+
580
+	/**
581
+	 * Checks if this form section itself is valid, and then checks its subsections
582
+	 *
583
+	 * @throws EE_Error
584
+	 * @return boolean
585
+	 */
586
+	public function is_valid()
587
+	{
588
+		if ($this->is_valid === null) {
589
+			if (! $this->has_received_submission()) {
590
+				throw new EE_Error(
591
+					sprintf(
592
+						esc_html__(
593
+							'You cannot check if a form is valid before receiving the form submission using receive_form_submission',
594
+							'event_espresso'
595
+						)
596
+					)
597
+				);
598
+			}
599
+			if (! parent::is_valid()) {
600
+				$this->is_valid = false;
601
+			} else {
602
+				// ok so no general errors to this entire form section.
603
+				// so let's check the subsections, but only set errors if that hasn't been done yet
604
+				$this->is_valid = true;
605
+				foreach ($this->get_validatable_subsections() as $subsection) {
606
+					if (! $subsection->is_valid()) {
607
+						$this->is_valid = false;
608
+					}
609
+				}
610
+			}
611
+		}
612
+		return $this->is_valid;
613
+	}
614
+
615
+
616
+	/**
617
+	 * gets the default name of this form section if none is specified
618
+	 *
619
+	 * @return void
620
+	 */
621
+	protected function _set_default_name_if_empty()
622
+	{
623
+		if (! $this->_name) {
624
+			$classname    = get_class($this);
625
+			$default_name = str_replace('EE_', '', $classname);
626
+			$this->_name  = $default_name;
627
+		}
628
+	}
629
+
630
+
631
+	/**
632
+	 * Returns the HTML for the form, except for the form opening and closing tags
633
+	 * (as the form section doesn't know where you necessarily want to send the information to),
634
+	 * and except for a submit button. Enqueues JS and CSS; if called early enough we will
635
+	 * try to enqueue them in the header, otherwise they'll be enqueued in the footer.
636
+	 * Not doing_it_wrong because theoretically this CAN be used properly,
637
+	 * provided its used during "wp_enqueue_scripts", or it doesn't need to enqueue
638
+	 * any CSS.
639
+	 *
640
+	 * @throws InvalidArgumentException
641
+	 * @throws InvalidInterfaceException
642
+	 * @throws InvalidDataTypeException
643
+	 * @throws EE_Error
644
+	 */
645
+	public function get_html_and_js()
646
+	{
647
+		$this->enqueue_js();
648
+		return $this->get_html();
649
+	}
650
+
651
+
652
+	/**
653
+	 * returns HTML for displaying this form section. recursively calls display_section() on all subsections
654
+	 *
655
+	 * @param bool $display_previously_submitted_data
656
+	 * @return string
657
+	 * @throws InvalidArgumentException
658
+	 * @throws InvalidInterfaceException
659
+	 * @throws InvalidDataTypeException
660
+	 * @throws EE_Error
661
+	 * @throws EE_Error
662
+	 * @throws EE_Error
663
+	 */
664
+	public function get_html($display_previously_submitted_data = true)
665
+	{
666
+		$this->ensure_construct_finalized_called();
667
+		if ($display_previously_submitted_data) {
668
+			$this->populate_from_session();
669
+		}
670
+		return $this->_form_html_filter
671
+			? $this->_form_html_filter->filterHtml($this->_layout_strategy->layout_form(), $this)
672
+			: $this->_layout_strategy->layout_form();
673
+	}
674
+
675
+
676
+	/**
677
+	 * enqueues JS and CSS for the form.
678
+	 * It is preferred to call this before wp_enqueue_scripts so the
679
+	 * scripts and styles can be put in the header, but if called later
680
+	 * they will be put in the footer (which is OK for JS, but in HTML4 CSS should
681
+	 * only be in the header; but in HTML5 its ok in the body.
682
+	 * See http://stackoverflow.com/questions/4957446/load-external-css-file-in-body-tag.
683
+	 * So if your form enqueues CSS, it's preferred to call this before wp_enqueue_scripts.)
684
+	 *
685
+	 * @return void
686
+	 * @throws EE_Error
687
+	 */
688
+	public function enqueue_js()
689
+	{
690
+		$this->_enqueue_and_localize_form_js();
691
+		foreach ($this->subsections() as $subsection) {
692
+			$subsection->enqueue_js();
693
+		}
694
+	}
695
+
696
+
697
+	/**
698
+	 * adds a filter so that jquery validate gets enqueued in EE_System::wp_enqueue_scripts().
699
+	 * This must be done BEFORE wp_enqueue_scripts() gets called, which is on
700
+	 * the wp_enqueue_scripts hook.
701
+	 * However, registering the form js and localizing it can happen when we
702
+	 * actually output the form (which is preferred, seeing how teh form's fields
703
+	 * could change until it's actually outputted)
704
+	 *
705
+	 * @param boolean $init_form_validation_automatically whether or not we want the form validation
706
+	 *                                                    to be triggered automatically or not
707
+	 * @return void
708
+	 */
709
+	public static function wp_enqueue_scripts($init_form_validation_automatically = true)
710
+	{
711
+		wp_register_script(
712
+			'ee_form_section_validation',
713
+			EE_GLOBAL_ASSETS_URL . 'scripts' . '/form_section_validation.js',
714
+			array('jquery-validate', 'jquery-ui-datepicker', 'jquery-validate-extra-methods'),
715
+			EVENT_ESPRESSO_VERSION,
716
+			true
717
+		);
718
+		wp_localize_script(
719
+			'ee_form_section_validation',
720
+			'ee_form_section_validation_init',
721
+			array('init' => $init_form_validation_automatically ? '1' : '0')
722
+		);
723
+	}
724
+
725
+
726
+	/**
727
+	 * gets the variables used by form_section_validation.js.
728
+	 * This needs to be called AFTER we've called $this->_enqueue_jquery_validate_script,
729
+	 * but before the wordpress hook wp_loaded
730
+	 *
731
+	 * @throws EE_Error
732
+	 */
733
+	public function _enqueue_and_localize_form_js()
734
+	{
735
+		$this->ensure_construct_finalized_called();
736
+		// actually, we don't want to localize just yet. There may be other forms on the page.
737
+		// so we need to add our form section data to a static variable accessible by all form sections
738
+		// and localize it just before the footer
739
+		$this->localize_validation_rules();
740
+		add_action('wp_footer', array('EE_Form_Section_Proper', 'localize_script_for_all_forms'), 2);
741
+		add_action('admin_footer', array('EE_Form_Section_Proper', 'localize_script_for_all_forms'));
742
+	}
743
+
744
+
745
+	/**
746
+	 * add our form section data to a static variable accessible by all form sections
747
+	 *
748
+	 * @param bool $return_for_subsection
749
+	 * @return void
750
+	 * @throws EE_Error
751
+	 */
752
+	public function localize_validation_rules($return_for_subsection = false)
753
+	{
754
+		// we only want to localize vars ONCE for the entire form,
755
+		// so if the form section doesn't have a parent, then it must be the top dog
756
+		if ($return_for_subsection || ! $this->parent_section()) {
757
+			EE_Form_Section_Proper::$_js_localization['form_data'][ $this->html_id() ] = array(
758
+				'form_section_id'  => $this->html_id(true),
759
+				'validation_rules' => $this->get_jquery_validation_rules(),
760
+				'other_data'       => $this->get_other_js_data(),
761
+				'errors'           => $this->subsection_validation_errors_by_html_name(),
762
+			);
763
+			EE_Form_Section_Proper::$_scripts_localized                                = true;
764
+		}
765
+	}
766
+
767
+
768
+	/**
769
+	 * Gets an array of extra data that will be useful for client-side javascript.
770
+	 * This is primarily data added by inputs and forms in addition to any
771
+	 * scripts they might enqueue
772
+	 *
773
+	 * @param array $form_other_js_data
774
+	 * @return array
775
+	 * @throws EE_Error
776
+	 */
777
+	public function get_other_js_data($form_other_js_data = array())
778
+	{
779
+		foreach ($this->subsections() as $subsection) {
780
+			$form_other_js_data = $subsection->get_other_js_data($form_other_js_data);
781
+		}
782
+		return $form_other_js_data;
783
+	}
784
+
785
+
786
+	/**
787
+	 * Gets a flat array of inputs for this form section and its subsections.
788
+	 * Keys are their form names, and values are the inputs themselves
789
+	 *
790
+	 * @return EE_Form_Input_Base
791
+	 * @throws EE_Error
792
+	 */
793
+	public function inputs_in_subsections()
794
+	{
795
+		$inputs = array();
796
+		foreach ($this->subsections() as $subsection) {
797
+			if ($subsection instanceof EE_Form_Input_Base) {
798
+				$inputs[ $subsection->html_name() ] = $subsection;
799
+			} elseif ($subsection instanceof EE_Form_Section_Proper) {
800
+				$inputs += $subsection->inputs_in_subsections();
801
+			}
802
+		}
803
+		return $inputs;
804
+	}
805
+
806
+
807
+	/**
808
+	 * Gets a flat array of all the validation errors.
809
+	 * Keys are html names (because those should be unique)
810
+	 * and values are a string of all their validation errors
811
+	 *
812
+	 * @return string[]
813
+	 * @throws EE_Error
814
+	 */
815
+	public function subsection_validation_errors_by_html_name()
816
+	{
817
+		$inputs = $this->inputs();
818
+		$errors = array();
819
+		foreach ($inputs as $form_input) {
820
+			if ($form_input instanceof EE_Form_Input_Base && $form_input->get_validation_errors()) {
821
+				$errors[ $form_input->html_name() ] = $form_input->get_validation_error_string();
822
+			}
823
+		}
824
+		return $errors;
825
+	}
826
+
827
+
828
+	/**
829
+	 * passes all the form data required by the JS to the JS, and enqueues the few required JS files.
830
+	 * Should be setup by each form during the _enqueues_and_localize_form_js
831
+	 *
832
+	 * @throws InvalidArgumentException
833
+	 * @throws InvalidInterfaceException
834
+	 * @throws InvalidDataTypeException
835
+	 */
836
+	public static function localize_script_for_all_forms()
837
+	{
838
+		// allow inputs and stuff to hook in their JS and stuff here
839
+		do_action('AHEE__EE_Form_Section_Proper__localize_script_for_all_forms__begin');
840
+		EE_Form_Section_Proper::$_js_localization['localized_error_messages'] = EE_Form_Section_Proper::_get_localized_error_messages();
841
+		$email_validation_level = isset(EE_Registry::instance()->CFG->registration->email_validation_level)
842
+			? EE_Registry::instance()->CFG->registration->email_validation_level
843
+			: 'wp_default';
844
+		EE_Form_Section_Proper::$_js_localization['email_validation_level']   = $email_validation_level;
845
+		wp_enqueue_script('ee_form_section_validation');
846
+		wp_localize_script(
847
+			'ee_form_section_validation',
848
+			'ee_form_section_vars',
849
+			EE_Form_Section_Proper::$_js_localization
850
+		);
851
+	}
852
+
853
+
854
+	/**
855
+	 * ensure_scripts_localized
856
+	 *
857
+	 * @throws EE_Error
858
+	 */
859
+	public function ensure_scripts_localized()
860
+	{
861
+		if (! EE_Form_Section_Proper::$_scripts_localized) {
862
+			$this->_enqueue_and_localize_form_js();
863
+		}
864
+	}
865
+
866
+
867
+	/**
868
+	 * Gets the hard-coded validation error messages to be used in the JS. The convention
869
+	 * is that the key here should be the same as the custom validation rule put in the JS file
870
+	 *
871
+	 * @return array keys are custom validation rules, and values are internationalized strings
872
+	 */
873
+	private static function _get_localized_error_messages()
874
+	{
875
+		return array(
876
+			'validUrl' => esc_html__('This is not a valid absolute URL. Eg, http://domain.com/monkey.jpg', 'event_espresso'),
877
+			'regex'    => esc_html__('Please check your input', 'event_espresso'),
878
+		);
879
+	}
880
+
881
+
882
+	/**
883
+	 * @return array
884
+	 */
885
+	public static function js_localization()
886
+	{
887
+		return self::$_js_localization;
888
+	}
889
+
890
+
891
+	/**
892
+	 * @return void
893
+	 */
894
+	public static function reset_js_localization()
895
+	{
896
+		self::$_js_localization = array();
897
+	}
898
+
899
+
900
+	/**
901
+	 * Gets the JS to put inside the jquery validation rules for subsection of this form section.
902
+	 * See parent function for more...
903
+	 *
904
+	 * @return array
905
+	 * @throws EE_Error
906
+	 */
907
+	public function get_jquery_validation_rules()
908
+	{
909
+		$jquery_validation_rules = array();
910
+		foreach ($this->get_validatable_subsections() as $subsection) {
911
+			$jquery_validation_rules = array_merge(
912
+				$jquery_validation_rules,
913
+				$subsection->get_jquery_validation_rules()
914
+			);
915
+		}
916
+		return $jquery_validation_rules;
917
+	}
918
+
919
+
920
+	/**
921
+	 * Sanitizes all the data and sets the sanitized value of each field
922
+	 *
923
+	 * @param array $req_data like $_POST
924
+	 * @return void
925
+	 * @throws EE_Error
926
+	 */
927
+	protected function _normalize($req_data)
928
+	{
929
+		$this->_received_submission = true;
930
+		$this->_validation_errors   = array();
931
+		foreach ($this->get_validatable_subsections() as $subsection) {
932
+			try {
933
+				$subsection->_normalize($req_data);
934
+			} catch (EE_Validation_Error $e) {
935
+				$subsection->add_validation_error($e);
936
+			}
937
+		}
938
+	}
939
+
940
+
941
+	/**
942
+	 * Performs validation on this form section and its subsections.
943
+	 * For each subsection,
944
+	 * calls _validate_{subsection_name} on THIS form (if the function exists)
945
+	 * and passes it the subsection, then calls _validate on that subsection.
946
+	 * If you need to perform validation on the form as a whole (considering multiple)
947
+	 * you would be best to override this _validate method,
948
+	 * calling parent::_validate() first.
949
+	 *
950
+	 * @throws EE_Error
951
+	 */
952
+	protected function _validate()
953
+	{
954
+		// reset the cache of whether this form is valid or not- we're re-validating it now
955
+		$this->is_valid = null;
956
+		foreach ($this->get_validatable_subsections() as $subsection_name => $subsection) {
957
+			if (method_exists($this, '_validate_' . $subsection_name)) {
958
+				call_user_func_array(array($this, '_validate_' . $subsection_name), array($subsection));
959
+			}
960
+			$subsection->_validate();
961
+		}
962
+	}
963
+
964
+
965
+	/**
966
+	 * Gets all the validated inputs for the form section
967
+	 *
968
+	 * @return array
969
+	 * @throws EE_Error
970
+	 */
971
+	public function valid_data()
972
+	{
973
+		$inputs = array();
974
+		foreach ($this->subsections() as $subsection_name => $subsection) {
975
+			if ($subsection instanceof EE_Form_Section_Proper) {
976
+				$inputs[ $subsection_name ] = $subsection->valid_data();
977
+			} elseif ($subsection instanceof EE_Form_Input_Base) {
978
+				$inputs[ $subsection_name ] = $subsection->normalized_value();
979
+			}
980
+		}
981
+		return $inputs;
982
+	}
983
+
984
+
985
+	/**
986
+	 * Gets all the inputs on this form section
987
+	 *
988
+	 * @return EE_Form_Input_Base[]
989
+	 * @throws EE_Error
990
+	 */
991
+	public function inputs()
992
+	{
993
+		$inputs = array();
994
+		foreach ($this->subsections() as $subsection_name => $subsection) {
995
+			if ($subsection instanceof EE_Form_Input_Base) {
996
+				$inputs[ $subsection_name ] = $subsection;
997
+			}
998
+		}
999
+		return $inputs;
1000
+	}
1001
+
1002
+
1003
+	/**
1004
+	 * Gets all the subsections which are a proper form
1005
+	 *
1006
+	 * @return EE_Form_Section_Proper[]
1007
+	 * @throws EE_Error
1008
+	 */
1009
+	public function subforms()
1010
+	{
1011
+		$form_sections = array();
1012
+		foreach ($this->subsections() as $name => $obj) {
1013
+			if ($obj instanceof EE_Form_Section_Proper) {
1014
+				$form_sections[ $name ] = $obj;
1015
+			}
1016
+		}
1017
+		return $form_sections;
1018
+	}
1019
+
1020
+
1021
+	/**
1022
+	 * Gets all the subsections (inputs, proper subsections, or html-only sections).
1023
+	 * Consider using inputs() or subforms()
1024
+	 * if you only want form inputs or proper form sections.
1025
+	 *
1026
+	 * @param boolean $require_construction_to_be_finalized most client code should
1027
+	 *                                                      leave this as TRUE so that the inputs will be properly
1028
+	 *                                                      configured. However, some client code may be ok with
1029
+	 *                                                      construction finalize being called later
1030
+	 *                                                      (realizing that the subsections' html names might not be
1031
+	 *                                                      set yet, etc.)
1032
+	 * @return EE_Form_Section_Proper[]
1033
+	 * @throws EE_Error
1034
+	 */
1035
+	public function subsections($require_construction_to_be_finalized = true)
1036
+	{
1037
+		if ($require_construction_to_be_finalized) {
1038
+			$this->ensure_construct_finalized_called();
1039
+		}
1040
+		return $this->_subsections;
1041
+	}
1042
+
1043
+
1044
+	/**
1045
+	 * Returns whether this form has any subforms or inputs
1046
+	 * @return bool
1047
+	 */
1048
+	public function hasSubsections()
1049
+	{
1050
+		return ! empty($this->_subsections);
1051
+	}
1052
+
1053
+
1054
+	/**
1055
+	 * Returns a simple array where keys are input names, and values are their normalized
1056
+	 * values. (Similar to calling get_input_value on inputs)
1057
+	 *
1058
+	 * @param boolean $include_subform_inputs Whether to include inputs from subforms,
1059
+	 *                                        or just this forms' direct children inputs
1060
+	 * @param boolean $flatten                Whether to force the results into 1-dimensional array,
1061
+	 *                                        or allow multidimensional array
1062
+	 * @return array if $flatten is TRUE it will always be a 1-dimensional array
1063
+	 *                                        with array keys being input names
1064
+	 *                                        (regardless of whether they are from a subsection or not),
1065
+	 *                                        and if $flatten is FALSE it can be a multidimensional array
1066
+	 *                                        where keys are always subsection names and values are either
1067
+	 *                                        the input's normalized value, or an array like the top-level array
1068
+	 * @throws EE_Error
1069
+	 */
1070
+	public function input_values($include_subform_inputs = false, $flatten = false)
1071
+	{
1072
+		return $this->_input_values(false, $include_subform_inputs, $flatten);
1073
+	}
1074
+
1075
+
1076
+	/**
1077
+	 * Similar to EE_Form_Section_Proper::input_values(), except this returns the 'display_value'
1078
+	 * of each input. On some inputs (especially radio boxes or checkboxes), the value stored
1079
+	 * is not necessarily the value we want to display to users. This creates an array
1080
+	 * where keys are the input names, and values are their display values
1081
+	 *
1082
+	 * @param boolean $include_subform_inputs Whether to include inputs from subforms,
1083
+	 *                                        or just this forms' direct children inputs
1084
+	 * @param boolean $flatten                Whether to force the results into 1-dimensional array,
1085
+	 *                                        or allow multidimensional array
1086
+	 * @return array if $flatten is TRUE it will always be a 1-dimensional array
1087
+	 *                                        with array keys being input names
1088
+	 *                                        (regardless of whether they are from a subsection or not),
1089
+	 *                                        and if $flatten is FALSE it can be a multidimensional array
1090
+	 *                                        where keys are always subsection names and values are either
1091
+	 *                                        the input's normalized value, or an array like the top-level array
1092
+	 * @throws EE_Error
1093
+	 */
1094
+	public function input_pretty_values($include_subform_inputs = false, $flatten = false)
1095
+	{
1096
+		return $this->_input_values(true, $include_subform_inputs, $flatten);
1097
+	}
1098
+
1099
+
1100
+	/**
1101
+	 * Gets the input values from the form
1102
+	 *
1103
+	 * @param boolean $pretty                 Whether to retrieve the pretty value,
1104
+	 *                                        or just the normalized value
1105
+	 * @param boolean $include_subform_inputs Whether to include inputs from subforms,
1106
+	 *                                        or just this forms' direct children inputs
1107
+	 * @param boolean $flatten                Whether to force the results into 1-dimensional array,
1108
+	 *                                        or allow multidimensional array
1109
+	 * @return array if $flatten is TRUE it will always be a 1-dimensional array with array keys being
1110
+	 *                                        input names (regardless of whether they are from a subsection or not),
1111
+	 *                                        and if $flatten is FALSE it can be a multidimensional array
1112
+	 *                                        where keys are always subsection names and values are either
1113
+	 *                                        the input's normalized value, or an array like the top-level array
1114
+	 * @throws EE_Error
1115
+	 */
1116
+	public function _input_values($pretty = false, $include_subform_inputs = false, $flatten = false)
1117
+	{
1118
+		$input_values = array();
1119
+		foreach ($this->subsections() as $subsection_name => $subsection) {
1120
+			if ($subsection instanceof EE_Form_Input_Base) {
1121
+				$input_values[ $subsection_name ] = $pretty
1122
+					? $subsection->pretty_value()
1123
+					: $subsection->normalized_value();
1124
+			} elseif ($subsection instanceof EE_Form_Section_Proper && $include_subform_inputs) {
1125
+				$subform_input_values = $subsection->_input_values(
1126
+					$pretty,
1127
+					$include_subform_inputs,
1128
+					$flatten
1129
+				);
1130
+				if ($flatten) {
1131
+					$input_values = array_merge($input_values, $subform_input_values);
1132
+				} else {
1133
+					$input_values[ $subsection_name ] = $subform_input_values;
1134
+				}
1135
+			}
1136
+		}
1137
+		return $input_values;
1138
+	}
1139
+
1140
+
1141
+	/**
1142
+	 * Gets the originally submitted input values from the form
1143
+	 *
1144
+	 * @param boolean $include_subforms  Whether to include inputs from subforms,
1145
+	 *                                   or just this forms' direct children inputs
1146
+	 * @return array                     if $flatten is TRUE it will always be a 1-dimensional array
1147
+	 *                                   with array keys being input names
1148
+	 *                                   (regardless of whether they are from a subsection or not),
1149
+	 *                                   and if $flatten is FALSE it can be a multidimensional array
1150
+	 *                                   where keys are always subsection names and values are either
1151
+	 *                                   the input's normalized value, or an array like the top-level array
1152
+	 * @throws EE_Error
1153
+	 */
1154
+	public function submitted_values($include_subforms = false)
1155
+	{
1156
+		$submitted_values = array();
1157
+		foreach ($this->subsections() as $subsection) {
1158
+			if ($subsection instanceof EE_Form_Input_Base) {
1159
+				// is this input part of an array of inputs?
1160
+				if (strpos($subsection->html_name(), '[') !== false) {
1161
+					$full_input_name  = EEH_Array::convert_array_values_to_keys(
1162
+						explode(
1163
+							'[',
1164
+							str_replace(']', '', $subsection->html_name())
1165
+						),
1166
+						$subsection->raw_value()
1167
+					);
1168
+					$submitted_values = array_replace_recursive($submitted_values, $full_input_name);
1169
+				} else {
1170
+					$submitted_values[ $subsection->html_name() ] = $subsection->raw_value();
1171
+				}
1172
+			} elseif ($subsection instanceof EE_Form_Section_Proper && $include_subforms) {
1173
+				$subform_input_values = $subsection->submitted_values($include_subforms);
1174
+				$submitted_values     = array_replace_recursive($submitted_values, $subform_input_values);
1175
+			}
1176
+		}
1177
+		return $submitted_values;
1178
+	}
1179
+
1180
+
1181
+	/**
1182
+	 * Indicates whether or not this form has received a submission yet
1183
+	 * (ie, had receive_form_submission called on it yet)
1184
+	 *
1185
+	 * @return boolean
1186
+	 * @throws EE_Error
1187
+	 */
1188
+	public function has_received_submission()
1189
+	{
1190
+		$this->ensure_construct_finalized_called();
1191
+		return $this->_received_submission;
1192
+	}
1193
+
1194
+
1195
+	/**
1196
+	 * Equivalent to passing 'exclude' in the constructor's options array.
1197
+	 * Removes the listed inputs from the form
1198
+	 *
1199
+	 * @param array $inputs_to_exclude values are the input names
1200
+	 * @return void
1201
+	 */
1202
+	public function exclude(array $inputs_to_exclude = array())
1203
+	{
1204
+		foreach ($inputs_to_exclude as $input_to_exclude_name) {
1205
+			unset($this->_subsections[ $input_to_exclude_name ]);
1206
+		}
1207
+	}
1208
+
1209
+
1210
+	/**
1211
+	 * Changes these inputs' display strategy to be EE_Hidden_Display_Strategy.
1212
+	 * @param array $inputs_to_hide
1213
+	 * @throws EE_Error
1214
+	 */
1215
+	public function hide(array $inputs_to_hide = array())
1216
+	{
1217
+		foreach ($inputs_to_hide as $input_to_hide) {
1218
+			$input = $this->get_input($input_to_hide);
1219
+			$input->set_display_strategy(new EE_Hidden_Display_Strategy());
1220
+		}
1221
+	}
1222
+
1223
+
1224
+	/**
1225
+	 * add_subsections
1226
+	 * Adds the listed subsections to the form section.
1227
+	 * If $subsection_name_to_target is provided,
1228
+	 * then new subsections are added before or after that subsection,
1229
+	 * otherwise to the start or end of the entire subsections array.
1230
+	 *
1231
+	 * @param EE_Form_Section_Base[] $new_subsections           array of new form subsections
1232
+	 *                                                          where keys are their names
1233
+	 * @param string                 $subsection_name_to_target an existing for section that $new_subsections
1234
+	 *                                                          should be added before or after
1235
+	 *                                                          IF $subsection_name_to_target is null,
1236
+	 *                                                          then $new_subsections will be added to
1237
+	 *                                                          the beginning or end of the entire subsections array
1238
+	 * @param boolean                $add_before                whether to add $new_subsections, before or after
1239
+	 *                                                          $subsection_name_to_target,
1240
+	 *                                                          or if $subsection_name_to_target is null,
1241
+	 *                                                          before or after entire subsections array
1242
+	 * @return void
1243
+	 * @throws EE_Error
1244
+	 */
1245
+	public function add_subsections($new_subsections, $subsection_name_to_target = null, $add_before = true)
1246
+	{
1247
+		foreach ($new_subsections as $subsection_name => $subsection) {
1248
+			if (! $subsection instanceof EE_Form_Section_Base) {
1249
+				EE_Error::add_error(
1250
+					sprintf(
1251
+						esc_html__(
1252
+							"Trying to add a %s as a subsection (it was named '%s') to the form section '%s'. It was removed.",
1253
+							'event_espresso'
1254
+						),
1255
+						get_class($subsection),
1256
+						$subsection_name,
1257
+						$this->name()
1258
+					)
1259
+				);
1260
+				unset($new_subsections[ $subsection_name ]);
1261
+			}
1262
+		}
1263
+		$this->_subsections = EEH_Array::insert_into_array(
1264
+			$this->_subsections,
1265
+			$new_subsections,
1266
+			$subsection_name_to_target,
1267
+			$add_before
1268
+		);
1269
+		if ($this->_construction_finalized) {
1270
+			foreach ($this->_subsections as $name => $subsection) {
1271
+				$subsection->_construct_finalize($this, $name);
1272
+			}
1273
+		}
1274
+	}
1275
+
1276
+
1277
+	/**
1278
+	 * @param string $subsection_name
1279
+	 * @param bool   $recursive
1280
+	 * @return bool
1281
+	 */
1282
+	public function has_subsection($subsection_name, $recursive = false)
1283
+	{
1284
+		foreach ($this->_subsections as $name => $subsection) {
1285
+			if ($name === $subsection_name
1286
+				|| (
1287
+					$recursive
1288
+					&& $subsection instanceof EE_Form_Section_Proper
1289
+					&& $subsection->has_subsection($subsection_name, $recursive)
1290
+				)
1291
+			) {
1292
+				return true;
1293
+			}
1294
+		}
1295
+		return false;
1296
+	}
1297
+
1298
+
1299
+
1300
+	/**
1301
+	 * Just gets all validatable subsections to clean their sensitive data
1302
+	 *
1303
+	 * @throws EE_Error
1304
+	 */
1305
+	public function clean_sensitive_data()
1306
+	{
1307
+		foreach ($this->get_validatable_subsections() as $subsection) {
1308
+			$subsection->clean_sensitive_data();
1309
+		}
1310
+	}
1311
+
1312
+
1313
+	/**
1314
+	 * Sets the submission error message (aka validation error message for this form section and all sub-sections)
1315
+	 * @param string                           $form_submission_error_message
1316
+	 * @param EE_Form_Section_Validatable $form_section unused
1317
+	 * @throws EE_Error
1318
+	 */
1319
+	public function set_submission_error_message(
1320
+		$form_submission_error_message = ''
1321
+	) {
1322
+		$this->_form_submission_error_message = ! empty($form_submission_error_message)
1323
+			? $form_submission_error_message
1324
+			: $this->getAllValidationErrorsString();
1325
+	}
1326
+
1327
+
1328
+	/**
1329
+	 * Returns the cached error message. A default value is set for this during _validate(),
1330
+	 * (called during receive_form_submission) but it can be explicitly set using
1331
+	 * set_submission_error_message
1332
+	 *
1333
+	 * @return string
1334
+	 */
1335
+	public function submission_error_message()
1336
+	{
1337
+		return $this->_form_submission_error_message;
1338
+	}
1339
+
1340
+
1341
+	/**
1342
+	 * Sets a message to display if the data submitted to the form was valid.
1343
+	 * @param string $form_submission_success_message
1344
+	 */
1345
+	public function set_submission_success_message($form_submission_success_message = '')
1346
+	{
1347
+		$this->_form_submission_success_message = ! empty($form_submission_success_message)
1348
+			? $form_submission_success_message
1349
+			: esc_html__('Form submitted successfully', 'event_espresso');
1350
+	}
1351
+
1352
+
1353
+	/**
1354
+	 * Gets a message appropriate for display when the form is correctly submitted
1355
+	 * @return string
1356
+	 */
1357
+	public function submission_success_message()
1358
+	{
1359
+		return $this->_form_submission_success_message;
1360
+	}
1361
+
1362
+
1363
+	/**
1364
+	 * Returns the prefix that should be used on child of this form section for
1365
+	 * their html names. If this form section itself has a parent, prepends ITS
1366
+	 * prefix onto this form section's prefix. Used primarily by
1367
+	 * EE_Form_Input_Base::_set_default_html_name_if_empty
1368
+	 *
1369
+	 * @return string
1370
+	 * @throws EE_Error
1371
+	 */
1372
+	public function html_name_prefix()
1373
+	{
1374
+		if ($this->parent_section() instanceof EE_Form_Section_Proper) {
1375
+			return $this->parent_section()->html_name_prefix() . '[' . $this->name() . ']';
1376
+		}
1377
+		return $this->name();
1378
+	}
1379
+
1380
+
1381
+	/**
1382
+	 * Gets the name, but first checks _construct_finalize has been called. If not,
1383
+	 * calls it (assumes there is no parent and that we want the name to be whatever
1384
+	 * was set, which is probably nothing, or the classname)
1385
+	 *
1386
+	 * @return string
1387
+	 * @throws EE_Error
1388
+	 */
1389
+	public function name()
1390
+	{
1391
+		$this->ensure_construct_finalized_called();
1392
+		return parent::name();
1393
+	}
1394
+
1395
+
1396
+	/**
1397
+	 * @return EE_Form_Section_Proper
1398
+	 * @throws EE_Error
1399
+	 */
1400
+	public function parent_section()
1401
+	{
1402
+		$this->ensure_construct_finalized_called();
1403
+		return parent::parent_section();
1404
+	}
1405
+
1406
+
1407
+	/**
1408
+	 * make sure construction finalized was called, otherwise children might not be ready
1409
+	 *
1410
+	 * @return void
1411
+	 * @throws EE_Error
1412
+	 */
1413
+	public function ensure_construct_finalized_called()
1414
+	{
1415
+		if (! $this->_construction_finalized) {
1416
+			$this->_construct_finalize($this->_parent_section, $this->_name);
1417
+		}
1418
+	}
1419
+
1420
+
1421
+	/**
1422
+	 * Checks if any of this form section's inputs, or any of its children's inputs,
1423
+	 * are in teh form data. If any are found, returns true. Else false
1424
+	 *
1425
+	 * @param array $req_data
1426
+	 * @return boolean
1427
+	 * @throws EE_Error
1428
+	 */
1429
+	public function form_data_present_in($req_data = null)
1430
+	{
1431
+		$req_data = $this->getCachedRequest($req_data);
1432
+		foreach ($this->subsections() as $subsection) {
1433
+			if ($subsection instanceof EE_Form_Input_Base) {
1434
+				if ($subsection->form_data_present_in($req_data)) {
1435
+					return true;
1436
+				}
1437
+			} elseif ($subsection instanceof EE_Form_Section_Proper) {
1438
+				if ($subsection->form_data_present_in($req_data)) {
1439
+					return true;
1440
+				}
1441
+			}
1442
+		}
1443
+		return false;
1444
+	}
1445
+
1446
+
1447
+	/**
1448
+	 * Gets validation errors for this form section and subsections
1449
+	 * Similar to EE_Form_Section_Validatable::get_validation_errors() except this
1450
+	 * gets the validation errors for ALL subsection
1451
+	 *
1452
+	 * @return EE_Validation_Error[]
1453
+	 * @throws EE_Error
1454
+	 */
1455
+	public function get_validation_errors_accumulated()
1456
+	{
1457
+		$validation_errors = $this->get_validation_errors();
1458
+		foreach ($this->get_validatable_subsections() as $subsection) {
1459
+			if ($subsection instanceof EE_Form_Section_Proper) {
1460
+				$validation_errors_on_this_subsection = $subsection->get_validation_errors_accumulated();
1461
+			} else {
1462
+				$validation_errors_on_this_subsection = $subsection->get_validation_errors();
1463
+			}
1464
+			if ($validation_errors_on_this_subsection) {
1465
+				$validation_errors = array_merge($validation_errors, $validation_errors_on_this_subsection);
1466
+			}
1467
+		}
1468
+		return $validation_errors;
1469
+	}
1470
+
1471
+	/**
1472
+	 * Fetch validation errors from children and grandchildren and puts them in a single string.
1473
+	 * This traverses the form section tree to generate this, but you probably want to instead use
1474
+	 * get_form_submission_error_message() which is usually this message cached (or a custom validation error message)
1475
+	 *
1476
+	 * @return string
1477
+	 * @since 4.9.59.p
1478
+	 */
1479
+	protected function getAllValidationErrorsString()
1480
+	{
1481
+		$submission_error_messages = array();
1482
+		// bad, bad, bad registrant
1483
+		foreach ($this->get_validation_errors_accumulated() as $validation_error) {
1484
+			if ($validation_error instanceof EE_Validation_Error) {
1485
+				$form_section = $validation_error->get_form_section();
1486
+				if ($form_section instanceof EE_Form_Input_Base) {
1487
+					$label = $validation_error->get_form_section()->html_label_text();
1488
+				} elseif ($form_section instanceof EE_Form_Section_Validatable) {
1489
+					$label = $validation_error->get_form_section()->name();
1490
+				} else {
1491
+					$label = esc_html__('Unknown', 'event_espresso');
1492
+				}
1493
+				$submission_error_messages[] = sprintf(
1494
+					__('%s : %s', 'event_espresso'),
1495
+					$label,
1496
+					$validation_error->getMessage()
1497
+				);
1498
+			}
1499
+		}
1500
+		return implode('<br>', $submission_error_messages);
1501
+	}
1502
+
1503
+
1504
+	/**
1505
+	 * This isn't just the name of an input, it's a path pointing to an input. The
1506
+	 * path is similar to a folder path: slash (/) means to descend into a subsection,
1507
+	 * dot-dot-slash (../) means to ascend into the parent section.
1508
+	 * After a series of slashes and dot-dot-slashes, there should be the name of an input,
1509
+	 * which will be returned.
1510
+	 * Eg, if you want the related input to be conditional on a sibling input name 'foobar'
1511
+	 * just use 'foobar'. If you want it to be conditional on an aunt/uncle input name
1512
+	 * 'baz', use '../baz'. If you want it to be conditional on a cousin input,
1513
+	 * the child of 'baz_section' named 'baz_child', use '../baz_section/baz_child'.
1514
+	 * Etc
1515
+	 *
1516
+	 * @param string|false $form_section_path we accept false also because substr( '../', '../' ) = false
1517
+	 * @return EE_Form_Section_Base
1518
+	 * @throws EE_Error
1519
+	 */
1520
+	public function find_section_from_path($form_section_path)
1521
+	{
1522
+		// check if we can find the input from purely going straight up the tree
1523
+		$input = parent::find_section_from_path($form_section_path);
1524
+		if ($input instanceof EE_Form_Section_Base) {
1525
+			return $input;
1526
+		}
1527
+		$next_slash_pos = strpos($form_section_path, '/');
1528
+		if ($next_slash_pos !== false) {
1529
+			$child_section_name = substr($form_section_path, 0, $next_slash_pos);
1530
+			$subpath            = substr($form_section_path, $next_slash_pos + 1);
1531
+		} else {
1532
+			$child_section_name = $form_section_path;
1533
+			$subpath            = '';
1534
+		}
1535
+		$child_section = $this->get_subsection($child_section_name);
1536
+		if ($child_section instanceof EE_Form_Section_Base) {
1537
+			return $child_section->find_section_from_path($subpath);
1538
+		}
1539
+		return null;
1540
+	}
1541 1541
 }
Please login to merge, or discard this patch.