Completed
Branch master (24c3eb)
by
unknown
34:49 queued 28:57
created
core/libraries/messages/EE_Messages_Generator.lib.php 1 patch
Indentation   +978 added lines, -978 removed lines patch added patch discarded remove patch
@@ -15,983 +15,983 @@
 block discarded – undo
15 15
  */
16 16
 class EE_Messages_Generator
17 17
 {
18
-    protected EE_Messages_Data_Handler_Collection $_data_handler_collection;
19
-
20
-    protected EE_Message_Template_Group_Collection $_template_collection;
21
-
22
-    /**
23
-     * This will hold the data handler for the current EE_Message being generated.
24
-     */
25
-    protected ?EE_Messages_incoming_data $_current_data_handler = null;
26
-
27
-    /**
28
-     * This holds the EE_Messages_Queue that contains the messages to generate.
29
-     */
30
-    protected EE_Messages_Queue $_generation_queue;
31
-
32
-    /**
33
-     * This holds the EE_Messages_Queue that will store the generated EE_Message objects.
34
-     */
35
-    protected EE_Messages_Queue $_ready_queue;
36
-
37
-    /**
38
-     * This is a container for any error messages that get created through the generation
39
-     * process.
40
-     */
41
-    protected array $_error_msg = [];
42
-
43
-    /**
44
-     * Flag used to set when the current EE_Message in the generation queue has been verified.
45
-     */
46
-    protected bool $_verified = false;
47
-
48
-    /**
49
-     * This will hold the current messenger object corresponding with the current EE_Message in the generation queue.
50
-     */
51
-    protected ?EE_messenger $_current_messenger = null;
52
-
53
-    /**
54
-     * This will hold the current message type object corresponding with the current EE_Message in the generation queue.
55
-     */
56
-    protected ?EE_message_type $_current_message_type = null;
57
-
58
-    protected EEH_Parse_Shortcodes $_shortcode_parser;
59
-
60
-
61
-    /**
62
-     * @param EE_Messages_Queue                    $generation_queue
63
-     * @param EE_Messages_Queue                    $ready_queue
64
-     * @param EE_Messages_Data_Handler_Collection  $data_handler_collection
65
-     * @param EE_Message_Template_Group_Collection $template_collection
66
-     * @param EEH_Parse_Shortcodes                 $shortcode_parser
67
-     */
68
-    public function __construct(
69
-        EE_Messages_Queue $generation_queue,
70
-        EE_Messages_Queue $ready_queue,
71
-        EE_Messages_Data_Handler_Collection $data_handler_collection,
72
-        EE_Message_Template_Group_Collection $template_collection,
73
-        EEH_Parse_Shortcodes $shortcode_parser
74
-    ) {
75
-        $this->_generation_queue        = $generation_queue;
76
-        $this->_ready_queue             = $ready_queue;
77
-        $this->_data_handler_collection = $data_handler_collection;
78
-        $this->_template_collection     = $template_collection;
79
-        $this->_shortcode_parser        = $shortcode_parser;
80
-    }
81
-
82
-
83
-    /**
84
-     * @return EE_Messages_Queue
85
-     */
86
-    public function generation_queue(): EE_Messages_Queue
87
-    {
88
-        return $this->_generation_queue;
89
-    }
90
-
91
-
92
-    /**
93
-     *  This iterates through the provided queue and generates the EE_Message objects.
94
-     *  When iterating through the queue, the queued item that served as the base for generating other EE_Message
95
-     *  objects gets removed and the new EE_Message objects get added to a NEW queue.  The NEW queue is then returned
96
-     *  for the caller to decide what to do with it.
97
-     *
98
-     * @param bool $save Whether to save the EE_Message objects in the new queue or just return.
99
-     * @return EE_Messages_Queue The new queue for holding generated EE_Message objects.
100
-     * @throws EE_Error
101
-     * @throws InvalidArgumentException
102
-     * @throws InvalidDataTypeException
103
-     * @throws InvalidInterfaceException
104
-     * @throws ReflectionException
105
-     */
106
-    public function generate(bool $save = true): EE_Messages_Queue
107
-    {
108
-        // iterate through the messages in the queue, generate, and add to new queue.
109
-        $this->_generation_queue->get_message_repository()->rewind();
110
-
111
-        while ($this->_generation_queue->get_message_repository()->valid()) {
112
-            // reset "current" properties
113
-            $this->_reset_current_properties();
114
-
115
-            $msg = $this->_generation_queue->get_message_repository()->current();
116
-
117
-            /**
118
-             * need to get the next object and capture it for setting manually after deletes.  The reason is that when
119
-             * an object is removed from the repo then valid for the next object will fail.
120
-             */
121
-            $this->_generation_queue->get_message_repository()->next();
122
-            $next_msg = $this->_generation_queue->get_message_repository()->valid()
123
-                ? $this->_generation_queue->get_message_repository()->current()
124
-                : null;
125
-            // restore pointer to current item
126
-            $this->_generation_queue->get_message_repository()->set_current($msg);
127
-
128
-            // skip and delete if the current $msg is NOT incomplete (queued for generation)
129
-            if ($msg->STS_ID() !== EEM_Message::status_incomplete) {
130
-                // we keep this item in the db just remove from the repo.
131
-                $this->_generation_queue->get_message_repository()->remove($msg);
132
-                // next item
133
-                $this->_generation_queue->get_message_repository()->set_current($next_msg);
134
-                continue;
135
-            }
136
-
137
-            if ($this->_verify()) {
138
-                // let's get generating!
139
-                $this->_generate();
140
-            }
141
-
142
-            // don't persist debug_only messages if the messages system is not in debug mode.
143
-            if (
144
-                $msg->STS_ID() === EEM_Message::status_debug_only
145
-                && ! EEM_Message::debug()
146
-            ) {
147
-                do_action(
148
-                    'AHEE__EE_Messages_Generator__generate__before_debug_delete',
149
-                    $msg,
150
-                    $this->_error_msg,
151
-                    $this->_current_messenger,
152
-                    $this->_current_message_type,
153
-                    $this->_current_data_handler
154
-                );
155
-                $this->_generation_queue->get_message_repository()->delete();
156
-                $this->_generation_queue->get_message_repository()->set_current($next_msg);
157
-                continue;
158
-            }
159
-
160
-            // if there are error messages then let's set the status and the error message.
161
-            if ($this->_error_msg) {
162
-                // if the status is already debug only, then let's leave it at that.
163
-                if ($msg->STS_ID() !== EEM_Message::status_debug_only) {
164
-                    $msg->set_STS_ID(EEM_Message::status_failed);
165
-                }
166
-                do_action(
167
-                    'AHEE__EE_Messages_Generator__generate__processing_failed_message',
168
-                    $msg,
169
-                    $this->_error_msg,
170
-                    $this->_current_messenger,
171
-                    $this->_current_message_type,
172
-                    $this->_current_data_handler
173
-                );
174
-                $msg->set_error_message(
175
-                    esc_html__('Message failed to generate for the following reasons: ', 'event_espresso')
176
-                    . "\n"
177
-                    . implode("\n", $this->_error_msg)
178
-                );
179
-                $msg->set_modified(time());
180
-            } else {
181
-                do_action(
182
-                    'AHEE__EE_Messages_Generator__generate__before_successful_generated_message_delete',
183
-                    $msg,
184
-                    $this->_error_msg,
185
-                    $this->_current_messenger,
186
-                    $this->_current_message_type,
187
-                    $this->_current_data_handler
188
-                );
189
-                // remove from db
190
-                $this->_generation_queue->get_message_repository()->delete();
191
-            }
192
-            // next item
193
-            $this->_generation_queue->get_message_repository()->set_current($next_msg);
194
-        }
195
-
196
-        // generation queue is ALWAYS saved to record any errors in the generation process.
197
-        $this->_generation_queue->save();
198
-
199
-        /**
200
-         * save _ready_queue if flag set.
201
-         * Note: The EE_Message objects have values set via the EE_Base_Class::set_field_or_extra_meta() method.  This
202
-         * means if a field was added that is not a valid database column.  The EE_Message was already saved to the db
203
-         * so a EE_Extra_Meta entry could be created and attached to the EE_Message.  In those cases the save flag is
204
-         * irrelevant.
205
-         */
206
-        if ($save) {
207
-            $this->_ready_queue->save();
208
-        }
209
-
210
-        // final reset of properties
211
-        $this->_reset_current_properties();
212
-
213
-        return $this->_ready_queue;
214
-    }
215
-
216
-
217
-    /**
218
-     * This resets all the properties used for holding "current" values corresponding to the current EE_Message object
219
-     * in the generation queue.
220
-     */
221
-    protected function _reset_current_properties()
222
-    {
223
-        $this->_verified = false;
224
-        // make sure any _data value in the current message type is reset
225
-        if ($this->_current_message_type instanceof EE_message_type) {
226
-            $this->_current_message_type->reset_data();
227
-        }
228
-        $this->_current_messenger = $this->_current_message_type = $this->_current_data_handler = null;
229
-    }
230
-
231
-
232
-    /**
233
-     * This proceeds with the actual generation of a message.  By the time this is called, there should already be a
234
-     * $_current_data_handler set and all incoming information should be validated for the current EE_Message in the
235
-     * _generating_queue.
236
-     *
237
-     * @return bool Whether the message was successfully generated or not.
238
-     * @throws EE_Error
239
-     * @throws InvalidArgumentException
240
-     * @throws InvalidDataTypeException
241
-     * @throws InvalidInterfaceException
242
-     * @throws ReflectionException
243
-     */
244
-    protected function _generate(): bool
245
-    {
246
-        // double check verification has run and that everything is ready to work with (saves us having to validate
247
-        // everything again).
248
-        if (! $this->_verified) {
249
-            return false; // get out because we don't have a valid setup to work with.
250
-        }
251
-
252
-
253
-        try {
254
-            $addressees = $this->_current_message_type->get_addressees(
255
-                $this->_current_data_handler,
256
-                $this->_generation_queue->get_message_repository()->current()->context()
257
-            );
258
-        } catch (EE_Error $e) {
259
-            $this->_error_msg[] = $e->getMessage();
260
-            return false;
261
-        }
262
-
263
-
264
-        // if no addressees then get out because there is nothing to generation (possible bad data).
265
-        if (! $this->_valid_addressees($addressees)) {
266
-            do_action(
267
-                'AHEE__EE_Messages_Generator___generate__invalid_addressees',
268
-                $this->_generation_queue->get_message_repository()->current(),
269
-                $addressees,
270
-                $this->_current_messenger,
271
-                $this->_current_message_type,
272
-                $this->_current_data_handler
273
-            );
274
-            $this->_generation_queue->get_message_repository()->current()->set_STS_ID(
275
-                EEM_Message::status_debug_only
276
-            );
277
-            $this->_error_msg[] = esc_html__(
278
-                'This is not a critical error but an informational notice. Unable to generate messages EE_Messages_Addressee objects.  There were no attendees prepared by the data handler. Sometimes this is because messages only get generated for certain registration statuses. For example, the ticket notice message type only goes to approved registrations.',
279
-                'event_espresso'
280
-            );
281
-            return false;
282
-        }
283
-
284
-        $message_template_group = $this->_get_message_template_group();
285
-
286
-        // in the unlikely event there is no EE_Message_Template_Group available, get out!
287
-        if (! $message_template_group instanceof EE_Message_Template_Group) {
288
-            $this->_error_msg[] = esc_html__(
289
-                'Unable to get the Message Templates for the Message being generated.  No message template group accessible.',
290
-                'event_espresso'
291
-            );
292
-            return false;
293
-        }
294
-
295
-        // get formatted templates for using to parse and setup EE_Message objects.
296
-        $templates = $this->_get_templates($message_template_group);
297
-
298
-
299
-        // setup new EE_Message objects (and add to _ready_queue)
300
-        return $this->_assemble_messages($addressees, $templates, $message_template_group);
301
-    }
302
-
303
-
304
-    /**
305
-     * Retrieves the message template group being used for generating messages.
306
-     * Note: this also utilizes the EE_Message_Template_Group_Collection to avoid having to hit the db multiple times.
307
-     *
308
-     * @return EE_Message_Template_Group|null
309
-     * @throws EE_Error
310
-     * @throws InvalidArgumentException
311
-     * @throws InvalidDataTypeException
312
-     * @throws InvalidInterfaceException
313
-     * @throws ReflectionException
314
-     */
315
-    protected function _get_message_template_group(): ?EE_Message_Template_Group
316
-    {
317
-        // first see if there is a specific message template group requested
318
-        // (current message in the queue has a specific GRP_ID)
319
-        $message_template_group = $this->_specific_message_template_group_from_queue();
320
-        if ($message_template_group instanceof EE_Message_Template_Group) {
321
-            return $message_template_group;
322
-        }
323
-
324
-        // get event_ids from the data handler so we can check to see
325
-        // if there's already a message template group for them in the collection.
326
-        $event_ids              = $this->_get_event_ids_from_current_data_handler();
327
-        $message_template_group = $this->_template_collection->get_by_key(
328
-            $this->_template_collection->getKey(
329
-                $this->_current_messenger->name,
330
-                $this->_current_message_type->name,
331
-                $event_ids
332
-            )
333
-        );
334
-
335
-        // if we have a message template group then no need to hit the database, just return it.
336
-        if ($message_template_group instanceof EE_Message_Template_Group) {
337
-            return $message_template_group;
338
-        }
339
-
340
-        // get the global group first for this messenger and message type
341
-        // to ensure there is no override set.
342
-        $global_message_template_group =
343
-            $this->_get_global_message_template_group_for_current_messenger_and_message_type();
344
-
345
-        if (
346
-            $global_message_template_group instanceof EE_Message_Template_Group
347
-            && $global_message_template_group->get('MTP_is_override')
348
-        ) {
349
-            return $global_message_template_group;
350
-        }
351
-
352
-        // if we're still here, that means there was no message template group for the events in the collection and
353
-        // the global message template group for the messenger and message type is not set for override.  So next step
354
-        // is to see if there is a common shared custom message template group for this set of events.
355
-        $message_template_group = $this->_get_shared_message_template_for_events($event_ids);
356
-        if ($message_template_group instanceof EE_Message_Template_Group) {
357
-            return $message_template_group;
358
-        }
359
-
360
-        // STILL here?  Okay that means the fallback is to just use the global message template group for this event
361
-        // set. So we'll cache the global group for this event set (so this logic doesn't have to be repeated in this
362
-        // request) and return it.
363
-        if ($global_message_template_group instanceof EE_Message_Template_Group) {
364
-            $this->_template_collection->add(
365
-                $global_message_template_group,
366
-                $event_ids
367
-            );
368
-            return $global_message_template_group;
369
-        }
370
-
371
-        // if we land here that means there's NO active message template group for this set.
372
-        // TODO this will be a good target for some optimization down the road.  Whenever there is no active message
373
-        // template group for a given event set then cache that result so we don't repeat the logic.  However, for now,
374
-        // this should likely bit hit rarely enough that it's not a significant issue.
375
-        return null;
376
-    }
377
-
378
-
379
-    /**
380
-     * This checks the current message in the queue and determines if there is a specific Message Template Group
381
-     * requested for that message.
382
-     *
383
-     * @return EE_Message_Template_Group|null
384
-     * @throws EE_Error
385
-     * @throws InvalidArgumentException
386
-     * @throws InvalidDataTypeException
387
-     * @throws InvalidInterfaceException
388
-     * @throws ReflectionException
389
-     */
390
-    protected function _specific_message_template_group_from_queue(): ?EE_Message_Template_Group
391
-    {
392
-        // is there a GRP_ID already on the EE_Message object?  If there is, then a specific template has been requested
393
-        // so let's use that.
394
-        $GRP_ID = $this->_generation_queue->get_message_repository()->current()->GRP_ID();
395
-
396
-        if ($GRP_ID) {
397
-            // attempt to retrieve from repo first
398
-            $message_template_group = $this->_template_collection->get_by_ID($GRP_ID);
399
-            if ($message_template_group instanceof EE_Message_Template_Group) {
400
-                return $message_template_group;  // got it!
401
-            }
402
-
403
-            // nope don't have it yet.  Get from DB then add to repo if its not here, then that means the current GRP_ID
404
-            // is not valid, so we'll continue on in the code assuming there's NO GRP_ID.
405
-            $message_template_group = EEM_Message_Template_Group::instance()->get_one_by_ID($GRP_ID);
406
-            if ($message_template_group instanceof EE_Message_Template_Group) {
407
-                $this->_template_collection->add($message_template_group);
408
-                return $message_template_group;
409
-            }
410
-        }
411
-        return null;
412
-    }
413
-
414
-
415
-    /**
416
-     * Returns whether the event ids passed in all share the same message template group for the current message type
417
-     * and messenger.
418
-     *
419
-     * @param array $event_ids
420
-     * @return bool true means they DO share the same message template group, false means they don't.
421
-     * @throws EE_Error
422
-     * @throws InvalidArgumentException
423
-     * @throws InvalidDataTypeException
424
-     * @throws InvalidInterfaceException
425
-     * @throws ReflectionException
426
-     */
427
-    protected function _queue_shares_same_message_template_group_for_events(array $event_ids): bool
428
-    {
429
-        foreach ($this->_current_data_handler->events as $event) {
430
-            $event_ids[ $event['ID'] ] = $event['ID'];
431
-        }
432
-        $count_of_message_template_groups = EEM_Message_Template_Group::instance()->count(
433
-            [
434
-                [
435
-                    'Event.EVT_ID'     => ['IN', $event_ids],
436
-                    'MTP_messenger'    => $this->_current_messenger->name,
437
-                    'MTP_message_type' => $this->_current_message_type->name,
438
-                ],
439
-            ],
440
-            'GRP_ID',
441
-            true
442
-        );
443
-        return $count_of_message_template_groups === 1;
444
-    }
445
-
446
-
447
-    /**
448
-     * This will get the shared message template group for events that are in the current data handler but ONLY if
449
-     * there's a single shared message template group among all the events.  Otherwise it returns null.
450
-     *
451
-     * @param array $event_ids
452
-     * @return EE_Message_Template_Group|null
453
-     * @throws EE_Error
454
-     * @throws InvalidArgumentException
455
-     * @throws InvalidDataTypeException
456
-     * @throws InvalidInterfaceException
457
-     * @throws ReflectionException
458
-     */
459
-    protected function _get_shared_message_template_for_events(array $event_ids): ?EE_Message_Template_Group
460
-    {
461
-        $message_template_group = null;
462
-        if ($this->_queue_shares_same_message_template_group_for_events($event_ids)) {
463
-            $message_template_group = EEM_Message_Template_Group::instance()->get_one(
464
-                [
465
-                    [
466
-                        'Event.EVT_ID'     => ['IN', $event_ids],
467
-                        'MTP_messenger'    => $this->_current_messenger->name,
468
-                        'MTP_message_type' => $this->_current_message_type->name,
469
-                        'MTP_is_active'    => true,
470
-                    ],
471
-                    'group_by' => 'GRP_ID',
472
-                ]
473
-            );
474
-            // store this in the collection if its valid
475
-            if ($message_template_group instanceof EE_Message_Template_Group) {
476
-                $this->_template_collection->add(
477
-                    $message_template_group,
478
-                    $event_ids
479
-                );
480
-            }
481
-        }
482
-        return $message_template_group;
483
-    }
484
-
485
-
486
-    /**
487
-     * Retrieves the global message template group for the current messenger and message type.
488
-     *
489
-     * @return EE_Message_Template_Group|null
490
-     * @throws EE_Error
491
-     * @throws InvalidArgumentException
492
-     * @throws InvalidDataTypeException
493
-     * @throws InvalidInterfaceException
494
-     * @throws ReflectionException
495
-     */
496
-    protected function _get_global_message_template_group_for_current_messenger_and_message_type(): ?EE_Message_Template_Group
497
-    {
498
-        // first check the collection (we use an array with 0 in it to represent global groups).
499
-        $global_message_template_group = $this->_template_collection->get_by_key(
500
-            $this->_template_collection->getKey(
501
-                $this->_current_messenger->name,
502
-                $this->_current_message_type->name,
503
-                [0]
504
-            )
505
-        );
506
-
507
-        // if we don't have a group lets hit the db.
508
-        if (! $global_message_template_group instanceof EE_Message_Template_Group) {
509
-            $global_message_template_group = EEM_Message_Template_Group::instance()->get_one(
510
-                [
511
-                    [
512
-                        'MTP_messenger'    => $this->_current_messenger->name,
513
-                        'MTP_message_type' => $this->_current_message_type->name,
514
-                        'MTP_is_active'    => true,
515
-                        'MTP_is_global'    => true,
516
-                    ],
517
-                ]
518
-            );
519
-            // if we have a group, add it to the collection.
520
-            if ($global_message_template_group instanceof EE_Message_Template_Group) {
521
-                $this->_template_collection->add(
522
-                    $global_message_template_group,
523
-                    [0]
524
-                );
525
-            }
526
-        }
527
-        return $global_message_template_group;
528
-    }
529
-
530
-
531
-    /**
532
-     * Returns an array of event ids for all the events within the current data handler.
533
-     *
534
-     * @return array
535
-     */
536
-    protected function _get_event_ids_from_current_data_handler(): array
537
-    {
538
-        $event_ids = [];
539
-        foreach ($this->_current_data_handler->events as $event) {
540
-            $event_ids[ $event['ID'] ] = $event['ID'];
541
-        }
542
-        return $event_ids;
543
-    }
544
-
545
-
546
-    /**
547
-     *  Retrieves formatted array of template information for each context specific to the given
548
-     *  EE_Message_Template_Group
549
-     *
550
-     * @param EE_Message_Template_Group $message_template_group
551
-     * @return array The returned array is in this structure:
552
-     *                          array(
553
-     *                          'field_name' => array(
554
-     *                          'context' => 'content'
555
-     *                          )
556
-     *                          )
557
-     * @throws EE_Error
558
-     * @throws InvalidArgumentException
559
-     * @throws InvalidDataTypeException
560
-     * @throws InvalidInterfaceException
561
-     * @throws ReflectionException
562
-     */
563
-    protected function _get_templates(EE_Message_Template_Group $message_template_group): array
564
-    {
565
-        $templates         = [];
566
-        $context_templates = $message_template_group->context_templates();
567
-        foreach ($context_templates as $context => $template_fields) {
568
-            foreach ($template_fields as $template_field => $template_obj) {
569
-                if (! $template_obj instanceof EE_Message_Template) {
570
-                    continue;
571
-                }
572
-                $templates[ $template_field ][ $context ] = $template_obj->get('MTP_content');
573
-            }
574
-        }
575
-        return $templates;
576
-    }
577
-
578
-
579
-    /**
580
-     * Assembles new fully generated EE_Message objects and adds to _ready_queue
581
-     *
582
-     * @param array                     $addressees  Array of EE_Messages_Addressee objects indexed by message type
583
-     *                                               context.
584
-     * @param array                     $templates   formatted array of templates used for parsing data.
585
-     * @param EE_Message_Template_Group $message_template_group
586
-     * @return bool true if message generation went a-ok.  false if some sort of exception occurred.  Note: The
587
-     *                                               method will attempt to generate ALL EE_Message objects and add to
588
-     *                                               the _ready_queue.  Successfully generated messages get added to the
589
-     *                                               queue with EEM_Message::status_idle, unsuccessfully generated
590
-     *                                               messages will get added to the queue as EEM_Message::status_failed.
591
-     *                                               Very rarely should "false" be returned from this method.
592
-     * @throws EE_Error
593
-     * @throws InvalidArgumentException
594
-     * @throws InvalidDataTypeException
595
-     * @throws InvalidIdentifierException
596
-     * @throws InvalidInterfaceException
597
-     * @throws ReflectionException
598
-     */
599
-    protected function _assemble_messages(
600
-        array $addressees,
601
-        array $templates,
602
-        EE_Message_Template_Group $message_template_group
603
-    ): bool {
604
-        // if templates are empty then get out because we can't generate anything.
605
-        if (! $templates) {
606
-            $this->_error_msg[] = esc_html__(
607
-                'Unable to assemble messages because there are no templates retrieved for generating the messages with',
608
-                'event_espresso'
609
-            );
610
-            return false;
611
-        }
612
-
613
-        // We use this as the counter for generated messages because don't forget we may be executing this inside of a
614
-        // generation_queue.  So _ready_queue may have generated EE_Message objects already.
615
-        $generated_count = 0;
616
-        foreach ($addressees as $context => $recipients) {
617
-            foreach ($recipients as $recipient) {
618
-                $message = $this->_setup_message_object($context, $recipient, $templates, $message_template_group);
619
-                if ($message instanceof EE_Message) {
620
-                    $this->_ready_queue->add(
621
-                        $message,
622
-                        [],
623
-                        $this->_generation_queue->get_message_repository()->is_preview(),
624
-                        $this->_generation_queue->get_message_repository()->is_test_send()
625
-                    );
626
-                    $generated_count++;
627
-                }
628
-
629
-                // if the current MSG being generated is for a test send then we'll only use ONE message in the
630
-                // generation.
631
-                if ($this->_generation_queue->get_message_repository()->is_test_send()) {
632
-                    break 2;
633
-                }
634
-            }
635
-        }
636
-
637
-        // if there are no generated messages then something else fatal went wrong.
638
-        return $generated_count > 0;
639
-    }
640
-
641
-
642
-    /**
643
-     * @param string                    $context   The context for the generated message.
644
-     * @param EE_Messages_Addressee     $recipient
645
-     * @param array                     $templates formatted array of templates used for parsing data.
646
-     * @param EE_Message_Template_Group $message_template_group
647
-     * @return bool|EE_Message
648
-     * @throws EE_Error
649
-     * @throws InvalidArgumentException
650
-     * @throws InvalidDataTypeException
651
-     * @throws InvalidInterfaceException
652
-     * @throws ReflectionException
653
-     * @throws InvalidIdentifierException
654
-     */
655
-    protected function _setup_message_object(
656
-        string $context,
657
-        EE_Messages_Addressee $recipient,
658
-        array $templates,
659
-        EE_Message_Template_Group $message_template_group
660
-    ) {
661
-        // stuff we already know
662
-        $transaction_id = $recipient->txn instanceof EE_Transaction
663
-            ? $recipient->txn->ID()
664
-            : 0;
665
-        $transaction_id = empty($transaction_id) && $this->_current_data_handler->txn instanceof EE_Transaction
666
-            ? $this->_current_data_handler->txn->ID()
667
-            : $transaction_id;
668
-        $message_fields = [
669
-            'GRP_ID'           => $message_template_group->ID(),
670
-            'TXN_ID'           => $transaction_id,
671
-            'MSG_messenger'    => $this->_current_messenger->name,
672
-            'MSG_message_type' => $this->_current_message_type->name,
673
-            'MSG_context'      => $context,
674
-        ];
675
-
676
-        // recipient id and type should be on the EE_Messages_Addressee object but if this is empty, let's try to grab
677
-        // the info from the att_obj found in the EE_Messages_Addressee object.
678
-        if (empty($recipient->recipient_id) || empty($recipient->recipient_type)) {
679
-            $message_fields['MSG_recipient_ID']   = $recipient->att_obj instanceof EE_Attendee
680
-                ? $recipient->att_obj->ID()
681
-                : 0;
682
-            $message_fields['MSG_recipient_type'] = 'Attendee';
683
-        } else {
684
-            $message_fields['MSG_recipient_ID']   = $recipient->recipient_id;
685
-            $message_fields['MSG_recipient_type'] = $recipient->recipient_type;
686
-        }
687
-        $message = EE_Message_Factory::create($message_fields);
688
-
689
-        // grab valid shortcodes for shortcode parser
690
-        $mt_shortcodes = $this->_current_message_type->get_valid_shortcodes();
691
-        $m_shortcodes  = $this->_current_messenger->get_valid_shortcodes();
692
-
693
-        // if the 'to' field is empty or the context is inactive we skip EXCEPT if we're previewing
694
-        if (
695
-            ! $this->_generation_queue->get_message_repository()->is_preview()
696
-            && (
697
-                (empty($templates['to'][ $context ]) && ! $this->_current_messenger->allow_empty_to_field())
698
-                || ! $message_template_group->is_context_active($context)
699
-            )
700
-        ) {
701
-            // we silently exit here and do NOT record a fail because the message is "turned off" by having no "to"
702
-            // field.
703
-            return false;
704
-        }
705
-        $error_msg = [];
706
-        foreach ($templates as $field => $field_context) {
707
-            $error_msg = [];
708
-            // let's setup the valid shortcodes for the incoming context.
709
-            $valid_shortcodes = $mt_shortcodes[ $context ];
710
-            // merge in valid shortcodes for the field.
711
-            $shortcodes = $m_shortcodes[ $field ] ?? $valid_shortcodes;
712
-            if (isset($field_context[ $context ])) {
713
-                // prefix field.
714
-                $column_name = 'MSG_' . $field;
715
-                try {
716
-                    $content = $this->_shortcode_parser->parse_message_template(
717
-                        $field_context[ $context ],
718
-                        $recipient,
719
-                        $shortcodes,
720
-                        $this->_current_message_type,
721
-                        $this->_current_messenger,
722
-                        $message
723
-                    );
724
-                    // the model field removes slashes when setting (usually necessary when the input is from the
725
-                    // request) but this value is from another model and has no slashes. So add them so it matches
726
-                    // what the field expected (otherwise slashes will have been stripped from this an extra time)
727
-                    $message->set_field_or_extra_meta($column_name, addslashes($content));
728
-                } catch (EE_Error $e) {
729
-                    $error_msg[] = sprintf(
730
-                    /* Translators: First place holder is message model field name.
18
+	protected EE_Messages_Data_Handler_Collection $_data_handler_collection;
19
+
20
+	protected EE_Message_Template_Group_Collection $_template_collection;
21
+
22
+	/**
23
+	 * This will hold the data handler for the current EE_Message being generated.
24
+	 */
25
+	protected ?EE_Messages_incoming_data $_current_data_handler = null;
26
+
27
+	/**
28
+	 * This holds the EE_Messages_Queue that contains the messages to generate.
29
+	 */
30
+	protected EE_Messages_Queue $_generation_queue;
31
+
32
+	/**
33
+	 * This holds the EE_Messages_Queue that will store the generated EE_Message objects.
34
+	 */
35
+	protected EE_Messages_Queue $_ready_queue;
36
+
37
+	/**
38
+	 * This is a container for any error messages that get created through the generation
39
+	 * process.
40
+	 */
41
+	protected array $_error_msg = [];
42
+
43
+	/**
44
+	 * Flag used to set when the current EE_Message in the generation queue has been verified.
45
+	 */
46
+	protected bool $_verified = false;
47
+
48
+	/**
49
+	 * This will hold the current messenger object corresponding with the current EE_Message in the generation queue.
50
+	 */
51
+	protected ?EE_messenger $_current_messenger = null;
52
+
53
+	/**
54
+	 * This will hold the current message type object corresponding with the current EE_Message in the generation queue.
55
+	 */
56
+	protected ?EE_message_type $_current_message_type = null;
57
+
58
+	protected EEH_Parse_Shortcodes $_shortcode_parser;
59
+
60
+
61
+	/**
62
+	 * @param EE_Messages_Queue                    $generation_queue
63
+	 * @param EE_Messages_Queue                    $ready_queue
64
+	 * @param EE_Messages_Data_Handler_Collection  $data_handler_collection
65
+	 * @param EE_Message_Template_Group_Collection $template_collection
66
+	 * @param EEH_Parse_Shortcodes                 $shortcode_parser
67
+	 */
68
+	public function __construct(
69
+		EE_Messages_Queue $generation_queue,
70
+		EE_Messages_Queue $ready_queue,
71
+		EE_Messages_Data_Handler_Collection $data_handler_collection,
72
+		EE_Message_Template_Group_Collection $template_collection,
73
+		EEH_Parse_Shortcodes $shortcode_parser
74
+	) {
75
+		$this->_generation_queue        = $generation_queue;
76
+		$this->_ready_queue             = $ready_queue;
77
+		$this->_data_handler_collection = $data_handler_collection;
78
+		$this->_template_collection     = $template_collection;
79
+		$this->_shortcode_parser        = $shortcode_parser;
80
+	}
81
+
82
+
83
+	/**
84
+	 * @return EE_Messages_Queue
85
+	 */
86
+	public function generation_queue(): EE_Messages_Queue
87
+	{
88
+		return $this->_generation_queue;
89
+	}
90
+
91
+
92
+	/**
93
+	 *  This iterates through the provided queue and generates the EE_Message objects.
94
+	 *  When iterating through the queue, the queued item that served as the base for generating other EE_Message
95
+	 *  objects gets removed and the new EE_Message objects get added to a NEW queue.  The NEW queue is then returned
96
+	 *  for the caller to decide what to do with it.
97
+	 *
98
+	 * @param bool $save Whether to save the EE_Message objects in the new queue or just return.
99
+	 * @return EE_Messages_Queue The new queue for holding generated EE_Message objects.
100
+	 * @throws EE_Error
101
+	 * @throws InvalidArgumentException
102
+	 * @throws InvalidDataTypeException
103
+	 * @throws InvalidInterfaceException
104
+	 * @throws ReflectionException
105
+	 */
106
+	public function generate(bool $save = true): EE_Messages_Queue
107
+	{
108
+		// iterate through the messages in the queue, generate, and add to new queue.
109
+		$this->_generation_queue->get_message_repository()->rewind();
110
+
111
+		while ($this->_generation_queue->get_message_repository()->valid()) {
112
+			// reset "current" properties
113
+			$this->_reset_current_properties();
114
+
115
+			$msg = $this->_generation_queue->get_message_repository()->current();
116
+
117
+			/**
118
+			 * need to get the next object and capture it for setting manually after deletes.  The reason is that when
119
+			 * an object is removed from the repo then valid for the next object will fail.
120
+			 */
121
+			$this->_generation_queue->get_message_repository()->next();
122
+			$next_msg = $this->_generation_queue->get_message_repository()->valid()
123
+				? $this->_generation_queue->get_message_repository()->current()
124
+				: null;
125
+			// restore pointer to current item
126
+			$this->_generation_queue->get_message_repository()->set_current($msg);
127
+
128
+			// skip and delete if the current $msg is NOT incomplete (queued for generation)
129
+			if ($msg->STS_ID() !== EEM_Message::status_incomplete) {
130
+				// we keep this item in the db just remove from the repo.
131
+				$this->_generation_queue->get_message_repository()->remove($msg);
132
+				// next item
133
+				$this->_generation_queue->get_message_repository()->set_current($next_msg);
134
+				continue;
135
+			}
136
+
137
+			if ($this->_verify()) {
138
+				// let's get generating!
139
+				$this->_generate();
140
+			}
141
+
142
+			// don't persist debug_only messages if the messages system is not in debug mode.
143
+			if (
144
+				$msg->STS_ID() === EEM_Message::status_debug_only
145
+				&& ! EEM_Message::debug()
146
+			) {
147
+				do_action(
148
+					'AHEE__EE_Messages_Generator__generate__before_debug_delete',
149
+					$msg,
150
+					$this->_error_msg,
151
+					$this->_current_messenger,
152
+					$this->_current_message_type,
153
+					$this->_current_data_handler
154
+				);
155
+				$this->_generation_queue->get_message_repository()->delete();
156
+				$this->_generation_queue->get_message_repository()->set_current($next_msg);
157
+				continue;
158
+			}
159
+
160
+			// if there are error messages then let's set the status and the error message.
161
+			if ($this->_error_msg) {
162
+				// if the status is already debug only, then let's leave it at that.
163
+				if ($msg->STS_ID() !== EEM_Message::status_debug_only) {
164
+					$msg->set_STS_ID(EEM_Message::status_failed);
165
+				}
166
+				do_action(
167
+					'AHEE__EE_Messages_Generator__generate__processing_failed_message',
168
+					$msg,
169
+					$this->_error_msg,
170
+					$this->_current_messenger,
171
+					$this->_current_message_type,
172
+					$this->_current_data_handler
173
+				);
174
+				$msg->set_error_message(
175
+					esc_html__('Message failed to generate for the following reasons: ', 'event_espresso')
176
+					. "\n"
177
+					. implode("\n", $this->_error_msg)
178
+				);
179
+				$msg->set_modified(time());
180
+			} else {
181
+				do_action(
182
+					'AHEE__EE_Messages_Generator__generate__before_successful_generated_message_delete',
183
+					$msg,
184
+					$this->_error_msg,
185
+					$this->_current_messenger,
186
+					$this->_current_message_type,
187
+					$this->_current_data_handler
188
+				);
189
+				// remove from db
190
+				$this->_generation_queue->get_message_repository()->delete();
191
+			}
192
+			// next item
193
+			$this->_generation_queue->get_message_repository()->set_current($next_msg);
194
+		}
195
+
196
+		// generation queue is ALWAYS saved to record any errors in the generation process.
197
+		$this->_generation_queue->save();
198
+
199
+		/**
200
+		 * save _ready_queue if flag set.
201
+		 * Note: The EE_Message objects have values set via the EE_Base_Class::set_field_or_extra_meta() method.  This
202
+		 * means if a field was added that is not a valid database column.  The EE_Message was already saved to the db
203
+		 * so a EE_Extra_Meta entry could be created and attached to the EE_Message.  In those cases the save flag is
204
+		 * irrelevant.
205
+		 */
206
+		if ($save) {
207
+			$this->_ready_queue->save();
208
+		}
209
+
210
+		// final reset of properties
211
+		$this->_reset_current_properties();
212
+
213
+		return $this->_ready_queue;
214
+	}
215
+
216
+
217
+	/**
218
+	 * This resets all the properties used for holding "current" values corresponding to the current EE_Message object
219
+	 * in the generation queue.
220
+	 */
221
+	protected function _reset_current_properties()
222
+	{
223
+		$this->_verified = false;
224
+		// make sure any _data value in the current message type is reset
225
+		if ($this->_current_message_type instanceof EE_message_type) {
226
+			$this->_current_message_type->reset_data();
227
+		}
228
+		$this->_current_messenger = $this->_current_message_type = $this->_current_data_handler = null;
229
+	}
230
+
231
+
232
+	/**
233
+	 * This proceeds with the actual generation of a message.  By the time this is called, there should already be a
234
+	 * $_current_data_handler set and all incoming information should be validated for the current EE_Message in the
235
+	 * _generating_queue.
236
+	 *
237
+	 * @return bool Whether the message was successfully generated or not.
238
+	 * @throws EE_Error
239
+	 * @throws InvalidArgumentException
240
+	 * @throws InvalidDataTypeException
241
+	 * @throws InvalidInterfaceException
242
+	 * @throws ReflectionException
243
+	 */
244
+	protected function _generate(): bool
245
+	{
246
+		// double check verification has run and that everything is ready to work with (saves us having to validate
247
+		// everything again).
248
+		if (! $this->_verified) {
249
+			return false; // get out because we don't have a valid setup to work with.
250
+		}
251
+
252
+
253
+		try {
254
+			$addressees = $this->_current_message_type->get_addressees(
255
+				$this->_current_data_handler,
256
+				$this->_generation_queue->get_message_repository()->current()->context()
257
+			);
258
+		} catch (EE_Error $e) {
259
+			$this->_error_msg[] = $e->getMessage();
260
+			return false;
261
+		}
262
+
263
+
264
+		// if no addressees then get out because there is nothing to generation (possible bad data).
265
+		if (! $this->_valid_addressees($addressees)) {
266
+			do_action(
267
+				'AHEE__EE_Messages_Generator___generate__invalid_addressees',
268
+				$this->_generation_queue->get_message_repository()->current(),
269
+				$addressees,
270
+				$this->_current_messenger,
271
+				$this->_current_message_type,
272
+				$this->_current_data_handler
273
+			);
274
+			$this->_generation_queue->get_message_repository()->current()->set_STS_ID(
275
+				EEM_Message::status_debug_only
276
+			);
277
+			$this->_error_msg[] = esc_html__(
278
+				'This is not a critical error but an informational notice. Unable to generate messages EE_Messages_Addressee objects.  There were no attendees prepared by the data handler. Sometimes this is because messages only get generated for certain registration statuses. For example, the ticket notice message type only goes to approved registrations.',
279
+				'event_espresso'
280
+			);
281
+			return false;
282
+		}
283
+
284
+		$message_template_group = $this->_get_message_template_group();
285
+
286
+		// in the unlikely event there is no EE_Message_Template_Group available, get out!
287
+		if (! $message_template_group instanceof EE_Message_Template_Group) {
288
+			$this->_error_msg[] = esc_html__(
289
+				'Unable to get the Message Templates for the Message being generated.  No message template group accessible.',
290
+				'event_espresso'
291
+			);
292
+			return false;
293
+		}
294
+
295
+		// get formatted templates for using to parse and setup EE_Message objects.
296
+		$templates = $this->_get_templates($message_template_group);
297
+
298
+
299
+		// setup new EE_Message objects (and add to _ready_queue)
300
+		return $this->_assemble_messages($addressees, $templates, $message_template_group);
301
+	}
302
+
303
+
304
+	/**
305
+	 * Retrieves the message template group being used for generating messages.
306
+	 * Note: this also utilizes the EE_Message_Template_Group_Collection to avoid having to hit the db multiple times.
307
+	 *
308
+	 * @return EE_Message_Template_Group|null
309
+	 * @throws EE_Error
310
+	 * @throws InvalidArgumentException
311
+	 * @throws InvalidDataTypeException
312
+	 * @throws InvalidInterfaceException
313
+	 * @throws ReflectionException
314
+	 */
315
+	protected function _get_message_template_group(): ?EE_Message_Template_Group
316
+	{
317
+		// first see if there is a specific message template group requested
318
+		// (current message in the queue has a specific GRP_ID)
319
+		$message_template_group = $this->_specific_message_template_group_from_queue();
320
+		if ($message_template_group instanceof EE_Message_Template_Group) {
321
+			return $message_template_group;
322
+		}
323
+
324
+		// get event_ids from the data handler so we can check to see
325
+		// if there's already a message template group for them in the collection.
326
+		$event_ids              = $this->_get_event_ids_from_current_data_handler();
327
+		$message_template_group = $this->_template_collection->get_by_key(
328
+			$this->_template_collection->getKey(
329
+				$this->_current_messenger->name,
330
+				$this->_current_message_type->name,
331
+				$event_ids
332
+			)
333
+		);
334
+
335
+		// if we have a message template group then no need to hit the database, just return it.
336
+		if ($message_template_group instanceof EE_Message_Template_Group) {
337
+			return $message_template_group;
338
+		}
339
+
340
+		// get the global group first for this messenger and message type
341
+		// to ensure there is no override set.
342
+		$global_message_template_group =
343
+			$this->_get_global_message_template_group_for_current_messenger_and_message_type();
344
+
345
+		if (
346
+			$global_message_template_group instanceof EE_Message_Template_Group
347
+			&& $global_message_template_group->get('MTP_is_override')
348
+		) {
349
+			return $global_message_template_group;
350
+		}
351
+
352
+		// if we're still here, that means there was no message template group for the events in the collection and
353
+		// the global message template group for the messenger and message type is not set for override.  So next step
354
+		// is to see if there is a common shared custom message template group for this set of events.
355
+		$message_template_group = $this->_get_shared_message_template_for_events($event_ids);
356
+		if ($message_template_group instanceof EE_Message_Template_Group) {
357
+			return $message_template_group;
358
+		}
359
+
360
+		// STILL here?  Okay that means the fallback is to just use the global message template group for this event
361
+		// set. So we'll cache the global group for this event set (so this logic doesn't have to be repeated in this
362
+		// request) and return it.
363
+		if ($global_message_template_group instanceof EE_Message_Template_Group) {
364
+			$this->_template_collection->add(
365
+				$global_message_template_group,
366
+				$event_ids
367
+			);
368
+			return $global_message_template_group;
369
+		}
370
+
371
+		// if we land here that means there's NO active message template group for this set.
372
+		// TODO this will be a good target for some optimization down the road.  Whenever there is no active message
373
+		// template group for a given event set then cache that result so we don't repeat the logic.  However, for now,
374
+		// this should likely bit hit rarely enough that it's not a significant issue.
375
+		return null;
376
+	}
377
+
378
+
379
+	/**
380
+	 * This checks the current message in the queue and determines if there is a specific Message Template Group
381
+	 * requested for that message.
382
+	 *
383
+	 * @return EE_Message_Template_Group|null
384
+	 * @throws EE_Error
385
+	 * @throws InvalidArgumentException
386
+	 * @throws InvalidDataTypeException
387
+	 * @throws InvalidInterfaceException
388
+	 * @throws ReflectionException
389
+	 */
390
+	protected function _specific_message_template_group_from_queue(): ?EE_Message_Template_Group
391
+	{
392
+		// is there a GRP_ID already on the EE_Message object?  If there is, then a specific template has been requested
393
+		// so let's use that.
394
+		$GRP_ID = $this->_generation_queue->get_message_repository()->current()->GRP_ID();
395
+
396
+		if ($GRP_ID) {
397
+			// attempt to retrieve from repo first
398
+			$message_template_group = $this->_template_collection->get_by_ID($GRP_ID);
399
+			if ($message_template_group instanceof EE_Message_Template_Group) {
400
+				return $message_template_group;  // got it!
401
+			}
402
+
403
+			// nope don't have it yet.  Get from DB then add to repo if its not here, then that means the current GRP_ID
404
+			// is not valid, so we'll continue on in the code assuming there's NO GRP_ID.
405
+			$message_template_group = EEM_Message_Template_Group::instance()->get_one_by_ID($GRP_ID);
406
+			if ($message_template_group instanceof EE_Message_Template_Group) {
407
+				$this->_template_collection->add($message_template_group);
408
+				return $message_template_group;
409
+			}
410
+		}
411
+		return null;
412
+	}
413
+
414
+
415
+	/**
416
+	 * Returns whether the event ids passed in all share the same message template group for the current message type
417
+	 * and messenger.
418
+	 *
419
+	 * @param array $event_ids
420
+	 * @return bool true means they DO share the same message template group, false means they don't.
421
+	 * @throws EE_Error
422
+	 * @throws InvalidArgumentException
423
+	 * @throws InvalidDataTypeException
424
+	 * @throws InvalidInterfaceException
425
+	 * @throws ReflectionException
426
+	 */
427
+	protected function _queue_shares_same_message_template_group_for_events(array $event_ids): bool
428
+	{
429
+		foreach ($this->_current_data_handler->events as $event) {
430
+			$event_ids[ $event['ID'] ] = $event['ID'];
431
+		}
432
+		$count_of_message_template_groups = EEM_Message_Template_Group::instance()->count(
433
+			[
434
+				[
435
+					'Event.EVT_ID'     => ['IN', $event_ids],
436
+					'MTP_messenger'    => $this->_current_messenger->name,
437
+					'MTP_message_type' => $this->_current_message_type->name,
438
+				],
439
+			],
440
+			'GRP_ID',
441
+			true
442
+		);
443
+		return $count_of_message_template_groups === 1;
444
+	}
445
+
446
+
447
+	/**
448
+	 * This will get the shared message template group for events that are in the current data handler but ONLY if
449
+	 * there's a single shared message template group among all the events.  Otherwise it returns null.
450
+	 *
451
+	 * @param array $event_ids
452
+	 * @return EE_Message_Template_Group|null
453
+	 * @throws EE_Error
454
+	 * @throws InvalidArgumentException
455
+	 * @throws InvalidDataTypeException
456
+	 * @throws InvalidInterfaceException
457
+	 * @throws ReflectionException
458
+	 */
459
+	protected function _get_shared_message_template_for_events(array $event_ids): ?EE_Message_Template_Group
460
+	{
461
+		$message_template_group = null;
462
+		if ($this->_queue_shares_same_message_template_group_for_events($event_ids)) {
463
+			$message_template_group = EEM_Message_Template_Group::instance()->get_one(
464
+				[
465
+					[
466
+						'Event.EVT_ID'     => ['IN', $event_ids],
467
+						'MTP_messenger'    => $this->_current_messenger->name,
468
+						'MTP_message_type' => $this->_current_message_type->name,
469
+						'MTP_is_active'    => true,
470
+					],
471
+					'group_by' => 'GRP_ID',
472
+				]
473
+			);
474
+			// store this in the collection if its valid
475
+			if ($message_template_group instanceof EE_Message_Template_Group) {
476
+				$this->_template_collection->add(
477
+					$message_template_group,
478
+					$event_ids
479
+				);
480
+			}
481
+		}
482
+		return $message_template_group;
483
+	}
484
+
485
+
486
+	/**
487
+	 * Retrieves the global message template group for the current messenger and message type.
488
+	 *
489
+	 * @return EE_Message_Template_Group|null
490
+	 * @throws EE_Error
491
+	 * @throws InvalidArgumentException
492
+	 * @throws InvalidDataTypeException
493
+	 * @throws InvalidInterfaceException
494
+	 * @throws ReflectionException
495
+	 */
496
+	protected function _get_global_message_template_group_for_current_messenger_and_message_type(): ?EE_Message_Template_Group
497
+	{
498
+		// first check the collection (we use an array with 0 in it to represent global groups).
499
+		$global_message_template_group = $this->_template_collection->get_by_key(
500
+			$this->_template_collection->getKey(
501
+				$this->_current_messenger->name,
502
+				$this->_current_message_type->name,
503
+				[0]
504
+			)
505
+		);
506
+
507
+		// if we don't have a group lets hit the db.
508
+		if (! $global_message_template_group instanceof EE_Message_Template_Group) {
509
+			$global_message_template_group = EEM_Message_Template_Group::instance()->get_one(
510
+				[
511
+					[
512
+						'MTP_messenger'    => $this->_current_messenger->name,
513
+						'MTP_message_type' => $this->_current_message_type->name,
514
+						'MTP_is_active'    => true,
515
+						'MTP_is_global'    => true,
516
+					],
517
+				]
518
+			);
519
+			// if we have a group, add it to the collection.
520
+			if ($global_message_template_group instanceof EE_Message_Template_Group) {
521
+				$this->_template_collection->add(
522
+					$global_message_template_group,
523
+					[0]
524
+				);
525
+			}
526
+		}
527
+		return $global_message_template_group;
528
+	}
529
+
530
+
531
+	/**
532
+	 * Returns an array of event ids for all the events within the current data handler.
533
+	 *
534
+	 * @return array
535
+	 */
536
+	protected function _get_event_ids_from_current_data_handler(): array
537
+	{
538
+		$event_ids = [];
539
+		foreach ($this->_current_data_handler->events as $event) {
540
+			$event_ids[ $event['ID'] ] = $event['ID'];
541
+		}
542
+		return $event_ids;
543
+	}
544
+
545
+
546
+	/**
547
+	 *  Retrieves formatted array of template information for each context specific to the given
548
+	 *  EE_Message_Template_Group
549
+	 *
550
+	 * @param EE_Message_Template_Group $message_template_group
551
+	 * @return array The returned array is in this structure:
552
+	 *                          array(
553
+	 *                          'field_name' => array(
554
+	 *                          'context' => 'content'
555
+	 *                          )
556
+	 *                          )
557
+	 * @throws EE_Error
558
+	 * @throws InvalidArgumentException
559
+	 * @throws InvalidDataTypeException
560
+	 * @throws InvalidInterfaceException
561
+	 * @throws ReflectionException
562
+	 */
563
+	protected function _get_templates(EE_Message_Template_Group $message_template_group): array
564
+	{
565
+		$templates         = [];
566
+		$context_templates = $message_template_group->context_templates();
567
+		foreach ($context_templates as $context => $template_fields) {
568
+			foreach ($template_fields as $template_field => $template_obj) {
569
+				if (! $template_obj instanceof EE_Message_Template) {
570
+					continue;
571
+				}
572
+				$templates[ $template_field ][ $context ] = $template_obj->get('MTP_content');
573
+			}
574
+		}
575
+		return $templates;
576
+	}
577
+
578
+
579
+	/**
580
+	 * Assembles new fully generated EE_Message objects and adds to _ready_queue
581
+	 *
582
+	 * @param array                     $addressees  Array of EE_Messages_Addressee objects indexed by message type
583
+	 *                                               context.
584
+	 * @param array                     $templates   formatted array of templates used for parsing data.
585
+	 * @param EE_Message_Template_Group $message_template_group
586
+	 * @return bool true if message generation went a-ok.  false if some sort of exception occurred.  Note: The
587
+	 *                                               method will attempt to generate ALL EE_Message objects and add to
588
+	 *                                               the _ready_queue.  Successfully generated messages get added to the
589
+	 *                                               queue with EEM_Message::status_idle, unsuccessfully generated
590
+	 *                                               messages will get added to the queue as EEM_Message::status_failed.
591
+	 *                                               Very rarely should "false" be returned from this method.
592
+	 * @throws EE_Error
593
+	 * @throws InvalidArgumentException
594
+	 * @throws InvalidDataTypeException
595
+	 * @throws InvalidIdentifierException
596
+	 * @throws InvalidInterfaceException
597
+	 * @throws ReflectionException
598
+	 */
599
+	protected function _assemble_messages(
600
+		array $addressees,
601
+		array $templates,
602
+		EE_Message_Template_Group $message_template_group
603
+	): bool {
604
+		// if templates are empty then get out because we can't generate anything.
605
+		if (! $templates) {
606
+			$this->_error_msg[] = esc_html__(
607
+				'Unable to assemble messages because there are no templates retrieved for generating the messages with',
608
+				'event_espresso'
609
+			);
610
+			return false;
611
+		}
612
+
613
+		// We use this as the counter for generated messages because don't forget we may be executing this inside of a
614
+		// generation_queue.  So _ready_queue may have generated EE_Message objects already.
615
+		$generated_count = 0;
616
+		foreach ($addressees as $context => $recipients) {
617
+			foreach ($recipients as $recipient) {
618
+				$message = $this->_setup_message_object($context, $recipient, $templates, $message_template_group);
619
+				if ($message instanceof EE_Message) {
620
+					$this->_ready_queue->add(
621
+						$message,
622
+						[],
623
+						$this->_generation_queue->get_message_repository()->is_preview(),
624
+						$this->_generation_queue->get_message_repository()->is_test_send()
625
+					);
626
+					$generated_count++;
627
+				}
628
+
629
+				// if the current MSG being generated is for a test send then we'll only use ONE message in the
630
+				// generation.
631
+				if ($this->_generation_queue->get_message_repository()->is_test_send()) {
632
+					break 2;
633
+				}
634
+			}
635
+		}
636
+
637
+		// if there are no generated messages then something else fatal went wrong.
638
+		return $generated_count > 0;
639
+	}
640
+
641
+
642
+	/**
643
+	 * @param string                    $context   The context for the generated message.
644
+	 * @param EE_Messages_Addressee     $recipient
645
+	 * @param array                     $templates formatted array of templates used for parsing data.
646
+	 * @param EE_Message_Template_Group $message_template_group
647
+	 * @return bool|EE_Message
648
+	 * @throws EE_Error
649
+	 * @throws InvalidArgumentException
650
+	 * @throws InvalidDataTypeException
651
+	 * @throws InvalidInterfaceException
652
+	 * @throws ReflectionException
653
+	 * @throws InvalidIdentifierException
654
+	 */
655
+	protected function _setup_message_object(
656
+		string $context,
657
+		EE_Messages_Addressee $recipient,
658
+		array $templates,
659
+		EE_Message_Template_Group $message_template_group
660
+	) {
661
+		// stuff we already know
662
+		$transaction_id = $recipient->txn instanceof EE_Transaction
663
+			? $recipient->txn->ID()
664
+			: 0;
665
+		$transaction_id = empty($transaction_id) && $this->_current_data_handler->txn instanceof EE_Transaction
666
+			? $this->_current_data_handler->txn->ID()
667
+			: $transaction_id;
668
+		$message_fields = [
669
+			'GRP_ID'           => $message_template_group->ID(),
670
+			'TXN_ID'           => $transaction_id,
671
+			'MSG_messenger'    => $this->_current_messenger->name,
672
+			'MSG_message_type' => $this->_current_message_type->name,
673
+			'MSG_context'      => $context,
674
+		];
675
+
676
+		// recipient id and type should be on the EE_Messages_Addressee object but if this is empty, let's try to grab
677
+		// the info from the att_obj found in the EE_Messages_Addressee object.
678
+		if (empty($recipient->recipient_id) || empty($recipient->recipient_type)) {
679
+			$message_fields['MSG_recipient_ID']   = $recipient->att_obj instanceof EE_Attendee
680
+				? $recipient->att_obj->ID()
681
+				: 0;
682
+			$message_fields['MSG_recipient_type'] = 'Attendee';
683
+		} else {
684
+			$message_fields['MSG_recipient_ID']   = $recipient->recipient_id;
685
+			$message_fields['MSG_recipient_type'] = $recipient->recipient_type;
686
+		}
687
+		$message = EE_Message_Factory::create($message_fields);
688
+
689
+		// grab valid shortcodes for shortcode parser
690
+		$mt_shortcodes = $this->_current_message_type->get_valid_shortcodes();
691
+		$m_shortcodes  = $this->_current_messenger->get_valid_shortcodes();
692
+
693
+		// if the 'to' field is empty or the context is inactive we skip EXCEPT if we're previewing
694
+		if (
695
+			! $this->_generation_queue->get_message_repository()->is_preview()
696
+			&& (
697
+				(empty($templates['to'][ $context ]) && ! $this->_current_messenger->allow_empty_to_field())
698
+				|| ! $message_template_group->is_context_active($context)
699
+			)
700
+		) {
701
+			// we silently exit here and do NOT record a fail because the message is "turned off" by having no "to"
702
+			// field.
703
+			return false;
704
+		}
705
+		$error_msg = [];
706
+		foreach ($templates as $field => $field_context) {
707
+			$error_msg = [];
708
+			// let's setup the valid shortcodes for the incoming context.
709
+			$valid_shortcodes = $mt_shortcodes[ $context ];
710
+			// merge in valid shortcodes for the field.
711
+			$shortcodes = $m_shortcodes[ $field ] ?? $valid_shortcodes;
712
+			if (isset($field_context[ $context ])) {
713
+				// prefix field.
714
+				$column_name = 'MSG_' . $field;
715
+				try {
716
+					$content = $this->_shortcode_parser->parse_message_template(
717
+						$field_context[ $context ],
718
+						$recipient,
719
+						$shortcodes,
720
+						$this->_current_message_type,
721
+						$this->_current_messenger,
722
+						$message
723
+					);
724
+					// the model field removes slashes when setting (usually necessary when the input is from the
725
+					// request) but this value is from another model and has no slashes. So add them so it matches
726
+					// what the field expected (otherwise slashes will have been stripped from this an extra time)
727
+					$message->set_field_or_extra_meta($column_name, addslashes($content));
728
+				} catch (EE_Error $e) {
729
+					$error_msg[] = sprintf(
730
+					/* Translators: First place holder is message model field name.
731 731
                      * Second placeholder is exception error message */
732
-                        esc_html__(
733
-                            'There was a problem generating the content for the field %s: %s',
734
-                            'event_espresso'
735
-                        ),
736
-                        $field,
737
-                        $e->getMessage()
738
-                    );
739
-                    $message->set_STS_ID(EEM_Message::status_failed);
740
-                }
741
-            }
742
-        }
743
-
744
-        if ($message->STS_ID() === EEM_Message::status_failed) {
745
-            $error_msg = esc_html__('There were problems generating this message:', 'event_espresso')
746
-                         . "\n"
747
-                         . implode("\n", $error_msg);
748
-            $message->set_error_message($error_msg);
749
-        } else {
750
-            $message->set_STS_ID(EEM_Message::status_idle);
751
-        }
752
-        return $message;
753
-    }
754
-
755
-
756
-    /**
757
-     * This verifies that the incoming array has a EE_messenger object and a EE_message_type object and sets appropriate
758
-     * error message if either is missing.
759
-     *
760
-     * @return bool true means there were no errors, false means there were errors.
761
-     */
762
-    protected function _verify(): bool
763
-    {
764
-        // reset error message to an empty array.
765
-        $this->_error_msg = [];
766
-        // set the verified flag so we know everything has been validated.
767
-        $this->_verified = $this->_validate_messenger_and_message_type() && $this->_validate_and_setup_data();
768
-        return $this->_verified;
769
-    }
770
-
771
-
772
-    /**
773
-     * This accepts an array and validates that it is an array indexed by context with each value being an array of
774
-     * EE_Messages_Addressee objects.
775
-     *
776
-     * @param array|null $addressees Keys correspond to contexts for the message type and values are
777
-     *                               EE_Messages_Addressee[]
778
-     * @return bool
779
-     */
780
-    protected function _valid_addressees(?array $addressees): bool
781
-    {
782
-        if (! $addressees || ! is_array($addressees)) {
783
-            return false;
784
-        }
785
-
786
-        foreach ($addressees as $addressee_array) {
787
-            foreach ($addressee_array as $addressee) {
788
-                if (! $addressee instanceof EE_Messages_Addressee) {
789
-                    return false;
790
-                }
791
-            }
792
-        }
793
-        return true;
794
-    }
795
-
796
-
797
-    /**
798
-     * This validates the messenger, message type, and presences of generation data for the current EE_Message in the
799
-     * queue. This process sets error messages if something is wrong.
800
-     *
801
-     * @return bool   true is if there are no errors.  false is if there is.
802
-     */
803
-    protected function _validate_messenger_and_message_type(): bool
804
-    {
805
-        // first are there any existing error messages?  If so then return.
806
-        if ($this->_error_msg) {
807
-            return false;
808
-        }
809
-        $message = $this->_generation_queue->get_message_repository()->current();
810
-        try {
811
-            $this->_current_messenger = $message->valid_messenger(true)
812
-                ? $message->messenger_object()
813
-                : null;
814
-        } catch (Exception $e) {
815
-            $this->_error_msg[] = $e->getMessage();
816
-        }
817
-        try {
818
-            $this->_current_message_type = $message->valid_message_type(true)
819
-                ? $message->message_type_object()
820
-                : null;
821
-        } catch (Exception $e) {
822
-            $this->_error_msg[] = $e->getMessage();
823
-        }
824
-
825
-        /**
826
-         * Check if there is any generation data, but only if this is not for a preview.
827
-         */
828
-        if (
829
-            ! $this->_generation_queue->get_message_repository()->get_generation_data()
830
-            && (
831
-                ! $this->_generation_queue->get_message_repository()->is_preview()
832
-                && $this->_generation_queue->get_message_repository()->get_data_handler()
833
-                   !== 'EE_Messages_Preview_incoming_data'
834
-            )
835
-        ) {
836
-            $this->_error_msg[] = esc_html__(
837
-                'There is no generation data for this message. Unable to generate.',
838
-                'event_espresso'
839
-            );
840
-        }
841
-
842
-        return empty($this->_error_msg);
843
-    }
844
-
845
-
846
-    /**
847
-     * This method retrieves the expected data handler for the message type and validates the generation data for that
848
-     * data handler.
849
-     *
850
-     * @return bool true means there are no errors.  false means there were errors (and handler did not get setup).
851
-     */
852
-    protected function _validate_and_setup_data(): bool
853
-    {
854
-        // First, are there any existing error messages?  If so, return because if there were errors elsewhere this
855
-        // can't be used anyways.
856
-        if ($this->_error_msg) {
857
-            return false;
858
-        }
859
-
860
-        $generation_data = $this->_generation_queue->get_message_repository()->get_generation_data();
861
-
862
-        /** @type EE_Messages_incoming_data $data_handler_class_name - well not really... just the class name actually */
863
-        $data_handler_class_name = $this->_generation_queue->get_message_repository()->get_data_handler()
864
-            ? $this->_generation_queue->get_message_repository()->get_data_handler()
865
-            : 'EE_Messages_' . $this->_current_message_type->get_data_handler($generation_data) . '_incoming_data';
866
-
867
-        // If this EE_Message is for a preview, then let's switch out to the preview data handler.
868
-        if ($this->_generation_queue->get_message_repository()->is_preview()) {
869
-            $data_handler_class_name = 'EE_Messages_Preview_incoming_data';
870
-        }
871
-
872
-        // First get the class name for the data handler (and also verifies it exists.
873
-        if (! class_exists($data_handler_class_name)) {
874
-            $this->_error_msg[] = sprintf(
875
-            /* Translators: Both placeholders are the names of php classes. */
876
-                esc_html__(
877
-                    'The included data handler class name does not match any valid, accessible, "%1$s" classes.  Looking for %2$s.',
878
-                    'event_espresso'
879
-                ),
880
-                'EE_Messages_incoming_data',
881
-                $data_handler_class_name
882
-            );
883
-            return false;
884
-        }
885
-
886
-        // convert generation_data for data_handler_instantiation.
887
-        $generation_data = $data_handler_class_name::convert_data_from_persistent_storage($generation_data);
888
-
889
-        // note, this may set error messages as well.
890
-        $this->_set_data_handler($generation_data, $data_handler_class_name);
891
-
892
-        return empty($this->_error_msg);
893
-    }
894
-
895
-
896
-    /**
897
-     * Sets the $_current_data_handler property that is used for generating the current EE_Message in the queue, and
898
-     * adds it to the _data repository.
899
-     *
900
-     * @param mixed  $generating_data           This is data expected by the instantiated data handler.
901
-     * @param string $data_handler_class_name   This is the reference string indicating what data handler is being
902
-     *                                          instantiated.
903
-     */
904
-    protected function _set_data_handler($generating_data, string $data_handler_class_name)
905
-    {
906
-        // valid classname for the data handler.  Now let's setup the key for the data handler repository to see if
907
-        // there is already a ready data handler in the repository.
908
-        $this->_current_data_handler = $this->_data_handler_collection->get_by_key(
909
-            $this->_data_handler_collection->get_key(
910
-                $data_handler_class_name,
911
-                $generating_data
912
-            )
913
-        );
914
-        if (! $this->_current_data_handler instanceof EE_Messages_incoming_data) {
915
-            // no saved data_handler in the repo so let's set one up and add it to the repo.
916
-            try {
917
-                $this->_current_data_handler = new $data_handler_class_name($generating_data);
918
-                $this->_data_handler_collection->add($this->_current_data_handler, $generating_data);
919
-            } catch (Exception $e) {
920
-                $this->_error_msg[] = $e->getMessage();
921
-            }
922
-        }
923
-    }
924
-
925
-
926
-    /**
927
-     * The queued EE_Message for generation does not save the data used for generation as objects
928
-     * because serialization of those objects could be problematic if the data is saved to the db.
929
-     * So this method calls the static method on the associated data_handler for the given message_type
930
-     * and that preps the data for later instantiation when generating.
931
-     *
932
-     * @param EE_Message_To_Generate $message_to_generate
933
-     * @param bool                   $preview Indicate whether this is being used for a preview or not.
934
-     * @return mixed Prepped data for persisting to the queue.  false is returned if unable to prep data.
935
-     */
936
-    protected function _prepare_data_for_queue(EE_Message_To_Generate $message_to_generate, bool $preview)
937
-    {
938
-        if (! $message_to_generate->valid()) {
939
-            return false; // unable to get the data because the info in the EE_Message_To_Generate class is invalid.
940
-        }
941
-        /** @type EE_Messages_incoming_data $data_handler - well not really... just the class name actually */
942
-        $data_handler = $message_to_generate->get_data_handler_class_name($preview);
943
-        return $data_handler::convert_data_for_persistent_storage($message_to_generate->data());
944
-    }
945
-
946
-
947
-    /**
948
-     * This sets up a EEM_Message::status_incomplete EE_Message object and adds it to the generation queue.
949
-     *
950
-     * @param EE_Message_To_Generate $message_to_generate
951
-     * @param bool                   $test_send Whether this is just a test send or not.  Typically used for previews.
952
-     * @return bool
953
-     * @throws InvalidArgumentException
954
-     * @throws InvalidDataTypeException
955
-     * @throws InvalidInterfaceException
956
-     */
957
-    public function create_and_add_message_to_queue(
958
-        EE_Message_To_Generate $message_to_generate,
959
-        bool $test_send = false
960
-    ): bool {
961
-        // prep data
962
-        $data    = $this->_prepare_data_for_queue($message_to_generate, $message_to_generate->preview());
963
-        $request = LoaderFactory::getLoader()->getShared('EventEspresso\core\services\request\RequestInterface');
964
-
965
-        $message = $message_to_generate->get_EE_Message();
966
-        $GRP_ID  = $request->getRequestParam('GRP_ID', 0, 'int');
967
-
968
-        $GRP_ID = apply_filters(
969
-            'FHEE__EE_Messages_Generator__create_and_add_message_to_queue_GRP_ID',
970
-            $GRP_ID > 0
971
-                ? $GRP_ID
972
-                : $message->GRP_ID(),
973
-            $message,
974
-            $message_to_generate,
975
-            $test_send
976
-        );
977
-
978
-        if ($GRP_ID > 0) {
979
-            $message->set_GRP_ID($GRP_ID);
980
-        }
981
-
982
-        if ($data === false) {
983
-            $message->set_STS_ID(EEM_Message::status_failed);
984
-            $message->set_error_message(
985
-                esc_html__(
986
-                    'Unable to prepare data for persistence to the database.',
987
-                    'event_espresso'
988
-                )
989
-            );
990
-        } else {
991
-            // make sure that the data handler is cached on the message as well
992
-            $data['data_handler_class_name'] = $message_to_generate->get_data_handler_class_name();
993
-        }
994
-
995
-        return $this->_generation_queue->add($message, $data, $message_to_generate->preview(), $test_send);
996
-    }
732
+						esc_html__(
733
+							'There was a problem generating the content for the field %s: %s',
734
+							'event_espresso'
735
+						),
736
+						$field,
737
+						$e->getMessage()
738
+					);
739
+					$message->set_STS_ID(EEM_Message::status_failed);
740
+				}
741
+			}
742
+		}
743
+
744
+		if ($message->STS_ID() === EEM_Message::status_failed) {
745
+			$error_msg = esc_html__('There were problems generating this message:', 'event_espresso')
746
+						 . "\n"
747
+						 . implode("\n", $error_msg);
748
+			$message->set_error_message($error_msg);
749
+		} else {
750
+			$message->set_STS_ID(EEM_Message::status_idle);
751
+		}
752
+		return $message;
753
+	}
754
+
755
+
756
+	/**
757
+	 * This verifies that the incoming array has a EE_messenger object and a EE_message_type object and sets appropriate
758
+	 * error message if either is missing.
759
+	 *
760
+	 * @return bool true means there were no errors, false means there were errors.
761
+	 */
762
+	protected function _verify(): bool
763
+	{
764
+		// reset error message to an empty array.
765
+		$this->_error_msg = [];
766
+		// set the verified flag so we know everything has been validated.
767
+		$this->_verified = $this->_validate_messenger_and_message_type() && $this->_validate_and_setup_data();
768
+		return $this->_verified;
769
+	}
770
+
771
+
772
+	/**
773
+	 * This accepts an array and validates that it is an array indexed by context with each value being an array of
774
+	 * EE_Messages_Addressee objects.
775
+	 *
776
+	 * @param array|null $addressees Keys correspond to contexts for the message type and values are
777
+	 *                               EE_Messages_Addressee[]
778
+	 * @return bool
779
+	 */
780
+	protected function _valid_addressees(?array $addressees): bool
781
+	{
782
+		if (! $addressees || ! is_array($addressees)) {
783
+			return false;
784
+		}
785
+
786
+		foreach ($addressees as $addressee_array) {
787
+			foreach ($addressee_array as $addressee) {
788
+				if (! $addressee instanceof EE_Messages_Addressee) {
789
+					return false;
790
+				}
791
+			}
792
+		}
793
+		return true;
794
+	}
795
+
796
+
797
+	/**
798
+	 * This validates the messenger, message type, and presences of generation data for the current EE_Message in the
799
+	 * queue. This process sets error messages if something is wrong.
800
+	 *
801
+	 * @return bool   true is if there are no errors.  false is if there is.
802
+	 */
803
+	protected function _validate_messenger_and_message_type(): bool
804
+	{
805
+		// first are there any existing error messages?  If so then return.
806
+		if ($this->_error_msg) {
807
+			return false;
808
+		}
809
+		$message = $this->_generation_queue->get_message_repository()->current();
810
+		try {
811
+			$this->_current_messenger = $message->valid_messenger(true)
812
+				? $message->messenger_object()
813
+				: null;
814
+		} catch (Exception $e) {
815
+			$this->_error_msg[] = $e->getMessage();
816
+		}
817
+		try {
818
+			$this->_current_message_type = $message->valid_message_type(true)
819
+				? $message->message_type_object()
820
+				: null;
821
+		} catch (Exception $e) {
822
+			$this->_error_msg[] = $e->getMessage();
823
+		}
824
+
825
+		/**
826
+		 * Check if there is any generation data, but only if this is not for a preview.
827
+		 */
828
+		if (
829
+			! $this->_generation_queue->get_message_repository()->get_generation_data()
830
+			&& (
831
+				! $this->_generation_queue->get_message_repository()->is_preview()
832
+				&& $this->_generation_queue->get_message_repository()->get_data_handler()
833
+				   !== 'EE_Messages_Preview_incoming_data'
834
+			)
835
+		) {
836
+			$this->_error_msg[] = esc_html__(
837
+				'There is no generation data for this message. Unable to generate.',
838
+				'event_espresso'
839
+			);
840
+		}
841
+
842
+		return empty($this->_error_msg);
843
+	}
844
+
845
+
846
+	/**
847
+	 * This method retrieves the expected data handler for the message type and validates the generation data for that
848
+	 * data handler.
849
+	 *
850
+	 * @return bool true means there are no errors.  false means there were errors (and handler did not get setup).
851
+	 */
852
+	protected function _validate_and_setup_data(): bool
853
+	{
854
+		// First, are there any existing error messages?  If so, return because if there were errors elsewhere this
855
+		// can't be used anyways.
856
+		if ($this->_error_msg) {
857
+			return false;
858
+		}
859
+
860
+		$generation_data = $this->_generation_queue->get_message_repository()->get_generation_data();
861
+
862
+		/** @type EE_Messages_incoming_data $data_handler_class_name - well not really... just the class name actually */
863
+		$data_handler_class_name = $this->_generation_queue->get_message_repository()->get_data_handler()
864
+			? $this->_generation_queue->get_message_repository()->get_data_handler()
865
+			: 'EE_Messages_' . $this->_current_message_type->get_data_handler($generation_data) . '_incoming_data';
866
+
867
+		// If this EE_Message is for a preview, then let's switch out to the preview data handler.
868
+		if ($this->_generation_queue->get_message_repository()->is_preview()) {
869
+			$data_handler_class_name = 'EE_Messages_Preview_incoming_data';
870
+		}
871
+
872
+		// First get the class name for the data handler (and also verifies it exists.
873
+		if (! class_exists($data_handler_class_name)) {
874
+			$this->_error_msg[] = sprintf(
875
+			/* Translators: Both placeholders are the names of php classes. */
876
+				esc_html__(
877
+					'The included data handler class name does not match any valid, accessible, "%1$s" classes.  Looking for %2$s.',
878
+					'event_espresso'
879
+				),
880
+				'EE_Messages_incoming_data',
881
+				$data_handler_class_name
882
+			);
883
+			return false;
884
+		}
885
+
886
+		// convert generation_data for data_handler_instantiation.
887
+		$generation_data = $data_handler_class_name::convert_data_from_persistent_storage($generation_data);
888
+
889
+		// note, this may set error messages as well.
890
+		$this->_set_data_handler($generation_data, $data_handler_class_name);
891
+
892
+		return empty($this->_error_msg);
893
+	}
894
+
895
+
896
+	/**
897
+	 * Sets the $_current_data_handler property that is used for generating the current EE_Message in the queue, and
898
+	 * adds it to the _data repository.
899
+	 *
900
+	 * @param mixed  $generating_data           This is data expected by the instantiated data handler.
901
+	 * @param string $data_handler_class_name   This is the reference string indicating what data handler is being
902
+	 *                                          instantiated.
903
+	 */
904
+	protected function _set_data_handler($generating_data, string $data_handler_class_name)
905
+	{
906
+		// valid classname for the data handler.  Now let's setup the key for the data handler repository to see if
907
+		// there is already a ready data handler in the repository.
908
+		$this->_current_data_handler = $this->_data_handler_collection->get_by_key(
909
+			$this->_data_handler_collection->get_key(
910
+				$data_handler_class_name,
911
+				$generating_data
912
+			)
913
+		);
914
+		if (! $this->_current_data_handler instanceof EE_Messages_incoming_data) {
915
+			// no saved data_handler in the repo so let's set one up and add it to the repo.
916
+			try {
917
+				$this->_current_data_handler = new $data_handler_class_name($generating_data);
918
+				$this->_data_handler_collection->add($this->_current_data_handler, $generating_data);
919
+			} catch (Exception $e) {
920
+				$this->_error_msg[] = $e->getMessage();
921
+			}
922
+		}
923
+	}
924
+
925
+
926
+	/**
927
+	 * The queued EE_Message for generation does not save the data used for generation as objects
928
+	 * because serialization of those objects could be problematic if the data is saved to the db.
929
+	 * So this method calls the static method on the associated data_handler for the given message_type
930
+	 * and that preps the data for later instantiation when generating.
931
+	 *
932
+	 * @param EE_Message_To_Generate $message_to_generate
933
+	 * @param bool                   $preview Indicate whether this is being used for a preview or not.
934
+	 * @return mixed Prepped data for persisting to the queue.  false is returned if unable to prep data.
935
+	 */
936
+	protected function _prepare_data_for_queue(EE_Message_To_Generate $message_to_generate, bool $preview)
937
+	{
938
+		if (! $message_to_generate->valid()) {
939
+			return false; // unable to get the data because the info in the EE_Message_To_Generate class is invalid.
940
+		}
941
+		/** @type EE_Messages_incoming_data $data_handler - well not really... just the class name actually */
942
+		$data_handler = $message_to_generate->get_data_handler_class_name($preview);
943
+		return $data_handler::convert_data_for_persistent_storage($message_to_generate->data());
944
+	}
945
+
946
+
947
+	/**
948
+	 * This sets up a EEM_Message::status_incomplete EE_Message object and adds it to the generation queue.
949
+	 *
950
+	 * @param EE_Message_To_Generate $message_to_generate
951
+	 * @param bool                   $test_send Whether this is just a test send or not.  Typically used for previews.
952
+	 * @return bool
953
+	 * @throws InvalidArgumentException
954
+	 * @throws InvalidDataTypeException
955
+	 * @throws InvalidInterfaceException
956
+	 */
957
+	public function create_and_add_message_to_queue(
958
+		EE_Message_To_Generate $message_to_generate,
959
+		bool $test_send = false
960
+	): bool {
961
+		// prep data
962
+		$data    = $this->_prepare_data_for_queue($message_to_generate, $message_to_generate->preview());
963
+		$request = LoaderFactory::getLoader()->getShared('EventEspresso\core\services\request\RequestInterface');
964
+
965
+		$message = $message_to_generate->get_EE_Message();
966
+		$GRP_ID  = $request->getRequestParam('GRP_ID', 0, 'int');
967
+
968
+		$GRP_ID = apply_filters(
969
+			'FHEE__EE_Messages_Generator__create_and_add_message_to_queue_GRP_ID',
970
+			$GRP_ID > 0
971
+				? $GRP_ID
972
+				: $message->GRP_ID(),
973
+			$message,
974
+			$message_to_generate,
975
+			$test_send
976
+		);
977
+
978
+		if ($GRP_ID > 0) {
979
+			$message->set_GRP_ID($GRP_ID);
980
+		}
981
+
982
+		if ($data === false) {
983
+			$message->set_STS_ID(EEM_Message::status_failed);
984
+			$message->set_error_message(
985
+				esc_html__(
986
+					'Unable to prepare data for persistence to the database.',
987
+					'event_espresso'
988
+				)
989
+			);
990
+		} else {
991
+			// make sure that the data handler is cached on the message as well
992
+			$data['data_handler_class_name'] = $message_to_generate->get_data_handler_class_name();
993
+		}
994
+
995
+		return $this->_generation_queue->add($message, $data, $message_to_generate->preview(), $test_send);
996
+	}
997 997
 }
Please login to merge, or discard this patch.
core/libraries/messages/defaults/EE_Messages_Template_Defaults.lib.php 1 patch
Indentation   +227 added lines, -227 removed lines patch added patch discarded remove patch
@@ -11,231 +11,231 @@
 block discarded – undo
11 11
  */
12 12
 class EE_Messages_Template_Defaults extends EE_Base
13 13
 {
14
-    /**
15
-     * Used for holding the EE_Message_Template GRP_ID field value.
16
-     *
17
-     * @var [type]
18
-     */
19
-    protected $_GRP_ID;
20
-
21
-    /**
22
-     * holds the messenger object
23
-     *
24
-     * @var EE_messenger
25
-     */
26
-    protected $_messenger;
27
-
28
-    /**
29
-     * holds the message type object
30
-     *
31
-     * @var EE_message_type
32
-     */
33
-    protected $_message_type;
34
-
35
-    /**
36
-     * holds the fields used (this is retrieved from the messenger)
37
-     *
38
-     * @var array
39
-     */
40
-    protected $_fields;
41
-
42
-    /**
43
-     * holds the assembled template (with defaults) for creation in the database
44
-     *
45
-     * @var array
46
-     */
47
-    protected $_templates;
48
-
49
-    /**
50
-     * holds the contexts used (this is retrieved from the message type)
51
-     *
52
-     * @var array
53
-     */
54
-    protected $_contexts;
55
-
56
-
57
-    /**
58
-     * @var EEM_Message_Template_Group
59
-     */
60
-    protected $_message_template_group_model;
61
-
62
-
63
-    /**
64
-     * @var EEM_Message_Template
65
-     */
66
-    protected $_message_template_model;
67
-
68
-
69
-    /**
70
-     * EE_Messages_Template_Defaults constructor.
71
-     *
72
-     * @param EE_messenger                    $messenger
73
-     * @param EE_message_type                 $message_type
74
-     * @param int|null                        $GRP_ID                 Optional.  If included then we're just
75
-     *                                                                regenerating the template fields for the given
76
-     *                                                                group not the message template group itself
77
-     * @param EEM_Message_Template_Group|null $message_template_group_model
78
-     * @param EEM_Message_Template|null       $message_template_model
79
-     * @throws EE_Error
80
-     */
81
-    public function __construct(
82
-        EE_messenger $messenger,
83
-        EE_message_type $message_type,
84
-        ?int $GRP_ID,
85
-        ?EEM_Message_Template_Group $message_template_group_model,
86
-        ?EEM_Message_Template $message_template_model
87
-    ) {
88
-        $this->_messenger    = $messenger;
89
-        $this->_message_type = $message_type;
90
-        $this->_GRP_ID       = $GRP_ID ?? 0;
91
-        // set the model object
92
-        $this->_message_template_group_model = $message_template_group_model ?? EEM_Message_Template_Group::instance();
93
-        $this->_message_template_model       = $message_template_model ?? EEM_Message_Template::instance();
94
-        $this->_fields                       = $this->_messenger->get_template_fields();
95
-        $this->_contexts                     = $this->_message_type->get_contexts();
96
-    }
97
-
98
-
99
-    /**
100
-     * Setup the _template_data property.
101
-     * This method sets the _templates property array before templates are created.
102
-     *
103
-     * @param string $template_pack This corresponds to a template pack class reference which will contain information
104
-     *                              about where to obtain the templates.
105
-     *
106
-     */
107
-    private function _set_templates(string $template_pack)
108
-    {
109
-        // get the corresponding template pack object (if present.  If not then we just load the default and add a
110
-        // notice).  The class name should be something like 'EE_Messages_Template_Pack_Default' where "default' would be
111
-        // the incoming template pack reference.
112
-        $class_name =
113
-            'EE_Messages_Template_Pack_' . str_replace(' ', '_', ucwords(str_replace('_', ' ', $template_pack)));
114
-
115
-        if (! class_exists($class_name)) {
116
-            EE_Error::add_error(
117
-                sprintf(
118
-                    esc_html__(
119
-                        'The template pack represented by a class corresponding to "%1$s" does not exist. Likely the autoloader for this class has the wrong path or the incoming reference is misspelled. The default template pack has been used to generate the templates instead.',
120
-                        'event_espresso'
121
-                    ),
122
-                    $class_name
123
-                ),
124
-                __FILE__,
125
-                __FUNCTION__,
126
-                __LINE__
127
-            );
128
-            $class_name = 'EE_Messages_Template_Pack_Default';
129
-        }
130
-        /** @type EE_Messages_Template_Pack $template_pack */
131
-        $template_pack = new $class_name();
132
-
133
-        // get all the templates from the template pack.
134
-        $this->_templates = $template_pack->get_templates($this->_messenger, $this->_message_type);
135
-    }
136
-
137
-
138
-    /**
139
-     * Return the contexts for the message type as cached on this instance.
140
-     *
141
-     * @return array
142
-     */
143
-    public function get_contexts(): array
144
-    {
145
-        return $this->_contexts;
146
-    }
147
-
148
-
149
-    /**
150
-     * public facing create new templates method
151
-     *
152
-     * @return array|false|int|string success array or false.
153
-     * @throws EE_Error
154
-     * @throws ReflectionException
155
-     */
156
-    public function create_new_templates()
157
-    {
158
-        $template_pack = 'default';
159
-        // if we have the GRP_ID then let's use that to see if there is a set template pack and use that for the new templates.
160
-        if (! empty($this->_GRP_ID)) {
161
-            $message_template_group = $this->_message_template_group_model->get_one_by_ID($this->_GRP_ID);
162
-            $template_pack          = $message_template_group instanceof EE_Message_Template_Group
163
-                ? $message_template_group->get_template_pack_name()
164
-                : 'default';
165
-            // we also need to reset the template variation to default
166
-            $message_template_group->set_template_pack_variation('default');
167
-        }
168
-        return $this->_create_new_templates($template_pack);
169
-    }
170
-
171
-
172
-    /**
173
-     *  Handles creating new default templates.
174
-     *
175
-     * @param string $template_pack This corresponds to a template pack class reference
176
-     *                              which will contain information about where to obtain the templates.
177
-     * @return array|bool            success array or false.
178
-     * @throws EE_Error
179
-     * @throws ReflectionException
180
-     */
181
-    protected function _create_new_templates(string $template_pack)
182
-    {
183
-        $this->_set_templates($template_pack);
184
-
185
-        // necessary properties are set, let's save the default templates
186
-        if (empty($this->_GRP_ID)) {
187
-            $main_template_data = [
188
-                'MTP_messenger'    => $this->_messenger->name,
189
-                'MTP_message_type' => $this->_message_type->name,
190
-                'MTP_is_override'  => 0,
191
-                'MTP_deleted'      => 0,
192
-                'MTP_is_global'    => 1,
193
-                'MTP_user_id'      => EEH_Activation::get_default_creator_id(),
194
-                'MTP_is_active'    => 1,
195
-            ];
196
-            // let's insert the above and get our GRP_ID, then reset the template data array to just include the GRP_ID
197
-            $grp_id = $this->_message_template_group_model->insert($main_template_data);
198
-            if (empty($grp_id)) {
199
-                return $grp_id;
200
-            }
201
-            $this->_GRP_ID = $grp_id;
202
-        }
203
-
204
-        $template_data = ['GRP_ID' => $this->_GRP_ID];
205
-
206
-        foreach ($this->_contexts as $context => $details) {
207
-            foreach ($this->_fields as $field => $field_type) {
208
-                if ($field != 'extra') {
209
-                    $template_data['MTP_context']        = $context;
210
-                    $template_data['MTP_template_field'] = $field;
211
-                    $template_data['MTP_content']        = $this->_templates[ $context ][ $field ];
212
-
213
-                    $MTP = $this->_message_template_model->insert($template_data);
214
-                    if (! $MTP) {
215
-                        EE_Error::add_error(
216
-                            sprintf(
217
-                                esc_html__(
218
-                                    'There was an error in saving new template data for %1$s messenger, %2$s message type, %3$s context and %4$s template field.',
219
-                                    'event_espresso'
220
-                                ),
221
-                                $this->_messenger->name,
222
-                                $this->_message_type->name,
223
-                                $context,
224
-                                $field
225
-                            ),
226
-                            __FILE__,
227
-                            __FUNCTION__,
228
-                            __LINE__
229
-                        );
230
-                        return false;
231
-                    }
232
-                }
233
-            }
234
-        }
235
-
236
-        return [
237
-            'GRP_ID'      => $this->_GRP_ID,
238
-            'MTP_context' => key($this->_contexts),
239
-        ];
240
-    }
14
+	/**
15
+	 * Used for holding the EE_Message_Template GRP_ID field value.
16
+	 *
17
+	 * @var [type]
18
+	 */
19
+	protected $_GRP_ID;
20
+
21
+	/**
22
+	 * holds the messenger object
23
+	 *
24
+	 * @var EE_messenger
25
+	 */
26
+	protected $_messenger;
27
+
28
+	/**
29
+	 * holds the message type object
30
+	 *
31
+	 * @var EE_message_type
32
+	 */
33
+	protected $_message_type;
34
+
35
+	/**
36
+	 * holds the fields used (this is retrieved from the messenger)
37
+	 *
38
+	 * @var array
39
+	 */
40
+	protected $_fields;
41
+
42
+	/**
43
+	 * holds the assembled template (with defaults) for creation in the database
44
+	 *
45
+	 * @var array
46
+	 */
47
+	protected $_templates;
48
+
49
+	/**
50
+	 * holds the contexts used (this is retrieved from the message type)
51
+	 *
52
+	 * @var array
53
+	 */
54
+	protected $_contexts;
55
+
56
+
57
+	/**
58
+	 * @var EEM_Message_Template_Group
59
+	 */
60
+	protected $_message_template_group_model;
61
+
62
+
63
+	/**
64
+	 * @var EEM_Message_Template
65
+	 */
66
+	protected $_message_template_model;
67
+
68
+
69
+	/**
70
+	 * EE_Messages_Template_Defaults constructor.
71
+	 *
72
+	 * @param EE_messenger                    $messenger
73
+	 * @param EE_message_type                 $message_type
74
+	 * @param int|null                        $GRP_ID                 Optional.  If included then we're just
75
+	 *                                                                regenerating the template fields for the given
76
+	 *                                                                group not the message template group itself
77
+	 * @param EEM_Message_Template_Group|null $message_template_group_model
78
+	 * @param EEM_Message_Template|null       $message_template_model
79
+	 * @throws EE_Error
80
+	 */
81
+	public function __construct(
82
+		EE_messenger $messenger,
83
+		EE_message_type $message_type,
84
+		?int $GRP_ID,
85
+		?EEM_Message_Template_Group $message_template_group_model,
86
+		?EEM_Message_Template $message_template_model
87
+	) {
88
+		$this->_messenger    = $messenger;
89
+		$this->_message_type = $message_type;
90
+		$this->_GRP_ID       = $GRP_ID ?? 0;
91
+		// set the model object
92
+		$this->_message_template_group_model = $message_template_group_model ?? EEM_Message_Template_Group::instance();
93
+		$this->_message_template_model       = $message_template_model ?? EEM_Message_Template::instance();
94
+		$this->_fields                       = $this->_messenger->get_template_fields();
95
+		$this->_contexts                     = $this->_message_type->get_contexts();
96
+	}
97
+
98
+
99
+	/**
100
+	 * Setup the _template_data property.
101
+	 * This method sets the _templates property array before templates are created.
102
+	 *
103
+	 * @param string $template_pack This corresponds to a template pack class reference which will contain information
104
+	 *                              about where to obtain the templates.
105
+	 *
106
+	 */
107
+	private function _set_templates(string $template_pack)
108
+	{
109
+		// get the corresponding template pack object (if present.  If not then we just load the default and add a
110
+		// notice).  The class name should be something like 'EE_Messages_Template_Pack_Default' where "default' would be
111
+		// the incoming template pack reference.
112
+		$class_name =
113
+			'EE_Messages_Template_Pack_' . str_replace(' ', '_', ucwords(str_replace('_', ' ', $template_pack)));
114
+
115
+		if (! class_exists($class_name)) {
116
+			EE_Error::add_error(
117
+				sprintf(
118
+					esc_html__(
119
+						'The template pack represented by a class corresponding to "%1$s" does not exist. Likely the autoloader for this class has the wrong path or the incoming reference is misspelled. The default template pack has been used to generate the templates instead.',
120
+						'event_espresso'
121
+					),
122
+					$class_name
123
+				),
124
+				__FILE__,
125
+				__FUNCTION__,
126
+				__LINE__
127
+			);
128
+			$class_name = 'EE_Messages_Template_Pack_Default';
129
+		}
130
+		/** @type EE_Messages_Template_Pack $template_pack */
131
+		$template_pack = new $class_name();
132
+
133
+		// get all the templates from the template pack.
134
+		$this->_templates = $template_pack->get_templates($this->_messenger, $this->_message_type);
135
+	}
136
+
137
+
138
+	/**
139
+	 * Return the contexts for the message type as cached on this instance.
140
+	 *
141
+	 * @return array
142
+	 */
143
+	public function get_contexts(): array
144
+	{
145
+		return $this->_contexts;
146
+	}
147
+
148
+
149
+	/**
150
+	 * public facing create new templates method
151
+	 *
152
+	 * @return array|false|int|string success array or false.
153
+	 * @throws EE_Error
154
+	 * @throws ReflectionException
155
+	 */
156
+	public function create_new_templates()
157
+	{
158
+		$template_pack = 'default';
159
+		// if we have the GRP_ID then let's use that to see if there is a set template pack and use that for the new templates.
160
+		if (! empty($this->_GRP_ID)) {
161
+			$message_template_group = $this->_message_template_group_model->get_one_by_ID($this->_GRP_ID);
162
+			$template_pack          = $message_template_group instanceof EE_Message_Template_Group
163
+				? $message_template_group->get_template_pack_name()
164
+				: 'default';
165
+			// we also need to reset the template variation to default
166
+			$message_template_group->set_template_pack_variation('default');
167
+		}
168
+		return $this->_create_new_templates($template_pack);
169
+	}
170
+
171
+
172
+	/**
173
+	 *  Handles creating new default templates.
174
+	 *
175
+	 * @param string $template_pack This corresponds to a template pack class reference
176
+	 *                              which will contain information about where to obtain the templates.
177
+	 * @return array|bool            success array or false.
178
+	 * @throws EE_Error
179
+	 * @throws ReflectionException
180
+	 */
181
+	protected function _create_new_templates(string $template_pack)
182
+	{
183
+		$this->_set_templates($template_pack);
184
+
185
+		// necessary properties are set, let's save the default templates
186
+		if (empty($this->_GRP_ID)) {
187
+			$main_template_data = [
188
+				'MTP_messenger'    => $this->_messenger->name,
189
+				'MTP_message_type' => $this->_message_type->name,
190
+				'MTP_is_override'  => 0,
191
+				'MTP_deleted'      => 0,
192
+				'MTP_is_global'    => 1,
193
+				'MTP_user_id'      => EEH_Activation::get_default_creator_id(),
194
+				'MTP_is_active'    => 1,
195
+			];
196
+			// let's insert the above and get our GRP_ID, then reset the template data array to just include the GRP_ID
197
+			$grp_id = $this->_message_template_group_model->insert($main_template_data);
198
+			if (empty($grp_id)) {
199
+				return $grp_id;
200
+			}
201
+			$this->_GRP_ID = $grp_id;
202
+		}
203
+
204
+		$template_data = ['GRP_ID' => $this->_GRP_ID];
205
+
206
+		foreach ($this->_contexts as $context => $details) {
207
+			foreach ($this->_fields as $field => $field_type) {
208
+				if ($field != 'extra') {
209
+					$template_data['MTP_context']        = $context;
210
+					$template_data['MTP_template_field'] = $field;
211
+					$template_data['MTP_content']        = $this->_templates[ $context ][ $field ];
212
+
213
+					$MTP = $this->_message_template_model->insert($template_data);
214
+					if (! $MTP) {
215
+						EE_Error::add_error(
216
+							sprintf(
217
+								esc_html__(
218
+									'There was an error in saving new template data for %1$s messenger, %2$s message type, %3$s context and %4$s template field.',
219
+									'event_espresso'
220
+								),
221
+								$this->_messenger->name,
222
+								$this->_message_type->name,
223
+								$context,
224
+								$field
225
+							),
226
+							__FILE__,
227
+							__FUNCTION__,
228
+							__LINE__
229
+						);
230
+						return false;
231
+					}
232
+				}
233
+			}
234
+		}
235
+
236
+		return [
237
+			'GRP_ID'      => $this->_GRP_ID,
238
+			'MTP_context' => key($this->_contexts),
239
+		];
240
+	}
241 241
 }
Please login to merge, or discard this patch.
core/libraries/messages/EE_Message_Resource_Manager.lib.php 2 patches
Indentation   +1137 added lines, -1137 removed lines patch added patch discarded remove patch
@@ -11,1141 +11,1141 @@
 block discarded – undo
11 11
  */
12 12
 class EE_Message_Resource_Manager
13 13
 {
14
-    /**
15
-     * This option in the database is used to keep a record of message types that have been activated for a messenger
16
-     * at some point in the history of the site.  It is utilized by the implementation of the 'force' flag in
17
-     * EE_Register_Message_Type.  The force flag is an indication of whether a message type should be activated by
18
-     * default when the message type is registered.  However, if a user has explicitly deactivated a message type, then
19
-     * the force flag is ignored.  The method by which the code knows whether to ignore this flag is via this option.
20
-     * Note, that this is NOT a historical record.  Its entirely possible for a message type to have been activated for
21
-     * a messenger and yet not have a record in this option.  This occurs when a message type is inactivated through an
22
-     * automated process (when an add-on registering the message type deactivates, or when some other code calls the
23
-     * EE_Registery_Message_Type::deregister method) and the related record(s) is(are) removed from this option to
24
-     * ensure the "force" flag is respected if that message type is later re-registered. This option should NOT be used
25
-     * to determine the current "active" state of a message type for a given messenger. The name of this option (and
26
-     * related methods/properties) is due to matching the original intended purpose for the option that got superseded
27
-     * by later behaviour requirements.
28
-     */
29
-    const HAS_ACTIVATED_MESSAGE_TYPE_FOR_MESSENGER_OPTION_NAME = 'ee_has_activated_messenger';
30
-
31
-    /**
32
-     * @type boolean $_initialized
33
-     */
34
-    protected $_initialized = false;
35
-
36
-    /**
37
-     * @type EE_Messenger_Collection $_messenger_collection_loader
38
-     */
39
-    protected $_messenger_collection_loader;
40
-
41
-    /**
42
-     * @type EE_Message_Type_Collection $_message_type_collection_loader
43
-     */
44
-    protected $_message_type_collection_loader;
45
-
46
-    /**
47
-     * @type EEM_Message_Template_Group $_message_template_group_model
48
-     */
49
-    protected $_message_template_group_model;
50
-
51
-    /**
52
-     * @type EE_messenger[]
53
-     */
54
-    protected $_installed_messengers = [];
55
-
56
-    /**
57
-     * @type EE_message_type[]
58
-     */
59
-    protected $_installed_message_types = [];
60
-
61
-    /**
62
-     * Array of active messengers.
63
-     * Format is this:
64
-     * array(
65
-     *      'messenger_name' => EE_messenger
66
-     * )
67
-     *
68
-     * @type EE_messenger[]
69
-     */
70
-    protected $_active_messengers = [];
71
-
72
-    /**
73
-     * Formatted array of active message types grouped per messenger.
74
-     * Format is this:
75
-     * array(
76
-     *      'messenger_name' => array(
77
-     *          'settings' => array(
78
-     *              '{messenger_name}-message_types' => array(
79
-     *                  'message_type_name' => array() //variable array of settings corresponding to message type.
80
-     *              )
81
-     *          )
82
-     *      )
83
-     * )
84
-     *
85
-     * @type array
86
-     */
87
-    protected $_active_message_types = [];
88
-
89
-
90
-    /**
91
-     * This holds the array of messengers and their corresponding message types that have
92
-     * been activated on a site at some point.  This is an important record that helps the messages system
93
-     * not accidentally reactivate something that was intentionally deactivated by a user.
94
-     *
95
-     * @see phpdocs on EE_Message_Resource_Manager::HAS_ACTIVATED_MESSAGE_TYPE_FOR_MESSENGER_OPTION_NAME for more
96
-     *      details.
97
-     * @type array
98
-     */
99
-    protected $_has_activated_messengers_and_message_types = [];
100
-
101
-    /**
102
-     * An array of unique message type contexts across all active message types.
103
-     * The array will be indexed by either 'slugs' or 'all'.
104
-     * The slugs index contains an array indexed by unique context slugs with the latest label representation for that
105
-     * slug. array(
106
-     *      'context_slug' => 'localized label for context obtained from latest message type in the loop'.
107
-     * );
108
-     * The all index returns an array in this format:
109
-     * array(
110
-     *      'message_type_name' => array(
111
-     *          'context_slug' => array(
112
-     *              'label' => 'localized label for context',
113
-     *              'description' => 'localized description for context'
114
-     *          )
115
-     *      )
116
-     * );
117
-     *
118
-     * @type array
119
-     */
120
-    protected $_contexts = [];
121
-
122
-
123
-    /**
124
-     * EE_Message_Resource_Manager constructor.
125
-     *
126
-     * @param \EE_Messenger_Collection_Loader    $Messenger_Collection_Loader
127
-     * @param \EE_Message_Type_Collection_Loader $Message_Type_Collection_Loader
128
-     * @param \EEM_Message_Template_Group        $Message_Template_Group_Model
129
-     */
130
-    public function __construct(
131
-        EE_Messenger_Collection_Loader $Messenger_Collection_Loader,
132
-        EE_Message_Type_Collection_Loader $Message_Type_Collection_Loader,
133
-        EEM_Message_Template_Group $Message_Template_Group_Model
134
-    ) {
135
-        $this->_messenger_collection_loader    = $Messenger_Collection_Loader;
136
-        $this->_message_type_collection_loader = $Message_Type_Collection_Loader;
137
-        $this->_message_template_group_model   = $Message_Template_Group_Model;
138
-    }
139
-
140
-
141
-    /**
142
-     * @return void
143
-     */
144
-    protected function _initialize_collections()
145
-    {
146
-        if ($this->_initialized) {
147
-            return;
148
-        }
149
-        $this->_initialized = true;
150
-        $this->_messenger_collection_loader->load_messengers_from_folder();
151
-        $this->_message_type_collection_loader->load_message_types_from_folder();
152
-        $this->get_has_activated_messengers_option(true);
153
-        $this->_set_active_messengers_and_message_types();
154
-    }
155
-
156
-
157
-    /**
158
-     * @return EE_Messenger_Collection
159
-     */
160
-    public function messenger_collection()
161
-    {
162
-        $this->_initialize_collections();
163
-        return $this->_messenger_collection_loader->messenger_collection();
164
-    }
165
-
166
-
167
-    /**
168
-     * @return EE_messenger[]
169
-     */
170
-    public function active_messengers()
171
-    {
172
-        $this->_initialize_collections();
173
-        return $this->_active_messengers;
174
-    }
175
-
176
-
177
-    /**
178
-     * @param string $messenger_name
179
-     * @return \EE_messenger
180
-     */
181
-    public function get_messenger($messenger_name)
182
-    {
183
-        return $this->messenger_collection()->get_by_info($messenger_name);
184
-    }
185
-
186
-
187
-    /**
188
-     * This returns the corresponding EE_messenger object for the given string if it is active.
189
-     *
190
-     * @param string $messenger
191
-     * @return EE_messenger | null
192
-     */
193
-    public function get_active_messenger($messenger)
194
-    {
195
-        $this->_initialize_collections();
196
-        return ! empty($this->_active_messengers[ $messenger ])
197
-            ? $this->_active_messengers[ $messenger ]
198
-            : null;
199
-    }
200
-
201
-
202
-    /**
203
-     * @return \EE_messenger[]
204
-     */
205
-    public function installed_messengers()
206
-    {
207
-        if (empty($this->_installed_messengers)) {
208
-            $this->_installed_messengers = [];
209
-            $this->messenger_collection()->rewind();
210
-            while ($this->messenger_collection()->valid()) {
211
-                $this->_installed_messengers[ $this->messenger_collection()->current()->name ] =
212
-                    $this->messenger_collection()->current();
213
-                $this->messenger_collection()->next();
214
-            }
215
-        }
216
-        return $this->_installed_messengers;
217
-    }
218
-
219
-
220
-    /**
221
-     * @param string $messenger_name
222
-     * @return \EE_messenger
223
-     * @throws EE_Error
224
-     */
225
-    public function valid_messenger($messenger_name)
226
-    {
227
-        $messenger = $this->get_messenger($messenger_name);
228
-        if ($messenger instanceof EE_messenger) {
229
-            return $messenger;
230
-        }
231
-        throw new EE_Error(
232
-            sprintf(
233
-                esc_html__('The "%1$s" messenger is either invalid or not installed', 'event_espresso'),
234
-                $messenger_name
235
-            )
236
-        );
237
-    }
238
-
239
-
240
-    /**
241
-     * @return EE_Message_Type_Collection
242
-     */
243
-    public function message_type_collection()
244
-    {
245
-        $this->_initialize_collections();
246
-        return $this->_message_type_collection_loader->message_type_collection();
247
-    }
248
-
249
-
250
-    /**
251
-     * @return array
252
-     */
253
-    public function active_message_types()
254
-    {
255
-        $this->_initialize_collections();
256
-        return $this->_active_message_types;
257
-    }
258
-
259
-
260
-    /**
261
-     * @param string $message_type_name
262
-     * @return \EE_message_type
263
-     */
264
-    public function get_message_type($message_type_name)
265
-    {
266
-        return $this->message_type_collection()->get_by_info($message_type_name);
267
-    }
268
-
269
-
270
-    /**
271
-     * This returns the EE_message_type from the active message types array ( if present );
272
-     *
273
-     * @param string $messenger_name
274
-     * @param string $message_type_name
275
-     * @return \EE_message_type|null
276
-     */
277
-    public function get_active_message_type_for_messenger($messenger_name, $message_type_name)
278
-    {
279
-        return $this->is_message_type_active_for_messenger($messenger_name, $message_type_name)
280
-            ? $this->get_message_type($message_type_name)
281
-            : null;
282
-    }
283
-
284
-
285
-    /**
286
-     * Returns whether the given message type is active for the given messenger.
287
-     *
288
-     * @param string $messenger_name
289
-     * @param string $message_type_name
290
-     * @return bool
291
-     */
292
-    public function is_message_type_active_for_messenger($messenger_name, $message_type_name)
293
-    {
294
-        $this->_initialize_collections();
295
-        return ! empty($this->_active_message_types[ $messenger_name ]['settings'][ $messenger_name . '-message_types' ][ $message_type_name ]);
296
-    }
297
-
298
-
299
-    /**
300
-     * Returns whether the given messenger is active.
301
-     *
302
-     * @param string $messenger_name the name of the messenger to check if active.
303
-     * @return bool
304
-     */
305
-    public function is_messenger_active($messenger_name)
306
-    {
307
-        $this->_initialize_collections();
308
-        return ! empty($this->_active_message_types[ $messenger_name ]);
309
-    }
310
-
311
-
312
-    /**
313
-     * This returns any settings that might be on a message type for a messenger
314
-     *
315
-     * @param string $messenger_name    The slug of the messenger
316
-     * @param string $message_type_name The slug of the message type getting the settings for.
317
-     * @return array
318
-     */
319
-    public function get_message_type_settings_for_messenger($messenger_name, $message_type_name)
320
-    {
321
-        $settings = [];
322
-        if ($this->is_message_type_active_for_messenger($messenger_name, $message_type_name)) {
323
-            $settings =
324
-                isset($this->_active_message_types[ $messenger_name ]['settings'][ $messenger_name . '-message_types' ][ $message_type_name ]['settings'])
325
-                    ? $this->_active_message_types[ $messenger_name ]['settings'][ $messenger_name . '-message_types' ][ $message_type_name ]['settings']
326
-                    : [];
327
-        }
328
-        return $settings;
329
-    }
330
-
331
-
332
-    /**
333
-     * Returns whether the given messenger name has active message types on it.
334
-     * Infers whether the messenger is active or not as well.
335
-     *
336
-     * @param string $messenger_name
337
-     * @return bool
338
-     */
339
-    public function messenger_has_active_message_types($messenger_name)
340
-    {
341
-        $this->_initialize_collections();
342
-        return
343
-            ! empty($this->_active_message_types[ $messenger_name ])
344
-            && ! empty($this->_active_message_types[ $messenger_name ]['settings'][ $messenger_name . '-message_types' ]);
345
-    }
346
-
347
-
348
-    /**
349
-     * This checks the _active_message_types property for any active message types
350
-     * that are present for the given messenger and returns them.
351
-     *
352
-     * @param string $messenger_name The messenger being checked
353
-     * @return EE_message_type[]|array    (empty array if no active_message_types)
354
-     * @since 4.9.0
355
-     */
356
-    public function get_active_message_types_for_messenger($messenger_name)
357
-    {
358
-        $message_types = [];
359
-        if (! $this->messenger_has_active_message_types($messenger_name)) {
360
-            return $message_types;
361
-        }
362
-        $installed_message_types = $this->installed_message_types();
363
-        foreach ($installed_message_types as $message_type_name => $message_type) {
364
-            if ($this->is_message_type_active_for_messenger($messenger_name, $message_type_name)) {
365
-                $message_types[ $message_type_name ] = $message_type;
366
-            }
367
-        }
368
-        return $message_types;
369
-    }
370
-
371
-
372
-    /**
373
-     * This does NOT return the _active_message_types property but
374
-     * simply returns an array of active message type names from that property.
375
-     * (The _active_message_types property is indexed by messenger and active message_types per messenger).
376
-     *
377
-     * @return array message_type references (string)
378
-     */
379
-    public function list_of_active_message_types()
380
-    {
381
-        $active_message_type_names = [];
382
-        $this->_initialize_collections();
383
-        foreach ($this->_active_message_types as $messenger => $messenger_settings) {
384
-            if (! isset($messenger_settings['settings'][ $messenger . '-message_types' ])) {
385
-                continue;
386
-            }
387
-            foreach ($messenger_settings['settings'][ $messenger . '-message_types' ] as $message_type_name => $message_type_config) {
388
-                if (! in_array($message_type_name, $active_message_type_names)) {
389
-                    $active_message_type_names[] = $message_type_name;
390
-                }
391
-            }
392
-        }
393
-        return $active_message_type_names;
394
-    }
395
-
396
-
397
-    /**
398
-     * Same as list_of_active_message_types() except this returns actual EE_message_type objects
399
-     *
400
-     * @return \EE_message_type[]
401
-     * @since 4.9.0
402
-     */
403
-    public function get_active_message_type_objects()
404
-    {
405
-        $active_message_types      = [];
406
-        $installed_message_types   = $this->installed_message_types();
407
-        $active_message_type_names = $this->list_of_active_message_types();
408
-        foreach ($active_message_type_names as $active_message_type_name) {
409
-            if (isset($installed_message_types[ $active_message_type_name ])) {
410
-                $active_message_types[ $active_message_type_name ] =
411
-                    $installed_message_types[ $active_message_type_name ];
412
-            }
413
-        }
414
-        return $active_message_types;
415
-    }
416
-
417
-
418
-    /**
419
-     * @return \EE_message_type[]
420
-     */
421
-    public function installed_message_types()
422
-    {
423
-        if (empty($this->_installed_message_types)) {
424
-            $this->message_type_collection()->rewind();
425
-            while ($this->message_type_collection()->valid()) {
426
-                $this->_installed_message_types[ $this->message_type_collection()->current()->name ] =
427
-                    $this->message_type_collection()->current();
428
-                $this->message_type_collection()->next();
429
-            }
430
-        }
431
-        return $this->_installed_message_types;
432
-    }
433
-
434
-
435
-    /**
436
-     * @param string $message_type_name
437
-     * @return \EE_message_type
438
-     * @throws EE_Error
439
-     */
440
-    public function valid_message_type($message_type_name)
441
-    {
442
-        $message_type = $this->get_message_type($message_type_name);
443
-        if ($message_type instanceof EE_message_type) {
444
-            return $message_type;
445
-        }
446
-        throw new EE_Error(
447
-            sprintf(
448
-                esc_html__('The "%1$s" message type is either invalid or not installed', 'event_espresso'),
449
-                $message_type_name
450
-            )
451
-        );
452
-    }
453
-
454
-
455
-    /**
456
-     * valid_message_type_for_messenger
457
-     *
458
-     * @param EE_messenger $messenger
459
-     * @param string       $message_type_name
460
-     * @return boolean
461
-     * @throws EE_Error
462
-     */
463
-    public function valid_message_type_for_messenger(EE_messenger $messenger, $message_type_name)
464
-    {
465
-        $valid_message_types = $messenger->get_valid_message_types();
466
-        if (! in_array($message_type_name, $valid_message_types)) {
467
-            throw new EE_Error(
468
-                sprintf(
469
-                    esc_html__(
470
-                        'The message type (%1$s) sent to "%2$s" is not valid for the "%3$s" messenger.  Double-check the spelling and verify that message type has been registered as a valid type with the messenger.',
471
-                        'event_espresso'
472
-                    ),
473
-                    $message_type_name,
474
-                    __METHOD__,
475
-                    $messenger->name
476
-                )
477
-            );
478
-        }
479
-        return true;
480
-    }
481
-
482
-
483
-    /**
484
-     * Used to return active messengers array stored in the wp options table.
485
-     * If no value is present in the option then an empty array is returned.
486
-     *
487
-     * @param bool $reset       If true then we ignore whether the option is cached on the _active_message_types
488
-     *                          property and pull directly from the db.  Otherwise whatever is currently on the
489
-     *                          $_active_message_types property is pulled.
490
-     * @return array
491
-     */
492
-    public function get_active_messengers_option($reset = false)
493
-    {
494
-        if ($reset) {
495
-            $this->_active_message_types = get_option('ee_active_messengers', []);
496
-        }
497
-        return $this->_active_message_types;
498
-    }
499
-
500
-
501
-    /**
502
-     * Used to update the active messengers array stored in the wp options table.
503
-     *
504
-     * @param array $active_messenger_settings Incoming data to save.  If empty, then the internal cached property
505
-     *                                         representing this data is used.
506
-     * @return bool FALSE if not updated, TRUE if updated.
507
-     */
508
-    public function update_active_messengers_option($active_messenger_settings = [])
509
-    {
510
-        $active_messenger_settings = empty($active_messenger_settings)
511
-            ? $this->_active_message_types
512
-            : $active_messenger_settings;
513
-        // make sure _active_message_types is updated (this is the internal cache for the settings).
514
-        $this->_active_message_types = $active_messenger_settings;
515
-        return update_option('ee_active_messengers', $active_messenger_settings);
516
-    }
517
-
518
-
519
-    /**
520
-     * Used to return has activated message types for messengers array stored in the wp options table.
521
-     * If no value is present in the option then an empty array is returned.
522
-     * The value is cached on the $_has_activated_messengers_and_message_types property for future calls.
523
-     *
524
-     * @param bool $reset Used to indicate that any cached value should be ignored.
525
-     * @return array
526
-     * @see phpdocs on EE_Message_Resource_Manager::HAS_ACTIVATED_MESSAGE_TYPE_FOR_MESSENGER_OPTION_NAME for more
527
-     *                    details.
528
-     */
529
-    public function get_has_activated_messengers_option($reset = false)
530
-    {
531
-        if ($reset || empty($this->_has_activated_messengers_and_message_types)) {
532
-            $this->_has_activated_messengers_and_message_types =
533
-                get_option(self::HAS_ACTIVATED_MESSAGE_TYPE_FOR_MESSENGER_OPTION_NAME, []);
534
-        }
535
-        return $this->_has_activated_messengers_and_message_types;
536
-    }
537
-
538
-
539
-    /**
540
-     * Used to update the has activated option in the db.
541
-     *
542
-     * @param array $has_activated_messengers Incoming data to save.  If empty, then the internal cached property
543
-     *                                        representing this data is used.
544
-     * @return bool FALSE if not updated, TRUE if updated.
545
-     * @see phpdocs on EE_Message_Resource_Manager::HAS_ACTIVATED_MESSAGE_TYPE_FOR_MESSENGER_OPTION_NAME for more
546
-     *                                        details.
547
-     */
548
-    public function update_has_activated_messengers_option($has_activated_messengers = [])
549
-    {
550
-        // make sure the option has been retrieved from first so we don't overwrite it accidentally.
551
-        if (empty($has_activated_messengers) && empty($this->_has_activated_messengers_and_message_types)) {
552
-            $this->get_has_activated_messengers_option();
553
-        }
554
-        $has_activated_messengers = empty($has_activated_messengers)
555
-            ? $this->_has_activated_messengers_and_message_types
556
-            : $has_activated_messengers;
557
-        return update_option(self::HAS_ACTIVATED_MESSAGE_TYPE_FOR_MESSENGER_OPTION_NAME, $has_activated_messengers);
558
-    }
559
-
560
-
561
-    /**
562
-     * wrapper for _set_active_messengers_and_message_types()
563
-     */
564
-    public function reset_active_messengers_and_message_types()
565
-    {
566
-        $this->_set_active_messengers_and_message_types();
567
-    }
568
-
569
-
570
-    /**
571
-     * Generate list of active messengers and message types from collection.
572
-     * This sets up the active messengers from what is present in the database.
573
-     */
574
-    protected function _set_active_messengers_and_message_types()
575
-    {
576
-        // echo "\n\n " . __LINE__ . ") " . __METHOD__ . "() \n";
577
-        // list of activated messengers as set via the admin
578
-        // note calling `get_active_messengers_options` also initializes the _active_message_types property.
579
-        $this->get_active_messengers_option(true);
580
-        $this->ensure_messengers_are_active([], false, true);
581
-        $this->update_active_messengers_option();
582
-        $this->update_has_activated_messengers_option();
583
-    }
584
-
585
-
586
-    /**
587
-     * Ensures that the specified messenger is currently active.
588
-     * If not, activates it and its default message types.
589
-     *
590
-     * @param string $messenger_name
591
-     * @param bool   $update_option Whether to update the option in the db or not.
592
-     * @return boolean true if either already active or successfully activated.
593
-     */
594
-    public function ensure_messenger_is_active($messenger_name, $update_option = true)
595
-    {
596
-        if (! isset($this->_active_messengers[ $messenger_name ])) {
597
-            try {
598
-                $this->activate_messenger($messenger_name, [], $update_option);
599
-            } catch (EE_Error $e) {
600
-                EE_Error::add_error(
601
-                    $e->getMessage(),
602
-                    __FILE__,
603
-                    __FUNCTION__,
604
-                    __LINE__
605
-                );
606
-                return false;
607
-            }
608
-        }
609
-        return true;
610
-    }
611
-
612
-
613
-    /**
614
-     * This ensures the given array of messenger names is active in the system.
615
-     * Note, this method will not activate any NEW message types for the messenger when it is called. Instead,
616
-     * it will automatically activate the default message types for the messenger if its not active.
617
-     *
618
-     * @param array $messenger_names  Array of messenger names for messengers to be activated.  If an empty array
619
-     *                                (default) then will attempt to set the active messengers from the
620
-     *                                activated_messengers option
621
-     *                                (stored in $_active_message_types property).
622
-     * @param bool  $update_option    Whether to update the related active messengers option.
623
-     * @param bool  $verify           Whether to verify the messengers are installed before activating. Note if this is
624
-     *                                set to true and a messenger is indicated as active, but is NOT installed, then it
625
-     *                                will automatically be deactivated.
626
-     */
627
-    public function ensure_messengers_are_active($messenger_names = [], $update_option = true, $verify = false)
628
-    {
629
-        $messenger_names = empty($messenger_names)
630
-            ? array_keys($this->_active_message_types)
631
-            : $messenger_names;
632
-
633
-        $not_installed = [];
634
-        foreach ($messenger_names as $messenger_name) {
635
-            if ($verify && ! $this->messenger_collection()->has_by_name($messenger_name)) {
636
-                $not_installed[] = $messenger_name;
637
-                $this->deactivate_messenger($messenger_name);
638
-                continue;
639
-            }
640
-            $this->ensure_messenger_is_active($messenger_name, $update_option);
641
-        }
642
-
643
-        if (! empty($not_installed)) {
644
-            EE_Error::add_error(
645
-                sprintf(
646
-                    esc_html__(
647
-                        'The following messengers are either not installed or are invalid:%1$s %2$s',
648
-                        'event_espresso'
649
-                    ),
650
-                    '<br />',
651
-                    implode(', ', $not_installed)
652
-                ),
653
-                __FILE__,
654
-                __FUNCTION__,
655
-                __LINE__
656
-            );
657
-        }
658
-    }
659
-
660
-
661
-    /**
662
-     * Ensures that the specified message type for the given messenger is currently active, if not activates it.
663
-     * This ALSO ensures that the given messenger is active as well!
664
-     *
665
-     * @param string $message_type_name message type name.
666
-     * @param        $messenger_name
667
-     * @param bool   $update_option     Whether to update the option in the db or not.
668
-     * @return bool  Returns true if already is active or if was activated successfully.
669
-     * @throws EE_Error
670
-     */
671
-    public function ensure_message_type_is_active($message_type_name, $messenger_name, $update_option = true)
672
-    {
673
-        // grab the messenger to work with.
674
-        $messenger = $this->valid_messenger($messenger_name);
675
-        if ($this->valid_message_type_for_messenger($messenger, $message_type_name)) {
676
-            // ensure messenger is active (that's an inherent coupling between active message types and the
677
-            // messenger they are being activated for.
678
-            try {
679
-                if (! $this->is_message_type_active_for_messenger($messenger_name, $message_type_name)) {
680
-                    // all is good so let's just get it active
681
-                    $this->activate_messenger($messenger, [$message_type_name], $update_option);
682
-                }
683
-            } catch (EE_Error $e) {
684
-                EE_Error::add_error(
685
-                    $e->getMessage(),
686
-                    __FILE__,
687
-                    __FUNCTION__,
688
-                    __LINE__
689
-                );
690
-                return false;
691
-            }
692
-        }
693
-        return true;
694
-    }
695
-
696
-
697
-    /**
698
-     * This is a wrapper for `ensure_message_type_is_active` that will handle ensuring multiple message types for a
699
-     * messenger are active in one go.
700
-     *
701
-     * @param array  $message_type_names Array of message type names to ensure are active.
702
-     * @param string $messenger_name     The name of the messenger that the message types are to be activated on.
703
-     * @param bool   $update_option      Whether to persist the activation to the database or not (default true).
704
-     */
705
-    public function ensure_message_types_are_active($message_type_names, $messenger_name, $update_option = true)
706
-    {
707
-        $message_type_names = (array) $message_type_names;
708
-        foreach ($message_type_names as $message_type_name) {
709
-            // note, intentionally not updating option here because we're in a loop.
710
-            // We'll follow the instructions of the incoming $update_option argument after the loop.
711
-            $this->ensure_message_type_is_active($message_type_name, $messenger_name, false);
712
-        }
713
-        if ($update_option) {
714
-            $this->update_active_messengers_option();
715
-            $this->update_has_activated_messengers_option();
716
-        }
717
-    }
718
-
719
-
720
-    /**
721
-     * Activates the specified messenger.
722
-     *
723
-     * @param EE_messenger|string $messenger          Instantiated EE_messenger OR messenger name if not already loaded!
724
-     * @param array               $message_type_names An array of message type names to activate with this messenger.
725
-     *                                                If included we do NOT setup the default message types
726
-     *                                                (assuming they are already setup.)
727
-     * @param bool                $update_active_messengers_option
728
-     * @return array of generated templates
729
-     * @throws EE_Error
730
-     */
731
-    public function activate_messenger(
732
-        $messenger,
733
-        $message_type_names = [],
734
-        $update_active_messengers_option = true
735
-    ) {
736
-        $templates = [];
737
-        // grab the messenger to work with.
738
-        $messenger = $messenger instanceof EE_messenger
739
-            ? $messenger
740
-            : $this->messenger_collection()->get_by_info($messenger);
741
-        // it's inactive. Activate it.
742
-        if ($messenger instanceof EE_messenger) {
743
-            $this->_active_messengers[ $messenger->name ] = $messenger;
744
-            // activate incoming message types set to be activated with messenger.
745
-            $message_type_names = $this->_activate_message_types($messenger, $message_type_names);
746
-            // setup any initial settings for the messenger if necessary.
747
-            $this->add_settings_for_messenger($messenger->name);
748
-            if ($update_active_messengers_option) {
749
-                $this->update_active_messengers_option();
750
-                $this->update_has_activated_messengers_option();
751
-            }
752
-            // generate new templates if necessary and ensure all related templates that are already in the database are
753
-            // marked active.  Note, this will also deactivate a message type for a messenger if the template
754
-            // cannot be successfully created during its attempt (only happens for global template attempts).
755
-            if (! empty($message_type_names)) {
756
-                $templates = EEH_MSG_Template::generate_new_templates($messenger->name, $message_type_names, 0, true);
757
-                EEH_MSG_Template::update_to_active([$messenger->name], $message_type_names);
758
-            }
759
-        }
760
-        return $templates;
761
-    }
762
-
763
-
764
-    /**
765
-     * Activates given message types for the given EE_messenger object.
766
-     * Note: (very important) This method does not persist the activation to the database.
767
-     * See code implementing this method in this class for examples of how to persist.
768
-     *
769
-     * @param \EE_messenger $messenger
770
-     * @param array         $message_type_names
771
-     * @return array
772
-     */
773
-    protected function _activate_message_types(EE_messenger $messenger, $message_type_names = [])
774
-    {
775
-        // If $message_type_names is empty, AND $this->_active_message_types is empty, then that means
776
-        // things have never been initialized (which should happen on EEH_Activation::generate_message_templates).
777
-        // So ONLY then do we need to actually grab defaults and cycle through them.  Otherwise we
778
-        // only override _active_message_types when an explicit array of $message_type_names has been provided.
779
-        $message_type_names = empty($message_type_names) && ! isset($this->_active_message_types[ $messenger->name ])
780
-            ? $messenger->get_default_message_types()
781
-            : (array) $message_type_names;
782
-
783
-        // now we ALWAYS need to make sure that the messenger is active for the message types we're activating!
784
-        if (! isset($this->_active_message_types[ $messenger->name ])) {
785
-            $this->_active_message_types[ $messenger->name ]['settings'] = [];
786
-        }
787
-
788
-        if ($message_type_names) {
789
-            // cycle thru message types
790
-            foreach ($message_type_names as $message_type_name) {
791
-                // only register the message type as active IF it isn't already active
792
-                // and if its actually installed.
793
-                if (
794
-                    ! $this->is_message_type_active_for_messenger($messenger->name, $message_type_name)
795
-                ) {
796
-                    $this->add_settings_for_message_type($messenger->name, $message_type_name);
797
-                    $this->_set_messenger_has_activated_message_type(
798
-                        $messenger,
799
-                        $message_type_name
800
-                    );
801
-                }
802
-            }
803
-        }
804
-        return $message_type_names;
805
-    }
806
-
807
-
808
-    /**
809
-     * add_settings_for_message_type
810
-     * NOTE This does NOT automatically persist any settings to the db.  Client code should call
811
-     * $this->update_active_messengers_option to persist.
812
-     *
813
-     * @param string $messenger_name    The name of the messenger adding the settings for
814
-     * @param string $message_type_name The name of the message type adding the settings for
815
-     * @param array  $new_settings      Any new settings being set for the message type and messenger
816
-     */
817
-    public function add_settings_for_message_type($messenger_name, $message_type_name, $new_settings = [])
818
-    {
819
-        // get installed message type from collection
820
-        $message_type      = $this->message_type_collection()->get_by_info($message_type_name);
821
-        $existing_settings = $this->get_message_type_settings_for_messenger($messenger_name, $message_type_name);
822
-        // we need to setup any initial settings for message types
823
-        if ($message_type instanceof EE_message_type) {
824
-            $default_settings = $message_type->get_admin_settings_fields();
825
-            foreach ($default_settings as $field => $values) {
826
-                if (isset($new_settings[ $field ])) {
827
-                    $existing_settings[ $field ] = $new_settings[ $field ];
828
-                    continue;
829
-                }
830
-                if (! isset($existing_settings[ $field ])) {
831
-                    $existing_settings[ $field ] = $values['default'];
832
-                }
833
-            }
834
-        }
835
-        $this->_active_message_types[ $messenger_name ]['settings'][ $messenger_name . '-message_types' ][ $message_type_name ]['settings'] =
836
-            $existing_settings;
837
-    }
838
-
839
-
840
-    /**
841
-     * Updates the internal cached _has_activated_messengers_and_message_types property with the given messenger
842
-     * and message type.
843
-     *
844
-     * @param \EE_messenger $messenger
845
-     * @param string        $message_type_name
846
-     * @see    phpdocs on EE_Message_Resource_Manager::HAS_ACTIVATED_MESSAGE_TYPE_FOR_MESSENGER_OPTION_NAME for more
847
-     *         details.
848
-     * @access protected
849
-     */
850
-    protected function _set_messenger_has_activated_message_type(EE_messenger $messenger, $message_type_name)
851
-    {
852
-        // if _has_activated_messengers_and_message_types is empty then lets ensure its initialized
853
-        if (empty($this->_has_activated_messengers_and_message_types)) {
854
-            $this->get_has_activated_messengers_option();
855
-        }
856
-
857
-        // make sure this messenger has a record in the has_activated array
858
-        if (! isset($this->_has_activated_messengers_and_message_types[ $messenger->name ])) {
859
-            $this->_has_activated_messengers_and_message_types[ $messenger->name ] = [];
860
-        }
861
-        // check if message type has already been added
862
-        if (! in_array($message_type_name, $this->_has_activated_messengers_and_message_types[ $messenger->name ])) {
863
-            $this->_has_activated_messengers_and_message_types[ $messenger->name ][] = $message_type_name;
864
-        }
865
-    }
866
-
867
-
868
-    /**
869
-     * add_settings_for_messenger
870
-     * NOTE This does NOT automatically persist any settings to the db.  Client code should call
871
-     * $this->update_active_messengers_option to persist.
872
-     *
873
-     * @param string $messenger_name The name of the messenger the settings is being added for.
874
-     * @param array  $new_settings   An array of settings to update the existing settings.
875
-     */
876
-    public function add_settings_for_messenger($messenger_name, $new_settings = [])
877
-    {
878
-        $messenger = $this->get_messenger($messenger_name);
879
-        if ($messenger instanceof EE_messenger) {
880
-            $msgr_settings = $messenger->get_admin_settings_fields();
881
-            if (! empty($msgr_settings)) {
882
-                foreach ($msgr_settings as $field => $value) {
883
-                    // is there a new setting for this?
884
-                    if (isset($new_settings[ $field ])) {
885
-                        $this->_active_message_types[ $messenger->name ]['settings'][ $field ] =
886
-                            $new_settings[ $field ];
887
-                        continue;
888
-                    }
889
-                    // only set the default if it isn't already set.
890
-                    if (! isset($this->_active_message_types[ $messenger->name ]['settings'][ $field ])) {
891
-                        $this->_active_message_types[ $messenger->name ]['settings'][ $field ] = $value;
892
-                    }
893
-                }
894
-            }
895
-        }
896
-    }
897
-
898
-
899
-    /**
900
-     * deactivate_messenger
901
-     *
902
-     * @param string|EE_messenger $messenger_name name of messenger
903
-     * @return void
904
-     */
905
-    public function deactivate_messenger($messenger_name)
906
-    {
907
-        $this->_initialize_collections();
908
-        if ($messenger_name instanceof EE_messenger) {
909
-            $messenger_name = $messenger_name->name;
910
-        }
911
-        unset($this->_active_messengers[ $messenger_name ]);
912
-        unset($this->_active_message_types[ $messenger_name ]);
913
-        $this->_message_template_group_model->deactivate_message_template_groups_for($messenger_name);
914
-        $this->update_active_messengers_option();
915
-    }
916
-
917
-
918
-    /**
919
-     * Deactivates a message type (note this will deactivate across all messenger's it is active on.
920
-     *
921
-     * @param string $message_type_name      name of message type being deactivated
922
-     * @param bool   $set_has_active_record  By default we always record the has_active record when deactivating a
923
-     *                                       message type.  However, this can be overridden if we don't want this set
924
-     *                                       (usually when this is called as a part of deregistration of a custom
925
-     *                                       message type)
926
-     */
927
-    public function deactivate_message_type($message_type_name, $set_has_active_record = true)
928
-    {
929
-        $this->_initialize_collections();
930
-        if ($message_type_name instanceof EE_message_type) {
931
-            $message_type_name = $message_type_name->name;
932
-        }
933
-        foreach ($this->_active_message_types as $messenger_name => $settings) {
934
-            unset(
935
-                $this->_active_message_types[ $messenger_name ]['settings'][ $messenger_name . '-message_types' ][ $message_type_name ]
936
-            );
937
-
938
-            // we always record (even on deactivation) that a message type has been activated because there should at
939
-            // least be a record in the "has_activated" option that it WAS active at one point.
940
-            if ($set_has_active_record) {
941
-                $messenger = $this->get_messenger($messenger_name);
942
-                $this->_set_messenger_has_activated_message_type($messenger, $message_type_name);
943
-            }
944
-        }
945
-        $this->_message_template_group_model->deactivate_message_template_groups_for('', $message_type_name);
946
-        $this->update_active_messengers_option();
947
-        $this->update_has_activated_messengers_option();
948
-    }
949
-
950
-
951
-    /**
952
-     * Deactivates a message type for a specific messenger as opposed to all messengers.
953
-     *
954
-     * @param string $message_type_name Name of message type being deactivated.
955
-     * @param string $messenger_name    Name of messenger the message type is being deactivated for.
956
-     */
957
-    public function deactivate_message_type_for_messenger($message_type_name, $messenger_name)
958
-    {
959
-        $this->_initialize_collections();
960
-        if ($this->is_message_type_active_for_messenger($messenger_name, $message_type_name)) {
961
-            unset($this->_active_message_types[ $messenger_name ]['settings'][ $messenger_name . '-message_types' ][ $message_type_name ]);
962
-        }
963
-        $this->_message_template_group_model->deactivate_message_template_groups_for(
964
-            [$messenger_name],
965
-            [$message_type_name]
966
-        );
967
-        $this->update_active_messengers_option();
968
-    }
969
-
970
-
971
-    /**
972
-     * Used to verify if a message can be sent for the given messenger and message type
973
-     * and that it is a generating messenger (used for generating message templates).
974
-     *
975
-     * @param EE_messenger    $messenger    messenger used in trigger
976
-     * @param EE_message_type $message_type message type used in trigger
977
-     * @return bool true is a generating messenger and can be sent OR FALSE meaning cannot send.
978
-     */
979
-    public function is_generating_messenger_and_active(EE_messenger $messenger, EE_message_type $message_type)
980
-    {
981
-        // get the $messengers the message type says it can be used with.
982
-        foreach ($message_type->with_messengers() as $generating_messenger => $secondary_messengers) {
983
-            if (
984
-                $messenger->name === $generating_messenger
985
-                && $this->is_message_type_active_for_messenger($messenger->name, $message_type->name)
986
-            ) {
987
-                return true;
988
-            }
989
-        }
990
-        return false;
991
-    }
992
-
993
-
994
-    /**
995
-     * This returns all the contexts that are registered by all message types.
996
-     * If $slugs_only is true,
997
-     * then just an array indexed by unique context slugs with the latest label representation for that slug.
998
-     * array(
999
-     *      'context_slug' => 'localized label for context obtained from latest message type in the loop'.
1000
-     * );
1001
-     * If $slugs_only is false, then the format is:
1002
-     * array(
1003
-     *      'message_type_name' => array(
1004
-     *          'context_slug' => array(
1005
-     *              'label' => 'localized label for context',
1006
-     *              'description' => 'localized description for context'
1007
-     *          )
1008
-     *      )
1009
-     * );
1010
-     * Keep in mind that although different message types may share the same context slugs,
1011
-     * it is possible that the context is described differently by the message type.
1012
-     *
1013
-     * @param bool $slugs_only   Whether to return an array of just slugs and labels (true)
1014
-     *                           or all contexts indexed by message type.
1015
-     * @return array
1016
-     * @since 4.9.0
1017
-     */
1018
-    public function get_all_contexts($slugs_only = true)
1019
-    {
1020
-        $key = $slugs_only
1021
-            ? 'slugs'
1022
-            : 'all';
1023
-        // check if contexts has been setup yet.
1024
-        if (empty($this->_contexts[ $key ])) {
1025
-            // So let's get all active message type objects and loop through to get all unique contexts
1026
-            foreach ($this->get_active_message_type_objects() as $message_type) {
1027
-                if ($message_type instanceof EE_message_type) {
1028
-                    $message_type_contexts = $message_type->get_contexts();
1029
-                    if ($slugs_only) {
1030
-                        foreach ($message_type_contexts as $context => $context_details) {
1031
-                            $this->_contexts[ $key ][ $context ] = $context_details['label'];
1032
-                        }
1033
-                    } else {
1034
-                        $this->_contexts[ $key ][ $message_type->name ] = $message_type_contexts;
1035
-                    }
1036
-                }
1037
-            }
1038
-        }
1039
-        return ! empty($this->_contexts[ $key ])
1040
-            ? $this->_contexts[ $key ]
1041
-            : [];
1042
-    }
1043
-
1044
-
1045
-    /**
1046
-     * This checks the internal record of what message types are considered "active" and verifies that
1047
-     * there is an installed class definition for that message type.  If the active message type does not have a
1048
-     * corresponding accessible message type class then it will be deactivated from all messengers it is active on and
1049
-     * any related message templates will be inactivated as well.
1050
-     *
1051
-     * @return bool   true means all active message types are valid, false means at least one message type was
1052
-     *                deactivated.
1053
-     */
1054
-    public function validate_active_message_types_are_installed()
1055
-    {
1056
-        $list_of_active_message_type_names = $this->list_of_active_message_types();
1057
-        $installed_message_types           = $this->installed_message_types();
1058
-        $all_message_types_valid           = true;
1059
-        // loop through list of active message types and verify they are installed.
1060
-        foreach ($list_of_active_message_type_names as $message_type_name) {
1061
-            if (! isset($installed_message_types[ $message_type_name ])) {
1062
-                $this->remove_message_type_has_been_activated_from_all_messengers(
1063
-                    $message_type_name,
1064
-                    true
1065
-                );
1066
-                $this->deactivate_message_type($message_type_name, false);
1067
-                $all_message_types_valid = false;
1068
-            }
1069
-        }
1070
-        return $all_message_types_valid;
1071
-    }
1072
-
1073
-
1074
-    /**
1075
-     * This method checks the `ee_has_activated_messenger` option to see if the message type has ever been
1076
-     * activated for the given messenger.  This can be called by client code on plugin updates etc to determine whether
1077
-     * to attempt automatically reactivating message types that should be activated by default or not.
1078
-     *
1079
-     * @param $message_type_name
1080
-     * @param $messenger_name
1081
-     * @return bool
1082
-     * @see phpdocs on EE_Message_Resource_Manager::HAS_ACTIVATED_MESSAGE_TYPE_FOR_MESSENGER_OPTION_NAME for more
1083
-     *      details.
1084
-     */
1085
-    public function has_message_type_been_activated_for_messenger($message_type_name, $messenger_name)
1086
-    {
1087
-        $has_activated = $this->get_has_activated_messengers_option();
1088
-        return isset($has_activated[ $messenger_name ])
1089
-               && in_array($message_type_name, $has_activated[ $messenger_name ]);
1090
-    }
1091
-
1092
-
1093
-    /**
1094
-     * This method unsets a message type from the given messenger has activated option.
1095
-     *
1096
-     * @param string $message_type_name
1097
-     * @param string $messenger_name
1098
-     * @param bool   $consider_current_state  Whether to consider whether the  message type is currently active or not.
1099
-     *                                        If it is currently active, then remove.  Otherwise leave it alone.
1100
-     * @see phpdocs on EE_Message_Resource_Manager::HAS_ACTIVATED_MESSAGE_TYPE_FOR_MESSENGER_OPTION_NAME for more
1101
-     *                                        details.
1102
-     */
1103
-    public function remove_message_type_has_been_activated_for_messenger(
1104
-        $message_type_name,
1105
-        $messenger_name,
1106
-        $consider_current_state = false
1107
-    ) {
1108
-        if (
1109
-            $consider_current_state
1110
-            && ! $this->is_message_type_active_for_messenger($messenger_name, $message_type_name)
1111
-        ) {
1112
-            // when consider current state is true, this means we don't want to change anything on the "has_activated"
1113
-            // record if the message type is currently active for this messenger.  This is used when we want to retain
1114
-            // the record for user initiated inactivations of the message type.
1115
-            return;
1116
-        }
1117
-        $has_activated        = $this->get_has_activated_messengers_option();
1118
-        $key_for_message_type = isset($has_activated[ $messenger_name ])
1119
-            ? array_search($message_type_name, $has_activated[ $messenger_name ], true)
1120
-            : false;
1121
-        if ($key_for_message_type !== false) {
1122
-            unset($has_activated[ $messenger_name ][ $key_for_message_type ]);
1123
-            $this->update_has_activated_messengers_option($has_activated);
1124
-            // reset the internal cached property
1125
-            $this->get_has_activated_messengers_option(true);
1126
-        }
1127
-    }
1128
-
1129
-
1130
-    /**
1131
-     * Removes a message type active record from all messengers it is attached to.
1132
-     *
1133
-     * @param      $message_type_name
1134
-     * @param bool $consider_current_state  Whether to consider whether the  message type is currently active or not.
1135
-     *                                      If it is currently active, then remove.  Otherwise leave it alone.
1136
-     * @see phpdocs on EE_Message_Resource_Manager::HAS_ACTIVATED_MESSAGE_TYPE_FOR_MESSENGER_OPTION_NAME for more
1137
-     *                                      details.
1138
-     */
1139
-    public function remove_message_type_has_been_activated_from_all_messengers(
1140
-        $message_type_name,
1141
-        $consider_current_state = false
1142
-    ) {
1143
-        foreach (array_keys($this->get_has_activated_messengers_option()) as $messenger_name) {
1144
-            $this->remove_message_type_has_been_activated_for_messenger(
1145
-                $message_type_name,
1146
-                $messenger_name,
1147
-                $consider_current_state
1148
-            );
1149
-        }
1150
-    }
14
+	/**
15
+	 * This option in the database is used to keep a record of message types that have been activated for a messenger
16
+	 * at some point in the history of the site.  It is utilized by the implementation of the 'force' flag in
17
+	 * EE_Register_Message_Type.  The force flag is an indication of whether a message type should be activated by
18
+	 * default when the message type is registered.  However, if a user has explicitly deactivated a message type, then
19
+	 * the force flag is ignored.  The method by which the code knows whether to ignore this flag is via this option.
20
+	 * Note, that this is NOT a historical record.  Its entirely possible for a message type to have been activated for
21
+	 * a messenger and yet not have a record in this option.  This occurs when a message type is inactivated through an
22
+	 * automated process (when an add-on registering the message type deactivates, or when some other code calls the
23
+	 * EE_Registery_Message_Type::deregister method) and the related record(s) is(are) removed from this option to
24
+	 * ensure the "force" flag is respected if that message type is later re-registered. This option should NOT be used
25
+	 * to determine the current "active" state of a message type for a given messenger. The name of this option (and
26
+	 * related methods/properties) is due to matching the original intended purpose for the option that got superseded
27
+	 * by later behaviour requirements.
28
+	 */
29
+	const HAS_ACTIVATED_MESSAGE_TYPE_FOR_MESSENGER_OPTION_NAME = 'ee_has_activated_messenger';
30
+
31
+	/**
32
+	 * @type boolean $_initialized
33
+	 */
34
+	protected $_initialized = false;
35
+
36
+	/**
37
+	 * @type EE_Messenger_Collection $_messenger_collection_loader
38
+	 */
39
+	protected $_messenger_collection_loader;
40
+
41
+	/**
42
+	 * @type EE_Message_Type_Collection $_message_type_collection_loader
43
+	 */
44
+	protected $_message_type_collection_loader;
45
+
46
+	/**
47
+	 * @type EEM_Message_Template_Group $_message_template_group_model
48
+	 */
49
+	protected $_message_template_group_model;
50
+
51
+	/**
52
+	 * @type EE_messenger[]
53
+	 */
54
+	protected $_installed_messengers = [];
55
+
56
+	/**
57
+	 * @type EE_message_type[]
58
+	 */
59
+	protected $_installed_message_types = [];
60
+
61
+	/**
62
+	 * Array of active messengers.
63
+	 * Format is this:
64
+	 * array(
65
+	 *      'messenger_name' => EE_messenger
66
+	 * )
67
+	 *
68
+	 * @type EE_messenger[]
69
+	 */
70
+	protected $_active_messengers = [];
71
+
72
+	/**
73
+	 * Formatted array of active message types grouped per messenger.
74
+	 * Format is this:
75
+	 * array(
76
+	 *      'messenger_name' => array(
77
+	 *          'settings' => array(
78
+	 *              '{messenger_name}-message_types' => array(
79
+	 *                  'message_type_name' => array() //variable array of settings corresponding to message type.
80
+	 *              )
81
+	 *          )
82
+	 *      )
83
+	 * )
84
+	 *
85
+	 * @type array
86
+	 */
87
+	protected $_active_message_types = [];
88
+
89
+
90
+	/**
91
+	 * This holds the array of messengers and their corresponding message types that have
92
+	 * been activated on a site at some point.  This is an important record that helps the messages system
93
+	 * not accidentally reactivate something that was intentionally deactivated by a user.
94
+	 *
95
+	 * @see phpdocs on EE_Message_Resource_Manager::HAS_ACTIVATED_MESSAGE_TYPE_FOR_MESSENGER_OPTION_NAME for more
96
+	 *      details.
97
+	 * @type array
98
+	 */
99
+	protected $_has_activated_messengers_and_message_types = [];
100
+
101
+	/**
102
+	 * An array of unique message type contexts across all active message types.
103
+	 * The array will be indexed by either 'slugs' or 'all'.
104
+	 * The slugs index contains an array indexed by unique context slugs with the latest label representation for that
105
+	 * slug. array(
106
+	 *      'context_slug' => 'localized label for context obtained from latest message type in the loop'.
107
+	 * );
108
+	 * The all index returns an array in this format:
109
+	 * array(
110
+	 *      'message_type_name' => array(
111
+	 *          'context_slug' => array(
112
+	 *              'label' => 'localized label for context',
113
+	 *              'description' => 'localized description for context'
114
+	 *          )
115
+	 *      )
116
+	 * );
117
+	 *
118
+	 * @type array
119
+	 */
120
+	protected $_contexts = [];
121
+
122
+
123
+	/**
124
+	 * EE_Message_Resource_Manager constructor.
125
+	 *
126
+	 * @param \EE_Messenger_Collection_Loader    $Messenger_Collection_Loader
127
+	 * @param \EE_Message_Type_Collection_Loader $Message_Type_Collection_Loader
128
+	 * @param \EEM_Message_Template_Group        $Message_Template_Group_Model
129
+	 */
130
+	public function __construct(
131
+		EE_Messenger_Collection_Loader $Messenger_Collection_Loader,
132
+		EE_Message_Type_Collection_Loader $Message_Type_Collection_Loader,
133
+		EEM_Message_Template_Group $Message_Template_Group_Model
134
+	) {
135
+		$this->_messenger_collection_loader    = $Messenger_Collection_Loader;
136
+		$this->_message_type_collection_loader = $Message_Type_Collection_Loader;
137
+		$this->_message_template_group_model   = $Message_Template_Group_Model;
138
+	}
139
+
140
+
141
+	/**
142
+	 * @return void
143
+	 */
144
+	protected function _initialize_collections()
145
+	{
146
+		if ($this->_initialized) {
147
+			return;
148
+		}
149
+		$this->_initialized = true;
150
+		$this->_messenger_collection_loader->load_messengers_from_folder();
151
+		$this->_message_type_collection_loader->load_message_types_from_folder();
152
+		$this->get_has_activated_messengers_option(true);
153
+		$this->_set_active_messengers_and_message_types();
154
+	}
155
+
156
+
157
+	/**
158
+	 * @return EE_Messenger_Collection
159
+	 */
160
+	public function messenger_collection()
161
+	{
162
+		$this->_initialize_collections();
163
+		return $this->_messenger_collection_loader->messenger_collection();
164
+	}
165
+
166
+
167
+	/**
168
+	 * @return EE_messenger[]
169
+	 */
170
+	public function active_messengers()
171
+	{
172
+		$this->_initialize_collections();
173
+		return $this->_active_messengers;
174
+	}
175
+
176
+
177
+	/**
178
+	 * @param string $messenger_name
179
+	 * @return \EE_messenger
180
+	 */
181
+	public function get_messenger($messenger_name)
182
+	{
183
+		return $this->messenger_collection()->get_by_info($messenger_name);
184
+	}
185
+
186
+
187
+	/**
188
+	 * This returns the corresponding EE_messenger object for the given string if it is active.
189
+	 *
190
+	 * @param string $messenger
191
+	 * @return EE_messenger | null
192
+	 */
193
+	public function get_active_messenger($messenger)
194
+	{
195
+		$this->_initialize_collections();
196
+		return ! empty($this->_active_messengers[ $messenger ])
197
+			? $this->_active_messengers[ $messenger ]
198
+			: null;
199
+	}
200
+
201
+
202
+	/**
203
+	 * @return \EE_messenger[]
204
+	 */
205
+	public function installed_messengers()
206
+	{
207
+		if (empty($this->_installed_messengers)) {
208
+			$this->_installed_messengers = [];
209
+			$this->messenger_collection()->rewind();
210
+			while ($this->messenger_collection()->valid()) {
211
+				$this->_installed_messengers[ $this->messenger_collection()->current()->name ] =
212
+					$this->messenger_collection()->current();
213
+				$this->messenger_collection()->next();
214
+			}
215
+		}
216
+		return $this->_installed_messengers;
217
+	}
218
+
219
+
220
+	/**
221
+	 * @param string $messenger_name
222
+	 * @return \EE_messenger
223
+	 * @throws EE_Error
224
+	 */
225
+	public function valid_messenger($messenger_name)
226
+	{
227
+		$messenger = $this->get_messenger($messenger_name);
228
+		if ($messenger instanceof EE_messenger) {
229
+			return $messenger;
230
+		}
231
+		throw new EE_Error(
232
+			sprintf(
233
+				esc_html__('The "%1$s" messenger is either invalid or not installed', 'event_espresso'),
234
+				$messenger_name
235
+			)
236
+		);
237
+	}
238
+
239
+
240
+	/**
241
+	 * @return EE_Message_Type_Collection
242
+	 */
243
+	public function message_type_collection()
244
+	{
245
+		$this->_initialize_collections();
246
+		return $this->_message_type_collection_loader->message_type_collection();
247
+	}
248
+
249
+
250
+	/**
251
+	 * @return array
252
+	 */
253
+	public function active_message_types()
254
+	{
255
+		$this->_initialize_collections();
256
+		return $this->_active_message_types;
257
+	}
258
+
259
+
260
+	/**
261
+	 * @param string $message_type_name
262
+	 * @return \EE_message_type
263
+	 */
264
+	public function get_message_type($message_type_name)
265
+	{
266
+		return $this->message_type_collection()->get_by_info($message_type_name);
267
+	}
268
+
269
+
270
+	/**
271
+	 * This returns the EE_message_type from the active message types array ( if present );
272
+	 *
273
+	 * @param string $messenger_name
274
+	 * @param string $message_type_name
275
+	 * @return \EE_message_type|null
276
+	 */
277
+	public function get_active_message_type_for_messenger($messenger_name, $message_type_name)
278
+	{
279
+		return $this->is_message_type_active_for_messenger($messenger_name, $message_type_name)
280
+			? $this->get_message_type($message_type_name)
281
+			: null;
282
+	}
283
+
284
+
285
+	/**
286
+	 * Returns whether the given message type is active for the given messenger.
287
+	 *
288
+	 * @param string $messenger_name
289
+	 * @param string $message_type_name
290
+	 * @return bool
291
+	 */
292
+	public function is_message_type_active_for_messenger($messenger_name, $message_type_name)
293
+	{
294
+		$this->_initialize_collections();
295
+		return ! empty($this->_active_message_types[ $messenger_name ]['settings'][ $messenger_name . '-message_types' ][ $message_type_name ]);
296
+	}
297
+
298
+
299
+	/**
300
+	 * Returns whether the given messenger is active.
301
+	 *
302
+	 * @param string $messenger_name the name of the messenger to check if active.
303
+	 * @return bool
304
+	 */
305
+	public function is_messenger_active($messenger_name)
306
+	{
307
+		$this->_initialize_collections();
308
+		return ! empty($this->_active_message_types[ $messenger_name ]);
309
+	}
310
+
311
+
312
+	/**
313
+	 * This returns any settings that might be on a message type for a messenger
314
+	 *
315
+	 * @param string $messenger_name    The slug of the messenger
316
+	 * @param string $message_type_name The slug of the message type getting the settings for.
317
+	 * @return array
318
+	 */
319
+	public function get_message_type_settings_for_messenger($messenger_name, $message_type_name)
320
+	{
321
+		$settings = [];
322
+		if ($this->is_message_type_active_for_messenger($messenger_name, $message_type_name)) {
323
+			$settings =
324
+				isset($this->_active_message_types[ $messenger_name ]['settings'][ $messenger_name . '-message_types' ][ $message_type_name ]['settings'])
325
+					? $this->_active_message_types[ $messenger_name ]['settings'][ $messenger_name . '-message_types' ][ $message_type_name ]['settings']
326
+					: [];
327
+		}
328
+		return $settings;
329
+	}
330
+
331
+
332
+	/**
333
+	 * Returns whether the given messenger name has active message types on it.
334
+	 * Infers whether the messenger is active or not as well.
335
+	 *
336
+	 * @param string $messenger_name
337
+	 * @return bool
338
+	 */
339
+	public function messenger_has_active_message_types($messenger_name)
340
+	{
341
+		$this->_initialize_collections();
342
+		return
343
+			! empty($this->_active_message_types[ $messenger_name ])
344
+			&& ! empty($this->_active_message_types[ $messenger_name ]['settings'][ $messenger_name . '-message_types' ]);
345
+	}
346
+
347
+
348
+	/**
349
+	 * This checks the _active_message_types property for any active message types
350
+	 * that are present for the given messenger and returns them.
351
+	 *
352
+	 * @param string $messenger_name The messenger being checked
353
+	 * @return EE_message_type[]|array    (empty array if no active_message_types)
354
+	 * @since 4.9.0
355
+	 */
356
+	public function get_active_message_types_for_messenger($messenger_name)
357
+	{
358
+		$message_types = [];
359
+		if (! $this->messenger_has_active_message_types($messenger_name)) {
360
+			return $message_types;
361
+		}
362
+		$installed_message_types = $this->installed_message_types();
363
+		foreach ($installed_message_types as $message_type_name => $message_type) {
364
+			if ($this->is_message_type_active_for_messenger($messenger_name, $message_type_name)) {
365
+				$message_types[ $message_type_name ] = $message_type;
366
+			}
367
+		}
368
+		return $message_types;
369
+	}
370
+
371
+
372
+	/**
373
+	 * This does NOT return the _active_message_types property but
374
+	 * simply returns an array of active message type names from that property.
375
+	 * (The _active_message_types property is indexed by messenger and active message_types per messenger).
376
+	 *
377
+	 * @return array message_type references (string)
378
+	 */
379
+	public function list_of_active_message_types()
380
+	{
381
+		$active_message_type_names = [];
382
+		$this->_initialize_collections();
383
+		foreach ($this->_active_message_types as $messenger => $messenger_settings) {
384
+			if (! isset($messenger_settings['settings'][ $messenger . '-message_types' ])) {
385
+				continue;
386
+			}
387
+			foreach ($messenger_settings['settings'][ $messenger . '-message_types' ] as $message_type_name => $message_type_config) {
388
+				if (! in_array($message_type_name, $active_message_type_names)) {
389
+					$active_message_type_names[] = $message_type_name;
390
+				}
391
+			}
392
+		}
393
+		return $active_message_type_names;
394
+	}
395
+
396
+
397
+	/**
398
+	 * Same as list_of_active_message_types() except this returns actual EE_message_type objects
399
+	 *
400
+	 * @return \EE_message_type[]
401
+	 * @since 4.9.0
402
+	 */
403
+	public function get_active_message_type_objects()
404
+	{
405
+		$active_message_types      = [];
406
+		$installed_message_types   = $this->installed_message_types();
407
+		$active_message_type_names = $this->list_of_active_message_types();
408
+		foreach ($active_message_type_names as $active_message_type_name) {
409
+			if (isset($installed_message_types[ $active_message_type_name ])) {
410
+				$active_message_types[ $active_message_type_name ] =
411
+					$installed_message_types[ $active_message_type_name ];
412
+			}
413
+		}
414
+		return $active_message_types;
415
+	}
416
+
417
+
418
+	/**
419
+	 * @return \EE_message_type[]
420
+	 */
421
+	public function installed_message_types()
422
+	{
423
+		if (empty($this->_installed_message_types)) {
424
+			$this->message_type_collection()->rewind();
425
+			while ($this->message_type_collection()->valid()) {
426
+				$this->_installed_message_types[ $this->message_type_collection()->current()->name ] =
427
+					$this->message_type_collection()->current();
428
+				$this->message_type_collection()->next();
429
+			}
430
+		}
431
+		return $this->_installed_message_types;
432
+	}
433
+
434
+
435
+	/**
436
+	 * @param string $message_type_name
437
+	 * @return \EE_message_type
438
+	 * @throws EE_Error
439
+	 */
440
+	public function valid_message_type($message_type_name)
441
+	{
442
+		$message_type = $this->get_message_type($message_type_name);
443
+		if ($message_type instanceof EE_message_type) {
444
+			return $message_type;
445
+		}
446
+		throw new EE_Error(
447
+			sprintf(
448
+				esc_html__('The "%1$s" message type is either invalid or not installed', 'event_espresso'),
449
+				$message_type_name
450
+			)
451
+		);
452
+	}
453
+
454
+
455
+	/**
456
+	 * valid_message_type_for_messenger
457
+	 *
458
+	 * @param EE_messenger $messenger
459
+	 * @param string       $message_type_name
460
+	 * @return boolean
461
+	 * @throws EE_Error
462
+	 */
463
+	public function valid_message_type_for_messenger(EE_messenger $messenger, $message_type_name)
464
+	{
465
+		$valid_message_types = $messenger->get_valid_message_types();
466
+		if (! in_array($message_type_name, $valid_message_types)) {
467
+			throw new EE_Error(
468
+				sprintf(
469
+					esc_html__(
470
+						'The message type (%1$s) sent to "%2$s" is not valid for the "%3$s" messenger.  Double-check the spelling and verify that message type has been registered as a valid type with the messenger.',
471
+						'event_espresso'
472
+					),
473
+					$message_type_name,
474
+					__METHOD__,
475
+					$messenger->name
476
+				)
477
+			);
478
+		}
479
+		return true;
480
+	}
481
+
482
+
483
+	/**
484
+	 * Used to return active messengers array stored in the wp options table.
485
+	 * If no value is present in the option then an empty array is returned.
486
+	 *
487
+	 * @param bool $reset       If true then we ignore whether the option is cached on the _active_message_types
488
+	 *                          property and pull directly from the db.  Otherwise whatever is currently on the
489
+	 *                          $_active_message_types property is pulled.
490
+	 * @return array
491
+	 */
492
+	public function get_active_messengers_option($reset = false)
493
+	{
494
+		if ($reset) {
495
+			$this->_active_message_types = get_option('ee_active_messengers', []);
496
+		}
497
+		return $this->_active_message_types;
498
+	}
499
+
500
+
501
+	/**
502
+	 * Used to update the active messengers array stored in the wp options table.
503
+	 *
504
+	 * @param array $active_messenger_settings Incoming data to save.  If empty, then the internal cached property
505
+	 *                                         representing this data is used.
506
+	 * @return bool FALSE if not updated, TRUE if updated.
507
+	 */
508
+	public function update_active_messengers_option($active_messenger_settings = [])
509
+	{
510
+		$active_messenger_settings = empty($active_messenger_settings)
511
+			? $this->_active_message_types
512
+			: $active_messenger_settings;
513
+		// make sure _active_message_types is updated (this is the internal cache for the settings).
514
+		$this->_active_message_types = $active_messenger_settings;
515
+		return update_option('ee_active_messengers', $active_messenger_settings);
516
+	}
517
+
518
+
519
+	/**
520
+	 * Used to return has activated message types for messengers array stored in the wp options table.
521
+	 * If no value is present in the option then an empty array is returned.
522
+	 * The value is cached on the $_has_activated_messengers_and_message_types property for future calls.
523
+	 *
524
+	 * @param bool $reset Used to indicate that any cached value should be ignored.
525
+	 * @return array
526
+	 * @see phpdocs on EE_Message_Resource_Manager::HAS_ACTIVATED_MESSAGE_TYPE_FOR_MESSENGER_OPTION_NAME for more
527
+	 *                    details.
528
+	 */
529
+	public function get_has_activated_messengers_option($reset = false)
530
+	{
531
+		if ($reset || empty($this->_has_activated_messengers_and_message_types)) {
532
+			$this->_has_activated_messengers_and_message_types =
533
+				get_option(self::HAS_ACTIVATED_MESSAGE_TYPE_FOR_MESSENGER_OPTION_NAME, []);
534
+		}
535
+		return $this->_has_activated_messengers_and_message_types;
536
+	}
537
+
538
+
539
+	/**
540
+	 * Used to update the has activated option in the db.
541
+	 *
542
+	 * @param array $has_activated_messengers Incoming data to save.  If empty, then the internal cached property
543
+	 *                                        representing this data is used.
544
+	 * @return bool FALSE if not updated, TRUE if updated.
545
+	 * @see phpdocs on EE_Message_Resource_Manager::HAS_ACTIVATED_MESSAGE_TYPE_FOR_MESSENGER_OPTION_NAME for more
546
+	 *                                        details.
547
+	 */
548
+	public function update_has_activated_messengers_option($has_activated_messengers = [])
549
+	{
550
+		// make sure the option has been retrieved from first so we don't overwrite it accidentally.
551
+		if (empty($has_activated_messengers) && empty($this->_has_activated_messengers_and_message_types)) {
552
+			$this->get_has_activated_messengers_option();
553
+		}
554
+		$has_activated_messengers = empty($has_activated_messengers)
555
+			? $this->_has_activated_messengers_and_message_types
556
+			: $has_activated_messengers;
557
+		return update_option(self::HAS_ACTIVATED_MESSAGE_TYPE_FOR_MESSENGER_OPTION_NAME, $has_activated_messengers);
558
+	}
559
+
560
+
561
+	/**
562
+	 * wrapper for _set_active_messengers_and_message_types()
563
+	 */
564
+	public function reset_active_messengers_and_message_types()
565
+	{
566
+		$this->_set_active_messengers_and_message_types();
567
+	}
568
+
569
+
570
+	/**
571
+	 * Generate list of active messengers and message types from collection.
572
+	 * This sets up the active messengers from what is present in the database.
573
+	 */
574
+	protected function _set_active_messengers_and_message_types()
575
+	{
576
+		// echo "\n\n " . __LINE__ . ") " . __METHOD__ . "() \n";
577
+		// list of activated messengers as set via the admin
578
+		// note calling `get_active_messengers_options` also initializes the _active_message_types property.
579
+		$this->get_active_messengers_option(true);
580
+		$this->ensure_messengers_are_active([], false, true);
581
+		$this->update_active_messengers_option();
582
+		$this->update_has_activated_messengers_option();
583
+	}
584
+
585
+
586
+	/**
587
+	 * Ensures that the specified messenger is currently active.
588
+	 * If not, activates it and its default message types.
589
+	 *
590
+	 * @param string $messenger_name
591
+	 * @param bool   $update_option Whether to update the option in the db or not.
592
+	 * @return boolean true if either already active or successfully activated.
593
+	 */
594
+	public function ensure_messenger_is_active($messenger_name, $update_option = true)
595
+	{
596
+		if (! isset($this->_active_messengers[ $messenger_name ])) {
597
+			try {
598
+				$this->activate_messenger($messenger_name, [], $update_option);
599
+			} catch (EE_Error $e) {
600
+				EE_Error::add_error(
601
+					$e->getMessage(),
602
+					__FILE__,
603
+					__FUNCTION__,
604
+					__LINE__
605
+				);
606
+				return false;
607
+			}
608
+		}
609
+		return true;
610
+	}
611
+
612
+
613
+	/**
614
+	 * This ensures the given array of messenger names is active in the system.
615
+	 * Note, this method will not activate any NEW message types for the messenger when it is called. Instead,
616
+	 * it will automatically activate the default message types for the messenger if its not active.
617
+	 *
618
+	 * @param array $messenger_names  Array of messenger names for messengers to be activated.  If an empty array
619
+	 *                                (default) then will attempt to set the active messengers from the
620
+	 *                                activated_messengers option
621
+	 *                                (stored in $_active_message_types property).
622
+	 * @param bool  $update_option    Whether to update the related active messengers option.
623
+	 * @param bool  $verify           Whether to verify the messengers are installed before activating. Note if this is
624
+	 *                                set to true and a messenger is indicated as active, but is NOT installed, then it
625
+	 *                                will automatically be deactivated.
626
+	 */
627
+	public function ensure_messengers_are_active($messenger_names = [], $update_option = true, $verify = false)
628
+	{
629
+		$messenger_names = empty($messenger_names)
630
+			? array_keys($this->_active_message_types)
631
+			: $messenger_names;
632
+
633
+		$not_installed = [];
634
+		foreach ($messenger_names as $messenger_name) {
635
+			if ($verify && ! $this->messenger_collection()->has_by_name($messenger_name)) {
636
+				$not_installed[] = $messenger_name;
637
+				$this->deactivate_messenger($messenger_name);
638
+				continue;
639
+			}
640
+			$this->ensure_messenger_is_active($messenger_name, $update_option);
641
+		}
642
+
643
+		if (! empty($not_installed)) {
644
+			EE_Error::add_error(
645
+				sprintf(
646
+					esc_html__(
647
+						'The following messengers are either not installed or are invalid:%1$s %2$s',
648
+						'event_espresso'
649
+					),
650
+					'<br />',
651
+					implode(', ', $not_installed)
652
+				),
653
+				__FILE__,
654
+				__FUNCTION__,
655
+				__LINE__
656
+			);
657
+		}
658
+	}
659
+
660
+
661
+	/**
662
+	 * Ensures that the specified message type for the given messenger is currently active, if not activates it.
663
+	 * This ALSO ensures that the given messenger is active as well!
664
+	 *
665
+	 * @param string $message_type_name message type name.
666
+	 * @param        $messenger_name
667
+	 * @param bool   $update_option     Whether to update the option in the db or not.
668
+	 * @return bool  Returns true if already is active or if was activated successfully.
669
+	 * @throws EE_Error
670
+	 */
671
+	public function ensure_message_type_is_active($message_type_name, $messenger_name, $update_option = true)
672
+	{
673
+		// grab the messenger to work with.
674
+		$messenger = $this->valid_messenger($messenger_name);
675
+		if ($this->valid_message_type_for_messenger($messenger, $message_type_name)) {
676
+			// ensure messenger is active (that's an inherent coupling between active message types and the
677
+			// messenger they are being activated for.
678
+			try {
679
+				if (! $this->is_message_type_active_for_messenger($messenger_name, $message_type_name)) {
680
+					// all is good so let's just get it active
681
+					$this->activate_messenger($messenger, [$message_type_name], $update_option);
682
+				}
683
+			} catch (EE_Error $e) {
684
+				EE_Error::add_error(
685
+					$e->getMessage(),
686
+					__FILE__,
687
+					__FUNCTION__,
688
+					__LINE__
689
+				);
690
+				return false;
691
+			}
692
+		}
693
+		return true;
694
+	}
695
+
696
+
697
+	/**
698
+	 * This is a wrapper for `ensure_message_type_is_active` that will handle ensuring multiple message types for a
699
+	 * messenger are active in one go.
700
+	 *
701
+	 * @param array  $message_type_names Array of message type names to ensure are active.
702
+	 * @param string $messenger_name     The name of the messenger that the message types are to be activated on.
703
+	 * @param bool   $update_option      Whether to persist the activation to the database or not (default true).
704
+	 */
705
+	public function ensure_message_types_are_active($message_type_names, $messenger_name, $update_option = true)
706
+	{
707
+		$message_type_names = (array) $message_type_names;
708
+		foreach ($message_type_names as $message_type_name) {
709
+			// note, intentionally not updating option here because we're in a loop.
710
+			// We'll follow the instructions of the incoming $update_option argument after the loop.
711
+			$this->ensure_message_type_is_active($message_type_name, $messenger_name, false);
712
+		}
713
+		if ($update_option) {
714
+			$this->update_active_messengers_option();
715
+			$this->update_has_activated_messengers_option();
716
+		}
717
+	}
718
+
719
+
720
+	/**
721
+	 * Activates the specified messenger.
722
+	 *
723
+	 * @param EE_messenger|string $messenger          Instantiated EE_messenger OR messenger name if not already loaded!
724
+	 * @param array               $message_type_names An array of message type names to activate with this messenger.
725
+	 *                                                If included we do NOT setup the default message types
726
+	 *                                                (assuming they are already setup.)
727
+	 * @param bool                $update_active_messengers_option
728
+	 * @return array of generated templates
729
+	 * @throws EE_Error
730
+	 */
731
+	public function activate_messenger(
732
+		$messenger,
733
+		$message_type_names = [],
734
+		$update_active_messengers_option = true
735
+	) {
736
+		$templates = [];
737
+		// grab the messenger to work with.
738
+		$messenger = $messenger instanceof EE_messenger
739
+			? $messenger
740
+			: $this->messenger_collection()->get_by_info($messenger);
741
+		// it's inactive. Activate it.
742
+		if ($messenger instanceof EE_messenger) {
743
+			$this->_active_messengers[ $messenger->name ] = $messenger;
744
+			// activate incoming message types set to be activated with messenger.
745
+			$message_type_names = $this->_activate_message_types($messenger, $message_type_names);
746
+			// setup any initial settings for the messenger if necessary.
747
+			$this->add_settings_for_messenger($messenger->name);
748
+			if ($update_active_messengers_option) {
749
+				$this->update_active_messengers_option();
750
+				$this->update_has_activated_messengers_option();
751
+			}
752
+			// generate new templates if necessary and ensure all related templates that are already in the database are
753
+			// marked active.  Note, this will also deactivate a message type for a messenger if the template
754
+			// cannot be successfully created during its attempt (only happens for global template attempts).
755
+			if (! empty($message_type_names)) {
756
+				$templates = EEH_MSG_Template::generate_new_templates($messenger->name, $message_type_names, 0, true);
757
+				EEH_MSG_Template::update_to_active([$messenger->name], $message_type_names);
758
+			}
759
+		}
760
+		return $templates;
761
+	}
762
+
763
+
764
+	/**
765
+	 * Activates given message types for the given EE_messenger object.
766
+	 * Note: (very important) This method does not persist the activation to the database.
767
+	 * See code implementing this method in this class for examples of how to persist.
768
+	 *
769
+	 * @param \EE_messenger $messenger
770
+	 * @param array         $message_type_names
771
+	 * @return array
772
+	 */
773
+	protected function _activate_message_types(EE_messenger $messenger, $message_type_names = [])
774
+	{
775
+		// If $message_type_names is empty, AND $this->_active_message_types is empty, then that means
776
+		// things have never been initialized (which should happen on EEH_Activation::generate_message_templates).
777
+		// So ONLY then do we need to actually grab defaults and cycle through them.  Otherwise we
778
+		// only override _active_message_types when an explicit array of $message_type_names has been provided.
779
+		$message_type_names = empty($message_type_names) && ! isset($this->_active_message_types[ $messenger->name ])
780
+			? $messenger->get_default_message_types()
781
+			: (array) $message_type_names;
782
+
783
+		// now we ALWAYS need to make sure that the messenger is active for the message types we're activating!
784
+		if (! isset($this->_active_message_types[ $messenger->name ])) {
785
+			$this->_active_message_types[ $messenger->name ]['settings'] = [];
786
+		}
787
+
788
+		if ($message_type_names) {
789
+			// cycle thru message types
790
+			foreach ($message_type_names as $message_type_name) {
791
+				// only register the message type as active IF it isn't already active
792
+				// and if its actually installed.
793
+				if (
794
+					! $this->is_message_type_active_for_messenger($messenger->name, $message_type_name)
795
+				) {
796
+					$this->add_settings_for_message_type($messenger->name, $message_type_name);
797
+					$this->_set_messenger_has_activated_message_type(
798
+						$messenger,
799
+						$message_type_name
800
+					);
801
+				}
802
+			}
803
+		}
804
+		return $message_type_names;
805
+	}
806
+
807
+
808
+	/**
809
+	 * add_settings_for_message_type
810
+	 * NOTE This does NOT automatically persist any settings to the db.  Client code should call
811
+	 * $this->update_active_messengers_option to persist.
812
+	 *
813
+	 * @param string $messenger_name    The name of the messenger adding the settings for
814
+	 * @param string $message_type_name The name of the message type adding the settings for
815
+	 * @param array  $new_settings      Any new settings being set for the message type and messenger
816
+	 */
817
+	public function add_settings_for_message_type($messenger_name, $message_type_name, $new_settings = [])
818
+	{
819
+		// get installed message type from collection
820
+		$message_type      = $this->message_type_collection()->get_by_info($message_type_name);
821
+		$existing_settings = $this->get_message_type_settings_for_messenger($messenger_name, $message_type_name);
822
+		// we need to setup any initial settings for message types
823
+		if ($message_type instanceof EE_message_type) {
824
+			$default_settings = $message_type->get_admin_settings_fields();
825
+			foreach ($default_settings as $field => $values) {
826
+				if (isset($new_settings[ $field ])) {
827
+					$existing_settings[ $field ] = $new_settings[ $field ];
828
+					continue;
829
+				}
830
+				if (! isset($existing_settings[ $field ])) {
831
+					$existing_settings[ $field ] = $values['default'];
832
+				}
833
+			}
834
+		}
835
+		$this->_active_message_types[ $messenger_name ]['settings'][ $messenger_name . '-message_types' ][ $message_type_name ]['settings'] =
836
+			$existing_settings;
837
+	}
838
+
839
+
840
+	/**
841
+	 * Updates the internal cached _has_activated_messengers_and_message_types property with the given messenger
842
+	 * and message type.
843
+	 *
844
+	 * @param \EE_messenger $messenger
845
+	 * @param string        $message_type_name
846
+	 * @see    phpdocs on EE_Message_Resource_Manager::HAS_ACTIVATED_MESSAGE_TYPE_FOR_MESSENGER_OPTION_NAME for more
847
+	 *         details.
848
+	 * @access protected
849
+	 */
850
+	protected function _set_messenger_has_activated_message_type(EE_messenger $messenger, $message_type_name)
851
+	{
852
+		// if _has_activated_messengers_and_message_types is empty then lets ensure its initialized
853
+		if (empty($this->_has_activated_messengers_and_message_types)) {
854
+			$this->get_has_activated_messengers_option();
855
+		}
856
+
857
+		// make sure this messenger has a record in the has_activated array
858
+		if (! isset($this->_has_activated_messengers_and_message_types[ $messenger->name ])) {
859
+			$this->_has_activated_messengers_and_message_types[ $messenger->name ] = [];
860
+		}
861
+		// check if message type has already been added
862
+		if (! in_array($message_type_name, $this->_has_activated_messengers_and_message_types[ $messenger->name ])) {
863
+			$this->_has_activated_messengers_and_message_types[ $messenger->name ][] = $message_type_name;
864
+		}
865
+	}
866
+
867
+
868
+	/**
869
+	 * add_settings_for_messenger
870
+	 * NOTE This does NOT automatically persist any settings to the db.  Client code should call
871
+	 * $this->update_active_messengers_option to persist.
872
+	 *
873
+	 * @param string $messenger_name The name of the messenger the settings is being added for.
874
+	 * @param array  $new_settings   An array of settings to update the existing settings.
875
+	 */
876
+	public function add_settings_for_messenger($messenger_name, $new_settings = [])
877
+	{
878
+		$messenger = $this->get_messenger($messenger_name);
879
+		if ($messenger instanceof EE_messenger) {
880
+			$msgr_settings = $messenger->get_admin_settings_fields();
881
+			if (! empty($msgr_settings)) {
882
+				foreach ($msgr_settings as $field => $value) {
883
+					// is there a new setting for this?
884
+					if (isset($new_settings[ $field ])) {
885
+						$this->_active_message_types[ $messenger->name ]['settings'][ $field ] =
886
+							$new_settings[ $field ];
887
+						continue;
888
+					}
889
+					// only set the default if it isn't already set.
890
+					if (! isset($this->_active_message_types[ $messenger->name ]['settings'][ $field ])) {
891
+						$this->_active_message_types[ $messenger->name ]['settings'][ $field ] = $value;
892
+					}
893
+				}
894
+			}
895
+		}
896
+	}
897
+
898
+
899
+	/**
900
+	 * deactivate_messenger
901
+	 *
902
+	 * @param string|EE_messenger $messenger_name name of messenger
903
+	 * @return void
904
+	 */
905
+	public function deactivate_messenger($messenger_name)
906
+	{
907
+		$this->_initialize_collections();
908
+		if ($messenger_name instanceof EE_messenger) {
909
+			$messenger_name = $messenger_name->name;
910
+		}
911
+		unset($this->_active_messengers[ $messenger_name ]);
912
+		unset($this->_active_message_types[ $messenger_name ]);
913
+		$this->_message_template_group_model->deactivate_message_template_groups_for($messenger_name);
914
+		$this->update_active_messengers_option();
915
+	}
916
+
917
+
918
+	/**
919
+	 * Deactivates a message type (note this will deactivate across all messenger's it is active on.
920
+	 *
921
+	 * @param string $message_type_name      name of message type being deactivated
922
+	 * @param bool   $set_has_active_record  By default we always record the has_active record when deactivating a
923
+	 *                                       message type.  However, this can be overridden if we don't want this set
924
+	 *                                       (usually when this is called as a part of deregistration of a custom
925
+	 *                                       message type)
926
+	 */
927
+	public function deactivate_message_type($message_type_name, $set_has_active_record = true)
928
+	{
929
+		$this->_initialize_collections();
930
+		if ($message_type_name instanceof EE_message_type) {
931
+			$message_type_name = $message_type_name->name;
932
+		}
933
+		foreach ($this->_active_message_types as $messenger_name => $settings) {
934
+			unset(
935
+				$this->_active_message_types[ $messenger_name ]['settings'][ $messenger_name . '-message_types' ][ $message_type_name ]
936
+			);
937
+
938
+			// we always record (even on deactivation) that a message type has been activated because there should at
939
+			// least be a record in the "has_activated" option that it WAS active at one point.
940
+			if ($set_has_active_record) {
941
+				$messenger = $this->get_messenger($messenger_name);
942
+				$this->_set_messenger_has_activated_message_type($messenger, $message_type_name);
943
+			}
944
+		}
945
+		$this->_message_template_group_model->deactivate_message_template_groups_for('', $message_type_name);
946
+		$this->update_active_messengers_option();
947
+		$this->update_has_activated_messengers_option();
948
+	}
949
+
950
+
951
+	/**
952
+	 * Deactivates a message type for a specific messenger as opposed to all messengers.
953
+	 *
954
+	 * @param string $message_type_name Name of message type being deactivated.
955
+	 * @param string $messenger_name    Name of messenger the message type is being deactivated for.
956
+	 */
957
+	public function deactivate_message_type_for_messenger($message_type_name, $messenger_name)
958
+	{
959
+		$this->_initialize_collections();
960
+		if ($this->is_message_type_active_for_messenger($messenger_name, $message_type_name)) {
961
+			unset($this->_active_message_types[ $messenger_name ]['settings'][ $messenger_name . '-message_types' ][ $message_type_name ]);
962
+		}
963
+		$this->_message_template_group_model->deactivate_message_template_groups_for(
964
+			[$messenger_name],
965
+			[$message_type_name]
966
+		);
967
+		$this->update_active_messengers_option();
968
+	}
969
+
970
+
971
+	/**
972
+	 * Used to verify if a message can be sent for the given messenger and message type
973
+	 * and that it is a generating messenger (used for generating message templates).
974
+	 *
975
+	 * @param EE_messenger    $messenger    messenger used in trigger
976
+	 * @param EE_message_type $message_type message type used in trigger
977
+	 * @return bool true is a generating messenger and can be sent OR FALSE meaning cannot send.
978
+	 */
979
+	public function is_generating_messenger_and_active(EE_messenger $messenger, EE_message_type $message_type)
980
+	{
981
+		// get the $messengers the message type says it can be used with.
982
+		foreach ($message_type->with_messengers() as $generating_messenger => $secondary_messengers) {
983
+			if (
984
+				$messenger->name === $generating_messenger
985
+				&& $this->is_message_type_active_for_messenger($messenger->name, $message_type->name)
986
+			) {
987
+				return true;
988
+			}
989
+		}
990
+		return false;
991
+	}
992
+
993
+
994
+	/**
995
+	 * This returns all the contexts that are registered by all message types.
996
+	 * If $slugs_only is true,
997
+	 * then just an array indexed by unique context slugs with the latest label representation for that slug.
998
+	 * array(
999
+	 *      'context_slug' => 'localized label for context obtained from latest message type in the loop'.
1000
+	 * );
1001
+	 * If $slugs_only is false, then the format is:
1002
+	 * array(
1003
+	 *      'message_type_name' => array(
1004
+	 *          'context_slug' => array(
1005
+	 *              'label' => 'localized label for context',
1006
+	 *              'description' => 'localized description for context'
1007
+	 *          )
1008
+	 *      )
1009
+	 * );
1010
+	 * Keep in mind that although different message types may share the same context slugs,
1011
+	 * it is possible that the context is described differently by the message type.
1012
+	 *
1013
+	 * @param bool $slugs_only   Whether to return an array of just slugs and labels (true)
1014
+	 *                           or all contexts indexed by message type.
1015
+	 * @return array
1016
+	 * @since 4.9.0
1017
+	 */
1018
+	public function get_all_contexts($slugs_only = true)
1019
+	{
1020
+		$key = $slugs_only
1021
+			? 'slugs'
1022
+			: 'all';
1023
+		// check if contexts has been setup yet.
1024
+		if (empty($this->_contexts[ $key ])) {
1025
+			// So let's get all active message type objects and loop through to get all unique contexts
1026
+			foreach ($this->get_active_message_type_objects() as $message_type) {
1027
+				if ($message_type instanceof EE_message_type) {
1028
+					$message_type_contexts = $message_type->get_contexts();
1029
+					if ($slugs_only) {
1030
+						foreach ($message_type_contexts as $context => $context_details) {
1031
+							$this->_contexts[ $key ][ $context ] = $context_details['label'];
1032
+						}
1033
+					} else {
1034
+						$this->_contexts[ $key ][ $message_type->name ] = $message_type_contexts;
1035
+					}
1036
+				}
1037
+			}
1038
+		}
1039
+		return ! empty($this->_contexts[ $key ])
1040
+			? $this->_contexts[ $key ]
1041
+			: [];
1042
+	}
1043
+
1044
+
1045
+	/**
1046
+	 * This checks the internal record of what message types are considered "active" and verifies that
1047
+	 * there is an installed class definition for that message type.  If the active message type does not have a
1048
+	 * corresponding accessible message type class then it will be deactivated from all messengers it is active on and
1049
+	 * any related message templates will be inactivated as well.
1050
+	 *
1051
+	 * @return bool   true means all active message types are valid, false means at least one message type was
1052
+	 *                deactivated.
1053
+	 */
1054
+	public function validate_active_message_types_are_installed()
1055
+	{
1056
+		$list_of_active_message_type_names = $this->list_of_active_message_types();
1057
+		$installed_message_types           = $this->installed_message_types();
1058
+		$all_message_types_valid           = true;
1059
+		// loop through list of active message types and verify they are installed.
1060
+		foreach ($list_of_active_message_type_names as $message_type_name) {
1061
+			if (! isset($installed_message_types[ $message_type_name ])) {
1062
+				$this->remove_message_type_has_been_activated_from_all_messengers(
1063
+					$message_type_name,
1064
+					true
1065
+				);
1066
+				$this->deactivate_message_type($message_type_name, false);
1067
+				$all_message_types_valid = false;
1068
+			}
1069
+		}
1070
+		return $all_message_types_valid;
1071
+	}
1072
+
1073
+
1074
+	/**
1075
+	 * This method checks the `ee_has_activated_messenger` option to see if the message type has ever been
1076
+	 * activated for the given messenger.  This can be called by client code on plugin updates etc to determine whether
1077
+	 * to attempt automatically reactivating message types that should be activated by default or not.
1078
+	 *
1079
+	 * @param $message_type_name
1080
+	 * @param $messenger_name
1081
+	 * @return bool
1082
+	 * @see phpdocs on EE_Message_Resource_Manager::HAS_ACTIVATED_MESSAGE_TYPE_FOR_MESSENGER_OPTION_NAME for more
1083
+	 *      details.
1084
+	 */
1085
+	public function has_message_type_been_activated_for_messenger($message_type_name, $messenger_name)
1086
+	{
1087
+		$has_activated = $this->get_has_activated_messengers_option();
1088
+		return isset($has_activated[ $messenger_name ])
1089
+			   && in_array($message_type_name, $has_activated[ $messenger_name ]);
1090
+	}
1091
+
1092
+
1093
+	/**
1094
+	 * This method unsets a message type from the given messenger has activated option.
1095
+	 *
1096
+	 * @param string $message_type_name
1097
+	 * @param string $messenger_name
1098
+	 * @param bool   $consider_current_state  Whether to consider whether the  message type is currently active or not.
1099
+	 *                                        If it is currently active, then remove.  Otherwise leave it alone.
1100
+	 * @see phpdocs on EE_Message_Resource_Manager::HAS_ACTIVATED_MESSAGE_TYPE_FOR_MESSENGER_OPTION_NAME for more
1101
+	 *                                        details.
1102
+	 */
1103
+	public function remove_message_type_has_been_activated_for_messenger(
1104
+		$message_type_name,
1105
+		$messenger_name,
1106
+		$consider_current_state = false
1107
+	) {
1108
+		if (
1109
+			$consider_current_state
1110
+			&& ! $this->is_message_type_active_for_messenger($messenger_name, $message_type_name)
1111
+		) {
1112
+			// when consider current state is true, this means we don't want to change anything on the "has_activated"
1113
+			// record if the message type is currently active for this messenger.  This is used when we want to retain
1114
+			// the record for user initiated inactivations of the message type.
1115
+			return;
1116
+		}
1117
+		$has_activated        = $this->get_has_activated_messengers_option();
1118
+		$key_for_message_type = isset($has_activated[ $messenger_name ])
1119
+			? array_search($message_type_name, $has_activated[ $messenger_name ], true)
1120
+			: false;
1121
+		if ($key_for_message_type !== false) {
1122
+			unset($has_activated[ $messenger_name ][ $key_for_message_type ]);
1123
+			$this->update_has_activated_messengers_option($has_activated);
1124
+			// reset the internal cached property
1125
+			$this->get_has_activated_messengers_option(true);
1126
+		}
1127
+	}
1128
+
1129
+
1130
+	/**
1131
+	 * Removes a message type active record from all messengers it is attached to.
1132
+	 *
1133
+	 * @param      $message_type_name
1134
+	 * @param bool $consider_current_state  Whether to consider whether the  message type is currently active or not.
1135
+	 *                                      If it is currently active, then remove.  Otherwise leave it alone.
1136
+	 * @see phpdocs on EE_Message_Resource_Manager::HAS_ACTIVATED_MESSAGE_TYPE_FOR_MESSENGER_OPTION_NAME for more
1137
+	 *                                      details.
1138
+	 */
1139
+	public function remove_message_type_has_been_activated_from_all_messengers(
1140
+		$message_type_name,
1141
+		$consider_current_state = false
1142
+	) {
1143
+		foreach (array_keys($this->get_has_activated_messengers_option()) as $messenger_name) {
1144
+			$this->remove_message_type_has_been_activated_for_messenger(
1145
+				$message_type_name,
1146
+				$messenger_name,
1147
+				$consider_current_state
1148
+			);
1149
+		}
1150
+	}
1151 1151
 }
Please login to merge, or discard this patch.
Spacing   +57 added lines, -57 removed lines patch added patch discarded remove patch
@@ -193,8 +193,8 @@  discard block
 block discarded – undo
193 193
     public function get_active_messenger($messenger)
194 194
     {
195 195
         $this->_initialize_collections();
196
-        return ! empty($this->_active_messengers[ $messenger ])
197
-            ? $this->_active_messengers[ $messenger ]
196
+        return ! empty($this->_active_messengers[$messenger])
197
+            ? $this->_active_messengers[$messenger]
198 198
             : null;
199 199
     }
200 200
 
@@ -208,7 +208,7 @@  discard block
 block discarded – undo
208 208
             $this->_installed_messengers = [];
209 209
             $this->messenger_collection()->rewind();
210 210
             while ($this->messenger_collection()->valid()) {
211
-                $this->_installed_messengers[ $this->messenger_collection()->current()->name ] =
211
+                $this->_installed_messengers[$this->messenger_collection()->current()->name] =
212 212
                     $this->messenger_collection()->current();
213 213
                 $this->messenger_collection()->next();
214 214
             }
@@ -292,7 +292,7 @@  discard block
 block discarded – undo
292 292
     public function is_message_type_active_for_messenger($messenger_name, $message_type_name)
293 293
     {
294 294
         $this->_initialize_collections();
295
-        return ! empty($this->_active_message_types[ $messenger_name ]['settings'][ $messenger_name . '-message_types' ][ $message_type_name ]);
295
+        return ! empty($this->_active_message_types[$messenger_name]['settings'][$messenger_name.'-message_types'][$message_type_name]);
296 296
     }
297 297
 
298 298
 
@@ -305,7 +305,7 @@  discard block
 block discarded – undo
305 305
     public function is_messenger_active($messenger_name)
306 306
     {
307 307
         $this->_initialize_collections();
308
-        return ! empty($this->_active_message_types[ $messenger_name ]);
308
+        return ! empty($this->_active_message_types[$messenger_name]);
309 309
     }
310 310
 
311 311
 
@@ -321,8 +321,8 @@  discard block
 block discarded – undo
321 321
         $settings = [];
322 322
         if ($this->is_message_type_active_for_messenger($messenger_name, $message_type_name)) {
323 323
             $settings =
324
-                isset($this->_active_message_types[ $messenger_name ]['settings'][ $messenger_name . '-message_types' ][ $message_type_name ]['settings'])
325
-                    ? $this->_active_message_types[ $messenger_name ]['settings'][ $messenger_name . '-message_types' ][ $message_type_name ]['settings']
324
+                isset($this->_active_message_types[$messenger_name]['settings'][$messenger_name.'-message_types'][$message_type_name]['settings'])
325
+                    ? $this->_active_message_types[$messenger_name]['settings'][$messenger_name.'-message_types'][$message_type_name]['settings']
326 326
                     : [];
327 327
         }
328 328
         return $settings;
@@ -340,8 +340,8 @@  discard block
 block discarded – undo
340 340
     {
341 341
         $this->_initialize_collections();
342 342
         return
343
-            ! empty($this->_active_message_types[ $messenger_name ])
344
-            && ! empty($this->_active_message_types[ $messenger_name ]['settings'][ $messenger_name . '-message_types' ]);
343
+            ! empty($this->_active_message_types[$messenger_name])
344
+            && ! empty($this->_active_message_types[$messenger_name]['settings'][$messenger_name.'-message_types']);
345 345
     }
346 346
 
347 347
 
@@ -356,13 +356,13 @@  discard block
 block discarded – undo
356 356
     public function get_active_message_types_for_messenger($messenger_name)
357 357
     {
358 358
         $message_types = [];
359
-        if (! $this->messenger_has_active_message_types($messenger_name)) {
359
+        if ( ! $this->messenger_has_active_message_types($messenger_name)) {
360 360
             return $message_types;
361 361
         }
362 362
         $installed_message_types = $this->installed_message_types();
363 363
         foreach ($installed_message_types as $message_type_name => $message_type) {
364 364
             if ($this->is_message_type_active_for_messenger($messenger_name, $message_type_name)) {
365
-                $message_types[ $message_type_name ] = $message_type;
365
+                $message_types[$message_type_name] = $message_type;
366 366
             }
367 367
         }
368 368
         return $message_types;
@@ -381,11 +381,11 @@  discard block
 block discarded – undo
381 381
         $active_message_type_names = [];
382 382
         $this->_initialize_collections();
383 383
         foreach ($this->_active_message_types as $messenger => $messenger_settings) {
384
-            if (! isset($messenger_settings['settings'][ $messenger . '-message_types' ])) {
384
+            if ( ! isset($messenger_settings['settings'][$messenger.'-message_types'])) {
385 385
                 continue;
386 386
             }
387
-            foreach ($messenger_settings['settings'][ $messenger . '-message_types' ] as $message_type_name => $message_type_config) {
388
-                if (! in_array($message_type_name, $active_message_type_names)) {
387
+            foreach ($messenger_settings['settings'][$messenger.'-message_types'] as $message_type_name => $message_type_config) {
388
+                if ( ! in_array($message_type_name, $active_message_type_names)) {
389 389
                     $active_message_type_names[] = $message_type_name;
390 390
                 }
391 391
             }
@@ -406,9 +406,9 @@  discard block
 block discarded – undo
406 406
         $installed_message_types   = $this->installed_message_types();
407 407
         $active_message_type_names = $this->list_of_active_message_types();
408 408
         foreach ($active_message_type_names as $active_message_type_name) {
409
-            if (isset($installed_message_types[ $active_message_type_name ])) {
410
-                $active_message_types[ $active_message_type_name ] =
411
-                    $installed_message_types[ $active_message_type_name ];
409
+            if (isset($installed_message_types[$active_message_type_name])) {
410
+                $active_message_types[$active_message_type_name] =
411
+                    $installed_message_types[$active_message_type_name];
412 412
             }
413 413
         }
414 414
         return $active_message_types;
@@ -423,7 +423,7 @@  discard block
 block discarded – undo
423 423
         if (empty($this->_installed_message_types)) {
424 424
             $this->message_type_collection()->rewind();
425 425
             while ($this->message_type_collection()->valid()) {
426
-                $this->_installed_message_types[ $this->message_type_collection()->current()->name ] =
426
+                $this->_installed_message_types[$this->message_type_collection()->current()->name] =
427 427
                     $this->message_type_collection()->current();
428 428
                 $this->message_type_collection()->next();
429 429
             }
@@ -463,7 +463,7 @@  discard block
 block discarded – undo
463 463
     public function valid_message_type_for_messenger(EE_messenger $messenger, $message_type_name)
464 464
     {
465 465
         $valid_message_types = $messenger->get_valid_message_types();
466
-        if (! in_array($message_type_name, $valid_message_types)) {
466
+        if ( ! in_array($message_type_name, $valid_message_types)) {
467 467
             throw new EE_Error(
468 468
                 sprintf(
469 469
                     esc_html__(
@@ -593,7 +593,7 @@  discard block
 block discarded – undo
593 593
      */
594 594
     public function ensure_messenger_is_active($messenger_name, $update_option = true)
595 595
     {
596
-        if (! isset($this->_active_messengers[ $messenger_name ])) {
596
+        if ( ! isset($this->_active_messengers[$messenger_name])) {
597 597
             try {
598 598
                 $this->activate_messenger($messenger_name, [], $update_option);
599 599
             } catch (EE_Error $e) {
@@ -640,7 +640,7 @@  discard block
 block discarded – undo
640 640
             $this->ensure_messenger_is_active($messenger_name, $update_option);
641 641
         }
642 642
 
643
-        if (! empty($not_installed)) {
643
+        if ( ! empty($not_installed)) {
644 644
             EE_Error::add_error(
645 645
                 sprintf(
646 646
                     esc_html__(
@@ -676,7 +676,7 @@  discard block
 block discarded – undo
676 676
             // ensure messenger is active (that's an inherent coupling between active message types and the
677 677
             // messenger they are being activated for.
678 678
             try {
679
-                if (! $this->is_message_type_active_for_messenger($messenger_name, $message_type_name)) {
679
+                if ( ! $this->is_message_type_active_for_messenger($messenger_name, $message_type_name)) {
680 680
                     // all is good so let's just get it active
681 681
                     $this->activate_messenger($messenger, [$message_type_name], $update_option);
682 682
                 }
@@ -740,7 +740,7 @@  discard block
 block discarded – undo
740 740
             : $this->messenger_collection()->get_by_info($messenger);
741 741
         // it's inactive. Activate it.
742 742
         if ($messenger instanceof EE_messenger) {
743
-            $this->_active_messengers[ $messenger->name ] = $messenger;
743
+            $this->_active_messengers[$messenger->name] = $messenger;
744 744
             // activate incoming message types set to be activated with messenger.
745 745
             $message_type_names = $this->_activate_message_types($messenger, $message_type_names);
746 746
             // setup any initial settings for the messenger if necessary.
@@ -752,7 +752,7 @@  discard block
 block discarded – undo
752 752
             // generate new templates if necessary and ensure all related templates that are already in the database are
753 753
             // marked active.  Note, this will also deactivate a message type for a messenger if the template
754 754
             // cannot be successfully created during its attempt (only happens for global template attempts).
755
-            if (! empty($message_type_names)) {
755
+            if ( ! empty($message_type_names)) {
756 756
                 $templates = EEH_MSG_Template::generate_new_templates($messenger->name, $message_type_names, 0, true);
757 757
                 EEH_MSG_Template::update_to_active([$messenger->name], $message_type_names);
758 758
             }
@@ -776,13 +776,13 @@  discard block
 block discarded – undo
776 776
         // things have never been initialized (which should happen on EEH_Activation::generate_message_templates).
777 777
         // So ONLY then do we need to actually grab defaults and cycle through them.  Otherwise we
778 778
         // only override _active_message_types when an explicit array of $message_type_names has been provided.
779
-        $message_type_names = empty($message_type_names) && ! isset($this->_active_message_types[ $messenger->name ])
779
+        $message_type_names = empty($message_type_names) && ! isset($this->_active_message_types[$messenger->name])
780 780
             ? $messenger->get_default_message_types()
781 781
             : (array) $message_type_names;
782 782
 
783 783
         // now we ALWAYS need to make sure that the messenger is active for the message types we're activating!
784
-        if (! isset($this->_active_message_types[ $messenger->name ])) {
785
-            $this->_active_message_types[ $messenger->name ]['settings'] = [];
784
+        if ( ! isset($this->_active_message_types[$messenger->name])) {
785
+            $this->_active_message_types[$messenger->name]['settings'] = [];
786 786
         }
787 787
 
788 788
         if ($message_type_names) {
@@ -823,16 +823,16 @@  discard block
 block discarded – undo
823 823
         if ($message_type instanceof EE_message_type) {
824 824
             $default_settings = $message_type->get_admin_settings_fields();
825 825
             foreach ($default_settings as $field => $values) {
826
-                if (isset($new_settings[ $field ])) {
827
-                    $existing_settings[ $field ] = $new_settings[ $field ];
826
+                if (isset($new_settings[$field])) {
827
+                    $existing_settings[$field] = $new_settings[$field];
828 828
                     continue;
829 829
                 }
830
-                if (! isset($existing_settings[ $field ])) {
831
-                    $existing_settings[ $field ] = $values['default'];
830
+                if ( ! isset($existing_settings[$field])) {
831
+                    $existing_settings[$field] = $values['default'];
832 832
                 }
833 833
             }
834 834
         }
835
-        $this->_active_message_types[ $messenger_name ]['settings'][ $messenger_name . '-message_types' ][ $message_type_name ]['settings'] =
835
+        $this->_active_message_types[$messenger_name]['settings'][$messenger_name.'-message_types'][$message_type_name]['settings'] =
836 836
             $existing_settings;
837 837
     }
838 838
 
@@ -855,12 +855,12 @@  discard block
 block discarded – undo
855 855
         }
856 856
 
857 857
         // make sure this messenger has a record in the has_activated array
858
-        if (! isset($this->_has_activated_messengers_and_message_types[ $messenger->name ])) {
859
-            $this->_has_activated_messengers_and_message_types[ $messenger->name ] = [];
858
+        if ( ! isset($this->_has_activated_messengers_and_message_types[$messenger->name])) {
859
+            $this->_has_activated_messengers_and_message_types[$messenger->name] = [];
860 860
         }
861 861
         // check if message type has already been added
862
-        if (! in_array($message_type_name, $this->_has_activated_messengers_and_message_types[ $messenger->name ])) {
863
-            $this->_has_activated_messengers_and_message_types[ $messenger->name ][] = $message_type_name;
862
+        if ( ! in_array($message_type_name, $this->_has_activated_messengers_and_message_types[$messenger->name])) {
863
+            $this->_has_activated_messengers_and_message_types[$messenger->name][] = $message_type_name;
864 864
         }
865 865
     }
866 866
 
@@ -878,17 +878,17 @@  discard block
 block discarded – undo
878 878
         $messenger = $this->get_messenger($messenger_name);
879 879
         if ($messenger instanceof EE_messenger) {
880 880
             $msgr_settings = $messenger->get_admin_settings_fields();
881
-            if (! empty($msgr_settings)) {
881
+            if ( ! empty($msgr_settings)) {
882 882
                 foreach ($msgr_settings as $field => $value) {
883 883
                     // is there a new setting for this?
884
-                    if (isset($new_settings[ $field ])) {
885
-                        $this->_active_message_types[ $messenger->name ]['settings'][ $field ] =
886
-                            $new_settings[ $field ];
884
+                    if (isset($new_settings[$field])) {
885
+                        $this->_active_message_types[$messenger->name]['settings'][$field] =
886
+                            $new_settings[$field];
887 887
                         continue;
888 888
                     }
889 889
                     // only set the default if it isn't already set.
890
-                    if (! isset($this->_active_message_types[ $messenger->name ]['settings'][ $field ])) {
891
-                        $this->_active_message_types[ $messenger->name ]['settings'][ $field ] = $value;
890
+                    if ( ! isset($this->_active_message_types[$messenger->name]['settings'][$field])) {
891
+                        $this->_active_message_types[$messenger->name]['settings'][$field] = $value;
892 892
                     }
893 893
                 }
894 894
             }
@@ -908,8 +908,8 @@  discard block
 block discarded – undo
908 908
         if ($messenger_name instanceof EE_messenger) {
909 909
             $messenger_name = $messenger_name->name;
910 910
         }
911
-        unset($this->_active_messengers[ $messenger_name ]);
912
-        unset($this->_active_message_types[ $messenger_name ]);
911
+        unset($this->_active_messengers[$messenger_name]);
912
+        unset($this->_active_message_types[$messenger_name]);
913 913
         $this->_message_template_group_model->deactivate_message_template_groups_for($messenger_name);
914 914
         $this->update_active_messengers_option();
915 915
     }
@@ -932,7 +932,7 @@  discard block
 block discarded – undo
932 932
         }
933 933
         foreach ($this->_active_message_types as $messenger_name => $settings) {
934 934
             unset(
935
-                $this->_active_message_types[ $messenger_name ]['settings'][ $messenger_name . '-message_types' ][ $message_type_name ]
935
+                $this->_active_message_types[$messenger_name]['settings'][$messenger_name.'-message_types'][$message_type_name]
936 936
             );
937 937
 
938 938
             // we always record (even on deactivation) that a message type has been activated because there should at
@@ -958,7 +958,7 @@  discard block
 block discarded – undo
958 958
     {
959 959
         $this->_initialize_collections();
960 960
         if ($this->is_message_type_active_for_messenger($messenger_name, $message_type_name)) {
961
-            unset($this->_active_message_types[ $messenger_name ]['settings'][ $messenger_name . '-message_types' ][ $message_type_name ]);
961
+            unset($this->_active_message_types[$messenger_name]['settings'][$messenger_name.'-message_types'][$message_type_name]);
962 962
         }
963 963
         $this->_message_template_group_model->deactivate_message_template_groups_for(
964 964
             [$messenger_name],
@@ -1021,23 +1021,23 @@  discard block
 block discarded – undo
1021 1021
             ? 'slugs'
1022 1022
             : 'all';
1023 1023
         // check if contexts has been setup yet.
1024
-        if (empty($this->_contexts[ $key ])) {
1024
+        if (empty($this->_contexts[$key])) {
1025 1025
             // So let's get all active message type objects and loop through to get all unique contexts
1026 1026
             foreach ($this->get_active_message_type_objects() as $message_type) {
1027 1027
                 if ($message_type instanceof EE_message_type) {
1028 1028
                     $message_type_contexts = $message_type->get_contexts();
1029 1029
                     if ($slugs_only) {
1030 1030
                         foreach ($message_type_contexts as $context => $context_details) {
1031
-                            $this->_contexts[ $key ][ $context ] = $context_details['label'];
1031
+                            $this->_contexts[$key][$context] = $context_details['label'];
1032 1032
                         }
1033 1033
                     } else {
1034
-                        $this->_contexts[ $key ][ $message_type->name ] = $message_type_contexts;
1034
+                        $this->_contexts[$key][$message_type->name] = $message_type_contexts;
1035 1035
                     }
1036 1036
                 }
1037 1037
             }
1038 1038
         }
1039
-        return ! empty($this->_contexts[ $key ])
1040
-            ? $this->_contexts[ $key ]
1039
+        return ! empty($this->_contexts[$key])
1040
+            ? $this->_contexts[$key]
1041 1041
             : [];
1042 1042
     }
1043 1043
 
@@ -1058,7 +1058,7 @@  discard block
 block discarded – undo
1058 1058
         $all_message_types_valid           = true;
1059 1059
         // loop through list of active message types and verify they are installed.
1060 1060
         foreach ($list_of_active_message_type_names as $message_type_name) {
1061
-            if (! isset($installed_message_types[ $message_type_name ])) {
1061
+            if ( ! isset($installed_message_types[$message_type_name])) {
1062 1062
                 $this->remove_message_type_has_been_activated_from_all_messengers(
1063 1063
                     $message_type_name,
1064 1064
                     true
@@ -1085,8 +1085,8 @@  discard block
 block discarded – undo
1085 1085
     public function has_message_type_been_activated_for_messenger($message_type_name, $messenger_name)
1086 1086
     {
1087 1087
         $has_activated = $this->get_has_activated_messengers_option();
1088
-        return isset($has_activated[ $messenger_name ])
1089
-               && in_array($message_type_name, $has_activated[ $messenger_name ]);
1088
+        return isset($has_activated[$messenger_name])
1089
+               && in_array($message_type_name, $has_activated[$messenger_name]);
1090 1090
     }
1091 1091
 
1092 1092
 
@@ -1115,11 +1115,11 @@  discard block
 block discarded – undo
1115 1115
             return;
1116 1116
         }
1117 1117
         $has_activated        = $this->get_has_activated_messengers_option();
1118
-        $key_for_message_type = isset($has_activated[ $messenger_name ])
1119
-            ? array_search($message_type_name, $has_activated[ $messenger_name ], true)
1118
+        $key_for_message_type = isset($has_activated[$messenger_name])
1119
+            ? array_search($message_type_name, $has_activated[$messenger_name], true)
1120 1120
             : false;
1121 1121
         if ($key_for_message_type !== false) {
1122
-            unset($has_activated[ $messenger_name ][ $key_for_message_type ]);
1122
+            unset($has_activated[$messenger_name][$key_for_message_type]);
1123 1123
             $this->update_has_activated_messengers_option($has_activated);
1124 1124
             // reset the internal cached property
1125 1125
             $this->get_has_activated_messengers_option(true);
Please login to merge, or discard this patch.
core/libraries/messages/EE_Message_Factory.lib.php 1 patch
Indentation   +136 added lines, -136 removed lines patch added patch discarded remove patch
@@ -12,140 +12,140 @@
 block discarded – undo
12 12
  */
13 13
 class EE_Message_Factory
14 14
 {
15
-    protected static ?EE_Message_Factory $_instance = null;
16
-
17
-    protected EE_Message_Resource_Manager $_message_resource_manager;
18
-
19
-
20
-    /**
21
-     * EE_Message_Factory constructor.
22
-     *
23
-     * @param EE_Message_Resource_Manager $Message_Resource_Manager
24
-     */
25
-    protected function __construct(EE_Message_Resource_Manager $Message_Resource_Manager)
26
-    {
27
-        $this->_message_resource_manager = $Message_Resource_Manager;
28
-    }
29
-
30
-
31
-    /**
32
-     * @singleton method used to instantiate class object
33
-     * @access    public
34
-     * @param EE_Message_Resource_Manager $Message_Resource_Manager
35
-     * @return EE_Message_Factory instance
36
-     */
37
-    public static function instance(EE_Message_Resource_Manager $Message_Resource_Manager): ?EE_Message_Factory
38
-    {
39
-        // check if class object is instantiated, and instantiated properly
40
-        if (! self::$_instance instanceof EE_Message_Factory) {
41
-            self::$_instance = new EE_Message_Factory($Message_Resource_Manager);
42
-        }
43
-        return self::$_instance;
44
-    }
45
-
46
-
47
-    /**
48
-     * @param array $props_n_values
49
-     * @return EE_Message
50
-     */
51
-    public static function create(array $props_n_values = []): EE_Message
52
-    {
53
-        /** @type EE_Message_Factory $Message_Factory */
54
-        $Message_Factory = LoaderFactory::getShared('EE_Message_Factory');
55
-        return $Message_Factory->_create($props_n_values);
56
-    }
57
-
58
-
59
-    /**
60
-     * @param EE_Message $message
61
-     * @return EE_Message
62
-     */
63
-    public static function set_messenger_and_message_type(EE_Message $message): EE_Message
64
-    {
65
-        /** @type EE_Message_Factory $Message_Factory */
66
-        $Message_Factory = LoaderFactory::getShared('EE_Message_Factory');
67
-        return $Message_Factory->_set_messenger_and_message_type($message);
68
-    }
69
-
70
-
71
-    /**
72
-     * @param EE_Message $message
73
-     * @return EE_Message
74
-     */
75
-    public static function set_messenger(EE_Message $message): EE_Message
76
-    {
77
-        /** @type EE_Message_Factory $Message_Factory */
78
-        $Message_Factory = LoaderFactory::getShared('EE_Message_Factory');
79
-        return $Message_Factory->_set_messenger($message);
80
-    }
81
-
82
-
83
-    /**
84
-     * @param EE_Message $message
85
-     * @return EE_Message
86
-     */
87
-    public static function set_message_type(EE_Message $message): EE_Message
88
-    {
89
-        /** @type EE_Message_Factory $Message_Factory */
90
-        $Message_Factory = LoaderFactory::getShared('EE_Message_Factory');
91
-        return $Message_Factory->_set_message_type($message);
92
-    }
93
-
94
-
95
-    /**
96
-     * @param array $props_n_values
97
-     * @return EE_Message
98
-     */
99
-    protected function _create(array $props_n_values = []): EE_Message
100
-    {
101
-        $new_instance = false;
102
-        if (! empty($props_n_values['MSG_ID'])) {
103
-            $message = EE_Message::new_instance_from_db($props_n_values);
104
-        } else {
105
-            $message      = EE_Message::new_instance($props_n_values);
106
-            $new_instance = true;
107
-        }
108
-        return $this->_set_messenger_and_message_type($message, $new_instance);
109
-    }
110
-
111
-
112
-    /**
113
-     * @param EE_Message $message
114
-     * @param bool       $new_instance Whether the message type was setup from the database (false) or not (true)
115
-     * @return EE_Message
116
-     */
117
-    protected function _set_messenger_and_message_type(EE_Message $message, bool $new_instance = false): EE_Message
118
-    {
119
-        $message = $this->_set_messenger($message);
120
-        return $this->_set_message_type($message, $new_instance);
121
-    }
122
-
123
-
124
-    /**
125
-     * @param EE_Message $message
126
-     * @return EE_Message
127
-     */
128
-    protected function _set_messenger(EE_Message $message): EE_Message
129
-    {
130
-        $messenger = $this->_message_resource_manager->get_messenger($message->messenger());
131
-        if ($messenger instanceof EE_messenger) {
132
-            $message->set_messenger_object($messenger);
133
-        }
134
-        return $message;
135
-    }
136
-
137
-
138
-    /**
139
-     * @param EE_Message $message
140
-     * @param bool       $new_instance Whether the message type was setup from the database (false) or not (true)
141
-     * @return EE_Message
142
-     */
143
-    protected function _set_message_type(EE_Message $message, bool $new_instance = false): EE_Message
144
-    {
145
-        $message_type = $this->_message_resource_manager->get_message_type($message->message_type());
146
-        if ($message_type instanceof EE_message_type) {
147
-            $message->set_message_type_object($message_type, $new_instance);
148
-        }
149
-        return $message;
150
-    }
15
+	protected static ?EE_Message_Factory $_instance = null;
16
+
17
+	protected EE_Message_Resource_Manager $_message_resource_manager;
18
+
19
+
20
+	/**
21
+	 * EE_Message_Factory constructor.
22
+	 *
23
+	 * @param EE_Message_Resource_Manager $Message_Resource_Manager
24
+	 */
25
+	protected function __construct(EE_Message_Resource_Manager $Message_Resource_Manager)
26
+	{
27
+		$this->_message_resource_manager = $Message_Resource_Manager;
28
+	}
29
+
30
+
31
+	/**
32
+	 * @singleton method used to instantiate class object
33
+	 * @access    public
34
+	 * @param EE_Message_Resource_Manager $Message_Resource_Manager
35
+	 * @return EE_Message_Factory instance
36
+	 */
37
+	public static function instance(EE_Message_Resource_Manager $Message_Resource_Manager): ?EE_Message_Factory
38
+	{
39
+		// check if class object is instantiated, and instantiated properly
40
+		if (! self::$_instance instanceof EE_Message_Factory) {
41
+			self::$_instance = new EE_Message_Factory($Message_Resource_Manager);
42
+		}
43
+		return self::$_instance;
44
+	}
45
+
46
+
47
+	/**
48
+	 * @param array $props_n_values
49
+	 * @return EE_Message
50
+	 */
51
+	public static function create(array $props_n_values = []): EE_Message
52
+	{
53
+		/** @type EE_Message_Factory $Message_Factory */
54
+		$Message_Factory = LoaderFactory::getShared('EE_Message_Factory');
55
+		return $Message_Factory->_create($props_n_values);
56
+	}
57
+
58
+
59
+	/**
60
+	 * @param EE_Message $message
61
+	 * @return EE_Message
62
+	 */
63
+	public static function set_messenger_and_message_type(EE_Message $message): EE_Message
64
+	{
65
+		/** @type EE_Message_Factory $Message_Factory */
66
+		$Message_Factory = LoaderFactory::getShared('EE_Message_Factory');
67
+		return $Message_Factory->_set_messenger_and_message_type($message);
68
+	}
69
+
70
+
71
+	/**
72
+	 * @param EE_Message $message
73
+	 * @return EE_Message
74
+	 */
75
+	public static function set_messenger(EE_Message $message): EE_Message
76
+	{
77
+		/** @type EE_Message_Factory $Message_Factory */
78
+		$Message_Factory = LoaderFactory::getShared('EE_Message_Factory');
79
+		return $Message_Factory->_set_messenger($message);
80
+	}
81
+
82
+
83
+	/**
84
+	 * @param EE_Message $message
85
+	 * @return EE_Message
86
+	 */
87
+	public static function set_message_type(EE_Message $message): EE_Message
88
+	{
89
+		/** @type EE_Message_Factory $Message_Factory */
90
+		$Message_Factory = LoaderFactory::getShared('EE_Message_Factory');
91
+		return $Message_Factory->_set_message_type($message);
92
+	}
93
+
94
+
95
+	/**
96
+	 * @param array $props_n_values
97
+	 * @return EE_Message
98
+	 */
99
+	protected function _create(array $props_n_values = []): EE_Message
100
+	{
101
+		$new_instance = false;
102
+		if (! empty($props_n_values['MSG_ID'])) {
103
+			$message = EE_Message::new_instance_from_db($props_n_values);
104
+		} else {
105
+			$message      = EE_Message::new_instance($props_n_values);
106
+			$new_instance = true;
107
+		}
108
+		return $this->_set_messenger_and_message_type($message, $new_instance);
109
+	}
110
+
111
+
112
+	/**
113
+	 * @param EE_Message $message
114
+	 * @param bool       $new_instance Whether the message type was setup from the database (false) or not (true)
115
+	 * @return EE_Message
116
+	 */
117
+	protected function _set_messenger_and_message_type(EE_Message $message, bool $new_instance = false): EE_Message
118
+	{
119
+		$message = $this->_set_messenger($message);
120
+		return $this->_set_message_type($message, $new_instance);
121
+	}
122
+
123
+
124
+	/**
125
+	 * @param EE_Message $message
126
+	 * @return EE_Message
127
+	 */
128
+	protected function _set_messenger(EE_Message $message): EE_Message
129
+	{
130
+		$messenger = $this->_message_resource_manager->get_messenger($message->messenger());
131
+		if ($messenger instanceof EE_messenger) {
132
+			$message->set_messenger_object($messenger);
133
+		}
134
+		return $message;
135
+	}
136
+
137
+
138
+	/**
139
+	 * @param EE_Message $message
140
+	 * @param bool       $new_instance Whether the message type was setup from the database (false) or not (true)
141
+	 * @return EE_Message
142
+	 */
143
+	protected function _set_message_type(EE_Message $message, bool $new_instance = false): EE_Message
144
+	{
145
+		$message_type = $this->_message_resource_manager->get_message_type($message->message_type());
146
+		if ($message_type instanceof EE_message_type) {
147
+			$message->set_message_type_object($message_type, $new_instance);
148
+		}
149
+		return $message;
150
+	}
151 151
 }
Please login to merge, or discard this patch.
core/libraries/batch/JobHandlers/RegistrationsReport.php 2 patches
Indentation   +640 added lines, -640 removed lines patch added patch discarded remove patch
@@ -47,644 +47,644 @@
 block discarded – undo
47 47
  */
48 48
 class RegistrationsReport extends JobHandlerFile
49 49
 {
50
-    // phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
51
-    // phpcs:disable PSR2.Methods.MethodDeclaration.Underscore
52
-    /**
53
-     * Performs any necessary setup for starting the job. This is also a good
54
-     * place to set up the $job_arguments which will be used for subsequent HTTP requests
55
-     * when continue_job will be called
56
-     *
57
-     * @param JobParameters $job_parameters
58
-     * @return JobStepResponse
59
-     * @throws BatchRequestException
60
-     * @throws EE_Error
61
-     * @throws ReflectionException
62
-     * @throws Exception
63
-     */
64
-    public function create_job(JobParameters $job_parameters): JobStepResponse
65
-    {
66
-        $event_id = absint($job_parameters->request_datum('EVT_ID', '0'));
67
-        $DTT_ID   = absint($job_parameters->request_datum('DTT_ID', '0'));
68
-        if (! EE_Capabilities::instance()->current_user_can('ee_read_registrations', 'generating_report')) {
69
-            throw new BatchRequestException(
70
-                esc_html__('You do not have permission to view registrations', 'event_espresso')
71
-            );
72
-        }
73
-        $filepath = $this->create_file_from_job_with_name(
74
-            $job_parameters->job_id(),
75
-            $this->get_filename()
76
-        );
77
-        $job_parameters->add_extra_data('filepath', $filepath);
78
-
79
-        if ($job_parameters->request_datum('use_filters', false)) {
80
-            $query_params = maybe_unserialize($job_parameters->request_datum('filters', []));
81
-        } else {
82
-            $query_params = [
83
-                [ 'Ticket.TKT_deleted' => ['IN', [true, false]] ],
84
-                'order_by'   => ['Transaction.TXN_ID' => 'asc', 'REG_count' => 'asc'],
85
-                'force_join' => ['Transaction', 'Ticket', 'Attendee'],
86
-                'caps'       => EEM_Base::caps_read_admin,
87
-            ];
88
-            if ($event_id) {
89
-                $query_params[0]['EVT_ID'] = $event_id;
90
-            } else {
91
-                $query_params['force_join'][] = 'Event';
92
-            }
93
-        }
94
-        // unless the query params already include a status,
95
-        // we want to exclude registrations from failed or abandoned transactions
96
-        if (! isset($query_params[0]['Transaction.STS_ID'])) {
97
-            $query_params[0]['OR'] = [
98
-                // don't include registrations from failed or abandoned transactions...
99
-                'Transaction.STS_ID' => [
100
-                    'NOT IN',
101
-                    [
102
-                        EEM_Transaction::failed_status_code,
103
-                        EEM_Transaction::abandoned_status_code,
104
-                    ],
105
-                ],
106
-                // unless the registration is approved,
107
-                // in which case include it regardless of transaction status
108
-                'STS_ID' => RegStatus::APPROVED,
109
-            ];
110
-        }
111
-
112
-        if (! isset($query_params['force_join'])) {
113
-            $query_params['force_join'] = ['Event', 'Transaction', 'Ticket', 'Attendee'];
114
-        }
115
-
116
-        $return_url_args = [];
117
-        parse_str(
118
-            parse_url(
119
-                $job_parameters->request_datum('return_url'),
120
-                PHP_URL_QUERY
121
-            ),
122
-            $return_url_args
123
-        );
124
-
125
-        if (
126
-            isset($return_url_args['orderby'], $return_url_args['order'])
127
-            && $return_url_args['orderby'] === 'ATT_lname'
128
-        ) {
129
-            $query_params['order_by'] = [
130
-                'Attendee.ATT_lname' => $return_url_args['order'],
131
-                'Attendee.ATT_fname' => $return_url_args['order'],
132
-                'REG_ID' => $return_url_args['order']
133
-            ];
134
-        }
135
-
136
-        $query_params = apply_filters(
137
-            'FHEE__EE_Export__report_registration_for_event',
138
-            $query_params,
139
-            $event_id
140
-        );
141
-
142
-        $utc_timezone = new DateTimeZone('UTC');
143
-        $site_timezone = new DateTimeZone(EEH_DTT_Helper::get_timezone());
144
-        $query_params = $this->convertDateStringsToObjects($query_params, $site_timezone, $utc_timezone);
145
-
146
-        $job_parameters->add_extra_data('query_params', $query_params);
147
-        $question_labels = $this->_get_question_labels($query_params);
148
-        $job_parameters->add_extra_data('question_labels', $question_labels);
149
-        $job_parameters->set_job_size($this->count_units_to_process($query_params));
150
-        // we need to set the header columns
151
-        // but to do that we need to process one row so that we can extract ALL the column headers
152
-        $csv_data_for_row = $this->get_csv_data_for(
153
-            $event_id,
154
-            0,
155
-            1,
156
-            $question_labels,
157
-            $query_params,
158
-            $DTT_ID
159
-        );
160
-        // but we don't want to write any actual data yet...
161
-        // so let's blank out all the values for that first row
162
-        array_walk(
163
-            $csv_data_for_row[0],
164
-            function (&$value) {
165
-                $value = null;
166
-            }
167
-        );
168
-
169
-        EEH_Export::write_data_array_to_csv($filepath, $csv_data_for_row, true, true);
170
-        $this->updateTextHeader(
171
-            esc_html__('Registrations report started successfully...', 'event_espresso')
172
-        );
173
-        return new JobStepResponse($job_parameters, $this->feedback);
174
-    }
175
-
176
-
177
-    /**
178
-     * Gets the filename
179
-     *
180
-     * @return string
181
-     */
182
-    protected function get_filename(): string
183
-    {
184
-        return apply_filters(
185
-            'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport__get_filename',
186
-            sprintf(
187
-                'event-espresso-registrations-%s.csv',
188
-                str_replace([':', ' '], '-', current_time('mysql'))
189
-            )
190
-        );
191
-    }
192
-
193
-
194
-    /**
195
-     * Gets the questions which are to be used for this report,
196
-     * so they can be remembered for later
197
-     *
198
-     * @param array $registration_query_params
199
-     * @return array question admin labels to be used for this report
200
-     * @throws EE_Error
201
-     * @throws ReflectionException
202
-     */
203
-    protected function _get_question_labels(array $registration_query_params): array
204
-    {
205
-        $where                 = $registration_query_params[0] ?? null;
206
-        $question_query_params = [];
207
-        if ($where !== null) {
208
-            $question_query_params = [
209
-                $this->_change_registration_where_params_to_question_where_params($registration_query_params[0]),
210
-            ];
211
-        }
212
-        // Make sure it's not a system question
213
-        $question_query_params[0]['OR*not-system-questions'] = [
214
-            'QST_system'      => '',
215
-            'QST_system*null' => ['IS_NULL']
216
-        ];
217
-        if (
218
-            apply_filters(
219
-                'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport___get_question_labels__only_include_answered_questions',
220
-                false,
221
-                $registration_query_params
222
-            )
223
-        ) {
224
-            $question_query_params[0]['Answer.ANS_ID'] = ['IS_NOT_NULL'];
225
-        }
226
-        $question_query_params['order_by'] = [
227
-            'Question_Group_Question.QGQ_order' => 'ASC',
228
-            'QST_order' => 'ASC',
229
-            'QST_admin_label' => 'ASC'
230
-        ];
231
-        $question_query_params['group_by'] = ['QST_ID'];
232
-        return array_unique(EEM_Question::instance()->get_col($question_query_params, 'QST_admin_label'));
233
-    }
234
-
235
-
236
-    /**
237
-     * Takes where params meant for registrations and changes them to work for questions
238
-     *
239
-     * @param array $reg_where_params
240
-     * @return array
241
-     * @throws EE_Error
242
-     * @throws ReflectionException
243
-     */
244
-    protected function _change_registration_where_params_to_question_where_params(array $reg_where_params): array
245
-    {
246
-        $question_where_params = [];
247
-        foreach ($reg_where_params as $key => $val) {
248
-            if (EEM_Registration::instance()->is_logic_query_param_key($key)) {
249
-                $question_where_params[ $key ] =
250
-                    $this->_change_registration_where_params_to_question_where_params($val);
251
-            } else {
252
-                // it's a normal where condition
253
-                $question_where_params[ 'Question_Group.Event.Registration.' . $key ] = $val;
254
-            }
255
-        }
256
-        return $question_where_params;
257
-    }
258
-
259
-
260
-    /**
261
-     * Performs another step of the job
262
-     *
263
-     * @param JobParameters $job_parameters
264
-     * @param int           $batch_size
265
-     * @return JobStepResponse
266
-     * @throws EE_Error
267
-     * @throws ReflectionException
268
-     */
269
-    public function continue_job(JobParameters $job_parameters, int $batch_size = 50): JobStepResponse
270
-    {
271
-        if ($job_parameters->units_processed() < $job_parameters->job_size()) {
272
-            $csv_data = $this->get_csv_data_for(
273
-                (int) $job_parameters->request_datum('EVT_ID', '0'),
274
-                $job_parameters->units_processed(),
275
-                $batch_size,
276
-                $job_parameters->extra_datum('question_labels'),
277
-                $job_parameters->extra_datum('query_params'),
278
-                (int) $job_parameters->request_datum('DTT_ID', '0')
279
-            );
280
-            EEH_Export::write_data_array_to_csv(
281
-                $job_parameters->extra_datum('filepath'),
282
-                $csv_data,
283
-                false
284
-            );
285
-            $units_processed = count($csv_data);
286
-            if ($units_processed) {
287
-                $job_parameters->mark_processed($units_processed);
288
-                $this->updateText(
289
-                    sprintf(
290
-                        esc_html__('Wrote %1$s rows to report CSV file...', 'event_espresso'),
291
-                        $units_processed
292
-                    )
293
-                );
294
-            }
295
-        }
296
-        $extra_response_data = ['file_url' => ''];
297
-        if ($job_parameters->units_processed() >= $job_parameters->job_size()) {
298
-            $job_parameters->set_status(JobParameters::status_complete);
299
-            $extra_response_data['file_url'] = $this->get_url_to_file($job_parameters->extra_datum('filepath'));
300
-            $this->displayJobFinalResults($job_parameters);
301
-        } else {
302
-            $job_parameters->set_status(JobParameters::status_continue);
303
-        }
304
-        return new JobStepResponse($job_parameters, $this->feedback, $extra_response_data);
305
-    }
306
-
307
-
308
-    /**
309
-     * Gets the csv data for a batch of registrations
310
-     *
311
-     * @param int|null $event_id
312
-     * @param int      $offset
313
-     * @param int      $limit
314
-     * @param array    $question_labels the IDs for all the questions which were answered by someone in this selection
315
-     * @param array    $query_params    for using where querying the model
316
-     * @param int      $DTT_ID
317
-     * @return array top-level keys are numeric, next-level keys are column headers
318
-     * @throws EE_Error
319
-     * @throws ReflectionException
320
-     */
321
-    public function get_csv_data_for(
322
-        ?int $event_id,
323
-        int $offset,
324
-        int $limit,
325
-        array $question_labels,
326
-        array $query_params,
327
-        int $DTT_ID = 0
328
-    ): array {
329
-        $reg_fields_to_include = [
330
-            'TXN_ID',
331
-            'ATT_ID',
332
-            'REG_ID',
333
-            'REG_date',
334
-            'REG_code',
335
-            'REG_count',
336
-            'REG_final_price',
337
-        ];
338
-        $att_fields_to_include = [
339
-            'ATT_fname',
340
-            'ATT_lname',
341
-            'ATT_email',
342
-            'ATT_address',
343
-            'ATT_address2',
344
-            'ATT_city',
345
-            'STA_ID',
346
-            'CNT_ISO',
347
-            'ATT_zip',
348
-            'ATT_phone',
349
-        ];
350
-
351
-        // get models
352
-        $event_model   = EEM_Event::instance();
353
-        $date_model    = EEM_Datetime::instance();
354
-        $ticket_model  = EEM_Ticket::instance();
355
-        $txn_model     = EEM_Transaction::instance();
356
-        $reg_model     = EEM_Registration::instance();
357
-        $pay_model     = EEM_Payment::instance();
358
-        $status_model  = EEM_Status::instance();
359
-
360
-        $registrations_csv_ready_array = [];
361
-        $query_params['limit']         = [$offset, $limit];
362
-        $registration_rows             = $reg_model->get_all_wpdb_results($query_params);
363
-
364
-        foreach ($registration_rows as $reg_row) {
365
-            if (! is_array($reg_row)) {
366
-                continue;
367
-            }
368
-            $reg_csv_array = [];
369
-            // registration ID
370
-            $reg_id_field = $reg_model->field_settings_for('REG_ID');
371
-            $reg_csv_array[ EEH_Export::get_column_name_for_field($reg_id_field) ] =
372
-                EEH_Export::prepare_value_from_db_for_display(
373
-                    $reg_model,
374
-                    'REG_ID',
375
-                    $reg_row[ $reg_id_field->get_qualified_column() ]
376
-                );
377
-            // ALL registrations, or is list filtered to just one?
378
-            if (! $event_id) {
379
-                // ALL registrations, so get each event's name and ID
380
-                $reg_csv_array[ esc_html__('Event', 'event_espresso') ] = sprintf(
381
-                    /* translators: 1: event name, 2: event ID */
382
-                    esc_html__('%1$s (%2$s)', 'event_espresso'),
383
-                    EEH_Export::prepare_value_from_db_for_display(
384
-                        $event_model,
385
-                        'EVT_name',
386
-                        $reg_row['Event_CPT.post_title']
387
-                    ),
388
-                    $reg_row['Event_CPT.ID']
389
-                );
390
-            }
391
-            // add attendee columns
392
-            $reg_csv_array = AttendeeCSV::addAttendeeColumns($att_fields_to_include, $reg_row, $reg_csv_array);
393
-            // add registration columns
394
-            $reg_csv_array = RegistrationCSV::addRegistrationColumns($reg_fields_to_include, $reg_row, $reg_csv_array);
395
-            // get pretty status
396
-            $stati = $status_model->localized_status(
397
-                [
398
-                    $reg_row['Registration.STS_ID']     => esc_html__('unknown', 'event_espresso'),
399
-                    $reg_row['TransactionTable.STS_ID'] => esc_html__('unknown', 'event_espresso'),
400
-                ],
401
-                false,
402
-                'sentence'
403
-            );
404
-            $is_primary_reg = $reg_row['Registration.REG_count'] == '1';
405
-
406
-            $reg_csv_array[ esc_html__('Registration Status', 'event_espresso') ] =
407
-                $stati[ $reg_row['Registration.STS_ID'] ];
408
-            // get pretty transaction status
409
-            $reg_csv_array[ esc_html__('Transaction Status', 'event_espresso') ]     =
410
-                $stati[ $reg_row['TransactionTable.STS_ID'] ];
411
-            $reg_csv_array[ esc_html__('Transaction Amount Due', 'event_espresso') ] = $is_primary_reg
412
-                ? EEH_Export::prepare_value_from_db_for_display(
413
-                    $txn_model,
414
-                    'TXN_total',
415
-                    $reg_row['TransactionTable.TXN_total'],
416
-                    'localized_float'
417
-                )
418
-                : '0.00';
419
-
420
-            $reg_csv_array[ esc_html__('Amount Paid', 'event_espresso') ]            = $is_primary_reg
421
-                ? EEH_Export::prepare_value_from_db_for_display(
422
-                    $txn_model,
423
-                    'TXN_paid',
424
-                    $reg_row['TransactionTable.TXN_paid'],
425
-                    'localized_float'
426
-                )
427
-                : '0.00';
428
-
429
-            $payment_methods                                                                  = [];
430
-            $gateway_txn_ids_etc                                                              = [];
431
-            $payment_times                                                                    = [];
432
-            if ($is_primary_reg && $reg_row['TransactionTable.TXN_ID']) {
433
-                $payments_info = $pay_model->get_all_wpdb_results(
434
-                    [
435
-                        [
436
-                            'TXN_ID' => $reg_row['TransactionTable.TXN_ID'],
437
-                            'STS_ID' => EEM_Payment::status_id_approved,
438
-                        ],
439
-                        'force_join' => ['Payment_Method'],
440
-                    ],
441
-                    ARRAY_A,
442
-                    'Payment_Method.PMD_admin_name as name, Payment.PAY_txn_id_chq_nmbr as gateway_txn_id, Payment.PAY_timestamp as payment_time'
443
-                );
444
-                [$payment_methods, $gateway_txn_ids_etc, $payment_times] = PaymentsInfoCSV::extractPaymentInfo(
445
-                    $payments_info
446
-                );
447
-            }
448
-
449
-            $reg_csv_array[ esc_html__('Payment Date(s)', 'event_espresso') ] = implode(
450
-                ',',
451
-                $payment_times
452
-            );
453
-
454
-            $reg_csv_array[ esc_html__('Payment Method(s)', 'event_espresso') ] = implode(
455
-                ',',
456
-                $payment_methods
457
-            );
458
-
459
-            $reg_csv_array[ esc_html__('Gateway Transaction ID(s)', 'event_espresso') ] = implode(
460
-                ',',
461
-                $gateway_txn_ids_etc
462
-            );
463
-
464
-            $ticket_name      = esc_html__('Unknown', 'event_espresso');
465
-            $datetime_strings = [esc_html__('Unknown', 'event_espresso')];
466
-            if ($reg_row['Ticket.TKT_ID']) {
467
-                $ticket_name       = EEH_Export::prepare_value_from_db_for_display(
468
-                    $ticket_model,
469
-                    'TKT_name',
470
-                    $reg_row['Ticket.TKT_name']
471
-                );
472
-                $datetime_strings = [];
473
-                $datetimes        = $date_model->get_all_wpdb_results(
474
-                    [
475
-                        ['Ticket.TKT_ID' => $reg_row['Ticket.TKT_ID']],
476
-                        'order_by'                 => ['DTT_EVT_start' => 'ASC'],
477
-                        'default_where_conditions' => 'none',
478
-                    ]
479
-                );
480
-                foreach ($datetimes as $datetime) {
481
-                    $datetime_strings[] = EEH_Export::prepare_value_from_db_for_display(
482
-                        $date_model,
483
-                        'DTT_EVT_start',
484
-                        $datetime['Datetime.DTT_EVT_start']
485
-                    );
486
-                }
487
-            }
488
-
489
-            $reg_csv_array[ $ticket_model->field_settings_for('TKT_name')->get_nicename() ] = $ticket_name;
490
-
491
-
492
-            $reg_csv_array[ esc_html__('Ticket Datetimes', 'event_espresso') ] = implode(
493
-                ', ',
494
-                $datetime_strings
495
-            );
496
-
497
-            // add answer columns
498
-            $reg_csv_array = AnswersCSV::addAnswerColumns($reg_row, $reg_csv_array, $question_labels);
499
-            // Include check-in data
500
-            if ($event_id && $DTT_ID) {
501
-                // get whether the user has checked in
502
-                $reg_csv_array[ esc_html__('Datetime Check-ins #', 'event_espresso') ] =
503
-                    $reg_model->count_related(
504
-                        $reg_row['Registration.REG_ID'],
505
-                        'Checkin',
506
-                        [
507
-                            [
508
-                                'DTT_ID' => $DTT_ID
509
-                            ]
510
-                        ]
511
-                    );
512
-                $datetime     = $date_model->get_one_by_ID($DTT_ID);
513
-                $checkin_rows = EEM_Checkin::instance()->get_all(
514
-                    [
515
-                        [
516
-                            'REG_ID' => $reg_row['Registration.REG_ID'],
517
-                            'DTT_ID' => $datetime->get('DTT_ID'),
518
-                        ],
519
-                    ]
520
-                );
521
-                $checkins     = [];
522
-                foreach ($checkin_rows as $checkin_row) {
523
-                    /** @var EE_Checkin $checkin_row */
524
-                    $checkin_value = CheckinsCSV::getCheckinValue($checkin_row);
525
-                    if ($checkin_value) {
526
-                        $checkins[] = $checkin_value;
527
-                    }
528
-                }
529
-                $datetime_name                   = CheckinsCSV::getDatetimeLabel($datetime);
530
-                $reg_csv_array[ $datetime_name ] = implode(' --- ', $checkins);
531
-            } elseif ($event_id) {
532
-                // get whether the user has checked in
533
-                $reg_csv_array[ esc_html__('Event Check-ins #', 'event_espresso') ] =
534
-                    $reg_model->count_related(
535
-                        $reg_row['Registration.REG_ID'],
536
-                        'Checkin'
537
-                    );
538
-
539
-                $datetimes = $date_model->get_all(
540
-                    [
541
-                        [
542
-                            'Ticket.TKT_ID' => $reg_row['Ticket.TKT_ID'],
543
-                        ],
544
-                        'order_by'                 => ['DTT_EVT_start' => 'ASC'],
545
-                        'default_where_conditions' => 'none',
546
-                    ]
547
-                );
548
-                foreach ($datetimes as $datetime) {
549
-                    if (! $datetime instanceof EE_Datetime) {
550
-                        continue;
551
-                    }
552
-
553
-                    /** @var EE_Checkin $checkin_row */
554
-                    $checkin_row = EEM_Checkin::instance()->get_one(
555
-                        [
556
-                            [
557
-                                'REG_ID' => $reg_row['Registration.REG_ID'],
558
-                                'DTT_ID' => $datetime->get('DTT_ID'),
559
-                            ],
560
-                            'limit'    => 1,
561
-                            'order_by' => [
562
-                                'CHK_ID' => 'DESC'
563
-                            ]
564
-                        ]
565
-                    );
566
-
567
-                    $checkin_value = CheckinsCSV::getCheckinValue($checkin_row);
568
-                    $datetime_name = CheckinsCSV::getDatetimeLabel($datetime);
569
-
570
-                    $reg_csv_array[ $datetime_name ] = $checkin_value;
571
-                }
572
-            }
573
-            /**
574
-             * Filter to change the contents of each row of the registrations report CSV file.
575
-             * This can be used to add or remote columns from the CSV file, or change their values.
576
-             * Note when using: all rows in the CSV should have the same columns.
577
-             *
578
-             * @param array $reg_csv_array keys are the column names, values are their cell values
579
-             * @param array $reg_row       one entry from EEM_Registration::get_all_wpdb_results()
580
-             */
581
-            $registrations_csv_ready_array[] = apply_filters(
582
-                'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport__reg_csv_array',
583
-                $reg_csv_array,
584
-                $reg_row
585
-            );
586
-        }
587
-        // if we couldn't export anything, we want to at least show the column headers
588
-        if (empty($registrations_csv_ready_array)) {
589
-            $reg_csv_array               = [];
590
-            $model_and_fields_to_include = [
591
-                'Registration' => $reg_fields_to_include,
592
-                'Attendee'     => $att_fields_to_include,
593
-            ];
594
-            foreach ($model_and_fields_to_include as $model_name => $field_list) {
595
-                $model = EE_Registry::instance()->load_model($model_name);
596
-                foreach ($field_list as $field_name) {
597
-                    $field                                                          =
598
-                        $model->field_settings_for($field_name);
599
-                    $reg_csv_array[ EEH_Export::get_column_name_for_field($field) ] = null;
600
-                }
601
-            }
602
-            $registrations_csv_ready_array[] = $reg_csv_array;
603
-        }
604
-        return $registrations_csv_ready_array;
605
-    }
606
-
607
-
608
-    /**
609
-     * recursively convert MySQL format date strings in query params array to Datetime objects
610
-     *
611
-     * @param array        $query_params
612
-     * @param DateTimeZone $site_timezone
613
-     * @param DateTimeZone $utc_timezone
614
-     * @return array
615
-     * @throws Exception
616
-     * @since 5.0.19.p
617
-     */
618
-    private function convertDateStringsToObjects(
619
-        array $query_params,
620
-        DateTimeZone $site_timezone,
621
-        DateTimeZone $utc_timezone
622
-    ): array {
623
-        foreach ($query_params as $key => $value) {
624
-            if (is_array($value)) {
625
-                $query_params[ $key ] = $this->convertDateStringsToObjects($value, $site_timezone, $utc_timezone);
626
-                continue;
627
-            }
628
-            if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $value)) {
629
-                $query_params[ $key ] = DbSafeDateTime::createFromFormat('Y-m-d H:i:s', $value, $site_timezone);
630
-                $query_params[ $key ] = $query_params[ $key ]->setTimezone($utc_timezone);
631
-            }
632
-        }
633
-        return $query_params;
634
-    }
635
-
636
-
637
-    /**
638
-     * Counts total unit to process
639
-     *
640
-     * @param array $query_params
641
-     * @return int
642
-     * @throws EE_Error
643
-     * @throws ReflectionException
644
-     */
645
-    public function count_units_to_process(array $query_params): int
646
-    {
647
-        return EEM_Registration::instance()->count(
648
-            array_diff_key(
649
-                $query_params,
650
-                array_flip(
651
-                    ['limit']
652
-                )
653
-            )
654
-        );
655
-    }
656
-
657
-
658
-    /**
659
-     * Performs any clean-up logic when we know the job is completed.
660
-     * In this case, we delete the temporary file
661
-     *
662
-     * @param JobParameters $job_parameters
663
-     * @return JobStepResponse
664
-     */
665
-    public function cleanup_job(JobParameters $job_parameters): JobStepResponse
666
-    {
667
-        $this->updateText(esc_html__('File Generation complete and downloaded', 'event_espresso'));
668
-
669
-        $this->_file_helper->delete(
670
-            EEH_File::remove_filename_from_filepath($job_parameters->extra_datum('filepath')),
671
-            true,
672
-            'd'
673
-        );
674
-        $this->updateText(esc_html__('Cleaned up temporary file', 'event_espresso'));
675
-        $this->updateText(
676
-            $this->infoWrapper(
677
-                sprintf(
678
-                    esc_html__(
679
-                        'If not automatically redirected in %1$s seconds, click here to return to the %2$sRegistrations List Table%3$s',
680
-                        'event_espresso'
681
-                    ),
682
-                    '<span id="ee-redirect-timer">10</span>',
683
-                    '<a href="' . $job_parameters->request_datum('return_url') . '">',
684
-                    '</a>'
685
-                )
686
-            )
687
-        );
688
-        return new JobStepResponse($job_parameters, $this->feedback);
689
-    }
50
+	// phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
51
+	// phpcs:disable PSR2.Methods.MethodDeclaration.Underscore
52
+	/**
53
+	 * Performs any necessary setup for starting the job. This is also a good
54
+	 * place to set up the $job_arguments which will be used for subsequent HTTP requests
55
+	 * when continue_job will be called
56
+	 *
57
+	 * @param JobParameters $job_parameters
58
+	 * @return JobStepResponse
59
+	 * @throws BatchRequestException
60
+	 * @throws EE_Error
61
+	 * @throws ReflectionException
62
+	 * @throws Exception
63
+	 */
64
+	public function create_job(JobParameters $job_parameters): JobStepResponse
65
+	{
66
+		$event_id = absint($job_parameters->request_datum('EVT_ID', '0'));
67
+		$DTT_ID   = absint($job_parameters->request_datum('DTT_ID', '0'));
68
+		if (! EE_Capabilities::instance()->current_user_can('ee_read_registrations', 'generating_report')) {
69
+			throw new BatchRequestException(
70
+				esc_html__('You do not have permission to view registrations', 'event_espresso')
71
+			);
72
+		}
73
+		$filepath = $this->create_file_from_job_with_name(
74
+			$job_parameters->job_id(),
75
+			$this->get_filename()
76
+		);
77
+		$job_parameters->add_extra_data('filepath', $filepath);
78
+
79
+		if ($job_parameters->request_datum('use_filters', false)) {
80
+			$query_params = maybe_unserialize($job_parameters->request_datum('filters', []));
81
+		} else {
82
+			$query_params = [
83
+				[ 'Ticket.TKT_deleted' => ['IN', [true, false]] ],
84
+				'order_by'   => ['Transaction.TXN_ID' => 'asc', 'REG_count' => 'asc'],
85
+				'force_join' => ['Transaction', 'Ticket', 'Attendee'],
86
+				'caps'       => EEM_Base::caps_read_admin,
87
+			];
88
+			if ($event_id) {
89
+				$query_params[0]['EVT_ID'] = $event_id;
90
+			} else {
91
+				$query_params['force_join'][] = 'Event';
92
+			}
93
+		}
94
+		// unless the query params already include a status,
95
+		// we want to exclude registrations from failed or abandoned transactions
96
+		if (! isset($query_params[0]['Transaction.STS_ID'])) {
97
+			$query_params[0]['OR'] = [
98
+				// don't include registrations from failed or abandoned transactions...
99
+				'Transaction.STS_ID' => [
100
+					'NOT IN',
101
+					[
102
+						EEM_Transaction::failed_status_code,
103
+						EEM_Transaction::abandoned_status_code,
104
+					],
105
+				],
106
+				// unless the registration is approved,
107
+				// in which case include it regardless of transaction status
108
+				'STS_ID' => RegStatus::APPROVED,
109
+			];
110
+		}
111
+
112
+		if (! isset($query_params['force_join'])) {
113
+			$query_params['force_join'] = ['Event', 'Transaction', 'Ticket', 'Attendee'];
114
+		}
115
+
116
+		$return_url_args = [];
117
+		parse_str(
118
+			parse_url(
119
+				$job_parameters->request_datum('return_url'),
120
+				PHP_URL_QUERY
121
+			),
122
+			$return_url_args
123
+		);
124
+
125
+		if (
126
+			isset($return_url_args['orderby'], $return_url_args['order'])
127
+			&& $return_url_args['orderby'] === 'ATT_lname'
128
+		) {
129
+			$query_params['order_by'] = [
130
+				'Attendee.ATT_lname' => $return_url_args['order'],
131
+				'Attendee.ATT_fname' => $return_url_args['order'],
132
+				'REG_ID' => $return_url_args['order']
133
+			];
134
+		}
135
+
136
+		$query_params = apply_filters(
137
+			'FHEE__EE_Export__report_registration_for_event',
138
+			$query_params,
139
+			$event_id
140
+		);
141
+
142
+		$utc_timezone = new DateTimeZone('UTC');
143
+		$site_timezone = new DateTimeZone(EEH_DTT_Helper::get_timezone());
144
+		$query_params = $this->convertDateStringsToObjects($query_params, $site_timezone, $utc_timezone);
145
+
146
+		$job_parameters->add_extra_data('query_params', $query_params);
147
+		$question_labels = $this->_get_question_labels($query_params);
148
+		$job_parameters->add_extra_data('question_labels', $question_labels);
149
+		$job_parameters->set_job_size($this->count_units_to_process($query_params));
150
+		// we need to set the header columns
151
+		// but to do that we need to process one row so that we can extract ALL the column headers
152
+		$csv_data_for_row = $this->get_csv_data_for(
153
+			$event_id,
154
+			0,
155
+			1,
156
+			$question_labels,
157
+			$query_params,
158
+			$DTT_ID
159
+		);
160
+		// but we don't want to write any actual data yet...
161
+		// so let's blank out all the values for that first row
162
+		array_walk(
163
+			$csv_data_for_row[0],
164
+			function (&$value) {
165
+				$value = null;
166
+			}
167
+		);
168
+
169
+		EEH_Export::write_data_array_to_csv($filepath, $csv_data_for_row, true, true);
170
+		$this->updateTextHeader(
171
+			esc_html__('Registrations report started successfully...', 'event_espresso')
172
+		);
173
+		return new JobStepResponse($job_parameters, $this->feedback);
174
+	}
175
+
176
+
177
+	/**
178
+	 * Gets the filename
179
+	 *
180
+	 * @return string
181
+	 */
182
+	protected function get_filename(): string
183
+	{
184
+		return apply_filters(
185
+			'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport__get_filename',
186
+			sprintf(
187
+				'event-espresso-registrations-%s.csv',
188
+				str_replace([':', ' '], '-', current_time('mysql'))
189
+			)
190
+		);
191
+	}
192
+
193
+
194
+	/**
195
+	 * Gets the questions which are to be used for this report,
196
+	 * so they can be remembered for later
197
+	 *
198
+	 * @param array $registration_query_params
199
+	 * @return array question admin labels to be used for this report
200
+	 * @throws EE_Error
201
+	 * @throws ReflectionException
202
+	 */
203
+	protected function _get_question_labels(array $registration_query_params): array
204
+	{
205
+		$where                 = $registration_query_params[0] ?? null;
206
+		$question_query_params = [];
207
+		if ($where !== null) {
208
+			$question_query_params = [
209
+				$this->_change_registration_where_params_to_question_where_params($registration_query_params[0]),
210
+			];
211
+		}
212
+		// Make sure it's not a system question
213
+		$question_query_params[0]['OR*not-system-questions'] = [
214
+			'QST_system'      => '',
215
+			'QST_system*null' => ['IS_NULL']
216
+		];
217
+		if (
218
+			apply_filters(
219
+				'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport___get_question_labels__only_include_answered_questions',
220
+				false,
221
+				$registration_query_params
222
+			)
223
+		) {
224
+			$question_query_params[0]['Answer.ANS_ID'] = ['IS_NOT_NULL'];
225
+		}
226
+		$question_query_params['order_by'] = [
227
+			'Question_Group_Question.QGQ_order' => 'ASC',
228
+			'QST_order' => 'ASC',
229
+			'QST_admin_label' => 'ASC'
230
+		];
231
+		$question_query_params['group_by'] = ['QST_ID'];
232
+		return array_unique(EEM_Question::instance()->get_col($question_query_params, 'QST_admin_label'));
233
+	}
234
+
235
+
236
+	/**
237
+	 * Takes where params meant for registrations and changes them to work for questions
238
+	 *
239
+	 * @param array $reg_where_params
240
+	 * @return array
241
+	 * @throws EE_Error
242
+	 * @throws ReflectionException
243
+	 */
244
+	protected function _change_registration_where_params_to_question_where_params(array $reg_where_params): array
245
+	{
246
+		$question_where_params = [];
247
+		foreach ($reg_where_params as $key => $val) {
248
+			if (EEM_Registration::instance()->is_logic_query_param_key($key)) {
249
+				$question_where_params[ $key ] =
250
+					$this->_change_registration_where_params_to_question_where_params($val);
251
+			} else {
252
+				// it's a normal where condition
253
+				$question_where_params[ 'Question_Group.Event.Registration.' . $key ] = $val;
254
+			}
255
+		}
256
+		return $question_where_params;
257
+	}
258
+
259
+
260
+	/**
261
+	 * Performs another step of the job
262
+	 *
263
+	 * @param JobParameters $job_parameters
264
+	 * @param int           $batch_size
265
+	 * @return JobStepResponse
266
+	 * @throws EE_Error
267
+	 * @throws ReflectionException
268
+	 */
269
+	public function continue_job(JobParameters $job_parameters, int $batch_size = 50): JobStepResponse
270
+	{
271
+		if ($job_parameters->units_processed() < $job_parameters->job_size()) {
272
+			$csv_data = $this->get_csv_data_for(
273
+				(int) $job_parameters->request_datum('EVT_ID', '0'),
274
+				$job_parameters->units_processed(),
275
+				$batch_size,
276
+				$job_parameters->extra_datum('question_labels'),
277
+				$job_parameters->extra_datum('query_params'),
278
+				(int) $job_parameters->request_datum('DTT_ID', '0')
279
+			);
280
+			EEH_Export::write_data_array_to_csv(
281
+				$job_parameters->extra_datum('filepath'),
282
+				$csv_data,
283
+				false
284
+			);
285
+			$units_processed = count($csv_data);
286
+			if ($units_processed) {
287
+				$job_parameters->mark_processed($units_processed);
288
+				$this->updateText(
289
+					sprintf(
290
+						esc_html__('Wrote %1$s rows to report CSV file...', 'event_espresso'),
291
+						$units_processed
292
+					)
293
+				);
294
+			}
295
+		}
296
+		$extra_response_data = ['file_url' => ''];
297
+		if ($job_parameters->units_processed() >= $job_parameters->job_size()) {
298
+			$job_parameters->set_status(JobParameters::status_complete);
299
+			$extra_response_data['file_url'] = $this->get_url_to_file($job_parameters->extra_datum('filepath'));
300
+			$this->displayJobFinalResults($job_parameters);
301
+		} else {
302
+			$job_parameters->set_status(JobParameters::status_continue);
303
+		}
304
+		return new JobStepResponse($job_parameters, $this->feedback, $extra_response_data);
305
+	}
306
+
307
+
308
+	/**
309
+	 * Gets the csv data for a batch of registrations
310
+	 *
311
+	 * @param int|null $event_id
312
+	 * @param int      $offset
313
+	 * @param int      $limit
314
+	 * @param array    $question_labels the IDs for all the questions which were answered by someone in this selection
315
+	 * @param array    $query_params    for using where querying the model
316
+	 * @param int      $DTT_ID
317
+	 * @return array top-level keys are numeric, next-level keys are column headers
318
+	 * @throws EE_Error
319
+	 * @throws ReflectionException
320
+	 */
321
+	public function get_csv_data_for(
322
+		?int $event_id,
323
+		int $offset,
324
+		int $limit,
325
+		array $question_labels,
326
+		array $query_params,
327
+		int $DTT_ID = 0
328
+	): array {
329
+		$reg_fields_to_include = [
330
+			'TXN_ID',
331
+			'ATT_ID',
332
+			'REG_ID',
333
+			'REG_date',
334
+			'REG_code',
335
+			'REG_count',
336
+			'REG_final_price',
337
+		];
338
+		$att_fields_to_include = [
339
+			'ATT_fname',
340
+			'ATT_lname',
341
+			'ATT_email',
342
+			'ATT_address',
343
+			'ATT_address2',
344
+			'ATT_city',
345
+			'STA_ID',
346
+			'CNT_ISO',
347
+			'ATT_zip',
348
+			'ATT_phone',
349
+		];
350
+
351
+		// get models
352
+		$event_model   = EEM_Event::instance();
353
+		$date_model    = EEM_Datetime::instance();
354
+		$ticket_model  = EEM_Ticket::instance();
355
+		$txn_model     = EEM_Transaction::instance();
356
+		$reg_model     = EEM_Registration::instance();
357
+		$pay_model     = EEM_Payment::instance();
358
+		$status_model  = EEM_Status::instance();
359
+
360
+		$registrations_csv_ready_array = [];
361
+		$query_params['limit']         = [$offset, $limit];
362
+		$registration_rows             = $reg_model->get_all_wpdb_results($query_params);
363
+
364
+		foreach ($registration_rows as $reg_row) {
365
+			if (! is_array($reg_row)) {
366
+				continue;
367
+			}
368
+			$reg_csv_array = [];
369
+			// registration ID
370
+			$reg_id_field = $reg_model->field_settings_for('REG_ID');
371
+			$reg_csv_array[ EEH_Export::get_column_name_for_field($reg_id_field) ] =
372
+				EEH_Export::prepare_value_from_db_for_display(
373
+					$reg_model,
374
+					'REG_ID',
375
+					$reg_row[ $reg_id_field->get_qualified_column() ]
376
+				);
377
+			// ALL registrations, or is list filtered to just one?
378
+			if (! $event_id) {
379
+				// ALL registrations, so get each event's name and ID
380
+				$reg_csv_array[ esc_html__('Event', 'event_espresso') ] = sprintf(
381
+					/* translators: 1: event name, 2: event ID */
382
+					esc_html__('%1$s (%2$s)', 'event_espresso'),
383
+					EEH_Export::prepare_value_from_db_for_display(
384
+						$event_model,
385
+						'EVT_name',
386
+						$reg_row['Event_CPT.post_title']
387
+					),
388
+					$reg_row['Event_CPT.ID']
389
+				);
390
+			}
391
+			// add attendee columns
392
+			$reg_csv_array = AttendeeCSV::addAttendeeColumns($att_fields_to_include, $reg_row, $reg_csv_array);
393
+			// add registration columns
394
+			$reg_csv_array = RegistrationCSV::addRegistrationColumns($reg_fields_to_include, $reg_row, $reg_csv_array);
395
+			// get pretty status
396
+			$stati = $status_model->localized_status(
397
+				[
398
+					$reg_row['Registration.STS_ID']     => esc_html__('unknown', 'event_espresso'),
399
+					$reg_row['TransactionTable.STS_ID'] => esc_html__('unknown', 'event_espresso'),
400
+				],
401
+				false,
402
+				'sentence'
403
+			);
404
+			$is_primary_reg = $reg_row['Registration.REG_count'] == '1';
405
+
406
+			$reg_csv_array[ esc_html__('Registration Status', 'event_espresso') ] =
407
+				$stati[ $reg_row['Registration.STS_ID'] ];
408
+			// get pretty transaction status
409
+			$reg_csv_array[ esc_html__('Transaction Status', 'event_espresso') ]     =
410
+				$stati[ $reg_row['TransactionTable.STS_ID'] ];
411
+			$reg_csv_array[ esc_html__('Transaction Amount Due', 'event_espresso') ] = $is_primary_reg
412
+				? EEH_Export::prepare_value_from_db_for_display(
413
+					$txn_model,
414
+					'TXN_total',
415
+					$reg_row['TransactionTable.TXN_total'],
416
+					'localized_float'
417
+				)
418
+				: '0.00';
419
+
420
+			$reg_csv_array[ esc_html__('Amount Paid', 'event_espresso') ]            = $is_primary_reg
421
+				? EEH_Export::prepare_value_from_db_for_display(
422
+					$txn_model,
423
+					'TXN_paid',
424
+					$reg_row['TransactionTable.TXN_paid'],
425
+					'localized_float'
426
+				)
427
+				: '0.00';
428
+
429
+			$payment_methods                                                                  = [];
430
+			$gateway_txn_ids_etc                                                              = [];
431
+			$payment_times                                                                    = [];
432
+			if ($is_primary_reg && $reg_row['TransactionTable.TXN_ID']) {
433
+				$payments_info = $pay_model->get_all_wpdb_results(
434
+					[
435
+						[
436
+							'TXN_ID' => $reg_row['TransactionTable.TXN_ID'],
437
+							'STS_ID' => EEM_Payment::status_id_approved,
438
+						],
439
+						'force_join' => ['Payment_Method'],
440
+					],
441
+					ARRAY_A,
442
+					'Payment_Method.PMD_admin_name as name, Payment.PAY_txn_id_chq_nmbr as gateway_txn_id, Payment.PAY_timestamp as payment_time'
443
+				);
444
+				[$payment_methods, $gateway_txn_ids_etc, $payment_times] = PaymentsInfoCSV::extractPaymentInfo(
445
+					$payments_info
446
+				);
447
+			}
448
+
449
+			$reg_csv_array[ esc_html__('Payment Date(s)', 'event_espresso') ] = implode(
450
+				',',
451
+				$payment_times
452
+			);
453
+
454
+			$reg_csv_array[ esc_html__('Payment Method(s)', 'event_espresso') ] = implode(
455
+				',',
456
+				$payment_methods
457
+			);
458
+
459
+			$reg_csv_array[ esc_html__('Gateway Transaction ID(s)', 'event_espresso') ] = implode(
460
+				',',
461
+				$gateway_txn_ids_etc
462
+			);
463
+
464
+			$ticket_name      = esc_html__('Unknown', 'event_espresso');
465
+			$datetime_strings = [esc_html__('Unknown', 'event_espresso')];
466
+			if ($reg_row['Ticket.TKT_ID']) {
467
+				$ticket_name       = EEH_Export::prepare_value_from_db_for_display(
468
+					$ticket_model,
469
+					'TKT_name',
470
+					$reg_row['Ticket.TKT_name']
471
+				);
472
+				$datetime_strings = [];
473
+				$datetimes        = $date_model->get_all_wpdb_results(
474
+					[
475
+						['Ticket.TKT_ID' => $reg_row['Ticket.TKT_ID']],
476
+						'order_by'                 => ['DTT_EVT_start' => 'ASC'],
477
+						'default_where_conditions' => 'none',
478
+					]
479
+				);
480
+				foreach ($datetimes as $datetime) {
481
+					$datetime_strings[] = EEH_Export::prepare_value_from_db_for_display(
482
+						$date_model,
483
+						'DTT_EVT_start',
484
+						$datetime['Datetime.DTT_EVT_start']
485
+					);
486
+				}
487
+			}
488
+
489
+			$reg_csv_array[ $ticket_model->field_settings_for('TKT_name')->get_nicename() ] = $ticket_name;
490
+
491
+
492
+			$reg_csv_array[ esc_html__('Ticket Datetimes', 'event_espresso') ] = implode(
493
+				', ',
494
+				$datetime_strings
495
+			);
496
+
497
+			// add answer columns
498
+			$reg_csv_array = AnswersCSV::addAnswerColumns($reg_row, $reg_csv_array, $question_labels);
499
+			// Include check-in data
500
+			if ($event_id && $DTT_ID) {
501
+				// get whether the user has checked in
502
+				$reg_csv_array[ esc_html__('Datetime Check-ins #', 'event_espresso') ] =
503
+					$reg_model->count_related(
504
+						$reg_row['Registration.REG_ID'],
505
+						'Checkin',
506
+						[
507
+							[
508
+								'DTT_ID' => $DTT_ID
509
+							]
510
+						]
511
+					);
512
+				$datetime     = $date_model->get_one_by_ID($DTT_ID);
513
+				$checkin_rows = EEM_Checkin::instance()->get_all(
514
+					[
515
+						[
516
+							'REG_ID' => $reg_row['Registration.REG_ID'],
517
+							'DTT_ID' => $datetime->get('DTT_ID'),
518
+						],
519
+					]
520
+				);
521
+				$checkins     = [];
522
+				foreach ($checkin_rows as $checkin_row) {
523
+					/** @var EE_Checkin $checkin_row */
524
+					$checkin_value = CheckinsCSV::getCheckinValue($checkin_row);
525
+					if ($checkin_value) {
526
+						$checkins[] = $checkin_value;
527
+					}
528
+				}
529
+				$datetime_name                   = CheckinsCSV::getDatetimeLabel($datetime);
530
+				$reg_csv_array[ $datetime_name ] = implode(' --- ', $checkins);
531
+			} elseif ($event_id) {
532
+				// get whether the user has checked in
533
+				$reg_csv_array[ esc_html__('Event Check-ins #', 'event_espresso') ] =
534
+					$reg_model->count_related(
535
+						$reg_row['Registration.REG_ID'],
536
+						'Checkin'
537
+					);
538
+
539
+				$datetimes = $date_model->get_all(
540
+					[
541
+						[
542
+							'Ticket.TKT_ID' => $reg_row['Ticket.TKT_ID'],
543
+						],
544
+						'order_by'                 => ['DTT_EVT_start' => 'ASC'],
545
+						'default_where_conditions' => 'none',
546
+					]
547
+				);
548
+				foreach ($datetimes as $datetime) {
549
+					if (! $datetime instanceof EE_Datetime) {
550
+						continue;
551
+					}
552
+
553
+					/** @var EE_Checkin $checkin_row */
554
+					$checkin_row = EEM_Checkin::instance()->get_one(
555
+						[
556
+							[
557
+								'REG_ID' => $reg_row['Registration.REG_ID'],
558
+								'DTT_ID' => $datetime->get('DTT_ID'),
559
+							],
560
+							'limit'    => 1,
561
+							'order_by' => [
562
+								'CHK_ID' => 'DESC'
563
+							]
564
+						]
565
+					);
566
+
567
+					$checkin_value = CheckinsCSV::getCheckinValue($checkin_row);
568
+					$datetime_name = CheckinsCSV::getDatetimeLabel($datetime);
569
+
570
+					$reg_csv_array[ $datetime_name ] = $checkin_value;
571
+				}
572
+			}
573
+			/**
574
+			 * Filter to change the contents of each row of the registrations report CSV file.
575
+			 * This can be used to add or remote columns from the CSV file, or change their values.
576
+			 * Note when using: all rows in the CSV should have the same columns.
577
+			 *
578
+			 * @param array $reg_csv_array keys are the column names, values are their cell values
579
+			 * @param array $reg_row       one entry from EEM_Registration::get_all_wpdb_results()
580
+			 */
581
+			$registrations_csv_ready_array[] = apply_filters(
582
+				'FHEE__EventEspressoBatchRequest__JobHandlers__RegistrationsReport__reg_csv_array',
583
+				$reg_csv_array,
584
+				$reg_row
585
+			);
586
+		}
587
+		// if we couldn't export anything, we want to at least show the column headers
588
+		if (empty($registrations_csv_ready_array)) {
589
+			$reg_csv_array               = [];
590
+			$model_and_fields_to_include = [
591
+				'Registration' => $reg_fields_to_include,
592
+				'Attendee'     => $att_fields_to_include,
593
+			];
594
+			foreach ($model_and_fields_to_include as $model_name => $field_list) {
595
+				$model = EE_Registry::instance()->load_model($model_name);
596
+				foreach ($field_list as $field_name) {
597
+					$field                                                          =
598
+						$model->field_settings_for($field_name);
599
+					$reg_csv_array[ EEH_Export::get_column_name_for_field($field) ] = null;
600
+				}
601
+			}
602
+			$registrations_csv_ready_array[] = $reg_csv_array;
603
+		}
604
+		return $registrations_csv_ready_array;
605
+	}
606
+
607
+
608
+	/**
609
+	 * recursively convert MySQL format date strings in query params array to Datetime objects
610
+	 *
611
+	 * @param array        $query_params
612
+	 * @param DateTimeZone $site_timezone
613
+	 * @param DateTimeZone $utc_timezone
614
+	 * @return array
615
+	 * @throws Exception
616
+	 * @since 5.0.19.p
617
+	 */
618
+	private function convertDateStringsToObjects(
619
+		array $query_params,
620
+		DateTimeZone $site_timezone,
621
+		DateTimeZone $utc_timezone
622
+	): array {
623
+		foreach ($query_params as $key => $value) {
624
+			if (is_array($value)) {
625
+				$query_params[ $key ] = $this->convertDateStringsToObjects($value, $site_timezone, $utc_timezone);
626
+				continue;
627
+			}
628
+			if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $value)) {
629
+				$query_params[ $key ] = DbSafeDateTime::createFromFormat('Y-m-d H:i:s', $value, $site_timezone);
630
+				$query_params[ $key ] = $query_params[ $key ]->setTimezone($utc_timezone);
631
+			}
632
+		}
633
+		return $query_params;
634
+	}
635
+
636
+
637
+	/**
638
+	 * Counts total unit to process
639
+	 *
640
+	 * @param array $query_params
641
+	 * @return int
642
+	 * @throws EE_Error
643
+	 * @throws ReflectionException
644
+	 */
645
+	public function count_units_to_process(array $query_params): int
646
+	{
647
+		return EEM_Registration::instance()->count(
648
+			array_diff_key(
649
+				$query_params,
650
+				array_flip(
651
+					['limit']
652
+				)
653
+			)
654
+		);
655
+	}
656
+
657
+
658
+	/**
659
+	 * Performs any clean-up logic when we know the job is completed.
660
+	 * In this case, we delete the temporary file
661
+	 *
662
+	 * @param JobParameters $job_parameters
663
+	 * @return JobStepResponse
664
+	 */
665
+	public function cleanup_job(JobParameters $job_parameters): JobStepResponse
666
+	{
667
+		$this->updateText(esc_html__('File Generation complete and downloaded', 'event_espresso'));
668
+
669
+		$this->_file_helper->delete(
670
+			EEH_File::remove_filename_from_filepath($job_parameters->extra_datum('filepath')),
671
+			true,
672
+			'd'
673
+		);
674
+		$this->updateText(esc_html__('Cleaned up temporary file', 'event_espresso'));
675
+		$this->updateText(
676
+			$this->infoWrapper(
677
+				sprintf(
678
+					esc_html__(
679
+						'If not automatically redirected in %1$s seconds, click here to return to the %2$sRegistrations List Table%3$s',
680
+						'event_espresso'
681
+					),
682
+					'<span id="ee-redirect-timer">10</span>',
683
+					'<a href="' . $job_parameters->request_datum('return_url') . '">',
684
+					'</a>'
685
+				)
686
+			)
687
+		);
688
+		return new JobStepResponse($job_parameters, $this->feedback);
689
+	}
690 690
 }
Please login to merge, or discard this patch.
Spacing   +35 added lines, -35 removed lines patch added patch discarded remove patch
@@ -65,7 +65,7 @@  discard block
 block discarded – undo
65 65
     {
66 66
         $event_id = absint($job_parameters->request_datum('EVT_ID', '0'));
67 67
         $DTT_ID   = absint($job_parameters->request_datum('DTT_ID', '0'));
68
-        if (! EE_Capabilities::instance()->current_user_can('ee_read_registrations', 'generating_report')) {
68
+        if ( ! EE_Capabilities::instance()->current_user_can('ee_read_registrations', 'generating_report')) {
69 69
             throw new BatchRequestException(
70 70
                 esc_html__('You do not have permission to view registrations', 'event_espresso')
71 71
             );
@@ -80,7 +80,7 @@  discard block
 block discarded – undo
80 80
             $query_params = maybe_unserialize($job_parameters->request_datum('filters', []));
81 81
         } else {
82 82
             $query_params = [
83
-                [ 'Ticket.TKT_deleted' => ['IN', [true, false]] ],
83
+                ['Ticket.TKT_deleted' => ['IN', [true, false]]],
84 84
                 'order_by'   => ['Transaction.TXN_ID' => 'asc', 'REG_count' => 'asc'],
85 85
                 'force_join' => ['Transaction', 'Ticket', 'Attendee'],
86 86
                 'caps'       => EEM_Base::caps_read_admin,
@@ -93,7 +93,7 @@  discard block
 block discarded – undo
93 93
         }
94 94
         // unless the query params already include a status,
95 95
         // we want to exclude registrations from failed or abandoned transactions
96
-        if (! isset($query_params[0]['Transaction.STS_ID'])) {
96
+        if ( ! isset($query_params[0]['Transaction.STS_ID'])) {
97 97
             $query_params[0]['OR'] = [
98 98
                 // don't include registrations from failed or abandoned transactions...
99 99
                 'Transaction.STS_ID' => [
@@ -109,7 +109,7 @@  discard block
 block discarded – undo
109 109
             ];
110 110
         }
111 111
 
112
-        if (! isset($query_params['force_join'])) {
112
+        if ( ! isset($query_params['force_join'])) {
113 113
             $query_params['force_join'] = ['Event', 'Transaction', 'Ticket', 'Attendee'];
114 114
         }
115 115
 
@@ -161,7 +161,7 @@  discard block
 block discarded – undo
161 161
         // so let's blank out all the values for that first row
162 162
         array_walk(
163 163
             $csv_data_for_row[0],
164
-            function (&$value) {
164
+            function(&$value) {
165 165
                 $value = null;
166 166
             }
167 167
         );
@@ -246,11 +246,11 @@  discard block
 block discarded – undo
246 246
         $question_where_params = [];
247 247
         foreach ($reg_where_params as $key => $val) {
248 248
             if (EEM_Registration::instance()->is_logic_query_param_key($key)) {
249
-                $question_where_params[ $key ] =
249
+                $question_where_params[$key] =
250 250
                     $this->_change_registration_where_params_to_question_where_params($val);
251 251
             } else {
252 252
                 // it's a normal where condition
253
-                $question_where_params[ 'Question_Group.Event.Registration.' . $key ] = $val;
253
+                $question_where_params['Question_Group.Event.Registration.'.$key] = $val;
254 254
             }
255 255
         }
256 256
         return $question_where_params;
@@ -362,22 +362,22 @@  discard block
 block discarded – undo
362 362
         $registration_rows             = $reg_model->get_all_wpdb_results($query_params);
363 363
 
364 364
         foreach ($registration_rows as $reg_row) {
365
-            if (! is_array($reg_row)) {
365
+            if ( ! is_array($reg_row)) {
366 366
                 continue;
367 367
             }
368 368
             $reg_csv_array = [];
369 369
             // registration ID
370 370
             $reg_id_field = $reg_model->field_settings_for('REG_ID');
371
-            $reg_csv_array[ EEH_Export::get_column_name_for_field($reg_id_field) ] =
371
+            $reg_csv_array[EEH_Export::get_column_name_for_field($reg_id_field)] =
372 372
                 EEH_Export::prepare_value_from_db_for_display(
373 373
                     $reg_model,
374 374
                     'REG_ID',
375
-                    $reg_row[ $reg_id_field->get_qualified_column() ]
375
+                    $reg_row[$reg_id_field->get_qualified_column()]
376 376
                 );
377 377
             // ALL registrations, or is list filtered to just one?
378
-            if (! $event_id) {
378
+            if ( ! $event_id) {
379 379
                 // ALL registrations, so get each event's name and ID
380
-                $reg_csv_array[ esc_html__('Event', 'event_espresso') ] = sprintf(
380
+                $reg_csv_array[esc_html__('Event', 'event_espresso')] = sprintf(
381 381
                     /* translators: 1: event name, 2: event ID */
382 382
                     esc_html__('%1$s (%2$s)', 'event_espresso'),
383 383
                     EEH_Export::prepare_value_from_db_for_display(
@@ -403,12 +403,12 @@  discard block
 block discarded – undo
403 403
             );
404 404
             $is_primary_reg = $reg_row['Registration.REG_count'] == '1';
405 405
 
406
-            $reg_csv_array[ esc_html__('Registration Status', 'event_espresso') ] =
407
-                $stati[ $reg_row['Registration.STS_ID'] ];
406
+            $reg_csv_array[esc_html__('Registration Status', 'event_espresso')] =
407
+                $stati[$reg_row['Registration.STS_ID']];
408 408
             // get pretty transaction status
409
-            $reg_csv_array[ esc_html__('Transaction Status', 'event_espresso') ]     =
410
-                $stati[ $reg_row['TransactionTable.STS_ID'] ];
411
-            $reg_csv_array[ esc_html__('Transaction Amount Due', 'event_espresso') ] = $is_primary_reg
409
+            $reg_csv_array[esc_html__('Transaction Status', 'event_espresso')]     =
410
+                $stati[$reg_row['TransactionTable.STS_ID']];
411
+            $reg_csv_array[esc_html__('Transaction Amount Due', 'event_espresso')] = $is_primary_reg
412 412
                 ? EEH_Export::prepare_value_from_db_for_display(
413 413
                     $txn_model,
414 414
                     'TXN_total',
@@ -417,7 +417,7 @@  discard block
 block discarded – undo
417 417
                 )
418 418
                 : '0.00';
419 419
 
420
-            $reg_csv_array[ esc_html__('Amount Paid', 'event_espresso') ]            = $is_primary_reg
420
+            $reg_csv_array[esc_html__('Amount Paid', 'event_espresso')] = $is_primary_reg
421 421
                 ? EEH_Export::prepare_value_from_db_for_display(
422 422
                     $txn_model,
423 423
                     'TXN_paid',
@@ -446,17 +446,17 @@  discard block
 block discarded – undo
446 446
                 );
447 447
             }
448 448
 
449
-            $reg_csv_array[ esc_html__('Payment Date(s)', 'event_espresso') ] = implode(
449
+            $reg_csv_array[esc_html__('Payment Date(s)', 'event_espresso')] = implode(
450 450
                 ',',
451 451
                 $payment_times
452 452
             );
453 453
 
454
-            $reg_csv_array[ esc_html__('Payment Method(s)', 'event_espresso') ] = implode(
454
+            $reg_csv_array[esc_html__('Payment Method(s)', 'event_espresso')] = implode(
455 455
                 ',',
456 456
                 $payment_methods
457 457
             );
458 458
 
459
-            $reg_csv_array[ esc_html__('Gateway Transaction ID(s)', 'event_espresso') ] = implode(
459
+            $reg_csv_array[esc_html__('Gateway Transaction ID(s)', 'event_espresso')] = implode(
460 460
                 ',',
461 461
                 $gateway_txn_ids_etc
462 462
             );
@@ -464,7 +464,7 @@  discard block
 block discarded – undo
464 464
             $ticket_name      = esc_html__('Unknown', 'event_espresso');
465 465
             $datetime_strings = [esc_html__('Unknown', 'event_espresso')];
466 466
             if ($reg_row['Ticket.TKT_ID']) {
467
-                $ticket_name       = EEH_Export::prepare_value_from_db_for_display(
467
+                $ticket_name = EEH_Export::prepare_value_from_db_for_display(
468 468
                     $ticket_model,
469 469
                     'TKT_name',
470 470
                     $reg_row['Ticket.TKT_name']
@@ -486,10 +486,10 @@  discard block
 block discarded – undo
486 486
                 }
487 487
             }
488 488
 
489
-            $reg_csv_array[ $ticket_model->field_settings_for('TKT_name')->get_nicename() ] = $ticket_name;
489
+            $reg_csv_array[$ticket_model->field_settings_for('TKT_name')->get_nicename()] = $ticket_name;
490 490
 
491 491
 
492
-            $reg_csv_array[ esc_html__('Ticket Datetimes', 'event_espresso') ] = implode(
492
+            $reg_csv_array[esc_html__('Ticket Datetimes', 'event_espresso')] = implode(
493 493
                 ', ',
494 494
                 $datetime_strings
495 495
             );
@@ -499,7 +499,7 @@  discard block
 block discarded – undo
499 499
             // Include check-in data
500 500
             if ($event_id && $DTT_ID) {
501 501
                 // get whether the user has checked in
502
-                $reg_csv_array[ esc_html__('Datetime Check-ins #', 'event_espresso') ] =
502
+                $reg_csv_array[esc_html__('Datetime Check-ins #', 'event_espresso')] =
503 503
                     $reg_model->count_related(
504 504
                         $reg_row['Registration.REG_ID'],
505 505
                         'Checkin',
@@ -518,7 +518,7 @@  discard block
 block discarded – undo
518 518
                         ],
519 519
                     ]
520 520
                 );
521
-                $checkins     = [];
521
+                $checkins = [];
522 522
                 foreach ($checkin_rows as $checkin_row) {
523 523
                     /** @var EE_Checkin $checkin_row */
524 524
                     $checkin_value = CheckinsCSV::getCheckinValue($checkin_row);
@@ -527,10 +527,10 @@  discard block
 block discarded – undo
527 527
                     }
528 528
                 }
529 529
                 $datetime_name                   = CheckinsCSV::getDatetimeLabel($datetime);
530
-                $reg_csv_array[ $datetime_name ] = implode(' --- ', $checkins);
530
+                $reg_csv_array[$datetime_name] = implode(' --- ', $checkins);
531 531
             } elseif ($event_id) {
532 532
                 // get whether the user has checked in
533
-                $reg_csv_array[ esc_html__('Event Check-ins #', 'event_espresso') ] =
533
+                $reg_csv_array[esc_html__('Event Check-ins #', 'event_espresso')] =
534 534
                     $reg_model->count_related(
535 535
                         $reg_row['Registration.REG_ID'],
536 536
                         'Checkin'
@@ -546,7 +546,7 @@  discard block
 block discarded – undo
546 546
                     ]
547 547
                 );
548 548
                 foreach ($datetimes as $datetime) {
549
-                    if (! $datetime instanceof EE_Datetime) {
549
+                    if ( ! $datetime instanceof EE_Datetime) {
550 550
                         continue;
551 551
                     }
552 552
 
@@ -567,7 +567,7 @@  discard block
 block discarded – undo
567 567
                     $checkin_value = CheckinsCSV::getCheckinValue($checkin_row);
568 568
                     $datetime_name = CheckinsCSV::getDatetimeLabel($datetime);
569 569
 
570
-                    $reg_csv_array[ $datetime_name ] = $checkin_value;
570
+                    $reg_csv_array[$datetime_name] = $checkin_value;
571 571
                 }
572 572
             }
573 573
             /**
@@ -596,7 +596,7 @@  discard block
 block discarded – undo
596 596
                 foreach ($field_list as $field_name) {
597 597
                     $field                                                          =
598 598
                         $model->field_settings_for($field_name);
599
-                    $reg_csv_array[ EEH_Export::get_column_name_for_field($field) ] = null;
599
+                    $reg_csv_array[EEH_Export::get_column_name_for_field($field)] = null;
600 600
                 }
601 601
             }
602 602
             $registrations_csv_ready_array[] = $reg_csv_array;
@@ -622,12 +622,12 @@  discard block
 block discarded – undo
622 622
     ): array {
623 623
         foreach ($query_params as $key => $value) {
624 624
             if (is_array($value)) {
625
-                $query_params[ $key ] = $this->convertDateStringsToObjects($value, $site_timezone, $utc_timezone);
625
+                $query_params[$key] = $this->convertDateStringsToObjects($value, $site_timezone, $utc_timezone);
626 626
                 continue;
627 627
             }
628 628
             if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $value)) {
629
-                $query_params[ $key ] = DbSafeDateTime::createFromFormat('Y-m-d H:i:s', $value, $site_timezone);
630
-                $query_params[ $key ] = $query_params[ $key ]->setTimezone($utc_timezone);
629
+                $query_params[$key] = DbSafeDateTime::createFromFormat('Y-m-d H:i:s', $value, $site_timezone);
630
+                $query_params[$key] = $query_params[$key]->setTimezone($utc_timezone);
631 631
             }
632 632
         }
633 633
         return $query_params;
@@ -680,7 +680,7 @@  discard block
 block discarded – undo
680 680
                         'event_espresso'
681 681
                     ),
682 682
                     '<span id="ee-redirect-timer">10</span>',
683
-                    '<a href="' . $job_parameters->request_datum('return_url') . '">',
683
+                    '<a href="'.$job_parameters->request_datum('return_url').'">',
684 684
                     '</a>'
685 685
                 )
686 686
             )
Please login to merge, or discard this patch.
core/libraries/payment_methods/EE_PMT_Base.lib.php 1 patch
Indentation   +834 added lines, -834 removed lines patch added patch discarded remove patch
@@ -16,838 +16,838 @@
 block discarded – undo
16 16
  */
17 17
 abstract class EE_PMT_Base
18 18
 {
19
-    const onsite  = 'on-site';
20
-
21
-    const offsite = 'off-site';
22
-
23
-    const offline = 'off-line';
24
-
25
-    /**
26
-     * @var EE_Payment_Method
27
-     */
28
-    protected $_pm_instance = null;
29
-
30
-    /**
31
-     * @var boolean
32
-     */
33
-    protected $_requires_https = false;
34
-
35
-    /**
36
-     * @var boolean
37
-     */
38
-    protected $_has_billing_form;
39
-
40
-    /**
41
-     * @var EE_Gateway
42
-     */
43
-    protected $_gateway = null;
44
-
45
-    /**
46
-     * @var EE_Payment_Method_Form
47
-     */
48
-    protected $_settings_form = null;
49
-
50
-    /**
51
-     * @var EE_Form_Section_Proper
52
-     */
53
-    protected $_billing_form = null;
54
-
55
-    /**
56
-     * @var boolean
57
-     */
58
-    protected $_cache_billing_form = true;
59
-
60
-    /**
61
-     * String of the absolute path to the folder containing this file, with a trailing slash.
62
-     * eg '/public_html/wp-site/wp-content/plugins/event-espresso/payment_methods/Invoice/'
63
-     *
64
-     * @var string|null
65
-     */
66
-    protected $_file_folder = null;
67
-
68
-    /**
69
-     * String to the absolute URL to this file (useful for getting its web-accessible resources
70
-     * like images, js, or css)
71
-     *
72
-     * @var string|null
73
-     */
74
-    protected $_file_url = null;
75
-
76
-    /**
77
-     * Pretty name for the payment method
78
-     *
79
-     * @var string|null
80
-     */
81
-    protected $_pretty_name = null;
82
-
83
-    /**
84
-     * @var string|null
85
-     */
86
-    protected $_default_button_url = null;
87
-
88
-    /**
89
-     * @var string|null
90
-     */
91
-    protected $_default_description = null;
92
-
93
-    /**
94
-     *  @var string|null
95
-     */
96
-    protected $_template_path = null;
97
-
98
-    /**
99
-     * @param EE_Payment_Method|null $pm_instance
100
-     * @throws ReflectionException
101
-     * @throws EE_Error
102
-     */
103
-    public function __construct($pm_instance = null)
104
-    {
105
-        if ($pm_instance instanceof EE_Payment_Method) {
106
-            $this->set_instance($pm_instance);
107
-        }
108
-        if ($this->_gateway) {
109
-            $this->_gateway->set_payment_model(EEM_Payment::instance());
110
-            $this->_gateway->set_payment_log(EEM_Change_Log::instance());
111
-            $this->_gateway->set_template_helper(new EEH_Template());
112
-            $this->_gateway->set_line_item_helper(new EEH_Line_Item());
113
-            $this->_gateway->set_money_helper(new EEH_Money());
114
-            $this->_gateway->set_gateway_data_formatter(new GatewayDataFormatter());
115
-            $this->_gateway->set_unsupported_character_remover(new AsciiOnly());
116
-            do_action('AHEE__EE_PMT_Base___construct__done_initializing_gateway_class', $this, $this->_gateway);
117
-        }
118
-        if (! isset($this->_has_billing_form)) {
119
-            // by default, On Site gateways have a billing form
120
-            if ($this->payment_occurs() == EE_PMT_Base::onsite) {
121
-                $this->set_has_billing_form(true);
122
-            } else {
123
-                $this->set_has_billing_form(false);
124
-            }
125
-        }
126
-
127
-        if (! $this->_pretty_name) {
128
-            throw new EE_Error(
129
-                esc_html__(
130
-                    'You must set the pretty name for the Payment Method Type in the constructor (_pretty_name), and please make it internationalized',
131
-                    'event_espresso'
132
-                )
133
-            );
134
-        }
135
-        // if the child didn't specify a default button, use the credit card one
136
-        if ($this->_default_button_url === null) {
137
-            $this->_default_button_url = EE_PLUGIN_DIR_URL . 'payment_methods/pay-by-credit-card.png';
138
-        }
139
-    }
140
-
141
-
142
-    /**
143
-     * @param boolean $has_billing_form
144
-     */
145
-    public function set_has_billing_form(bool $has_billing_form)
146
-    {
147
-        $this->_has_billing_form = filter_var($has_billing_form, FILTER_VALIDATE_BOOLEAN);
148
-    }
149
-
150
-
151
-    /**
152
-     * sets the file_folder property
153
-     */
154
-    protected function _set_file_folder()
155
-    {
156
-        $reflector          = new ReflectionClass(get_class($this));
157
-        $fn                 = $reflector->getFileName();
158
-        $this->_file_folder = dirname($fn) . '/';
159
-    }
160
-
161
-
162
-    /**
163
-     * sets the file URL with a trailing slash for this PMT
164
-     */
165
-    protected function _set_file_url()
166
-    {
167
-        $plugins_dir_fixed = str_replace('\\', '/', WP_PLUGIN_DIR);
168
-        $file_folder_fixed = str_replace('\\', '/', $this->file_folder());
169
-        $file_path         = str_replace($plugins_dir_fixed, WP_PLUGIN_URL, $file_folder_fixed);
170
-        $this->_file_url   = set_url_scheme($file_path);
171
-    }
172
-
173
-
174
-    /**
175
-     * Gets the default description on all payment methods of this type
176
-     *
177
-     * @return string
178
-     */
179
-    public function default_description(): ?string
180
-    {
181
-        return $this->_default_description;
182
-    }
183
-
184
-
185
-    /**
186
-     * Returns the folder containing the PMT child class, with a trailing slash
187
-     *
188
-     * @return string
189
-     */
190
-    public function file_folder(): ?string
191
-    {
192
-        if (! $this->_file_folder) {
193
-            $this->_set_file_folder();
194
-        }
195
-        return $this->_file_folder;
196
-    }
197
-
198
-
199
-    /**
200
-     * @return string
201
-     */
202
-    public function file_url(): ?string
203
-    {
204
-        if (! $this->_file_url) {
205
-            $this->_set_file_url();
206
-        }
207
-        return $this->_file_url;
208
-    }
209
-
210
-
211
-    /**
212
-     * Sets the payment method instance this payment method type is for.
213
-     * Its important teh payment method instance is set before
214
-     *
215
-     * @param EE_Payment_Method $payment_method_instance
216
-     * @throws EE_Error
217
-     * @throws ReflectionException
218
-     */
219
-    public function set_instance(EE_Payment_Method $payment_method_instance)
220
-    {
221
-        $this->_pm_instance = $payment_method_instance;
222
-        // if they have already requested the settings form, make sure its
223
-        // data matches this model object
224
-        if ($this->_settings_form) {
225
-            $this->settings_form()->populate_model_obj($payment_method_instance);
226
-        }
227
-        if ($this->_gateway instanceof EE_Gateway) {
228
-            $this->_gateway->set_settings($payment_method_instance->settings_array());
229
-        }
230
-    }
231
-
232
-
233
-    /**
234
-     * Gets teh form for displaying to admins where they set up the payment method
235
-     *
236
-     * @return EE_Payment_Method_Form
237
-     * @throws EE_Error
238
-     * @throws ReflectionException
239
-     */
240
-    public function settings_form(): EE_Payment_Method_Form
241
-    {
242
-        if (! $this->_settings_form) {
243
-            $this->_settings_form = $this->generate_new_settings_form();
244
-            $this->_settings_form->set_payment_method_type($this);
245
-            // if we have already assigned a model object to this pmt, make
246
-            // sure it's reflected in the form we just generated
247
-            if ($this->_pm_instance) {
248
-                $this->_settings_form->populate_model_obj($this->_pm_instance);
249
-            }
250
-        }
251
-        return $this->_settings_form;
252
-    }
253
-
254
-
255
-    /**
256
-     * Gets the form for all the settings related to this payment method type
257
-     *
258
-     * @return EE_Payment_Method_Form
259
-     */
260
-    abstract public function generate_new_settings_form();
261
-
262
-
263
-    /**
264
-     * Sets the form for settings. This may be useful if we have already received
265
-     * a form submission and have form data it in, and want to use it anytime we're showing
266
-     * this payment method type's settings form later in the request
267
-     *
268
-     * @param EE_Payment_Method_Form $form
269
-     */
270
-    public function set_settings_form(EE_Payment_Method_Form $form)
271
-    {
272
-        $this->_settings_form = $form;
273
-    }
274
-
275
-
276
-    /**
277
-     * @return boolean
278
-     */
279
-    public function has_billing_form(): bool
280
-    {
281
-        return $this->_has_billing_form;
282
-    }
283
-
284
-
285
-    /**
286
-     * Gets the form for displaying to attendees where they can enter their billing info
287
-     * which will be sent to teh gateway (can be null)
288
-     *
289
-     * @param EE_Transaction|null $transaction
290
-     * @param array               $extra_args
291
-     * @return EE_Billing_Attendee_Info_Form|EE_Billing_Info_Form|null
292
-     * @throws EE_Error
293
-     * @throws ReflectionException
294
-     */
295
-    public function billing_form(EE_Transaction $transaction = null, array $extra_args = [])
296
-    {
297
-        // has billing form already been regenerated ? or overwrite cache?
298
-        if (! $this->_billing_form instanceof EE_Billing_Info_Form || ! $this->_cache_billing_form) {
299
-            $this->_billing_form = $this->generate_new_billing_form($transaction, $extra_args);
300
-        }
301
-        // if we know who the attendee is, and this is a billing form
302
-        // that uses attendee info, populate it
303
-        if (
304
-            apply_filters(
305
-                'FHEE__populate_billing_form_fields_from_attendee',
306
-                (
307
-                    $this->_billing_form instanceof EE_Billing_Attendee_Info_Form
308
-                    && $transaction instanceof EE_Transaction
309
-                    && $transaction->primary_registration() instanceof EE_Registration
310
-                    && $transaction->primary_registration()->attendee() instanceof EE_Attendee
311
-                ),
312
-                $this->_billing_form,
313
-                $transaction
314
-            )
315
-        ) {
316
-            $this->_billing_form->populate_from_attendee($transaction->primary_registration()->attendee());
317
-        }
318
-        return $this->_billing_form;
319
-    }
320
-
321
-
322
-    /**
323
-     * Creates the billing form for this payment method type
324
-     *
325
-     * @param EE_Transaction|null $transaction
326
-     * @return EE_Billing_Info_Form|null
327
-     */
328
-    abstract public function generate_new_billing_form(EE_Transaction $transaction = null);
329
-
330
-
331
-    /**
332
-     * applies debug data to the form
333
-     *
334
-     * @param EE_Billing_Info_Form $billing_form
335
-     * @return EE_Billing_Info_Form|null
336
-     */
337
-    public function apply_billing_form_debug_settings(EE_Billing_Info_Form $billing_form)
338
-    {
339
-        return $billing_form;
340
-    }
341
-
342
-
343
-    /**
344
-     * Sets the billing form for this payment method type. You may want to use this
345
-     * if you have form
346
-     *
347
-     * @param EE_Payment_Method $form
348
-     */
349
-    public function set_billing_form(EE_Payment_Method $form)
350
-    {
351
-        $this->_billing_form = $form;
352
-    }
353
-
354
-
355
-    /**
356
-     * Returns whether this payment method requires HTTPS to be used
357
-     *
358
-     * @return boolean
359
-     */
360
-    public function requires_https(): bool
361
-    {
362
-        return $this->_requires_https;
363
-    }
364
-
365
-
366
-    /**
367
-     * @param EE_Transaction            $transaction
368
-     * @param float|null                $amount
369
-     * @param EE_Billing_Info_Form|null $billing_info
370
-     * @param string|null               $return_url
371
-     * @param string                    $fail_url
372
-     * @param string                    $method
373
-     * @param bool                      $by_admin
374
-     * @return EE_Payment
375
-     * @throws EE_Error
376
-     * @throws ReflectionException
377
-     */
378
-    public function process_payment(
379
-        EE_Transaction $transaction,
380
-        $amount = null,
381
-        $billing_info = null,
382
-        $return_url = null,
383
-        $fail_url = '',
384
-        $method = 'CART',
385
-        $by_admin = false
386
-    ) {
387
-        // @todo: add surcharge for the payment method, if any
388
-        if ($this->_gateway) {
389
-            // there is a gateway, so we're going to make a payment object
390
-            // but wait! do they already have a payment in progress that we thought was failed?
391
-            $duplicate_properties = [
392
-                'STS_ID'               => EEM_Payment::status_id_failed,
393
-                'TXN_ID'               => $transaction->ID(),
394
-                'PMD_ID'               => $this->_pm_instance->ID(),
395
-                'PAY_source'           => $method,
396
-                'PAY_amount'           => $amount !== null
397
-                    ? $amount
398
-                    : $transaction->remaining(),
399
-                'PAY_gateway_response' => null,
400
-            ];
401
-            $payment              = EEM_Payment::instance()->get_one([$duplicate_properties]);
402
-            // if we didn't already have a payment in progress for the same thing,
403
-            // then we actually want to make a new payment
404
-            if (! $payment instanceof EE_Payment) {
405
-                $payment = EE_Payment::new_instance(
406
-                    array_merge(
407
-                        $duplicate_properties,
408
-                        [
409
-                            'PAY_timestamp'       => time(),
410
-                            'PAY_txn_id_chq_nmbr' => null,
411
-                            'PAY_po_number'       => null,
412
-                            'PAY_extra_accntng'   => null,
413
-                            'PAY_details'         => null,
414
-                        ]
415
-                    )
416
-                );
417
-            }
418
-            // make sure the payment has been saved to show we started it, and so it has an ID should the gateway try to log it
419
-            $payment->save();
420
-            $billing_values = $this->_get_billing_values_from_form($billing_info);
421
-
422
-            //  Offsite Gateway
423
-            if ($this->_gateway instanceof EE_Offsite_Gateway) {
424
-                $payment = $this->_gateway->set_redirection_info(
425
-                    $payment,
426
-                    $billing_values,
427
-                    $return_url,
428
-                    EE_Config::instance()->core->txn_page_url(
429
-                        [
430
-                            'e_reg_url_link'    => $transaction->primary_registration()->reg_url_link(),
431
-                            'ee_payment_method' => $this->_pm_instance->slug(),
432
-                        ]
433
-                    ),
434
-                    $fail_url
435
-                );
436
-                $payment->save();
437
-                //  Onsite Gateway
438
-            } elseif ($this->_gateway instanceof EE_Onsite_Gateway) {
439
-                $payment = $this->_gateway->do_direct_payment($payment, $billing_values);
440
-                $payment->save();
441
-            } else {
442
-                throw new EE_Error(
443
-                    sprintf(
444
-                        esc_html__(
445
-                            'Gateway for payment method type "%s" is "%s", not a subclass of either EE_Offsite_Gateway or EE_Onsite_Gateway, or null (to indicate NO gateway)',
446
-                            'event_espresso'
447
-                        ),
448
-                        get_class($this),
449
-                        gettype($this->_gateway)
450
-                    )
451
-                );
452
-            }
453
-        } else {
454
-            // no gateway provided
455
-            // there is no payment. Must be an offline gateway
456
-            // create a payment object anyways, but dont save it
457
-            $payment = EE_Payment::new_instance(
458
-                [
459
-                    'STS_ID'        => EEM_Payment::status_id_pending,
460
-                    'TXN_ID'        => $transaction->ID(),
461
-                    'PMD_ID'        => $transaction->payment_method_ID(),
462
-                    'PAY_amount'    => 0.00,
463
-                    'PAY_timestamp' => time(),
464
-                ]
465
-            );
466
-        }
467
-
468
-        // if there is billing info, clean it and save it now
469
-        if ($billing_info instanceof EE_Billing_Attendee_Info_Form) {
470
-            $this->_save_billing_info_to_attendee($billing_info, $transaction);
471
-        }
472
-
473
-        return $payment;
474
-    }
475
-
476
-
477
-    /**
478
-     * Gets the values we want to pass onto the gateway. Normally these
479
-     * are just the 'pretty' values, but there may be times the data may need
480
-     * a  little massaging. Proper subsections will become arrays of inputs
481
-     *
482
-     * @param EE_Billing_Info_Form|null $billing_form
483
-     * @return array
484
-     * @throws EE_Error
485
-     */
486
-    protected function _get_billing_values_from_form($billing_form)
487
-    {
488
-        return $billing_form instanceof EE_Form_Section_Proper
489
-            ? $billing_form->input_pretty_values(true)
490
-            : [];
491
-    }
492
-
493
-
494
-    /**
495
-     * Handles an instant payment notification when the transaction is known (by default).
496
-     *
497
-     * @param array          $req_data
498
-     * @param EE_Transaction $transaction
499
-     * @return EE_Payment
500
-     * @throws EE_Error
501
-     * @throws ReflectionException
502
-     */
503
-    public function handle_ipn(array $req_data, EE_Transaction $transaction): EE_Payment
504
-    {
505
-        $transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
506
-        if (! $this->_gateway instanceof EE_Offsite_Gateway) {
507
-            throw new EE_Error(
508
-                sprintf(
509
-                    esc_html__("Could not handle IPN because '%s' is not an offsite gateway", "event_espresso"),
510
-                    print_r($this->_gateway, true)
511
-                )
512
-            );
513
-        }
514
-        return $this->_gateway->handle_payment_update($req_data, $transaction);
515
-    }
516
-
517
-
518
-    /**
519
-     * Saves the billing info onto the attendee of the primary registrant on this transaction, and
520
-     * cleans it first.
521
-     *
522
-     * @param EE_Billing_Attendee_Info_Form $billing_form
523
-     * @param EE_Transaction|null           $transaction
524
-     * @return boolean success
525
-     * @throws EE_Error
526
-     * @throws ReflectionException
527
-     */
528
-    protected function _save_billing_info_to_attendee(
529
-        EE_Billing_Attendee_Info_Form $billing_form,
530
-        ?EE_Transaction $transaction
531
-    ): bool {
532
-        if (! $transaction instanceof EE_Transaction) {
533
-            EE_Error::add_error(
534
-                esc_html__("Cannot save billing info because no transaction was specified", "event_espresso"),
535
-                __FILE__,
536
-                __FUNCTION__,
537
-                __LINE__
538
-            );
539
-            return false;
540
-        }
541
-        $primary_reg = $transaction->primary_registration();
542
-        if (! $primary_reg) {
543
-            EE_Error::add_error(
544
-                esc_html__(
545
-                    "Cannot save billing info because the transaction has no primary registration",
546
-                    "event_espresso"
547
-                ),
548
-                __FILE__,
549
-                __FUNCTION__,
550
-                __LINE__
551
-            );
552
-            return false;
553
-        }
554
-        $attendee = $primary_reg->attendee();
555
-        if (! $attendee) {
556
-            EE_Error::add_error(
557
-                esc_html__(
558
-                    "Cannot save billing info because the transaction's primary registration has no attendee!",
559
-                    "event_espresso"
560
-                ),
561
-                __FILE__,
562
-                __FUNCTION__,
563
-                __LINE__
564
-            );
565
-            return false;
566
-        }
567
-        return $attendee->save_and_clean_billing_info_for_payment_method($billing_form, $transaction->payment_method());
568
-    }
569
-
570
-
571
-    /**
572
-     * Gets the payment this IPN is for. Children may often want to
573
-     * override this to inspect the request
574
-     *
575
-     * @param EE_Transaction $transaction
576
-     * @param array          $req_data
577
-     * @return EE_Payment
578
-     * @throws EE_Error
579
-     * @throws ReflectionException
580
-     */
581
-    protected function find_payment_for_ipn(EE_Transaction $transaction, array $req_data = []): EE_Payment
582
-    {
583
-        return $transaction->last_payment();
584
-    }
585
-
586
-
587
-    /**
588
-     * In case generic code cannot provide the payment processor with a specific payment method
589
-     * and transaction, it will try calling this method on each activate payment method.
590
-     * If the payment method is able to identify the request as being for it, it should fetch
591
-     * the payment it's for and return it. If not, it should throw an EE_Error to indicate it cannot
592
-     * handle the IPN
593
-     *
594
-     * @param array $req_data
595
-     * @return EE_Payment only if this payment method can find the info its needs from $req_data
596
-     * and identifies the IPN as being for this payment method (not just fo ra payment method of this type)
597
-     * @throws EE_Error
598
-     */
599
-    public function handle_unclaimed_ipn(array $req_data = []): EE_Payment
600
-    {
601
-        throw new EE_Error(
602
-            sprintf(
603
-                esc_html__("Payment Method '%s' cannot handle unclaimed IPNs", "event_espresso"),
604
-                get_class($this)
605
-            )
606
-        );
607
-    }
608
-
609
-
610
-    /**
611
-     * Logic to be accomplished when the payment attempt is complete.
612
-     * Most payment methods don't need to do anything at this point; but some, like Mijireh, do.
613
-     * (Mijireh is an offsite gateway which doesn't send an IPN. So when the user returns to EE from
614
-     * mijireh, this method needs to be called so the Mijireh PM can ping Mijireh to know the status
615
-     * of the payment). Fed a transaction because it's always assumed to be the last payment that
616
-     * we're dealing with. Returns that last payment (if there is one)
617
-     *
618
-     * @param EE_Transaction $transaction
619
-     * @return EE_Payment|null
620
-     * @throws EE_Error
621
-     * @throws ReflectionException
622
-     */
623
-    public function finalize_payment_for(EE_Transaction $transaction): ?EE_Payment
624
-    {
625
-        return $transaction->last_payment();
626
-    }
627
-
628
-
629
-    /**
630
-     * Whether this payment method's gateway supports sending refund requests
631
-     *
632
-     * @return boolean
633
-     */
634
-    public function supports_sending_refunds(): bool
635
-    {
636
-        return $this->_gateway instanceof EE_Gateway && $this->_gateway->supports_sending_refunds();
637
-    }
638
-
639
-
640
-    /**
641
-     * @param EE_Payment $payment
642
-     * @param array      $refund_info
643
-     * @return EE_Payment
644
-     * @throws EE_Error
645
-     */
646
-    public function process_refund(EE_Payment $payment, array $refund_info = []): EE_Payment
647
-    {
648
-        if ($this->_gateway instanceof EE_Gateway) {
649
-            return $this->_gateway->do_direct_refund($payment, $refund_info);
650
-        } else {
651
-            throw new EE_Error(
652
-                sprintf(
653
-                    esc_html__('Payment Method Type "%s" does not support sending refund requests', 'event_espresso'),
654
-                    get_class($this)
655
-                )
656
-            );
657
-        }
658
-    }
659
-
660
-
661
-    /**
662
-     * Returns one the class's constants onsite,offsite, or offline, depending on this
663
-     * payment method's gateway.
664
-     *
665
-     * @return string
666
-     * @throws EE_Error
667
-     */
668
-    public function payment_occurs(): string
669
-    {
670
-        if (! $this->_gateway) {
671
-            return EE_PMT_Base::offline;
672
-        }
673
-        if ($this->_gateway instanceof EE_Onsite_Gateway) {
674
-            return EE_PMT_Base::onsite;
675
-        }
676
-        if ($this->_gateway instanceof EE_Offsite_Gateway) {
677
-            return EE_PMT_Base::offsite;
678
-        }
679
-        throw new EE_Error(
680
-            sprintf(
681
-                esc_html__(
682
-                    "Payment method type '%s's gateway isn't an instance of EE_Onsite_Gateway, EE_Offsite_Gateway, or null. It must be one of those",
683
-                    "event_espresso"
684
-                ),
685
-                get_class($this)
686
-            )
687
-        );
688
-    }
689
-
690
-
691
-    /**
692
-     * For adding any html output ab ove the payment overview.
693
-     * Many gateways won't want ot display anything, so this function just returns an empty string.
694
-     * Other gateways may want to override this, such as offline gateways.
695
-     *
696
-     * @param EE_Payment $payment
697
-     * @return string
698
-     */
699
-    public function payment_overview_content(EE_Payment $payment)
700
-    {
701
-        return EEH_Template::display_template(
702
-            EE_LIBRARIES . 'payment_methods/templates/payment_details_content.template.php',
703
-            ['payment_method' => $this->_pm_instance, 'payment' => $payment],
704
-            true
705
-        );
706
-    }
707
-
708
-
709
-    /**
710
-     * @return array where keys are the help tab name,
711
-     * values are: array {
712
-     * @type string $title         i18n name for the help tab
713
-     * @type string $filename      name of the file located in ./help_tabs/ (ie, in a folder next to this file)
714
-     * @type array  $template_args any arguments you want passed to the template file while rendering.
715
-     *                             Keys will be variable names and values with be their values.
716
-     */
717
-    public function help_tabs_config()
718
-    {
719
-        return [];
720
-    }
721
-
722
-
723
-    /**
724
-     * The system name for this PMT (eg AIM, Paypal_Pro, Invoice... what gets put into
725
-     * the payment method's table's PMT_type column)
726
-     *
727
-     * @return string
728
-     */
729
-    public function system_name()
730
-    {
731
-        $classname = get_class($this);
732
-        return str_replace("EE_PMT_", '', $classname);
733
-    }
734
-
735
-
736
-    /**
737
-     * A pretty i18n version of the PMT name. Often the same as the "pretty_name", but you can change it by overriding
738
-     * this method.
739
-     *
740
-     * @return string|null
741
-     */
742
-    public function defaultFrontendName()
743
-    {
744
-        return $this->pretty_name();
745
-    }
746
-
747
-
748
-    /**
749
-     * A pretty i18n version of the PMT name
750
-     *
751
-     * @return string|null
752
-     */
753
-    public function pretty_name(): ?string
754
-    {
755
-        return $this->_pretty_name;
756
-    }
757
-
758
-
759
-    /**
760
-     * Gets the default absolute URL to the payment method type's button
761
-     *
762
-     * @return string|null
763
-     */
764
-    public function default_button_url(): ?string
765
-    {
766
-        return $this->_default_button_url;
767
-    }
768
-
769
-
770
-    /**
771
-     * Gets the gateway used by this payment method (if any)
772
-     *
773
-     * @return EE_Gateway
774
-     */
775
-    public function get_gateway(): ?EE_Gateway
776
-    {
777
-        return $this->_gateway;
778
-    }
779
-
780
-
781
-    /**
782
-     * @return string html for the link to a help tab
783
-     */
784
-    public function get_help_tab_link(): string
785
-    {
786
-        return EEH_Template::get_help_tab_link(
787
-            $this->get_help_tab_name(),
788
-            'espresso_payment_settings',
789
-            'default'
790
-        );
791
-    }
792
-
793
-
794
-    /**
795
-     * Returns the name of the help tab for this PMT
796
-     *
797
-     * @return string
798
-     */
799
-    public function get_help_tab_name(): string
800
-    {
801
-        return 'ee_' . strtolower($this->system_name()) . '_help_tab';
802
-    }
803
-
804
-
805
-    /**
806
-     * The name of the wp capability that should be associated with the usage of
807
-     * this PMT by an admin
808
-     *
809
-     * @return string
810
-     */
811
-    public function cap_name(): string
812
-    {
813
-        return 'ee_payment_method_' . strtolower($this->system_name());
814
-    }
815
-
816
-
817
-    /**
818
-     * Called by client code to tell the gateway that if it wants to change
819
-     * the transaction or line items or registrations related to teh payment it already
820
-     * processed (we think, but possibly not) that now's the time to do it.
821
-     * It is expected that gateways will store any info they need for this on the PAY_details,
822
-     * or maybe an extra meta value
823
-     *
824
-     * @param EE_Payment $payment
825
-     * @return void
826
-     */
827
-    public function update_txn_based_on_payment($payment)
828
-    {
829
-        if ($this->_gateway instanceof EE_Gateway) {
830
-            $this->_gateway->update_txn_based_on_payment($payment);
831
-        }
832
-    }
833
-
834
-
835
-    /**
836
-     * Returns a string of HTML describing this payment method type for an admin,
837
-     * primarily intended for them to read before activating it.
838
-     * The easiest way to set this is to create a folder 'templates' alongside
839
-     * your EE_PMT_{System_Name} file, and in it create a file named "{system_name}_intro.template.php".
840
-     * Eg, if your payment method file is named "EE_PMT_Foo_Bar.pm.php",
841
-     * then you'd create a file named "templates" in the same folder as it, and name the file
842
-     * "foo_bar_intro.template.php", and its content will be returned by this method
843
-     *
844
-     * @return string
845
-     */
846
-    public function introductory_html(): string
847
-    {
848
-        return EEH_Template::locate_template(
849
-            $this->file_folder() . 'templates/' . strtolower($this->system_name()) . '_intro.template.php',
850
-            ['pmt_obj' => $this, 'pm_instance' => $this->_pm_instance]
851
-        );
852
-    }
19
+	const onsite  = 'on-site';
20
+
21
+	const offsite = 'off-site';
22
+
23
+	const offline = 'off-line';
24
+
25
+	/**
26
+	 * @var EE_Payment_Method
27
+	 */
28
+	protected $_pm_instance = null;
29
+
30
+	/**
31
+	 * @var boolean
32
+	 */
33
+	protected $_requires_https = false;
34
+
35
+	/**
36
+	 * @var boolean
37
+	 */
38
+	protected $_has_billing_form;
39
+
40
+	/**
41
+	 * @var EE_Gateway
42
+	 */
43
+	protected $_gateway = null;
44
+
45
+	/**
46
+	 * @var EE_Payment_Method_Form
47
+	 */
48
+	protected $_settings_form = null;
49
+
50
+	/**
51
+	 * @var EE_Form_Section_Proper
52
+	 */
53
+	protected $_billing_form = null;
54
+
55
+	/**
56
+	 * @var boolean
57
+	 */
58
+	protected $_cache_billing_form = true;
59
+
60
+	/**
61
+	 * String of the absolute path to the folder containing this file, with a trailing slash.
62
+	 * eg '/public_html/wp-site/wp-content/plugins/event-espresso/payment_methods/Invoice/'
63
+	 *
64
+	 * @var string|null
65
+	 */
66
+	protected $_file_folder = null;
67
+
68
+	/**
69
+	 * String to the absolute URL to this file (useful for getting its web-accessible resources
70
+	 * like images, js, or css)
71
+	 *
72
+	 * @var string|null
73
+	 */
74
+	protected $_file_url = null;
75
+
76
+	/**
77
+	 * Pretty name for the payment method
78
+	 *
79
+	 * @var string|null
80
+	 */
81
+	protected $_pretty_name = null;
82
+
83
+	/**
84
+	 * @var string|null
85
+	 */
86
+	protected $_default_button_url = null;
87
+
88
+	/**
89
+	 * @var string|null
90
+	 */
91
+	protected $_default_description = null;
92
+
93
+	/**
94
+	 *  @var string|null
95
+	 */
96
+	protected $_template_path = null;
97
+
98
+	/**
99
+	 * @param EE_Payment_Method|null $pm_instance
100
+	 * @throws ReflectionException
101
+	 * @throws EE_Error
102
+	 */
103
+	public function __construct($pm_instance = null)
104
+	{
105
+		if ($pm_instance instanceof EE_Payment_Method) {
106
+			$this->set_instance($pm_instance);
107
+		}
108
+		if ($this->_gateway) {
109
+			$this->_gateway->set_payment_model(EEM_Payment::instance());
110
+			$this->_gateway->set_payment_log(EEM_Change_Log::instance());
111
+			$this->_gateway->set_template_helper(new EEH_Template());
112
+			$this->_gateway->set_line_item_helper(new EEH_Line_Item());
113
+			$this->_gateway->set_money_helper(new EEH_Money());
114
+			$this->_gateway->set_gateway_data_formatter(new GatewayDataFormatter());
115
+			$this->_gateway->set_unsupported_character_remover(new AsciiOnly());
116
+			do_action('AHEE__EE_PMT_Base___construct__done_initializing_gateway_class', $this, $this->_gateway);
117
+		}
118
+		if (! isset($this->_has_billing_form)) {
119
+			// by default, On Site gateways have a billing form
120
+			if ($this->payment_occurs() == EE_PMT_Base::onsite) {
121
+				$this->set_has_billing_form(true);
122
+			} else {
123
+				$this->set_has_billing_form(false);
124
+			}
125
+		}
126
+
127
+		if (! $this->_pretty_name) {
128
+			throw new EE_Error(
129
+				esc_html__(
130
+					'You must set the pretty name for the Payment Method Type in the constructor (_pretty_name), and please make it internationalized',
131
+					'event_espresso'
132
+				)
133
+			);
134
+		}
135
+		// if the child didn't specify a default button, use the credit card one
136
+		if ($this->_default_button_url === null) {
137
+			$this->_default_button_url = EE_PLUGIN_DIR_URL . 'payment_methods/pay-by-credit-card.png';
138
+		}
139
+	}
140
+
141
+
142
+	/**
143
+	 * @param boolean $has_billing_form
144
+	 */
145
+	public function set_has_billing_form(bool $has_billing_form)
146
+	{
147
+		$this->_has_billing_form = filter_var($has_billing_form, FILTER_VALIDATE_BOOLEAN);
148
+	}
149
+
150
+
151
+	/**
152
+	 * sets the file_folder property
153
+	 */
154
+	protected function _set_file_folder()
155
+	{
156
+		$reflector          = new ReflectionClass(get_class($this));
157
+		$fn                 = $reflector->getFileName();
158
+		$this->_file_folder = dirname($fn) . '/';
159
+	}
160
+
161
+
162
+	/**
163
+	 * sets the file URL with a trailing slash for this PMT
164
+	 */
165
+	protected function _set_file_url()
166
+	{
167
+		$plugins_dir_fixed = str_replace('\\', '/', WP_PLUGIN_DIR);
168
+		$file_folder_fixed = str_replace('\\', '/', $this->file_folder());
169
+		$file_path         = str_replace($plugins_dir_fixed, WP_PLUGIN_URL, $file_folder_fixed);
170
+		$this->_file_url   = set_url_scheme($file_path);
171
+	}
172
+
173
+
174
+	/**
175
+	 * Gets the default description on all payment methods of this type
176
+	 *
177
+	 * @return string
178
+	 */
179
+	public function default_description(): ?string
180
+	{
181
+		return $this->_default_description;
182
+	}
183
+
184
+
185
+	/**
186
+	 * Returns the folder containing the PMT child class, with a trailing slash
187
+	 *
188
+	 * @return string
189
+	 */
190
+	public function file_folder(): ?string
191
+	{
192
+		if (! $this->_file_folder) {
193
+			$this->_set_file_folder();
194
+		}
195
+		return $this->_file_folder;
196
+	}
197
+
198
+
199
+	/**
200
+	 * @return string
201
+	 */
202
+	public function file_url(): ?string
203
+	{
204
+		if (! $this->_file_url) {
205
+			$this->_set_file_url();
206
+		}
207
+		return $this->_file_url;
208
+	}
209
+
210
+
211
+	/**
212
+	 * Sets the payment method instance this payment method type is for.
213
+	 * Its important teh payment method instance is set before
214
+	 *
215
+	 * @param EE_Payment_Method $payment_method_instance
216
+	 * @throws EE_Error
217
+	 * @throws ReflectionException
218
+	 */
219
+	public function set_instance(EE_Payment_Method $payment_method_instance)
220
+	{
221
+		$this->_pm_instance = $payment_method_instance;
222
+		// if they have already requested the settings form, make sure its
223
+		// data matches this model object
224
+		if ($this->_settings_form) {
225
+			$this->settings_form()->populate_model_obj($payment_method_instance);
226
+		}
227
+		if ($this->_gateway instanceof EE_Gateway) {
228
+			$this->_gateway->set_settings($payment_method_instance->settings_array());
229
+		}
230
+	}
231
+
232
+
233
+	/**
234
+	 * Gets teh form for displaying to admins where they set up the payment method
235
+	 *
236
+	 * @return EE_Payment_Method_Form
237
+	 * @throws EE_Error
238
+	 * @throws ReflectionException
239
+	 */
240
+	public function settings_form(): EE_Payment_Method_Form
241
+	{
242
+		if (! $this->_settings_form) {
243
+			$this->_settings_form = $this->generate_new_settings_form();
244
+			$this->_settings_form->set_payment_method_type($this);
245
+			// if we have already assigned a model object to this pmt, make
246
+			// sure it's reflected in the form we just generated
247
+			if ($this->_pm_instance) {
248
+				$this->_settings_form->populate_model_obj($this->_pm_instance);
249
+			}
250
+		}
251
+		return $this->_settings_form;
252
+	}
253
+
254
+
255
+	/**
256
+	 * Gets the form for all the settings related to this payment method type
257
+	 *
258
+	 * @return EE_Payment_Method_Form
259
+	 */
260
+	abstract public function generate_new_settings_form();
261
+
262
+
263
+	/**
264
+	 * Sets the form for settings. This may be useful if we have already received
265
+	 * a form submission and have form data it in, and want to use it anytime we're showing
266
+	 * this payment method type's settings form later in the request
267
+	 *
268
+	 * @param EE_Payment_Method_Form $form
269
+	 */
270
+	public function set_settings_form(EE_Payment_Method_Form $form)
271
+	{
272
+		$this->_settings_form = $form;
273
+	}
274
+
275
+
276
+	/**
277
+	 * @return boolean
278
+	 */
279
+	public function has_billing_form(): bool
280
+	{
281
+		return $this->_has_billing_form;
282
+	}
283
+
284
+
285
+	/**
286
+	 * Gets the form for displaying to attendees where they can enter their billing info
287
+	 * which will be sent to teh gateway (can be null)
288
+	 *
289
+	 * @param EE_Transaction|null $transaction
290
+	 * @param array               $extra_args
291
+	 * @return EE_Billing_Attendee_Info_Form|EE_Billing_Info_Form|null
292
+	 * @throws EE_Error
293
+	 * @throws ReflectionException
294
+	 */
295
+	public function billing_form(EE_Transaction $transaction = null, array $extra_args = [])
296
+	{
297
+		// has billing form already been regenerated ? or overwrite cache?
298
+		if (! $this->_billing_form instanceof EE_Billing_Info_Form || ! $this->_cache_billing_form) {
299
+			$this->_billing_form = $this->generate_new_billing_form($transaction, $extra_args);
300
+		}
301
+		// if we know who the attendee is, and this is a billing form
302
+		// that uses attendee info, populate it
303
+		if (
304
+			apply_filters(
305
+				'FHEE__populate_billing_form_fields_from_attendee',
306
+				(
307
+					$this->_billing_form instanceof EE_Billing_Attendee_Info_Form
308
+					&& $transaction instanceof EE_Transaction
309
+					&& $transaction->primary_registration() instanceof EE_Registration
310
+					&& $transaction->primary_registration()->attendee() instanceof EE_Attendee
311
+				),
312
+				$this->_billing_form,
313
+				$transaction
314
+			)
315
+		) {
316
+			$this->_billing_form->populate_from_attendee($transaction->primary_registration()->attendee());
317
+		}
318
+		return $this->_billing_form;
319
+	}
320
+
321
+
322
+	/**
323
+	 * Creates the billing form for this payment method type
324
+	 *
325
+	 * @param EE_Transaction|null $transaction
326
+	 * @return EE_Billing_Info_Form|null
327
+	 */
328
+	abstract public function generate_new_billing_form(EE_Transaction $transaction = null);
329
+
330
+
331
+	/**
332
+	 * applies debug data to the form
333
+	 *
334
+	 * @param EE_Billing_Info_Form $billing_form
335
+	 * @return EE_Billing_Info_Form|null
336
+	 */
337
+	public function apply_billing_form_debug_settings(EE_Billing_Info_Form $billing_form)
338
+	{
339
+		return $billing_form;
340
+	}
341
+
342
+
343
+	/**
344
+	 * Sets the billing form for this payment method type. You may want to use this
345
+	 * if you have form
346
+	 *
347
+	 * @param EE_Payment_Method $form
348
+	 */
349
+	public function set_billing_form(EE_Payment_Method $form)
350
+	{
351
+		$this->_billing_form = $form;
352
+	}
353
+
354
+
355
+	/**
356
+	 * Returns whether this payment method requires HTTPS to be used
357
+	 *
358
+	 * @return boolean
359
+	 */
360
+	public function requires_https(): bool
361
+	{
362
+		return $this->_requires_https;
363
+	}
364
+
365
+
366
+	/**
367
+	 * @param EE_Transaction            $transaction
368
+	 * @param float|null                $amount
369
+	 * @param EE_Billing_Info_Form|null $billing_info
370
+	 * @param string|null               $return_url
371
+	 * @param string                    $fail_url
372
+	 * @param string                    $method
373
+	 * @param bool                      $by_admin
374
+	 * @return EE_Payment
375
+	 * @throws EE_Error
376
+	 * @throws ReflectionException
377
+	 */
378
+	public function process_payment(
379
+		EE_Transaction $transaction,
380
+		$amount = null,
381
+		$billing_info = null,
382
+		$return_url = null,
383
+		$fail_url = '',
384
+		$method = 'CART',
385
+		$by_admin = false
386
+	) {
387
+		// @todo: add surcharge for the payment method, if any
388
+		if ($this->_gateway) {
389
+			// there is a gateway, so we're going to make a payment object
390
+			// but wait! do they already have a payment in progress that we thought was failed?
391
+			$duplicate_properties = [
392
+				'STS_ID'               => EEM_Payment::status_id_failed,
393
+				'TXN_ID'               => $transaction->ID(),
394
+				'PMD_ID'               => $this->_pm_instance->ID(),
395
+				'PAY_source'           => $method,
396
+				'PAY_amount'           => $amount !== null
397
+					? $amount
398
+					: $transaction->remaining(),
399
+				'PAY_gateway_response' => null,
400
+			];
401
+			$payment              = EEM_Payment::instance()->get_one([$duplicate_properties]);
402
+			// if we didn't already have a payment in progress for the same thing,
403
+			// then we actually want to make a new payment
404
+			if (! $payment instanceof EE_Payment) {
405
+				$payment = EE_Payment::new_instance(
406
+					array_merge(
407
+						$duplicate_properties,
408
+						[
409
+							'PAY_timestamp'       => time(),
410
+							'PAY_txn_id_chq_nmbr' => null,
411
+							'PAY_po_number'       => null,
412
+							'PAY_extra_accntng'   => null,
413
+							'PAY_details'         => null,
414
+						]
415
+					)
416
+				);
417
+			}
418
+			// make sure the payment has been saved to show we started it, and so it has an ID should the gateway try to log it
419
+			$payment->save();
420
+			$billing_values = $this->_get_billing_values_from_form($billing_info);
421
+
422
+			//  Offsite Gateway
423
+			if ($this->_gateway instanceof EE_Offsite_Gateway) {
424
+				$payment = $this->_gateway->set_redirection_info(
425
+					$payment,
426
+					$billing_values,
427
+					$return_url,
428
+					EE_Config::instance()->core->txn_page_url(
429
+						[
430
+							'e_reg_url_link'    => $transaction->primary_registration()->reg_url_link(),
431
+							'ee_payment_method' => $this->_pm_instance->slug(),
432
+						]
433
+					),
434
+					$fail_url
435
+				);
436
+				$payment->save();
437
+				//  Onsite Gateway
438
+			} elseif ($this->_gateway instanceof EE_Onsite_Gateway) {
439
+				$payment = $this->_gateway->do_direct_payment($payment, $billing_values);
440
+				$payment->save();
441
+			} else {
442
+				throw new EE_Error(
443
+					sprintf(
444
+						esc_html__(
445
+							'Gateway for payment method type "%s" is "%s", not a subclass of either EE_Offsite_Gateway or EE_Onsite_Gateway, or null (to indicate NO gateway)',
446
+							'event_espresso'
447
+						),
448
+						get_class($this),
449
+						gettype($this->_gateway)
450
+					)
451
+				);
452
+			}
453
+		} else {
454
+			// no gateway provided
455
+			// there is no payment. Must be an offline gateway
456
+			// create a payment object anyways, but dont save it
457
+			$payment = EE_Payment::new_instance(
458
+				[
459
+					'STS_ID'        => EEM_Payment::status_id_pending,
460
+					'TXN_ID'        => $transaction->ID(),
461
+					'PMD_ID'        => $transaction->payment_method_ID(),
462
+					'PAY_amount'    => 0.00,
463
+					'PAY_timestamp' => time(),
464
+				]
465
+			);
466
+		}
467
+
468
+		// if there is billing info, clean it and save it now
469
+		if ($billing_info instanceof EE_Billing_Attendee_Info_Form) {
470
+			$this->_save_billing_info_to_attendee($billing_info, $transaction);
471
+		}
472
+
473
+		return $payment;
474
+	}
475
+
476
+
477
+	/**
478
+	 * Gets the values we want to pass onto the gateway. Normally these
479
+	 * are just the 'pretty' values, but there may be times the data may need
480
+	 * a  little massaging. Proper subsections will become arrays of inputs
481
+	 *
482
+	 * @param EE_Billing_Info_Form|null $billing_form
483
+	 * @return array
484
+	 * @throws EE_Error
485
+	 */
486
+	protected function _get_billing_values_from_form($billing_form)
487
+	{
488
+		return $billing_form instanceof EE_Form_Section_Proper
489
+			? $billing_form->input_pretty_values(true)
490
+			: [];
491
+	}
492
+
493
+
494
+	/**
495
+	 * Handles an instant payment notification when the transaction is known (by default).
496
+	 *
497
+	 * @param array          $req_data
498
+	 * @param EE_Transaction $transaction
499
+	 * @return EE_Payment
500
+	 * @throws EE_Error
501
+	 * @throws ReflectionException
502
+	 */
503
+	public function handle_ipn(array $req_data, EE_Transaction $transaction): EE_Payment
504
+	{
505
+		$transaction = EEM_Transaction::instance()->ensure_is_obj($transaction);
506
+		if (! $this->_gateway instanceof EE_Offsite_Gateway) {
507
+			throw new EE_Error(
508
+				sprintf(
509
+					esc_html__("Could not handle IPN because '%s' is not an offsite gateway", "event_espresso"),
510
+					print_r($this->_gateway, true)
511
+				)
512
+			);
513
+		}
514
+		return $this->_gateway->handle_payment_update($req_data, $transaction);
515
+	}
516
+
517
+
518
+	/**
519
+	 * Saves the billing info onto the attendee of the primary registrant on this transaction, and
520
+	 * cleans it first.
521
+	 *
522
+	 * @param EE_Billing_Attendee_Info_Form $billing_form
523
+	 * @param EE_Transaction|null           $transaction
524
+	 * @return boolean success
525
+	 * @throws EE_Error
526
+	 * @throws ReflectionException
527
+	 */
528
+	protected function _save_billing_info_to_attendee(
529
+		EE_Billing_Attendee_Info_Form $billing_form,
530
+		?EE_Transaction $transaction
531
+	): bool {
532
+		if (! $transaction instanceof EE_Transaction) {
533
+			EE_Error::add_error(
534
+				esc_html__("Cannot save billing info because no transaction was specified", "event_espresso"),
535
+				__FILE__,
536
+				__FUNCTION__,
537
+				__LINE__
538
+			);
539
+			return false;
540
+		}
541
+		$primary_reg = $transaction->primary_registration();
542
+		if (! $primary_reg) {
543
+			EE_Error::add_error(
544
+				esc_html__(
545
+					"Cannot save billing info because the transaction has no primary registration",
546
+					"event_espresso"
547
+				),
548
+				__FILE__,
549
+				__FUNCTION__,
550
+				__LINE__
551
+			);
552
+			return false;
553
+		}
554
+		$attendee = $primary_reg->attendee();
555
+		if (! $attendee) {
556
+			EE_Error::add_error(
557
+				esc_html__(
558
+					"Cannot save billing info because the transaction's primary registration has no attendee!",
559
+					"event_espresso"
560
+				),
561
+				__FILE__,
562
+				__FUNCTION__,
563
+				__LINE__
564
+			);
565
+			return false;
566
+		}
567
+		return $attendee->save_and_clean_billing_info_for_payment_method($billing_form, $transaction->payment_method());
568
+	}
569
+
570
+
571
+	/**
572
+	 * Gets the payment this IPN is for. Children may often want to
573
+	 * override this to inspect the request
574
+	 *
575
+	 * @param EE_Transaction $transaction
576
+	 * @param array          $req_data
577
+	 * @return EE_Payment
578
+	 * @throws EE_Error
579
+	 * @throws ReflectionException
580
+	 */
581
+	protected function find_payment_for_ipn(EE_Transaction $transaction, array $req_data = []): EE_Payment
582
+	{
583
+		return $transaction->last_payment();
584
+	}
585
+
586
+
587
+	/**
588
+	 * In case generic code cannot provide the payment processor with a specific payment method
589
+	 * and transaction, it will try calling this method on each activate payment method.
590
+	 * If the payment method is able to identify the request as being for it, it should fetch
591
+	 * the payment it's for and return it. If not, it should throw an EE_Error to indicate it cannot
592
+	 * handle the IPN
593
+	 *
594
+	 * @param array $req_data
595
+	 * @return EE_Payment only if this payment method can find the info its needs from $req_data
596
+	 * and identifies the IPN as being for this payment method (not just fo ra payment method of this type)
597
+	 * @throws EE_Error
598
+	 */
599
+	public function handle_unclaimed_ipn(array $req_data = []): EE_Payment
600
+	{
601
+		throw new EE_Error(
602
+			sprintf(
603
+				esc_html__("Payment Method '%s' cannot handle unclaimed IPNs", "event_espresso"),
604
+				get_class($this)
605
+			)
606
+		);
607
+	}
608
+
609
+
610
+	/**
611
+	 * Logic to be accomplished when the payment attempt is complete.
612
+	 * Most payment methods don't need to do anything at this point; but some, like Mijireh, do.
613
+	 * (Mijireh is an offsite gateway which doesn't send an IPN. So when the user returns to EE from
614
+	 * mijireh, this method needs to be called so the Mijireh PM can ping Mijireh to know the status
615
+	 * of the payment). Fed a transaction because it's always assumed to be the last payment that
616
+	 * we're dealing with. Returns that last payment (if there is one)
617
+	 *
618
+	 * @param EE_Transaction $transaction
619
+	 * @return EE_Payment|null
620
+	 * @throws EE_Error
621
+	 * @throws ReflectionException
622
+	 */
623
+	public function finalize_payment_for(EE_Transaction $transaction): ?EE_Payment
624
+	{
625
+		return $transaction->last_payment();
626
+	}
627
+
628
+
629
+	/**
630
+	 * Whether this payment method's gateway supports sending refund requests
631
+	 *
632
+	 * @return boolean
633
+	 */
634
+	public function supports_sending_refunds(): bool
635
+	{
636
+		return $this->_gateway instanceof EE_Gateway && $this->_gateway->supports_sending_refunds();
637
+	}
638
+
639
+
640
+	/**
641
+	 * @param EE_Payment $payment
642
+	 * @param array      $refund_info
643
+	 * @return EE_Payment
644
+	 * @throws EE_Error
645
+	 */
646
+	public function process_refund(EE_Payment $payment, array $refund_info = []): EE_Payment
647
+	{
648
+		if ($this->_gateway instanceof EE_Gateway) {
649
+			return $this->_gateway->do_direct_refund($payment, $refund_info);
650
+		} else {
651
+			throw new EE_Error(
652
+				sprintf(
653
+					esc_html__('Payment Method Type "%s" does not support sending refund requests', 'event_espresso'),
654
+					get_class($this)
655
+				)
656
+			);
657
+		}
658
+	}
659
+
660
+
661
+	/**
662
+	 * Returns one the class's constants onsite,offsite, or offline, depending on this
663
+	 * payment method's gateway.
664
+	 *
665
+	 * @return string
666
+	 * @throws EE_Error
667
+	 */
668
+	public function payment_occurs(): string
669
+	{
670
+		if (! $this->_gateway) {
671
+			return EE_PMT_Base::offline;
672
+		}
673
+		if ($this->_gateway instanceof EE_Onsite_Gateway) {
674
+			return EE_PMT_Base::onsite;
675
+		}
676
+		if ($this->_gateway instanceof EE_Offsite_Gateway) {
677
+			return EE_PMT_Base::offsite;
678
+		}
679
+		throw new EE_Error(
680
+			sprintf(
681
+				esc_html__(
682
+					"Payment method type '%s's gateway isn't an instance of EE_Onsite_Gateway, EE_Offsite_Gateway, or null. It must be one of those",
683
+					"event_espresso"
684
+				),
685
+				get_class($this)
686
+			)
687
+		);
688
+	}
689
+
690
+
691
+	/**
692
+	 * For adding any html output ab ove the payment overview.
693
+	 * Many gateways won't want ot display anything, so this function just returns an empty string.
694
+	 * Other gateways may want to override this, such as offline gateways.
695
+	 *
696
+	 * @param EE_Payment $payment
697
+	 * @return string
698
+	 */
699
+	public function payment_overview_content(EE_Payment $payment)
700
+	{
701
+		return EEH_Template::display_template(
702
+			EE_LIBRARIES . 'payment_methods/templates/payment_details_content.template.php',
703
+			['payment_method' => $this->_pm_instance, 'payment' => $payment],
704
+			true
705
+		);
706
+	}
707
+
708
+
709
+	/**
710
+	 * @return array where keys are the help tab name,
711
+	 * values are: array {
712
+	 * @type string $title         i18n name for the help tab
713
+	 * @type string $filename      name of the file located in ./help_tabs/ (ie, in a folder next to this file)
714
+	 * @type array  $template_args any arguments you want passed to the template file while rendering.
715
+	 *                             Keys will be variable names and values with be their values.
716
+	 */
717
+	public function help_tabs_config()
718
+	{
719
+		return [];
720
+	}
721
+
722
+
723
+	/**
724
+	 * The system name for this PMT (eg AIM, Paypal_Pro, Invoice... what gets put into
725
+	 * the payment method's table's PMT_type column)
726
+	 *
727
+	 * @return string
728
+	 */
729
+	public function system_name()
730
+	{
731
+		$classname = get_class($this);
732
+		return str_replace("EE_PMT_", '', $classname);
733
+	}
734
+
735
+
736
+	/**
737
+	 * A pretty i18n version of the PMT name. Often the same as the "pretty_name", but you can change it by overriding
738
+	 * this method.
739
+	 *
740
+	 * @return string|null
741
+	 */
742
+	public function defaultFrontendName()
743
+	{
744
+		return $this->pretty_name();
745
+	}
746
+
747
+
748
+	/**
749
+	 * A pretty i18n version of the PMT name
750
+	 *
751
+	 * @return string|null
752
+	 */
753
+	public function pretty_name(): ?string
754
+	{
755
+		return $this->_pretty_name;
756
+	}
757
+
758
+
759
+	/**
760
+	 * Gets the default absolute URL to the payment method type's button
761
+	 *
762
+	 * @return string|null
763
+	 */
764
+	public function default_button_url(): ?string
765
+	{
766
+		return $this->_default_button_url;
767
+	}
768
+
769
+
770
+	/**
771
+	 * Gets the gateway used by this payment method (if any)
772
+	 *
773
+	 * @return EE_Gateway
774
+	 */
775
+	public function get_gateway(): ?EE_Gateway
776
+	{
777
+		return $this->_gateway;
778
+	}
779
+
780
+
781
+	/**
782
+	 * @return string html for the link to a help tab
783
+	 */
784
+	public function get_help_tab_link(): string
785
+	{
786
+		return EEH_Template::get_help_tab_link(
787
+			$this->get_help_tab_name(),
788
+			'espresso_payment_settings',
789
+			'default'
790
+		);
791
+	}
792
+
793
+
794
+	/**
795
+	 * Returns the name of the help tab for this PMT
796
+	 *
797
+	 * @return string
798
+	 */
799
+	public function get_help_tab_name(): string
800
+	{
801
+		return 'ee_' . strtolower($this->system_name()) . '_help_tab';
802
+	}
803
+
804
+
805
+	/**
806
+	 * The name of the wp capability that should be associated with the usage of
807
+	 * this PMT by an admin
808
+	 *
809
+	 * @return string
810
+	 */
811
+	public function cap_name(): string
812
+	{
813
+		return 'ee_payment_method_' . strtolower($this->system_name());
814
+	}
815
+
816
+
817
+	/**
818
+	 * Called by client code to tell the gateway that if it wants to change
819
+	 * the transaction or line items or registrations related to teh payment it already
820
+	 * processed (we think, but possibly not) that now's the time to do it.
821
+	 * It is expected that gateways will store any info they need for this on the PAY_details,
822
+	 * or maybe an extra meta value
823
+	 *
824
+	 * @param EE_Payment $payment
825
+	 * @return void
826
+	 */
827
+	public function update_txn_based_on_payment($payment)
828
+	{
829
+		if ($this->_gateway instanceof EE_Gateway) {
830
+			$this->_gateway->update_txn_based_on_payment($payment);
831
+		}
832
+	}
833
+
834
+
835
+	/**
836
+	 * Returns a string of HTML describing this payment method type for an admin,
837
+	 * primarily intended for them to read before activating it.
838
+	 * The easiest way to set this is to create a folder 'templates' alongside
839
+	 * your EE_PMT_{System_Name} file, and in it create a file named "{system_name}_intro.template.php".
840
+	 * Eg, if your payment method file is named "EE_PMT_Foo_Bar.pm.php",
841
+	 * then you'd create a file named "templates" in the same folder as it, and name the file
842
+	 * "foo_bar_intro.template.php", and its content will be returned by this method
843
+	 *
844
+	 * @return string
845
+	 */
846
+	public function introductory_html(): string
847
+	{
848
+		return EEH_Template::locate_template(
849
+			$this->file_folder() . 'templates/' . strtolower($this->system_name()) . '_intro.template.php',
850
+			['pmt_obj' => $this, 'pm_instance' => $this->_pm_instance]
851
+		);
852
+	}
853 853
 }
Please login to merge, or discard this patch.
core/services/routing/Router.php 1 patch
Indentation   +219 added lines, -219 removed lines patch added patch discarded remove patch
@@ -29,243 +29,243 @@
 block discarded – undo
29 29
  */
30 30
 class Router
31 31
 {
32
-    protected EE_Dependency_Map $dependency_map;
32
+	protected EE_Dependency_Map $dependency_map;
33 33
 
34
-    protected LoaderInterface $loader;
34
+	protected LoaderInterface $loader;
35 35
 
36
-    protected RouteHandler $route_handler;
36
+	protected RouteHandler $route_handler;
37 37
 
38
-    protected string $route_request_type;
38
+	protected string $route_request_type;
39 39
 
40
-    protected array $routes_loaded;
40
+	protected array $routes_loaded;
41 41
 
42 42
 
43
-    /**
44
-     * RoutingSwitch constructor.
45
-     *
46
-     * @param EE_Dependency_Map $dependency_map
47
-     * @param LoaderInterface   $loader
48
-     * @param RouteHandler      $router
49
-     */
50
-    public function __construct(EE_Dependency_Map $dependency_map, LoaderInterface $loader, RouteHandler $router)
51
-    {
52
-        $this->dependency_map = $dependency_map;
53
-        $this->loader         = $loader;
54
-        $this->route_handler  = $router;
55
-    }
43
+	/**
44
+	 * RoutingSwitch constructor.
45
+	 *
46
+	 * @param EE_Dependency_Map $dependency_map
47
+	 * @param LoaderInterface   $loader
48
+	 * @param RouteHandler      $router
49
+	 */
50
+	public function __construct(EE_Dependency_Map $dependency_map, LoaderInterface $loader, RouteHandler $router)
51
+	{
52
+		$this->dependency_map = $dependency_map;
53
+		$this->loader         = $loader;
54
+		$this->route_handler  = $router;
55
+	}
56 56
 
57 57
 
58
-    /**
59
-     * @throws Exception
60
-     */
61
-    public function loadPrimaryRoutes()
62
-    {
63
-        if (isset($this->routes_loaded[ __FUNCTION__ ])) {
64
-            return;
65
-        }
66
-        $this->dependency_map->registerDependencies(
67
-            'EventEspresso\core\domain\entities\routing\handlers\admin\ActivationRequests',
68
-            Route::getFullDependencies()
69
-        );
70
-        $this->dependency_map->registerDependencies(
71
-            'EventEspresso\core\domain\entities\routing\handlers\shared\RegularRequests',
72
-            Route::getFullDependencies()
73
-        );
74
-        // now load and prep all primary Routes
75
-        $this->route_handler->addRoute('EventEspresso\core\domain\entities\routing\handlers\admin\ActivationRequests');
76
-        $this->route_handler->addRoute('EventEspresso\core\domain\entities\routing\handlers\shared\RegularRequests');
77
-        $this->route_request_type = $this->route_handler->getRouteRequestType();
78
-        do_action(
79
-            'AHEE__EventEspresso_core_services_routing_Router__loadPrimaryRoutes',
80
-            $this->route_handler,
81
-            $this->route_request_type,
82
-            $this->dependency_map
83
-        );
84
-        $this->routes_loaded[ __FUNCTION__ ] = true;
85
-    }
58
+	/**
59
+	 * @throws Exception
60
+	 */
61
+	public function loadPrimaryRoutes()
62
+	{
63
+		if (isset($this->routes_loaded[ __FUNCTION__ ])) {
64
+			return;
65
+		}
66
+		$this->dependency_map->registerDependencies(
67
+			'EventEspresso\core\domain\entities\routing\handlers\admin\ActivationRequests',
68
+			Route::getFullDependencies()
69
+		);
70
+		$this->dependency_map->registerDependencies(
71
+			'EventEspresso\core\domain\entities\routing\handlers\shared\RegularRequests',
72
+			Route::getFullDependencies()
73
+		);
74
+		// now load and prep all primary Routes
75
+		$this->route_handler->addRoute('EventEspresso\core\domain\entities\routing\handlers\admin\ActivationRequests');
76
+		$this->route_handler->addRoute('EventEspresso\core\domain\entities\routing\handlers\shared\RegularRequests');
77
+		$this->route_request_type = $this->route_handler->getRouteRequestType();
78
+		do_action(
79
+			'AHEE__EventEspresso_core_services_routing_Router__loadPrimaryRoutes',
80
+			$this->route_handler,
81
+			$this->route_request_type,
82
+			$this->dependency_map
83
+		);
84
+		$this->routes_loaded[ __FUNCTION__ ] = true;
85
+	}
86 86
 
87 87
 
88
-    /**
89
-     * @throws Exception
90
-     */
91
-    public function registerShortcodesModulesAndWidgets()
92
-    {
93
-        if (isset($this->routes_loaded[ __FUNCTION__ ])) {
94
-            return;
95
-        }
96
-        do_action(
97
-            'AHEE__EventEspresso_core_services_routing_Router__registerShortcodesModulesAndWidgets',
98
-            $this->route_handler,
99
-            $this->route_request_type,
100
-            $this->dependency_map
101
-        );
88
+	/**
89
+	 * @throws Exception
90
+	 */
91
+	public function registerShortcodesModulesAndWidgets()
92
+	{
93
+		if (isset($this->routes_loaded[ __FUNCTION__ ])) {
94
+			return;
95
+		}
96
+		do_action(
97
+			'AHEE__EventEspresso_core_services_routing_Router__registerShortcodesModulesAndWidgets',
98
+			$this->route_handler,
99
+			$this->route_request_type,
100
+			$this->dependency_map
101
+		);
102 102
 
103
-        /** @var LegacyModulesManager $legacy_modules_manager */
104
-        $legacy_modules_manager = $this->loader->getShared(LegacyModulesManager::class);
105
-        $legacy_modules_manager->setHooks();
103
+		/** @var LegacyModulesManager $legacy_modules_manager */
104
+		$legacy_modules_manager = $this->loader->getShared(LegacyModulesManager::class);
105
+		$legacy_modules_manager->setHooks();
106 106
 
107
-        switch ($this->route_request_type) {
108
-            case PrimaryRoute::ROUTE_REQUEST_TYPE_ACTIVATION:
109
-                break;
110
-            case PrimaryRoute::ROUTE_REQUEST_TYPE_REGULAR:
111
-                $this->route_handler->addRoute(
112
-                    'EventEspresso\core\domain\entities\routing\handlers\frontend\ShortcodeRequests'
113
-                );
114
-                /** @var LegacyWidgetsManager $legacy_widgets_manager */
115
-                $legacy_widgets_manager = $this->loader->getShared(LegacyWidgetsManager::class);
116
-                $legacy_widgets_manager->setHooks();
117
-                break;
118
-        }
119
-        $this->routes_loaded[ __FUNCTION__ ] = true;
120
-    }
107
+		switch ($this->route_request_type) {
108
+			case PrimaryRoute::ROUTE_REQUEST_TYPE_ACTIVATION:
109
+				break;
110
+			case PrimaryRoute::ROUTE_REQUEST_TYPE_REGULAR:
111
+				$this->route_handler->addRoute(
112
+					'EventEspresso\core\domain\entities\routing\handlers\frontend\ShortcodeRequests'
113
+				);
114
+				/** @var LegacyWidgetsManager $legacy_widgets_manager */
115
+				$legacy_widgets_manager = $this->loader->getShared(LegacyWidgetsManager::class);
116
+				$legacy_widgets_manager->setHooks();
117
+				break;
118
+		}
119
+		$this->routes_loaded[ __FUNCTION__ ] = true;
120
+	}
121 121
 
122 122
 
123
-    /**
124
-     * @throws Exception
125
-     */
126
-    public function brewEspresso()
127
-    {
128
-        if (isset($this->routes_loaded[ __FUNCTION__ ])) {
129
-            return;
130
-        }
131
-        do_action(
132
-            'AHEE__EventEspresso_core_services_routing_Router__brewEspresso',
133
-            $this->route_handler,
134
-            $this->route_request_type,
135
-            $this->dependency_map
136
-        );
137
-        switch ($this->route_request_type) {
138
-            case PrimaryRoute::ROUTE_REQUEST_TYPE_ACTIVATION:
139
-                break;
140
-            case PrimaryRoute::ROUTE_REQUEST_TYPE_REGULAR:
141
-                $this->route_handler->addRoute(
142
-                    'EventEspresso\core\domain\entities\routing\handlers\shared\GQLRequests'
143
-                );
144
-                $this->route_handler->addRoute(
145
-                    'EventEspresso\core\domain\entities\routing\handlers\shared\RestApiRequests'
146
-                );
147
-                break;
148
-        }
149
-        $this->routes_loaded[ __FUNCTION__ ] = true;
150
-    }
123
+	/**
124
+	 * @throws Exception
125
+	 */
126
+	public function brewEspresso()
127
+	{
128
+		if (isset($this->routes_loaded[ __FUNCTION__ ])) {
129
+			return;
130
+		}
131
+		do_action(
132
+			'AHEE__EventEspresso_core_services_routing_Router__brewEspresso',
133
+			$this->route_handler,
134
+			$this->route_request_type,
135
+			$this->dependency_map
136
+		);
137
+		switch ($this->route_request_type) {
138
+			case PrimaryRoute::ROUTE_REQUEST_TYPE_ACTIVATION:
139
+				break;
140
+			case PrimaryRoute::ROUTE_REQUEST_TYPE_REGULAR:
141
+				$this->route_handler->addRoute(
142
+					'EventEspresso\core\domain\entities\routing\handlers\shared\GQLRequests'
143
+				);
144
+				$this->route_handler->addRoute(
145
+					'EventEspresso\core\domain\entities\routing\handlers\shared\RestApiRequests'
146
+				);
147
+				break;
148
+		}
149
+		$this->routes_loaded[ __FUNCTION__ ] = true;
150
+	}
151 151
 
152 152
 
153
-    /**
154
-     * @throws Exception
155
-     */
156
-    public function loadControllers()
157
-    {
158
-        if (isset($this->routes_loaded[ __FUNCTION__ ])) {
159
-            return;
160
-        }
161
-        do_action(
162
-            'AHEE__EventEspresso_core_services_routing_Router__loadControllers',
163
-            $this->route_handler,
164
-            $this->route_request_type,
165
-            $this->dependency_map
166
-        );
167
-        $this->route_handler->addRoute(
168
-            'EventEspresso\core\domain\entities\routing\handlers\admin\AdminRoute'
169
-        );
170
-        switch ($this->route_request_type) {
171
-            case PrimaryRoute::ROUTE_REQUEST_TYPE_ACTIVATION:
172
-                $this->route_handler->addRoute(
173
-                    'EventEspresso\core\domain\entities\routing\handlers\admin\WordPressPluginsPage'
174
-                );
175
-                break;
176
-            case PrimaryRoute::ROUTE_REQUEST_TYPE_REGULAR:
177
-                $this->route_handler->addRoute(
178
-                    'EventEspresso\core\domain\entities\routing\handlers\frontend\FrontendRequests'
179
-                );
180
-                $this->route_handler->addRoute(
181
-                    'EventEspresso\core\domain\entities\routing\handlers\admin\EspressoLegacyAdmin'
182
-                );
183
-                $this->route_handler->addRoute(
184
-                    'EventEspresso\core\domain\entities\routing\handlers\admin\EspressoEventsAdmin'
185
-                );
186
-                $this->route_handler->addRoute(
187
-                    'EventEspresso\core\domain\entities\routing\handlers\admin\EspressoEventEditor'
188
-                );
189
-                $this->route_handler->addRoute(
190
-                    'EventEspresso\core\domain\entities\routing\handlers\admin\GutenbergEditor'
191
-                );
192
-                $this->route_handler->addRoute(
193
-                    'EventEspresso\core\domain\entities\routing\handlers\admin\NonEspressoAdminAjax'
194
-                );
195
-                $this->route_handler->addRoute(
196
-                    'EventEspresso\core\domain\entities\routing\handlers\admin\WordPressPluginsPage'
197
-                );
198
-                $this->route_handler->addRoute(
199
-                    'EventEspresso\core\domain\entities\routing\handlers\admin\WordPressProfilePage'
200
-                );
201
-                $this->route_handler->addRoute(
202
-                    'EventEspresso\core\domain\entities\routing\handlers\admin\WordPressPostsPage'
203
-                );
204
-                $this->route_handler->addRoute(
205
-                    'EventEspresso\core\domain\entities\routing\handlers\shared\WordPressHeartbeat'
206
-                );
207
-                $this->route_handler->addRoute(
208
-                    'EventEspresso\core\domain\entities\routing\handlers\admin\EspressoBatchJob'
209
-                );
210
-                break;
211
-        }
212
-        $this->routes_loaded[ __FUNCTION__ ] = true;
213
-    }
153
+	/**
154
+	 * @throws Exception
155
+	 */
156
+	public function loadControllers()
157
+	{
158
+		if (isset($this->routes_loaded[ __FUNCTION__ ])) {
159
+			return;
160
+		}
161
+		do_action(
162
+			'AHEE__EventEspresso_core_services_routing_Router__loadControllers',
163
+			$this->route_handler,
164
+			$this->route_request_type,
165
+			$this->dependency_map
166
+		);
167
+		$this->route_handler->addRoute(
168
+			'EventEspresso\core\domain\entities\routing\handlers\admin\AdminRoute'
169
+		);
170
+		switch ($this->route_request_type) {
171
+			case PrimaryRoute::ROUTE_REQUEST_TYPE_ACTIVATION:
172
+				$this->route_handler->addRoute(
173
+					'EventEspresso\core\domain\entities\routing\handlers\admin\WordPressPluginsPage'
174
+				);
175
+				break;
176
+			case PrimaryRoute::ROUTE_REQUEST_TYPE_REGULAR:
177
+				$this->route_handler->addRoute(
178
+					'EventEspresso\core\domain\entities\routing\handlers\frontend\FrontendRequests'
179
+				);
180
+				$this->route_handler->addRoute(
181
+					'EventEspresso\core\domain\entities\routing\handlers\admin\EspressoLegacyAdmin'
182
+				);
183
+				$this->route_handler->addRoute(
184
+					'EventEspresso\core\domain\entities\routing\handlers\admin\EspressoEventsAdmin'
185
+				);
186
+				$this->route_handler->addRoute(
187
+					'EventEspresso\core\domain\entities\routing\handlers\admin\EspressoEventEditor'
188
+				);
189
+				$this->route_handler->addRoute(
190
+					'EventEspresso\core\domain\entities\routing\handlers\admin\GutenbergEditor'
191
+				);
192
+				$this->route_handler->addRoute(
193
+					'EventEspresso\core\domain\entities\routing\handlers\admin\NonEspressoAdminAjax'
194
+				);
195
+				$this->route_handler->addRoute(
196
+					'EventEspresso\core\domain\entities\routing\handlers\admin\WordPressPluginsPage'
197
+				);
198
+				$this->route_handler->addRoute(
199
+					'EventEspresso\core\domain\entities\routing\handlers\admin\WordPressProfilePage'
200
+				);
201
+				$this->route_handler->addRoute(
202
+					'EventEspresso\core\domain\entities\routing\handlers\admin\WordPressPostsPage'
203
+				);
204
+				$this->route_handler->addRoute(
205
+					'EventEspresso\core\domain\entities\routing\handlers\shared\WordPressHeartbeat'
206
+				);
207
+				$this->route_handler->addRoute(
208
+					'EventEspresso\core\domain\entities\routing\handlers\admin\EspressoBatchJob'
209
+				);
210
+				break;
211
+		}
212
+		$this->routes_loaded[ __FUNCTION__ ] = true;
213
+	}
214 214
 
215 215
 
216
-    /**
217
-     * @throws Exception
218
-     */
219
-    public function coreLoadedAndReady()
220
-    {
221
-        if (isset($this->routes_loaded[ __FUNCTION__ ])) {
222
-            return;
223
-        }
224
-        do_action(
225
-            'AHEE__EventEspresso_core_services_routing_Router__coreLoadedAndReady',
226
-            $this->route_handler,
227
-            $this->route_request_type,
228
-            $this->dependency_map
229
-        );
230
-        switch ($this->route_request_type) {
231
-            case PrimaryRoute::ROUTE_REQUEST_TYPE_ACTIVATION:
232
-                break;
233
-            case PrimaryRoute::ROUTE_REQUEST_TYPE_REGULAR:
234
-                $this->route_handler->addRoute(
235
-                    'EventEspresso\core\domain\entities\routing\handlers\shared\AssetRequests'
236
-                );
237
-                $this->route_handler->addRoute(
238
-                    'EventEspresso\core\domain\entities\routing\handlers\shared\SessionRequests'
239
-                );
240
-                break;
241
-        }
242
-        $this->routes_loaded[ __FUNCTION__ ] = true;
243
-    }
216
+	/**
217
+	 * @throws Exception
218
+	 */
219
+	public function coreLoadedAndReady()
220
+	{
221
+		if (isset($this->routes_loaded[ __FUNCTION__ ])) {
222
+			return;
223
+		}
224
+		do_action(
225
+			'AHEE__EventEspresso_core_services_routing_Router__coreLoadedAndReady',
226
+			$this->route_handler,
227
+			$this->route_request_type,
228
+			$this->dependency_map
229
+		);
230
+		switch ($this->route_request_type) {
231
+			case PrimaryRoute::ROUTE_REQUEST_TYPE_ACTIVATION:
232
+				break;
233
+			case PrimaryRoute::ROUTE_REQUEST_TYPE_REGULAR:
234
+				$this->route_handler->addRoute(
235
+					'EventEspresso\core\domain\entities\routing\handlers\shared\AssetRequests'
236
+				);
237
+				$this->route_handler->addRoute(
238
+					'EventEspresso\core\domain\entities\routing\handlers\shared\SessionRequests'
239
+				);
240
+				break;
241
+		}
242
+		$this->routes_loaded[ __FUNCTION__ ] = true;
243
+	}
244 244
 
245 245
 
246
-    /**
247
-     * @throws Exception
248
-     */
249
-    public function initializeLast()
250
-    {
251
-        if (isset($this->routes_loaded[ __FUNCTION__ ])) {
252
-            return;
253
-        }
254
-        do_action(
255
-            'AHEE__EventEspresso_core_services_routing_Router__initializeLast',
256
-            $this->route_handler,
257
-            $this->route_request_type,
258
-            $this->dependency_map
259
-        );
260
-        switch ($this->route_request_type) {
261
-            case PrimaryRoute::ROUTE_REQUEST_TYPE_ACTIVATION:
262
-                break;
263
-            case PrimaryRoute::ROUTE_REQUEST_TYPE_REGULAR:
264
-                $this->route_handler->addRoute(
265
-                    'EventEspresso\core\domain\entities\routing\handlers\admin\PersonalDataRequests'
266
-                );
267
-                break;
268
-        }
269
-        $this->routes_loaded[ __FUNCTION__ ] = true;
270
-    }
246
+	/**
247
+	 * @throws Exception
248
+	 */
249
+	public function initializeLast()
250
+	{
251
+		if (isset($this->routes_loaded[ __FUNCTION__ ])) {
252
+			return;
253
+		}
254
+		do_action(
255
+			'AHEE__EventEspresso_core_services_routing_Router__initializeLast',
256
+			$this->route_handler,
257
+			$this->route_request_type,
258
+			$this->dependency_map
259
+		);
260
+		switch ($this->route_request_type) {
261
+			case PrimaryRoute::ROUTE_REQUEST_TYPE_ACTIVATION:
262
+				break;
263
+			case PrimaryRoute::ROUTE_REQUEST_TYPE_REGULAR:
264
+				$this->route_handler->addRoute(
265
+					'EventEspresso\core\domain\entities\routing\handlers\admin\PersonalDataRequests'
266
+				);
267
+				break;
268
+		}
269
+		$this->routes_loaded[ __FUNCTION__ ] = true;
270
+	}
271 271
 }
Please login to merge, or discard this patch.
core/services/encryption/openssl/CipherMethod.php 2 patches
Indentation   +190 added lines, -190 removed lines patch added patch discarded remove patch
@@ -14,194 +14,194 @@
 block discarded – undo
14 14
  */
15 15
 class CipherMethod
16 16
 {
17
-    protected string $cipher_method_option_name = '';
18
-
19
-    /**
20
-     * list of cipher methods that we consider usable,
21
-     * essentially all of the installed_cipher_methods minus weak_algorithms
22
-     */
23
-    protected array $cipher_methods        = [];
24
-
25
-    protected string $default_cipher_method = '';
26
-
27
-    /**
28
-     * list of ALL cipher methods available on the server
29
-     */
30
-    protected array $installed_cipher_methods = [];
31
-
32
-    /**
33
-     * the OpenSSL cipher method to use. default: AES-128-CBC
34
-     */
35
-    protected ?string $validated_cipher_method = null;
36
-
37
-    /**
38
-     * as early as Aug 2016, Openssl declared the following weak: RC2, RC4, DES, 3DES, MD5 based
39
-     * and ECB mode should be avoided
40
-     */
41
-    protected array $weak_algorithms = ['des', 'ecb', 'md5', 'rc2', 'rc4'];
42
-
43
-
44
-    /**
45
-     * @param string $default_cipher_method
46
-     * @param string $cipher_method_option_name
47
-     */
48
-    public function __construct(string $default_cipher_method, string $cipher_method_option_name)
49
-    {
50
-        $this->default_cipher_method     = $default_cipher_method;
51
-        $this->cipher_method_option_name = $cipher_method_option_name;
52
-        $this->installed_cipher_methods  = openssl_get_cipher_methods();
53
-    }
54
-
55
-
56
-    /**
57
-     * Returns a cipher method that has been verified to work.
58
-     * First checks if the cached cipher has been set already and if so, returns that.
59
-     * Then tests the incoming default and returns that if it's good.
60
-     * If not, then it retrieves the previously tested and saved cipher method.
61
-     * But if that doesn't exist, then calls getAvailableCipherMethod()
62
-     * to see what is available on the server, and returns the results.
63
-     *
64
-     * @param string $cipher_method
65
-     * @param bool   $load_alternate [optional] if TRUE, will load the default cipher method (or any strong algorithm)
66
-     *                               if the requested cipher method is not installed or invalid.
67
-     *                               if FALSE, will throw an exception if the requested cipher method is not valid.
68
-     * @return string
69
-     * @throws RuntimeException
70
-     */
71
-    public function getCipherMethod(string $cipher_method = '', bool $load_alternate = true): ?string
72
-    {
73
-        if (empty($cipher_method) && $this->validated_cipher_method !== null) {
74
-            return $this->validated_cipher_method;
75
-        }
76
-        // if nothing specific was requested and it's ok to load an alternate, then grab the system default
77
-        if (empty($cipher_method) && $load_alternate) {
78
-            $cipher_method = $this->default_cipher_method;
79
-        }
80
-        // verify that the cipher method can produce an initialization vector.
81
-        // but if the requested is invalid and we don't want to load an alternate, then throw an exception
82
-        $throw_exception = ! $load_alternate;
83
-        if ($this->validateCipherMethod($cipher_method, $throw_exception) === false) {
84
-            // nope? ... ok let's see what we can find
85
-            $cipher_method = $this->getAvailableCipherMethod();
86
-        }
87
-        // if nothing has been previously validated, then save the currently requested cipher which appears to be good
88
-        if ($this->validated_cipher_method === null) {
89
-            $this->validated_cipher_method = $cipher_method;
90
-        }
91
-        return $cipher_method;
92
-    }
93
-
94
-
95
-    /**
96
-     * returns true if the selected cipher method either uses Galois/Counter Mode (GCM)
97
-     * or Counter with CBC-MAC (CCM) authenticated encryption modes
98
-     * (also need to be using PHP 7.1 or greater to actually use authenticated encryption modes)
99
-     *
100
-     * @return bool
101
-     */
102
-    public function usesAuthenticatedEncryptionMode(): bool
103
-    {
104
-        return PHP_VERSION_ID >= 70100
105
-               && (
106
-                   stripos($this->validated_cipher_method, 'gcm') !== false
107
-                   || stripos($this->validated_cipher_method, 'ccm') !== false
108
-               );
109
-    }
110
-
111
-
112
-    /**
113
-     * @param string $cipher_method
114
-     * @return string
115
-     * @throws RuntimeException
116
-     */
117
-    protected function getAvailableCipherMethod(string $cipher_method = ''): ?string
118
-    {
119
-        // if nothing was supplied, the get what we found in the past to work
120
-        $cipher_method_to_test = $cipher_method
121
-            ?: get_option($this->cipher_method_option_name, '');
122
-        // verify that the incoming cipher method exists and can produce an initialization vector
123
-        if ($this->validateCipherMethod($cipher_method_to_test) === false) {
124
-            // what? there's no list?
125
-            if (empty($this->cipher_methods)) {
126
-                // generate that list and cache it
127
-                $this->cipher_methods = $this->getAvailableStrongCipherMethods();
128
-            }
129
-            // then grab the first item from the list (we'll add it back later if it is good)
130
-            $cipher_method_to_test = array_shift($this->cipher_methods);
131
-            if ($cipher_method_to_test === null) {
132
-                throw new RuntimeException(
133
-                    esc_html__(
134
-                        'OpenSSL support appears to be enabled on the server, but no cipher methods are available. Please contact the server administrator.',
135
-                        'event_espresso'
136
-                    )
137
-                );
138
-            }
139
-            // verify that the next cipher method works
140
-            return $this->getAvailableCipherMethod($cipher_method_to_test);
141
-        }
142
-        // if we've gotten this far, then we found an available cipher method that works
143
-        // so save that for next time, if it's not the same as what's there already
144
-        if ($cipher_method_to_test !== $cipher_method) {
145
-            update_option($this->cipher_method_option_name, $cipher_method_to_test);
146
-        }
147
-        // if we previously removed this cipher method from the list of valid ones, then let's put it back
148
-        if (! in_array($cipher_method_to_test, $this->cipher_methods, true)) {
149
-            array_unshift($this->cipher_methods, $cipher_method_to_test);
150
-        }
151
-        return $cipher_method_to_test;
152
-    }
153
-
154
-
155
-    /**
156
-     * @return array
157
-     */
158
-    protected function getAvailableStrongCipherMethods(): array
159
-    {
160
-        return array_filter($this->installed_cipher_methods, [$this, 'weakAlgorithmFilter']);
161
-    }
162
-
163
-
164
-    /**
165
-     * @param string $cipher_method
166
-     * @param bool   $throw_exception
167
-     * @return bool
168
-     */
169
-    protected function validateCipherMethod(string $cipher_method, bool $throw_exception = false): bool
170
-    {
171
-        // verify that the requested cipher method is actually installed and can produce an initialization vector
172
-        if (
173
-            in_array($cipher_method, $this->installed_cipher_methods, true)
174
-            && openssl_cipher_iv_length($cipher_method) !== false
175
-        ) {
176
-            return true;
177
-        }
178
-        if (! $throw_exception) {
179
-            return false;
180
-        }
181
-        throw new RuntimeException(
182
-            sprintf(
183
-                esc_html__(
184
-                    'The requested OpenSSL cipher method "%1$s" is invalid or not installed on the server. Please contact the server administrator.',
185
-                    'event_espresso'
186
-                ),
187
-                $cipher_method
188
-            )
189
-        );
190
-    }
191
-
192
-
193
-    /**
194
-     * @see https://www.php.net/manual/en/function.openssl-get-cipher-methods.php#example-890
195
-     * @param string $cipher_method
196
-     * @return bool
197
-     */
198
-    protected function weakAlgorithmFilter(string $cipher_method): bool
199
-    {
200
-        foreach ($this->weak_algorithms as $weak_algorithm) {
201
-            if (stripos($cipher_method, $weak_algorithm) !== false) {
202
-                return false;
203
-            }
204
-        }
205
-        return true;
206
-    }
17
+	protected string $cipher_method_option_name = '';
18
+
19
+	/**
20
+	 * list of cipher methods that we consider usable,
21
+	 * essentially all of the installed_cipher_methods minus weak_algorithms
22
+	 */
23
+	protected array $cipher_methods        = [];
24
+
25
+	protected string $default_cipher_method = '';
26
+
27
+	/**
28
+	 * list of ALL cipher methods available on the server
29
+	 */
30
+	protected array $installed_cipher_methods = [];
31
+
32
+	/**
33
+	 * the OpenSSL cipher method to use. default: AES-128-CBC
34
+	 */
35
+	protected ?string $validated_cipher_method = null;
36
+
37
+	/**
38
+	 * as early as Aug 2016, Openssl declared the following weak: RC2, RC4, DES, 3DES, MD5 based
39
+	 * and ECB mode should be avoided
40
+	 */
41
+	protected array $weak_algorithms = ['des', 'ecb', 'md5', 'rc2', 'rc4'];
42
+
43
+
44
+	/**
45
+	 * @param string $default_cipher_method
46
+	 * @param string $cipher_method_option_name
47
+	 */
48
+	public function __construct(string $default_cipher_method, string $cipher_method_option_name)
49
+	{
50
+		$this->default_cipher_method     = $default_cipher_method;
51
+		$this->cipher_method_option_name = $cipher_method_option_name;
52
+		$this->installed_cipher_methods  = openssl_get_cipher_methods();
53
+	}
54
+
55
+
56
+	/**
57
+	 * Returns a cipher method that has been verified to work.
58
+	 * First checks if the cached cipher has been set already and if so, returns that.
59
+	 * Then tests the incoming default and returns that if it's good.
60
+	 * If not, then it retrieves the previously tested and saved cipher method.
61
+	 * But if that doesn't exist, then calls getAvailableCipherMethod()
62
+	 * to see what is available on the server, and returns the results.
63
+	 *
64
+	 * @param string $cipher_method
65
+	 * @param bool   $load_alternate [optional] if TRUE, will load the default cipher method (or any strong algorithm)
66
+	 *                               if the requested cipher method is not installed or invalid.
67
+	 *                               if FALSE, will throw an exception if the requested cipher method is not valid.
68
+	 * @return string
69
+	 * @throws RuntimeException
70
+	 */
71
+	public function getCipherMethod(string $cipher_method = '', bool $load_alternate = true): ?string
72
+	{
73
+		if (empty($cipher_method) && $this->validated_cipher_method !== null) {
74
+			return $this->validated_cipher_method;
75
+		}
76
+		// if nothing specific was requested and it's ok to load an alternate, then grab the system default
77
+		if (empty($cipher_method) && $load_alternate) {
78
+			$cipher_method = $this->default_cipher_method;
79
+		}
80
+		// verify that the cipher method can produce an initialization vector.
81
+		// but if the requested is invalid and we don't want to load an alternate, then throw an exception
82
+		$throw_exception = ! $load_alternate;
83
+		if ($this->validateCipherMethod($cipher_method, $throw_exception) === false) {
84
+			// nope? ... ok let's see what we can find
85
+			$cipher_method = $this->getAvailableCipherMethod();
86
+		}
87
+		// if nothing has been previously validated, then save the currently requested cipher which appears to be good
88
+		if ($this->validated_cipher_method === null) {
89
+			$this->validated_cipher_method = $cipher_method;
90
+		}
91
+		return $cipher_method;
92
+	}
93
+
94
+
95
+	/**
96
+	 * returns true if the selected cipher method either uses Galois/Counter Mode (GCM)
97
+	 * or Counter with CBC-MAC (CCM) authenticated encryption modes
98
+	 * (also need to be using PHP 7.1 or greater to actually use authenticated encryption modes)
99
+	 *
100
+	 * @return bool
101
+	 */
102
+	public function usesAuthenticatedEncryptionMode(): bool
103
+	{
104
+		return PHP_VERSION_ID >= 70100
105
+			   && (
106
+				   stripos($this->validated_cipher_method, 'gcm') !== false
107
+				   || stripos($this->validated_cipher_method, 'ccm') !== false
108
+			   );
109
+	}
110
+
111
+
112
+	/**
113
+	 * @param string $cipher_method
114
+	 * @return string
115
+	 * @throws RuntimeException
116
+	 */
117
+	protected function getAvailableCipherMethod(string $cipher_method = ''): ?string
118
+	{
119
+		// if nothing was supplied, the get what we found in the past to work
120
+		$cipher_method_to_test = $cipher_method
121
+			?: get_option($this->cipher_method_option_name, '');
122
+		// verify that the incoming cipher method exists and can produce an initialization vector
123
+		if ($this->validateCipherMethod($cipher_method_to_test) === false) {
124
+			// what? there's no list?
125
+			if (empty($this->cipher_methods)) {
126
+				// generate that list and cache it
127
+				$this->cipher_methods = $this->getAvailableStrongCipherMethods();
128
+			}
129
+			// then grab the first item from the list (we'll add it back later if it is good)
130
+			$cipher_method_to_test = array_shift($this->cipher_methods);
131
+			if ($cipher_method_to_test === null) {
132
+				throw new RuntimeException(
133
+					esc_html__(
134
+						'OpenSSL support appears to be enabled on the server, but no cipher methods are available. Please contact the server administrator.',
135
+						'event_espresso'
136
+					)
137
+				);
138
+			}
139
+			// verify that the next cipher method works
140
+			return $this->getAvailableCipherMethod($cipher_method_to_test);
141
+		}
142
+		// if we've gotten this far, then we found an available cipher method that works
143
+		// so save that for next time, if it's not the same as what's there already
144
+		if ($cipher_method_to_test !== $cipher_method) {
145
+			update_option($this->cipher_method_option_name, $cipher_method_to_test);
146
+		}
147
+		// if we previously removed this cipher method from the list of valid ones, then let's put it back
148
+		if (! in_array($cipher_method_to_test, $this->cipher_methods, true)) {
149
+			array_unshift($this->cipher_methods, $cipher_method_to_test);
150
+		}
151
+		return $cipher_method_to_test;
152
+	}
153
+
154
+
155
+	/**
156
+	 * @return array
157
+	 */
158
+	protected function getAvailableStrongCipherMethods(): array
159
+	{
160
+		return array_filter($this->installed_cipher_methods, [$this, 'weakAlgorithmFilter']);
161
+	}
162
+
163
+
164
+	/**
165
+	 * @param string $cipher_method
166
+	 * @param bool   $throw_exception
167
+	 * @return bool
168
+	 */
169
+	protected function validateCipherMethod(string $cipher_method, bool $throw_exception = false): bool
170
+	{
171
+		// verify that the requested cipher method is actually installed and can produce an initialization vector
172
+		if (
173
+			in_array($cipher_method, $this->installed_cipher_methods, true)
174
+			&& openssl_cipher_iv_length($cipher_method) !== false
175
+		) {
176
+			return true;
177
+		}
178
+		if (! $throw_exception) {
179
+			return false;
180
+		}
181
+		throw new RuntimeException(
182
+			sprintf(
183
+				esc_html__(
184
+					'The requested OpenSSL cipher method "%1$s" is invalid or not installed on the server. Please contact the server administrator.',
185
+					'event_espresso'
186
+				),
187
+				$cipher_method
188
+			)
189
+		);
190
+	}
191
+
192
+
193
+	/**
194
+	 * @see https://www.php.net/manual/en/function.openssl-get-cipher-methods.php#example-890
195
+	 * @param string $cipher_method
196
+	 * @return bool
197
+	 */
198
+	protected function weakAlgorithmFilter(string $cipher_method): bool
199
+	{
200
+		foreach ($this->weak_algorithms as $weak_algorithm) {
201
+			if (stripos($cipher_method, $weak_algorithm) !== false) {
202
+				return false;
203
+			}
204
+		}
205
+		return true;
206
+	}
207 207
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -20,7 +20,7 @@  discard block
 block discarded – undo
20 20
      * list of cipher methods that we consider usable,
21 21
      * essentially all of the installed_cipher_methods minus weak_algorithms
22 22
      */
23
-    protected array $cipher_methods        = [];
23
+    protected array $cipher_methods = [];
24 24
 
25 25
     protected string $default_cipher_method = '';
26 26
 
@@ -145,7 +145,7 @@  discard block
 block discarded – undo
145 145
             update_option($this->cipher_method_option_name, $cipher_method_to_test);
146 146
         }
147 147
         // if we previously removed this cipher method from the list of valid ones, then let's put it back
148
-        if (! in_array($cipher_method_to_test, $this->cipher_methods, true)) {
148
+        if ( ! in_array($cipher_method_to_test, $this->cipher_methods, true)) {
149 149
             array_unshift($this->cipher_methods, $cipher_method_to_test);
150 150
         }
151 151
         return $cipher_method_to_test;
@@ -175,7 +175,7 @@  discard block
 block discarded – undo
175 175
         ) {
176 176
             return true;
177 177
         }
178
-        if (! $throw_exception) {
178
+        if ( ! $throw_exception) {
179 179
             return false;
180 180
         }
181 181
         throw new RuntimeException(
Please login to merge, or discard this patch.
core/services/graphql/GraphQLEndpoint.php 2 patches
Indentation   +73 added lines, -73 removed lines patch added patch discarded remove patch
@@ -18,88 +18,88 @@
 block discarded – undo
18 18
  */
19 19
 class GraphQLEndpoint extends WordPressOption
20 20
 {
21
-    const DEFAULT_ENDPOINT = 'graphql';
21
+	const DEFAULT_ENDPOINT = 'graphql';
22 22
 
23
-    const OPTION_NAME      = 'ee-graphql-endpoint';
23
+	const OPTION_NAME      = 'ee-graphql-endpoint';
24 24
 
25
-    private bool $is_gql_request;
25
+	private bool $is_gql_request;
26 26
 
27 27
 
28
-    /**
29
-     * GraphQLEndpoint constructor.
30
-     */
31
-    public function __construct()
32
-    {
33
-        parent::__construct(GraphQLEndpoint::OPTION_NAME, GraphQLEndpoint::DEFAULT_ENDPOINT, true);
34
-        add_action('graphql_register_settings', [$this, 'verifyAndSetEndpoint'], 20);
35
-        if (! $this->graphqlDebug()) {
36
-            // disable WPGraphQL admin by default.
37
-            add_filter('graphql_show_admin', '__return_false');
38
-            add_filter('graphql_enable_graphiql', '__return_false');
39
-        }
40
-    }
28
+	/**
29
+	 * GraphQLEndpoint constructor.
30
+	 */
31
+	public function __construct()
32
+	{
33
+		parent::__construct(GraphQLEndpoint::OPTION_NAME, GraphQLEndpoint::DEFAULT_ENDPOINT, true);
34
+		add_action('graphql_register_settings', [$this, 'verifyAndSetEndpoint'], 20);
35
+		if (! $this->graphqlDebug()) {
36
+			// disable WPGraphQL admin by default.
37
+			add_filter('graphql_show_admin', '__return_false');
38
+			add_filter('graphql_enable_graphiql', '__return_false');
39
+		}
40
+	}
41 41
 
42
-    /**
43
-     * Determine the value of global constant GRAPHQL_DEBUG
44
-     * @return bool
45
-     */
46
-    private function graphqlDebug(): bool
47
-    {
48
-        return defined('GRAPHQL_DEBUG') && GRAPHQL_DEBUG;
49
-    }
42
+	/**
43
+	 * Determine the value of global constant GRAPHQL_DEBUG
44
+	 * @return bool
45
+	 */
46
+	private function graphqlDebug(): bool
47
+	{
48
+		return defined('GRAPHQL_DEBUG') && GRAPHQL_DEBUG;
49
+	}
50 50
 
51 51
 
52
-    /**
53
-     * @return false|mixed|void
54
-     */
55
-    public function getEndpoint()
56
-    {
57
-        return $this->loadOption();
58
-    }
52
+	/**
53
+	 * @return false|mixed|void
54
+	 */
55
+	public function getEndpoint()
56
+	{
57
+		return $this->loadOption();
58
+	}
59 59
 
60 60
 
61
-    /**
62
-     * @return mixed|void|bool
63
-     */
64
-    public function isGraphqlRequest()
65
-    {
66
-        if (! isset($this->is_gql_request)) {
67
-            // grab the GQL endpoint that we saved in the future... wait... wut?
68
-            $endpoint = $this->getEndpoint();
69
-            // set our saved endpoint on the WP GQL Pouter class
70
-            // don't worry, this is a static property that they overwrite when they initialize things,
71
-            // and we are essentially getting the value from them anyways, so it should be ok (fingers crossed)
72
-            Router::$route = $endpoint;
73
-            // now call their function for checking if this is a GQL request
74
-            // because they use a bunch of filters and stuff we don't want to duplicate here
75
-            $this->is_gql_request = Router::is_graphql_http_request();
76
-        }
77
-        return $this->is_gql_request;
78
-    }
61
+	/**
62
+	 * @return mixed|void|bool
63
+	 */
64
+	public function isGraphqlRequest()
65
+	{
66
+		if (! isset($this->is_gql_request)) {
67
+			// grab the GQL endpoint that we saved in the future... wait... wut?
68
+			$endpoint = $this->getEndpoint();
69
+			// set our saved endpoint on the WP GQL Pouter class
70
+			// don't worry, this is a static property that they overwrite when they initialize things,
71
+			// and we are essentially getting the value from them anyways, so it should be ok (fingers crossed)
72
+			Router::$route = $endpoint;
73
+			// now call their function for checking if this is a GQL request
74
+			// because they use a bunch of filters and stuff we don't want to duplicate here
75
+			$this->is_gql_request = Router::is_graphql_http_request();
76
+		}
77
+		return $this->is_gql_request;
78
+	}
79 79
 
80 80
 
81
-    /**
82
-     * callback hooked into the graphql_register_settings action
83
-     * basically grabs the value for the 'graphql_endpoint' setting and saves it to our own WP option.
84
-     * i know, i know, i know...
85
-     * WHY are we saving the graphql_endpoint to a WP option if WP GraphQL is already doing the same ?!?!
86
-     * Because we perform our request type detection during `plugins_loaded`,
87
-     * but they don't settle on their endpoint until `after_setup_theme` has run,
88
-     * which is too late for us, so we are just going to save this separately so that we have it.
89
-     * Oh, and this will essentially recheck this value on EVERY request
90
-     * but will only update our saved value if it has actually changed
91
-     */
92
-    public function verifyAndSetEndpoint()
93
-    {
94
-        try {
95
-            $this->updateOption(
96
-                get_graphql_setting(
97
-                    'graphql_endpoint',
98
-                    apply_filters('graphql_endpoint', GraphQLEndpoint::DEFAULT_ENDPOINT)
99
-                )
100
-            );
101
-        } catch (Throwable $t) {
102
-            // eat it
103
-        }
104
-    }
81
+	/**
82
+	 * callback hooked into the graphql_register_settings action
83
+	 * basically grabs the value for the 'graphql_endpoint' setting and saves it to our own WP option.
84
+	 * i know, i know, i know...
85
+	 * WHY are we saving the graphql_endpoint to a WP option if WP GraphQL is already doing the same ?!?!
86
+	 * Because we perform our request type detection during `plugins_loaded`,
87
+	 * but they don't settle on their endpoint until `after_setup_theme` has run,
88
+	 * which is too late for us, so we are just going to save this separately so that we have it.
89
+	 * Oh, and this will essentially recheck this value on EVERY request
90
+	 * but will only update our saved value if it has actually changed
91
+	 */
92
+	public function verifyAndSetEndpoint()
93
+	{
94
+		try {
95
+			$this->updateOption(
96
+				get_graphql_setting(
97
+					'graphql_endpoint',
98
+					apply_filters('graphql_endpoint', GraphQLEndpoint::DEFAULT_ENDPOINT)
99
+				)
100
+			);
101
+		} catch (Throwable $t) {
102
+			// eat it
103
+		}
104
+	}
105 105
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -32,7 +32,7 @@  discard block
 block discarded – undo
32 32
     {
33 33
         parent::__construct(GraphQLEndpoint::OPTION_NAME, GraphQLEndpoint::DEFAULT_ENDPOINT, true);
34 34
         add_action('graphql_register_settings', [$this, 'verifyAndSetEndpoint'], 20);
35
-        if (! $this->graphqlDebug()) {
35
+        if ( ! $this->graphqlDebug()) {
36 36
             // disable WPGraphQL admin by default.
37 37
             add_filter('graphql_show_admin', '__return_false');
38 38
             add_filter('graphql_enable_graphiql', '__return_false');
@@ -63,7 +63,7 @@  discard block
 block discarded – undo
63 63
      */
64 64
     public function isGraphqlRequest()
65 65
     {
66
-        if (! isset($this->is_gql_request)) {
66
+        if ( ! isset($this->is_gql_request)) {
67 67
             // grab the GQL endpoint that we saved in the future... wait... wut?
68 68
             $endpoint = $this->getEndpoint();
69 69
             // set our saved endpoint on the WP GQL Pouter class
Please login to merge, or discard this patch.