Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 535
0.00% covered (danger)
0.00%
0 / 18
CRAP
0.00% covered (danger)
0.00%
0 / 7
zeroBSCRM_QuotesMetaboxSetup
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
zeroBS__Metabox_Quote
0.00% covered (danger)
0.00%
0 / 239
0.00% covered (danger)
0.00%
0 / 5
5550
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
2
 html
0.00% covered (danger)
0.00%
0 / 140
0.00% covered (danger)
0.00%
0 / 1
1406
 save_data
0.00% covered (danger)
0.00%
0 / 74
0.00% covered (danger)
0.00%
0 / 1
992
 post_save_data
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
20
 updateMessage
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
zeroBS__Metabox_QuoteContent
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 2
42
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
2
 html
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
30
zeroBS__Metabox_QuoteNextStep
0.00% covered (danger)
0.00%
0 / 89
0.00% covered (danger)
0.00%
0 / 2
930
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
2
 html
0.00% covered (danger)
0.00%
0 / 72
0.00% covered (danger)
0.00%
0 / 1
870
zeroBS__Metabox_QuoteFiles
0.00% covered (danger)
0.00%
0 / 69
0.00% covered (danger)
0.00%
0 / 3
462
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 / 31
0.00% covered (danger)
0.00%
0 / 1
56
 save_data
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
182
zeroBS__Metabox_QuoteAcceptedDetails
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 2
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
6
 html
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 1
306
zeroBS__Metabox_QuoteTags
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_QuoteActions
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 2
42
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
2
 html
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
30
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_QuotesMetaboxSetup() {
20
21    // main detail
22    $zeroBS__Metabox_Quote = new zeroBS__Metabox_Quote( __FILE__ );
23
24    // quote content box
25    $zeroBS__Metabox_QuoteContent = new zeroBS__Metabox_QuoteContent( __FILE__ );
26
27    // quote next step box (publish etc.)
28    $zeroBS__Metabox_QuoteNextStep = new zeroBS__Metabox_QuoteNextStep( __FILE__ );
29
30    // quote actions box
31    $zeroBS__Metabox_QuoteActions = new zeroBS__Metabox_QuoteActions( __FILE__ );
32
33    // quote tags box
34    $zeroBS__Metabox_QuoteTags = new zeroBS__Metabox_QuoteTags( __FILE__ );
35
36    // quote accepted details
37    $zeroBS__Metabox_QuoteAcceptedDetails = new zeroBS__Metabox_QuoteAcceptedDetails( __FILE__ );
38
39    // files
40    $zeroBS__Metabox_QuoteFiles = new zeroBS__Metabox_QuoteFiles( __FILE__ );
41}
42
43    add_action( 'admin_init', 'zeroBSCRM_QuotesMetaboxSetup' );
44
45    /*
46        $zeroBS__MetaboxQuote = new zeroBS__MetaboxQuote( __FILE__ );
47        $zeroBS__QuoteContentMetabox = new zeroBS__QuoteContentMetabox( __FILE__ );
48        $zeroBS__QuoteActionsMetabox = new zeroBS__QuoteActionsMetabox( __FILE__ );
49        $zeroBS__QuoteStatusMetabox = new zeroBS__QuoteStatusMetabox( __FILE__ );
50    */
51
52/*
53======================================================
54    / Init Func
55    ====================================================== */
56
57/*
58======================================================
59    Quote Metabox
60    ====================================================== */
61
62class zeroBS__Metabox_Quote extends zeroBS__Metabox {
63
64    // this is for catching 'new' quotes
65    private $newRecordNeedsRedir = false;
66
67    public function __construct( $plugin_file ) {
68
69        // set these
70        $this->objType         = 'quote';
71        $this->metaboxID       = 'zerobs-quote-edit';
72        $this->metaboxTitle    = __( 'Step 1: Quote Details', 'zero-bs-crm' ); // will be headless anyhow
73        $this->headless        = true;
74        $this->metaboxScreen   = 'zbs-add-edit-quote-edit';
75        $this->metaboxArea     = 'normal';
76        $this->metaboxLocation = 'high';
77        $this->saveOrder       = 1;
78        $this->capabilities    = array(
79
80            'can_hide'        => false, // can be hidden
81            'areas'           => array( 'normal' ), // areas can be dragged to - normal side = only areas currently
82            'can_accept_tabs' => true,  // can/can't accept tabs onto it
83            'can_become_tab'  => false, // can be added as tab
84            'can_minimise'    => true, // can be minimised
85            'can_move'        => true, // can be moved
86
87        );
88
89        // call this
90        $this->initMetabox();
91    }
92
93    public function html( $quote, $metabox ) {
94
95            // localise ID
96            $quoteID = -1;
97        if ( is_array( $quote ) && isset( $quote['id'] ) ) {
98            $quoteID = (int) $quote['id'];
99        }
100
101            // if new + $zbsObjDataPrefill passed, use that instead of loaded trans.
102        if ( $quoteID == -1 ) {
103            global $zbsObjDataPrefill;
104            $quote = $zbsObjDataPrefill;
105        }
106
107            global $zbs;
108
109            // Define Quote statuses.
110            $acceptable_quote_statuses = jpcrm_get_quote_statuses();
111
112            // status
113            $status = $acceptable_quote_statuses['draft'];
114        if ( is_array( $quote ) && isset( $quote['status'] ) ) {
115            if ( $quote['status'] === -2 ) {
116                $status = $acceptable_quote_statuses['published'];
117            }
118            if ( $quote['status'] === 1 ) {
119                $status = $acceptable_quote_statuses['accepted'];
120            }
121        }
122            // Debug echo 'Quote:<pre>'.print_r($quote,1).'</pre>';
123
124        ?>                
125                <script type="text/javascript">var zbscrmjs_secToken = '<?php echo esc_js( wp_create_nonce( 'zbscrmjs-ajax-nonce' ) ); ?>';</script>
126                <?php
127
128                #} retrieve
129                // some legacy bits from CPT days:
130                $quoteContactID = -1;
131                if ( is_array( $quote ) && isset( $quote['contact'] ) && is_array( $quote['contact'] ) && count( $quote['contact'] ) > 0 ) {
132                    $quoteContactID = $quote['contact'][0]['id']; // get_post_meta($post->ID, 'zbs_customer_quote_customer', true);
133                }
134                $templateUsed = -1;
135                if ( is_array( $quote ) && isset( $quote['template'] ) ) {
136                    $templateUsed = $quote['template']; // get_post_meta($post->ID, 'zbs_quote_template_id', true);
137                }
138
139                #} this is a temp weird one, just passing onto meta for now (long day):
140                // ? Not used DAL3?
141                // $zbsTemplated = get_post_meta($post->ID, 'templated', true);
142                // if (!empty($zbsTemplated)) $quote['templated'] = true;
143                // quick WH predictive hack, not sure if viable - to test DAL3
144                $quote['templated'] = false;
145                if ( $templateUsed !== -1 && ! empty( $templateUsed ) ) {
146                    $quote['templated'] = true;
147                }
148
149                #} if customer id is empty, but prefill isn't, use prefill
150                if ( $quoteContactID == -1 && isset( $_GET['zbsprefillcust'] ) ) {
151                    $quoteContactID = (int) $_GET['zbsprefillcust'];
152                }
153
154                #} pass to other metaboxes (cache?)
155                global $zbsCurrentEditQuote;
156                $zbsCurrentEditQuote = $quote;
157
158                #} Retrieve fields from global
159                global $zbsCustomerQuoteFields;
160                $fields = $zbsCustomerQuoteFields;
161                // Debug echo 'Fields:<pre>'.print_r($fields,1).'</pre>';
162
163                #} Using "Quote Builder" or not?
164                $useQuoteBuilder = zeroBSCRM_getSetting( 'usequotebuilder' );
165
166                // Inputs out:
167
168                #} New quote?
169                if ( ! isset( $quote['id'] ) ) {
170                    echo '<input type="hidden" name="zbscrm_newquote" value="1" />';
171                }
172
173                #} pass this if already templated:
174                if ( $useQuoteBuilder == 1 && isset( $quote['template'] ) ) {
175                    echo '<input type="hidden" name="zbscrm_templated" id="zbscrm_templated" value="1" />';
176                }
177
178                #} Nonce used for loading quote template, left in for now, could be centralised to normal sec nonce
179                echo '<input type="hidden" name="quo-ajax-nonce" id="quo-ajax-nonce" value="' . esc_attr( wp_create_nonce( 'quo-ajax-nonce' ) ) . '" />';
180
181                // we pass the hash along the chain here too :)
182                echo '<input type="hidden" name="zbscq_hash" id="zbscq_hash" value="' . ( isset( $quote['hash'] ) ? esc_attr( $quote['hash'] ) : '' ) . '" />';
183                ?>
184                    <div>
185                        <div class="jpcrm-form-grid" id="wptbpMetaBoxMainItem">
186                <?php
187
188                // DAL3 only show after saved, easier
189                if ( ! empty( $quoteID ) && $quoteID > 0 ) {
190                    ?>
191                                <div class="jpcrm-form-group jpcrm-form-group-span-2">
192                                    <label class="jpcrm-form-label"><?php esc_html_e( 'Quote (ID)', 'zero-bs-crm' ); ?>:</label>
193                            <?php
194                                echo '<b>' . esc_html( $quoteID ) . '</b>'; //phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
195                            ?>
196                                    <input type="hidden" name="zbsquoteid" value="<?php echo esc_attr( $quoteID ); //phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase ?>" />
197                                </div>
198                            <?php
199
200                }
201                // Quote status.
202                if ( $quoteID > 0 ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
203                    ?>
204                
205                    <div class="jpcrm-form-group jpcrm-form-group-span-2">
206                        <label class="jpcrm-form-label" for="quote_status"><?php esc_html_e( 'Status', 'zero-bs-crm' ); ?>:</label>
207                        <select class="form-control" id="quote_status" name="quote_status">
208                            <?php
209                            foreach ( $acceptable_quote_statuses as $status_opt => $status_str ) {
210                                $sel = '';
211                                if ( $status_str === $status ) {
212                                    $sel = ' selected="selected"';
213                                }
214                                /* Translators:  %s is the Quote status. */
215                                echo '<option value="' . esc_attr( $status_opt ) . '"' . esc_attr( $sel ) . '>' . esc_html( sprintf( __( '%s', 'zero-bs-crm' ), $status_str ) ) . '</option>'; // phpcs:ignore WordPress.WP.I18n.NoEmptyStrings
216                            }
217                            ?>
218                        </select>
219                    </div>
220                        <?php
221                } // end if
222
223                ?>
224                        <div class="jpcrm-form-group jpcrm-form-group-span-2 jpcrm-override-typeahead-padding">
225                        <label class="jpcrm-form-label"><?php esc_html_e( 'Contact', 'zero-bs-crm' ); ?>:</label>
226                    <?php
227
228                    #} 27/09/16 - switched select for typeahead
229
230                        #} Any customer?
231                        $prefillStr = ''; if ( isset( $quoteContactID ) && ! empty( $quoteContactID ) ) {
232
233                            $prefillStr = $zbs->DAL->contacts->getContactNameWithFallback( $quoteContactID );
234
235                        }
236
237                        #} Output select box
238                        echo zeroBSCRM_CustomerTypeList( 'zbscrmjs_quoteCustomerSelect', $prefillStr, true, 'zbscrmjs_quote_unsetCustomer' );
239
240                        #} Output input which will pass the value via post
241                        ?>
242                            <input type="hidden" name="zbscq_customer" id="zbscq_customer" value="<?php echo esc_attr( $quoteContactID ); ?>" />
243                            <?php
244
245                            #} Output function which will copy over value - maybe later move to js
246                            ?>
247                            <script type="text/javascript">
248
249                                jQuery(function(){
250
251                                    // bind 
252                                    setTimeout(function(){
253                                        zeroBSCRMJS_showContactLinkIf(jQuery("#zbscq_customer").val());
254                                    },0);
255
256                                });
257
258                                function zbscrmjs_quoteCustomerSelect(cust){
259
260                                    // pass id to hidden input
261                                    jQuery('#zbscq_customer').val(cust.id);
262
263                                    // enable/disable button if present (here is def present)
264                                    jQuery('#zbsQuoteBuilderStep2').prop( 'disabled', false );
265                                    jQuery('#zbsQuoteBuilderStep2info').hide();
266
267
268                                    setTimeout(function(){
269
270                                        var lID = cust.id;
271
272                                        // when inv select drop down changed, show/hide quick nav
273                                        zeroBSCRMJS_showContactLinkIf(lID);
274
275                                    },0);
276
277                                }
278
279                                function zbscrmjs_quote_unsetCustomer(o){
280
281                                    if (typeof o == "undefined" || o == ''){
282
283                                        jQuery("#zbscq_customer").val('');
284                                        //jQuery("#bill").val('');
285                                        //jQuery("#cusbill").val('');
286
287                                        setTimeout(function(){
288
289                                            // when inv select drop down changed, show/hide quick nav
290                                            zeroBSCRMJS_showContactLinkIf('');
291
292                                        },0);
293                                        
294                                    }
295                                }
296
297                                // if an contact is selected (against a trans) can 'quick nav' to contact
298                                function zeroBSCRMJS_showContactLinkIf(contactID){
299
300                                    // remove old
301                                    //jQuery('#zbs-customer-title .zbs-view-contact').remove();
302                                    jQuery('#zbs-quote-learn-nav .zbs-quote-quicknav-contact').remove();
303
304                                    if (typeof contactID != "undefined" && contactID !== null && contactID !== ''){
305
306                                        contactID = parseInt(contactID);
307                                        if (contactID > 0){
308
309                                            // seems like a legit inv, add
310
311                                            /* this was from trans meta, here just add to top
312                                                var html = '<div class="ui right floated mini animated button zbs-view-contact">';
313                                                        html += '<div class="visible content"><?php zeroBSCRM_slashOut( __( 'View', 'zero-bs-crm' ) ); ?></div>';
314                                                            html += '<div class="hidden content">';
315                                                                html += '<i class="user icon"></i>';
316                                                            html += '</div>';
317                                                        html += '</div>';
318
319                                                jQuery('#zbs-customer-title').prepend(html); */
320
321                                                // ALSO show in header bar, if so
322                                                                var navButton = '<a target="_blank" style="margin-left:6px;" class="zbs-quote-quicknav-contact ui icon button black mini labeled" href="<?php echo jpcrm_esc_link( 'edit', -1, 'zerobs_customer', true ); ?>' + contactID + '"><i class="user icon"></i> <?php zeroBSCRM_slashOut( __( 'Contact', 'zero-bs-crm' ) ); ?></a>';
323                                                jQuery('#zbs-quote-learn-nav').append(navButton);
324
325                                                // bind
326                                                //zeroBSCRMJS_bindContactLinkIf();
327                                        }
328                                    }
329
330                                }
331
332                            </script>
333                            </div>
334                        <?php
335
336                        // wh centralised 20/7/18 - 2.91+ skipFields
337                        zeroBSCRM_html_editFields( $quote, $fields, 'zbscq_' );
338
339                        #} if enabled, and new quote, or one which hasn't had the 'templated' meta key added.
340                        if ( $useQuoteBuilder == 1 && ! isset( $quote['template'] ) ) {
341                            ?>
342                            <div class="jpcrm-form-group jpcrm-form-group-span-2">
343                            <div class="zbs-move-on-wrap">
344
345                                <!-- infoz -->
346                                <h3><?php esc_html_e( 'Publish this Quote', 'zero-bs-crm' ); ?></h3>
347                                <p><?php esc_html_e( 'Do you want to use the Quote Builder to publish this quote? (This lets you email it to a client directly, for approval)', 'zero-bs-crm' ); ?></p>
348
349                                <input type="hidden" name="zbs_quote_template_id_used" id="zbs_quote_template_id_used" value="<?php echo ! empty( $templateUsed ) ? esc_attr( $templateUsed ) : ''; ?>" />
350                                <select class="form-control" name="zbs_quote_template_id" id="zbs_quote_template_id">
351                                    <option value="" disabled="disabled"><?php esc_html_e( 'Select a template', 'zero-bs-crm' ); ?>:</option>
352                                    <?php
353
354                                        $templates = zeroBS_getQuoteTemplates( true, 100, 0 );
355
356                                        #} If this quote has already selected a template it'll be stored in the meta under 'templateid'
357                                        #} But if it's not the first, we never need to show this anyway...
358
359                                    if ( count( $templates ) > 0 ) {
360                                        foreach ( $templates as $template ) {
361
362                                            $templateName = __( 'Template', 'zero-bs-crm' ) . ' ' . $template['id'];
363                                            if ( isset( $template['title'] ) && ! empty( $template['title'] ) ) {
364                                                $templateName = $template['title'] . ' (' . $template['id'] . ')';
365                                            }
366
367                                            echo '<option value="' . esc_attr( $template['id'] ) . '"';
368                                            #if (isset())
369                                            echo '>' . esc_html( $templateName ) . '</option>';
370
371                                        }
372                                    }
373
374                                    ?>
375                                    <option value=""><?php esc_html_e( 'Blank Template', 'zero-bs-crm' ); ?></option>
376                                </select>
377                                <br />
378                                <p>
379                                    <?php esc_html_e( 'Create additional quote templates', 'zero-bs-crm' ); ?>
380                                    <a href="<?php echo jpcrm_esc_link( $zbs->slugs['quote-templates'] ); ?>">
381                                        <?php esc_html_e( 'here', 'zero-bs-crm' ); ?>
382                                    </a>
383                                </p>
384                                <button type="button" id="zbsQuoteBuilderStep2" class="ui button button-primary black button-large xl"
385                                    <?php disabled( $quoteContactID <= 0 ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase ?>><?php esc_html_e( 'Use Quote Builder', 'zero-bs-crm' ); ?>
386                                </button>
387                                <?php if ( $quoteContactID <= 0 ) { /* phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase, Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace */ ?>
388                                    <p id="zbsQuoteBuilderStep2info">(<?php esc_html_e( "You'll need to assign this Quote to a contact to use this", 'zero-bs-crm' ); ?>);</p>
389                                <?php } ?>
390
391                            </div>
392                        </div>
393                        <?php } ?>
394
395                        </div>
396                    </div>
397                <?php
398    }
399
400    public function save_data( $quoteID, $quote ) {
401
402        if ( ! defined( 'ZBS_OBJ_SAVED' ) ) {
403
404            define( 'ZBS_OBJ_SAVED', 1 );
405
406            // DAL3.0+
407            global $zbs;
408
409            // check this
410            if ( empty( $quoteID ) || $quoteID < 1 ) {
411                $quoteID = -1;
412            }
413
414                // defaults, pulled from DAL obj 25/2/19
415                /*
416                    $quote = array(
417                    'title' => '',
418                    'currency' => '',
419                    'value' => '',
420                    'date' => '',
421                    'template' => '',
422                    'content' => '',
423                    'notes' => '',
424                    'send_attachments' => -1, (removed 4.0.9)
425                    'hash' => '',
426                    'lastviewed' => '',
427                    'viewed_count' => '',
428                    'accepted' => '',
429                    //'created' => '',
430                    //'lastupdated' => '',
431                );
432                */
433                $extraMeta = array(); // can pass any additional meta here
434
435                // retrieve _POST into arr
436                // global $zbsCustomerQuoteFields;
437                // $zbsCustomerQuoteMeta = zeroBSCRM_save_fields($zbsCustomerQuoteFields,'zbscq_');
438                $autoGenAutonumbers = true; // generate if not set :)
439                $removeEmpties      = false; // req for autoGenAutonumbers
440                $quote              = zeroBS_buildObjArr( $_POST, array(), 'zbscq_', '', $removeEmpties, ZBS_TYPE_QUOTE, $autoGenAutonumbers );
441
442                // Use the tag-class function to retrieve any tags so we can add inline.
443                // Save tags against objid
444                $quote['tags'] = zeroBSCRM_tags_retrieveFromPostBag( true, ZBS_TYPE_QUOTE );
445
446                // we always get this, because it's used below, but not part of buildObjArr (currently at 3.0)
447            if ( $quoteID > 0 ) {
448                $quote['template'] = (int) $zbs->DAL->quotes->getQuoteTemplateID( $quoteID );
449            }
450
451                // content (from other metabox actually)
452            if ( isset( $_POST['zbs_quote_content'] ) ) {
453
454                #} Save content
455                // $data=htmlspecialchars($_POST['zbs_quote_content'], ENT_COMPAT);
456                $quote['content'] = wp_kses( $_POST['zbs_quote_content'], $zbs->acceptable_html ); // phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- to follow up with.
457                #} update templated vars
458                if ( isset( $_POST['zbs_quote_template_id'] ) ) {
459                    $quote['template'] = (int) sanitize_text_field( $_POST['zbs_quote_template_id'] );
460                }
461            }
462
463                #} First up, save quote id! #TRANSITIONTOMETANO
464                // DAL 3 will probs move away from this, for now leaving for refactoring round 2
465                // for now store as meta (though perhaps needs a new field zbsid)
466                $quoteOffset = zeroBSCRM_getQuoteOffset();
467                $quoteZBSID  = (int) $quoteID + $quoteOffset;
468            if ( isset( $_POST['zbsquoteid'] ) && ! empty( $_POST['zbsquoteid'] ) ) {
469                $quoteZBSID = (int) $_POST['zbsquoteid'];
470            }
471                // update_post_meta($post_id,"zbsid",$quoteID);
472                $extraMeta['zbsid'] = $quoteZBSID;
473                #} and increment this
474            if ( ! empty( $quoteZBSID ) ) {
475                zeroBSCRM_setMaxQuoteID( $quoteZBSID );
476            }
477
478                // assignments
479                $zbsQuoteContact = -1;
480            if ( isset( $_POST['zbscq_customer'] ) ) {
481                $zbsQuoteContact = (int) sanitize_text_field( $_POST['zbscq_customer'] );
482            }
483                $quote['contacts'] = ( $zbsQuoteContact > 0 ) ? array( $zbsQuoteContact ) : array();
484                $zbsQuoteCompany   = -1;
485            if ( isset( $_POST['zbscq_company'] ) ) {
486                $zbsQuoteCompany = (int) sanitize_text_field( $_POST['zbscq_company'] );
487            }
488                $quote['companies'] = ( $zbsQuoteCompany > 0 ) ? array( $zbsQuoteCompany ) : array();
489
490                /*
491                    line item (temp here from Inv metabox, not yet implemented in ui)
492                //new way..  now not limited to 30 lines as now they are stored in [] type array in JS draw
493                $zbsInvoiceLines = array();
494                foreach($_POST['zbsli_itemname'] as $k => $v){
495
496                    $ks = sanitize_text_field( $k ); // at least this
497
498                    $zbsInvoiceLines[$ks]['title']      = sanitize_text_field($_POST['zbsli_itemname'][$k]);
499                    $zbsInvoiceLines[$ks]['desc']           = sanitize_text_field($_POST['zbsli_itemdes'][$k]);
500                    $zbsInvoiceLines[$ks]['quantity']          = sanitize_text_field($_POST['zbsli_quan'][$k]);
501                    $zbsInvoiceLines[$ks]['price']         = sanitize_text_field($_POST['zbsli_price'][$k]);
502                    $zbsInvoiceLines[$ks]['tax']           = sanitize_text_field($_POST['zbsli_tax'][$k]);
503
504                }
505                if (count($zbsInvoiceLines) > 0) $invoice['lineitems'] = $zbsInvoiceLines;
506                */
507
508                // Status Overwrites (manual changes, only after initial save)
509            if ( $quoteID > 0 && isset( $_POST['quote_status'] ) ) {
510
511                switch ( $_POST['quote_status'] ) {
512
513                    case 'draft':
514                        // if changing to draft, remove any accepted date + template ID
515                        $quote['accepted'] = 0;
516                        $quote['template'] = -1;
517
518                        break;
519                    case 'published':
520                        // if changing to published, just needs accepted unsetting, and if no template, populate
521                        $quote['accepted'] = 0;
522
523                        // got template?
524
525                            // if not already set, set, otherwise leave existing set time in.
526                        if ( ! isset( $quote['template'] ) || $quote['template'] <= 0 ) {
527
528                            // hacky setting of it to unlikely cieling
529                            $quote['template'] = 99999;
530
531                        }
532
533                        break;
534                    case 'accepted':
535                        // if not already accepted, mark accepted.
536
537                            // existing
538                            $accepted = (int) $zbs->DAL->quotes->getQuoteAcceptedTime( $quoteID );
539
540                            // if not already set, set, otherwise leave existing set time in.
541                        if ( $accepted <= 0 ) {
542
543                            // set it (first time, manual)
544                            $quote['accepted']       = time();
545                            $quote['acceptedsigned'] = 'manual';
546                            $quote['acceptedip']     = '';
547
548                        }
549
550                        break;
551
552                }
553            }
554
555                // add/update
556                $addUpdateReturn = $zbs->DAL->quotes->addUpdateQuote(
557                    array(
558
559                        'id'            => $quoteID,
560                        'data'          => $quote,
561                        'extraMeta'     => $extraMeta,
562                        'limitedFields' => -1,
563
564                    )
565                );
566
567            // Note: For NEW objs, we make sure a global is set here, that other update funcs can catch
568            // ... so it's essential this one runs first!
569            // this is managed in the metabox Class :)
570            if ( $quoteID == -1 && ! empty( $addUpdateReturn ) && $addUpdateReturn != -1 ) {
571
572                $quoteID = $addUpdateReturn;
573                global $zbsJustInsertedMetaboxID;
574                $zbsJustInsertedMetaboxID = $quoteID;
575
576                // set this so it redirs
577                $this->newRecordNeedsRedir = true;
578            }
579
580            // success?
581            if ( $addUpdateReturn != -1 && $addUpdateReturn > 0 ) {
582
583                // Update Msg
584                // this adds an update message which'll go out ahead of any content
585                // 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');
586                // This adds to edit page
587                $this->updateMessage();
588
589                // catch any non-critical messages
590                $nonCriticalMessages = $zbs->DAL->getErrors( ZBS_TYPE_QUOTE );
591                if ( is_array( $nonCriticalMessages ) && count( $nonCriticalMessages ) > 0 ) {
592                    $this->dalNoticeMessage( $nonCriticalMessages );
593                }
594            } else {
595
596                // fail somehow
597                $failMessages = $zbs->DAL->getErrors( ZBS_TYPE_QUOTE );
598
599                // show msg (retrieved from DAL err stack)
600                if ( is_array( $failMessages ) && count( $failMessages ) > 0 ) {
601                    $this->dalErrorMessage( $failMessages );
602                } else {
603                    $this->dalErrorMessage( array( __( 'Insert/Update Failed with general error', 'zero-bs-crm' ) ) );
604                }
605
606                // pass the pre-fill:
607                global $zbsObjDataPrefill;
608                $zbsObjDataPrefill = $quote;
609
610            }
611        }
612
613        return $quote;
614    }
615
616    // This catches 'new' contacts + redirs to right url
617    public function post_save_data( $objID, $obj ) {
618
619        if ( $this->newRecordNeedsRedir ) {
620
621            global $zbsJustInsertedMetaboxID;
622            if ( ! empty( $zbsJustInsertedMetaboxID ) && $zbsJustInsertedMetaboxID > 0 ) {
623
624                // redir
625                wp_redirect( jpcrm_esc_link( 'edit', $zbsJustInsertedMetaboxID, $this->objType ) );
626                exit( 0 );
627
628            }
629        }
630    }
631
632    public function updateMessage() {
633
634        global $zbs;
635
636        // zbs-not-urgent means it'll auto hide after 1.5s
637        // genericified from DAL3.0
638        $msg = zeroBSCRM_UI2_messageHTML( 'info olive mini zbs-not-urgent', $zbs->DAL->typeStr( $zbs->DAL->objTypeKey( $this->objType ) ) . ' ' . __( 'Updated', 'zero-bs-crm' ), '', 'address book outline', 'contactUpdated' );
639
640        $zbs->pageMessages[] = $msg;
641    }
642}
643
644/*
645======================================================
646    / Quote Metabox
647    ====================================================== */
648
649/*
650======================================================
651    Quote Content Metabox
652    ====================================================== */
653
654class zeroBS__Metabox_QuoteContent extends zeroBS__Metabox {
655
656    public function __construct( $plugin_file ) {
657
658        // set these
659        $this->objType         = 'quote';
660        $this->metaboxID       = 'zerobs-quote-content-edit';
661        $this->metaboxTitle    = __( 'Step 2: Quote Content', 'zero-bs-crm' ); // will be headless anyhow
662        $this->headless        = true;
663        $this->metaboxScreen   = 'zbs-add-edit-quote-edit';
664        $this->metaboxArea     = 'normal';
665        $this->metaboxLocation = 'low';
666        $this->saveOrder       = 1;
667        $this->capabilities    = array(
668
669            'can_hide'        => false, // can be hidden
670            'areas'           => array( 'normal' ), // areas can be dragged to - normal side = only areas currently
671            'can_accept_tabs' => true,  // can/can't accept tabs onto it
672            'can_become_tab'  => false, // can be added as tab
673            'can_minimise'    => true, // can be minimised
674            'can_move'        => true, // can be moved
675
676        );
677
678        // call this
679        $this->initMetabox();
680    }
681
682    public function html( $quote, $metabox ) {
683
684        global $zbs;
685
686        // localise ID & content
687        $quoteID = -1;
688        if ( is_array( $quote ) && isset( $quote['id'] ) ) {
689            $quoteID = (int) $quote['id'];
690        }
691        $quote_content = '';
692        if ( is_array( $quote ) && isset( $quote['content'] ) ) {
693            $quote_content = wp_kses( $quote['content'], $zbs->acceptable_html );
694        }
695
696        // remove "Add contact form" button from Jetpack
697        remove_action( 'media_buttons', 'grunion_media_button', 999 );
698        wp_editor(
699            $quote_content,
700            'zbs_quote_content',
701            array(
702                'editor_height' => 580,
703                'wpautop'       => false,
704            )
705        );
706    }
707
708    // saved via main metabox
709}
710
711/*
712======================================================
713    / Quote Content Metabox
714    ====================================================== */
715
716/*
717======================================================
718    Quote Next Step Metabox
719    ====================================================== */
720
721class zeroBS__Metabox_QuoteNextStep extends zeroBS__Metabox {
722
723    public function __construct( $plugin_file ) {
724
725        // set these
726        $this->objType         = 'quote';
727        $this->metaboxID       = 'zerobs-quote-nextstep';
728        $this->metaboxTitle    = __( 'Step 3: Publish and Send', 'zero-bs-crm' ); // will be headless anyhow
729        $this->headless        = true;
730        $this->metaboxScreen   = 'zbs-add-edit-quote-edit';
731        $this->metaboxArea     = 'normal';
732        $this->metaboxLocation = 'low';
733        $this->saveOrder       = 1;
734        $this->capabilities    = array(
735
736            'can_hide'        => false, // can be hidden
737            'areas'           => array( 'normal' ), // areas can be dragged to - normal side = only areas currently
738            'can_accept_tabs' => true,  // can/can't accept tabs onto it
739            'can_become_tab'  => false, // can be added as tab
740            'can_minimise'    => true, // can be minimised
741            'can_move'        => true, // can be moved
742
743        );
744
745        // call this
746        $this->initMetabox();
747    }
748
749    public function html( $quote, $metabox ) {
750
751        if ( $quote === false ) {
752            $quote = array();
753        }
754        global $zbs;
755
756        // localise ID & content
757        $quoteID = -1;
758        if ( is_array( $quote ) && isset( $quote['id'] ) ) {
759            $quoteID = (int) $quote['id'];
760        }
761
762        #} retrieve
763        // some legacy bits from CPT days:
764        $quoteContactID = -1;
765        if ( is_array( $quote ) && isset( $quote['contact'] ) && is_array( $quote['contact'] ) && count( $quote['contact'] ) > 0 ) {
766            $quoteContactID = $quote['contact'][0]['id']; // get_post_meta($post->ID, 'zbs_customer_quote_customer', true);
767        }
768        $templateUsed = -1;
769        if ( is_array( $quote ) && isset( $quote['template'] ) ) {
770            $templateUsed = $quote['template']; // get_post_meta($post->ID, 'zbs_quote_template_id', true);
771        }
772
773        #} Using "Quote Builder" or not?
774        $useQuoteBuilder = zeroBSCRM_getSetting( 'usequotebuilder' );
775        $useHash         = zeroBSCRM_getSetting( 'easyaccesslinks' );
776
777        #} if enabled, and new quote, or one which hasn't had the 'templated' meta key added.
778        if ( $useQuoteBuilder == '1' ) {
779
780            // retrieve email $contactEmail = '';
781            $contactEmail = $zbs->DAL->contacts->getContactEmail( $quoteContactID );// zeroBS_contactEmail($quoteContactID);
782
783            // quick WH predictive hack, not sure if viable - to test DAL3
784            $quote['templated'] = false;
785            if ( $templateUsed !== -1 && ! empty( $templateUsed ) ) {
786                $quote['templated'] = true;
787            }
788
789            #} first load?
790            if ( gettype( $quote ) != 'array' || ! isset( $quote['templated'] ) ) {
791
792                ?>
793                            <div class="zbs-move-on-wrap" style="padding-top:30px;">
794
795                                <!-- infoz -->
796                                <h3><?php esc_html_e( 'Publish this Quote', 'zero-bs-crm' ); ?></h3>
797                                <p><?php esc_html_e( "When you've finished writing your Quote, save it here before sending on to your contact", 'zero-bs-crm' ); ?>:</p>
798
799                                <button type="button" id="zbsQuoteBuilderStep3" class="button button-primary button-large xl"><?php esc_html_e( 'Save Quote', 'zero-bs-crm' ); ?></button>
800
801                            </div>
802
803                        <?php
804
805            } else {
806
807                # already has a saved quote
808                #} If Portal is uninstalled it will break Quotes. So show a message warning them that this should be on
809                if ( ! zeroBSCRM_isExtensionInstalled( 'portal' ) ) {
810                    ?>
811                            <div class="ui message red" style="font-size:18px;">
812                                <b><i class="ui icon warning"></i><?php esc_html_e( 'Client Portal Deactivated', 'zero-bs-crm' ); ?></b>
813                                <p><?php esc_html_e( 'You have uninstalled the Client Portal. The only way you will be able to send your Quote to your contact is by downloading a PDF (needs PDF invoicing installed) and then emailing it to them manually.', 'zero-bs-crm' ); ?></p>
814                                <a class="ui button blue" href="<?php echo esc_url( admin_url( 'admin.php?page=zerobscrm-extensions' ) ); ?>"><?php esc_html_e( 'Enable Client Portal', 'zero-bs-crm' ); ?></a>
815                            </div>
816                        <?php
817                } else {
818
819                    // v3.0+ we use hash urls, so check exists
820                    $dal3HashCheck = true;
821                    if ( ( ! isset( $quote['hash'] ) || empty( $quote['hash'] ) ) ) {
822                        $dal3HashCheck = false; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
823                    }
824
825                    if ( isset( $contactEmail ) && ! empty( $contactEmail ) && zeroBSCRM_validateEmail( $contactEmail ) && ( ! $useHash || ( $useHash && $dal3HashCheck ) ) ) {
826
827                        // has email, and portal, all good
828
829                        ?>
830
831                                <div class="zbs-move-on-wrap" style="padding-top:30px;">
832                                <?php
833                                    #} Add nonce
834                                    echo '<script type="text/javascript">var zbscrmjs_secToken = \'' . esc_js( wp_create_nonce( 'zbscrmjs-ajax-nonce' ) ) . '\';</script>';
835                                ?>
836
837                                    <!-- infoz -->
838                                    <h3><?php esc_html_e( 'Email or Share', 'zero-bs-crm' ); ?></h3>
839                                    <p><?php esc_html_e( 'Great! Your Quote has been published. You can now email it to your contact, or share the link directly', 'zero-bs-crm' ); ?>:</p>
840
841                                    <?php do_action( 'zbs_quote_actions' ); ?>
842
843                                    <div class="zbsEmailOrShare">
844                                        <h4><?php esc_html_e( 'Email to Contact', 'zero-bs-crm' ); ?>:</h4>
845                                        <!-- todo -->                                    
846                                        <p><input type="text" class="form-control" id="zbsQuoteBuilderEmailTo" value="<?php echo esc_attr( $contactEmail ); ?>" placeholder="<?php esc_attr_e( 'e.g. customer@yahoo.com', 'zero-bs-crm' ); ?>" data-quoteid="<?php echo esc_attr( $quoteID ); ?>" /></p>
847                                                    <p><button type="button" id="zbsQuoteBuilderSendNotification" class="ui button black"><?php esc_html_e( 'Send Quote', 'zero-bs-crm' ); ?></button></p>
848                                        <p class="small" id="zbsQuoteBuilderEmailToErr" style="display:none"><?php esc_html_e( 'An Email Address to send to is required', 'zero-bs-crm' ); ?>!</p>
849                                    </div>
850                                            <?php
851                                            if ( property_exists( $zbs->modules, 'portal' ) ) :
852                                                $quote_id_or_hash  = $useHash ? $quote['hash'] : $quoteID;
853                                                $single_quote_slug = $zbs->modules->portal->get_endpoint( ZBS_TYPE_QUOTE );
854                                                $preview_url       = zeroBS_portal_link( $single_quote_slug, $quote_id_or_hash );
855
856                                                ?>
857                                    <div class="zbsEmailOrShare">
858                                            <h4><?php esc_html_e( 'Share the Link or', 'zero-bs-crm' ); ?> <a href="<?php echo esc_url( $preview_url ); ?>" target="_blank"><?php esc_html_e( 'preview', 'zero-bs-crm' ); ?></a>:</h4>
859                                            <p><input type="text" class="form-control" id="zbsQuoteBuilderURL" value="<?php echo esc_url( $preview_url ); ?>" /></p>
860                                    </div>  
861                                                <?php
862                                                    endif;
863                                            ?>
864
865                                    <?php
866                                    #} WH second change, only showed if dompdf extension installed
867                                    if ( zeroBSCRM_isExtensionInstalled( 'pdfinv' ) ) {
868
869                                        #} PDF Invoicing is installed
870                                        ?>
871                                                <div class="zbsEmailOrShare">
872                                                <h4><?php esc_html_e( 'Download PDF', 'zero-bs-crm' ); ?></h4>
873                                                <p><i class="file pdf outline icon red" style="font-size:30px;margin-top:10px;"></i></p>
874                                                                <input type="button" name="jpcrm_quote_download_pdf" id="jpcrm_quote_download_pdf" class="ui button black" value="<?php esc_attr_e( 'Download PDF', 'zero-bs-crm' ); ?>" />
875                                               
876                                                </div>
877                                                <script type="text/javascript">
878                                                jQuery(function(){
879
880                                                    // add your form to the end of body (outside <form>)
881                                                    var formHTML = '<form target="_blank" method="post" id="jpcrm_quote_download_pdf_form" action="">';
882                                                        formHTML += '<input type="hidden" name="jpcrm_quote_download_pdf" value="1" />';
883                                                        formHTML += '<input type="hidden" name="jpcrm_quote_id" value="<?php echo esc_attr( $quoteID ); ?>" />';
884                                                        formHTML += '<input type="hidden" name="jpcrm_quote_pdf_gen_nonce" value="<?php echo esc_attr( wp_create_nonce( 'jpcrm-quote-pdf-gen' ) ); ?>" />';
885                                                        formHTML += '</form>';
886                                                    jQuery('#wpbody').append(formHTML);
887
888                                                    // on click
889                                                    jQuery('#jpcrm_quote_download_pdf').on( 'click', function(){
890
891                                                        // submit form
892                                                        jQuery('#jpcrm_quote_download_pdf_form').submit();
893
894                                                    });
895
896                                                });                    
897                                                </script>
898                                            <?php
899
900                                    }
901                                    ?>
902                                
903                                </div>
904
905                            <?php
906
907                    } elseif ( isset( $quoteContactID ) && $quoteContactID > 0 ) {
908
909                            // Contact, but they don't have an email addr on file:
910                        ?>
911
912                                <div class="zbs-move-on-wrap" style="padding-top:30px;">
913
914                                    <h3><?php esc_html_e( 'Email or Share', 'zero-bs-crm' ); ?></h3>
915                                    <div class="zbsEmailOrShare">
916                                        <h4><?php esc_html_e( "Add Contact's Email", 'zero-bs-crm' ); ?>:</h4>
917                                        <p><?php esc_html_e( 'To proceed, edit the contact and add their email address, that way we can then send them this quote online.', 'zero-bs-crm' ); ?></p>
918                                        <p><a href="<?php echo jpcrm_esc_link( 'edit', $quoteContactID, 'zerobs_customer', true ); ?>" class="button button-primary button-large"><?php esc_html_e( 'Edit Contact', 'zero-bs-crm' ); ?></a></p>
919                                    </div>              
920
921                                </div>
922
923                            <?php
924                    } else {
925
926                        // not yet assigned to anyone.
927                        ?>
928
929                                <div class="zbs-move-on-wrap" style="padding-top:30px;">
930
931                                    <h3><?php esc_html_e( 'Email or Share', 'zero-bs-crm' ); ?></h3>
932                                    <div class="zbsEmailOrShare">
933                                        <h4><?php esc_html_e( 'Assign to Contact', 'zero-bs-crm' ); ?>:</h4>
934                                        <p><?php esc_html_e( 'To proceed, assign this quote to a contact and save it.', 'zero-bs-crm' ); ?></p>
935                                    </div>              
936
937                                </div>
938
939                            <?php
940
941                    }
942                }
943            }
944        } # if quotebuilder
945    }
946
947    // nothing to save.
948}
949
950/*
951======================================================
952    / Quote Actions Metabox
953    ====================================================== */
954
955/*
956======================================================
957    Quote files Metabox
958    ====================================================== */
959
960class zeroBS__Metabox_QuoteFiles extends zeroBS__Metabox {
961
962    public function __construct( $plugin_file ) {
963
964        // DAL3 switched for objType $this->postType = 'zerobs_customer';
965        $this->objType         = 'quote';
966        $this->metaboxID       = 'zerobs-quote-files';
967        $this->metaboxTitle    = __( 'Associated Files', 'zero-bs-crm' );
968        $this->metaboxScreen   = 'zbs-add-edit-quote-edit'; // 'zerobs_edit_contact'; // we can use anything here as is now using our func
969        $this->metaboxArea     = 'normal';
970        $this->metaboxLocation = 'low';
971        $this->capabilities    = array(
972
973            'can_hide'        => true, // can be hidden
974            'areas'           => array( 'normal' ), // areas can be dragged to - normal side = only areas currently
975            'can_accept_tabs' => true,  // can/can't accept tabs onto it
976            'can_become_tab'  => true, // can be added as tab
977            'can_minimise'    => true, // can be minimised
978
979        );
980
981        // call this
982        $this->initMetabox();
983    }
984
985    public function html( $quote, $metabox ) {
986
987            global $zbs;
988
989            $html = '';
990
991            // localise ID
992            $quoteID = -1;
993        if ( is_array( $quote ) && isset( $quote['id'] ) ) {
994            $quoteID = (int) $quote['id'];
995        }
996
997            #} retrieve
998            $zbsFiles = array();
999        if ( $quoteID > 0 ) {
1000            $zbsFiles = zeroBSCRM_files_getFiles( 'quote', $quoteID );
1001        }
1002
1003        ?>
1004                <table class="form-table wh-metatab wptbp" id="wptbpMetaBoxMainItemFiles">
1005
1006                <?php
1007
1008                    // WH only slightly updated this for DAL3 - could do with a cleanup run (contact file edit has more functionality)
1009
1010                    #} Any existing
1011                if ( is_array( $zbsFiles ) && count( $zbsFiles ) > 0 ) {
1012                    ?>
1013                            <tr class="wh-large"><th><label><?php printf( esc_html( _n( '%s associated file', '%s associated files', count( $zbsFiles ), 'text-domain' ) ), esc_html( number_format_i18n( count( $zbsFiles ) ) ) ); ?></label></th>
1014                                    <td id="zbsFileWrapInvoices">
1015                                <?php
1016                                    $fileLineIndx = 1; foreach ( $zbsFiles as $zbsFile ) {
1017
1018                                        $file = zeroBSCRM_files_baseName( $zbsFile['file'], isset( $zbsFile['priv'] ) );
1019
1020                                        echo '<div class="zbsFileLine" id="zbsFileLineQuote' . esc_attr( $fileLineIndx ) . '"><a href="' . esc_url( $zbsFile['url'] ) . '" target="_blank">' . esc_html( $file ) . '</a> (<span class="zbsDelFile" data-delurl="' . esc_attr( $zbsFile['url'] ) . '"><i class="fa fa-trash"></i></span>)</div>';
1021                                        ++$fileLineIndx;
1022
1023                                    }
1024                                    ?>
1025                                    </td></tr>
1026                                    <?php
1027
1028                }
1029                ?>
1030
1031                    <?php
1032                    #adapted from http://code.tutsplus.com/articles/attaching-files-to-your-posts-using-wordpress-custom-meta-boxes-part-1--wp-22291
1033
1034                        $html .= '<input type="file" id="zbsobj_file_attachment" name="zbsobj_file_attachment" size="25" class="zbs-dc">';
1035
1036                    ?>
1037                            <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>
1038                                <td>
1039                                <?php
1040                                wp_nonce_field( plugin_basename( __FILE__ ), 'zbsobj_file_attachment_nonce' );
1041                                echo $html;
1042                                ?>
1043                    </td></tr>
1044        
1045            </table>
1046            <script type="text/javascript">
1047
1048                var zbsQuotesCurrentlyDeleting = false;
1049                var zbsMetaboxFilesLang = {
1050                    'err': '<?php echo esc_html( zeroBSCRM_slashOut( __( 'Error', 'zero-bs-crm' ) ) ); ?>',
1051                    'unabletodel' : '<?php echo esc_html( zeroBSCRM_slashOut( __( 'Unable to delete this file', 'zero-bs-crm' ) ) ); ?>',
1052
1053                }
1054
1055                jQuery(function(){
1056
1057                    jQuery('.zbsDelFile').on( 'click', function(){
1058
1059                        if (!window.zbsQuotesCurrentlyDeleting){
1060
1061                            // blocking
1062                            window.zbsQuotesCurrentlyDeleting = true;
1063
1064                            var delUrl = jQuery(this).attr('data-delurl');
1065                            var lineIDtoRemove = jQuery(this).closest('.zbsFileLine').attr('id');
1066
1067                            if (typeof delUrl != "undefined" && delUrl != ''){
1068
1069
1070
1071                                    // postbag!
1072                                    var data = {
1073                                    'action': 'delFile',
1074                                    'zbsfType': 'quotes',
1075                                    'zbsDel':  delUrl, // could be csv, never used though
1076                                    'zbsCID': <?php echo esc_html( $quoteID ); ?>,
1077                                    'sec': window.zbscrmjs_secToken
1078                                    };
1079
1080                                    // Send it Pat :D
1081                                    jQuery.ajax({
1082                                            type: "POST",
1083                                            url: ajaxurl, // admin side is just ajaxurl not wptbpAJAX.ajaxurl,
1084                                            "data": data,
1085                                            dataType: 'json',
1086                                            timeout: 20000,
1087                                            success: function(response) {
1088
1089                                            // visually remove
1090                                            jQuery('#' + lineIDtoRemove).remove();
1091
1092                                            // file deletion errors, show msg:
1093                                            if (typeof response.errors != "undefined" && response.errors.length > 0){
1094
1095                                                jQuery.each(response.errors,function(ind,ele){
1096
1097                                                    jQuery('#zerobs-quotes-files-box').append('<div class="ui warning message" style="margin-top:10px;">' + ele + '</div>');
1098
1099                                                });
1100                                                     
1101                                            }
1102
1103
1104                                            },
1105                                            error: function(response){
1106
1107                                            jQuery('#zerobs-quotes-files-box').append('<div class="ui warning message" style="margin-top:10px;"><strong>' + window.zbsMetaboxFilesLang.err + ':</strong> ' + window.zbsMetaboxFilesLang.unabletodel + '</div>');
1108
1109                                            }
1110
1111                                        });
1112
1113                            }
1114
1115                            window.zbsQuotesCurrentlyDeleting = false;
1116
1117                        } // / blocking
1118
1119                    });
1120
1121                });
1122
1123
1124            </script>
1125            <?php
1126    }
1127
1128    public function save_data( $quoteID, $quote ) {
1129
1130        global $zbsobj_justUploadedObjFile;
1131        $id = $quoteID;
1132
1133        if ( ! empty( $_FILES['zbsobj_file_attachment']['name'] ) &&
1134            ( ! isset( $zbsobj_justUploadedObjFile ) ||
1135                ( isset( $zbsobj_justUploadedObjFile ) && $zbsobj_justUploadedObjFile != $_FILES['zbsobj_file_attachment']['name'] )
1136            )
1137            ) {
1138
1139            /* --- security verification --- */
1140            if ( ! wp_verify_nonce( $_POST['zbsobj_file_attachment_nonce'], plugin_basename( __FILE__ ) ) ) {
1141                return $id;
1142            } // end if
1143
1144            if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
1145                return $id;
1146            } // end if
1147
1148            if ( ! zeroBSCRM_permsQuotes() ) {
1149                return $id;
1150            }
1151            /* - end security verification - */
1152
1153            // Blocking repeat-upload bug
1154            $zbsobj_justUploadedObjFile = $_FILES['zbsobj_file_attachment']['name'];
1155
1156            // verify file extension and mime type
1157            if ( jpcrm_file_check_mime_extension( $_FILES['zbsobj_file_attachment'] ) ) {
1158
1159                $quote_dir_info = jpcrm_storage_dir_info_for_quotes( $quoteID );
1160                $upload         = jpcrm_save_admin_upload_to_folder( 'zbsobj_file_attachment', $quote_dir_info['files'] );
1161
1162                if ( isset( $upload['error'] ) && $upload['error'] != 0 ) {
1163                    wp_die( 'There was an error uploading your file. The error is: ' . esc_html( $upload['error'] ) );
1164                } else {
1165                        // w mod - adds to array :)
1166                        $zbsFiles = zeroBSCRM_files_getFiles( 'quote', $quoteID );
1167
1168                    if ( is_array( $zbsFiles ) ) {
1169
1170                        // add it
1171                        $zbsFiles[] = $upload;
1172
1173                    } else {
1174
1175                        // first
1176                        $zbsFiles = array( $upload );
1177
1178                    }
1179
1180                        // update
1181                        zeroBSCRM_files_updateFiles( 'quote', $quoteID, $zbsFiles );
1182
1183                        // Fire any 'post-upload-processing' (e.g. CPP makes thumbnails of pdf, jpg, etc.)
1184                        // not req invoicing: do_action('zbs_post_upload_contact',$upload);
1185                }
1186            } else {
1187                wp_die( "The file type that you've uploaded is not an accepted file format." );
1188            }
1189        }
1190
1191        return $quote;
1192    }
1193}
1194
1195/*
1196======================================================
1197    / Attach files to quote metabox
1198    ====================================================== */
1199
1200/*
1201======================================================
1202    Quote Accepted Details Metabox
1203    ====================================================== */
1204
1205class zeroBS__Metabox_QuoteAcceptedDetails extends zeroBS__Metabox {
1206
1207    // this is for catching 'new' contacts
1208    private $newRecordNeedsRedir = false;
1209
1210    public function __construct( $plugin_file ) {
1211
1212        // set these
1213        $this->objType         = 'quote';
1214        $this->metaboxID       = 'zerobs-quote-status-edit';
1215        $this->metaboxTitle    = __( 'Quote Public Status', 'zero-bs-crm' ); // will be headless anyhow
1216        $this->headless        = true;
1217        $this->metaboxScreen   = 'zbs-add-edit-quote-edit';
1218        $this->metaboxArea     = 'side';
1219        $this->metaboxLocation = 'low';
1220        $this->saveOrder       = 1;
1221        $this->capabilities    = array(
1222
1223            'can_hide'        => false, // can be hidden
1224            'areas'           => array( 'side' ), // areas can be dragged to - normal side = only areas currently
1225            'can_accept_tabs' => true,  // can/can't accept tabs onto it
1226            'can_become_tab'  => false, // can be added as tab
1227            'can_minimise'    => true, // can be minimised
1228            'can_move'        => true, // can be moved
1229
1230        );
1231
1232        global $useQuoteBuilder;
1233
1234        // call this
1235        if ( $useQuoteBuilder == '1' ) {
1236            $this->initMetabox();
1237        }
1238    }
1239
1240    public function html( $quote, $metabox ) {
1241
1242        // localise ID & template
1243        $quoteID = -1;
1244        if ( is_array( $quote ) && isset( $quote['id'] ) ) {
1245            $quoteID = (int) $quote['id'];
1246        }
1247        $templateUsed = -1;
1248        if ( is_array( $quote ) && isset( $quote['template'] ) ) {
1249            $templateUsed = $quote['template'];
1250        }
1251
1252        // quick WH predictive hack, not sure if viable - to test DAL3
1253        $quote['templated'] = false;
1254        if ( $templateUsed !== -1 && ! empty( $templateUsed ) ) {
1255            $quote['templated'] = true;
1256        }
1257
1258        global $useQuoteBuilder;
1259
1260        #} if enabled, and new quote, or one which hasn't had the 'templated' meta key added.
1261        #} ... also hide unless it's been "published"
1262        if ( $useQuoteBuilder == '1' && ( is_array( $quote ) && isset( $quote['templated'] ) && $quote['templated'] ) ) {
1263
1264            if ( isset( $quote ) && is_array( $quote ) && isset( $quote['accepted'] ) && $quote['accepted'] > 0 ) {
1265
1266                #} Deets
1267                $acceptedDate = date( zeroBSCRM_getTimeFormat() . ' ' . zeroBSCRM_getDateFormat(), $quote['accepted'] );
1268                $acceptedBy   = $quote['acceptedsigned'];
1269                $acceptedIP   = $quote['acceptedip'];
1270
1271                ?>
1272
1273                        <table class="wh-metatab-side wptbp" id="wptbpMetaBoxQuoteStatus">
1274                            <tr><td style="text-align:center;color:green"><strong><?php esc_html_e( 'Accepted', 'zero-bs-crm' ); ?> <?php echo esc_html( $acceptedDate ); ?></strong></td></tr>
1275                    <?php
1276                    if ( ! empty( $acceptedBy ) ) {
1277                        ?>
1278                        <tr><td style="text-align:center"><?php esc_html_e( 'By: ', 'zero-bs-crm' ); ?> <a href="mailto:<?php echo esc_attr( $acceptedBy ); ?>" target="_blank"
1279                        <?php
1280                        if ( ! empty( $acceptedIP ) ) {
1281                                                echo ' title="IP address:' . esc_attr( $acceptedIP ) . '"'; }
1282                        ?>
1283><?php echo esc_html( $acceptedBy ); ?></a></td></tr><?php } ?>                            
1284                        </table>   
1285
1286                <?php
1287
1288            } else {
1289
1290                ?>
1291
1292                        <table class="wh-metatab-side wptbp" id="wptbpMetaBoxQuoteStatus">
1293                            <tr>
1294                            <td style="text-align:center"><strong><?php esc_html_e( 'Not Yet Accepted', 'zero-bs-crm' ); ?></td></tr>
1295                        </table>  
1296
1297                    <?php
1298
1299            }
1300        } else { // / only load if post type
1301
1302            #} Gross hide :/
1303            ?>
1304                <style type="text/css">#wpzbscquote_status {display:none;}</style>
1305                <?php
1306
1307        }
1308    }
1309
1310    // nothing to save
1311}
1312
1313/*
1314======================================================
1315    / Quote Accepted Details Metabox
1316    ====================================================== */
1317
1318/*
1319======================================================
1320    Create Tags Box
1321    ====================================================== */
1322
1323class zeroBS__Metabox_QuoteTags extends zeroBS__Metabox_Tags {
1324
1325    public function __construct( $plugin_file ) {
1326
1327        $this->objTypeID = ZBS_TYPE_QUOTE;
1328        // DAL3 switched for objType $this->postType = 'zerobs_customer';
1329        $this->objType         = 'quote';
1330        $this->metaboxID       = 'zerobs-quote-tags';
1331        $this->metaboxTitle    = __( 'Quote Tags', 'zero-bs-crm' );
1332        $this->metaboxScreen   = 'zbs-add-edit-quote-edit'; // 'zerobs_edit_contact'; // we can use anything here as is now using our func
1333        $this->metaboxArea     = 'side';
1334        $this->metaboxLocation = 'high';
1335        $this->showSuggestions = true;
1336        $this->capabilities    = array(
1337
1338            'can_hide'        => true, // can be hidden
1339            'areas'           => array( 'side' ), // areas can be dragged to - normal side = only areas currently
1340            'can_accept_tabs' => false,  // can/can't accept tabs onto it
1341            'can_become_tab'  => false, // can be added as tab
1342            'can_minimise'    => true, // can be minimised
1343
1344        );
1345
1346        // call this
1347        $this->initMetabox();
1348    }
1349
1350    // html + save dealt with by parent class :)
1351}
1352
1353/*
1354======================================================
1355    / Create Tags Box
1356    ====================================================== */
1357
1358/*
1359======================================================
1360    Quote Actions Metabox
1361    ====================================================== */
1362
1363class zeroBS__Metabox_QuoteActions extends zeroBS__Metabox {
1364
1365    public function __construct( $plugin_file ) {
1366
1367        // set these
1368        $this->objType         = 'quote';
1369        $this->metaboxID       = 'zerobs-quote-actions';
1370        $this->metaboxTitle    = __( 'Quote Actions', 'zero-bs-crm' ); // will be headless anyhow
1371        $this->headless        = true;
1372        $this->metaboxScreen   = 'zbs-add-edit-quote-edit';
1373        $this->metaboxArea     = 'side';
1374        $this->metaboxLocation = 'high';
1375        $this->saveOrder       = 1;
1376        $this->capabilities    = array(
1377
1378            'can_hide'        => false, // can be hidden
1379            'areas'           => array( 'high' ), // areas can be dragged to - normal side = only areas currently
1380            'can_accept_tabs' => true,  // can/can't accept tabs onto it
1381            'can_become_tab'  => false, // can be added as tab
1382            'can_minimise'    => true, // can be minimised
1383            'can_move'        => true, // can be moved
1384
1385        );
1386
1387        // call this
1388        $this->initMetabox();
1389    }
1390
1391    public function html( $quote, $metabox ) {
1392
1393        ?>
1394            <div class="zbs-generic-save-wrap">
1395
1396                    <div class="ui medium dividing header"><i class="save icon"></i> <?php esc_html_e( 'Quote Actions', 'zero-bs-crm' ); ?></div>
1397
1398            <?php
1399
1400            // localise ID & content
1401            $quoteID = -1;
1402            if ( is_array( $quote ) && isset( $quote['id'] ) ) {
1403                $quoteID = (int) $quote['id'];
1404            }
1405
1406            #} if a saved post...
1407            // if (isset($post->post_status) && $post->post_status != "auto-draft"){
1408            if ( $quoteID > 0 ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
1409                ?>
1410                    <div class="zbs-quote-actions-bottom zbs-objedit-actions-bottom">
1411
1412                                <button class="ui button black" type="button" id="zbs-edit-save"><?php esc_html_e( 'Update', 'zero-bs-crm' ); ?> <?php esc_html_e( 'Quote', 'zero-bs-crm' ); ?></button>
1413
1414                        <?php
1415
1416                            // delete?
1417
1418                        // for now just check if can modify, later better, granular perms.
1419                        if ( zeroBSCRM_permsQuotes() ) {
1420                            ?>
1421                        <div id="zbs-quote-actions-delete" class="zbs-objedit-actions-delete">
1422                            <a class="submitdelete deletion" href="<?php echo jpcrm_esc_link( 'delete', $quoteID, 'quote' ); ?>"><?php esc_html_e( 'Delete Permanently', 'zero-bs-crm' ); ?></a>
1423                        </div>
1424                        <?php } // can delete ?>
1425                        
1426                        <div class='clear'></div>
1427
1428                    </div>
1429                <?php
1430
1431            } else {
1432
1433                // NEW quote
1434                ?>
1435
1436                        <button class="ui button black" type="button" id="zbs-edit-save"><?php esc_html_e( 'Save', 'zero-bs-crm' ); ?> <?php esc_html_e( 'Quote', 'zero-bs-crm' ); ?></button>
1437
1438                <?php
1439
1440            }
1441
1442            ?>
1443            </div>
1444            <?php
1445            // / .zbs-generic-save-wrap
1446    } // html
1447
1448    // saved via main metabox
1449}
1450
1451/*
1452======================================================
1453    / Quotes Actions Metabox
1454    ====================================================== */