Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 1098
0.00% covered (danger)
0.00%
0 / 33
CRAP
0.00% covered (danger)
0.00%
0 / 11
zeroBSCRM_CustomersMetaboxSetup
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
156
zeroBS__Metabox_Contact
0.00% covered (danger)
0.00%
0 / 288
0.00% covered (danger)
0.00%
0 / 8
6972
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
2
 html
0.00% covered (danger)
0.00%
0 / 90
0.00% covered (danger)
0.00%
0 / 1
1056
 save_data
0.00% covered (danger)
0.00%
0 / 92
0.00% covered (danger)
0.00%
0 / 1
812
 save_profile_picture
0.00% covered (danger)
0.00%
0 / 69
0.00% covered (danger)
0.00%
0 / 1
210
 post_save_data
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
20
 updateMessage
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 update_invalid_email
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 updateEmailDupeMessage
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
zeroBS__Metabox_ContactActions
0.00% covered (danger)
0.00%
0 / 83
0.00% covered (danger)
0.00%
0 / 3
506
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
12
 html
0.00% covered (danger)
0.00%
0 / 63
0.00% covered (danger)
0.00%
0 / 1
342
 save_data
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
zeroBS__Metabox_ContactCustomFiles
0.00% covered (danger)
0.00%
0 / 111
0.00% covered (danger)
0.00%
0 / 3
2550
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
12
 html
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 1
342
 save_data
0.00% covered (danger)
0.00%
0 / 48
0.00% covered (danger)
0.00%
0 / 1
870
zeroBS__Metabox_ContactFiles
0.00% covered (danger)
0.00%
0 / 130
0.00% covered (danger)
0.00%
0 / 3
702
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
 html
0.00% covered (danger)
0.00%
0 / 86
0.00% covered (danger)
0.00%
0 / 1
110
 save_data
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
240
zeroBS__Metabox_ContactPortal
0.00% covered (danger)
0.00%
0 / 162
0.00% covered (danger)
0.00%
0 / 2
600
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
 html
0.00% covered (danger)
0.00%
0 / 148
0.00% covered (danger)
0.00%
0 / 1
552
zeroBS__Metabox_ContactSocial
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 3
240
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
 html
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
110
 save_data
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
zeroBS__Metabox_ContactAKA
0.00% covered (danger)
0.00%
0 / 108
0.00% covered (danger)
0.00%
0 / 2
156
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
 html
0.00% covered (danger)
0.00%
0 / 94
0.00% covered (danger)
0.00%
0 / 1
132
zeroBS__Metabox_ContactTags
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
2
zeroBS__Metabox_ContactLogs
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
2
zeroBS__Metabox_ContactCompany
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 3
240
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
2
 html
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
182
 save_data
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
zeroBS__Metabox_Contact_Activity
0.00% covered (danger)
0.00%
0 / 78
0.00% covered (danger)
0.00%
0 / 3
506
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 html
0.00% covered (danger)
0.00%
0 / 70
0.00% covered (danger)
0.00%
0 / 1
420
 save_data
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/*
3 * Jetpack CRM
4 * https://jetpackcrm.com
5 * V3.0
6 *
7 * Copyright 2020 Automattic
8 *
9 * Date: 20/02/2019
10 */
11
12defined( 'ZEROBSCRM_PATH' ) || exit( 0 );
13
14/*
15======================================================
16    Init Func
17    ====================================================== */
18
19function zeroBSCRM_CustomersMetaboxSetup() {
20
21    if ( zeroBSCRM_is_customer_edit_page() ) {
22
23        // Customer Fields
24        $zeroBS__Metabox_Contact = new zeroBS__Metabox_Contact( __FILE__ );
25
26        // Actions
27        $zeroBS__Metabox_ContactActions = new zeroBS__Metabox_ContactActions( __FILE__ );
28
29        // Logs
30
31            // req. for custom log types
32            zeroBSCRM_setupLogTypes();
33
34            // metabox
35            $zeroBS__Metabox_ContactLogs = new zeroBS__Metabox_ContactLogs( __FILE__ );
36
37        // Tags
38        $zeroBS__Metabox_ContactTags = new zeroBS__Metabox_ContactTags( __FILE__ );
39
40        // External sources
41        // $zeroBS__Metabox_ContactExternalSources = new zeroBS__Metabox_ContactExternalSources( __FILE__ );
42        $zeroBS__Metabox_ExtSource = new zeroBS__Metabox_ExtSource( __FILE__, 'contact', 'zbs-add-edit-contact-edit' );
43
44        // Quotes, Invs, Trans
45        // Don't need now we have singular :)
46        // $zeroBS__MetaboxAssociated = new zeroBS__MetaboxAssociated( __FILE__ );
47
48        // Client Portal access
49        if ( zeroBSCRM_isExtensionInstalled( 'portal' ) ) {
50            $zeroBS__Metabox_ContactPortal = new zeroBS__Metabox_ContactPortal( __FILE__ );
51        }
52
53        // Customer File Attachments
54
55            // Custom file attachment boxes
56        if ( zeroBSCRM_is_customer_edit_page() ) {
57
58            /*
59                old way
60            $settings = zeroBSCRM_getSetting('customfields'); $cfbInd = 1;
61
62            if (isset($settings['customersfiles']) && is_array($settings['customersfiles']) && count($settings['customersfiles']) > 0) foreach ($settings['customersfiles'] as $cfb){
63
64                $cfbName = ''; if (isset($cfb[0])) $cfbName = $cfb[0];
65
66                //add_meta_box('zerobs-customer-files-'.$cfbInd, $cfbName, 'zeroBS__MetaboxFilesCustom', 'zerobs_customer', 'normal', 'low',$cfbName);
67                $zeroBS__Metabox_ContactCustomFiles = new zeroBS__Metabox_ContactCustomFiles( __FILE__, 'zerobs-customer-files-'.$cfbInd , $cfbName);
68
69                $cfbInd++;
70            } */
71
72            $fileSlots = zeroBSCRM_fileSlots_getFileSlots();
73            if ( count( $fileSlots ) > 0 ) {
74                foreach ( $fileSlots as $fs ) {
75
76                    $zeroBS__Metabox_ContactCustomFiles = new zeroBS__Metabox_ContactCustomFiles( __FILE__, 'zerobs-customer-files-' . $fs['key'], $fs['name'] );
77
78                }
79            }
80        }
81
82        #} Social
83        if ( zeroBSCRM_getSetting( 'usesocial' ) == '1' ) {
84            $zeroBS__Metabox_ContactSocial = new zeroBS__Metabox_ContactSocial( __FILE__ );
85        }
86
87        #} AKA
88        if ( zeroBSCRM_getSetting( 'useaka' ) == '1' ) {
89            $zeroBS__Metabox_ContactAKA = new zeroBS__Metabox_ContactAKA( __FILE__ );
90        }
91
92        #} Ownership
93        if ( zeroBSCRM_getSetting( 'perusercustomers' ) == '1' ) {
94            $zeroBS__Metabox_Ownership = new zeroBS__Metabox_Ownership( __FILE__, ZBS_TYPE_CONTACT );
95        }
96
97        #} B2B mode (assign to co)
98        if ( zeroBSCRM_getSetting( 'companylevelcustomers' ) == '1' ) {
99            $zeroBS__Metabox_ContactCompany = new zeroBS__Metabox_ContactCompany( __FILE__ );
100        }
101    }
102
103    // Activity box on view page
104    if ( zeroBSCRM_is_customer_view_page() ) {
105            $zeroBS__Metabox_Contact_Activity = new zeroBS__Metabox_Contact_Activity( __FILE__ );
106        if ( zeroBSCRM_isExtensionInstalled( 'portal' ) ) {
107            $zeroBS__Metabox_ContactPortal = new zeroBS__Metabox_ContactPortal( __FILE__, 'zbs-view-contact' );
108        }
109    }
110}
111
112    add_action( 'admin_init', 'zeroBSCRM_CustomersMetaboxSetup' );
113
114/*
115======================================================
116    / Init Func
117    ====================================================== */
118
119/*
120======================================================
121    Declare Globals
122    ====================================================== */
123
124    #} Used throughout
125    // Don't know who added this, but GLOBALS are out of scope here
126    // global $zbsCustomerFields,$zbsCustomerQuoteFields,$zbsCustomerInvoiceFields;
127
128/*
129======================================================
130    / Declare Globals
131    ====================================================== */
132
133    // PerfTest: zeroBSCRM_performanceTest_startTimer('custmetabox');
134
135/*
136======================================================
137    Customer Metabox
138    ====================================================== */
139
140class zeroBS__Metabox_Contact extends zeroBS__Metabox {
141
142    // this is for catching 'new' contacts
143    private $newRecordNeedsRedir = false;
144
145    public function __construct( $plugin_file ) {
146
147        // set these
148        $this->objType         = 'contact';
149        $this->metaboxID       = 'zerobs-customer-edit';
150        $this->metaboxTitle    = __( 'Contact Details', 'zero-bs-crm' );
151        $this->metaboxScreen   = 'zbs-add-edit-contact-edit'; // 'zerobs_edit_contact'; // we can use anything here as is now using our func
152        $this->metaboxArea     = 'normal';
153        $this->metaboxLocation = 'high';
154        $this->saveOrder       = 1;
155        $this->capabilities    = array(
156
157            'can_hide'        => false, // can be hidden
158            'areas'           => array( 'normal' ), // areas can be dragged to - normal side = only areas currently
159            'can_accept_tabs' => true,  // can/can't accept tabs onto it
160            'can_become_tab'  => false, // can be added as tab
161            'can_minimise'    => true, // can be minimised
162            'can_move'        => true, // can be moved
163
164        );
165
166        // call this
167        $this->initMetabox();
168    }
169
170    /**
171     * This method generates HTML content for contact and metadata.
172     *
173     * It contains a series of operations to format and display contact data
174     * and metadata according to the parameters. This includes information retrieval,
175     * settings configuration, field hiding, address display and others.
176     *
177     * @param array $contact An associative array containing contact details.
178     * @param array $metabox Metabox information.
179     *
180     * @return  void
181     */
182    public function html( $contact, $metabox ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
183        global $zbs; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
184        global $zbsContactEditing;  // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
185        global $zbsCustomerFields; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
186
187        if ( ! isset( $zbsContactEditing ) && isset( $contact['id'] ) ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
188            $customer          = zeroBS_getCustomer( $contact['id'], false, false, false );
189            $zbsContactEditing = $customer; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
190        } else {
191            $customer = $zbsContactEditing; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
192        }
193
194        $fields_to_hide       = $zbs->settings->get( 'fieldhides' );
195        $show_id              = (int) $zbs->settings->get( 'showid' );
196        $fields               = $zbsCustomerFields; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
197        $show_addresses       = zeroBSCRM_getSetting( 'showaddress' );
198        $show_second_address  = (int) zeroBSCRM_getSetting( 'secondaddress' );
199        $show_country_fields  = zeroBSCRM_getSetting( 'countries' );
200        $second_address_label = zeroBSCRM_getSetting( 'secondaddresslabel' );
201        if ( empty( $second_address_label ) ) {
202            $second_address_label = __( 'Second Address', 'zero-bs-crm' );
203        }
204        ?>
205
206<script type="text/javascript">var zbscrmjs_secToken = '<?php echo esc_js( wp_create_nonce( 'zbscrmjs-ajax-nonce' ) ); ?>';</script>
207
208        <?php
209        if ( gettype( $customer ) !== 'array' ) {
210            echo '<input type="hidden" name="zbscrm_newcustomer" value="1" />';
211        }
212        ?>
213            <div>
214                <div class="jpcrm-form-grid" id="wptbpMetaBoxMainItem">
215        <?php
216        $avatar_mode = (int) zeroBSCRM_getSetting( 'avatarmode' );
217        if ( $avatar_mode === 2 ) :
218            ?>
219                    <div class="jpcrm-form-group jpcrm-form-group-span-2">
220                        <label class="jpcrm-form-label"><?php esc_html_e( 'Profile Picture', 'zero-bs-crm' ); ?>:</label>
221            <?php
222                            $avatar_url       = isset( $contact['id'] ) ? $zbs->DAL->contacts->getContactAvatar( $contact['id'] ) : zeroBSCRM_getDefaultContactAvatar(); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase, WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
223                            $empty_avatar_url = zeroBSCRM_getDefaultContactAvatar();
224            ?>
225                                <div class="zbs-text-input">
226                                    <div class="jpcrm-customer-profile-picture-container" style="margin-right:10px;">
227                                            <img src="<?php echo esc_attr( $avatar_url ); ?>" id="profile-picture-img" class="jpcrm-customer-profile-picture" />
228                                            <img src="<?php echo esc_attr( $empty_avatar_url ); ?>" id="empty-profile-picture" class="jpcrm-customer-profile-picture" style="display:none;" />
229                                    <br />
230                                    <label for="zbsc_profile-picture-file" class="jpcrm-customer-file-upload">
231                                            <?php esc_html_e( 'Change Picture', 'zero-bs-crm' ); ?>
232                                            <input id="zbsc_profile-picture-file" type="file" name="zbsc_profile-picture-file" class="jpcrm-customer-input-file"/>
233                                    </label>
234                                    <label id="zbsc_remove-profile-picture-button" class="jpcrm-customer-file-upload-remove">
235                                            <?php esc_html_e( 'Remove', 'zero-bs-crm' ); ?>
236                                            <input type="hidden" id="zbsc_remove-profile-picture" name="zbsc_remove-profile-picture" value="0" />
237                                    </label>
238                                    </div>
239                                </div>
240                            </div>
241
242            <?php
243        endif;
244
245        if ( $show_id === 1 && isset( $contact['id'] ) && ! empty( $contact['id'] ) ) :
246            ?>
247                            <div class="jpcrm-form-group">
248                                <label class="jpcrm-form-label"><?php esc_html_e( 'Contact ID', 'zero-bs-crm' ); ?>:</label>
249                                <b>#<?php echo isset( $contact['id'] ) ? esc_html( $contact['id'] ) : ''; ?></b>
250                            </div>
251                            <div class="jpcrm-form-group">
252                            </div>
253            <?php
254        endif;
255
256        global $zbsFieldsEnabled; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
257        if ( $show_second_address === 1 ) {
258            $zbsFieldsEnabled['secondaddress'] = true; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
259        }
260        $field_group = '';
261
262        foreach ( $fields as $field_key => $field_value ) {
263            $show_field = ! isset( $field_value['opt'] ) || isset( $zbsFieldsEnabled[ $field_value['opt'] ] ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
264            $show_field = isset( $fields_to_hide['customer'] )
265                && is_array( $fields_to_hide['customer'] )
266                && in_array( $field_key, $fields_to_hide['customer'] ) // phpcs:ignore WordPress.PHP.StrictInArray.MissingTrueStrict
267                ? false
268                : $show_field;
269            $show_field = isset( $field_value[0] )
270                && 'selectcountry' === $field_value[0]
271                && 0 === $show_country_fields
272                ? false
273                : $show_field;
274
275            if ( isset( $field_value['area'] ) && $field_value['area'] !== '' ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
276                if ( $show_addresses !== 1 ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
277                    continue;
278                } elseif ( $field_group === '' ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
279                    echo '<div class="jpcrm-form-grid" style="padding:0;grid-template-columns: 1fr;">';
280                    echo '<div class="jpcrm-form-group"><label>';
281                    echo esc_html__( $field_value['area'], 'zero-bs-crm' ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase, WordPress.WP.I18n.NonSingularStringLiteralText
282                    echo '</label></div>';
283                } elseif ( $field_group !== $field_value['area'] ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
284                    echo '</div>';
285                    echo '<div class="jpcrm-form-grid" style="padding:0;grid-template-columns: 1fr;">';
286                    echo '<div class="jpcrm-form-group"><label>';
287                    echo $show_field ? esc_html( $second_address_label ) : '';
288                    echo '</label></div>';
289                }
290                $field_group = $field_value['area']; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase, WordPress.WP.I18n.NonSingularStringLiteralText
291            }
292
293            if ( $field_group !== '' && ( ! isset( $field_value['area'] ) || $field_value['area'] === '' ) ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
294                $field_group = ''; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
295                echo '</div>';
296                echo '<div class="jpcrm-form-group jpcrm-form-group-span-2">&nbsp;</div>';
297            }
298
299            if ( $show_field ) {
300                if ( isset( $field_value[0] ) ) {
301                    if ( $field_group === 'Second Address' ) {
302                        $field_value[1] = str_replace( ' (' . $second_address_label . ')', '', $field_value[1] );
303                    }
304                    zeroBSCRM_html_editField( $customer, $field_key, $field_value, 'zbsc_' );
305                }
306            }
307        }
308        ?>
309                    </div>
310                </div>
311        <?php
312    }
313
314    public function save_data( $contact_id, $contact ) {
315
316        if ( ! defined( 'ZBS_C_SAVED' ) ) {
317
318            // debug if (get_current_user_id() == 12) echo 'FIRING<br>';
319
320            define( 'ZBS_C_SAVED', 1 );
321
322            global $zbs;
323
324            // check this
325            if ( empty( $contact_id ) || $contact_id < 1 ) {
326                $contact_id = -1;
327            }
328
329            $dataArr = zeroBS_buildContactMeta( $_POST );
330
331            // Use the tag-class function to retrieve any tags so we can add inline.
332            // Save tags against objid
333            $dataArr['tags'] = zeroBSCRM_tags_retrieveFromPostBag( true, ZBS_TYPE_CONTACT );
334
335            // owner - saved here now, rather than ownership box, to allow for pre-hook update. (as tags)
336            $owner = -1;
337            // Only allow an existing contact to change owners if they have permission to do so.
338            if ( $contact_id > -1 ) {
339
340                $can_edit_all_contacts = current_user_can( 'admin_zerobs_customers' ) && $zbs->settings->get( 'perusercustomers' ) == 0; // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual,WordPress.WP.Capabilities.Unknown  -- capability was defined in ZeroBSCRM.Permissions.php
341                $can_give_ownership    = $zbs->settings->get( 'usercangiveownership' ) == 1; // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual -- also above, there is the chance the numbers could be strings here, as expected elsewhere in the plugin.
342                $can_change_owner      = ( $can_give_ownership || current_user_can( 'manage_options' ) || $can_edit_all_contacts );
343                if ( $can_change_owner ) {
344
345                    if ( isset( $_POST['zerobscrm-owner'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- todo - noted in zero-bs-crm 2457.
346
347                        $potential_owner = (int) sanitize_text_field( wp_unslash( $_POST['zerobscrm-owner'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- todo - noted in zero-bs-crm 2457.
348                        if ( $potential_owner > 0 ) {
349                            $owner = $potential_owner;
350                        }
351                    }
352                } else {
353                    $owner = (int) $zbs->DAL->contacts->getContactOwner( $contact_id ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
354                }
355            }
356
357            // now we check whether a user with this email already exists (separate to this contact id), so we can warn them
358            // ... that it wont have changed the email
359            if ( ! empty( $dataArr['email'] ) ) {
360
361                if ( ! zeroBSCRM_validateEmail( $dataArr['email'] ) ) {
362
363                    $this->update_invalid_email( $dataArr['email'] );
364                    $dataArr['email'] = '';
365
366                } else {
367
368                    $potentialID = zeroBS_getCustomerIDWithEmail( $dataArr['email'] );
369
370                    if ( ! empty( $potentialID ) && $potentialID != $contact_id ) {
371
372                        // no go.
373                        $this->updateEmailDupeMessage( $potentialID );
374
375                        // unset email change (leave as was)
376                        $dataArr['email'] = zeroBS_customerEmail( $contact_id );
377
378                    }
379                }
380            }
381            // phpcs:disable WordPress.NamingConventions.ValidVariableName -- to be refactored.
382            // We have to explicitly retrieve the avatar from the DB.
383            $dataArr['avatar'] = ( $contact_id !== -1 ) ? $zbs->DAL->contacts->getContactAvatar( $contact_id ) : '';
384            //phpcs:enable WordPress.NamingConventions.ValidVariableName
385
386            // make a copy for IA below (just fields)
387            $contactData = $dataArr;
388
389            // Company assignment?
390            if ( isset( $_POST['zbs_company'] ) ) {
391                $dataArr['companies'] = array( (int) sanitize_text_field( $_POST['zbs_company'] ) );
392            }
393
394            // Stripslashes
395            // This avoids us adding `O\'toole ltd' into the db. see #1107
396            // ...this is more sensitive than using zeroBSCRM_stripSlashesFromArr
397            // in the long term it may make more sense to stripslashes pre insert/update in the DAL
398            // in the case of contacts, there are no core fields which will be broken by stripslashes at this time (4.0.11)
399            $data_array = $dataArr;
400            foreach ( $dataArr as $key => $val ) {
401
402                // op strings
403                $value = $val;
404                if ( is_string( $value ) ) {
405                    $value = stripslashes( $value );
406                }
407
408                // pass into final array
409                $data_array[ $key ] = $value;
410
411            }
412
413            // add update directly
414            $addUpdateReturn = $zbs->DAL->contacts->addUpdateContact(
415                array(
416
417                    'id'            => $contact_id,
418                    'owner'         => $owner,
419                    'data'          => $data_array,
420                    'limitedFields' => -1,
421                    /*
422                        array(
423
424                            'email' => $userDeets['email'], // Unique Field !
425
426                            'status' => $userDeets['status'],
427                            'prefix' => $userDeets['prefix'],
428                            'fname' => $userDeets['fname'],
429                            'lname' => $userDeets['lname'],
430                            'addr1' => $userDeets['addr1'],
431                            'addr2' => $userDeets['addr2'],
432                            'city' => $userDeets['city'],
433                            'county' => $userDeets['county'],
434                            'country' => $userDeets['country'],
435                            'postcode' => $userDeets['postcode'],
436                            'secaddr1' => $userDeets['secaddr_addr1'],
437                            'secaddr2' => $userDeets['secaddr_addr2'],
438                            'seccity' => $userDeets['secaddr_city'],
439                            'seccounty' => $userDeets['secaddr_county'],
440                            'seccountry' => $userDeets['secaddr_country'],
441                            'secpostcode' => $userDeets['secaddr_postcode'],
442                            'hometel' => $userDeets['hometel'],
443                            'worktel' => $userDeets['worktel'],
444                            'mobtel' => $userDeets['mobtel'],
445                            'wpid'  => -1,
446                            'avatar' => $avatarURL,
447
448                            // Note Custom fields may be passed here, but will not have defaults so check isset()
449
450                            'tags' => $tags,
451
452                            // wh added for later use.
453                            'lastcontacted' => $lastcontacted,
454
455                            'companies' => $companies // array of co id's :)
456                    ) */
457
458                )
459            );
460
461            // Note: For NEW contacts, we make sure a global is set here, that other update funcs can catch
462            // ... so it's essential this one runs first!
463            // this is managed in the metabox Class :)
464            if ( $contact_id == -1 && ! empty( $addUpdateReturn ) && $addUpdateReturn != -1 ) {
465
466                $contact_id = $addUpdateReturn;
467                global $zbsJustInsertedMetaboxID;
468                $zbsJustInsertedMetaboxID = $contact_id;
469
470                // set this so it redirs
471                $this->newRecordNeedsRedir = true;
472            }
473
474            // success?
475            if ( $addUpdateReturn != -1 && $addUpdateReturn > 0 ) {
476                $this->save_profile_picture( $contact_id, $contact );
477                // Update Msg
478                // this adds an update message which'll go out ahead of any content
479                // This adds to metabox: $this->updateMessages['update'] = zeroBSCRM_UI2_messageHTML('info olive mini zbs-not-urgent',__('Contact Updated',"zero-bs-crm"),'','address book outline','contactUpdated');
480                // This adds to edit page
481                $this->updateMessage( $this->newRecordNeedsRedir );
482
483                // catch any non-critical messages
484                $nonCriticalMessages = $zbs->DAL->getErrors( ZBS_TYPE_CONTACT );
485                if ( is_array( $nonCriticalMessages ) && count( $nonCriticalMessages ) > 0 ) {
486                    $this->dalNoticeMessage( $nonCriticalMessages );
487                }
488            } else {
489
490                // fail somehow
491                $failMessages = $zbs->DAL->getErrors( ZBS_TYPE_CONTACT );
492
493                // show msg (retrieved from DAL err stack)
494                if ( is_array( $failMessages ) && count( $failMessages ) > 0 ) {
495                    $this->dalErrorMessage( $failMessages );
496                } else {
497                    $this->dalErrorMessage( array( __( 'Insert/Update Failed with general error', 'zero-bs-crm' ) ) );
498                }
499
500                // pass the pre-fill:
501                global $zbsObjDataPrefill;
502                $zbsObjDataPrefill = $dataArr;
503
504            }
505        }
506
507        return $contact;
508    }
509
510    /*
511    * Saves the profile picture
512    */
513    public function save_profile_picture( $contact_id, $crm_contact ) {
514        global $zbs;
515
516        $contact_dir_info    = jpcrm_storage_dir_info_for_contact( $contact_id );
517        $field_key           = 'jpcrm-profile-picture';
518        $is_remove_flag_set  = isset( $_POST['zbsc_remove-profile-picture'] ) && $_POST['zbsc_remove-profile-picture'] == '1';
519        $remove_old_avatar   = false;
520        $has_new_avatar_file =
521            isset( $_FILES['zbsc_profile-picture-file'] )
522            && empty( $_FILES['zbsc_profile-picture-file']['error'] )
523            && is_uploaded_file( $_FILES['zbsc_profile-picture-file']['tmp_name'] );
524
525        if ( $is_remove_flag_set ) {
526            $zbs->DAL->contacts->addUpdateContact(
527                array(
528                    'id'            => $contact_id,
529                    'limitedFields' => array(
530                        array(
531                            'key'  => 'zbsc_avatar',
532                            'val'  => '',
533                            'type' => '%s',
534                        ),
535                    ),
536                )
537            );
538            $remove_old_avatar = true;
539        } elseif ( $has_new_avatar_file ) {
540
541            // verify image file type
542            $allowed_image_types     = array(
543                'image/jpeg' => 'jpg',
544                'image/jpg'  => 'jpg',
545                'image/gif'  => 'gif',
546                'image/png'  => 'png',
547            );
548            $allowed_file_extensions = array( '.jpg', '.jpeg', '.gif', '.png' );
549            $allowed_mime_types      = array( 'image/jpeg', 'image/jpg', 'image/gif', 'image/png' );
550            if ( ! jpcrm_file_check_mime_extension( $_FILES['zbsc_profile-picture-file'], $allowed_file_extensions, $allowed_mime_types ) ) {
551
552                $this->dalErrorMessage( array( __( 'Error: Profile Picture only accepts jpg, png, and gif images!', 'zero-bs-crm' ) ) );
553                return;
554
555            }
556
557            if ( $contact_dir_info === false ) {
558                $this->dalErrorMessage( array( __( 'Error while retrieving the contact\'s folder.', 'zero-bs-crm' ) ) );
559                return;
560            }
561
562            $avatar_path           = $contact_dir_info['avatar']['path'];
563            $contact_folder_exists = jpcrm_create_and_secure_dir_from_external_access( $avatar_path, false );
564
565            if ( ! $contact_folder_exists ) {
566                $this->dalErrorMessage( array( __( 'There was an error creating the profile picture directory.', 'zero-bs-crm' ) ) );
567                return;
568            }
569
570            $zbs->load_encryption();
571            $avatar_filename = sprintf(
572                'avatar_%s.%s',
573                $zbs->encryption->get_rand_hex( 10 ),
574                $allowed_image_types[ $_FILES['zbsc_profile-picture-file']['type'] ] // extension for this filetype
575            );
576
577            if ( ! file_exists( $avatar_path . '/' . $avatar_filename )
578                && move_uploaded_file( $_FILES['zbsc_profile-picture-file']['tmp_name'], $avatar_path . '/' . $avatar_filename )
579            ) {
580                $zbs->DAL->contacts->addUpdateContact(
581                    array(
582                        'id'            => $contact_id,
583                        'limitedFields' => array(
584                            array(
585                                'key'  => 'zbsc_avatar',
586                                'val'  => $contact_dir_info['avatar']['url'] . '/' . $avatar_filename,
587                                'type' => '%s',
588                            ),
589                        ),
590                    )
591                );
592
593                $remove_old_avatar = true;
594            } else {
595                $this->dalErrorMessage( array( __( 'There was an error updating the profile picture.', 'zero-bs-crm' ) ) );
596            }
597        }
598
599        if ( $remove_old_avatar && ! empty( $crm_contact['avatar'] ) ) {
600            $previous_avatar_full_path = $contact_dir_info['avatar']['path'] . '/' . basename( $crm_contact['avatar'] );
601            if ( file_exists( $previous_avatar_full_path ) ) {
602                unlink( $previous_avatar_full_path );
603            }
604        }
605    }
606
607    // This catches 'new' contacts + redirs to right url
608    public function post_save_data( $objID, $obj ) {
609
610        if ( $this->newRecordNeedsRedir ) {
611
612            global $zbs, $zbsJustInsertedMetaboxID;
613            if ( ! empty( $zbsJustInsertedMetaboxID ) && $zbsJustInsertedMetaboxID > 0 ) {
614
615                // redir
616                $zbs->new_record_edit_redirect( 'zerobs_customer', $zbsJustInsertedMetaboxID );
617            }
618        }
619    }
620
621    public function updateMessage( $created = false ) {
622        $text = $created ? __( 'Contact Created', 'zero-bs-crm' ) : __( 'Contact Updated', 'zero-bs-crm' );
623        // zbs-not-urgent means it'll auto hide after 1.5s
624        $msg = zeroBSCRM_UI2_messageHTML( 'info olive mini zbs-not-urgent', $text, '', 'address book outline', 'contactUpdated' );
625
626        // quick + dirty
627        global $zbs;
628
629        $zbs->pageMessages[] = $msg;
630    }
631
632    public function update_invalid_email( $invalid_email ) {
633        global $zbs;
634        $msg                 = zeroBSCRM_UI2_messageHTML(
635            'info orange mini',
636            sprintf( __( 'The contact email specified (%s) is not valid.', 'zero-bs-crm' ), $invalid_email ),
637            '',
638            'address book outline',
639            'contactUpdated'
640        );
641        $zbs->pageMessages[] = $msg;
642    }
643
644    public function updateEmailDupeMessage( $otherContactID = -1 ) {
645
646        $viewHTML = ' <a href="' . jpcrm_esc_link( 'view', $otherContactID, 'zerobs_customer' ) . '" target="_blank">' . __( 'View Contact', 'zero-bs-crm' ) . '</a>';
647
648        $msg = zeroBSCRM_UI2_messageHTML( 'info orange mini', __( 'Contact email could not be updated because a contact already exists with this email address.', 'zero-bs-crm' ) . $viewHTML, '', 'address book outline', 'contactUpdated' );
649
650        // quick + dirty
651        global $zbs;
652
653        $zbs->pageMessages[] = $msg;
654    }
655}
656
657/*
658======================================================
659    / Customer Metabox
660    ====================================================== */
661
662/*
663======================================================
664    Create Actions Box
665    ====================================================== */
666
667class zeroBS__Metabox_ContactActions extends zeroBS__Metabox {
668
669    private $actions = array();
670
671    public function __construct( $plugin_file ) {
672
673        $this->objType         = 'contact';
674        $this->metaboxID       = 'zerobs-customer-actions';
675        $this->metaboxTitle    = __( 'Contact Actions', 'zero-bs-crm' );
676        $this->metaboxScreen   = 'zbs-add-edit-contact-edit'; // 'zerobs_edit_contact'; // we can use anything here as is now using our func
677        $this->metaboxArea     = 'side';
678        $this->metaboxLocation = 'high';
679        $this->headless        = true;
680        $this->capabilities    = array(
681
682            'can_hide'        => false, // can be hidden
683            'areas'           => array( 'side' ), // areas can be dragged to - normal side = only areas currently
684            'can_accept_tabs' => false,  // can/can't accept tabs onto it
685            'can_become_tab'  => false, // can be added as tab
686            'can_minimise'    => false, // can be minimised
687            'can_move'        => false, // can be moved
688
689        );
690
691        // hacky id check for now:
692        if ( isset( $_GET['zbsid'] ) && ! empty( $_GET['zbsid'] ) ) {
693            $id = (int) sanitize_text_field( $_GET['zbsid'] );
694            // call this, if actions
695            $this->actions = zeroBS_contact_actions( $id );
696
697        }
698        $this->initMetabox();
699    }
700
701    public function html( $contact, $metabox ) {
702
703        $avatarMode     = zeroBSCRM_getSetting( 'avatarmode' );
704        $avatarStr      = '';
705        $is_new_contact = count( $this->actions ) > 0 ? false : true;
706        if ( $avatarMode !== 3 ) {
707
708            $cID = -1;
709            if ( is_array( $contact ) && isset( $contact['id'] ) ) {
710                $cID = (int) $contact['id'];
711            }
712            $avatarStr = zeroBS_customerAvatarHTML( $cID );
713
714        }
715
716        ?>
717        <div class="zbs-generic-save-wrap">
718            <div class="ui medium dividing header"><i class="save icon"></i> <?php esc_html_e( 'Contact Actions', 'zero-bs-crm' ); ?></div>
719            <div class="clear"></div>
720        <?php
721
722        # https://codepen.io/kyleshockey/pen/bdeLrE
723        if ( ! $is_new_contact ) {
724            ?>
725        <script type="text/javascript">
726            var zbsContactAvatarLang = {
727                'upload': '<?php esc_html_e( 'Upload Image', 'zero-bs-crm' ); ?>',
728            };
729        </script>
730        <div class="action-wrap">
731            <div class="ui dropdown jpcrm-button white-bg jpcrm-dropdown"><?php esc_html_e( 'Contact Actions', 'zero-bs-crm' ); ?><i class="fa fa-angle-down"></i>
732                <div class="menu" style="margin: 4px;">
733                <?php
734                foreach ( $this->actions as $actKey => $action ) {
735
736                    // filter out 'edit' as on that page :)
737                    if ( $actKey != 'edit' ) {
738                        ?>
739                <div class="item zbs-contact-action" id="zbs-contact-action-<?php echo esc_attr( $actKey ); ?>"
740                        <?php
741                        // if url isset, pass that data-action, otherwise leave for js to attach to
742                        if ( isset( $action['url'] ) && ! empty( $action['url'] ) ) {
743                            ?>
744                        data-action="
745                            <?php
746                            if ( isset( $action['url'] ) ) {
747                                echo 'url';
748                            }
749                            ?>
750                            " data-url="
751                            <?php
752                            if ( isset( $action['url'] ) ) {
753                                echo esc_attr( $action['url'] );
754                            }
755                            ?>
756                                "
757                            <?php
758                        }
759
760                        // got extra attributes?
761                        if ( isset( $action['extraattr'] ) && is_array( $action['extraattr'] ) ) {
762
763                                // dump extra attr into item
764                            foreach ( $action['extraattr'] as $k => $v ) {
765                                echo ' data-' . esc_attr( $k ) . '="' . esc_attr( $v ) . '"';
766                            }
767                        }
768                        ?>
769                        >
770                        <?php
771
772                        // got ico?
773                        if ( isset( $action['ico'] ) ) {
774                            echo '<i class="' . esc_attr( $action['ico'] ) . '"></i>';
775                        }
776
777                        // got text?
778                        if ( isset( $action['label'] ) ) {
779                            echo esc_html( $action['label'] );
780                        }
781
782                        ?>
783                </div>
784                        <?php
785                    }
786                }
787                ?>
788                </div>
789        </div>
790        </div>
791        <script type="text/javascript">
792        jQuery(function(){
793
794            // actions drop down
795            jQuery('.ui.dropdown').dropdown();
796
797            // action items
798            jQuery('.zbs-contact-action').off('click').on( 'click', function(){
799
800                // get action type (at launch, only url)
801                var actionType = jQuery(this).attr('data-action');
802
803                if (typeof actionType != "undefined") switch (actionType){
804
805                    case 'url':
806
807                        var u = jQuery(this).attr('data-url');
808                        if (typeof u != "undefined" && u != '') window.location = u;
809
810                        break;
811
812
813                }
814
815            });
816
817        });
818        </script>
819            <?php
820        }
821        ?>
822            <div class="zbs-contact-actions-bottom zbs-objedit-actions-bottom">
823                <button class="jpcrm-button" type="button" id="zbs-edit-save"><?php $is_new_contact ? esc_html_e( 'Save', 'zero-bs-crm' ) : esc_html_e( 'Update', 'zero-bs-crm' ); ?> <?php esc_html_e( 'Contact', 'zero-bs-crm' ); ?></button>
824                <div class='clear'></div>
825            </div>
826            <?php
827    }
828
829    public function save_data( $contact_id, $contact ) {
830
831        // avatar changes saved by main contact save func (field editor), allowing for all-in-one creation/updates, see #AVATARSAVE
832
833        return $contact;
834    }
835}
836
837/*
838======================================================
839    / Create Actions Box
840    ====================================================== */
841
842/*
843======================================================
844    Attach (custom) fileboxes to customer metabox
845    ====================================================== */
846
847class zeroBS__Metabox_ContactCustomFiles extends zeroBS__Metabox {
848
849    public function __construct( $plugin_file, $idOverride = '', $titleOverride = '' ) {
850
851        $this->objType         = 'contact';
852        $this->metaboxID       = 'zerobs-customer-custom-files';
853        $this->metaboxTitle    = __( 'Other Files', 'zero-bs-crm' );
854        $this->metaboxScreen   = 'zbs-add-edit-contact-edit'; // 'zerobs_edit_contact'; // we can use anything here as is now using our func
855        $this->metaboxArea     = 'normal';
856        $this->metaboxLocation = 'low';
857        $this->capabilities    = array(
858
859            'can_hide'        => true, // can be hidden
860            'areas'           => array( 'normal' ), // areas can be dragged to - normal side = only areas currently
861            'can_accept_tabs' => false,  // can/can't accept tabs onto it
862            'can_become_tab'  => true, // can be added as tab
863            'can_minimise'    => true, // can be minimised
864
865        );
866
867        if ( ! empty( $idOverride ) ) {
868            $this->metaboxID = $idOverride;
869        }
870        if ( ! empty( $titleOverride ) ) {
871            $this->metaboxTitle = __( $titleOverride, 'zero-bs-crm' );
872        }
873
874        // call this
875        $this->initMetabox();
876    }
877
878    public function html( $contact, $args ) {
879
880        global $zbs;
881
882                $html = '';
883
884                $thisFileSlotName = '';
885        if ( isset( $args['title'] ) ) {
886            $thisFileSlotName = $args['title'];
887        }
888                $filePerma = ''; if ( isset( $thisFileSlotName ) && ! empty( $thisFileSlotName ) ) {
889                    // $filePerma = strtolower(str_replace(' ','_',str_replace('.','_',substr($thisFileSlotName,0,20))));
890                    $filePerma = $zbs->DAL->makeSlug( $thisFileSlotName );
891                }
892
893                // phpcs:disable WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
894                $zbsFiles = array();
895                if ( $contact ) {
896                    // retrieve - shouldn't these vars be "other files"... confusing
897                    $zbsFiles = zeroBSCRM_getCustomerFiles( $contact['id'] );
898
899                    // This specifically looks for $args['title'] file
900                    $fileSlotSrc = zeroBSCRM_fileslots_fileInSlot( $filePerma, $contact['id'], ZBS_TYPE_CONTACT );
901
902                    // check for file + only show that
903                    $zbsFilesArr = array();
904                    if ( $fileSlotSrc !== '' && is_array( $zbsFiles ) && count( $zbsFiles ) > 0 ) {
905                        foreach ( $zbsFiles as $f ) {
906                            if ( $f['file'] === $fileSlotSrc ) {
907                                $zbsFilesArr[] = $f;
908                            }
909                        }
910                    }
911                    $zbsFiles = $zbsFilesArr;
912                }
913
914                // while we only have 1 file per slot, we can do this:
915                // *js uses this to empty if deleted elsewhere (other metabox)
916                $fileSlotURL = '';
917                if ( is_array( $zbsFiles ) && count( $zbsFiles ) === 1 ) {
918                    $fileSlotURL = $zbsFiles[0]['url'];
919                }
920                // phpcs:enable WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
921
922                ?>
923                            <table class="form-table wh-metatab wptbp zbsFileSlotTable" data-sloturl="<?php echo esc_attr( $fileSlotURL ); ?>" id="<?php echo esc_attr( $this->metaboxID ); ?>-tab">
924
925                            <?php
926
927                            #} Any slot filled?
928                            if ( is_array( $zbsFiles ) && count( $zbsFiles ) > 0 ) {
929                                ?>
930                                    <tr class="wh-large zbsFileSlotWrap"><th class="zbsFileSlotTitle"><label><?php echo '<span>' . esc_html( count( $zbsFiles ) ) . '</span> ' . esc_html__( 'File(s)', 'zero-bs-crm' ) . ':'; ?></label></th>
931                                            <td class="">
932                                            <?php
933                                                $fileLineIndx = 1; foreach ( $zbsFiles as $zbsFile ) {
934
935                                                    /*
936                                                    $file = basename($zbsFile['file']);
937
938                                                    // if in privatised system, ignore first hash in name
939                                                    if (isset($zbsFile['priv'])){
940
941                                                        $file = substr($file,strpos($file, '-')+1);
942                                                    } */
943                                                    $file = zeroBSCRM_files_baseName( $zbsFile['file'], isset( $zbsFile['priv'] ) );
944                                                    echo '<div class="zbsFileLine" id="zbsFileLineCustomer' . esc_attr( $fileLineIndx ) . '"><a href="' . esc_url( $zbsFile['url'] ) . '" target="_blank">' . esc_html( $file ) . '</a> </div>';
945
946                                                    // if using portal.. state shown/hidden
947                                                    // this is also shown in each file slot :) if you change any of it change that too
948                                                if ( defined( 'ZBS_CLIENTPRO_TEMPLATES' ) ) {
949                                                    if ( isset( $zbsFile['portal'] ) && $zbsFile['portal'] ) {
950                                                        echo "<p><i class='icon check circle green inverted'></i> " . esc_html__( 'Shown on Portal', 'zero-bs-crm' ) . '</p>';
951                                                    } else {
952                                                        echo "<p><i class='icon ban inverted red'></i> " . esc_html__( 'Not shown on Portal', 'zero-bs-crm' ) . '</p>';
953                                                    }
954                                                }
955
956                                                    ++$fileLineIndx;
957
958                                                }
959                                                ?>
960                                            </td></tr>
961                                            <?php
962
963                            }
964                            ?>
965
966                                <?php
967                                #adapted from http://code.tutsplus.com/articles/attaching-files-to-your-posts-using-wordpress-custom-meta-boxes-part-1--wp-22291
968
969                                    // will be done by mainfunc wp_nonce_field(plugin_basename(__FILE__), 'zbsc_file_attachment_nonce');
970
971                                    $html .= '<input type="file" id="zbsc_file_' . $filePerma . '" name="zbsc_file_' . $filePerma . '" size="25" class="zbs-dc">';
972
973                                ?>
974                                        <tr class="wh-large"><th><label><?php esc_html_e( 'Add File', 'zero-bs-crm' ); ?>:</label><br />(<?php esc_html_e( 'Optional', 'zero-bs-crm' ); ?>)<br /><?php esc_html_e( 'Accepted File Types', 'zero-bs-crm' ); ?>:<br /><?php echo esc_html( zeroBS_acceptableFileTypeListStr() ); ?></th>
975                                            <td>
976                                            <?php
977                                            echo $html;
978                                            ?>
979                                </td></tr>
980
981                            
982                            </table>
983                            <?php
984
985                            // PerfTest: zeroBSCRM_performanceTest_finishTimer('custmetabox');
986                            // PerfTest: zeroBSCRM_performanceTest_debugOut();
987
988                            ?>
989                            <script type="text/javascript">
990
991                                jQuery(function(){
992
993                                });
994
995
996                            </script>
997
998                            <?php
999
1000                            // PerfTest: zeroBSCRM_performanceTest_finishTimer('other');
1001    }
1002
1003    public function save_data( $contact_id, $contact ) {
1004
1005                    // when multiple custom file boxes, this only needs to fire once :)
1006        if ( zeroBSCRM_is_customer_edit_page() && ! defined( 'ZBS_CUSTOMFILES_SAVED' ) ) {
1007
1008            define( 'ZBS_CUSTOMFILES_SAVED', 1 );
1009
1010            global $zbsc_justUploadedCustomer, $zbs;
1011
1012            $settings = $zbs->settings->get( 'customfields' );
1013            $cfbInd   = 1;
1014
1015            $cfbsubs = array();
1016
1017            if ( isset( $settings['customersfiles'] ) && is_array( $settings['customersfiles'] ) && count( $settings['customersfiles'] ) > 0 ) {
1018                foreach ( $settings['customersfiles'] as $cfb ) {
1019
1020                    $thisFileSlotName = '';
1021                    if ( isset( $cfb[0] ) ) {
1022                        $thisFileSlotName = $cfb[0];
1023                    }
1024                    $filePerma = ''; if ( isset( $thisFileSlotName ) && ! empty( $thisFileSlotName ) ) {
1025
1026                        // $filePerma = strtolower(str_replace(' ','_',str_replace('.','_',substr($thisFileSlotName,0,20))));
1027                        $filePerma = $zbs->DAL->makeSlug( $thisFileSlotName );
1028
1029                    }
1030
1031                    if ( ! empty( $thisFileSlotName ) && ! empty( $filePerma ) ) {
1032                        $cfbsubs[ $filePerma ] = $thisFileSlotName;
1033                    }
1034                }
1035            }
1036
1037            if ( count( $cfbsubs ) > 0 ) {
1038                foreach ( $cfbsubs as $cfSubKey => $cfSubName ) {
1039
1040                                /* --- security verification --- */
1041                    if ( isset( $_POST['zbsc_file_attachment_nonce'] ) ) {
1042                        if ( ! wp_verify_nonce( $_POST['zbsc_file_attachment_nonce'], plugin_basename( __FILE__ ) ) ) {
1043                                return $contact_id;
1044                        } // end if
1045                    }
1046
1047                    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
1048                        return $contact_id;
1049                    } // end if
1050
1051                    if ( ! zeroBSCRM_permsCustomers() ) {
1052                        return $contact_id;
1053                    }
1054                            /* - end security verification - */
1055
1056                    if ( ! empty( $_FILES[ 'zbsc_file_' . $cfSubKey ]['name'] ) &&
1057                            ( ! isset( $zbsc_justUploadedCustomer ) ||
1058                            ( isset( $zbsc_justUploadedCustomer ) && $zbsc_justUploadedCustomer != $_FILES[ 'zbsc_file_' . $cfSubKey ]['name'] )
1059                            )
1060                            ) {
1061
1062                        // Blocking repeat-upload bug
1063                        $zbsc_justUploadedCustomer = $_FILES[ 'zbsc_file_' . $cfSubKey ]['name'];
1064
1065                        // verify file extension and mime type
1066                        if ( jpcrm_file_check_mime_extension( $_FILES[ 'zbsc_file_' . $cfSubKey ] ) ) {
1067
1068                            $upload = wp_upload_bits( $_FILES[ 'zbsc_file_' . $cfSubKey ]['name'], null, file_get_contents( $_FILES[ 'zbsc_file_' . $cfSubKey ]['tmp_name'] ) );
1069
1070                            if ( isset( $upload['error'] ) && $upload['error'] != 0 ) {
1071                                wp_die( 'There was an error uploading your file. The error is: ' . esc_html( $upload['error'] ) );
1072                            } else {
1073                                // update_post_meta($contact_id, 'zbsc_file_'.$cfSubKey, $upload);
1074
1075                                // v2.13 - also privatise the file (move to our asset store)
1076                                // $upload will have 'file' and 'url'
1077                                $fileName        = basename( $upload['file'] );
1078                                $fileDir         = dirname( $upload['file'] );
1079                                $privateThatFile = zeroBSCRM_privatiseUploadedFile( $fileDir, $fileName );
1080                                if ( is_array( $privateThatFile ) && isset( $privateThatFile['file'] ) ) {
1081
1082                                    // successfully moved to our store
1083
1084                                        // modify URL + file attributes
1085                                        $upload['file'] = $privateThatFile['file'];
1086                                        $upload['url']  = $privateThatFile['url'];
1087
1088                                        // add this extra identifier if in privatised sys
1089                                        $upload['priv'] = true;
1090
1091                                } else {
1092
1093                                    // couldn't move to store, leave in uploaded for now :)
1094
1095                                }
1096
1097                                // w mod - adds to array :)
1098                                $zbsCustomerFiles = zeroBSCRM_getCustomerFiles( $contact_id );
1099
1100                                if ( is_array( $zbsCustomerFiles ) ) {
1101
1102                                    // add it
1103                                    $zbsCustomerFiles[] = $upload;
1104
1105                                } else {
1106
1107                                    // first
1108                                    $zbsCustomerFiles = array( $upload );
1109
1110                                }
1111
1112                                // update_post_meta($contact_id, 'zbs_customer_files', $zbsCustomerFiles);
1113                                zeroBSCRM_updateCustomerFiles( $contact_id, $zbsCustomerFiles );
1114
1115                                // this'll override any prev in that slot, too
1116                                zeroBSCRM_fileslots_addToSlot( $cfSubKey, $upload['file'], $contact_id, ZBS_TYPE_CONTACT, true );
1117
1118                                // Fire any 'post-upload-processing' (e.g. CPP makes thumbnails of pdf, jpg, etc.)
1119                                do_action( 'zbs_post_upload_contact', $upload );
1120                            }
1121                        } else {
1122                            wp_die( "The file type that you've uploaded is not an accepted file format." );
1123                        }
1124                    } // if file
1125
1126                } /// / foreach
1127            }
1128        }
1129
1130            return $contact;
1131    }
1132}
1133
1134/*
1135======================================================
1136    / Attach (custom) fileboxes to customer metabox
1137    ====================================================== */
1138
1139/*
1140======================================================
1141    Attach files to customer metabox
1142    ====================================================== */
1143
1144/*
1145======================================================
1146    Contact Files Metabox
1147    ====================================================== */
1148
1149class zeroBS__Metabox_ContactFiles extends zeroBS__Metabox {
1150
1151    public function __construct( $plugin_file ) {
1152
1153        $this->objType         = 'contact';
1154        $this->metaboxID       = 'zerobs-customer-files';
1155        $this->metaboxTitle    = __( 'Other Files', 'zero-bs-crm' );
1156        $this->metaboxScreen   = 'zbs-add-edit-contact-edit'; // 'zerobs_edit_contact'; // we can use anything here as is now using our func
1157        $this->metaboxArea     = 'normal';
1158        $this->metaboxLocation = 'low';
1159        $this->capabilities    = array(
1160
1161            'can_hide'        => true, // can be hidden
1162            'areas'           => array( 'normal' ), // areas can be dragged to - normal side = only areas currently
1163            'can_accept_tabs' => true,  // can/can't accept tabs onto it
1164            'can_become_tab'  => true, // can be added as tab
1165            'can_minimise'    => true, // can be minimised
1166
1167        );
1168
1169        // call this
1170        $this->initMetabox();
1171    }
1172
1173    public function html( $contact, $metabox ) {
1174
1175            global $zbs;
1176
1177            $html = '';
1178
1179            // wmod
1180
1181                    #} retrieve - shouldn't these vars be "other files"... confusing
1182                    $zbsFiles = false;
1183        if ( isset( $contact['id'] ) ) {
1184            $zbsFiles = zeroBSCRM_getCustomerFiles( $contact['id'] );
1185        }
1186
1187        ?>
1188                <table class="form-table wh-metatab wptbp" id="wptbpMetaBoxMainItemFiles">
1189
1190                <?php
1191
1192                #} Whole file delete method could do with rewrite
1193                #} Also sort JS into something usable - should be ajax all this
1194
1195                #} Any existing
1196                if ( is_array( $zbsFiles ) && count( $zbsFiles ) > 0 ) {
1197                    ?>
1198                        <tr class="wh-large zbsFileDetails"><th class="zbsFilesTitle"><label><?php echo '<span>' . count( $zbsFiles ) . '</span> ' . esc_html__( 'File(s)', 'zero-bs-crm' ) . ':'; ?></label></th>
1199                                <td id="zbsFileWrapOther">
1200                                    <table class="ui celled table" id="zbsFilesTable">
1201                                        <thead>
1202                                        <tr>
1203                                            <th><?php esc_html_e( 'File', 'zero-bs-crm' ); ?></th>
1204                                            <th class="collapsing center aligned"><?php esc_html_e( 'Actions', 'zero-bs-crm' ); ?></th>
1205                                        </tr>
1206                                    </thead><tbody>
1207                                            <?php
1208                                                $fileLineIndx = 1; foreach ( $zbsFiles as $zbsFile ) {
1209
1210                                                    /*
1211                                                    $file = basename($zbsFile['file']);
1212
1213                                                    // if in privatised system, ignore first hash in name
1214                                                    if (isset($zbsFile['priv'])){
1215
1216                                                        $file = substr($file,strpos($file, '-')+1);
1217                                                    } */
1218                                                    $file = zeroBSCRM_files_baseName( $zbsFile['file'], isset( $zbsFile['priv'] ) );
1219
1220                                                    $fileEditUrl = admin_url( 'admin.php?page=' . $zbs->slugs['editfile'] ) . '&customer=' . $contact['id'] . '&fileid=' . ( $fileLineIndx - 1 );
1221
1222                                                    echo '<tr class="zbsFileLineTR" id="zbsFileLineTRCustomer' . esc_attr( $fileLineIndx ) . '">';
1223                                                    echo '<td><div class="zbsFileLine" id="zbsFileLineCustomer' . esc_attr( $fileLineIndx ) . '"><a href="' . esc_url( $zbsFile['url'] ) . '" target="_blank">' . esc_html( $file ) . '</a></div>';
1224
1225                                                    // if using portal.. state shown/hidden
1226                                                    // this is also shown in each file slot :) if you change any of it change that too
1227                                                if ( defined( 'ZBS_CLIENTPRO_TEMPLATES' ) ) {
1228                                                    if ( isset( $zbsFile['portal'] ) && $zbsFile['portal'] ) {
1229                                                        echo "<p><i class='icon check circle green inverted'></i> " . esc_html__( 'Shown on Portal', 'zero-bs-crm' ) . '</p>';
1230                                                    } else {
1231                                                        echo "<p><i class='icon ban inverted red'></i> " . esc_html__( 'Not shown on Portal', 'zero-bs-crm' ) . '</p>';
1232                                                    }
1233                                                }
1234
1235                                                    echo '</td>';
1236                                                    echo '<td class="collapsing center aligned"><span class="zbsDelFile ui button basic" data-delurl="' . esc_attr( $zbsFile['url'] ) . '"><i class="trash alternate icon"></i> ' . esc_html__( 'Delete', 'zero-bs-crm' ) . '</span> <a href="' . esc_url( $fileEditUrl ) . '" target="_blank" class="ui button basic"><i class="edit icon"></i> ' . esc_html__( 'Edit', 'zero-bs-crm' ) . '</a></td></tr>';
1237                                                    ++$fileLineIndx;
1238
1239                                                }
1240                                                ?>
1241                                    </tbody></table>
1242                                </td></tr>
1243                                <?php
1244
1245                }
1246                ?>
1247
1248                    <?php
1249                    #adapted from http://code.tutsplus.com/articles/attaching-files-to-your-posts-using-wordpress-custom-meta-boxes-part-1--wp-22291
1250
1251                        $html .= '<input type="file" id="zbsc_file_attachment" name="zbsc_file_attachment" size="25" class="zbs-dc">';
1252
1253                    ?>
1254                            <tr class="wh-large"><th><label><?php esc_html_e( 'Add File', 'zero-bs-crm' ); ?>:</label><br />(<?php esc_html_e( 'Optional', 'zero-bs-crm' ); ?>)<br /><?php esc_html_e( 'Accepted File Types', 'zero-bs-crm' ); ?>:<br /><?php echo esc_html( zeroBS_acceptableFileTypeListStr() ); ?></th>
1255                                <td>
1256                                <?php
1257                                wp_nonce_field( plugin_basename( __FILE__ ), 'zbsc_file_attachment_nonce' );
1258                                echo $html;
1259                                ?>
1260                    </td></tr>
1261
1262                
1263                </table>
1264                <?php
1265
1266                // PerfTest: zeroBSCRM_performanceTest_finishTimer('custmetabox');
1267                // PerfTest: zeroBSCRM_performanceTest_debugOut();
1268
1269                ?>
1270                <script type="text/javascript">
1271
1272                    var zbsCustomerCurrentlyDeleting = false;
1273                    var zbsMetaboxFilesLang = {
1274
1275                        'error': '<?php echo esc_html( zeroBSCRM_slashOut( __( 'Error', 'zero-bs-crm' ) ) ); ?>',
1276                        'unabletodelete': '<?php echo esc_html( zeroBSCRM_slashOut( __( 'Unable to delete this file.', 'zero-bs-crm' ) ) ); ?>'
1277                    };
1278
1279                    jQuery(function(){
1280
1281                        jQuery('.zbsDelFile').on( 'click', function(){
1282
1283                            if (!window.zbsCustomerCurrentlyDeleting){
1284
1285                                // blocking
1286                                window.zbsCustomerCurrentlyDeleting = true;
1287
1288                                var delUrl = jQuery(this).attr('data-delurl');
1289                                //var lineIDtoRemove = jQuery(this).closest('.zbsFileLine').attr('id');
1290                                var lineToRemove = jQuery(this).closest('tr');
1291
1292                                if (typeof delUrl != "undefined" && delUrl != ''){
1293
1294
1295
1296                                        // postbag!
1297                                        var data = {
1298                                        'action': 'delFile',
1299                                        'zbsfType': 'customer',
1300                                        'zbsDel':  delUrl, // could be csv, never used though
1301                                        'zbsCID': 
1302                                        <?php
1303                                        if ( ! empty( $contact['id'] ) && $contact['id'] > 0 ) {
1304                                            echo esc_html( $contact['id'] );
1305                                        } else {
1306                                            echo -1;
1307                                        }
1308                                        ?>
1309                                                    ,
1310                                        'sec': window.zbscrmjs_secToken
1311                                        };
1312
1313                                        // Send it Pat :D
1314                                        jQuery.ajax({
1315                                                type: "POST",
1316                                                url: ajaxurl, // admin side is just ajaxurl not wptbpAJAX.ajaxurl,
1317                                                "data": data,
1318                                                dataType: 'json',
1319                                                timeout: 20000,
1320                                                success: function(response) {
1321
1322                                                var localLineToRemove = lineToRemove, localDelURL = delUrl;
1323
1324                                                // visually remove
1325                                                jQuery(localLineToRemove).remove();
1326
1327                                                // update number
1328                                                var newNumber = jQuery('#zbsFilesTable tr').length-1;
1329                                                if (newNumber > 0)
1330                                                    jQuery('#wptbpMetaBoxMainItemFiles .zbsFilesTitle span').html();
1331                                                else
1332                                                    jQuery('#wptbpMetaBoxMainItemFiles .zbsFileDetails').remove();
1333
1334
1335                                                // remove any filled slots (with this file) 
1336                                                jQuery('.zbsFileSlotTable').each(function(ind,ele){
1337
1338                                                    if (jQuery(ele).attr('data-sloturl') == localDelURL){
1339
1340                                                        jQuery('.zbsFileSlotWrap',jQuery(ele)).remove();
1341                                                
1342                                                    }
1343
1344                                                });
1345
1346                                                // file deletion errors, show msg:
1347                                                if (typeof response.errors != "undefined" && response.errors.length > 0){
1348
1349                                                    jQuery.each(response.errors,function(ind,ele){
1350
1351                                                        jQuery('#zerobs-customer-files-box').append('<div class="ui warning message" style="margin-top:10px;">' + ele + '</div>');
1352
1353                                                    });
1354                                                         
1355
1356                                                }
1357
1358
1359                                                },
1360                                                error: function(response){
1361
1362                                                jQuery('#zerobs-customer-files-box').append('<div class="ui warning message" style="margin-top:10px;"><strong>' + window.zbsMetaboxFilesLang.error + ':</strong> ' + window.zbsMetaboxFilesLang.unabletodelete + '</div>');
1363
1364                                                }
1365
1366                                            });
1367
1368                                }
1369
1370                                window.zbsCustomerCurrentlyDeleting = false;
1371
1372                            } // / blocking
1373
1374                        });
1375
1376                    });
1377
1378
1379                </script>
1380                <?php
1381
1382                // PerfTest: zeroBSCRM_performanceTest_finishTimer('other');
1383    }
1384
1385    public function save_data( $contact_id, $contact ) {
1386
1387        global $zbsc_justUploadedCustomer;
1388
1389        if ( ! empty( $_FILES['zbsc_file_attachment']['name'] ) &&
1390            ( ! isset( $zbsc_justUploadedCustomer ) ||
1391                ( isset( $zbsc_justUploadedCustomer ) && $zbsc_justUploadedCustomer != $_FILES['zbsc_file_attachment']['name'] )
1392            )
1393            ) {
1394
1395            /* --- security verification --- */
1396            if ( ! wp_verify_nonce( $_POST['zbsc_file_attachment_nonce'], plugin_basename( __FILE__ ) ) ) {
1397                return $id;
1398            } // end if
1399
1400            if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
1401                return $id;
1402            } // end if
1403
1404            if ( ! zeroBSCRM_permsCustomers() ) {
1405                return $contact_id;
1406            }
1407            /* - end security verification - */
1408
1409            // Blocking repeat-upload bug
1410            $zbsc_justUploadedCustomer = $_FILES['zbsc_file_attachment']['name'];
1411
1412            // verify file extension and mime type
1413            if ( jpcrm_file_check_mime_extension( $_FILES['zbsc_file_attachment'] ) ) {
1414
1415                $upload = wp_upload_bits( $_FILES['zbsc_file_attachment']['name'], null, file_get_contents( $_FILES['zbsc_file_attachment']['tmp_name'] ) );
1416
1417                if ( isset( $upload['error'] ) && $upload['error'] != 0 ) {
1418                    wp_die( 'There was an error uploading your file. The error is: ' . esc_html( $upload['error'] ) );
1419                } else {
1420                    // update_post_meta($id, 'zbsc_file_attachment', $upload);
1421
1422                        // v2.13 - also privatise the file (move to our asset store)
1423                        // $upload will have 'file' and 'url'
1424                        $fileName        = basename( $upload['file'] );
1425                        $fileDir         = dirname( $upload['file'] );
1426                        $privateThatFile = zeroBSCRM_privatiseUploadedFile( $fileDir, $fileName );
1427                    if ( is_array( $privateThatFile ) && isset( $privateThatFile['file'] ) ) {
1428
1429                        // successfully moved to our store
1430
1431                            // modify URL + file attributes
1432                            $upload['file'] = $privateThatFile['file'];
1433                            $upload['url']  = $privateThatFile['url'];
1434
1435                            // add this extra identifier if in privatised sys
1436                            $upload['priv'] = true;
1437
1438                    } else {
1439
1440                        // couldn't move to store, leave in uploaded for now :)
1441
1442                    }
1443
1444                        // w mod - adds to array :)
1445                        $zbsCustomerFiles = zeroBSCRM_getCustomerFiles( $contact_id );
1446
1447                    if ( is_array( $zbsCustomerFiles ) ) {
1448
1449                        // add it
1450                        $zbsCustomerFiles[] = $upload;
1451
1452                    } else {
1453
1454                        // first
1455                        $zbsCustomerFiles = array( $upload );
1456
1457                    }
1458
1459                        // update_post_meta($id, 'zbs_customer_files', $zbsCustomerFiles);
1460                        zeroBSCRM_updateCustomerFiles( $contact_id, $zbsCustomerFiles );
1461
1462                        // Fire any 'post-upload-processing' (e.g. CPP makes thumbnails of pdf, jpg, etc.)
1463                        do_action( 'zbs_post_upload_contact', $upload );
1464                }
1465            } else {
1466                wp_die( "The file type that you've uploaded is not an accepted file format." );
1467            }
1468        }
1469
1470        return $contact;
1471    }
1472}
1473
1474/*
1475======================================================
1476    / Attach files to customer metabox
1477    ====================================================== */
1478
1479/*
1480======================================================
1481    Create Client Portal
1482    ====================================================== */
1483
1484class zeroBS__Metabox_ContactPortal extends zeroBS__Metabox {
1485
1486    public function __construct( $plugin_file, $metabox_screen = 'zbs-add-edit-contact-edit' ) {
1487
1488        $this->objType         = 'contact';
1489        $this->metaboxID       = 'zerobs-customer-portal';
1490        $this->metaboxTitle    = __( 'Client Portal', 'zero-bs-crm' );
1491        $this->metaboxScreen   = $metabox_screen; // we can use anything here as is now using our func
1492        $this->metaboxArea     = 'side';
1493        $this->metaboxLocation = 'high';
1494        $this->capabilities    = array(
1495
1496            'can_hide'        => true, // can be hidden
1497            'areas'           => array( 'side' ), // areas can be dragged to - normal side = only areas currently
1498            'can_accept_tabs' => false,  // can/can't accept tabs onto it
1499            'can_become_tab'  => false, // can be added as tab
1500            'can_minimise'    => true, // can be minimised
1501
1502        );
1503
1504        // call this
1505        $this->initMetabox();
1506    }
1507
1508    public function html( $contact, $metabox ) {
1509
1510        // PerfTest: zeroBSCRM_performanceTest_startTimer('portal-draw');
1511
1512        global $plugin_page, $zbs;
1513        $screen = get_current_screen();
1514
1515        $wp_user_id = '';
1516        #} Rather than reload all the time :)
1517        global $zbsContactEditing;
1518
1519        #} retrieve
1520        // $zbsCustomer = get_post_meta($contact['id'], 'zbs_customer_meta', true);
1521        if ( ! isset( $zbsContactEditing ) && isset( $contact['id'] ) ) {
1522            $zbsCustomer       = zeroBS_getCustomer( $contact['id'], false, false, false );
1523            $zbsContactEditing = $zbsCustomer;
1524        } else {
1525            $zbsCustomer = $zbsContactEditing;
1526        }
1527
1528        if ( isset( $zbsCustomer ) && is_array( $zbsCustomer ) && isset( $zbsCustomer['email'] ) ) {
1529
1530            // check customer link to see if it exists - wh moved to dal
1531            $wp_user_id = zeroBSCRM_getClientPortalUserID( $contact['id'] );
1532
1533            /*
1534            nope
1535            if($wp_user_id == ''){
1536                $wp_user_id = email_exists( $zbsCustomer['email'] );
1537            } */
1538        }
1539
1540        echo '<div class="waiting-togen">';
1541
1542        // get user obj
1543        $user_object = get_userdata( $wp_user_id );
1544
1545        if ( $user_object ) {
1546
1547            // a user already exists with this email
1548
1549            echo '<div class="zbs-customerportal-activeuser">';
1550
1551                esc_html_e( 'WordPress User Linked', 'zero-bs-crm' );
1552                echo ' #<span class="zbs-user-id">' . esc_html( $wp_user_id ) . '</span>:<br />';
1553
1554                echo '<span class="ui label">' . esc_html( $user_object->user_email ) . '</span>';
1555
1556                // wp admins get link
1557            if ( zeroBSCRM_isWPAdmin() ) {
1558
1559                $url = admin_url( 'user-edit.php?user_id=' . $wp_user_id );
1560                        echo '<br /><a style="font-size: 12px;color:black;font-weight:600;" href="' . esc_url( $url ) . '" target="_blank"><i class="wordpress simple icon"></i> ' . esc_html__( 'View WordPress Profile', 'zero-bs-crm' ) . '</a>'; // phpcs:ignore WordPress.WP.CapitalPDangit.MisspelledInText
1561
1562            }
1563
1564            echo '</div>';
1565
1566            // user ID will now have access to this area..
1567            echo '<hr /><div class="zbs-customerportal-activeuser-actions">';
1568
1569                echo esc_html( __( 'Client Portal Access:', 'zero-bs-crm' ) );
1570
1571                $customerPortalActive = true;
1572            if ( zeroBSCRM_isCustomerPortalDisabled( $contact['id'] ) ) {
1573                $customerPortalActive = false;
1574            }
1575
1576            if ( $customerPortalActive ) {
1577
1578                // revoke/disable access
1579                echo ' <span class="ui green empty circular label"></span> <span class="zbs-portal-label">' . esc_html( __( 'Enabled', 'zero-bs-crm' ) ) . '</span>';
1580
1581                // wp admins get reset link, unless the crm contact is assigned to any other role than CRM Customer
1582                if ( zeroBSCRM_isWPAdmin() && jpcrm_role_check( $user_object, array(), array(), array( 'zerobs_customer' ) ) ) {
1583
1584                    echo '<div id="zbs-customerportal-access-actions" class="zbs-customerportal-activeuser">';
1585
1586                            echo '<button type="button" id="zbs-customerportal-resetpw" class="ui mini button white">' . esc_html( __( 'Reset Password', 'zero-bs-crm' ) ) . '</button>';
1587
1588                            echo '<button type="button" id="zbs-customerportal-toggle" data-zbsportalaction="disable" class="ui mini button white negative">' . esc_html( __( 'Disable Access', 'zero-bs-crm' ) ) . '</button>';
1589
1590                    echo '</div>';
1591
1592                } else {
1593
1594                    // explainer - rarely shown
1595                    echo '<p style="font-size: 0.9em;margin-top: 0.5em;">' . esc_html__( 'The WordPress user has a role other than CRM Contact. They will need to reset their password via the WP login page.', 'zero-bs-crm' ) . '</p>';
1596
1597                }
1598
1599                echo '<hr /><div class="zbs-customerportal-activeuser-actions">';
1600                        printf( '<a target="_blank" href="%s" class="ui mini button white">%s</a>', esc_url( zeroBS_portal_link() ), esc_html( __( 'Preview Portal', 'zero-bs-crm' ) ) );
1601                echo '</div>';
1602            } else {
1603
1604                // enable access
1605                echo ' <span class="ui red empty circular label"></span> <span class="zbs-portal-label">' . esc_html( __( 'Disabled', 'zero-bs-crm' ) ) . '</span>';
1606
1607                // wp admins get enable link, unless the crm contact is assigned to any other role than CRM Customer
1608                if ( zeroBSCRM_isWPAdmin() && jpcrm_role_check( $user_object, array(), array(), array( 'zerobs_customer' ) ) ) {
1609
1610                    echo '<div id="zbs-customerportal-access-actions">';
1611                        echo '<button type="button" id="zbs-customerportal-toggle" data-zbsportalaction="enable" class="ui mini button positive">' . esc_html( __( 'Enable Access', 'zero-bs-crm' ) ) . '</button>';
1612                    echo '</div>';
1613
1614                }
1615            }
1616
1617                echo '<input type="hidden" id="zbsportalaction-ajax-nonce" value="' . esc_attr( wp_create_nonce( 'zbsportalaction-ajax-nonce' ) ) . '" />';
1618
1619            echo '</div>';
1620
1621        } elseif ( is_array( $zbsCustomer ) && isset( $zbsCustomer['email'] ) && ! empty( $zbsCustomer['email'] ) ) {
1622            echo '<div class="no-gen" style="text-align:center">';
1623            echo esc_html( __( 'No WordPress User exists with this email', 'zero-bs-crm' ) );
1624            echo '<br/><br/>';
1625                echo '<div class="ui primary black button button-primary wp-user-generate">';
1626            echo esc_html( __( 'Generate WordPress User', 'zero-bs-crm' ) );
1627            echo '</div>';
1628            echo '<input type="hidden" name="newwp-ajax-nonce" id="newwp-ajax-nonce" value="' . esc_attr( wp_create_nonce( 'newwp-ajax-nonce' ) ) . '" />';
1629            echo '</div>';
1630        } else {
1631            echo esc_html( __( 'Save your contact, or add an email to enable Client Portal functionality', 'zero-bs-crm' ) );
1632        }
1633
1634        echo '</div>';
1635
1636        ?>
1637        <script type="text/javascript">
1638
1639            jQuery(function(){
1640
1641                // bind activate/deactivate
1642                jQuery('#zbs-customerportal-toggle').off("click").on('click',function(e){
1643
1644                    // action
1645                    var action = jQuery(this).attr('data-zbsportalaction');
1646
1647                    // fire ajax
1648                    var t = {
1649                        action: "zbsPortalAction",
1650                        portalAction: action,
1651                        cid: 
1652                        <?php
1653                        if ( ! empty( $contact['id'] ) && $contact['id'] > 0 ) {
1654                            echo esc_html( $contact['id'] );
1655                        } else {
1656                            echo -1;
1657                        }
1658                        ?>
1659                            ,
1660                        security: jQuery( '#zbsportalaction-ajax-nonce' ).val()
1661                    }
1662                    i = jQuery.ajax({
1663                        url: ajaxurl,
1664                        type: "POST",
1665                        data: t,
1666                        dataType: "json"
1667                    });
1668                    i.done(function(e) {
1669                        //console.log(e);
1670                        if(typeof e.success != "undefined"){
1671
1672                            // localise
1673                            var cAction = action;
1674
1675                            if (action == 'enable'){
1676
1677                                // switch label
1678                                jQuery('.ui.circular.label',jQuery('.zbs-customerportal-activeuser-actions')).removeClass('red').addClass('green');
1679                                jQuery('.zbs-portal-label',jQuery('.zbs-customerportal-activeuser-actions')).html('<?php echo esc_html( __( 'Enabled', 'zero-bs-crm' ) ); ?>');
1680                                jQuery('#zbs-customerportal-toggle').removeClass('positive').addClass('negative').html('<?php echo esc_html( __( 'Disable Access', 'zero-bs-crm' ) ); ?>').attr('data-zbsportalaction','disable');
1681                                
1682
1683                            } else if (action == 'disable'){
1684
1685                                // switch label
1686                                jQuery('.ui.circular.label',jQuery('.zbs-customerportal-activeuser-actions')).addClass('red').removeClass('green');
1687                                jQuery('.zbs-portal-label',jQuery('.zbs-customerportal-activeuser-actions')).html('<?php echo esc_html( __( 'Disabled', 'zero-bs-crm' ) ); ?>');
1688                                jQuery('#zbs-customerportal-toggle').removeClass('negative').addClass('positive').html('<?php echo esc_html( __( 'Enable Access', 'zero-bs-crm' ) ); ?>').attr('data-zbsportalaction','enable');
1689                                
1690
1691                            }
1692
1693                        }
1694                    }), i.fail(function(e) {
1695                        //error
1696                    });
1697
1698                });
1699
1700                // bind reset pw
1701                jQuery('#zbs-customerportal-resetpw').off("click").on('click',function(e){
1702
1703                    // fire ajax
1704                    var t = {
1705                        action: "zbsPortalAction",
1706                        portalAction: 'resetpw',
1707                        cid: 
1708                        <?php
1709                        if ( ! empty( $contact['id'] ) && $contact['id'] > 0 ) {
1710                            echo esc_html( $contact['id'] );
1711                        } else {
1712                            echo -1;
1713                        }
1714                        ?>
1715                            ,
1716                        security: jQuery( '#zbsportalaction-ajax-nonce' ).val()
1717                    }
1718                    i = jQuery.ajax({
1719                        url: ajaxurl,
1720                        type: "POST",
1721                        data: t,
1722                        dataType: "json"
1723                    });
1724                    i.done(function(e) {
1725                        //console.log(e);
1726                        if(typeof e.success != "undefined"){
1727
1728                            var newPassword =  '<?php zeroBSCRM_slashOut( esc_html__( 'Unknown', 'zero-bs-crm' ) ); ?>';
1729                            if (typeof e.pw != "undefined") newPassword = e.pw;
1730
1731                            if ( newPassword !== false ){
1732
1733                                // swal confirm
1734                                swal(
1735                                    '<?php zeroBSCRM_slashOut( esc_html__( 'Client Portal Password Reset', 'zero-bs-crm' ) ); ?>',
1736                                    '<?php zeroBSCRM_slashOut( esc_html__( 'Client Portal password has been reset for this contact, and they have been emailed with the new password. The new password is:', 'zero-bs-crm' ) ); ?><br /><span class="ui label">' + newPassword + '</span>',
1737                                    'info'
1738                                );
1739
1740                            } else {
1741
1742                                // swal confirm
1743                                swal(
1744                                    '<?php zeroBSCRM_slashOut( esc_html__( 'Client Portal Password Reset Error', 'zero-bs-crm' ) ); ?>',
1745                                    '<?php zeroBSCRM_slashOut( esc_html__( 'Error: Client Portal password has not been reset for this contact.', 'zero-bs-crm' ) ); ?>',
1746                                    'info'
1747                                );
1748
1749                            }
1750
1751
1752                        }
1753                    }), i.fail(function(e) {
1754                        //error
1755                    });
1756
1757                });
1758
1759
1760                // bind create
1761                jQuery('.wp-user-generate').off("click").on('click',function(e){
1762                    email = jQuery('#email').val();
1763                    customerid = 
1764                    <?php
1765                    if ( ! empty( $contact['id'] ) && $contact['id'] > 0 ) {
1766                        echo esc_html( $contact['id'] );
1767                    } else {
1768                        echo -1;
1769                    }
1770                    ?>
1771                                ;
1772                    if(email == ''){
1773                        alert("The email field is blank. Please fill in the email and save");
1774                        return false;
1775                    }
1776                    var t = {
1777                        action: "zbs_new_user",
1778                        email: email,
1779                        cid: customerid,
1780                        security: jQuery( '#newwp-ajax-nonce' ).val(),
1781                    }                    
1782                    i = jQuery.ajax({
1783                        url: ajaxurl,
1784                        type: "POST",
1785                        data: t,
1786                        dataType: "json"
1787                    });
1788                    i.done(function(e) {
1789                        console.log(e);
1790                        if(e.success){
1791                            jQuery('.zbs-user-id').html(e.user_id);
1792                            jQuery('.no-gen').remove();
1793                            jQuery('.waiting-togen').html('<div class="alert alert-success">Success: ' + e.message + '</div>');
1794                        } else {
1795                            jQuery('.no-gen').remove();
1796                            jQuery('.waiting-togen').html('<div class="alert alert-danger">Error: ' + e.message + '</div>');
1797                        }
1798                    }), i.fail(function(e) {
1799                        //error
1800                    });
1801                });
1802
1803            });
1804
1805
1806        </script>
1807        <?php
1808
1809        // PerfTest: zeroBSCRM_performanceTest_finishTimer('portal-draw');
1810        // PerfTest: zeroBSCRM_performanceTest_finishTimer('portal');
1811    }
1812}
1813
1814/*
1815======================================================
1816    / Create Client Portal
1817    ====================================================== */
1818
1819/*
1820======================================================
1821    Create Social Box
1822    ====================================================== */
1823
1824class zeroBS__Metabox_ContactSocial extends zeroBS__Metabox {
1825
1826    public function __construct( $plugin_file ) {
1827
1828        $this->objType         = 'contact';
1829        $this->metaboxID       = 'zerobs-customer-social';
1830        $this->metaboxTitle    = __( 'Social Profiles', 'zero-bs-crm' );
1831        $this->metaboxScreen   = 'zbs-add-edit-contact-edit'; // 'zerobs_edit_contact'; // we can use anything here as is now using our func
1832        $this->metaboxArea     = 'side';
1833        $this->metaboxLocation = 'high';
1834        $this->capabilities    = array(
1835
1836            'can_hide'        => true, // can be hidden
1837            'areas'           => array( 'side' ), // areas can be dragged to - normal side = only areas currently
1838            'can_accept_tabs' => false,  // can/can't accept tabs onto it
1839            'can_become_tab'  => false, // can be added as tab
1840            'can_minimise'    => true, // can be minimised
1841
1842        );
1843
1844        // call this
1845        $this->initMetabox();
1846    }
1847
1848    public function html( $contact, $metabox ) {
1849
1850        global $plugin_page, $zbs;
1851
1852        // declare + load existing
1853        global $zbsSocialAccountTypes;
1854        $zbsSocials = false;
1855        if ( isset( $contact['id'] ) ) {
1856            $zbsSocials = $zbs->DAL->contacts->getContactSocials( $contact['id'] ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase,WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
1857        }
1858
1859        if ( count( $zbsSocialAccountTypes ) > 0 ) {
1860            foreach ( $zbsSocialAccountTypes as $socialKey => $socialAccType ) {
1861
1862                ?>
1863            <div class="zbs-social-acc <?php echo esc_attr( $socialAccType['slug'] ); ?>" title="<?php echo esc_attr( $socialAccType['name'] ); ?>">
1864                <?php
1865                if ( is_array( $zbsSocials ) && isset( $zbsSocials[ $socialKey ] ) && ! empty( $zbsSocials[ $socialKey ] ) ) {
1866
1867                    // got acc? link to it
1868                    $socialLink = zeroBSCRM_getSocialLink( $socialKey, $zbsSocials );
1869
1870                    ?>
1871                    <a href="<?php echo esc_url( $socialLink ); ?>" target="_blank" title="<?php echo esc_attr__( 'View', 'zero-bs-crm' ) . ' ' . esc_attr( $socialAccType['name'] ); ?>"><i class="fa <?php echo esc_attr( $socialAccType['fa'] ); ?>" aria-hidden="true"></i></a>
1872                <?php } else { ?>
1873                    <i class="fa <?php echo esc_attr( $socialAccType['fa'] ); ?>" aria-hidden="true"></i>
1874                <?php } ?>
1875                <input type="text" class="zbs-social-acc-input zbs-dc" title="<?php echo esc_attr( $socialAccType['name'] ); ?>" name="zbs-social-<?php echo esc_attr( $socialAccType['slug'] ); ?>" id="zbs-social-<?php echo esc_attr( $socialAccType['slug'] ); ?>" value="
1876                <?php
1877                if ( is_array( $zbsSocials ) && isset( $zbsSocials[ $socialKey ] ) && ! empty( $zbsSocials[ $socialKey ] ) ) {
1878                    echo esc_attr( $zbsSocials[ $socialKey ] );}
1879                ?>
1880                " placeholder="<?php echo esc_attr( $socialAccType['placeholder'] ); ?>" />
1881            </div>
1882                            <?php
1883
1884            }
1885        }
1886
1887        // ++ get counts etc.
1888    }
1889
1890    public function save_data( $contact_id, $contact ) {
1891
1892            $zbsSocials = array();
1893
1894            global $zbsSocialAccountTypes;
1895        foreach ( $zbsSocialAccountTypes as $socialKey => $socialAccType ) {
1896
1897            // set
1898            $zbsSocials[ $socialKey ] = false;
1899
1900            // get from post if present
1901            if ( isset( $_POST[ 'zbs-social-' . $socialAccType['slug'] ] ) && ! empty( $_POST[ 'zbs-social-' . $socialAccType['slug'] ] ) ) {
1902                $zbsSocials[ $socialKey ] = sanitize_text_field( $_POST[ 'zbs-social-' . $socialAccType['slug'] ] );
1903            }
1904        }
1905
1906            zeroBS_updateCustomerSocialAccounts( $contact_id, $zbsSocials );
1907
1908        return $contact;
1909    }
1910}
1911
1912/*
1913======================================================
1914    / Create Social Box
1915    ====================================================== */
1916
1917/*
1918======================================================
1919    Create AKA Box
1920    ====================================================== */
1921
1922class zeroBS__Metabox_ContactAKA extends zeroBS__Metabox {
1923
1924    public function __construct( $plugin_file ) {
1925
1926        $this->objType         = 'contact';
1927        $this->metaboxID       = 'zerobs-customer-aka';
1928        $this->metaboxTitle    = __( 'Contact Aliases (AKA)', 'zero-bs-crm' );
1929        $this->metaboxScreen   = 'zbs-add-edit-contact-edit'; // 'zerobs_edit_contact'; // we can use anything here as is now using our func
1930        $this->metaboxArea     = 'side';
1931        $this->metaboxLocation = 'low';
1932        $this->capabilities    = array(
1933
1934            'can_hide'        => true, // can be hidden
1935            'areas'           => array( 'side' ), // areas can be dragged to - normal side = only areas currently
1936            'can_accept_tabs' => false,  // can/can't accept tabs onto it
1937            'can_become_tab'  => false, // can be added as tab
1938            'can_minimise'    => true, // can be minimised
1939
1940        );
1941
1942        // call this
1943        $this->initMetabox();
1944    }
1945
1946    public function html( $contact, $metabox ) {
1947
1948        global $plugin_page, $zbs;
1949        $screen = get_current_screen();
1950        ?>
1951        <div class="ui active inverted dimmer" style="display:none" id="zbs-aka-alias-loader"></div>
1952        <?php
1953
1954        #} Rather than reload all the time :)
1955        global $zbsContactEditing;
1956
1957        #} retrieve
1958        // $zbsCustomer = get_post_meta($contact['id'], 'zbs_customer_meta', true);
1959        if ( ! isset( $zbsContactEditing ) && isset( $contact['id'] ) ) {
1960            $zbsCustomer       = zeroBS_getCustomer( $contact['id'], false, false, false );
1961            $zbsContactEditing = $zbsCustomer;
1962        } else {
1963            $zbsCustomer = $zbsContactEditing;
1964        }
1965
1966        if ( gettype( $zbsCustomer ) != 'array' ) {
1967
1968            // new cust, can't add till saved.
1969            ?>
1970            <div class="ui message"><?php esc_html_e( 'You will not be able to add an alias until you\'ve saved this contact', 'zero-bs-crm' ); ?></div>
1971            <?php
1972
1973        } else {
1974
1975            // customer saved, so proceed - aka mode
1976
1977            // declare + load existing
1978            $customerAliases = zeroBS_getCustomerAliases( $contact['id'] );
1979
1980            ?>
1981            <div id="zbs-aka-alias-wrap">
1982            <?php
1983
1984            // each alias: ID,aka_alias,aka_create,aka_lastupdated
1985            if ( is_array( $customerAliases ) && count( $customerAliases ) > 0 ) {
1986                foreach ( $customerAliases as $alias ) {
1987
1988                    ?>
1989                <div class="zbs-aka-alias" id="zbs-aka-alias-<?php echo esc_attr( $alias['ID'] ); ?>">
1990                    <div class="ui label"><?php echo esc_html( $alias['aka_alias'] ); ?> <button type="button" class="ui mini icon button negative zbs-aka-alias-remove" data-akaid="<?php echo esc_attr( $alias['ID'] ); ?>" title="<?php esc_attr_e( 'Remove Alias', 'zero-bs-crm' ); ?>"><i class="icon remove"></i></button></div>
1991                </div>
1992                    <?php
1993
1994                }
1995            }
1996
1997            ?>
1998            </div><div id="zbs-aka-alias-input-wrap">
1999                <input type="text" class="zbs-aka-alias-input" placeholder="<?php esc_attr_e( 'Add Alias.. e.g.', 'zero-bs-crm' ); ?> mike2@domain.com" />
2000                <div class="ui pointing label" style="display:none;margin-bottom: 1em;margin-top: 0;" id="zbs-aka-alias-input-msg"><?php esc_html_e( 'Must be a valid email', 'zero-bs-crm' ); ?></div>
2001                        <button type="button" class="ui small black button primary" id="zbs-aka-alias-add"><?php esc_html_e( 'Add Alias', 'zero-bs-crm' ); ?></button>
2002            </div>
2003
2004            <script type="text/javascript">
2005            var zbsAliasAKABlocker = false;
2006            jQuery(function(){
2007
2008                jQuery('.zbs-aka-alias-input').on( 'keydown', function(){
2009                            
2010                    // hide 'must be valid email'
2011                    jQuery('#zbs-aka-alias-input-msg').hide();
2012
2013                });
2014
2015                jQuery('#zbs-aka-alias-add').off('click').on( 'click', function(){
2016
2017                    var v = jQuery('.zbs-aka-alias-input').val();
2018                    
2019                    if ( typeof v === 'string' ) {
2020                        v = v.trim();
2021                    }
2022
2023                    // lazy check for now
2024                    if (v != "" && zbscrm_JS_validateEmail(v)){
2025
2026                        // blocker
2027                        if (!window.zbsAliasAKABlocker){
2028
2029                            // block
2030                            window.zbsAliasAKABlocker = true;
2031                            jQuery('#zbs-aka-alias-loader').show();
2032
2033                                        // postbag!
2034                                        var data = {
2035                                        'action': 'addAlias',
2036                                        'cid': 
2037                                        <?php
2038                                        if ( ! empty( $contact['id'] ) && $contact['id'] > 0 ) {
2039                                            echo esc_html( $contact['id'] );
2040                                        } else {
2041                                            echo -1;
2042                                        }
2043                                        ?>
2044                                                ,
2045                                        'aka': v,
2046                                        'sec': window.zbscrmjs_secToken
2047                                        };
2048
2049                                        // Send it Pat :D
2050                                        jQuery.ajax({
2051                                                type: "POST",
2052                                                url: ajaxurl, // admin side is just ajaxurl not wptbpAJAX.ajaxurl,
2053                                                "data": data,
2054                                                dataType: 'json',
2055                                                timeout: 20000,
2056                                                success: function(response) {
2057
2058                                                if (typeof response.res != "undefined"){
2059
2060                                                    //console.log('added:',response);
2061
2062                                                    var id = response.res;
2063                                                    var alias = v;
2064
2065                                                    var lineHTML = '<div class="zbs-aka-alias" id="zbs-aka-alias-' + id + '"><div class="ui label">' + alias + ' <button type="button" class=" ui mini icon button negative zbs-aka-alias-remove" data-akaid="' + id + '""><i class="icon remove"></i></button></div></div>';
2066
2067                                                    // add to ui
2068                                                    jQuery('#zbs-aka-alias-wrap').append(lineHTML);
2069
2070                                                    // empty this
2071                                                    jQuery('.zbs-aka-alias-input').val('');
2072
2073                                                    // bind
2074                                                    setTimeout(function(){
2075
2076                                                        zeroBSJS_bindAKAMode();
2077
2078                                                    },0);
2079
2080                                                    //unblock
2081                                                    window.zbsAliasAKABlocker = false;
2082                                                    jQuery('#zbs-aka-alias-loader').hide();
2083
2084                                                } else {
2085
2086                                                    if (typeof response.fail != "undefined"){
2087
2088                                                        if (response.fail == 'existing'){
2089
2090                                                            // already in use err
2091                                                            swal(
2092                                                                '<?php esc_html_e( 'Error', 'zero-bs-crm' ); ?>',
2093                                                                '<?php esc_html_e( 'This Alias is already in use by another contact.', 'zero-bs-crm' ); ?>',
2094                                                                'warning'
2095                                                            );
2096
2097                                                        }
2098
2099                                                    } else {
2100
2101                                                        // general err
2102                                                        swal(
2103                                                            '<?php esc_html_e( 'Error', 'zero-bs-crm' ); ?>',
2104                                                            '<?php esc_html_e( 'There was an error adding this alias', 'zero-bs-crm' ); ?>',
2105                                                            'warning'
2106                                                        );
2107
2108                                                    }
2109                                                    //unblock
2110                                                    window.zbsAliasAKABlocker = false;
2111                                                    jQuery('#zbs-aka-alias-loader').hide();
2112                                                }
2113
2114                                                },
2115                                                error: function(response){
2116
2117                                                    // err
2118                                                    swal(
2119                                                        '<?php esc_html_e( 'Error', 'zero-bs-crm' ); ?>',
2120                                                        '<?php esc_html_e( 'There was an error adding this alias', 'zero-bs-crm' ); ?>',
2121                                                        'warning'
2122                                                    );
2123                                                    //unblock
2124                                                    window.zbsAliasAKABlocker = false;
2125                                                    jQuery('#zbs-aka-alias-loader').hide();
2126
2127                                                }
2128
2129                                            });
2130
2131
2132                        } // / blocker
2133
2134                    } // / if not empty 
2135                    else {
2136
2137                        // not valid email, showxxx
2138                        jQuery('#zbs-aka-alias-input-msg').show();
2139
2140
2141
2142                        // hide after 2s
2143                        setTimeout(function(){
2144                            jQuery('#zbs-aka-alias-input-msg').hide();
2145                        },2000);
2146
2147                    }
2148
2149                });
2150
2151                
2152                // other bind
2153                zeroBSJS_bindAKAMode();
2154
2155
2156            });
2157    
2158            function zeroBSJS_bindAKAMode(){
2159
2160                // hover over
2161                jQuery('.zbs-aka-alias').on( 'mouseenter', function () {
2162                    jQuery(this).addClass("hovering");
2163                }).on( 'mouseleave', function () {
2164                    jQuery(this).removeClass("hovering");
2165                });
2166
2167                // remoe aka
2168                jQuery('.zbs-aka-alias-remove').off('click').on( 'click', function(){
2169
2170                    // blocker
2171                    if (!window.zbsAliasAKABlocker){
2172
2173                        // block
2174                        window.zbsAliasAKABlocker = true;
2175                        jQuery('#zbs-aka-alias-loader').show();
2176
2177                        // get id
2178                        var akaID = jQuery(this).attr('data-akaid');
2179
2180                        if (akaID > 0){
2181
2182
2183                                // postbag!
2184                                var data = {
2185                                'action': 'removeAlias',
2186                                'cid': 
2187                                <?php
2188                                if ( ! empty( $contact['id'] ) && $contact['id'] > 0 ) {
2189                                    echo esc_html( $contact['id'] );
2190                                } else {
2191                                    echo -1;
2192                                }
2193                                ?>
2194                                        ,
2195                                'akaid': akaID,
2196                                'sec': window.zbscrmjs_secToken
2197                                };
2198
2199                                // Send it Pat :D
2200                                jQuery.ajax({
2201                                        type: "POST",
2202                                        url: ajaxurl, // admin side is just ajaxurl not wptbpAJAX.ajaxurl,
2203                                        "data": data,
2204                                        dataType: 'json',
2205                                        timeout: 20000,
2206                                        success: function(response) {
2207
2208                                        if (typeof response.res != "undefined"){
2209
2210                                            console.log('removed:',response);
2211
2212                                            var lID = akaID;
2213
2214                                            // remove from ui
2215                                            jQuery('#zbs-aka-alias-' + lID).remove();
2216
2217                                            //unblock
2218                                            window.zbsAliasAKABlocker = false;
2219                                            jQuery('#zbs-aka-alias-loader').hide();
2220
2221                                        } else {
2222
2223                                            // err
2224                                            swal(
2225                                                '<?php esc_html_e( 'Error', 'zero-bs-crm' ); ?>',
2226                                                '<?php esc_html_e( 'There was an error removing this alias', 'zero-bs-crm' ); ?>',
2227                                                'warning'
2228                                            );
2229                                            //unblock
2230                                            window.zbsAliasAKABlocker = false;
2231                                            jQuery('#zbs-aka-alias-loader').hide();
2232                                        }
2233
2234                                        },
2235                                        error: function(response){
2236
2237                                            // err
2238                                            swal(
2239                                                '<?php esc_html_e( 'Error', 'zero-bs-crm' ); ?>',
2240                                                '<?php esc_html_e( 'There was an error removing this alias', 'zero-bs-crm' ); ?>',
2241                                                'warning'
2242                                            );
2243                                            //unblock
2244                                            window.zbsAliasAKABlocker = false;
2245                                            jQuery('#zbs-aka-alias-loader').hide();
2246
2247                                        }
2248
2249                                    });
2250
2251
2252                        } // / akai id present
2253
2254                    }
2255
2256                });
2257            }
2258
2259            </script>
2260            <?php
2261
2262        } // / if cust defined
2263    }
2264}
2265
2266/*
2267======================================================
2268    / Create AKA Box
2269    ====================================================== */
2270
2271/*
2272======================================================
2273    Create Tags Box
2274    ====================================================== */
2275
2276class zeroBS__Metabox_ContactTags extends zeroBS__Metabox_Tags {
2277
2278    public function __construct( $plugin_file ) {
2279
2280        $this->objTypeID       = ZBS_TYPE_CONTACT;
2281        $this->objType         = 'contact';
2282        $this->metaboxID       = 'zerobs-customer-tags';
2283        $this->metaboxTitle    = __( 'Contact Tags', 'zero-bs-crm' );
2284        $this->metaboxScreen   = 'zbs-add-edit-contact-edit'; // 'zerobs_edit_contact'; // we can use anything here as is now using our func
2285        $this->metaboxArea     = 'side';
2286        $this->metaboxLocation = 'high';
2287        $this->showSuggestions = true;
2288        $this->capabilities    = array(
2289
2290            'can_hide'        => true, // can be hidden
2291            'areas'           => array( 'side' ), // areas can be dragged to - normal side = only areas currently
2292            'can_accept_tabs' => false,  // can/can't accept tabs onto it
2293            'can_become_tab'  => false, // can be added as tab
2294            'can_minimise'    => true, // can be minimised
2295
2296        );
2297
2298        // call this
2299        $this->initMetabox();
2300    }
2301
2302    // html + save dealt with by parent class :)
2303}
2304
2305/*
2306======================================================
2307    / Create Tags Box
2308    ====================================================== */
2309
2310/*
2311======================================================
2312    Create Logs Box
2313    ====================================================== */
2314
2315class zeroBS__Metabox_ContactLogs extends zeroBS__Metabox_LogsV2 {
2316
2317    public function __construct( $plugin_file ) {
2318
2319        $this->objtypeid = ZBS_TYPE_CONTACT;
2320
2321        $this->objType         = 'contact';
2322        $this->metaboxID       = 'zerobs-customer-logs';
2323        $this->metaboxTitle    = __( 'Activity Log', 'zero-bs-crm' );
2324        $this->metaboxScreen   = 'zbs-add-edit-contact-edit'; // 'zerobs_edit_contact'; // we can use anything here as is now using our func
2325        $this->metaboxArea     = 'normal';
2326        $this->metaboxLocation = 'high';
2327        $this->capabilities    = array(
2328
2329            'can_hide'        => true, // can be hidden
2330            'areas'           => array( 'normal' ), // areas can be dragged to - normal side = only areas currently
2331            'can_accept_tabs' => true,  // can/can't accept tabs onto it
2332            'can_become_tab'  => true, // can be added as tab
2333            'can_minimise'    => true, // can be minimised
2334            'hide_on_new'     => true, // no point in adding logs while adding contact, add after creation :)
2335
2336        );
2337
2338        // call this
2339        $this->initMetabox();
2340    }
2341
2342    // html + save dealt with by parent class :)
2343}
2344
2345/*
2346======================================================
2347    / Create Logs Box
2348    ====================================================== */
2349
2350/*
2351======================================================
2352    "Contacts at Company" Metabox
2353    ====================================================== */
2354
2355class zeroBS__Metabox_ContactCompany extends zeroBS__Metabox {
2356
2357    public function __construct( $plugin_file ) {
2358
2359        # (language switch)
2360        $companyOrOrg = zeroBSCRM_getSetting( 'coororg' );
2361        $companyLabel = jpcrm_label_company();
2362
2363        $this->objType         = 'contact';
2364        $this->metaboxID       = 'zerobs-customer-company';
2365        $this->metaboxTitle    = __( $companyLabel, 'zero-bs-crm' );
2366        $this->metaboxScreen   = 'zbs-add-edit-contact-edit'; // 'zerobs_edit_contact'; // we can use anything here as is now using our func
2367        $this->metaboxArea     = 'side';
2368        $this->metaboxLocation = 'core';
2369        $this->capabilities    = array(
2370
2371            'can_hide'        => true, // can be hidden
2372            'areas'           => array( 'side' ), // areas can be dragged to - normal side = only areas currently
2373            'can_accept_tabs' => false,  // can/can't accept tabs onto it
2374            'can_become_tab'  => false, // can be added as tab
2375            'can_minimise'    => true, // can be minimised
2376
2377        );
2378
2379        // call this
2380        $this->initMetabox();
2381    }
2382
2383    public function html( $contact, $metabox ) {
2384
2385        global $plugin_page, $zbs;
2386
2387        // PerfTest: zeroBSCRM_performanceTest_startTimer('companydropdown');
2388
2389        # (language switch)
2390        $companyOrOrg = zeroBSCRM_getSetting( 'coororg' );
2391        $companyLabel = jpcrm_label_company();
2392
2393        #} Typeahead instead
2394        echo "<div id='zbscompnew'>";
2395        echo '<span>' . esc_html( sprintf( __( 'Type to assign %s', 'zero-bs-crm' ), jpcrm_label_company() ) ) . '</span>';
2396
2397        #} Co Name Default
2398        $coName = '';
2399        $coID   = '';
2400        if ( 'contact' == $this->objType ) {
2401            $coID = -1;
2402            if ( is_array( $contact ) && isset( $contact['id'] ) ) {
2403                $coID = zeroBS_getCustomerCompanyID( $contact['id'] );
2404            }
2405            if ( ! empty( $coID ) ) {
2406                $co = zeroBS_getCompany( $coID );
2407
2408                // 3.0
2409                if ( isset( $co ) && isset( $co['name'] ) ) {
2410                    $coName = $co['name'];
2411                }
2412
2413                // < 3.0
2414                if ( isset( $co ) && isset( $co['meta'] ) && isset( $co['meta']['coname'] ) ) {
2415                    $coName = $co['meta']['coname'];
2416                }
2417
2418                if ( empty( $coName ) && isset( $co['coname'] ) ) {
2419                    $coName = $co['coname'];
2420                }
2421            }
2422        }
2423
2424        #} Output
2425
2426        if ( get_option( 'permalink_structure' ) == '' ) {
2427            echo "<div class='ui message red'>" . esc_html__( 'You are using Plain Permalinks. Please set your permalinks to %postname% by visiting ', 'zero-bs-crm' ) . "<a href='" . esc_url( admin_url( 'options-permalink.php' ) ) . "'>" . esc_html__( 'here', 'zero-bs-crm' ) . '</a></div>';
2428        } else {
2429            echo zeroBSCRM_CompanyTypeList( 'zbscrmjs_customer_setCompany', $coName, true, 'zbscrmjs_customer_changedCompany' );
2430        }
2431        #} Hidden input (real input) & Callback func
2432        ?>
2433        <input type="hidden" name="zbs_company" id="zbs_company" value="<?php echo esc_attr( $coID ); ?>" />
2434        <script type="text/javascript">
2435
2436        // custom fuction to copy company details from typeahead company deets
2437        function zbscrmjs_customer_setCompany(obj){
2438
2439            if (typeof obj.id != "undefined"){
2440
2441                // set vals
2442                jQuery("#zbs_company").val(obj.id);
2443
2444                // set dirty
2445                zbscrm_JS_addDirty('zbs-company');
2446
2447            } 
2448
2449        }
2450
2451        // custom fuction to copy company details from typeahead company deets
2452        // this one fires on any change, but here we're just using to catch empties :)
2453        // in fact, this one now overrides above^ 
2454        function zbscrmjs_customer_changedCompany(newval){
2455
2456            if (typeof newval == "undefined" || newval == ''){
2457
2458                // set vals
2459                jQuery("#zbs_company").val('');
2460
2461                // set dirty
2462                zbscrm_JS_addDirty('zbs-company');
2463
2464            }
2465
2466        }
2467
2468        </script>
2469
2470    
2471        </div>
2472        <?php
2473
2474        // PerfTest: zeroBSCRM_performanceTest_finishTimer('companydropdown');
2475    }
2476
2477    public function save_data( $contact_id, $contact ) {
2478
2479        return $contact;
2480    }
2481}
2482
2483/*
2484======================================================
2485    / "Contacts at Company" Metabox Related Funcs
2486    ====================================================== */
2487
2488/*
2489======================================================
2490    Contact Activity Metabox
2491    ====================================================== */
2492class zeroBS__Metabox_Contact_Activity extends zeroBS__Metabox {
2493
2494    public function __construct( $plugin_file ) {
2495
2496        $this->metaboxID       = 'zbs-contact-activity-metabox';
2497        $this->metaboxTitle    = __( 'Activity', 'zero-bs-crm' );
2498        $this->metaboxIcon     = 'heartbeat';
2499        $this->metaboxScreen   = 'zbs-view-contact'; // we can use anything here as is now using our func
2500        $this->metaboxArea     = 'side';
2501        $this->metaboxLocation = 'high';
2502
2503        // call this
2504        $this->initMetabox();
2505    }
2506
2507    public function html( $obj, $metabox ) {
2508
2509            global $zbs, $zeroBSCRM_logTypes;
2510
2511            $objid = -1;
2512        if ( is_array( $obj ) && isset( $obj['id'] ) ) {
2513            $objid = $obj['id'];
2514        }
2515
2516            // output any pinned logs
2517            $pinned_logs = $zbs->DAL->logs->getLogsForObj(
2518                array(
2519
2520                    'objtype'     => ZBS_TYPE_CONTACT,
2521                    'objid'       => $objid,
2522                    'only_pinned' => true,
2523                    'incMeta'     => true,
2524                    'sortByField' => 'zbsl_created',
2525                    'sortOrder'   => 'DESC',
2526                    'page'        => -1,
2527                    'perPage'     => -1,
2528                    'ignoreowner' => zeroBSCRM_DAL2_ignoreOwnership( ZBS_TYPE_CONTACT ),
2529
2530                )
2531            );
2532
2533        if ( is_array( $pinned_logs ) && count( $pinned_logs ) > 0 ) {
2534
2535            $pinned_log_count = 0;
2536
2537            echo '<h4 style="text-align:right" class="ui top attached header" id="jpcrm-pinned-logs-header">' . esc_attr__( 'Pinned Logs', 'zero-bs-crm' ) . '</h4>';
2538            echo '<div class="jpcrm-pinned-logs ui green attached segment">';
2539            foreach ( $pinned_logs as $log ) {
2540
2541                if ( is_array( $log ) && isset( $log['created'] ) ) {
2542
2543                    if ( $pinned_log_count > 0 ) {
2544
2545                        ?>
2546                            <div class="ui divider"></div>
2547                            <?php
2548
2549                    }
2550
2551                    echo '<div class="jpcrm-pinned-log">';
2552
2553                        // ico?
2554                        $ico = '';
2555                    $logKey  = strtolower( str_replace( ' ', '_', str_replace( ':', '_', $log['type'] ) ) );
2556                    if ( isset( $zeroBSCRM_logTypes['zerobs_customer'][ $logKey ] ) ) {
2557                        $ico = $zeroBSCRM_logTypes['zerobs_customer'][ $logKey ]['ico'];
2558                    }
2559                        // these are FA ico's at this point
2560
2561                        // compile this first, so can catch default (empty types)
2562                        $logTitle = '';
2563                    if ( ! empty( $ico ) ) {
2564                        $logTitle .= '<i class="fa ' . $ico . '"></i> ';
2565                    }
2566                        // DAL 2 saves type as permalinked
2567                    if ( isset( $zeroBSCRM_logTypes['zerobs_customer'][ $logKey ] ) ) {
2568                        $logTitle .= __( $zeroBSCRM_logTypes['zerobs_customer'][ $logKey ]['label'], 'zero-bs-crm' );
2569                    }
2570
2571                    ?>
2572
2573                        <?php
2574                        // short desc
2575                        if ( isset( $log['shortdesc'] ) && ! empty( $log['shortdesc'] ) ) {
2576                            ?>
2577                            <h4 class="jpcrm-pinned-log-shortdesc">
2578                                <?php echo $logTitle . ' | ' . esc_html( $log['shortdesc'] ); ?>
2579                            </h4>
2580                            <?php } ?>
2581
2582                            <?php
2583                            // long desc
2584                            if ( isset( $log['longdesc'] ) && ! empty( $log['longdesc'] ) ) {
2585                                ?>
2586                            <div class="jpcrm-pinned-log-longdesc">
2587                                <?php echo wp_kses( html_entity_decode( $log['longdesc'], ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ), $zbs->acceptable_restricted_html ); ?>
2588                            </div>
2589                            <?php } ?>
2590
2591                            <div class="jpcrm-pinned-log-author-date-meta">
2592                            <?php
2593                            $meta_string = '';
2594                            if ( ! empty( $log['author'] ) ) {
2595                                $meta_string = $log['author'];
2596                            }
2597                            if ( ! empty( $log['createduts'] ) && $log['createduts'] > 0 ) {
2598                                if ( ! empty( $meta_string ) ) {
2599                                    $meta_string .= ' &mdash; ';
2600                                }
2601                                $meta_string .= jpcrm_uts_to_date_str( $log['createduts'] );
2602                            }
2603
2604                            echo esc_html( $meta_string );
2605
2606                            ?>
2607                            </div>
2608
2609                        </div>
2610                        <?php
2611
2612                        ++$pinned_log_count;
2613
2614                }
2615            }
2616
2617            ?>
2618                </div>
2619                <?php
2620
2621        }
2622
2623            // normal activity output
2624            echo '<div class="zbs-activity">';
2625                echo '<div class="">';
2626                    $zbsCustomerActivity = zeroBSCRM_getContactLogs( $objid, true, 100, 0, '', false );
2627                    zeroBSCRM_html_contactTimeline( $objid, $zbsCustomerActivity, $obj );
2628                echo '</div>';
2629            echo '</div>';
2630    }
2631
2632    // nothing to save here.
2633    public function save_data( $objID, $obj ) {
2634        return $obj;
2635    }
2636}
2637
2638/*
2639======================================================
2640    / Contact Activity Metabox
2641    ====================================================== */