Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 323
0.00% covered (danger)
0.00%
0 / 19
CRAP
0.00% covered (danger)
0.00%
0 / 1
zeroBSCRM_add_meta_box
0.00% covered (danger)
0.00%
0 / 61
0.00% covered (danger)
0.00%
0 / 1
506
zeroBSCRM_do_meta_boxes
0.00% covered (danger)
0.00%
0 / 43
0.00% covered (danger)
0.00%
0 / 1
506
zeroBSCRM_applyScreenOptions
0.00% covered (danger)
0.00%
0 / 48
0.00% covered (danger)
0.00%
0 / 1
930
zeroBSCRM_do_meta_box_htmlTabHead
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
zeroBSCRM_do_meta_box_html
0.00% covered (danger)
0.00%
0 / 53
0.00% covered (danger)
0.00%
0 / 1
552
zeroBSCRM_getCurrentMetaboxesFlatArr
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
56
zeroBSCRM_getCurrentMetaboxes
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
zeroBSCRM_getMetaboxes
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
zeroBS__Metabox
0.00% covered (danger)
0.00%
0 / 84
0.00% covered (danger)
0.00%
0 / 11
1640
0.00% covered (danger)
0.00%
0 / 1
 initMetabox
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
42
 create_meta_box
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 1
30
 html
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 save_data
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 post_save_data
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 print_meta_box
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 save_meta_box
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
156
 post_save_meta_box
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 dalNoticeMessage
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 dalErrorMessage
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 dalOutputMessage
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 1
90
1<?php
2/*
3 * Jetpack CRM
4 * https://jetpackcrm.com
5 * V2.52+
6 *
7 * Copyright 2020 Automattic
8 *
9 * Date: 27/02/18
10 */
11
12defined( 'ZEROBSCRM_PATH' ) || exit( 0 );
13
14/*
15======================================================
16    Parent Class for all Metabox
17    ====================================================== */
18
19class zeroBS__Metabox {
20
21    static $instance;
22
23    public $typeInt = false; // this was added late in DAL2 migration (28/04/18) but we should migrate to this over the other $postType etc. (started with Metaboxes.TagManger.php)
24
25    // v3.0 this was renamed objType public $postType = false; // set in child class 'zerobs_customer' // ONLY USED IN save funcs etc. maybe, potentially just legacy now.
26    public $objType      = false; // set in child class 'contact' // ONLY USED IN save funcs etc. maybe, potentially just legacy now.
27    public $metaboxID    = false; // set in child class 'zerobs-customer-edit';
28    public $metaboxTitle = ''; // set in child class __('Customer Details',"zero-bs-crm")
29    public $metaboxIcon  = ''; // Semantic ui icon e.g. "heartbeat" = <i class="heartbeat icon"></i> - since 2.98.7
30
31    // default positions
32    public $metaboxScreen   = false; // set in child class zerobs_contact
33    public $metaboxScreens  = false; // added v3.0 - if set and is array, this'll override metaboxScreen in terms of WHERE it's shown - allowing multiple exposed points from one metabox (e.g. contacts + co's) - set in child class zerobs_contact
34    public $metaboxArea     = false; // set in child class normal
35    public $metaboxLocation = false; // set in child class high
36
37    // style choices
38    public $headless       = false; // hides header?
39    public $metaboxClasses = ''; // extra classes to add to the wrappers
40
41    // save options
42    public $saveOrder      = 10; // priority for this metabox to save. MAIN RECORDS use 1 so that later metaboxes can gain ID from newly inserted via global
43    public $updateMessages = array();
44
45    // hide/show
46    public $live = true; // switching this to false before initMetabox will STOP it loading (allows for 'checks')
47
48    // screen options - defaults - note these are also used in add_meta_box for empties
49    public $capabilities = array(
50
51        'can_hide'        => true, // can be hidden
52        'areas'           => array( 'normal', 'side' ), // areas can be dragged to - normal side = only areas currently
53        'can_accept_tabs' => false,  // can/can't accept tabs onto it
54        'can_become_tab'  => false, // can be added as tab
55        'can_minimise'    => true, // can be minimised
56        'can_move'        => true, // can be rearranged
57        'hide_on_new'     => false, // if this is true, on "new" edit's this'll hide (e.g. logs no point till added)
58
59    );
60
61    // public function __construct( $plugin_file ) {
62    public function initMetabox() {
63
64        // catch old screen's and translate (in few rare cases)
65        if ( $this->metaboxScreen == 'zerobs_edit_contact' ) {
66            $this->metaboxScreen = 'zbs-add-edit-contact-edit';
67        }
68
69        if ( $this->live ) {
70
71            // lazy hackaround for now, can be more classy later.
72            if ( ! empty( $this->metaboxIcon ) ) {
73                $this->metaboxTitle = '<i class="' . $this->metaboxIcon . ' icon"></i> ' . $this->metaboxTitle;
74            }
75
76            // self::$instance = $this;
77
78            // Create on init, for zbs metaboxes, rather than: add_action( 'add_meta_boxes', array( $this, 'create_meta_box' ) );
79            $this->create_meta_box();
80
81            // add save func, even if will be blank :)
82            // WP was using filters, I'll move to actions (makes more sense to me, having read actions vs filters)
83            // add_filter( 'zerobs_save_'.$this->postType, array( $this, 'save_meta_box' ), 10, 2 );
84            if ( $this->objType ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
85                add_action( 'zerobs_save_' . $this->objType, array( $this, 'save_meta_box' ), $this->saveOrder, 2 );
86            }
87            // This is fired by edit page do_action
88
89            // this is then set to fire after ALL other save funcs :)
90            // ... by way of a 999 priority
91            if ( $this->objType ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
92                add_action( 'zerobs_save_' . $this->objType, array( $this, 'post_save_meta_box' ), 999, 2 );
93            }
94        }
95    }
96
97    public function create_meta_box() {
98
99        if ( isset( $this->metaboxScreens ) && is_array( $this->metaboxScreens ) ) {
100
101            foreach ( $this->metaboxScreens as $metaboxScreen ) {
102
103                // shown in multiple screens, so add for each
104
105                if ( ! empty( $metaboxScreen ) ) {
106
107                    zeroBSCRM_add_meta_box(
108                        $this->metaboxID,
109                        $this->metaboxTitle,
110                        array( $this, 'print_meta_box' ),
111                        $metaboxScreen, // $this->metaboxScreen,
112                        $this->metaboxArea,
113                        $this->metaboxLocation,
114                        array(),
115                        $this->headless,
116                        $this->metaboxClasses,
117                        $this->capabilities
118                    );
119
120                }
121            }
122        } else {
123
124            // normal, only shown in one place
125            zeroBSCRM_add_meta_box(
126                $this->metaboxID,
127                $this->metaboxTitle,
128                array( $this, 'print_meta_box' ),
129                $this->metaboxScreen,
130                $this->metaboxArea,
131                $this->metaboxLocation,
132                array(),
133                $this->headless,
134                $this->metaboxClasses,
135                $this->capabilities
136            );
137
138        }
139    }
140
141    // use this to output data (post nonce :)
142    public function html( $obj, $metabox ) {
143
144        // child class will print box here
145    }
146
147    /*
148        Not actually used yet... wh used $zbs->pageMessages for brevity, but will work (though will output IN a metabox)
149    public function html_msgs($obj,$metabox){
150
151        // This outputs any html msgs directly (probably called by the edit class above)
152        if (is_array($this->updateMessages) && count($this->updateMessages) > 0) foreach ($this->updateMessages as $m) echo $m;
153    }*/
154
155    // use this to save data in child classes (nonce-checked :)
156    public function save_data( $objID, $obj ) {
157
158        // save data
159    }
160
161    // use this for post-save data in child classes (nonce-checked :)
162    public function post_save_data( $objID, $obj ) {
163
164        // do any actions needed after all metaboxes have saved data
165    }
166
167    public function print_meta_box( $obj, $metabox ) {
168
169                // nonce output by parent class
170        if ( $this->metaboxID ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
171            wp_nonce_field( 'save_' . $this->metaboxID, $this->metaboxID . '_nonce' );
172        }
173
174        $this->html( $obj, $metabox );
175    }
176
177    // Parent class only, let children class simply use func 'save_data'
178    public function save_meta_box( $objID, $obj ) {
179
180        // metabox set?
181        if ( ! $this->metaboxID || ! isset( $_POST[ $this->metaboxID . '_nonce' ] ) || empty( $_POST[ $this->metaboxID . '_nonce' ] ) ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
182            return;
183        }
184        // autosave? (legacy, probswill never do as our own page now)
185        if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
186            return; }
187        // final nonce check
188        if ( isset( $_POST[ $this->metaboxID . '_nonce' ] ) && wp_verify_nonce( $_POST[ $this->metaboxID . '_nonce' ], 'save_' . $this->metaboxID ) ) {
189
190            // here we check if another metabox (e.g. main save record) has fired first + inserted, leaving us an id:)
191            global $zbsJustInsertedMetaboxID;
192            if ( $objID == -1 && isset( $zbsJustInsertedMetaboxID ) && ! empty( $zbsJustInsertedMetaboxID ) && $zbsJustInsertedMetaboxID > 0 ) {
193                $objID = $zbsJustInsertedMetaboxID;
194            }
195
196            // Good to save, this has basically 'checked' the metabox nonce for us, so avoids child-classes having to :)
197            $this->save_data( $objID, $obj );
198
199        }
200
201        return $obj;
202    }
203
204    // Parent class only, let children class simply use func 'post_save_data'
205    public function post_save_meta_box( $objID, $obj ) {
206
207        // simply fires this
208        $this->post_save_data( $objID, $obj );
209
210        return $obj;
211    }
212
213    // outputs zbs dal notice stack:
214    public function dalNoticeMessage( $messages = array() ) {
215
216        $this->dalOutputMessage( $messages, 'notice', __( 'General Notice', 'zero-bs-crm' ) );
217    }
218
219    // outputs zbs dal error stack:
220    public function dalErrorMessage( $messages = array() ) {
221
222        $this->dalOutputMessage( $messages, 'error', __( 'General Error', 'zero-bs-crm' ) );
223    }
224
225    // outputs html for a message stack
226    public function dalOutputMessage( $messages = array(), $type = 'error', $fallback = '' ) {
227
228        global $zbs;
229
230        // type switch
231        switch ( $type ) {
232
233            case 'notice':
234                $classes = 'info green mini';
235                if ( empty( $fallback ) ) {
236                    $fallback = __( 'General Notice', 'zero-bs-crm' );
237                }
238                $msgHeader = __( 'Update Notice:', 'zero-bs-crm' );
239                $ico       = 'sticky note outline';
240                $id        = 'generalNotice';
241                break;
242
243            // errs (Default)
244            default:
245                $classes = 'info orange mini';
246                if ( empty( $fallback ) ) {
247                    $fallback = __( 'General Error', 'zero-bs-crm' );
248                }
249                $msgHeader = $zbs->DAL->typeStr( $zbs->DAL->objTypeKey( $this->objType ) ) . ' ' . __( 'Could not be saved', 'zero-bs-crm' );
250                $ico       = 'red frown outline';
251                $id        = 'couldNotUpdate';
252                break;
253
254        }
255
256        $msgDetail = '';
257        if ( is_array( $messages ) ) {
258            foreach ( $messages as $message ) {
259
260                // < v3.0
261                $messageStr = $message;
262
263                // v3.0+ $message becomes a more thoughtful array
264                if ( is_array( $message ) ) {
265
266                    // set this to stop php warnings if not got 'str' attr
267                    $messageStr = $fallback;
268
269                    // retrieve
270                    if ( isset( $message['str'] ) ) {
271                        $messageStr = $message['str'];
272                    }
273                }
274
275                $msgDetail .= $messageStr . '<br />';
276            }
277        }
278
279        // genericified from DAL3.0
280        $msg = zeroBSCRM_UI2_messageHTML( $classes, $msgHeader, $msgDetail, $ico, $id );
281
282        $zbs->pageMessages[] = $msg;
283    }
284}
285
286/*
287======================================================
288    / Parent Class for all Metabox
289    ====================================================== */
290
291/*
292======================================================
293    Global metabox helper funcs
294    ====================================================== */
295
296// ZBS splintered version of add_meta_box (wholly our own from now on, do not assume can be swapped out directly, may need integration)
297// https://developer.wordpress.org/reference/functions/add_meta_box/
298function zeroBSCRM_add_meta_box( $id, $title, $callback, $screen = null, $context = 'advanced', $priority = 'default', $callback_args = null, $headless = false, $extraClasses = '', $capabilities = false ) {
299
300    // $id, $title, $callback,$headless
301    // echo 'zeroBSCRM_add_meta_box '.$id.':<pre>'; print_r(array('head'=>$headless)); echo '</pre>';
302
303    global $zbs;
304
305    // simply maintains this array $zbs->metaboxes
306    // $zbs->metaboxes[]
307
308    if ( empty( $screen ) ) {
309        $screen = get_current_screen();
310    } elseif ( is_string( $screen ) ) {
311        // leave as is for zbs :)
312        $screen = convert_to_screen( $screen );
313    } elseif ( is_array( $screen ) ) {
314        foreach ( $screen as $single_screen ) {
315            zeroBSCRM_add_meta_box( $id, $title, $callback, $single_screen, $context, $priority, $callback_args, $headless, $extraClasses, $capabilities );
316        }
317    }
318
319    if ( ! isset( $screen->id ) ) {
320        return;
321    }
322
323    $page = $screen->id;
324
325    if ( ! isset( $zbs->metaboxes ) ) {
326        $zbs->metaboxes = array();
327    }
328    if ( ! isset( $zbs->metaboxes[ $page ] ) ) {
329        $zbs->metaboxes[ $page ] = array();
330    }
331    if ( ! isset( $zbs->metaboxes[ $page ][ $context ] ) ) {
332        $zbs->metaboxes[ $page ][ $context ] = array();
333    }
334
335    foreach ( array_keys( $zbs->metaboxes[ $page ] ) as $a_context ) {
336        foreach ( array( 'high', 'core', 'default', 'low' ) as $a_priority ) {
337            if ( ! isset( $zbs->metaboxes[ $page ][ $a_context ][ $a_priority ][ $id ] ) ) {
338                continue;
339            }
340
341            // If a core box was previously added or removed by a plugin, don't add.
342            if ( 'core' == $priority ) {
343                // If core box previously deleted, don't add
344                if ( false === $zbs->metaboxes[ $page ][ $a_context ][ $a_priority ][ $id ] ) {
345                    return;
346                }
347
348                /*
349                 * If box was added with default priority, give it core priority to
350                 * maintain sort order.
351                 */
352                if ( 'default' == $a_priority ) {
353                    $zbs->metaboxes[ $page ][ $a_context ]['core'][ $id ] = $zbs->metaboxes[ $page ][ $a_context ]['default'][ $id ];
354                    unset( $zbs->metaboxes[ $page ][ $a_context ]['default'][ $id ] );
355                }
356                return;
357            }
358            // If no priority given and id already present, use existing priority.
359            if ( empty( $priority ) ) {
360                $priority = $a_priority;
361                /*
362                * Else, if we're adding to the sorted priority, we don't know the title
363                * or callback. Grab them from the previously added context/priority.
364                */
365            } elseif ( 'sorted' == $priority ) {
366                $title         = $zbs->metaboxes[ $page ][ $a_context ][ $a_priority ][ $id ]['title'];
367                $callback      = $zbs->metaboxes[ $page ][ $a_context ][ $a_priority ][ $id ]['callback'];
368                $callback_args = $zbs->metaboxes[ $page ][ $a_context ][ $a_priority ][ $id ]['args'];
369                $headless      = $zbs->metaboxes[ $page ][ $a_context ][ $a_priority ][ $id ]['headless'];
370                $extraClasses  = $zbs->metaboxes[ $page ][ $a_context ][ $a_priority ][ $id ]['extraclasses'];
371                $capabilities  = $zbs->metaboxes[ $page ][ $a_context ][ $a_priority ][ $id ]['capabilities'];
372            }
373            // An id can be in only one priority and one context.
374            if ( $priority != $a_priority || $context != $a_context ) {
375                unset( $zbs->metaboxes[ $page ][ $a_context ][ $a_priority ][ $id ] );
376            }
377        }
378    }
379
380    if ( empty( $priority ) ) {
381        $priority = 'low';
382    }
383
384    // default caps
385    if ( ! is_array( $capabilities ) ) {
386        $capabilities = array(
387
388            'can_hide'        => false, // can be hidden
389            'areas'           => array( 'normal', 'side' ), // areas can be dragged to - normal side = only areas currently
390            'can_accept_tabs' => false,  // can/can't accept tabs onto it
391            'can_become_tab'  => false, // can be added as tab
392            'can_minimise'    => true, // can be minimised
393            'can_move'        => true, // can be moved
394
395        );
396    }
397
398    if ( ! isset( $zbs->metaboxes[ $page ][ $context ][ $priority ] ) ) {
399        $zbs->metaboxes[ $page ][ $context ][ $priority ] = array();
400    }
401
402    $zbs->metaboxes[ $page ][ $context ][ $priority ][ $id ] = array(
403        'id'           => $id,
404        'title'        => $title,
405        'callback'     => $callback,
406        'args'         => $callback_args,
407        'headless'     => $headless,
408        'extraclasses' => $extraClasses,
409        'capabilities' => $capabilities,
410    );
411}
412/**
413 * Meta-Box template function (ZBS MODIFIED VER)
414 *
415 * @global array $zbs->metaboxes
416 *
417 * @staticvar bool $already_sorted
418 *
419 * @param string $screen
420 * @param string $context box context
421 * @param mixed  $object  gets passed to the box callback function as first parameter
422 * @return int number of meta_boxes
423 */
424function zeroBSCRM_do_meta_boxes( $screen, $context, $object ) {
425        global $zbs;
426        static $already_sorted = false;
427
428    if ( empty( $screen ) ) {
429            $screen = get_current_screen();
430    } elseif ( is_string( $screen ) ) {
431            $screen = convert_to_screen( $screen );
432    }
433        $page = $screen->id;
434
435        // is this page a new edit:
436        global $zbsPage;
437        $isNewEdit = false;
438    if ( isset( $zbsPage['new_edit'] ) ) {
439        $isNewEdit = $zbsPage['new_edit']; // echo 'pre:<pre>'.print_r($zbsPage,1).'</pre>';
440    }
441
442        printf( '<div id="zbs-%s-sortables" class="zbs-metaboxes zbs-metabox-sortables">', esc_html( $context ) );
443        /*
444        This was the previous wp organisation, which nearly worked for us, except we now want tab groups :)
445        $hidden = get_hidden_meta_boxes( $screen );
446        // Grab the ones the user has manually sorted. Pull them out of their previous context/priority and into the one the user chose
447        if ( ! $already_sorted && $sorted = get_user_option( "meta-box-order_$page" ) ) {
448                foreach ( $sorted as $box_context => $ids ) {
449                        foreach ( explode( ',', $ids ) as $id ) {
450                                if ( $id && 'dashboard_browser_nag' !== $id ) {
451                                        zeroBSCRM_add_meta_box( $id, null, null, $screen, $box_context, 'sorted' );
452                                }
453                        }
454                }
455        }
456        */
457
458        // echo 'Screen: '.$zbs->pageKey.' # '.$context;
459        // use ours
460                $screenOpts = $zbs->global_screen_options(); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
461        $hidden             = array();
462    if ( is_array( $screenOpts ) && isset( $screenOpts['mb_hidden'] ) && is_array( $screenOpts['mb_hidden'] ) ) {
463        $hidden = $screenOpts['mb_hidden'];
464    }
465        $minimised = array();
466    if ( is_array( $screenOpts ) && isset( $screenOpts['mb_mini'] ) && is_array( $screenOpts['mb_mini'] ) ) {
467        $minimised = $screenOpts['mb_mini'];
468    }
469
470        // Basically here
471        // $zbs->metaboxes[ $page ][ $context ] = DEFAULTs
472        // $screenOptionsList = USER DESIRED ORDER
473        zeroBSCRM_applyScreenOptions( $screenOpts, $page, $context );
474
475        $already_sorted = true;
476        $i              = 0;
477    if ( isset( $zbs->metaboxes[ $page ][ $context ] ) ) {
478        foreach ( array( 'high', 'sorted', 'core', 'default', 'low' ) as $priority ) {
479            if ( isset( $zbs->metaboxes[ $page ][ $context ][ $priority ] ) ) {
480                foreach ( (array) $zbs->metaboxes[ $page ][ $context ][ $priority ] as $boxID => $box ) {
481                    if ( false == $box ) { // || ! $box['title']
482                        continue;
483                    }
484
485                        // hide_on_new (if this page is edit->new, and this metabox has hide_on_new - just don't load it.)
486                    if ( $isNewEdit && isset( $box['capabilities'] ) && isset( $box['capabilities']['hide_on_new'] ) && $box['capabilities']['hide_on_new'] ) {
487                        continue;
488                    }
489
490                        ++$i;
491
492                        // if tab group
493                        // https://semantic-ui.com/modules/tab.html#/examples
494                    if ( isset( $box['istagroup'] ) ) {
495
496                            // this puts out header
497                            zeroBSCRM_do_meta_box_htmlTabHead( $boxID, $box );
498
499                            // this puts out each as body to header
500                            $indx = 0;
501                        foreach ( $box['boxes'] as $subbox ) {
502
503                            // active tab
504                            $isActive = false;
505                            if ( $indx == 0 ) {
506                                $isActive = true;
507                            }
508
509                            // final html - simple normal out
510                            zeroBSCRM_do_meta_box_html( $subbox, $page, $hidden, $object, $minimised, true, $isActive );
511
512                            ++$indx;
513
514                        }
515                    } else {
516
517                                    // final html - simple normal out
518                                    zeroBSCRM_do_meta_box_html( $box, $page, $hidden, $object, $minimised );
519
520                    }
521                }
522            }
523        }
524    }
525        echo '</div>';
526        return $i;
527}
528
529function zeroBSCRM_applyScreenOptions( $screenOpts = false, $page = '', $context = '' ) {
530
531    global $zbs;
532
533    if ( ! is_array( $screenOpts ) ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
534        $screenOpts = $zbs->global_screen_options(); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
535    }
536    $screenOptionsList = array();
537    if ( is_array( $screenOpts ) && isset( $screenOpts[ 'mb_' . $context ] ) && is_array( $screenOpts[ 'mb_' . $context ] ) ) {
538        $screenOptionsList = $screenOpts[ 'mb_' . $context ];
539    }
540    // not needed here: $hidden = array(); if (is_array($screenOpts) && isset($screenOpts['mb_hidden']) && is_array($screenOpts['mb_hidden'])) $hidden = $screenOpts['mb_hidden'];
541
542    // Basically here
543    // $zbs->metaboxes[ $page ][ $context ] = DEFAULTs
544    // $screenOptionsList = USER DESIRED ORDER
545
546    // if default + screenoptions order:
547    if ( isset( $zbs->metaboxes[ $page ][ $context ] ) && isset( $screenOptionsList ) && is_array( $screenOptionsList ) && count( $screenOptionsList ) > 0 ) {
548
549        // DEBUG echo '<h2>Global</h2><pre>'; print_r($zbs->metaboxes[ $page ][ $context ]); echo '</pre>';
550        // DEBUG echo '<h2>User</h2><pre>'; print_r($screenOptionsList); echo '</pre>';
551
552        // so here we merge the two into a MEGA list :)
553        $metaboxes   = $zbs->metaboxes[ $page ][ $context ]; // array('high'=>array(), 'sorted'=>array(), 'core'=>array(), 'default'=>array(), 'low'=>array());
554        $soMetaboxes = array();
555
556        // for the sake of our metaboxes + simplicity
557        // for now we'll only use to priority slots 'high','low'
558        // ... leaving the others in place.
559        // So:
560        // - No screen options = load as normal, based on wp priorities (as added in metabox init)
561        // - Screen options = load these in order user has saved (all under 'high')
562        // ... when there is a new metabox added by us (so not in 'screenoptions') this is added either 'high' or 'low'
563        // ... always at the end of whatever priority it is given in its metabox init :)
564
565        foreach ( $screenOptionsList as $metaboxID => $metaboxVal ) {
566
567            // these'll be in priority order
568            // ... so we PICK them out of wherever they are, and add them to wherever they need to be
569            // but because we just store the ID in the screenopts we have to do a bit of a dance..
570
571            // NOTE this also has to catch TABS which are csv's of mb id's
572            // here we check if tab
573            $tabgroup = false;
574            if ( $metaboxVal != 'self' ) {
575
576                // is tab section
577                $mbIDsToAdd = explode( ',', $metaboxVal );
578                $tabgroup   = true;
579
580            } else {
581
582                // only one
583                $mbIDsToAdd = array( $metaboxID );
584
585            }
586
587            // then we cycle through each (provided) id
588            foreach ( $mbIDsToAdd as $mbIDToAdd ) {
589
590                    // first we find it in existing arrs
591                    $existingMB   = false;
592                    $fromPriority = false;
593
594                        // through each prioty
595                foreach ( $metaboxes as $priority => $metaboxArr ) {
596
597                    // log source
598                    $fromPriority = $priority;
599
600                    // through each mb
601                    foreach ( $metaboxArr as $mbID => $mb ) {
602
603                                // found it
604                        if ( $mbIDToAdd == $mbID ) {
605
606                            // grab item
607                            $existingMB = $mb;
608                            break 2;
609
610                        }
611                    }
612                }
613
614                        // if found it
615                if ( $existingMB !== false ) {
616
617                    // remove it from current place
618                    unset( $metaboxes[ $fromPriority ][ $mbIDToAdd ] );
619
620                    if ( $tabgroup ) {
621
622                        // set if not set
623                        if ( ! isset( $soMetaboxes[ $metaboxID ] ) ) {
624                            $soMetaboxes[ $metaboxID ] = array();
625                        }
626                        if ( ! isset( $soMetaboxes[ $metaboxID ]['boxes'] ) ) {
627                            $soMetaboxes[ $metaboxID ]['boxes'] = array();
628                        }
629
630                        // add to our pile (of piles)
631                        $soMetaboxes[ $metaboxID ]['boxes'][ $mbIDToAdd ] = $existingMB;
632
633                        // make sure this flag is set
634                        $soMetaboxes[ $metaboxID ]['istagroup'] = true;
635
636                    } else {
637
638                        // add to our pile
639                        $soMetaboxes[ $metaboxID ] = $existingMB;
640
641                    }
642                }
643            } // each mb id (1 or tabs multi)
644
645        }
646
647        // now if we have any in our pile, we shoehorn them in at the top of high (for now)
648        // so... when we add a new metabox (That people wont have saved in their screen opts)
649        // ... it'll do this:
650        // if priority = high - add to TOP
651        // if priority != high - add at bottom
652        if ( count( $soMetaboxes ) > 0 ) {
653
654            $newHighPriority = array();
655
656            // first clone in any in from other high priority array
657            // (this'll catch where we've added new metaboxes that the user doesn't have in their screen opts)
658            if ( isset( $metaboxes['high'] ) && is_array( $metaboxes['high'] ) && count( $metaboxes['high'] ) > 0 ) {
659                foreach ( $metaboxes['high'] as $mbID => $mb ) {
660                    if ( ! in_array( $mbID, array_keys( $soMetaboxes ) ) ) {
661                        $newHighPriority[ $mbID ] = $mb;
662                    }
663                }
664            }
665
666            // add ours :)
667            $newHighPriority = array_merge( $newHighPriority, $soMetaboxes );
668
669            // add any in from other priority arrays
670            // (this'll catch where we've added new metaboxes that the user doesn't have in their screen opts)
671            foreach ( array( 'sorted', 'core', 'default', 'low' ) as $priority ) {
672                if ( isset( $metaboxes[ $priority ] ) && count( $metaboxes[ $priority ] ) > 0 ) {
673                    foreach ( $metaboxes[ $priority ] as $mbID => $mb ) {
674                        if ( ! in_array( $mbID, array_keys( $soMetaboxes ) ) ) {
675                            $newHighPriority[ $mbID ] = $mb;
676                        }
677                    }
678                }
679            }
680
681            // then overwrite it! (effectively for )
682            // actually needs to be totally overwritten $metaboxes['high'] = $newHighPriority;
683            $zbs->metaboxes[ $page ][ $context ] = array( 'high' => $newHighPriority );
684
685            // and clean up
686            unset( $newHighPriority, $soMetaboxes, $metaboxes );
687
688        }
689    }
690}
691
692// https://semantic-ui.com/modules/tab.html#/examples
693function zeroBSCRM_do_meta_box_htmlTabHead( $tabsID = '', $tabs = false ) {
694
695    if ( $tabs !== false ) {
696
697        // echo '<h1>'.$tabsID.'</h1><pre>';print_r($tabs); echo '</pre>';
698
699        echo '<div class="ui top attached tabular menu zbs-metabox-tabgroup" id="zbs-metaboxtabs-' . esc_attr( $tabsID ) . '" data-tabid="' . esc_attr( $tabsID ) . '">';
700
701            $indx = 0;
702
703            // this puts out each as body to header
704        foreach ( $tabs['boxes'] as $subbox ) {
705
706            echo '<a class="item';
707            if ( $indx == 0 ) {
708                echo ' active';
709            }
710            echo '" data-tab="' . esc_attr( $subbox['id'] ) . '">' . esc_html( $subbox['title'] ) . '</a>';
711            ++$indx;
712
713        }
714
715        echo '</div>';
716
717    }
718}
719
720function zeroBSCRM_do_meta_box_html( $box, $page, $hidden, $object, $minimised, $isTabPane = false, $isActiveTab = false ) {
721
722        /*
723        HTML adapted to suit semantic, from this, the previous wp code:
724        $hidden_class = in_array($box['id'], $hidden) ? ' hide-if-js' : '';
725        echo '<div id="' . $box['id'] . '" class="postbox ' . postbox_classes($box['id'], $page) . $hidden_class . '" ' . '>' . "\n";
726        if ( 'dashboard_browser_nag' != $box['id'] ) {
727                $widget_title = $box[ 'title' ];
728                if ( is_array( $box[ 'args' ] ) && isset( $box[ 'args' ][ '__widget_basename' ] ) ) {
729                        $widget_title = $box[ 'args' ][ '__widget_basename' ];
730                        // Do not pass this parameter to the user callback function.
731                        unset( $box[ 'args' ][ '__widget_basename' ] );
732                }
733                echo '<button type="button" class="handlediv" aria-expanded="true">';
734                echo '<span class="screen-reader-text">' . sprintf( __( 'Toggle panel: %s', 'zero-bs-crm' ), $widget_title ) . '</span>';
735                echo '<span class="toggle-indicator" aria-hidden="true"></span>';
736                echo '</button>';
737        }
738        echo "<h2 class='hndle'><span>{$box['title']}</span></h2>\n";
739        echo '<div class="inside">' . "\n";
740        call_user_func($box['callback'], $object, $box);
741        echo "</div>\n";
742        echo "</div>\n";
743        */
744
745        $htmlClasses = 'ui segment';
746
747        // headless?
748        $headless = false;
749    if ( isset( $box['headless'] ) && $box['headless'] ) {
750        $headless = true;
751    }
752
753        // extra classes
754        $extraClasses = '';
755    if ( isset( $box['extraclasses'] ) && $box['extraclasses'] ) {
756        $extraClasses = $box['extraclasses'];
757    }
758
759        // convert capabilites into data-atr
760        // these get added to headed meta only...
761        // Also set vars here e.g. canMinimise
762        $dataAttrStr = '';
763    $canMinimise     = false;
764    $canHide         = false;
765    $canMove         = false;
766    if ( isset( $box['capabilities'] ) && is_array( $box['capabilities'] ) ) {
767
768        if ( isset( $box['capabilities']['can_minimise'] ) && $box['capabilities']['can_minimise'] ) {
769            $canMinimise = true;
770        }
771        if ( isset( $box['capabilities']['can_hide'] ) && $box['capabilities']['can_hide'] ) {
772            $canHide = true;
773        }
774        if ( isset( $box['capabilities']['can_move'] ) && $box['capabilities']['can_move'] ) {
775            $canMove = true;
776        }
777
778        foreach ( $box['capabilities'] as $capKey => $capVal ) {
779            if ( ! empty( $dataAttrStr ) ) {
780                $dataAttrStr .= ' ';
781            }
782
783            // arrays get csv'd
784            $capVStr = '';
785            if ( is_array( $capVal ) ) {
786                $capVStr = implode( ',', $capVal );
787            } else {
788                $capVStr = $capVal;
789            }
790
791            $dataAttrStr .= 'data-' . str_replace( '_', '-', $capKey ) . '="' . $capVStr . '"';
792        }
793    }
794
795        // classes
796        $classes = 'zbs-metabox';
797    $extraAttrs  = '';
798
799        // hidden class
800        $classes .= in_array( $box['id'], $hidden ) ? ' hide-if-js zbs-hidden' : '';
801
802        // minimised class
803        $classes .= in_array( $box['id'], $minimised ) ? ' zbs-minimised' : '';
804
805        // static class
806        $classes .= $canMove ? '' : ' zbs-static';
807
808        // if is a tab pane...
809        // class="ui bottom attached tab segment active" data-tab="first"
810    if ( $isTabPane ) {
811        $classes .= ' ui bottom attached tab segment';
812        if ( $isActiveTab ) {
813            $classes .= ' active';
814        }
815        $extraAttrs .= ' data-tab="' . $box['id'] . '"';
816
817        // always
818        $canMinimise = false;
819        $headless    = true;
820        $htmlClasses = '';
821    }
822
823        echo '<div class="' . esc_attr( $classes ) . '" id="' . esc_attr( $box['id'] ) . '" ' . esc_attr( $extraAttrs . $dataAttrStr ) . '>';
824
825            // hide/minimise option
826            $hideMinimiseMenu = '';
827    if ( $canMinimise ) {
828
829        // minimise - <i class="dropdown icon"></i>
830        // now inc both carets, class presence turns on/off
831        $hideMinimiseMenu = '<div class="ui right item zbs-metabox-minimise"><i class="caret up icon"></i><i class="caret down icon"></i></div>';
832
833    }
834
835    if ( ! $headless ) {
836        echo '<div id="' . esc_attr( $box['id'] ) . '-head" class="zbs-metabox-head ui top attached borderless menu ' . esc_attr( $extraClasses . postbox_classes( $box['id'], $page ) ) . '" ' . '>' . "\n"; // . $hidden_class
837        /*
838                not sure if we need this :)
839        if ( 'dashboard_browser_nag' != $box['id'] ) {
840                $widget_title = $box[ 'title' ];
841                if ( is_array( $box[ 'args' ] ) && isset( $box[ 'args' ][ '__widget_basename' ] ) ) {
842                        $widget_title = $box[ 'args' ][ '__widget_basename' ];
843                        // Do not pass this parameter to the user callback function.
844                        unset( $box[ 'args' ][ '__widget_basename' ] );
845                }
846                echo '<button type="button" class="handlediv" aria-expanded="true">';
847                echo '<span class="screen-reader-text">' . sprintf( __( 'Toggle panel: %s', 'zero-bs-crm' ), $widget_title ) . '</span>';
848                echo '<span class="toggle-indicator" aria-hidden="true"></span>';
849                echo '</button>';
850        } */
851
852            // txt
853            echo '<div class="header item">' . esc_html( $box['title'] ) . '</div>' . $hideMinimiseMenu . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- This is hard-coded HTML with no vars.
854
855            // right hand menu, if one
856            /*
857                    For now, was css glitching, can look later as don't need yet
858            echo '<div class="right menu">';
859
860                // drop down
861                echo '<div class="ui dropdown icon item"><i class="angle double up icon"></i>';
862
863                    echo '<div class="menu">';
864
865                        echo '<div class="item">Testing</div>';
866
867                    echo '</div>';
868
869                echo '</div>';// / dropdown
870
871            echo '</div>';
872            */
873
874        echo '</div>';
875
876        $htmlClasses .= ' bottom attached';
877    }
878            echo '<div id="' . esc_attr( $box['id'] ) . '-box" class="zbs-metabox-body ' . esc_attr( $htmlClasses ) . ' ' . esc_attr( $extraClasses ) . '">' . "\n"; // $hidden_class.
879                call_user_func( $box['callback'], $object, $box );
880            echo '</div>'; // /.zbs-metabox-body
881            echo '<div id="' . esc_attr( $box['id'] ) . '-block" class="zbs-metabox-block"><div>' . esc_html( $box['title'] ) . '</div></div>'; // this is BLOCKER for drag-drop support -//<i class="arrows alternate icon"></i>
882        echo "</div>\n";
883}
884
885// retrieves metabox list for current page (all contexts)
886// does so without priorities (e.g. high,low)
887function zeroBSCRM_getCurrentMetaboxesFlatArr() {
888
889    $mb               = array(
890        'normal' => array(),
891        'side'   => array(),
892    );
893    $mbWithPriorities = zeroBSCRM_getCurrentMetaboxes();
894
895    if ( is_array( $mbWithPriorities ) ) {
896
897        foreach ( array( 'normal', 'side' ) as $context ) {
898
899            foreach ( array( 'high', 'sorted', 'core', 'default', 'low' ) as $priority ) {
900
901                if ( isset( $mbWithPriorities[ $context ] ) && isset( $mbWithPriorities[ $context ][ $priority ] ) && is_array( $mbWithPriorities[ $context ][ $priority ] ) ) {
902
903                    $mb[ $context ] = array_merge( $mb[ $context ], $mbWithPriorities[ $context ][ $priority ] );
904                }
905            }
906        }
907    }
908
909    return $mb;
910}
911
912// retrieves metabox list for current page (all contexts)
913function zeroBSCRM_getCurrentMetaboxes() {
914
915    global $zbs;
916
917    $metaboxes = array();
918    $pageKey   = $zbs->pageKey;
919
920    // cycle through context/[priority]
921    foreach ( array( 'normal', 'side' ) as $context ) {
922
923        $mb = zeroBSCRM_getMetaboxes( $pageKey, $context );
924        // echo 'pagekey:'.$pageKey.'+'.$context.'<br><pre>'; print_r($mb); echo '</pre>';
925
926        if ( is_array( $mb ) && count( $mb ) > 0 ) {
927            $metaboxes[ $context ] = $mb;
928        }
929    }
930
931    return $metaboxes;
932}
933
934// retrieves metabox list for a page + context
935// (NOTE: Does not sort by user screen options)
936function zeroBSCRM_getMetaboxes( $page = '', $context = '' ) {
937
938    global $zbs;
939
940    if ( isset( $zbs->metaboxes[ $page ] ) && isset( $zbs->metaboxes[ $page ][ $context ] ) ) {
941        return $zbs->metaboxes[ $page ][ $context ];
942    }
943
944    return array();
945}