<?php 
/*!
 * Jetpack CRM
 * https://jetpackcrm.com
 * V1.0
 *
 * Copyright 2020 Automattic
 *
 * Date: 26/05/16
 */

defined( 'ZEROBSCRM_PATH' ) || exit( 0 );

class zeroBSCRM_list{

    private $objType = false;
    private $objTypeID = false; // Will be set in v3.0+ - is autogenned from $objType ^^
    private $singular = false;
    private $plural = false;
    private $tag = false;
    private $postType = false;
    private $postPage = false;
    private $langLabels = false;
    private $bulkActions = false;
    private $sortables = false;
    private $unsortables = false;
    private $extraBoxes = '';
    private $extraJS = '';
    private $messages = false;
        #} All messages need params to match this func: 
        #} ... zeroBSCRM_UI2_messageHTML($msgClass='',$msgHeader='',$msg='',$iconClass='',$id='')

	/**
	 * Construct function
	 *
	 * @param array $args Object construct args.
	 */
	public function __construct( $args = array() ) {

		$default_args = array(

			'objType'     => false, // transaction
			'singular'    => false, // Transaction
			'plural'      => false, // Transactions
			'tag'         => false, // zerobs_transactiontag
			'postType'    => false, // zerobs_transaction
			'postPage'    => false, // manage-transactions
			'langLabels'  => array(

				// bulk actions - general
				'view'                => __( 'View', 'zero-bs-crm' ),
				'edit'                => __( 'Edit', 'zero-bs-crm' ),
				'deletestr'           => __( 'Delete', 'zero-bs-crm' ),
				'nocustomer'          => __( 'Not Assigned', 'zero-bs-crm' ),
				'areyousure'          => __( 'Are you sure?', 'zero-bs-crm' ),
				'acceptyesdoit'       => __( 'Yes, accept', 'zero-bs-crm' ),
				'yesproceed'          => __( 'Yes, proceed', 'zero-bs-crm' ),
				'changestatus'        => __( 'Change Status', 'zero-bs-crm' ),
				'yesupdate'           => __( 'Yes, update', 'zero-bs-crm' ),

				// bulk actions - delete
				'areyousurethese'     => __( 'Are you sure you want to delete these?', 'zero-bs-crm' ),
				'yesdelete'           => __( 'Yes, delete!', 'zero-bs-crm' ),
				'noleave'             => __( 'No, leave them', 'zero-bs-crm' ),
				'yesthose'            => __( 'Yes, remove everything', 'zero-bs-crm' ),
				'deleted'             => __( 'Deleted', 'zero-bs-crm' ),
				'notdeleted'          => __( 'Could not delete!', 'zero-bs-crm' ),

				// tag related
				'addtags'             => __( 'Add tags', 'zero-bs-crm' ),
				'removetags'          => __( 'Remove tag(s)', 'zero-bs-crm' ),
				'whichtags'           => __( 'Which Tag(s)?', 'zero-bs-crm' ),
				'whichtagsadd'        => __( 'Which Tag(s) would you like to add?', 'zero-bs-crm' ),
				'whichtagsremove'     => __( 'Which Tag(s) would you like to remove?', 'zero-bs-crm' ),
				'addthesetags'        => __( 'Add Tags', 'zero-bs-crm' ),
				'tagsadded'           => __( 'Tags Added', 'zero-bs-crm' ),
				'tagsaddeddesc'       => __( 'Your tags have been successsfully added.', 'zero-bs-crm' ),
				'tagsnotadded'        => __( 'Tags Not Added', 'zero-bs-crm' ),
				'tagsnotaddeddesc'    => __( 'Your tags could not be added.', 'zero-bs-crm' ),
				'tagsnotselected'     => __( 'No Tags Selected', 'zero-bs-crm' ),
				'tagsnotselecteddesc' => __( 'You did not select any tags.', 'zero-bs-crm' ),
				'removethesetags'     => __( 'Remove Tags', 'zero-bs-crm' ),
				'tagsremoved'         => __( 'Tags Removed', 'zero-bs-crm' ),
				'tagsremoveddesc'     => __( 'Your tags have been successsfully removed.', 'zero-bs-crm' ),
				'tagsnotremoved'      => __( 'Tags Not Removed', 'zero-bs-crm' ),
				'tagsnotremoveddesc'  => __( 'Your tags could not be removed.', 'zero-bs-crm' ),
				'notags'              => __( 'You do not have any tags', 'zero-bs-crm' ),

				// bulk actions - merge 2 records
				'merged'              => __( 'Merged', 'zero-bs-crm' ),
				'notmerged'           => __( 'Not Merged', 'zero-bs-crm' ),
				'yesmerge'            => __( 'Yes, merge them', 'zero-bs-crm' ),

				// error handling
				'badperms'            => __( 'Invalid permissions', 'zero-bs-crm' ),
				'badperms_desc'       => __( 'You do not have permissions to make this change.', 'zero-bs-crm' ),

			),
			'bulkActions' => array(),
			'sortables'   => array( 'id' ),
			'unsortables' => array( 'tagged', 'editlink', 'phonelink', 'viewlink' ),
			'extraBoxes'  => '', // html for extra boxes e.g. upsells :)
			'extraJS'     => '',
			'messages'    => '',

			// not implemented 'hideSidebar' => false // ability to hard-hide sidebar

		);
		// phpcs:disable
		foreach ($default_args as $argK => $argV){ $this->$argK = $argV; if (is_array($args) && isset($args[$argK])) {  if (is_array($args[$argK])){ $newData = $this->$argK; if (!is_array($newData)) $newData = array(); foreach ($args[$argK] as $subK => $subV){ $newData[$subK] = $subV; }$this->$argK = $newData;} else { $this->$argK = $args[$argK]; } } }
		// phpcs:enable

		global $zbs;

		if ( $this->objTypeID == false ) { // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual,WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase

			$this->objTypeID = $zbs->DAL->objTypeID( $this->objType ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase

		}
	}

    public function drawListView(){

        if (empty($this->objType) || empty($this->postType) || empty($this->postPage) || empty($this->singular) || empty($this->plural)){

            return 'Error.';
        }

        global $zbs;


        #} Retrieve all passed filters (tags, etc.)
        $listViewFilters = array(); if (isset($_GET['zbs_tag'])){

            $possibleTag = (int)sanitize_text_field($_GET['zbs_tag']);

			$possibleTagObj = $zbs->DAL->getTag( $possibleTag, array( 'objtype' => $this->objTypeID ) ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase,WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase

			if ( isset( $possibleTagObj['id'] ) ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
				$listViewFilters['tags'] = array( $possibleTagObj ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
			}
        }
        if (isset($_GET['s']) && !empty($_GET['s'])){

            $listViewFilters['s'] = sanitize_text_field($_GET['s']);

        }
        if (isset($_GET['quickfilters']) && !empty($_GET['quickfilters'])){

            // set it whether legit? what'll this do on error urls people make up?
            // v2.2+ hone this + add multi-filter
            // v2.99.5 - ALWAYS lowercase :) 
					$possible_quick_filters          = sanitize_text_field( $_GET['quickfilters'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.MissingUnslash
					$listViewFilters['quickfilters'] = array( $possible_quick_filters ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase

        }


        #} Paging
        $currentPage = 1; if (isset($_GET['paged'])) $currentPage = (int)sanitize_text_field($_GET['paged']);

        #} Sort
        $sort = false; if (isset($_GET['sort']) && !empty($_GET['sort'])) $sort = sanitize_text_field($_GET['sort']);
        $sortOrder = false; if (isset($_GET['sortdirection']) && ($_GET['sortdirection'] == 'asc' || $_GET['sortdirection'] == 'desc')) $sortOrder = sanitize_text_field($_GET['sortdirection']);


        # SCAFFOLDING - TO BE RE-ARRANGED :) 
        #} NOTE SECOND FIELD IN THESE ARE NOW IGNORED!?!? (30/7)

            #} Centralised into ZeroBSCRM.List.Columns.php 30/7/17
            $columnVar = 'zeroBSCRM_columns_'.$this->objType; //$zeroBSCRM_columns_transaction;
            $defaultColumns = $GLOBALS[ $columnVar ]['default'];
            $allColumns = $GLOBALS[ $columnVar ]['all'];


        global $zbs;
        $usingOwnership = $zbs->settings->get('perusercustomers');

        #} Retrieve columns settings
        $customViews = $zbs->settings->get('customviews2');

        $currentColumns = false; if (isset($customViews) && isset($customViews[$this->objType])) $currentColumns = $customViews[$this->objType];
        if ($currentColumns == false) $currentColumns = $defaultColumns;

		// add all columns to sortables :)
		if ( is_array( $currentColumns ) ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
			foreach ( $currentColumns as $col => $var ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
				if ( ! in_array( $col, $this->unsortables, true ) && ! in_array( $col, $this->sortables, true ) ) {
					$this->sortables[] = $col;
				}
			}
		}

		$screen_opts = $zbs->global_screen_options();

		if ( ! empty( $screen_opts['perpage'] ) && $screen_opts['perpage'] > 0 ) {
			$per_page = (int) $screen_opts['perpage'];
		} else {
			$per_page = 20;
		}

        #} Refresh 2
        ?>

            <!-- title + edit ico -->

            <!-- col editor -->
						<div id="zbs-list-col-editor" class="hidden">

                <h4 class="ui horizontal divider header">
                  <i class="list layout icon"></i>
                  <?php echo esc_html( sprintf(__('%s List View Options',"zero-bs-crm"),$this->singular) ); ?>
                </h4>
    
                <?php if (zeroBSCRM_isZBSAdminOrAdmin()){ // only admin can manage columns (globally) ?>
                <div id="zbs-list-view-options-wrap" class="ui divided grid">

                  <div class="ui active inverted dimmer hidden" id="zbs-col-manager-loading" style="display:none">
                    <div class="ui text loader"><?php esc_html_e('Loading',"zero-bs-crm");?></div>
                  </div>

                    <div class="row">
                        <div class="ten wide column">

                            <h4><?php esc_html_e('Current Columns',"zero-bs-crm"); ?></h4>


                            <div id="zbs-column-manager-current-cols" class="ui segment zbs-column-manager-connected"> 
                                <?php if (is_array($currentColumns)) foreach ($currentColumns as $colKey => $col){

                                    ?><div id="zbs-column-manager-col-<?php echo esc_attr( $colKey ); ?>" class="ui compact tiny basic button zbs-column-manager-col" data-key="<?php echo esc_attr( $colKey ); ?>"><?php esc_html_e($col[0],"zero-bs-crm"); ?></div><?php

                                } ?>
                            </div>

                        </div>
                        <div class="six wide column">

                            <h4><?php esc_html_e('Available Columns',"zero-bs-crm"); ?></h4>

                            <div id="zbs-column-manager-available-cols" class="ui segment"> 
                                <?php if (is_array($allColumns)) {

                                    // here we split them into groups, where there is. This allows a seperation of 'base fields' and compute fields (e.g. total value)
                                    $allColumnsSorted = array('basefields'=>array(),'other'=>array());
                                    $hasMultiColumnGroups = 0;

                                    foreach ($allColumns as $colKey => $col){

                                        if (!array_key_exists($colKey, $currentColumns)){

                                            // split em up
                                            if (isset($col[2]) && $col[2] == 'basefield'){
                                                $allColumnsSorted['basefields'][$colKey] = $col;
                                                $hasMultiColumnGroups = true;
                                            } else
                                                $allColumnsSorted['other'][$colKey] = $col;

                                        }

                                    }

                                    // now we put them out sequentially
                                    $colGroupCount = 0;
                                    foreach ($allColumnsSorted as $sortGroup => $columns){

                                        if (is_array($columns) && count($columns) > 0){

                                            // put out a grouper + title
                                            echo '<div>';

                                            if ($hasMultiColumnGroups){

                                                // header - <i class="list layout icon"></i>

                                                $title = ''; $extraStyles = '';
                                                switch ($sortGroup){

                                                    case 'basefields':
                                                        $title = __('Fields','zero-bs-crm');
                                                        break;

                                                    default: 
                                                        $title = __('Extra Fields','zero-bs-crm');
                                                        break;
                                                }

                                                if ($colGroupCount > 0) $extraStyles = 'margin-top: 1em;';

                                                if (!empty($title)) echo '<h4 class="ui horizontal divider header" style="'. esc_attr( $extraStyles ) .'">'. esc_html( $title ) .'</h4>';
                                            }

                                            echo '<div class="zbs-column-manager-connected">';

                                            foreach ($columns as $colKey => $col){

                                                if (!array_key_exists($colKey, $currentColumns)){
                                                    
                                                    ?><div id="zbs-column-manager-col-<?php echo esc_attr( $colKey ); ?>" class="ui compact tiny basic button zbs-column-manager-col" data-key="<?php echo esc_attr( $colKey ); ?>"><?php esc_html_e($col[0],"zero-bs-crm"); ?></div><?php

                                                }

                                            }

                                            echo '</div></div>';

                                            $colGroupCount++;
                                        }

                                    }

                                    // if NONE output, we need to always have smt to drop to, so put empty:
                                    if ($colGroupCount == 0){
                                        echo '<div class="zbs-column-manager-connected">';
                                        echo '</div>';
                                    }


                                } ?>
                            </div>

                        </div>
                    </div>
                </div>

                <div class="ui divider"></div>
                <?php } // if admin/can manage columns ?>

								<div id="zbs-list-options-base-wrap" class="ui grid">

									<div class="two column clearing centered row">
										<div class="column" style="max-width:364px;">
											<div class="ui labeled input">
												<div class="ui label"><i class="table icon"></i>  <?php esc_html_e( 'Records per page:', 'zero-bs-crm' ); ?></div>
												<input type="text" style="width:70px;" class="intOnly" id="zbs-screenoptions-records-per-page" value="<?php echo esc_attr( $per_page ); ?>" />
											</div>
										</div>
									</div>

									<div class="ui divider" style="margin-bottom:0;margin-top:0;"></div>

									<div class="two column clearing centered row">
										<div class="column" style="max-width:364px;">
											<button id="zbs-columnmanager-bottomsave" type="button" class="ui button black positive">
												<i class="check square icon"></i> <?php esc_html_e( 'Save Options and Close', 'zero-bs-crm' ); ?>
											</button>
										</div>
									</div>

								</div>


            </div>

			<div class="jpcrm-listview">
				<?php
				// phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
				$this->draw_listview_header( $listViewFilters );
				?>
				<div class="jpcrm-listview-table-container">
					<!-- Drawn by Javascript -->
				</div>
				<?php $this->draw_listview_footer(); ?>
				<div id="zbs-list-warnings-wrap">
					<?php
					// Preloaded error messages
					// phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped,WordPress.WP.I18n.MissingTranslatorsComment,WordPress.WP.I18n.UnorderedPlaceholdersText
					echo zeroBSCRM_UI2_messageHTML( 'warning hidden', sprintf( __( 'Error retrieving %s', 'zero-bs-crm' ), $this->plural ), sprintf( __( 'There has been a problem retrieving your %s. If this issue persists, please contact support.', 'zero-bs-crm' ), $this->plural ), 'disabled warning sign', 'zbsCantLoadData' );
					echo zeroBSCRM_UI2_messageHTML( 'warning hidden', sprintf( __( 'Error updating columns %s', 'zero-bs-crm' ), $this->plural ), __( 'There has been a problem saving your column configuration. If this issue persists, please contact support.', 'zero-bs-crm' ), 'disabled warning sign', 'zbsCantSaveCols' );
					echo zeroBSCRM_UI2_messageHTML( 'warning hidden', sprintf( __( 'Error updating columns %s', 'zero-bs-crm' ), $this->plural ), __( 'There has been a problem saving your filter button configuration. If this issue persists, please contact support.', 'zero-bs-crm' ), 'disabled warning sign', 'zbsCantSaveButtons' ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
					echo zeroBSCRM_UI2_messageHTML( 'info hidden', sprintf( __( 'No %s Found', 'zero-bs-crm' ), $this->plural ), sprintf( __( 'There are no %s here. Do you want to <a href="%s">create one</a>?', 'zero-bs-crm' ), $this->plural, jpcrm_esc_link( 'create', -1, $this->postType ) ), 'disabled warning sign', 'zbsNoResults' ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase

					// any additional messages?
					if ( isset( $this->messages ) && is_array( $this->messages ) && count( $this->messages ) > 0 ) {
						foreach ( $this->messages as $message ) {
							// $message needs to match this func :)
							echo zeroBSCRM_UI2_messageHTML( $message[0], $message[1], $message[2], $message[3], $message[4] );
						}
					}
					// phpcs:enable WordPress.Security.EscapeOutput.OutputNotEscaped,WordPress.WP.I18n.MissingTranslatorsComment,WordPress.WP.I18n.UnorderedPlaceholdersText
					?>
				</div>
			</div>
			<?php

			// If totals, show the wrapper. Currently only implemented in contacts
			if ( $zbs->settings->get( 'show_totals_table' ) === 1 && $this->objType === 'customer' ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
				?>
				<jpcrm-dashcount class="wide-cards"></jpcrm-dashcount>
				<?php
			}
			##WLREMOVE
			// e.g. upsell boxes
			echo $this->extraBoxes; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase,WordPress.Security.EscapeOutput.OutputNotEscaped
			##/WLREMOVE

			?>

        <script type="text/javascript">
					<?php
					// phpcs:disable Squiz.PHP.EmbeddedPhp.ContentBeforeOpen
					// phpcs:disable Squiz.PHP.EmbeddedPhp.ContentAfterEnd
					?>
            // expose log types (For columns)
            var zbsLogTypes = <?php global $zeroBSCRM_logTypes; echo json_encode($zeroBSCRM_logTypes); ?>;

            <?php

            $allowinlineedits = ( zeroBSCRM_getSetting('allowinlineedits') == "1" );
            $inlineEditStr = array();
            $columns = array();

            #} Current cols
            if ( is_array( $currentColumns ) ) {
	            foreach ( $currentColumns as $colKey => $col ) {

		            // set column title
		            $column_title = __($col[0],"zero-bs-crm");

		            // overrides

		            // Invoicing: Ref
		            if ( $this->objType == 'invoice' && $colKey == 'ref' ) {
			            $column_title = $zbs->settings->get('reflabel');
		            }

		            // can column be inline edited?
		            $inline = '-1';
		            if ( isset( $allColumns[$colKey] ) && isset( $allColumns[$colKey]['editinline'] ) && $allColumns[$colKey]['editinline'] ) {
			            $inline = '1';
		            }

		            $columns[] = array(
			            'namestr'  => esc_html( zeroBSCRM_slashOut($column_title,true) ),
			            'fieldstr' => esc_html( zeroBSCRM_slashOut($colKey,true) ),
			            'inline'   => (int) $inline,
		            );

		            $inlineEditStr[ $colKey ] = (int) $inline;
	            }
            }

            // build options objects
            $list_view_settings = array(

                'objdbname' => $this->objType,
                'search' => true,
                'filters' => true,
                'tags' => true,
                'c2c' => true,
                'editinline' => $allowinlineedits

            );

            $list_view_parameters = array(

                'listtype' => $this->objType,
                'columns' => $columns,
                'editinline' => $inlineEditStr,
                'retrieved' => false,
                'count' => (int)$per_page,
                'pagination' => true,
                'paged' => (int)$currentPage,

				// cast to object so an empty array is {} instead of [] when encoded as JSON
				'filters'    => (object) $listViewFilters, // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
                'sort' => ( !empty( $sort ) ? esc_html( $sort ) : false ),
                'sortorder' => ( !empty( $sortOrder ) ?  esc_html( $sortOrder ) : 'desc' ),

                // expose page key (used to retrieve data with screen opts - perpage)
                'pagekey' => ( isset( $zbs->pageKey ) ? esc_html( $zbs->pageKey ) : '' ),

            );

            ?>

            // General options for listview
            var zbsListViewSettings = <?php echo wp_json_encode( $list_view_settings ) ?>;

            // Vars for zbs list view drawer
            var zbsListViewParams = <?php echo wp_json_encode( $list_view_parameters ) ?>;

            var zbsSortables = [<?php 

                $c = 0; if (count($this->sortables) > 0) foreach ($this->sortables as $sortableStr) {

                            if ($c > 0) echo ',';

                            echo "'". esc_html( $sortableStr ) ."'";

                            $c++;
                            
                } 

            ?>]; // for v2.2 this is only lot that will show sort, until we redo db this'll be hard
            var zbsBulkActions = [<?php $bulkCount = 0; if (count($this->bulkActions) > 0) foreach ($this->bulkActions as $bulkActionStr) {

                        if ($bulkCount > 0) echo ',';

                        echo "'". esc_html( $bulkActionStr ) ."'";

                        $bulkCount++;

            } ?>]; // :D
            var zbsListViewData = []; var zbsListViewCount = 0;
            var zbsDrawListViewBlocker = false;
            var zbsDrawListViewAJAXBlocker = false;
            var zbsDrawListViewColUpdateBlocker = false;
            var zbsDrawListViewColUpdateAJAXBlocker = false;

            var zbsObjectEmailLinkPrefix = '<?php 

                // this assumes is contact for now, just sends to prefill - perhaps later add mailto: optional (wh wants lol)
                echo jpcrm_esc_link( 'email',-1,'zerobs_customer',true );

            ?>';
            var zbsObjectViewLinkPrefixCustomer = '<?php echo jpcrm_esc_link( 'view',-1,'zerobs_customer',true ); ?>';
            var zbsObjectViewLinkPrefixCompany = '<?php echo jpcrm_esc_link( 'view',-1,'zerobs_company',true ); ?>';
            var zbsObjectViewLinkPrefixQuote = '<?php echo jpcrm_esc_link( 'edit',-1,'zerobs_quote',true ); ?>';
            var zbsObjectViewLinkPrefixInvoice = '<?php echo jpcrm_esc_link( 'edit',-1,'zerobs_invoice',true ); ?>';
            var zbsObjectViewLinkPrefixTransaction = '<?php echo jpcrm_esc_link( 'edit',-1,'zerobs_transaction',true ); ?>';
            var zbsObjectViewLinkPrefixForm = '<?php echo jpcrm_esc_link( 'edit',-1,ZBS_TYPE_FORM,true ); ?>';
            var zbsObjectViewLinkPrefixSegment = '<?php echo jpcrm_esc_link( 'edit',-1,ZBS_TYPE_SEGMENT,true ); ?>';
            var zbsObjectViewLinkPrefixTask = '<?php echo jpcrm_esc_link( 'edit', -1, ZBS_TYPE_TASK, true  ); ?>';

            var zbsObjectEditLinkPrefixCustomer = '<?php echo jpcrm_esc_link( 'edit',-1,'zerobs_customer',true ); ?>';
            var zbsObjectEditLinkPrefixCompany = '<?php echo jpcrm_esc_link( 'edit',-1,'zerobs_company',true ); ?>';
            var zbsObjectEditLinkPrefixQuote = '<?php echo jpcrm_esc_link( 'edit',-1,'zerobs_quote',true ); ?>';
            var zbsObjectEditLinkPrefixQuoteTemplate = '<?php echo jpcrm_esc_link( 'edit',-1,'zerobs_quo_template',true ); ?>';
            var zbsObjectEditLinkPrefixInvoice = '<?php echo jpcrm_esc_link( 'edit',-1,'zerobs_invoice',true ); ?>';
            var zbsObjectEditLinkPrefixTransaction = '<?php echo jpcrm_esc_link( 'edit',-1,'zerobs_transaction',true ); ?>';
            var zbsObjectEditLinkPrefixForm = '<?php echo jpcrm_esc_link( 'edit',-1,ZBS_TYPE_FORM,true ); ?>';
            var zbsObjectEditLinkPrefixSegment = '<?php echo jpcrm_esc_link( 'edit',-1,ZBS_TYPE_SEGMENT,true ); ?>';

            var jpcrm_segment_export_url_prefix = '<?php echo jpcrm_esc_link( $zbs->slugs['export-tools'] . '&segment-id=' ); ?>';

						var zbsListViewLink = '<?php echo esc_url( admin_url( 'admin.php?page=' . $this->postPage ) ); /* phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase */ ?>';
            var zbsExportPostURL = '<?php echo esc_url( zeroBSCRM_getAdminURL($zbs->slugs['export-tools']) ); ?>';
						var zbsTagSkipLinkPrefix = zbsListViewLink + '&zbs_tag=';
            var zbsListViewObjName = '<?php

                switch ($this->postType){


                    case 'zerobs_customer':
                        zeroBSCRM_slashOut(__('Contact',"zero-bs-crm"));
                        break;

                    case 'zerobs_company':
                        zeroBSCRM_slashOut(jpcrm_label_company());
                        break;

                    case 'zerobs_quote':
                        zeroBSCRM_slashOut(__('Quote',"zero-bs-crm"));
                        break;

                    case 'zerobs_invoice':
                        zeroBSCRM_slashOut(__('Invoice',"zero-bs-crm"));
                        break;

                    case 'zerobs_transaction':
                        zeroBSCRM_slashOut(__('Transaction',"zero-bs-crm"));
                        break;

                    case 'zerobs_form':
                        zeroBSCRM_slashOut(__('Form',"zero-bs-crm"));
                        break;

                    case 'zerobs_quotetemplate':
                        zeroBSCRM_slashOut(__('Quote Template',"zero-bs-crm"));
                        break;

                    default:
                        zeroBSCRM_slashOut(__('Item',"zero-bs-crm"));
                        break;



                } 

            ?>';
            var zbsClick2CallType = parseInt('<?php echo esc_url( zeroBSCRM_getSetting('clicktocalltype') ); ?>');

			<?php
			$jpcrm_listview_lang_labels = array();

			// add any object-specific language labels
			if ( count( $this->langLabels ) > 0 ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase

				$jpcrm_listview_lang_labels = array_merge( $jpcrm_listview_lang_labels, $this->langLabels ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase

			}
			?>
			var zbsListViewLangLabels = <?php echo wp_json_encode( $jpcrm_listview_lang_labels ); ?>;
			var zbsTagsForBulkActions = <?php
				// the linter was having issues with these indents, so disabling for this block
				// phpcs:disable Generic.WhiteSpace.ScopeIndent.IncorrectExact,Generic.WhiteSpace.ScopeIndent.Incorrect
				$tags = $zbs->DAL->getTagsForObjType( // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
					array(
						'objtypeid'    => $this->objTypeID, // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
						'withCount'    => true,
						'excludeEmpty' => false,
						'ignoreowner'  => true,
					)
				);

				// make simplified
				$simple_tags = array();
				if ( is_array( $tags ) && count( $tags ) > 0 ) {
					foreach ( $tags as $t ) {
						$simple_tags[] = array(
							'id'   => $t['id'],
							'name' => $t['name'],
							'slug' => $t['slug'],
						);
					}
				}

				$zbs_tags_for_bulk_actions = wp_json_encode( $simple_tags );
				echo ( $zbs_tags_for_bulk_actions ? $zbs_tags_for_bulk_actions : '[]' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped

				?>;
				var zbsListViewIcos = {};
				// gives data used by inline editor
				var zbsListViewInlineEdit = {

					// for now just put contacts in here
					customer: {
						statuses: <?php
							// MUST be a better way than this to get customer statuses...
							// phpcs:disable WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
							global $zbsCustomerFields;
							if ( is_array( $zbsCustomerFields['status'][3] ) ) {
								echo wp_json_encode( $zbsCustomerFields['status'][3] );
							} else {
								echo '[]';
							}
							// phpcs:enable WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
						?>
					},

					owners: <?php

						// hardcoded customer perms atm
						$possible_owners = zeroBS_getPossibleOwners( array( 'zerobs_admin', 'zerobs_customermgr' ), true );
						if ( ! is_array( $possible_owners ) ) {
								echo wp_json_encode( array() );
						} else {
							echo wp_json_encode( $possible_owners );
						}

					?>

					};
					<?php
					// Nonce for AJAX
					echo 'var zbscrmjs_secToken = "' . esc_js( wp_create_nonce( 'zbscrmjs-ajax-nonce' ) ) . '";';

					// any last JS?
					if ( isset( $this->extraJS ) && ! empty( $this->extraJS ) ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
						echo $this->extraJS; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase,WordPress.Security.EscapeOutput.OutputNotEscaped
					}
					// phpcs:enable Generic.WhiteSpace.ScopeIndent.IncorrectExact,Generic.WhiteSpace.ScopeIndent.Incorrect
					?>
					</script>

					<?php
					// phpcs:enable Squiz.PHP.EmbeddedPhp.ContentBeforeOpen
					// phpcs:enable Squiz.PHP.EmbeddedPhp.ContentAfterEnd

    } // /draw func

	/**
	 * Draws listview header that contains search, bulk actions, and filter dropdowns
	 *
	 * @param array $listview_filters Array of current listview filters.
	 */
	public function draw_listview_header( $listview_filters ) {
		global $zbs;

		$all_quickfilters = array_key_exists( $this->objTypeID, $zbs->listview_filters ) ? $zbs->listview_filters[ $this->objTypeID ] : array(); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase

		$quickfilter_category_count = count( $all_quickfilters );
		$quickfilter_categories     = array(
			'general' => __( 'General', 'zero-bs-crm' ),
			'status'  => __( 'Status', 'zero-bs-crm' ),
			'segment' => __( 'Segment', 'zero-bs-crm' ),
		);

		$all_tags = $zbs->DAL->getTagsForObjType( // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
			array(
				'objtypeid'    => $this->objTypeID, // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
				'excludeEmpty' => false,
			)
		);

		$current_quickfilter       = ( ! empty( $listview_filters['quickfilters'][0] ) ? $listview_filters['quickfilters'][0] : '' );
		$current_quickfilter_label = '';

		if ( $quickfilter_category_count > 0 ) {
			$quickfilter_html = '<option disabled selected>' . esc_html__( 'Select filter', 'zero-bs-crm' ) . '</option>';
			foreach ( $all_quickfilters as $category => $filters ) {
				if ( $quickfilter_category_count > 1 ) {
					$category_label    = array_key_exists( $category, $quickfilter_categories ) ? $quickfilter_categories[ $category ] : $category;
					$quickfilter_html .= '<optgroup label="' . esc_attr( $category_label ) . '">';
				}
				foreach ( $filters as $filter_slug => $filter_label ) {
					if ( $current_quickfilter === $filter_slug ) {
						$current_quickfilter_label = $filter_label;
					}
					$quickfilter_html .= '<option value="' . esc_attr( $filter_slug ) . '">' . esc_html( $filter_label ) . '</option>';
				}
				if ( $quickfilter_category_count > 1 ) {
					$quickfilter_html .= '</optgroup>';
				}
			}
		}

		$current_tag    = ( ! empty( $listview_filters['tags'][0] ) ? $listview_filters['tags'][0]['name'] : '' );
		$current_search = ( ! empty( $listview_filters['s'] ) ? $listview_filters['s'] : '' );
		?>

		<jpcrm-listview-header id="jpcrm-listview-header">
			<header-item>
				<input type="search"  value="<?php echo esc_attr( $current_search ); ?>" placeholder="<?php echo esc_attr__( 'Search...', 'zero-bs-crm' ); ?>" autocomplete="<?php echo esc_attr( jpcrm_disable_browser_autocomplete() ); ?>"/>
				<select class="bulk-actions-dropdown hidden">
					<option><?php echo esc_html__( 'Bulk actions (no rows)', 'zero-bs-crm' ); ?></option>
				</select>
			</header-item>
			<header-item>
				<?php
				// add quickfilters filter if current object has quickfilters
				if ( $quickfilter_category_count > 0 ) {
					echo '<select class="filter-dropdown' . esc_attr( ! empty( $current_quickfilter_label ) ? ' hidden' : '' ) . '" data-filtertype="quickfilters">';
					echo $quickfilter_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
					echo '</select>';
					echo '<div class="jpcrm-current-filter' . ( empty( $current_quickfilter_label ) ? ' hidden' : '' ) . '">';
					echo '<button class="dashicons dashicons-remove" title="' . esc_attr__( 'Remove filter', 'zero-bs-crm' ) . '"></button>';
					echo esc_html__( 'Filter', 'zero-bs-crm' ) . ': ';
					echo '<span>' . esc_html( $current_quickfilter_label ) . '</span>';
					echo '</div>';
				}

				// add tags filter if current object has tags
				if ( is_array( $all_tags ) && count( $all_tags ) > 0 ) {
					echo '<select class="filter-dropdown' . ( ! empty( $current_tag ) ? ' hidden' : '' ) . '" data-filtertype="tags">';
					echo '<option disabled selected>' . esc_html__( 'Select tag', 'zero-bs-crm' ) . '</option>';
					foreach ( $all_tags as $tag ) {
						echo '<option value="' . esc_attr( $tag['id'] ) . '">' . esc_html( $tag['name'] ) . '</option>';
					}
					echo '</select>';
					echo '<div class="jpcrm-current-filter' . ( empty( $current_tag ) ? ' hidden' : '' ) . '">';
					echo '<button class="dashicons dashicons-remove" title="' . esc_attr__( 'Remove tag', 'zero-bs-crm' ) . '"></button>';
					echo esc_html__( 'Tag', 'zero-bs-crm' ) . ': ';
					echo '<span>' . esc_html( $current_tag ) . '</span>';
					echo '</div>';
				}
				?>
			</header-item>
		</jpcrm-listview-header>
		<?php
	}

	/**
	 * Draws a listview footer and its containers for JS to use
	 */
	public function draw_listview_footer() {
		?>
		<jpcrm-listview-footer>
			<footer-left>
				<div class= "jpcrm-listview-counts-container">
					<!-- drawn by JS -->
				</div>
			</footer-left>
			<footer-right>
				<div class= "jpcrm-pagination-container">
					<!-- drawn by JS -->
				</div>
			</footer-right>
		</jpcrm-listview-footer>
		<?php
	}
} // class

/**
 * Language labels for JS
 *
 * @param array $language_array Array of language labels.
 */
function jpcrm_listview_language_labels( $language_array ) { // phpcs:ignore Universal.Files.SeparateFunctionsFromOO.Mixed
	$jpcrm_listview_lang_labels = array(
		'click_to_sort'      => esc_html__( 'Click to sort', 'zero-bs-crm' ),
		/* translators: Placeholder is the number of selected rows. */
		'rows_selected_x'    => esc_html__( 'Bulk actions (%s rows)', 'zero-bs-crm' ),
		'rows_selected_1'    => esc_html__( 'Bulk actions (1 row)', 'zero-bs-crm' ),
		'rows_selected_0'    => esc_html__( 'Bulk actions (no rows)', 'zero-bs-crm' ),
		'zbs_edit'           => esc_html__( 'Edit', 'zero-bs-crm' ),
		'daysago'            => esc_html__( 'days ago', 'zero-bs-crm' ),
		'notcontacted'       => esc_html__( 'Not Contacted', 'zero-bs-crm' ),
		'couldntupdate'      => esc_html__( 'Could not update', 'zero-bs-crm' ),
		'couldntupdatedeets' => esc_html__( 'This record could not be updated. Please try again, if this persists please let admin know.', 'zero-bs-crm' ),
		/* translators: Placeholders are the range of the current record result and the total object count. */
		'listview_counts'    => esc_html__( 'Showing %s of %s items', 'zero-bs-crm' ), // phpcs:ignore WordPress.WP.I18n.UnorderedPlaceholdersText
	);

	return array_merge( $language_array, $jpcrm_listview_lang_labels );
}
add_filter( 'zbs_globaljs_lang', 'jpcrm_listview_language_labels' );
