Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 1093
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 / 74
0.00% covered (danger)
0.00%
0 / 3
380
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 / 54
0.00% covered (danger)
0.00%
0 / 1
240
 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 / 42
0.00% covered (danger)
0.00%
0 / 3
132
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 / 20
0.00% covered (danger)
0.00%
0 / 1
42
 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 ( ! empty( $action['url'] ) ) {
743                            ?>
744                            data-action="url" data-url="<?php echo esc_attr( $action['url'] ); ?>"
745                            <?php
746                        }
747
748                        // got extra attributes?
749                        if ( isset( $action['extraattr'] ) && is_array( $action['extraattr'] ) ) {
750
751                                // dump extra attr into item
752                            foreach ( $action['extraattr'] as $k => $v ) {
753                                echo ' data-' . esc_attr( $k ) . '="' . esc_attr( $v ) . '"';
754                            }
755                        }
756                        ?>
757                        >
758                        <?php
759
760                        // got ico?
761                        if ( isset( $action['ico'] ) ) {
762                            echo '<i class="' . esc_attr( $action['ico'] ) . '"></i>';
763                        }
764
765                        // got text?
766                        if ( isset( $action['label'] ) ) {
767                            echo esc_html( $action['label'] );
768                        }
769
770                        ?>
771                </div>
772                        <?php
773                    }
774                }
775                ?>
776                </div>
777        </div>
778        </div>
779        <script type="text/javascript">
780        jQuery(function(){
781
782            // actions drop down
783            jQuery('.ui.dropdown').dropdown();
784
785            // action items
786            jQuery('.zbs-contact-action').off('click').on( 'click', function(){
787
788                // get action type (at launch, only url)
789                var actionType = jQuery(this).attr('data-action');
790
791                if (typeof actionType != "undefined") switch (actionType){
792
793                    case 'url':
794
795                        var u = jQuery(this).attr('data-url');
796                        if (typeof u != "undefined" && u != '') window.location = u;
797
798                        break;
799
800
801                }
802
803            });
804
805        });
806        </script>
807            <?php
808        }
809        ?>
810            <div class="zbs-contact-actions-bottom zbs-objedit-actions-bottom">
811                <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>
812                <div class='clear'></div>
813            </div>
814            <?php
815    }
816
817    public function save_data( $contact_id, $contact ) {
818
819        // avatar changes saved by main contact save func (field editor), allowing for all-in-one creation/updates, see #AVATARSAVE
820
821        return $contact;
822    }
823}
824
825/*
826======================================================
827    / Create Actions Box
828    ====================================================== */
829
830/*
831======================================================
832    Attach (custom) fileboxes to customer metabox
833    ====================================================== */
834
835class zeroBS__Metabox_ContactCustomFiles extends zeroBS__Metabox {
836
837    public function __construct( $plugin_file, $idOverride = '', $titleOverride = '' ) {
838
839        $this->objType         = 'contact';
840        $this->metaboxID       = 'zerobs-customer-custom-files';
841        $this->metaboxTitle    = __( 'Other Files', 'zero-bs-crm' );
842        $this->metaboxScreen   = 'zbs-add-edit-contact-edit'; // 'zerobs_edit_contact'; // we can use anything here as is now using our func
843        $this->metaboxArea     = 'normal';
844        $this->metaboxLocation = 'low';
845        $this->capabilities    = array(
846
847            'can_hide'        => true, // can be hidden
848            'areas'           => array( 'normal' ), // areas can be dragged to - normal side = only areas currently
849            'can_accept_tabs' => false,  // can/can't accept tabs onto it
850            'can_become_tab'  => true, // can be added as tab
851            'can_minimise'    => true, // can be minimised
852
853        );
854
855        if ( ! empty( $idOverride ) ) {
856            $this->metaboxID = $idOverride;
857        }
858        if ( ! empty( $titleOverride ) ) {
859            $this->metaboxTitle = __( $titleOverride, 'zero-bs-crm' );
860        }
861
862        // call this
863        $this->initMetabox();
864    }
865
866    public function html( $contact, $args ) {
867
868        global $zbs;
869
870                $html = '';
871
872                $thisFileSlotName = '';
873        if ( isset( $args['title'] ) ) {
874            $thisFileSlotName = $args['title'];
875        }
876                $filePerma = ''; if ( isset( $thisFileSlotName ) && ! empty( $thisFileSlotName ) ) {
877                    // $filePerma = strtolower(str_replace(' ','_',str_replace('.','_',substr($thisFileSlotName,0,20))));
878                    $filePerma = $zbs->DAL->makeSlug( $thisFileSlotName );
879                }
880
881                // phpcs:disable WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
882                $zbsFiles = array();
883                if ( $contact ) {
884                    // retrieve - shouldn't these vars be "other files"... confusing
885                    $zbsFiles = zeroBSCRM_getCustomerFiles( $contact['id'] );
886
887                    // This specifically looks for $args['title'] file
888                    $fileSlotSrc = zeroBSCRM_fileslots_fileInSlot( $filePerma, $contact['id'], ZBS_TYPE_CONTACT );
889
890                    // check for file + only show that
891                    $zbsFilesArr = array();
892                    if ( $fileSlotSrc !== '' && is_array( $zbsFiles ) && count( $zbsFiles ) > 0 ) {
893                        foreach ( $zbsFiles as $f ) {
894                            if ( $f['file'] === $fileSlotSrc ) {
895                                $zbsFilesArr[] = $f;
896                            }
897                        }
898                    }
899                    $zbsFiles = $zbsFilesArr;
900                }
901
902                // while we only have 1 file per slot, we can do this:
903                // *js uses this to empty if deleted elsewhere (other metabox)
904                $fileSlotURL = '';
905                if ( is_array( $zbsFiles ) && count( $zbsFiles ) === 1 ) {
906                    $fileSlotURL = $zbsFiles[0]['url'];
907                }
908                // phpcs:enable WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
909
910                ?>
911                            <table class="form-table wh-metatab wptbp zbsFileSlotTable" data-sloturl="<?php echo esc_attr( $fileSlotURL ); ?>" id="<?php echo esc_attr( $this->metaboxID ); ?>-tab">
912
913                            <?php
914
915                            #} Any slot filled?
916                            if ( is_array( $zbsFiles ) && count( $zbsFiles ) > 0 ) {
917                                ?>
918                                    <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>
919                                            <td class="">
920                                            <?php
921                                                $fileLineIndx = 1; foreach ( $zbsFiles as $zbsFile ) {
922
923                                                    /*
924                                                    $file = basename($zbsFile['file']);
925
926                                                    // if in privatised system, ignore first hash in name
927                                                    if (isset($zbsFile['priv'])){
928
929                                                        $file = substr($file,strpos($file, '-')+1);
930                                                    } */
931                                                    $file = zeroBSCRM_files_baseName( $zbsFile['file'], isset( $zbsFile['priv'] ) );
932                                                    echo '<div class="zbsFileLine" id="zbsFileLineCustomer' . esc_attr( $fileLineIndx ) . '"><a href="' . esc_url( $zbsFile['url'] ) . '" target="_blank">' . esc_html( $file ) . '</a> </div>';
933
934                                                    // if using portal.. state shown/hidden
935                                                    // this is also shown in each file slot :) if you change any of it change that too
936                                                if ( defined( 'ZBS_CLIENTPRO_TEMPLATES' ) ) {
937                                                    if ( isset( $zbsFile['portal'] ) && $zbsFile['portal'] ) {
938                                                        echo "<p><i class='icon check circle green inverted'></i> " . esc_html__( 'Shown on Portal', 'zero-bs-crm' ) . '</p>';
939                                                    } else {
940                                                        echo "<p><i class='icon ban inverted red'></i> " . esc_html__( 'Not shown on Portal', 'zero-bs-crm' ) . '</p>';
941                                                    }
942                                                }
943
944                                                    ++$fileLineIndx;
945
946                                                }
947                                                ?>
948                                            </td></tr>
949                                            <?php
950
951                            }
952                            ?>
953
954                                <?php
955                                #adapted from http://code.tutsplus.com/articles/attaching-files-to-your-posts-using-wordpress-custom-meta-boxes-part-1--wp-22291
956
957                                    // will be done by mainfunc wp_nonce_field(plugin_basename(__FILE__), 'zbsc_file_attachment_nonce');
958
959                                    $html .= '<input type="file" id="zbsc_file_' . $filePerma . '" name="zbsc_file_' . $filePerma . '" size="25" class="zbs-dc">';
960
961                                ?>
962                                        <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>
963                                            <td>
964                                            <?php
965                                            echo $html;
966                                            ?>
967                                </td></tr>
968
969                            
970                            </table>
971                            <?php
972
973                            // PerfTest: zeroBSCRM_performanceTest_finishTimer('custmetabox');
974                            // PerfTest: zeroBSCRM_performanceTest_debugOut();
975
976                            ?>
977                            <script type="text/javascript">
978
979                                jQuery(function(){
980
981                                });
982
983
984                            </script>
985
986                            <?php
987
988                            // PerfTest: zeroBSCRM_performanceTest_finishTimer('other');
989    }
990
991    public function save_data( $contact_id, $contact ) {
992
993                    // when multiple custom file boxes, this only needs to fire once :)
994        if ( zeroBSCRM_is_customer_edit_page() && ! defined( 'ZBS_CUSTOMFILES_SAVED' ) ) {
995
996            define( 'ZBS_CUSTOMFILES_SAVED', 1 );
997
998            global $zbsc_justUploadedCustomer, $zbs;
999
1000            $settings = $zbs->settings->get( 'customfields' );
1001            $cfbInd   = 1;
1002
1003            $cfbsubs = array();
1004
1005            if ( isset( $settings['customersfiles'] ) && is_array( $settings['customersfiles'] ) && count( $settings['customersfiles'] ) > 0 ) {
1006                foreach ( $settings['customersfiles'] as $cfb ) {
1007
1008                    $thisFileSlotName = '';
1009                    if ( isset( $cfb[0] ) ) {
1010                        $thisFileSlotName = $cfb[0];
1011                    }
1012                    $filePerma = ''; if ( isset( $thisFileSlotName ) && ! empty( $thisFileSlotName ) ) {
1013
1014                        // $filePerma = strtolower(str_replace(' ','_',str_replace('.','_',substr($thisFileSlotName,0,20))));
1015                        $filePerma = $zbs->DAL->makeSlug( $thisFileSlotName );
1016
1017                    }
1018
1019                    if ( ! empty( $thisFileSlotName ) && ! empty( $filePerma ) ) {
1020                        $cfbsubs[ $filePerma ] = $thisFileSlotName;
1021                    }
1022                }
1023            }
1024
1025            if ( count( $cfbsubs ) > 0 ) {
1026                foreach ( $cfbsubs as $cfSubKey => $cfSubName ) {
1027
1028                                /* --- security verification --- */
1029                    if ( isset( $_POST['zbsc_file_attachment_nonce'] ) ) {
1030                        if ( ! wp_verify_nonce( $_POST['zbsc_file_attachment_nonce'], plugin_basename( __FILE__ ) ) ) {
1031                                return $contact_id;
1032                        } // end if
1033                    }
1034
1035                    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
1036                        return $contact_id;
1037                    } // end if
1038
1039                    if ( ! zeroBSCRM_permsCustomers() ) {
1040                        return $contact_id;
1041                    }
1042                            /* - end security verification - */
1043
1044                    if ( ! empty( $_FILES[ 'zbsc_file_' . $cfSubKey ]['name'] ) &&
1045                            ( ! isset( $zbsc_justUploadedCustomer ) ||
1046                            ( isset( $zbsc_justUploadedCustomer ) && $zbsc_justUploadedCustomer != $_FILES[ 'zbsc_file_' . $cfSubKey ]['name'] )
1047                            )
1048                            ) {
1049
1050                        // Blocking repeat-upload bug
1051                        $zbsc_justUploadedCustomer = $_FILES[ 'zbsc_file_' . $cfSubKey ]['name'];
1052
1053                        // verify file extension and mime type
1054                        if ( jpcrm_file_check_mime_extension( $_FILES[ 'zbsc_file_' . $cfSubKey ] ) ) {
1055
1056                            $upload = wp_upload_bits( $_FILES[ 'zbsc_file_' . $cfSubKey ]['name'], null, file_get_contents( $_FILES[ 'zbsc_file_' . $cfSubKey ]['tmp_name'] ) );
1057
1058                            if ( isset( $upload['error'] ) && $upload['error'] != 0 ) {
1059                                wp_die( 'There was an error uploading your file. The error is: ' . esc_html( $upload['error'] ) );
1060                            } else {
1061                                // update_post_meta($contact_id, 'zbsc_file_'.$cfSubKey, $upload);
1062
1063                                // v2.13 - also privatise the file (move to our asset store)
1064                                // $upload will have 'file' and 'url'
1065                                $fileName        = basename( $upload['file'] );
1066                                $fileDir         = dirname( $upload['file'] );
1067                                $privateThatFile = zeroBSCRM_privatiseUploadedFile( $fileDir, $fileName );
1068                                if ( is_array( $privateThatFile ) && isset( $privateThatFile['file'] ) ) {
1069
1070                                    // successfully moved to our store
1071
1072                                        // modify URL + file attributes
1073                                        $upload['file'] = $privateThatFile['file'];
1074                                        $upload['url']  = $privateThatFile['url'];
1075
1076                                        // add this extra identifier if in privatised sys
1077                                        $upload['priv'] = true;
1078
1079                                } else {
1080
1081                                    // couldn't move to store, leave in uploaded for now :)
1082
1083                                }
1084
1085                                // w mod - adds to array :)
1086                                $zbsCustomerFiles = zeroBSCRM_getCustomerFiles( $contact_id );
1087
1088                                if ( is_array( $zbsCustomerFiles ) ) {
1089
1090                                    // add it
1091                                    $zbsCustomerFiles[] = $upload;
1092
1093                                } else {
1094
1095                                    // first
1096                                    $zbsCustomerFiles = array( $upload );
1097
1098                                }
1099
1100                                // update_post_meta($contact_id, 'zbs_customer_files', $zbsCustomerFiles);
1101                                zeroBSCRM_updateCustomerFiles( $contact_id, $zbsCustomerFiles );
1102
1103                                // this'll override any prev in that slot, too
1104                                zeroBSCRM_fileslots_addToSlot( $cfSubKey, $upload['file'], $contact_id, ZBS_TYPE_CONTACT, true );
1105
1106                                // Fire any 'post-upload-processing' (e.g. CPP makes thumbnails of pdf, jpg, etc.)
1107                                do_action( 'zbs_post_upload_contact', $upload );
1108                            }
1109                        } else {
1110                            wp_die( "The file type that you've uploaded is not an accepted file format." );
1111                        }
1112                    } // if file
1113
1114                } /// / foreach
1115            }
1116        }
1117
1118            return $contact;
1119    }
1120}
1121
1122/*
1123======================================================
1124    / Attach (custom) fileboxes to customer metabox
1125    ====================================================== */
1126
1127/*
1128======================================================
1129    Attach files to customer metabox
1130    ====================================================== */
1131
1132/*
1133======================================================
1134    Contact Files Metabox
1135    ====================================================== */
1136
1137class zeroBS__Metabox_ContactFiles extends zeroBS__Metabox {
1138
1139    public function __construct( $plugin_file ) {
1140
1141        $this->objType         = 'contact';
1142        $this->metaboxID       = 'zerobs-customer-files';
1143        $this->metaboxTitle    = __( 'Other Files', 'zero-bs-crm' );
1144        $this->metaboxScreen   = 'zbs-add-edit-contact-edit'; // 'zerobs_edit_contact'; // we can use anything here as is now using our func
1145        $this->metaboxArea     = 'normal';
1146        $this->metaboxLocation = 'low';
1147        $this->capabilities    = array(
1148
1149            'can_hide'        => true, // can be hidden
1150            'areas'           => array( 'normal' ), // areas can be dragged to - normal side = only areas currently
1151            'can_accept_tabs' => true,  // can/can't accept tabs onto it
1152            'can_become_tab'  => true, // can be added as tab
1153            'can_minimise'    => true, // can be minimised
1154
1155        );
1156
1157        // call this
1158        $this->initMetabox();
1159    }
1160
1161    public function html( $contact, $metabox ) {
1162
1163            global $zbs;
1164
1165            $html = '';
1166
1167            // wmod
1168
1169                    #} retrieve - shouldn't these vars be "other files"... confusing
1170                    $zbsFiles = false;
1171        if ( isset( $contact['id'] ) ) {
1172            $zbsFiles = zeroBSCRM_getCustomerFiles( $contact['id'] );
1173        }
1174
1175        ?>
1176                <table class="form-table wh-metatab wptbp" id="wptbpMetaBoxMainItemFiles">
1177
1178                <?php
1179
1180                #} Whole file delete method could do with rewrite
1181                #} Also sort JS into something usable - should be ajax all this
1182
1183                #} Any existing
1184                if ( is_array( $zbsFiles ) && count( $zbsFiles ) > 0 ) {
1185                    ?>
1186                        <tr class="wh-large zbsFileDetails"><th class="zbsFilesTitle"><label><?php echo '<span>' . count( $zbsFiles ) . '</span> ' . esc_html__( 'File(s)', 'zero-bs-crm' ) . ':'; ?></label></th>
1187                                <td id="zbsFileWrapOther">
1188                                    <table class="ui celled table" id="zbsFilesTable">
1189                                        <thead>
1190                                        <tr>
1191                                            <th><?php esc_html_e( 'File', 'zero-bs-crm' ); ?></th>
1192                                            <th class="collapsing center aligned"><?php esc_html_e( 'Actions', 'zero-bs-crm' ); ?></th>
1193                                        </tr>
1194                                    </thead><tbody>
1195                                            <?php
1196                                                $fileLineIndx = 1; foreach ( $zbsFiles as $zbsFile ) {
1197
1198                                                    /*
1199                                                    $file = basename($zbsFile['file']);
1200
1201                                                    // if in privatised system, ignore first hash in name
1202                                                    if (isset($zbsFile['priv'])){
1203
1204                                                        $file = substr($file,strpos($file, '-')+1);
1205                                                    } */
1206                                                    $file = zeroBSCRM_files_baseName( $zbsFile['file'], isset( $zbsFile['priv'] ) );
1207
1208                                                    $fileEditUrl = admin_url( 'admin.php?page=' . $zbs->slugs['editfile'] ) . '&customer=' . $contact['id'] . '&fileid=' . ( $fileLineIndx - 1 );
1209
1210                                                    echo '<tr class="zbsFileLineTR" id="zbsFileLineTRCustomer' . esc_attr( $fileLineIndx ) . '">';
1211                                                    echo '<td><div class="zbsFileLine" id="zbsFileLineCustomer' . esc_attr( $fileLineIndx ) . '"><a href="' . esc_url( $zbsFile['url'] ) . '" target="_blank">' . esc_html( $file ) . '</a></div>';
1212
1213                                                    // if using portal.. state shown/hidden
1214                                                    // this is also shown in each file slot :) if you change any of it change that too
1215                                                if ( defined( 'ZBS_CLIENTPRO_TEMPLATES' ) ) {
1216                                                    if ( isset( $zbsFile['portal'] ) && $zbsFile['portal'] ) {
1217                                                        echo "<p><i class='icon check circle green inverted'></i> " . esc_html__( 'Shown on Portal', 'zero-bs-crm' ) . '</p>';
1218                                                    } else {
1219                                                        echo "<p><i class='icon ban inverted red'></i> " . esc_html__( 'Not shown on Portal', 'zero-bs-crm' ) . '</p>';
1220                                                    }
1221                                                }
1222
1223                                                    echo '</td>';
1224                                                    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>';
1225                                                    ++$fileLineIndx;
1226
1227                                                }
1228                                                ?>
1229                                    </tbody></table>
1230                                </td></tr>
1231                                <?php
1232
1233                }
1234                ?>
1235
1236                    <?php
1237                    #adapted from http://code.tutsplus.com/articles/attaching-files-to-your-posts-using-wordpress-custom-meta-boxes-part-1--wp-22291
1238
1239                        $html .= '<input type="file" id="zbsc_file_attachment" name="zbsc_file_attachment" size="25" class="zbs-dc">';
1240
1241                    ?>
1242                            <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>
1243                                <td>
1244                                <?php
1245                                wp_nonce_field( plugin_basename( __FILE__ ), 'zbsc_file_attachment_nonce' );
1246                                echo $html;
1247                                ?>
1248                    </td></tr>
1249
1250                
1251                </table>
1252                <?php
1253
1254                // PerfTest: zeroBSCRM_performanceTest_finishTimer('custmetabox');
1255                // PerfTest: zeroBSCRM_performanceTest_debugOut();
1256
1257                ?>
1258                <script type="text/javascript">
1259
1260                    var zbsCustomerCurrentlyDeleting = false;
1261                    var zbsMetaboxFilesLang = {
1262
1263                        'error': '<?php echo esc_html( zeroBSCRM_slashOut( __( 'Error', 'zero-bs-crm' ) ) ); ?>',
1264                        'unabletodelete': '<?php echo esc_html( zeroBSCRM_slashOut( __( 'Unable to delete this file.', 'zero-bs-crm' ) ) ); ?>'
1265                    };
1266
1267                    jQuery(function(){
1268
1269                        jQuery('.zbsDelFile').on( 'click', function(){
1270
1271                            if (!window.zbsCustomerCurrentlyDeleting){
1272
1273                                // blocking
1274                                window.zbsCustomerCurrentlyDeleting = true;
1275
1276                                var delUrl = jQuery(this).attr('data-delurl');
1277                                //var lineIDtoRemove = jQuery(this).closest('.zbsFileLine').attr('id');
1278                                var lineToRemove = jQuery(this).closest('tr');
1279
1280                                if (typeof delUrl != "undefined" && delUrl != ''){
1281
1282
1283
1284                                        // postbag!
1285                                        var data = {
1286                                        'action': 'delFile',
1287                                        'zbsfType': 'customer',
1288                                        'zbsDel':  delUrl, // could be csv, never used though
1289                                        'zbsCID': 
1290                                        <?php
1291                                        if ( ! empty( $contact['id'] ) && $contact['id'] > 0 ) {
1292                                            echo esc_html( $contact['id'] );
1293                                        } else {
1294                                            echo -1;
1295                                        }
1296                                        ?>
1297                                                    ,
1298                                        'sec': window.zbscrmjs_secToken
1299                                        };
1300
1301                                        // Send it Pat :D
1302                                        jQuery.ajax({
1303                                                type: "POST",
1304                                                url: ajaxurl, // admin side is just ajaxurl not wptbpAJAX.ajaxurl,
1305                                                "data": data,
1306                                                dataType: 'json',
1307                                                timeout: 20000,
1308                                                success: function(response) {
1309
1310                                                var localLineToRemove = lineToRemove, localDelURL = delUrl;
1311
1312                                                // visually remove
1313                                                jQuery(localLineToRemove).remove();
1314
1315                                                // update number
1316                                                var newNumber = jQuery('#zbsFilesTable tr').length-1;
1317                                                if (newNumber > 0)
1318                                                    jQuery('#wptbpMetaBoxMainItemFiles .zbsFilesTitle span').html();
1319                                                else
1320                                                    jQuery('#wptbpMetaBoxMainItemFiles .zbsFileDetails').remove();
1321
1322
1323                                                // remove any filled slots (with this file) 
1324                                                jQuery('.zbsFileSlotTable').each(function(ind,ele){
1325
1326                                                    if (jQuery(ele).attr('data-sloturl') == localDelURL){
1327
1328                                                        jQuery('.zbsFileSlotWrap',jQuery(ele)).remove();
1329                                                
1330                                                    }
1331
1332                                                });
1333
1334                                                // file deletion errors, show msg:
1335                                                if (typeof response.errors != "undefined" && response.errors.length > 0){
1336
1337                                                    jQuery.each(response.errors,function(ind,ele){
1338
1339                                                        jQuery('#zerobs-customer-files-box').append('<div class="ui warning message" style="margin-top:10px;">' + ele + '</div>');
1340
1341                                                    });
1342                                                         
1343
1344                                                }
1345
1346
1347                                                },
1348                                                error: function(response){
1349
1350                                                jQuery('#zerobs-customer-files-box').append('<div class="ui warning message" style="margin-top:10px;"><strong>' + window.zbsMetaboxFilesLang.error + ':</strong> ' + window.zbsMetaboxFilesLang.unabletodelete + '</div>');
1351
1352                                                }
1353
1354                                            });
1355
1356                                }
1357
1358                                window.zbsCustomerCurrentlyDeleting = false;
1359
1360                            } // / blocking
1361
1362                        });
1363
1364                    });
1365
1366
1367                </script>
1368                <?php
1369
1370                // PerfTest: zeroBSCRM_performanceTest_finishTimer('other');
1371    }
1372
1373    public function save_data( $contact_id, $contact ) {
1374
1375        global $zbsc_justUploadedCustomer;
1376
1377        if ( ! empty( $_FILES['zbsc_file_attachment']['name'] ) &&
1378            ( ! isset( $zbsc_justUploadedCustomer ) ||
1379                ( isset( $zbsc_justUploadedCustomer ) && $zbsc_justUploadedCustomer != $_FILES['zbsc_file_attachment']['name'] )
1380            )
1381            ) {
1382
1383            /* --- security verification --- */
1384            if ( ! wp_verify_nonce( $_POST['zbsc_file_attachment_nonce'], plugin_basename( __FILE__ ) ) ) {
1385                return $id;
1386            } // end if
1387
1388            if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
1389                return $id;
1390            } // end if
1391
1392            if ( ! zeroBSCRM_permsCustomers() ) {
1393                return $contact_id;
1394            }
1395            /* - end security verification - */
1396
1397            // Blocking repeat-upload bug
1398            $zbsc_justUploadedCustomer = $_FILES['zbsc_file_attachment']['name'];
1399
1400            // verify file extension and mime type
1401            if ( jpcrm_file_check_mime_extension( $_FILES['zbsc_file_attachment'] ) ) {
1402
1403                $upload = wp_upload_bits( $_FILES['zbsc_file_attachment']['name'], null, file_get_contents( $_FILES['zbsc_file_attachment']['tmp_name'] ) );
1404
1405                if ( isset( $upload['error'] ) && $upload['error'] != 0 ) {
1406                    wp_die( 'There was an error uploading your file. The error is: ' . esc_html( $upload['error'] ) );
1407                } else {
1408                    // update_post_meta($id, 'zbsc_file_attachment', $upload);
1409
1410                        // v2.13 - also privatise the file (move to our asset store)
1411                        // $upload will have 'file' and 'url'
1412                        $fileName        = basename( $upload['file'] );
1413                        $fileDir         = dirname( $upload['file'] );
1414                        $privateThatFile = zeroBSCRM_privatiseUploadedFile( $fileDir, $fileName );
1415                    if ( is_array( $privateThatFile ) && isset( $privateThatFile['file'] ) ) {
1416
1417                        // successfully moved to our store
1418
1419                            // modify URL + file attributes
1420                            $upload['file'] = $privateThatFile['file'];
1421                            $upload['url']  = $privateThatFile['url'];
1422
1423                            // add this extra identifier if in privatised sys
1424                            $upload['priv'] = true;
1425
1426                    } else {
1427
1428                        // couldn't move to store, leave in uploaded for now :)
1429
1430                    }
1431
1432                        // w mod - adds to array :)
1433                        $zbsCustomerFiles = zeroBSCRM_getCustomerFiles( $contact_id );
1434
1435                    if ( is_array( $zbsCustomerFiles ) ) {
1436
1437                        // add it
1438                        $zbsCustomerFiles[] = $upload;
1439
1440                    } else {
1441
1442                        // first
1443                        $zbsCustomerFiles = array( $upload );
1444
1445                    }
1446
1447                        // update_post_meta($id, 'zbs_customer_files', $zbsCustomerFiles);
1448                        zeroBSCRM_updateCustomerFiles( $contact_id, $zbsCustomerFiles );
1449
1450                        // Fire any 'post-upload-processing' (e.g. CPP makes thumbnails of pdf, jpg, etc.)
1451                        do_action( 'zbs_post_upload_contact', $upload );
1452                }
1453            } else {
1454                wp_die( "The file type that you've uploaded is not an accepted file format." );
1455            }
1456        }
1457
1458        return $contact;
1459    }
1460}
1461
1462/*
1463======================================================
1464    / Attach files to customer metabox
1465    ====================================================== */
1466
1467/*
1468======================================================
1469    Create Client Portal
1470    ====================================================== */
1471
1472class zeroBS__Metabox_ContactPortal extends zeroBS__Metabox {
1473
1474    public function __construct( $plugin_file, $metabox_screen = 'zbs-add-edit-contact-edit' ) {
1475
1476        $this->objType         = 'contact';
1477        $this->metaboxID       = 'zerobs-customer-portal';
1478        $this->metaboxTitle    = __( 'Client Portal', 'zero-bs-crm' );
1479        $this->metaboxScreen   = $metabox_screen; // we can use anything here as is now using our func
1480        $this->metaboxArea     = 'side';
1481        $this->metaboxLocation = 'high';
1482        $this->capabilities    = array(
1483
1484            'can_hide'        => true, // can be hidden
1485            'areas'           => array( 'side' ), // areas can be dragged to - normal side = only areas currently
1486            'can_accept_tabs' => false,  // can/can't accept tabs onto it
1487            'can_become_tab'  => false, // can be added as tab
1488            'can_minimise'    => true, // can be minimised
1489
1490        );
1491
1492        // call this
1493        $this->initMetabox();
1494    }
1495
1496    public function html( $contact, $metabox ) {
1497
1498        // PerfTest: zeroBSCRM_performanceTest_startTimer('portal-draw');
1499
1500        global $plugin_page, $zbs;
1501        $screen = get_current_screen();
1502
1503        $wp_user_id = '';
1504        #} Rather than reload all the time :)
1505        global $zbsContactEditing;
1506
1507        #} retrieve
1508        // $zbsCustomer = get_post_meta($contact['id'], 'zbs_customer_meta', true);
1509        if ( ! isset( $zbsContactEditing ) && isset( $contact['id'] ) ) {
1510            $zbsCustomer       = zeroBS_getCustomer( $contact['id'], false, false, false );
1511            $zbsContactEditing = $zbsCustomer;
1512        } else {
1513            $zbsCustomer = $zbsContactEditing;
1514        }
1515
1516        if ( isset( $zbsCustomer ) && is_array( $zbsCustomer ) && isset( $zbsCustomer['email'] ) ) {
1517
1518            // check customer link to see if it exists - wh moved to dal
1519            $wp_user_id = zeroBSCRM_getClientPortalUserID( $contact['id'] );
1520
1521            /*
1522            nope
1523            if($wp_user_id == ''){
1524                $wp_user_id = email_exists( $zbsCustomer['email'] );
1525            } */
1526        }
1527
1528        echo '<div class="waiting-togen">';
1529
1530        // get user obj
1531        $user_object = get_userdata( $wp_user_id );
1532
1533        if ( $user_object ) {
1534
1535            // a user already exists with this email
1536
1537            echo '<div class="zbs-customerportal-activeuser">';
1538
1539                esc_html_e( 'WordPress User Linked', 'zero-bs-crm' );
1540                echo ' #<span class="zbs-user-id">' . esc_html( $wp_user_id ) . '</span>:<br />';
1541
1542                echo '<span class="ui label">' . esc_html( $user_object->user_email ) . '</span>';
1543
1544                // wp admins get link
1545            if ( zeroBSCRM_isWPAdmin() ) {
1546
1547                $url = admin_url( 'user-edit.php?user_id=' . $wp_user_id );
1548                        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
1549
1550            }
1551
1552            echo '</div>';
1553
1554            // user ID will now have access to this area..
1555            echo '<hr /><div class="zbs-customerportal-activeuser-actions">';
1556
1557                echo esc_html( __( 'Client Portal Access:', 'zero-bs-crm' ) );
1558
1559                $customerPortalActive = true;
1560            if ( zeroBSCRM_isCustomerPortalDisabled( $contact['id'] ) ) {
1561                $customerPortalActive = false;
1562            }
1563
1564            if ( $customerPortalActive ) {
1565
1566                // revoke/disable access
1567                echo ' <span class="ui green empty circular label"></span> <span class="zbs-portal-label">' . esc_html( __( 'Enabled', 'zero-bs-crm' ) ) . '</span>';
1568
1569                // wp admins get reset link, unless the crm contact is assigned to any other role than CRM Customer
1570                if ( zeroBSCRM_isWPAdmin() && jpcrm_role_check( $user_object, array(), array(), array( 'zerobs_customer' ) ) ) {
1571
1572                    echo '<div id="zbs-customerportal-access-actions" class="zbs-customerportal-activeuser">';
1573
1574                            echo '<button type="button" id="zbs-customerportal-resetpw" class="ui mini button white">' . esc_html( __( 'Reset Password', 'zero-bs-crm' ) ) . '</button>';
1575
1576                            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>';
1577
1578                    echo '</div>';
1579
1580                } else {
1581
1582                    // explainer - rarely shown
1583                    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>';
1584
1585                }
1586
1587                echo '<hr /><div class="zbs-customerportal-activeuser-actions">';
1588                        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' ) ) );
1589                echo '</div>';
1590            } else {
1591
1592                // enable access
1593                echo ' <span class="ui red empty circular label"></span> <span class="zbs-portal-label">' . esc_html( __( 'Disabled', 'zero-bs-crm' ) ) . '</span>';
1594
1595                // wp admins get enable link, unless the crm contact is assigned to any other role than CRM Customer
1596                if ( zeroBSCRM_isWPAdmin() && jpcrm_role_check( $user_object, array(), array(), array( 'zerobs_customer' ) ) ) {
1597
1598                    echo '<div id="zbs-customerportal-access-actions">';
1599                        echo '<button type="button" id="zbs-customerportal-toggle" data-zbsportalaction="enable" class="ui mini button positive">' . esc_html( __( 'Enable Access', 'zero-bs-crm' ) ) . '</button>';
1600                    echo '</div>';
1601
1602                }
1603            }
1604
1605                echo '<input type="hidden" id="zbsportalaction-ajax-nonce" value="' . esc_attr( wp_create_nonce( 'zbsportalaction-ajax-nonce' ) ) . '" />';
1606
1607            echo '</div>';
1608
1609        } elseif ( is_array( $zbsCustomer ) && isset( $zbsCustomer['email'] ) && ! empty( $zbsCustomer['email'] ) ) {
1610            echo '<div class="no-gen" style="text-align:center">';
1611            echo esc_html( __( 'No WordPress User exists with this email', 'zero-bs-crm' ) );
1612            echo '<br/><br/>';
1613                echo '<div class="ui primary black button button-primary wp-user-generate">';
1614            echo esc_html( __( 'Generate WordPress User', 'zero-bs-crm' ) );
1615            echo '</div>';
1616            echo '<input type="hidden" name="newwp-ajax-nonce" id="newwp-ajax-nonce" value="' . esc_attr( wp_create_nonce( 'newwp-ajax-nonce' ) ) . '" />';
1617            echo '</div>';
1618        } else {
1619            echo esc_html( __( 'Save your contact, or add an email to enable Client Portal functionality', 'zero-bs-crm' ) );
1620        }
1621
1622        echo '</div>';
1623
1624        ?>
1625        <script type="text/javascript">
1626
1627            jQuery(function(){
1628
1629                // bind activate/deactivate
1630                jQuery('#zbs-customerportal-toggle').off("click").on('click',function(e){
1631
1632                    // action
1633                    var action = jQuery(this).attr('data-zbsportalaction');
1634
1635                    // fire ajax
1636                    var t = {
1637                        action: "zbsPortalAction",
1638                        portalAction: action,
1639                        cid: 
1640                        <?php
1641                        if ( ! empty( $contact['id'] ) && $contact['id'] > 0 ) {
1642                            echo esc_html( $contact['id'] );
1643                        } else {
1644                            echo -1;
1645                        }
1646                        ?>
1647                            ,
1648                        security: jQuery( '#zbsportalaction-ajax-nonce' ).val()
1649                    }
1650                    i = jQuery.ajax({
1651                        url: ajaxurl,
1652                        type: "POST",
1653                        data: t,
1654                        dataType: "json"
1655                    });
1656                    i.done(function(e) {
1657                        //console.log(e);
1658                        if(typeof e.success != "undefined"){
1659
1660                            // localise
1661                            var cAction = action;
1662
1663                            if (action == 'enable'){
1664
1665                                // switch label
1666                                jQuery('.ui.circular.label',jQuery('.zbs-customerportal-activeuser-actions')).removeClass('red').addClass('green');
1667                                jQuery('.zbs-portal-label',jQuery('.zbs-customerportal-activeuser-actions')).html('<?php echo esc_html( __( 'Enabled', 'zero-bs-crm' ) ); ?>');
1668                                jQuery('#zbs-customerportal-toggle').removeClass('positive').addClass('negative').html('<?php echo esc_html( __( 'Disable Access', 'zero-bs-crm' ) ); ?>').attr('data-zbsportalaction','disable');
1669                                
1670
1671                            } else if (action == 'disable'){
1672
1673                                // switch label
1674                                jQuery('.ui.circular.label',jQuery('.zbs-customerportal-activeuser-actions')).addClass('red').removeClass('green');
1675                                jQuery('.zbs-portal-label',jQuery('.zbs-customerportal-activeuser-actions')).html('<?php echo esc_html( __( 'Disabled', 'zero-bs-crm' ) ); ?>');
1676                                jQuery('#zbs-customerportal-toggle').removeClass('negative').addClass('positive').html('<?php echo esc_html( __( 'Enable Access', 'zero-bs-crm' ) ); ?>').attr('data-zbsportalaction','enable');
1677                                
1678
1679                            }
1680
1681                        }
1682                    }), i.fail(function(e) {
1683                        //error
1684                    });
1685
1686                });
1687
1688                // bind reset pw
1689                jQuery('#zbs-customerportal-resetpw').off("click").on('click',function(e){
1690
1691                    // fire ajax
1692                    var t = {
1693                        action: "zbsPortalAction",
1694                        portalAction: 'resetpw',
1695                        cid: 
1696                        <?php
1697                        if ( ! empty( $contact['id'] ) && $contact['id'] > 0 ) {
1698                            echo esc_html( $contact['id'] );
1699                        } else {
1700                            echo -1;
1701                        }
1702                        ?>
1703                            ,
1704                        security: jQuery( '#zbsportalaction-ajax-nonce' ).val()
1705                    }
1706                    i = jQuery.ajax({
1707                        url: ajaxurl,
1708                        type: "POST",
1709                        data: t,
1710                        dataType: "json"
1711                    });
1712                    i.done(function(e) {
1713                        //console.log(e);
1714                        if(typeof e.success != "undefined"){
1715
1716                            var newPassword =  '<?php zeroBSCRM_slashOut( esc_html__( 'Unknown', 'zero-bs-crm' ) ); ?>';
1717                            if (typeof e.pw != "undefined") newPassword = e.pw;
1718
1719                            if ( newPassword !== false ){
1720
1721                                // swal confirm
1722                                swal(
1723                                    '<?php zeroBSCRM_slashOut( esc_html__( 'Client Portal Password Reset', 'zero-bs-crm' ) ); ?>',
1724                                    '<?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>',
1725                                    'info'
1726                                );
1727
1728                            } else {
1729
1730                                // swal confirm
1731                                swal(
1732                                    '<?php zeroBSCRM_slashOut( esc_html__( 'Client Portal Password Reset Error', 'zero-bs-crm' ) ); ?>',
1733                                    '<?php zeroBSCRM_slashOut( esc_html__( 'Error: Client Portal password has not been reset for this contact.', 'zero-bs-crm' ) ); ?>',
1734                                    'info'
1735                                );
1736
1737                            }
1738
1739
1740                        }
1741                    }), i.fail(function(e) {
1742                        //error
1743                    });
1744
1745                });
1746
1747
1748                // bind create
1749                jQuery('.wp-user-generate').off("click").on('click',function(e){
1750                    email = jQuery('#email').val();
1751                    customerid = 
1752                    <?php
1753                    if ( ! empty( $contact['id'] ) && $contact['id'] > 0 ) {
1754                        echo esc_html( $contact['id'] );
1755                    } else {
1756                        echo -1;
1757                    }
1758                    ?>
1759                                ;
1760                    if(email == ''){
1761                        alert("The email field is blank. Please fill in the email and save");
1762                        return false;
1763                    }
1764                    var t = {
1765                        action: "zbs_new_user",
1766                        email: email,
1767                        cid: customerid,
1768                        security: jQuery( '#newwp-ajax-nonce' ).val(),
1769                    }                    
1770                    i = jQuery.ajax({
1771                        url: ajaxurl,
1772                        type: "POST",
1773                        data: t,
1774                        dataType: "json"
1775                    });
1776                    i.done(function(e) {
1777                        console.log(e);
1778                        if(e.success){
1779                            jQuery('.zbs-user-id').html(e.user_id);
1780                            jQuery('.no-gen').remove();
1781                            jQuery('.waiting-togen').html('<div class="alert alert-success">Success: ' + e.message + '</div>');
1782                        } else {
1783                            jQuery('.no-gen').remove();
1784                            jQuery('.waiting-togen').html('<div class="alert alert-danger">Error: ' + e.message + '</div>');
1785                        }
1786                    }), i.fail(function(e) {
1787                        //error
1788                    });
1789                });
1790
1791            });
1792
1793
1794        </script>
1795        <?php
1796
1797        // PerfTest: zeroBSCRM_performanceTest_finishTimer('portal-draw');
1798        // PerfTest: zeroBSCRM_performanceTest_finishTimer('portal');
1799    }
1800}
1801
1802/*
1803======================================================
1804    / Create Client Portal
1805    ====================================================== */
1806
1807/*
1808======================================================
1809    Create Social Box
1810    ====================================================== */
1811
1812class zeroBS__Metabox_ContactSocial extends zeroBS__Metabox {
1813
1814    public function __construct( $plugin_file ) {
1815
1816        $this->objType         = 'contact';
1817        $this->metaboxID       = 'zerobs-customer-social';
1818        $this->metaboxTitle    = __( 'Social Profiles', 'zero-bs-crm' );
1819        $this->metaboxScreen   = 'zbs-add-edit-contact-edit'; // 'zerobs_edit_contact'; // we can use anything here as is now using our func
1820        $this->metaboxArea     = 'side';
1821        $this->metaboxLocation = 'high';
1822        $this->capabilities    = array(
1823
1824            'can_hide'        => true, // can be hidden
1825            'areas'           => array( 'side' ), // areas can be dragged to - normal side = only areas currently
1826            'can_accept_tabs' => false,  // can/can't accept tabs onto it
1827            'can_become_tab'  => false, // can be added as tab
1828            'can_minimise'    => true, // can be minimised
1829
1830        );
1831
1832        // call this
1833        $this->initMetabox();
1834    }
1835
1836    public function html( $contact, $metabox ) {
1837
1838        global $plugin_page, $zbs;
1839
1840        // declare + load existing
1841        global $zbsSocialAccountTypes;
1842        $zbsSocials = false;
1843        if ( isset( $contact['id'] ) ) {
1844            $zbsSocials = $zbs->DAL->contacts->getContactSocials( $contact['id'] ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase,WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
1845        }
1846
1847        if ( count( $zbsSocialAccountTypes ) > 0 ) {
1848            foreach ( $zbsSocialAccountTypes as $socialKey => $socialAccType ) {
1849
1850                ?>
1851            <div class="zbs-social-acc <?php echo esc_attr( $socialAccType['slug'] ); ?>" title="<?php echo esc_attr( $socialAccType['name'] ); ?>">
1852                <?php
1853                if ( ! empty( $zbsSocials[ $socialKey ] ) ) {
1854
1855                    // got acc? link to it
1856                    $socialLink = zeroBSCRM_getSocialLink( $socialKey, $zbsSocials );
1857
1858                    ?>
1859                    <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>
1860                <?php } else { ?>
1861                    <i class="fa <?php echo esc_attr( $socialAccType['fa'] ); ?>" aria-hidden="true"></i>
1862                <?php } ?>
1863                <input
1864                    type="text"
1865                    class="zbs-social-acc-input zbs-dc"
1866                    title="<?php echo esc_attr( $socialAccType['name'] ); ?>"
1867                    name="zbs-social-<?php echo esc_attr( $socialAccType['slug'] ); ?>"
1868                    id="zbs-social-<?php echo esc_attr( $socialAccType['slug'] ); ?>"
1869                    value="<?php echo ! empty( $zbsSocials[ $socialKey ] ) ? esc_attr( $zbsSocials[ $socialKey ] ) : ''; ?>"
1870                    placeholder="<?php echo esc_attr( $socialAccType['placeholder'] ); ?>"
1871                />
1872            </div>
1873                            <?php
1874
1875            }
1876        }
1877
1878        // ++ get counts etc.
1879    }
1880
1881    public function save_data( $contact_id, $contact ) {
1882
1883            $zbsSocials = array();
1884
1885            global $zbsSocialAccountTypes;
1886        foreach ( $zbsSocialAccountTypes as $socialKey => $socialAccType ) {
1887
1888            // set
1889            $zbsSocials[ $socialKey ] = false;
1890
1891            // get from post if present
1892            if ( isset( $_POST[ 'zbs-social-' . $socialAccType['slug'] ] ) && ! empty( $_POST[ 'zbs-social-' . $socialAccType['slug'] ] ) ) {
1893                $zbsSocials[ $socialKey ] = sanitize_text_field( $_POST[ 'zbs-social-' . $socialAccType['slug'] ] );
1894            }
1895        }
1896
1897            zeroBS_updateCustomerSocialAccounts( $contact_id, $zbsSocials );
1898
1899        return $contact;
1900    }
1901}
1902
1903/*
1904======================================================
1905    / Create Social Box
1906    ====================================================== */
1907
1908/*
1909======================================================
1910    Create AKA Box
1911    ====================================================== */
1912
1913class zeroBS__Metabox_ContactAKA extends zeroBS__Metabox {
1914
1915    public function __construct( $plugin_file ) {
1916
1917        $this->objType         = 'contact';
1918        $this->metaboxID       = 'zerobs-customer-aka';
1919        $this->metaboxTitle    = __( 'Contact Aliases (AKA)', 'zero-bs-crm' );
1920        $this->metaboxScreen   = 'zbs-add-edit-contact-edit'; // 'zerobs_edit_contact'; // we can use anything here as is now using our func
1921        $this->metaboxArea     = 'side';
1922        $this->metaboxLocation = 'low';
1923        $this->capabilities    = array(
1924
1925            'can_hide'        => true, // can be hidden
1926            'areas'           => array( 'side' ), // areas can be dragged to - normal side = only areas currently
1927            'can_accept_tabs' => false,  // can/can't accept tabs onto it
1928            'can_become_tab'  => false, // can be added as tab
1929            'can_minimise'    => true, // can be minimised
1930
1931        );
1932
1933        // call this
1934        $this->initMetabox();
1935    }
1936
1937    public function html( $contact, $metabox ) {
1938
1939        global $plugin_page, $zbs;
1940        $screen = get_current_screen();
1941        ?>
1942        <div class="ui active inverted dimmer" style="display:none" id="zbs-aka-alias-loader"></div>
1943        <?php
1944
1945        #} Rather than reload all the time :)
1946        global $zbsContactEditing;
1947
1948        #} retrieve
1949        // $zbsCustomer = get_post_meta($contact['id'], 'zbs_customer_meta', true);
1950        if ( ! isset( $zbsContactEditing ) && isset( $contact['id'] ) ) {
1951            $zbsCustomer       = zeroBS_getCustomer( $contact['id'], false, false, false );
1952            $zbsContactEditing = $zbsCustomer;
1953        } else {
1954            $zbsCustomer = $zbsContactEditing;
1955        }
1956
1957        if ( gettype( $zbsCustomer ) != 'array' ) {
1958
1959            // new cust, can't add till saved.
1960            ?>
1961            <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>
1962            <?php
1963
1964        } else {
1965
1966            // customer saved, so proceed - aka mode
1967
1968            // declare + load existing
1969            $customerAliases = zeroBS_getCustomerAliases( $contact['id'] );
1970
1971            ?>
1972            <div id="zbs-aka-alias-wrap">
1973            <?php
1974
1975            // each alias: ID,aka_alias,aka_create,aka_lastupdated
1976            if ( is_array( $customerAliases ) && count( $customerAliases ) > 0 ) {
1977                foreach ( $customerAliases as $alias ) {
1978
1979                    ?>
1980                <div class="zbs-aka-alias" id="zbs-aka-alias-<?php echo esc_attr( $alias['ID'] ); ?>">
1981                    <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>
1982                </div>
1983                    <?php
1984
1985                }
1986            }
1987
1988            ?>
1989            </div><div id="zbs-aka-alias-input-wrap">
1990                <input type="text" class="zbs-aka-alias-input" placeholder="<?php esc_attr_e( 'Add Alias.. e.g.', 'zero-bs-crm' ); ?> mike2@domain.com" />
1991                <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>
1992                        <button type="button" class="ui small black button primary" id="zbs-aka-alias-add"><?php esc_html_e( 'Add Alias', 'zero-bs-crm' ); ?></button>
1993            </div>
1994
1995            <script type="text/javascript">
1996            var zbsAliasAKABlocker = false;
1997            jQuery(function(){
1998
1999                jQuery('.zbs-aka-alias-input').on( 'keydown', function(){
2000                            
2001                    // hide 'must be valid email'
2002                    jQuery('#zbs-aka-alias-input-msg').hide();
2003
2004                });
2005
2006                jQuery('#zbs-aka-alias-add').off('click').on( 'click', function(){
2007
2008                    var v = jQuery('.zbs-aka-alias-input').val();
2009                    
2010                    if ( typeof v === 'string' ) {
2011                        v = v.trim();
2012                    }
2013
2014                    // lazy check for now
2015                    if (v != "" && zbscrm_JS_validateEmail(v)){
2016
2017                        // blocker
2018                        if (!window.zbsAliasAKABlocker){
2019
2020                            // block
2021                            window.zbsAliasAKABlocker = true;
2022                            jQuery('#zbs-aka-alias-loader').show();
2023
2024                                        // postbag!
2025                                        var data = {
2026                                        'action': 'addAlias',
2027                                        'cid': 
2028                                        <?php
2029                                        if ( ! empty( $contact['id'] ) && $contact['id'] > 0 ) {
2030                                            echo esc_html( $contact['id'] );
2031                                        } else {
2032                                            echo -1;
2033                                        }
2034                                        ?>
2035                                                ,
2036                                        'aka': v,
2037                                        'sec': window.zbscrmjs_secToken
2038                                        };
2039
2040                                        // Send it Pat :D
2041                                        jQuery.ajax({
2042                                                type: "POST",
2043                                                url: ajaxurl, // admin side is just ajaxurl not wptbpAJAX.ajaxurl,
2044                                                "data": data,
2045                                                dataType: 'json',
2046                                                timeout: 20000,
2047                                                success: function(response) {
2048
2049                                                if (typeof response.res != "undefined"){
2050
2051                                                    //console.log('added:',response);
2052
2053                                                    var id = response.res;
2054                                                    var alias = v;
2055
2056                                                    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>';
2057
2058                                                    // add to ui
2059                                                    jQuery('#zbs-aka-alias-wrap').append(lineHTML);
2060
2061                                                    // empty this
2062                                                    jQuery('.zbs-aka-alias-input').val('');
2063
2064                                                    // bind
2065                                                    setTimeout(function(){
2066
2067                                                        zeroBSJS_bindAKAMode();
2068
2069                                                    },0);
2070
2071                                                    //unblock
2072                                                    window.zbsAliasAKABlocker = false;
2073                                                    jQuery('#zbs-aka-alias-loader').hide();
2074
2075                                                } else {
2076
2077                                                    if (typeof response.fail != "undefined"){
2078
2079                                                        if (response.fail == 'existing'){
2080
2081                                                            // already in use err
2082                                                            swal(
2083                                                                '<?php esc_html_e( 'Error', 'zero-bs-crm' ); ?>',
2084                                                                '<?php esc_html_e( 'This Alias is already in use by another contact.', 'zero-bs-crm' ); ?>',
2085                                                                'warning'
2086                                                            );
2087
2088                                                        }
2089
2090                                                    } else {
2091
2092                                                        // general err
2093                                                        swal(
2094                                                            '<?php esc_html_e( 'Error', 'zero-bs-crm' ); ?>',
2095                                                            '<?php esc_html_e( 'There was an error adding this alias', 'zero-bs-crm' ); ?>',
2096                                                            'warning'
2097                                                        );
2098
2099                                                    }
2100                                                    //unblock
2101                                                    window.zbsAliasAKABlocker = false;
2102                                                    jQuery('#zbs-aka-alias-loader').hide();
2103                                                }
2104
2105                                                },
2106                                                error: function(response){
2107
2108                                                    // err
2109                                                    swal(
2110                                                        '<?php esc_html_e( 'Error', 'zero-bs-crm' ); ?>',
2111                                                        '<?php esc_html_e( 'There was an error adding this alias', 'zero-bs-crm' ); ?>',
2112                                                        'warning'
2113                                                    );
2114                                                    //unblock
2115                                                    window.zbsAliasAKABlocker = false;
2116                                                    jQuery('#zbs-aka-alias-loader').hide();
2117
2118                                                }
2119
2120                                            });
2121
2122
2123                        } // / blocker
2124
2125                    } // / if not empty 
2126                    else {
2127
2128                        // not valid email, showxxx
2129                        jQuery('#zbs-aka-alias-input-msg').show();
2130
2131
2132
2133                        // hide after 2s
2134                        setTimeout(function(){
2135                            jQuery('#zbs-aka-alias-input-msg').hide();
2136                        },2000);
2137
2138                    }
2139
2140                });
2141
2142                
2143                // other bind
2144                zeroBSJS_bindAKAMode();
2145
2146
2147            });
2148    
2149            function zeroBSJS_bindAKAMode(){
2150
2151                // hover over
2152                jQuery('.zbs-aka-alias').on( 'mouseenter', function () {
2153                    jQuery(this).addClass("hovering");
2154                }).on( 'mouseleave', function () {
2155                    jQuery(this).removeClass("hovering");
2156                });
2157
2158                // remoe aka
2159                jQuery('.zbs-aka-alias-remove').off('click').on( 'click', function(){
2160
2161                    // blocker
2162                    if (!window.zbsAliasAKABlocker){
2163
2164                        // block
2165                        window.zbsAliasAKABlocker = true;
2166                        jQuery('#zbs-aka-alias-loader').show();
2167
2168                        // get id
2169                        var akaID = jQuery(this).attr('data-akaid');
2170
2171                        if (akaID > 0){
2172
2173
2174                                // postbag!
2175                                var data = {
2176                                'action': 'removeAlias',
2177                                'cid': 
2178                                <?php
2179                                if ( ! empty( $contact['id'] ) && $contact['id'] > 0 ) {
2180                                    echo esc_html( $contact['id'] );
2181                                } else {
2182                                    echo -1;
2183                                }
2184                                ?>
2185                                        ,
2186                                'akaid': akaID,
2187                                'sec': window.zbscrmjs_secToken
2188                                };
2189
2190                                // Send it Pat :D
2191                                jQuery.ajax({
2192                                        type: "POST",
2193                                        url: ajaxurl, // admin side is just ajaxurl not wptbpAJAX.ajaxurl,
2194                                        "data": data,
2195                                        dataType: 'json',
2196                                        timeout: 20000,
2197                                        success: function(response) {
2198
2199                                        if (typeof response.res != "undefined"){
2200
2201                                            console.log('removed:',response);
2202
2203                                            var lID = akaID;
2204
2205                                            // remove from ui
2206                                            jQuery('#zbs-aka-alias-' + lID).remove();
2207
2208                                            //unblock
2209                                            window.zbsAliasAKABlocker = false;
2210                                            jQuery('#zbs-aka-alias-loader').hide();
2211
2212                                        } else {
2213
2214                                            // err
2215                                            swal(
2216                                                '<?php esc_html_e( 'Error', 'zero-bs-crm' ); ?>',
2217                                                '<?php esc_html_e( 'There was an error removing this alias', 'zero-bs-crm' ); ?>',
2218                                                'warning'
2219                                            );
2220                                            //unblock
2221                                            window.zbsAliasAKABlocker = false;
2222                                            jQuery('#zbs-aka-alias-loader').hide();
2223                                        }
2224
2225                                        },
2226                                        error: function(response){
2227
2228                                            // err
2229                                            swal(
2230                                                '<?php esc_html_e( 'Error', 'zero-bs-crm' ); ?>',
2231                                                '<?php esc_html_e( 'There was an error removing this alias', 'zero-bs-crm' ); ?>',
2232                                                'warning'
2233                                            );
2234                                            //unblock
2235                                            window.zbsAliasAKABlocker = false;
2236                                            jQuery('#zbs-aka-alias-loader').hide();
2237
2238                                        }
2239
2240                                    });
2241
2242
2243                        } // / akai id present
2244
2245                    }
2246
2247                });
2248            }
2249
2250            </script>
2251            <?php
2252
2253        } // / if cust defined
2254    }
2255}
2256
2257/*
2258======================================================
2259    / Create AKA Box
2260    ====================================================== */
2261
2262/*
2263======================================================
2264    Create Tags Box
2265    ====================================================== */
2266
2267class zeroBS__Metabox_ContactTags extends zeroBS__Metabox_Tags {
2268
2269    public function __construct( $plugin_file ) {
2270
2271        $this->objTypeID       = ZBS_TYPE_CONTACT;
2272        $this->objType         = 'contact';
2273        $this->metaboxID       = 'zerobs-customer-tags';
2274        $this->metaboxTitle    = __( 'Contact Tags', 'zero-bs-crm' );
2275        $this->metaboxScreen   = 'zbs-add-edit-contact-edit'; // 'zerobs_edit_contact'; // we can use anything here as is now using our func
2276        $this->metaboxArea     = 'side';
2277        $this->metaboxLocation = 'high';
2278        $this->showSuggestions = true;
2279        $this->capabilities    = array(
2280
2281            'can_hide'        => true, // can be hidden
2282            'areas'           => array( 'side' ), // areas can be dragged to - normal side = only areas currently
2283            'can_accept_tabs' => false,  // can/can't accept tabs onto it
2284            'can_become_tab'  => false, // can be added as tab
2285            'can_minimise'    => true, // can be minimised
2286
2287        );
2288
2289        // call this
2290        $this->initMetabox();
2291    }
2292
2293    // html + save dealt with by parent class :)
2294}
2295
2296/*
2297======================================================
2298    / Create Tags Box
2299    ====================================================== */
2300
2301/*
2302======================================================
2303    Create Logs Box
2304    ====================================================== */
2305
2306class zeroBS__Metabox_ContactLogs extends zeroBS__Metabox_LogsV2 {
2307
2308    public function __construct( $plugin_file ) {
2309
2310        $this->objtypeid = ZBS_TYPE_CONTACT;
2311
2312        $this->objType         = 'contact';
2313        $this->metaboxID       = 'zerobs-customer-logs';
2314        $this->metaboxTitle    = __( 'Activity Log', 'zero-bs-crm' );
2315        $this->metaboxScreen   = 'zbs-add-edit-contact-edit'; // 'zerobs_edit_contact'; // we can use anything here as is now using our func
2316        $this->metaboxArea     = 'normal';
2317        $this->metaboxLocation = 'high';
2318        $this->capabilities    = array(
2319
2320            'can_hide'        => true, // can be hidden
2321            'areas'           => array( 'normal' ), // areas can be dragged to - normal side = only areas currently
2322            'can_accept_tabs' => true,  // can/can't accept tabs onto it
2323            'can_become_tab'  => true, // can be added as tab
2324            'can_minimise'    => true, // can be minimised
2325            'hide_on_new'     => true, // no point in adding logs while adding contact, add after creation :)
2326
2327        );
2328
2329        // call this
2330        $this->initMetabox();
2331    }
2332
2333    // html + save dealt with by parent class :)
2334}
2335
2336/*
2337======================================================
2338    / Create Logs Box
2339    ====================================================== */
2340
2341/*
2342======================================================
2343    "Contacts at Company" Metabox
2344    ====================================================== */
2345
2346class zeroBS__Metabox_ContactCompany extends zeroBS__Metabox {
2347
2348    public function __construct( $plugin_file ) {
2349
2350        # (language switch)
2351        $companyOrOrg = zeroBSCRM_getSetting( 'coororg' );
2352        $companyLabel = jpcrm_label_company();
2353
2354        $this->objType         = 'contact';
2355        $this->metaboxID       = 'zerobs-customer-company';
2356        $this->metaboxTitle    = __( $companyLabel, 'zero-bs-crm' );
2357        $this->metaboxScreen   = 'zbs-add-edit-contact-edit'; // 'zerobs_edit_contact'; // we can use anything here as is now using our func
2358        $this->metaboxArea     = 'side';
2359        $this->metaboxLocation = 'core';
2360        $this->capabilities    = array(
2361
2362            'can_hide'        => true, // can be hidden
2363            'areas'           => array( 'side' ), // areas can be dragged to - normal side = only areas currently
2364            'can_accept_tabs' => false,  // can/can't accept tabs onto it
2365            'can_become_tab'  => false, // can be added as tab
2366            'can_minimise'    => true, // can be minimised
2367
2368        );
2369
2370        // call this
2371        $this->initMetabox();
2372    }
2373
2374    public function html( $contact, $metabox ) {
2375
2376        global $plugin_page, $zbs;
2377
2378        // PerfTest: zeroBSCRM_performanceTest_startTimer('companydropdown');
2379
2380        # (language switch)
2381        $companyOrOrg = zeroBSCRM_getSetting( 'coororg' );
2382        $companyLabel = jpcrm_label_company();
2383
2384        #} Typeahead instead
2385        echo "<div id='zbscompnew'>";
2386        echo '<span>' . esc_html( sprintf( __( 'Type to assign %s', 'zero-bs-crm' ), jpcrm_label_company() ) ) . '</span>';
2387
2388        #} Co Name Default
2389        $coName = '';
2390        $coID   = '';
2391        if ( 'contact' == $this->objType ) {
2392            $coID = -1;
2393            if ( is_array( $contact ) && isset( $contact['id'] ) ) {
2394                $coID = zeroBS_getCustomerCompanyID( $contact['id'] );
2395            }
2396            if ( ! empty( $coID ) ) {
2397                $co = zeroBS_getCompany( $coID );
2398
2399                // 3.0
2400                if ( isset( $co ) && isset( $co['name'] ) ) {
2401                    $coName = $co['name'];
2402                }
2403
2404                // < 3.0
2405                if ( isset( $co ) && isset( $co['meta'] ) && isset( $co['meta']['coname'] ) ) {
2406                    $coName = $co['meta']['coname'];
2407                }
2408
2409                if ( empty( $coName ) && isset( $co['coname'] ) ) {
2410                    $coName = $co['coname'];
2411                }
2412            }
2413        }
2414
2415        #} Output
2416
2417        if ( get_option( 'permalink_structure' ) == '' ) {
2418            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>';
2419        } else {
2420            echo zeroBSCRM_CompanyTypeList( 'zbscrmjs_customer_setCompany', $coName, true, 'zbscrmjs_customer_changedCompany' );
2421        }
2422        #} Hidden input (real input) & Callback func
2423        ?>
2424        <input type="hidden" name="zbs_company" id="zbs_company" value="<?php echo esc_attr( $coID ); ?>" />
2425        <script type="text/javascript">
2426
2427        // custom fuction to copy company details from typeahead company deets
2428        function zbscrmjs_customer_setCompany(obj){
2429
2430            if (typeof obj.id != "undefined"){
2431
2432                // set vals
2433                jQuery("#zbs_company").val(obj.id);
2434
2435                // set dirty
2436                zbscrm_JS_addDirty('zbs-company');
2437
2438            } 
2439
2440        }
2441
2442        // custom fuction to copy company details from typeahead company deets
2443        // this one fires on any change, but here we're just using to catch empties :)
2444        // in fact, this one now overrides above^ 
2445        function zbscrmjs_customer_changedCompany(newval){
2446
2447            if (typeof newval == "undefined" || newval == ''){
2448
2449                // set vals
2450                jQuery("#zbs_company").val('');
2451
2452                // set dirty
2453                zbscrm_JS_addDirty('zbs-company');
2454
2455            }
2456
2457        }
2458
2459        </script>
2460
2461    
2462        </div>
2463        <?php
2464
2465        // PerfTest: zeroBSCRM_performanceTest_finishTimer('companydropdown');
2466    }
2467
2468    public function save_data( $contact_id, $contact ) {
2469
2470        return $contact;
2471    }
2472}
2473
2474/*
2475======================================================
2476    / "Contacts at Company" Metabox Related Funcs
2477    ====================================================== */
2478
2479/*
2480======================================================
2481    Contact Activity Metabox
2482    ====================================================== */
2483class zeroBS__Metabox_Contact_Activity extends zeroBS__Metabox {
2484
2485    public function __construct( $plugin_file ) {
2486
2487        $this->metaboxID       = 'zbs-contact-activity-metabox';
2488        $this->metaboxTitle    = __( 'Activity', 'zero-bs-crm' );
2489        $this->metaboxIcon     = 'heartbeat';
2490        $this->metaboxScreen   = 'zbs-view-contact'; // we can use anything here as is now using our func
2491        $this->metaboxArea     = 'side';
2492        $this->metaboxLocation = 'high';
2493
2494        // call this
2495        $this->initMetabox();
2496    }
2497
2498    public function html( $obj, $metabox ) {
2499
2500            global $zbs, $zeroBSCRM_logTypes;
2501
2502            $objid = -1;
2503        if ( is_array( $obj ) && isset( $obj['id'] ) ) {
2504            $objid = $obj['id'];
2505        }
2506
2507            // output any pinned logs
2508            $pinned_logs = $zbs->DAL->logs->getLogsForObj(
2509                array(
2510
2511                    'objtype'     => ZBS_TYPE_CONTACT,
2512                    'objid'       => $objid,
2513                    'only_pinned' => true,
2514                    'incMeta'     => true,
2515                    'sortByField' => 'zbsl_created',
2516                    'sortOrder'   => 'DESC',
2517                    'page'        => -1,
2518                    'perPage'     => -1,
2519                    'ignoreowner' => zeroBSCRM_DAL2_ignoreOwnership( ZBS_TYPE_CONTACT ),
2520
2521                )
2522            );
2523
2524        if ( is_array( $pinned_logs ) && count( $pinned_logs ) > 0 ) {
2525
2526            $pinned_log_count = 0;
2527
2528            echo '<h4 style="text-align:right" class="ui top attached header" id="jpcrm-pinned-logs-header">' . esc_attr__( 'Pinned Logs', 'zero-bs-crm' ) . '</h4>';
2529            echo '<div class="jpcrm-pinned-logs ui green attached segment">';
2530            foreach ( $pinned_logs as $log ) {
2531
2532                if ( is_array( $log ) && isset( $log['created'] ) ) {
2533
2534                    if ( $pinned_log_count > 0 ) {
2535
2536                        ?>
2537                            <div class="ui divider"></div>
2538                            <?php
2539
2540                    }
2541
2542                    echo '<div class="jpcrm-pinned-log">';
2543
2544                        // ico?
2545                        $ico = '';
2546                    $logKey  = strtolower( str_replace( ' ', '_', str_replace( ':', '_', $log['type'] ) ) );
2547                    if ( isset( $zeroBSCRM_logTypes['zerobs_customer'][ $logKey ] ) ) {
2548                        $ico = $zeroBSCRM_logTypes['zerobs_customer'][ $logKey ]['ico'];
2549                    }
2550                        // these are FA ico's at this point
2551
2552                        // compile this first, so can catch default (empty types)
2553                        $logTitle = '';
2554                    if ( ! empty( $ico ) ) {
2555                        $logTitle .= '<i class="fa ' . $ico . '"></i> ';
2556                    }
2557                        // DAL 2 saves type as permalinked
2558                    if ( isset( $zeroBSCRM_logTypes['zerobs_customer'][ $logKey ] ) ) {
2559                        $logTitle .= __( $zeroBSCRM_logTypes['zerobs_customer'][ $logKey ]['label'], 'zero-bs-crm' );
2560                    }
2561
2562                    ?>
2563
2564                        <?php
2565                        // short desc
2566                        if ( isset( $log['shortdesc'] ) && ! empty( $log['shortdesc'] ) ) {
2567                            ?>
2568                            <h4 class="jpcrm-pinned-log-shortdesc">
2569                                <?php echo $logTitle . ' | ' . esc_html( $log['shortdesc'] ); ?>
2570                            </h4>
2571                            <?php } ?>
2572
2573                            <?php
2574                            // long desc
2575                            if ( isset( $log['longdesc'] ) && ! empty( $log['longdesc'] ) ) {
2576                                ?>
2577                            <div class="jpcrm-pinned-log-longdesc">
2578                                <?php echo wp_kses( html_entity_decode( $log['longdesc'], ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ), $zbs->acceptable_restricted_html ); ?>
2579                            </div>
2580                            <?php } ?>
2581
2582                            <div class="jpcrm-pinned-log-author-date-meta">
2583                            <?php
2584                            $meta_string = '';
2585                            if ( ! empty( $log['author'] ) ) {
2586                                $meta_string = $log['author'];
2587                            }
2588                            if ( ! empty( $log['createduts'] ) && $log['createduts'] > 0 ) {
2589                                if ( ! empty( $meta_string ) ) {
2590                                    $meta_string .= ' &mdash; ';
2591                                }
2592                                $meta_string .= jpcrm_uts_to_date_str( $log['createduts'] );
2593                            }
2594
2595                            echo esc_html( $meta_string );
2596
2597                            ?>
2598                            </div>
2599
2600                        </div>
2601                        <?php
2602
2603                        ++$pinned_log_count;
2604
2605                }
2606            }
2607
2608            ?>
2609                </div>
2610                <?php
2611
2612        }
2613
2614            // normal activity output
2615            echo '<div class="zbs-activity">';
2616                echo '<div class="">';
2617                    $zbsCustomerActivity = zeroBSCRM_getContactLogs( $objid, true, 100, 0, '', false );
2618                    zeroBSCRM_html_contactTimeline( $objid, $zbsCustomerActivity, $obj );
2619                echo '</div>';
2620            echo '</div>';
2621    }
2622
2623    // nothing to save here.
2624    public function save_data( $objID, $obj ) {
2625        return $obj;
2626    }
2627}
2628
2629/*
2630======================================================
2631    / Contact Activity Metabox
2632    ====================================================== */