Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 336
0.00% covered (danger)
0.00%
0 / 42
CRAP
0.00% covered (danger)
0.00%
0 / 1
Mailpoet
0.00% covered (danger)
0.00%
0 / 335
0.00% covered (danger)
0.00%
0 / 42
11342
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 instance
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 definitions
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 check_dependencies
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
6
 init_settings
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_settings
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 init_hooks
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 init_features
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
30
 load_ajax
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
42
 add_listview_filters
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 contact_query_quickfilter_addition
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
6
 new_contact_log_override
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 register_styles_scripts
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 add_settings_tab
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 default_settings
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 add_wp_pages
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 add_tools_menu_sub_item_link
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 render_learn_menu
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
6
 load_admin_page
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 override_crm_external_source_infobox
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
30
 get_crm_mailpoet_contact_count
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 is_hub_page
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 is_add_edit_page
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
 get_jpcrm_mailpoet_latest_stats
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 get_mailpoet_sub_stats_link
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
20
 get_mailpoet_list_subs_link
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_local_mailpoet_admin_url
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_mailpoet_list_tagged_link
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_mailpoet_subscriber_by_subscriber_id
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
12
 get_mailpoet_subscriber_by_email
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
56
 get_mailpoet_lists_for_contact_from_email
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
30
 get_mailpoet_list_summary_by_name
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
20
 get_mailpoet_lists_summary
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
30
 get_all_mailpoet_subscribers_count
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_all_mailpoet_subscribers
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 get_mailpoet_subscribers
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
56
 get_mailpoet_list_by_name
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 reset_mailpoet_list_by_segment_name
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
12
 get_mailpoet_setup_status
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 run_migrations
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 contacts_to_subscribers
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
72
 add_segments_condition_category_positions
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/*
3 * Jetpack CRM
4 * https://jetpackcrm.com
5 *
6 * MailPoet Sync
7 *
8 */
9namespace Automattic\JetpackCRM;
10
11// block direct access
12defined( 'ZEROBSCRM_PATH' ) || exit( 0 );
13
14use MailPoet\API\MP\v1\APIException;
15
16/**
17 * MailPoet class
18 */
19class Mailpoet {
20
21    /**
22     * Extension settings key
23     *
24     * @var string
25     */
26    public $config_key = 'mailpoet';
27
28    /**
29     * Extension name.
30     *
31     * @var string
32     */
33    public $ext_name = 'MailPoet Sync';
34
35    /**
36     * Settings object
37     *
38     * @var \WHWPConfigExtensionsLib | null
39     */
40    public $settings = null;
41
42    /**
43     * Show extension settings tab
44     *
45     * @var string
46     */
47    public $settings_tab = true;
48
49    /**
50     * Feature class object: Background Sync
51     *
52     * @var Mailpoet_Background_Sync | null
53     */
54    public $background_sync = null;
55
56    /**
57     * Feature class object: Contact Tabs
58     *
59     * @var Mailpoet_Contact_Tabs | null
60     */
61    public $contact_tabs = null;
62
63    /**
64     * Feature class object: MailPoet Admin UI modifications
65     *
66     * @var Mailpoet_Admin_Integration | null
67     */
68    public $mailpoet_ui = null;
69
70    /**
71     * Feature class object: Export CRM segment to MailPoet list class
72     *
73     * @var Mailpoet_Export_Segment_To_MailPoet | null
74     */
75    public $mailpoet_export_segment = null;
76
77    /**
78     * Feature class object: MailPoet Segment Conditions
79     *
80     * @var mailpoet_Segment_Conditions | null
81     */
82    public $segment_conditions = null;
83
84    /**
85     * The single instance of the class.
86     */
87    protected static $_instance = null;
88
89    /**
90     * Slugs for internal pages
91     *
92     * @var array()
93     */
94    public $slugs = array(
95        'hub'      => 'crm-mail-poet-hub', // note needs to match `$zbs->slugs['mailpoet'] and can't use `*mailpoet*` as the plugin inteferes with styles
96        'settings' => 'mailpoet',
97        'add-edit' => 'zbs-add-edit',
98    );
99
100    /**
101     * URLs that the MailPoet module uses
102     *
103     * @var array()
104     */
105    public $urls = array(
106
107        'install_mailpoet' => '/wp-admin/plugin-install.php?tab=plugin-information&plugin=mailpoet',
108
109    );
110
111    /**
112     * Setup MailPoet
113     * Note: This will effectively fire after core settings and modules loaded on tail end of `init`
114     */
115    public function __construct() {
116
117        if ( $this->check_dependencies() ) {
118
119            // Definitions
120            $this->definitions();
121
122            // Initialise Settings
123            $this->init_settings();
124
125            // Initialise Features
126            $this->init_features();
127
128            // Run migrations (if any)
129            $this->run_migrations();
130
131            // Initialise Hooks
132            $this->init_hooks();
133
134            // Autoload page AJAX
135            $this->load_ajax();
136
137            // Register frontend/backend styles and scripts
138            $this->register_styles_scripts();
139
140        }
141    }
142
143    /**
144     * Main Class Instance.
145     *
146     * Ensures only one instance of Mailpoet_Sync is loaded or can be loaded.
147     *
148     * @since 2.0
149     * @static
150     * @see
151     * @return Mailpoet_Sync main instance
152     */
153    public static function instance() {
154        if ( self::$_instance === null ) {
155            self::$_instance = new self();
156        }
157        return self::$_instance;
158    }
159
160    /**
161     * Define any key vars.
162     */
163    private function definitions() {
164
165        // for now there is ONLY local, but precursors..
166        define( 'JPCRM_MAILPOET_MODE_LOCAL', 0 );
167        define( 'JPCRM_MAILPOET_MODE_API', 1 );
168    }
169
170    /**
171     *
172     * Checks dependencies
173     *
174     * @return bool
175     */
176    public function check_dependencies() {
177
178        global $zbs;
179
180        $core_reqs      = array(
181            'req_core_ver' => $zbs::VERSION, // will match current core version
182            'req_DAL_ver'  => '3.0',
183        );
184        $plugin_reqs    = array(
185            'name'    => 'MailPoet',
186            'slug'    => 'mailpoet/mailpoet.php',
187            'link'    => 'https://wordpress.org/plugins/mailpoet/',
188            'kb_link' => $zbs->urls['kb-mailpoet'],
189            'req_ver' => '3.103.0',
190        );
191        $meets_all_reqs = $zbs->dependency_checker->check_all_reqs(
192            $this->ext_name,
193            $core_reqs,
194            $plugin_reqs
195        );
196
197        if ( $meets_all_reqs ) {
198            return true;
199        }
200        return false;
201    }
202
203    /**
204     * Initialise Settings
205     */
206    private function init_settings() {
207
208        $this->settings = new \WHWPConfigExtensionsLib( $this->config_key, $this->default_settings() );
209    }
210
211    /**
212     * Retrieve Settings
213     */
214    public function get_settings() {
215
216        return $this->settings->getAll();
217    }
218
219    /**
220     * Initialise Hooks
221     */
222    private function init_hooks() {
223
224        // Add settings tab
225        add_filter( 'zbs_settings_tabs', array( $this, 'add_settings_tab' ) );
226
227        // Menus:
228
229        // Adds Tools menu subitem
230        add_filter( 'zbs-tools-menu', array( $this, 'add_tools_menu_sub_item_link' ) );
231        // Learn menu
232        add_action( 'wp_after_admin_bar_render', array( $this, 'render_learn_menu' ), 12 );
233        // Admin menu
234        add_filter( 'zbs_menu_wpmenu', array( $this, 'add_wp_pages' ), 10, 1 );
235
236        // JPCRM effecting:
237
238        // Add MailPoet related info to CRM external source infobox
239        add_filter( 'zbs_external_source_infobox_line', array( $this, 'override_crm_external_source_infobox' ), 10, 2 );
240
241        // Add listview filters.
242        add_filter( 'jpcrm_listview_filters', array( $this, 'add_listview_filters' ) );
243
244        // Hook in to Contact query generation to support quickfilter
245        add_filter( 'jpcrm_contact_query_quickfilter', array( $this, 'contact_query_quickfilter_addition' ), 10, 2 );
246
247        // Hook in to new contact log creation and add string manipulation
248        add_filter( 'jpcrm_new_contact_log', array( $this, 'new_contact_log_override' ), 10, 3 );
249
250        // add a position to the MailPoet segment condition category positions array
251        add_filter( 'jpcrm_segment_condition_category_positions', array( $this, 'add_segments_condition_category_positions' ) );
252    }
253
254    /**
255     * Initialise Features
256     */
257    private function init_features() {
258
259        global $zbs;
260
261        // Contact Tabs
262        if ( zeroBSCRM_is_customer_view_page() ) {
263
264            require_once JPCRM_MAILPOET_ROOT_PATH . 'includes/jpcrm-mailpoet-contact-tabs.php';
265            $this->contact_tabs = Mailpoet_Contact_Tabs::instance();
266            wp_enqueue_style( 'jpcrm-mailpoet-contact-tabs', plugins_url( '/css/jpcrm-mailpoet-contact-tabs.min.css', JPCRM_MAILPOET_ROOT_FILE ) );
267
268        }
269
270        // Settings page
271        if ( jpcrm_is_settings_page() ) {
272
273            $this->load_admin_page( 'settings/router' );
274
275        }
276
277        // Hub page
278        if ( $this->is_hub_page() ) {
279
280            $this->load_admin_page( 'mailpoet-hub/main' );
281
282        }
283
284        // Edit / Add page
285        if ( $this->is_add_edit_page() ) {
286
287            wp_enqueue_style( 'jpcrm-mailpoet-add-edit-page', plugins_url( '/css/jpcrm-mailpoet-add-edit-page' . wp_scripts_get_suffix() . '.css', JPCRM_MAILPOET_ROOT_FILE ), array(), $zbs::VERSION );
288            wp_enqueue_script( 'jpcrm-mailpoet-add-edit-page', plugins_url( '/js/jpcrm-mailpoet-add-edit-page' . wp_scripts_get_suffix() . '.js', JPCRM_MAILPOET_ROOT_FILE ), array( 'jquery' ), $zbs::VERSION ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.NotInFooter
289
290        }
291
292        // Background sync
293        require_once JPCRM_MAILPOET_ROOT_PATH . 'includes/class-mailpoet-background-sync.php';
294        $this->background_sync = Mailpoet_Background_Sync::instance();
295
296        // MailPoet UI additions
297        require_once JPCRM_MAILPOET_ROOT_PATH . 'includes/class-mailpoet-admin-integration.php';
298        $this->mailpoet_ui = Mailpoet_Admin_Integration::instance();
299
300        // MailPoet Export CRM Segment to MailPoet List
301        require_once JPCRM_MAILPOET_ROOT_PATH . 'includes/class-mailpoet-export-segment-to-mailpoet.php';
302        $this->mailpoet_export_segment = Mailpoet_Export_Segment_To_MailPoet::instance();
303
304        // Segment conditions
305        require_once JPCRM_MAILPOET_ROOT_PATH . 'includes/class-mailpoet-segment-conditions.php';
306        $this->segment_conditions = MailPoet_Segment_Conditions::instance();
307    }
308
309    /**
310     * Autoload page AJAX
311     */
312    private function load_ajax() {
313
314        $admin_page_directories = jpcrm_get_directories( JPCRM_MAILPOET_ROOT_PATH . 'admin' );
315
316        if ( is_array( $admin_page_directories ) ) {
317
318            foreach ( $admin_page_directories as $directory ) {
319
320                $files = scandir( JPCRM_MAILPOET_ROOT_PATH . 'admin/' . $directory );
321
322                if ( is_array( $files ) ) {
323
324                    foreach ( $files as $file ) {
325
326                        // find files `*.ajax.*`
327                        if ( strrpos( $file, '.ajax.' ) > 0 ) {
328
329                            // load it
330                            require_once JPCRM_MAILPOET_ROOT_PATH . 'admin/' . $directory . '/' . $file;
331
332                        }
333                    }
334                }
335            }
336        }
337    }
338
339    /**
340     * Adds items to listview filter using `jpcrm_listview_filters` hook.
341     *
342     * @param array $listview_filters Listview filters.
343     */
344    public function add_listview_filters( $listview_filters ) {
345        $listview_filters[ ZBS_TYPE_CONTACT ]['general']['mailpoet_subscriber'] = __( 'MailPoet', 'zero-bs-crm' );
346        return $listview_filters;
347    }
348
349    /**
350     * Hook in to Contact query generation and add the quickfilter
351     * (Hooked into `jpcrm_contact_query_quickfilter`)
352     */
353    public function contact_query_quickfilter_addition( $wheres, $quick_filter_key ) {
354
355        global $ZBSCRM_t;
356
357        // is a MailPoet subscriber? (Could be copied/generalised for other ext sources)
358        if ( $quick_filter_key == 'mailpoet_subscriber' ) {
359            $wheres['is_mailpoet_customer'] = array(
360                'ID',
361                'IN',
362                '(SELECT DISTINCT zbss_objid FROM ' . $ZBSCRM_t['externalsources'] . ' WHERE zbss_objtype = ' . ZBS_TYPE_CONTACT . ' AND zbss_source = %s)',
363                array( 'mailpoet' ),
364            );
365        }
366
367        return $wheres;
368    }
369
370    /**
371     * Hook in to new contact log creation and add string manipulation
372     * (Hooked into `jpcrm_new_contact_log`)
373     */
374    public function new_contact_log_override( $note_long_description, $source_key, $uid ) {
375
376        if ( $source_key == 'mailpoet' ) {
377
378            $note_long_description = __( 'Synchronised from MailPoet', 'zero-bs-crm' ) . '&nbsp;&nbsp;<i class="users icon"></i>';
379
380        }
381
382        return $note_long_description;
383    }
384
385    /**
386     * Register styles & scripts
387     *  (previously on `init`)
388     */
389    public function register_styles_scripts() {
390    }
391
392    /**
393     * Filter settings tabs, adding this extension
394     *  (previously `load_settings_tab`)
395     *
396     * @param array $tabs
397     */
398    public function add_settings_tab( $tabs ) {
399
400        // Append our tab if enabled
401        if ( $this->settings_tab ) {
402
403            $main_tab          = $this->slugs['settings'];
404            $tabs[ $main_tab ] = array(
405                'name'    => $this->ext_name,
406                'ico'     => '',
407                'submenu' => array(),
408            );
409
410        }
411
412        return $tabs;
413    }
414
415    /**
416     * Return default settings
417     */
418    public function default_settings() {
419
420        return require JPCRM_MAILPOET_ROOT_PATH . 'includes/jpcrm-mailpoet-default-settings.php';
421    }
422
423    /**
424     * Main page addition
425     */
426    function add_wp_pages( $menu_array = array() ) {
427
428        // add a submenu item to main CRM menu
429        $menu_array['jpcrm']['subitems']['mailpoet'] = array(
430            'title'      => 'MailPoet Sync',
431            'url'        => $this->slugs['hub'],
432            'perms'      => 'admin_zerobs_manage_options',
433            'order'      => 11,
434            'wpposition' => 11,
435            'callback'   => 'jpcrm_mailpoet_render_hub_page',
436            'stylefuncs' => array( 'zeroBSCRM_global_admin_styles', 'jpcrm_mailpoet_hub_page_styles_scripts' ),
437        );
438
439        return $menu_array;
440    }
441
442    /**
443     * Adds Tools menu sub item
444     */
445    public function add_tools_menu_sub_item_link( $menu_items ) {
446
447        global $zbs;
448
449        $menu_items[] = '<a href="' . zeroBSCRM_getAdminURL( $this->slugs['hub'] ) . '" class="item"><i class="users icon"></i> MailPoet Sync</a>';
450
451        return $menu_items;
452    }
453
454    /**
455     * Output learn menu
456     */
457    public function render_learn_menu() {
458
459        if ( $this->is_hub_page() ) {
460
461            global $zbs;
462
463            $learn_content = '<p>' . __( 'Here you can import your MailPoet data.', 'zerobscrm' ) . '</p>';
464
465            // output
466            $zbs->learn_menu->render_generic_learn_menu(
467                'MailPoet Sync',
468                '',
469                '',
470                true,
471                __( 'Import MailPoet Subscribers', 'zerobscrm' ),
472                $learn_content,
473                $zbs->urls['kb-mailpoet'],
474                false,
475                false,
476                ''
477            );
478
479        }
480    }
481
482    /**
483     * Load the file for a given page
484     *
485     * @param string $page_name (e.g. `settings/main`)
486     */
487    public function load_admin_page( $page_name ) {
488
489        jpcrm_load_admin_page( $page_name, JPCRM_MAILPOET_ROOT_PATH );
490    }
491
492    /**
493     * Append/override MailPoet related info to CRM external source infobox
494     *
495     * @param string $html
496     * @param array  $external_source
497     */
498    public function override_crm_external_source_infobox( $html, $external_source ) {
499
500        global $zbs;
501
502        if ( $external_source['source'] == 'mailpoet' ) {
503
504            // verify subscriber is still in MailPoet before showing a link:
505            $potential_subscriber = $this->get_mailpoet_subscriber_by_subscriber_id( $external_source['unique_id'] );
506
507            if ( $potential_subscriber ) {
508
509                // retrieve origin info (where available)
510                /*
511                Not in v1.0 of this module
512                $origin_str = '';
513                $origin_detail = $zbs->DAL->hydrate_origin( $external_source['origin'] );
514                if ( is_array( $origin_detail ) && isset( $origin_detail['origin_type'] ) && $origin_detail['origin_type'] == 'domain' ){
515
516                    // clean the domain (at this point strip protocols)
517                    $clean_domain = $zbs->DAL->clean_external_source_domain_string( $origin_detail['origin'] );
518                    $origin_str = __( ' from ', 'zero-bs-crm' ) . '<span class="jpcrm-ext-source-domain">' . $clean_domain . '</span>';
519
520                } */
521
522                // adds button to subscriber page
523                // e.g. http://july.local/wp-admin/admin.php?page=mailpoet-subscribers#/stats/1
524                $mailpoet_stats_link = $this->get_mailpoet_sub_stats_link( $external_source['unique_id'] ); // admin_url( 'post.php?post=' . $external_source['unique_id'] . '&action=edit' );
525
526                switch ( $external_source['objtype'] ) {
527
528                    case ZBS_TYPE_CONTACT:
529                        $html = '<div class="jpcrm-ext-source-mailpoet-subscriber">' . sprintf( __( 'Subscriber ID #%s', 'zero-bs-crm' ), $external_source['unique_id'] ) . ' <a class="compact ui mini button right floated" href="' . esc_url( $mailpoet_stats_link ) . '" target="_blank">' . __( 'View Subscriber', 'zero-bs-crm' ) . '</a></div>';
530
531                        break;
532
533                }
534            } else {
535
536                // probably has been deleted in MailPoet
537                // (where user had a delete_action option of `none|add_note`)
538                switch ( $external_source['objtype'] ) {
539
540                    case ZBS_TYPE_CONTACT:
541                        $html = '<div class="jpcrm-ext-source-mailpoet-subscriber">' . sprintf( __( 'Subscriber ID #%s', 'zero-bs-crm' ), $external_source['unique_id'] ) . ' <span class="compact ui mini label right floated">' . __( 'Subscriber not found', 'zero-bs-crm' ) . '</span></div>';
542
543                        break;
544
545                }
546            }
547        }
548
549        return $html;
550    }
551
552    /**
553     * Returns the total number of mailpoet imported contacts present in CRM
554     */
555    public function get_crm_mailpoet_contact_count() {
556
557        global $zbs;
558
559        return (int) $zbs->DAL->contacts->getContacts(
560            array(
561                'externalSource' => 'mailpoet',
562                'count'          => true,
563                'ignoreowner'    => true,
564            )
565        );
566    }
567
568    /**
569     * Returns bool: is the loading page, our hub page
570     *
571     * @return bool hub page
572     */
573    public function is_hub_page() {
574
575        $page = '';
576
577        if ( isset( $_GET['page'] ) ) {
578            $page = sanitize_text_field( $_GET['page'] );
579        }
580
581        if ( $page == $this->slugs['hub'] ) {
582
583            return true;
584
585        }
586
587        return false;
588    }
589
590    /**
591     * Returns bool: true if the page is zbs-add-edit
592     *
593     * @return bool
594     */
595    public function is_add_edit_page() {
596
597        $page = '';
598
599        if ( isset( $_GET['page'] ) ) {
600            $page = sanitize_text_field( $_GET['page'] );
601        }
602
603        // specifically segment add-edit
604        $type = '';
605        if ( isset( $_GET['zbstype'] ) ) {
606            $type = sanitize_text_field( $_GET['zbstype'] );
607        }
608        if ( $type !== 'segment' ) {
609            return false;
610        }
611
612        return $page == $this->slugs['add-edit'];
613    }
614
615    /**
616     * Returns Summarised JPCRM MailPoet stats
617     * Ripe for expansion
618     *
619     * @return
620     */
621    public function get_jpcrm_mailpoet_latest_stats() {
622
623        return array(
624
625            'subscribers_synced' => $this->get_crm_mailpoet_contact_count(),
626
627        );
628    }
629
630    /**
631     * Returns link to MailPoet subscriber
632     *
633     * @param int    $subscriber_id
634     * or
635     * @param string $email
636     *
637     * @return string URL
638     */
639    public function get_mailpoet_sub_stats_link( $subscriber_id = false, $email = false ) {
640
641        global $zbs;
642
643        // sits at /wp-admin/admin.php?page=mailpoet-subscribers#/stats/4
644        $id = false;
645
646        if ( $subscriber_id > 0 ) {
647            $id = $subscriber_id;
648        } elseif ( ! empty( $email ) ) {
649
650            $id = $zbs->DAL->contacts->getContact(
651                -1,
652                array(
653                    'email'       => $email,
654                    'onlyID'      => true,
655                    'ignoreowner' => zeroBSCRM_DAL2_ignoreOwnership( ZBS_TYPE_CONTACT ),
656                )
657            );
658
659        }
660
661        if ( $id ) {
662
663            return '/wp-admin/admin.php?page=mailpoet-subscribers#/stats/' . $id;
664
665        }
666
667        return '#';
668    }
669
670    /**
671     * Returns link to MailPoet list page
672     *
673     * @param int $segment_id
674     *
675     * @return string URL
676     */
677    public function get_mailpoet_list_subs_link( $segment_id ) {
678
679        // sits at /wp-admin/admin.php?page=mailpoet-segments#/edit/3
680
681        return site_url( '/wp-admin/admin.php?page=mailpoet-segments#/edit/' . $segment_id );
682    }
683
684    /**
685     * Returns URL to hop to local Mailpoet wp-admin
686     *
687     * @return string URL
688     */
689    public function get_local_mailpoet_admin_url() {
690
691        return site_url( '/wp-admin/admin.php?page=mailpoet-newsletters' );
692    }
693
694    /**
695     * Returns link to MailPoet subscribers page (filtered by tag)
696     *
697     * @param int $tag_id
698     *
699     * @return string URL
700     */
701    public function get_mailpoet_list_tagged_link( $tag_id ) {
702
703        return site_url( '/wp-admin/admin.php?page=mailpoet-subscribers#/filter[tag=' . $tag_id . ']' );
704    }
705
706    // ===============================================================================
707    // =========== MailPoet DAL ======================================================
708
709    /*
710    * Retrieve MailPoet data on a subscriber (from their MAILPOET subscriber ID)
711    */
712    public function get_mailpoet_subscriber_by_subscriber_id( $subscriber_id, $with_meta = false, $with_tags = false ) {
713
714        // currently there's no direct MailPoet API way of doing this
715        // so here we use the subscriber ID (MP Unique ID) to find the contact via external source search
716        // ... then use their main email.
717        // ... this is flawed, because users can change emails, or use alias emails,
718        // ... but until MailPoet has a `getSubscriberByID()` endpoint...
719        // gh-2565
720
721        global $zbs;
722
723        $potential_contact = $zbs->DAL->contacts->getContact(
724            -1,
725            array(
726
727                'externalSource'    => 'mailpoet',
728                'externalSourceUID' => $subscriber_id,
729
730            )
731        );
732
733        if ( is_array( $potential_contact ) && isset( $potential_contact['email'] ) ) {
734
735            return $this->get_mailpoet_subscriber_by_email( $potential_contact['email'], $with_meta, $with_tags );
736
737        }
738
739        return false;
740    }
741
742    /*
743    * Retrieve MailPoet data on a subscriber
744    */
745    public function get_mailpoet_subscriber_by_email( $email, $with_meta = false, $with_tags = false ) {
746
747        // API+API Method
748        // https://github.com/mailpoet/mailpoet/blob/trunk/doc/api_methods/GetSubscriber.md
749        if ( class_exists( \MailPoet\API\API::class ) ) {
750
751            // load api
752            $mailpoet_api = \MailPoet\API\API::MP( 'v1' );
753
754            // attempt retrieval
755            try {
756
757                // basic retrieval
758                $subscriber = $mailpoet_api->getSubscriber( $email );
759                $lists      = $this->get_mailpoet_lists_summary();
760
761                // hydrate subscriptions
762                if ( is_array( $subscriber['subscriptions'] ) ) {
763
764                    $full_subscriptions_array = array();
765                    foreach ( $subscriber['subscriptions'] as $sub ) {
766
767                        $new_sub = $sub;
768
769                        // find sub list
770                        foreach ( $lists as $list ) {
771
772                            if ( $new_sub['segment_id'] == $list['id'] ) {
773
774                                // add some useful attributes not present in raw subscriptions obj
775                                $new_sub['segment_name']        = $list['name'];
776                                $new_sub['segment_type']        = $list['type'];
777                                $new_sub['segment_description'] = $list['description'];
778
779                            }
780                        }
781
782                        $full_subscriptions_array[] = $new_sub;
783
784                    }
785
786                    $subscriber['subscriptions'] = $full_subscriptions_array;
787                }
788
789                return $subscriber;
790
791            } catch ( \Exception $e ) {
792
793            }
794        }
795
796        /*
797        Bunch of ways could seemingly do this without API, tried the following with some luck,
798        but API method above more reliable in this instance. Would need to further check with
799        MailPoet team if wanted to switch from API for one of these methods...
800
801        // here's adirect call method
802        $subscriber = Subscriber::findOne( $email )->asArray();
803
804        // or by MP id...
805        $SubscribersRepository = ContainerWrapper::getInstance()->get(SubscribersRepository::class);
806        $subscriberEntity = $SubscribersRepository->findOneById( 4 );
807
808        // then see /mailpoet/lib/API/JSON/ResponseBuilders/SubscribersResponseBuilder.php Line 71
809        // ... on how could 'build' $subscriberEntity into a more usable array
810
811        // ... adding tags
812        $subscriber_array['tags'] = array();
813        if ( method_exists( $subscriber, 'getSubscriberTags' ) ){
814
815            $subscriber_array['tags'] = $subscriber->getSubscriberTags();
816
817        }
818
819        return $subscriber_array;
820        */
821    }
822
823    /**
824     * Return an array of mailpoet lists which a contact is in
825     *
826     * @param strng $email
827     */
828    public function get_mailpoet_lists_for_contact_from_email( $email ) {
829
830        $subscriber = $this->get_mailpoet_subscriber_by_email( $email );
831
832        // return just the lists (Segments in MP nomenclature)
833        if ( is_array( $subscriber ) && isset( $subscriber['segments'] ) ) {
834
835            return $subscriber['segments'];
836
837        }
838
839        // ... actually we use `subscriptions` which amounts to the same thing
840        if ( is_array( $subscriber ) && isset( $subscriber['subscriptions'] ) ) {
841
842            return $subscriber['subscriptions'];
843
844        }
845
846        return array();
847    }
848
849    /**
850     * Retrieve MailPoet list summary data by name
851     * (currently needs to retrieve all lists and enumerate)
852     */
853    public function get_mailpoet_list_summary_by_name( $mailpoet_list_name = '' ) {
854
855        $lists = $this->get_mailpoet_lists_summary();
856        if ( is_array( $lists ) ) {
857
858            foreach ( $lists as $list ) {
859
860                if ( $list['name'] == $mailpoet_list_name ) {
861
862                    return $list;
863
864                }
865            }
866        }
867
868        return false;
869    }
870
871    /**
872     * Retrieve MailPoet lists summary data
873     */
874    public function get_mailpoet_lists_summary( $keyed_by_mailpoet_segment_id = false ) {
875
876        // https://github.com/mailpoet/mailpoet/blob/trunk/doc/api_methods/GetLists.md
877        if ( class_exists( \MailPoet\API\API::class ) ) {
878
879            // load api
880            $mailpoet_api = \MailPoet\API\API::MP( 'v1' );
881
882            // attempt retrieval
883            try {
884
885                $list_of_lists = $mailpoet_api->getLists();
886
887                if ( $keyed_by_mailpoet_segment_id ) {
888
889                    $keyed_array_of_lists = array();
890                    foreach ( $list_of_lists as $list ) {
891                        $keyed_array_of_lists[ $list['id'] ] = $list;
892                    }
893
894                    return $keyed_array_of_lists;
895
896                }
897
898                return $list_of_lists;
899
900            } catch ( \Exception $e ) {
901
902            }
903        }
904
905        return false;
906    }
907
908    /*
909    * Retrieve an int count of all MailPoet subscribers
910    */
911    public function get_all_mailpoet_subscribers_count() {
912
913        return $this->get_all_mailpoet_subscribers( false, false, true );
914    }
915
916    /*
917    * Retrieve MailPoet subscribers (all of them)
918    * Wrapper for `get_mailpoet_subscribers()`
919    */
920    public function get_all_mailpoet_subscribers(
921        $limit = 50,
922        $offset = 0,
923        $count = false,
924        $with_meta = false,
925        $with_tags = false
926    ) {
927
928        return $this->get_mailpoet_subscribers(
929            false,
930            false,
931            false,
932            $limit,
933            $offset,
934            $count
935        );
936    }
937
938    /*
939    * Retrieve MailPoet subscribers
940    *
941    * @param int $limit
942    * @param int $offset
943    * @param int $count
944    * @param bool $with_meta
945    * @param bool $with_tags (Tag support is very new in MailPoet, TBC)
946    */
947    public function get_mailpoet_subscribers(
948        $status = false,
949        $list_id = false,
950        $min_updated_at = false,
951        $limit = 50,
952        $offset = 0,
953        $count = false,
954        $with_meta = false,
955        $with_tags = false
956    ) {
957
958        // https://github.com/mailpoet/mailpoet/blob/trunk/doc/api_methods/GetSubscribers.md
959        // https://github.com/mailpoet/mailpoet/blob/trunk/doc/api_methods/GetSubscribersCount.md
960        if ( class_exists( \MailPoet\API\API::class ) ) {
961
962            // load api
963            $mailpoet_api = \MailPoet\API\API::MP( 'v1' );
964
965            // attempt retrieval
966            try {
967
968                // build params
969                $filters = array();
970                if ( ! empty( $status ) ) {
971                    $filters['status'] = $status;
972                }
973                if ( $list_id > 0 ) {
974                    $filters['listId'] = $list_id;
975                }
976                if ( $min_updated_at > 0 ) {
977                    $filters['minUpdatedAt'] = $min_updated_at;
978                }
979
980                // if count, return that
981                if ( $count ) {
982
983                    return $mailpoet_api->getSubscribersCount(
984                        $filters
985                    );
986
987                }
988
989                // as at 19/10/22 the MailPoet API endpoint does not yet support returning segments/tags
990                // $with_meta, $with_tags
991
992                // return
993                return $mailpoet_api->getSubscribers(
994                    $filters,
995                    $limit,
996                    $offset
997                );
998
999            } catch ( \Exception $e ) {
1000
1001                // debug: echo 'MailPoet API Error: ' . $e->getMessage();
1002
1003            }
1004        }
1005
1006        return false;
1007    }
1008
1009    /*
1010    * Helper function to filter MailPoet lists by name.
1011    */
1012    public function get_mailpoet_list_by_name( $name ) {
1013        $mailpoet_api = \MailPoet\API\API::MP( 'v1' );
1014        $lists        = $mailpoet_api->getLists();
1015        $found        = array_filter(
1016            $lists,
1017            function ( $i ) use ( $name ) {
1018                return ( $i['name'] == $name );
1019            }
1020        );
1021
1022        return array_pop( $found );
1023    }
1024
1025    /*
1026    * Creates a mew MailPoet mailing List.
1027    * Deletes a previous one if exists with the same name.
1028    */
1029    public function reset_mailpoet_list_by_segment_name( $name ) {
1030
1031        $mailpoet_api = \MailPoet\API\API::MP( 'v1' );
1032
1033        $list = $this->get_mailpoet_list_by_name( $name );
1034
1035        // Delete all subscribers from this list
1036        if ( ! empty( $list ) && ! empty( $list['id'] ) ) {
1037            $mailpoet_api->deleteList( $list['id'] );
1038        }
1039
1040        // Create list again with same name
1041
1042        $description = __( 'Created by CRM', 'zero-bs-crm' );
1043        ##WLREMOVE
1044        $description = __( 'Created by Jetpack CRM', 'zero-bs-crm' );
1045        ##/WLREMOVE
1046
1047        $list = $mailpoet_api->addList(
1048            array(
1049                'name'        => $name,
1050                'description' => $description,
1051            )
1052        );
1053
1054        return $list['id'];
1055    }
1056
1057    /*
1058    * Retrieve MailPoet setup status
1059    */
1060    public function get_mailpoet_setup_status() {
1061
1062        // https://github.com/mailpoet/mailpoet/blob/trunk/doc/api_methods/IsSetupComplete.md
1063        if ( class_exists( \MailPoet\API\API::class ) ) {
1064
1065            // load api
1066            $mailpoet_api = \MailPoet\API\API::MP( 'v1' );
1067
1068            // attempt retrieval
1069            try {
1070
1071                return $mailpoet_api->isSetupComplete();
1072
1073            } catch ( \Exception $e ) {
1074
1075            }
1076        }
1077
1078        return false;
1079    }
1080
1081    // =========== / MailPoet Specific Migrations  ===================================
1082    // ===============================================================================
1083
1084    // ===============================================================================
1085    // =========== MailPoet Specific Migrations  =====================================
1086
1087    /*
1088    * Migrations
1089    */
1090    private function run_migrations() {
1091    }
1092
1093    // =========== / MailPoet Specific Migrations  ===================================
1094    // ===============================================================================
1095
1096    /**
1097     * Iterates through list of contacts to either `subscribeToList` or `addSubscriber` on MailPoet
1098     */
1099    public function contacts_to_subscribers( $list_id, $subscribers ) {
1100
1101        try {
1102
1103            $mailpoet_api = \MailPoet\API\API::MP( 'v1' );
1104
1105            foreach ( $subscribers as $sc ) {
1106
1107                if ( ! zeroBSCRM_validateEmail( $sc['email'] ) ) {
1108                    continue;
1109                }
1110
1111                try {
1112
1113                    $subscriber = $mailpoet_api->getSubscriber( $sc['email'] );
1114
1115                    // Found an existing subscriber
1116                    if ( ! empty( $subscriber['id'] ) ) {
1117                        $mailpoet_api->subscribeToList(
1118                            $subscriber['id'],
1119                            $list_id,
1120                            array(
1121                                'send_confirmation_email' => false,
1122                                'schedule_welcome_email'  => false,
1123                                'skip_subscriber_notification' => true,
1124                            )
1125                        );
1126                    }
1127                } catch ( APIException $th ) {
1128
1129                    // Subscriber not found. Create new Subscriber
1130                    if ( $th->getCode() === APIException::SUBSCRIBER_NOT_EXISTS ) {
1131                        try {
1132                            $mailpoet_api->addSubscriber(
1133                                array(
1134                                    'email'      => $sc['email'],
1135                                    'first_name' => $sc['fname'],
1136                                    'last_name'  => $sc['lname'],
1137                                ),
1138                                array( $list_id ),
1139                                array(
1140                                    'send_confirmation_email' => false,
1141                                    'schedule_welcome_email' => false,
1142                                    'skip_subscriber_notification' => true,
1143                                )
1144                            );
1145                        } catch ( APIException $th ) {
1146                            return null;
1147                        }
1148                    }
1149                }
1150            }
1151
1152            return array(
1153                'success' => true,
1154            );
1155
1156        } catch ( \Throwable $th ) {
1157
1158            return array(
1159                'success' => false,
1160                'error'   => zeroBSCRM_locale_utsToDatetime( time() ) . '] ' . $th->getMessage(),
1161            );
1162
1163        }
1164    }
1165
1166    /*
1167     *   Adds segment condition category positions (to effect the display order)
1168     */
1169    public function add_segments_condition_category_positions( $positions = array() ) {
1170
1171        global $zbs;
1172
1173        $positions['mailpoet'] = 11;
1174
1175        return $positions;
1176    }
1177}