756 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			756 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
/*********************************************************************************
 | 
						|
 *
 | 
						|
 * TimeTrex is a Workforce Management program developed by
 | 
						|
 * TimeTrex Software Inc. Copyright (C) 2003 - 2021 TimeTrex Software Inc.
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or modify it under
 | 
						|
 * the terms of the GNU Affero General Public License version 3 as published by
 | 
						|
 * the Free Software Foundation with the addition of the following permission
 | 
						|
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 | 
						|
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY TIMETREX, TIMETREX DISCLAIMS THE
 | 
						|
 * WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 | 
						|
 *
 | 
						|
 * This program is distributed in the hope that it will be useful, but WITHOUT
 | 
						|
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | 
						|
 * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
 | 
						|
 * details.
 | 
						|
 *
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU Affero General Public License along
 | 
						|
 * with this program; if not, see http://www.gnu.org/licenses or write to the Free
 | 
						|
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | 
						|
 * 02110-1301 USA.
 | 
						|
 *
 | 
						|
 *
 | 
						|
 * You can contact TimeTrex headquarters at Unit 22 - 2475 Dobbin Rd. Suite
 | 
						|
 * #292 West Kelowna, BC V4T 2E9, Canada or at email address info@timetrex.com.
 | 
						|
 *
 | 
						|
 *
 | 
						|
 * The interactive user interfaces in modified source and object code versions
 | 
						|
 * of this program must display Appropriate Legal Notices, as required under
 | 
						|
 * Section 5 of the GNU Affero General Public License version 3.
 | 
						|
 *
 | 
						|
 *
 | 
						|
 * In accordance with Section 7(b) of the GNU Affero General Public License
 | 
						|
 * version 3, these Appropriate Legal Notices must retain the display of the
 | 
						|
 * "Powered by TimeTrex" logo. If the display of the logo is not reasonably
 | 
						|
 * feasible for technical reasons, the Appropriate Legal Notices must display
 | 
						|
 * the words "Powered by TimeTrex".
 | 
						|
 *
 | 
						|
 ********************************************************************************/
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * @package API\Core
 | 
						|
 */
 | 
						|
abstract class APIFactory {
 | 
						|
 | 
						|
	public $data = [];
 | 
						|
 | 
						|
	protected $main_class_obj = null;
 | 
						|
 | 
						|
	protected $api_message_id = null;
 | 
						|
 | 
						|
	protected $pager_obj = null;
 | 
						|
 | 
						|
	protected $current_company = null;
 | 
						|
	protected $current_user = null;
 | 
						|
	protected $current_user_prefs = null;
 | 
						|
	protected $permission = null;
 | 
						|
 | 
						|
	protected $progress_bar_obj = null;
 | 
						|
 | 
						|
	/**
 | 
						|
	 * APIFactory constructor.
 | 
						|
	 */
 | 
						|
	function __construct() {
 | 
						|
		global $current_company, $current_user, $current_user_prefs;
 | 
						|
 | 
						|
		$this->current_company = $current_company;
 | 
						|
		$this->current_user = $current_user;
 | 
						|
		$this->current_user_prefs = $current_user_prefs;
 | 
						|
 | 
						|
		$this->permission = new Permission();
 | 
						|
 | 
						|
		return true;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @return int
 | 
						|
	 */
 | 
						|
	function getProtocolVersion() {
 | 
						|
		if ( isset( $_GET['v'] ) && $_GET['v'] != '' ) {
 | 
						|
			return (int)$_GET['v'];     //1=Initial, 2=Always return detailed
 | 
						|
		} else {
 | 
						|
			$retval = 2; //Default to v2.
 | 
						|
 | 
						|
			$mobile_app_client_version = Misc::getMobileAppClientVersion();
 | 
						|
			if ( $mobile_app_client_version !== false ) { //If mobile app version is specified at all, default to API protocol v1, otherwise it should be specified on its own.
 | 
						|
				//NOTE: Mobile app currently requires API v1, but older versions of the app don't send the protocol version. So we can't default to v2 without breaking the app.
 | 
						|
				//      Also app versions >= v5.0.0 to < v5.1.0 did not specify a protocol version.
 | 
						|
				$retval = 1;
 | 
						|
				Debug::Text( 'NOTICE: Falling back to API protocol v1.0...', __FILE__, __LINE__, __METHOD__, 10 );
 | 
						|
			}
 | 
						|
 | 
						|
		}
 | 
						|
 | 
						|
		return $retval;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Returns the API messageID for each individual call.
 | 
						|
	 * @return bool|null
 | 
						|
	 */
 | 
						|
	function getAPIMessageID() {
 | 
						|
		if ( $this->api_message_id != null ) {
 | 
						|
			return $this->api_message_id;
 | 
						|
		}
 | 
						|
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @param string $id UUID
 | 
						|
	 * @return bool
 | 
						|
	 */
 | 
						|
	function setAPIMessageID( $id ) {
 | 
						|
		if ( $id != '' ) {
 | 
						|
			global $api_message_id; //Make this global so Debug() class can reference it on Shutdown()
 | 
						|
			$this->api_message_id = $api_message_id = $id;
 | 
						|
 | 
						|
			return true;
 | 
						|
		}
 | 
						|
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @return bool|CompanyFactory
 | 
						|
	 */
 | 
						|
	function getCurrentCompanyObject() {
 | 
						|
		if ( is_object( $this->current_company ) ) {
 | 
						|
			return $this->current_company;
 | 
						|
		}
 | 
						|
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @return bool|UserFactory
 | 
						|
	 */
 | 
						|
	function getCurrentUserObject() {
 | 
						|
		if ( is_object( $this->current_user ) ) {
 | 
						|
			return $this->current_user;
 | 
						|
		}
 | 
						|
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @return bool|UserPreferenceFactory
 | 
						|
	 */
 | 
						|
	function getCurrentUserPreferenceObject() {
 | 
						|
		if ( is_object( $this->current_user_prefs ) ) {
 | 
						|
			return $this->current_user_prefs;
 | 
						|
		}
 | 
						|
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @return bool|null|Permission
 | 
						|
	 */
 | 
						|
	function getPermissionObject() {
 | 
						|
		if ( is_object( $this->permission ) ) {
 | 
						|
			return $this->permission;
 | 
						|
		}
 | 
						|
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @return bool
 | 
						|
	 */
 | 
						|
	function isProgressBarStarted() {
 | 
						|
		if ( is_object( $this->progress_bar_obj ) ) {
 | 
						|
			return true;
 | 
						|
		}
 | 
						|
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @param $progress_bar_obj
 | 
						|
	 * @return bool
 | 
						|
	 */
 | 
						|
	function setProgressBarObject( $progress_bar_obj ) {
 | 
						|
		$this->progress_bar_obj = $progress_bar_obj;
 | 
						|
 | 
						|
		return true;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @return null|ProgressBar
 | 
						|
	 */
 | 
						|
	function getProgressBarObject() {
 | 
						|
		if ( !is_object( $this->progress_bar_obj ) ) {
 | 
						|
			$this->progress_bar_obj = new ProgressBar();
 | 
						|
		}
 | 
						|
 | 
						|
		return $this->progress_bar_obj;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @param object $lf
 | 
						|
	 * @return bool
 | 
						|
	 */
 | 
						|
	function setPagerObject( $lf ) {
 | 
						|
		if ( is_object( $lf ) ) {
 | 
						|
			$this->pager_obj = new Pager( $lf );
 | 
						|
		}
 | 
						|
 | 
						|
		return true;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @return Pager
 | 
						|
	 */
 | 
						|
	function getPagerObject() {
 | 
						|
		return $this->pager_obj;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @return array|bool
 | 
						|
	 */
 | 
						|
	function getPagerData() {
 | 
						|
		if ( is_object( $this->pager_obj ) ) {
 | 
						|
			return $this->pager_obj->getPageVariables();
 | 
						|
		}
 | 
						|
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Allow storing the main class object persistently in memory, so we can build up other variables to help out things like getOptions()
 | 
						|
	 * Mainly used for the APIReport class.
 | 
						|
	 * @param object $obj
 | 
						|
	 * @return bool
 | 
						|
	 */
 | 
						|
	function setMainClassObject( $obj ) {
 | 
						|
		if ( is_object( $obj ) ) {
 | 
						|
			$this->main_class_obj = $obj;
 | 
						|
 | 
						|
			return true;
 | 
						|
		}
 | 
						|
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @return string
 | 
						|
	 */
 | 
						|
	function getMainClassObject() {
 | 
						|
		if ( !is_object( $this->main_class_obj ) ) {
 | 
						|
			$this->main_class_obj = new $this->main_class;
 | 
						|
 | 
						|
			return $this->main_class_obj;
 | 
						|
		} else {
 | 
						|
			return $this->main_class_obj;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @param array $data
 | 
						|
	 * @param bool $disable_paging
 | 
						|
	 * @return array|bool
 | 
						|
	 */
 | 
						|
	function initializeFilterAndPager( $data, $disable_paging = false ) {
 | 
						|
		//If $data is not an array, it will trigger PHP errors, so force it that way and report an error so we can troubleshoot if needed.
 | 
						|
		//This will avoid the PHP fatal errors that look like the below, but it doesn't actually fix the root cause, which is currently unknown.
 | 
						|
		//		DEBUG [L0228] [00014ms] Array: [Function](): Arguments: (Size: 114)
 | 
						|
		//		array(4) {
 | 
						|
		//					["POST_/api/json/api_php?Class"]=> string(18) "APIUserGenericData"
 | 
						|
		//					["Method"]=> string(18) "getUserGenericData"
 | 
						|
		//					["v"]=> string(1) "2"
 | 
						|
		//					["MessageID"]=> string(26) "5dd90933-f97c-9001-9efe-e2"
 | 
						|
		//		}
 | 
						|
		//		DEBUG [L0139] [00030ms] Array: Debug::ErrorHandler(): Raw POST Request:
 | 
						|
		//		string(114) "POST /api/json/api.php?Class=APIUserGenericData&Method=getUserGenericData&v=2&MessageID=5dd90933-f97c-9001-9efe-e2"
 | 
						|
		if ( is_array( $data ) == false ) {
 | 
						|
			Debug::Arr( $data, 'ERROR: Input data is not an array: ', __FILE__, __LINE__, __METHOD__, 10 );
 | 
						|
			$data = [];
 | 
						|
		}
 | 
						|
 | 
						|
		//Preset values for LF search function.
 | 
						|
		$data = Misc::preSetArrayValues( $data, [ 'filter_data', 'filter_columns', 'filter_items_per_page', 'filter_page', 'filter_sort' ], null );
 | 
						|
 | 
						|
		if ( $disable_paging == false && (int)$data['filter_items_per_page'] <= 0 ) { //Used to check $data['filter_items_per_page'] === NULL
 | 
						|
			$data['filter_items_per_page'] = $this->getCurrentUserPreferenceObject()->getItemsPerPage();
 | 
						|
		}
 | 
						|
 | 
						|
		if ( $disable_paging == true ) {
 | 
						|
			$data['filter_items_per_page'] = $data['filter_page'] = false;
 | 
						|
		}
 | 
						|
 | 
						|
		//Debug::Arr($data, 'Getting Data: ', __FILE__, __LINE__, __METHOD__, 10);
 | 
						|
 | 
						|
		return $data;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * In cases where data can be displayed in just a list_view (dropdown boxes), ie: branch, department, job, task in In/Out punch view
 | 
						|
	 * restrict the dropdown box to just a subset of columns, so not all data is shown.
 | 
						|
	 * @param array $filter_columns
 | 
						|
	 * @param array $allowed_columns
 | 
						|
	 * @return array|null
 | 
						|
	 */
 | 
						|
	function handlePermissionFilterColumns( $filter_columns, $allowed_columns ) {
 | 
						|
		//Always allow these columns to be returned.
 | 
						|
		$allowed_columns['id'] = true;
 | 
						|
		$allowed_columns['is_owner'] = true;
 | 
						|
		$allowed_columns['is_child'] = true;
 | 
						|
 | 
						|
		if ( is_array( $filter_columns ) ) {
 | 
						|
			$retarr = Misc::arrayIntersectByKey( $allowed_columns, $filter_columns );
 | 
						|
		} else {
 | 
						|
			$retarr = $allowed_columns;
 | 
						|
		}
 | 
						|
 | 
						|
		//If no valid columns are being returned, revert back to allowed columns.
 | 
						|
		//Never return *NULL* or a blank array from here, as that will allow all columns to be displayed.
 | 
						|
		if ( !is_array( $retarr ) ) {
 | 
						|
			//Return all allowed columns
 | 
						|
			$retarr = $allowed_columns;
 | 
						|
		}
 | 
						|
 | 
						|
		return $retarr;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @param array $data
 | 
						|
	 * @return mixed
 | 
						|
	 */
 | 
						|
	function convertToSingleRecord( $data ) {
 | 
						|
		if ( isset( $data[0] ) && !isset( $data[1] ) ) {
 | 
						|
			return $data[0];
 | 
						|
		} else {
 | 
						|
			return $data;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @param array $data
 | 
						|
	 * @return array
 | 
						|
	 */
 | 
						|
	function convertToMultipleRecords( $data ) {
 | 
						|
		//if ( isset($data[0]) AND is_array($data[0]) ) {
 | 
						|
		//Better way to detect if $data has numeric or string keys, which works across sparse arrays that could come from importing. ie: 3 => array(), 6 => array(), ...
 | 
						|
		//  Array indexes can only be integer or string, so  (string)"8" can never happen as it would always be (int)8
 | 
						|
		if ( count( array_filter( array_keys( $data ), 'is_string' ) ) == 0 ) {
 | 
						|
			$retarr = [
 | 
						|
				//'data' => $data,
 | 
						|
				//'total_records' => count($data)
 | 
						|
				//Switch to an array that is compatible with list() rather than extract() as it allows IDEs to better inspect code.
 | 
						|
				$data,
 | 
						|
				count( $data ),
 | 
						|
			];
 | 
						|
		} else {
 | 
						|
			$retarr = [
 | 
						|
				//'data' => array( 0 => $data ),
 | 
						|
				//'total_records' => 1
 | 
						|
				//Switch to an array that is compatible with list() rather than extract() as it allows IDEs to better inspect code.
 | 
						|
				[ 0 => $data ],
 | 
						|
				1,
 | 
						|
			];
 | 
						|
		}
 | 
						|
 | 
						|
		//Debug::Arr($retarr, 'Array: ', __FILE__, __LINE__, __METHOD__, 10);
 | 
						|
 | 
						|
		return $retarr;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @return string
 | 
						|
	 */
 | 
						|
	function getNextInsertID() {
 | 
						|
		return $this->getMainClassObject()->getNextInsertId();
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @return array
 | 
						|
	 */
 | 
						|
	function getPermissionChildren() {
 | 
						|
		return $this->getPermissionObject()->getPermissionHierarchyChildren( $this->getCurrentCompanyObject()->getId(), $this->getCurrentUserObject()->getId() );
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Controls returning information to client in a standard format.
 | 
						|
	 * FIXME: Need to return the original request (with any modified values due to restrictions/validation issues)
 | 
						|
	 *        Also need to return paging data variables here too, as JSON can't make multiple calls.
 | 
						|
	 *        In order to do this we need to always return a special data structure that includes this information.
 | 
						|
	 *        static function returnHandler( $retval = TRUE, $args = array( 'code' => FALSE, 'description' => FALSE, 'details' = FALSE, 'validator_stats' => FALSE, 'user_generic_status_batch_id' => FALSE ) ) {
 | 
						|
	 *        The above will require too many changes, just add two more variables at the end, as it will only really be used by API->get*() functions.
 | 
						|
	 * FIXME: Use a requestHandler() to handle all input requests, so we can parse out things like validate_only, ignore_warning (for user acknowledgable warnings) and handling all parameter parsing in a central place.
 | 
						|
	 *        static function returnHandler( $retval = TRUE, $code = FALSE, $description = FALSE, $details = FALSE, $validator_stats = FALSE, $user_generic_status_batch_id = FALSE, $request = FALSE, $pager = FALSE ) {
 | 
						|
	 * @param bool|mixed $retval
 | 
						|
	 * @param string|bool $code
 | 
						|
	 * @param string|bool $description
 | 
						|
	 * @param array|bool $details
 | 
						|
	 * @param array|bool $validator_stats
 | 
						|
	 * @param string|int|bool $user_generic_status_batch_id
 | 
						|
	 * @param array|bool $request_data
 | 
						|
	 * @param string|bool $system_job_queue
 | 
						|
	 * @return array|bool
 | 
						|
	 */
 | 
						|
	function returnHandler( $retval = true, $code = false, $description = false, $details = false, $validator_stats = false, $user_generic_status_batch_id = false, $request_data = false, $system_job_queue = false ) {
 | 
						|
		global $config_vars;
 | 
						|
		if ( isset( $config_vars['other']['enable_job_queue'] ) && $config_vars['other']['enable_job_queue'] != true ) { //If the job queue is disabled, force system_job_queue flag to always be false so we don't trigger a spinner in the UI.
 | 
						|
			$system_job_queue = false;
 | 
						|
		}
 | 
						|
 | 
						|
		if ( $this->getProtocolVersion() == 1 ) {
 | 
						|
			if ( $retval === false || ( $retval === true && $code !== false ) || ( $user_generic_status_batch_id !== false ) ) {
 | 
						|
				if ( $retval === false ) {
 | 
						|
					if ( $code == '' ) {
 | 
						|
						$code = 'GENERAL';
 | 
						|
					}
 | 
						|
					if ( $description == '' ) {
 | 
						|
						$description = 'Insufficient data to carry out action';
 | 
						|
					}
 | 
						|
				} else if ( $retval === true ) {
 | 
						|
					if ( $code == '' ) {
 | 
						|
						$code = 'SUCCESS';
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				$validator_stats = Misc::preSetArrayValues( $validator_stats, [ 'total_records', 'valid_records', 'invalids_records' ], 0 );
 | 
						|
 | 
						|
				$retarr = [
 | 
						|
						'api_retval'  => $retval,
 | 
						|
						'api_details' => [
 | 
						|
								'code'                         => $code,
 | 
						|
								'description'                  => $description,
 | 
						|
								'record_details'               => [
 | 
						|
										'total'   => $validator_stats['total_records'],
 | 
						|
										'valid'   => $validator_stats['valid_records'],
 | 
						|
										'invalid' => ( $validator_stats['total_records'] - $validator_stats['valid_records'] ),
 | 
						|
								],
 | 
						|
								'user_generic_status_batch_id' => $user_generic_status_batch_id,
 | 
						|
								'system_job_queue'             => $system_job_queue,
 | 
						|
								'details'                      => $details,
 | 
						|
						],
 | 
						|
				];
 | 
						|
 | 
						|
				if ( $retval === false ) {
 | 
						|
					Debug::Arr( $retarr, 'returnHandler v1 ERROR: ' . (int)$retval, __FILE__, __LINE__, __METHOD__, 10 );
 | 
						|
				}
 | 
						|
 | 
						|
				//Handle progress bar here, make sure they are stopped and if an error occurs display the error.
 | 
						|
				if ( $retval === false ) {
 | 
						|
					//Try to show detailed validation error messages if at all possible.
 | 
						|
					// Check for $details[0] because returnHandlers that lead into this seem to force an array with '0' key as per:
 | 
						|
					//   $this->returnHandler( FALSE, 'VALIDATION', TTi18n::getText('INVALID DATA'), array( 0 => $validation_obj->getErrorsArray() ), array('total_records' => 1, 'valid_records' => 0 ) );
 | 
						|
					if ( isset( $details ) && is_array( $details ) && isset( $details[0] ) ) {
 | 
						|
						$validator = new Validator();
 | 
						|
						$description .= "<br>\n<br>\n" . $validator->getTextErrors( true, $details[0] );
 | 
						|
						unset( $validator );
 | 
						|
					}
 | 
						|
					if ( $this->isProgressBarStarted() == true ) {
 | 
						|
						$this->getProgressBarObject()->error( $this->getAPIMessageID(), $description );
 | 
						|
					}
 | 
						|
				} else {
 | 
						|
					if ( $this->isProgressBarStarted() == true ) {
 | 
						|
						$this->getProgressBarObject()->stop( $this->getAPIMessageID() );
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				return $retarr;
 | 
						|
			}
 | 
						|
 | 
						|
			//No errors, or additional information, return unmodified data.
 | 
						|
			return $retval;
 | 
						|
		} else {
 | 
						|
			if ( $retval === false ) {
 | 
						|
				if ( $code == '' ) {
 | 
						|
					$code = 'GENERAL';
 | 
						|
				}
 | 
						|
				if ( $description == '' ) {
 | 
						|
					$description = 'Insufficient data to carry out action';
 | 
						|
				}
 | 
						|
			} else if ( $retval === true ) {
 | 
						|
				if ( $code == '' ) {
 | 
						|
					$code = 'SUCCESS';
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			$validator_stats = Misc::preSetArrayValues( $validator_stats, [ 'total_records', 'valid_records', 'invalids_records' ], 0 );
 | 
						|
 | 
						|
			$retarr = [
 | 
						|
					'api_retval'  => $retval,
 | 
						|
					'api_details' => [
 | 
						|
							'code'                         => $code,
 | 
						|
							'description'                  => $description,
 | 
						|
							'record_details'               => [
 | 
						|
									'total'   => $validator_stats['total_records'],
 | 
						|
									'valid'   => $validator_stats['valid_records'],
 | 
						|
									'invalid' => ( $validator_stats['total_records'] - $validator_stats['valid_records'] ),
 | 
						|
							],
 | 
						|
							'user_generic_status_batch_id' => $user_generic_status_batch_id,
 | 
						|
							'system_job_queue'             => $system_job_queue,
 | 
						|
							//Allows the API to modify the original request data to send back to the UI for notifying the user.
 | 
						|
							//We would like to implement validation on non-set*() calls as well perhaps?
 | 
						|
							'request'                      => $request_data,
 | 
						|
							'pager'                        => $this->getPagerData(),
 | 
						|
							'details'                      => $details,
 | 
						|
					],
 | 
						|
			];
 | 
						|
 | 
						|
			if ( $retval === false ) {
 | 
						|
				Debug::Arr( $retarr, 'returnHandler v2 ERROR: ' . (int)$retval, __FILE__, __LINE__, __METHOD__, 10 );
 | 
						|
			}
 | 
						|
 | 
						|
			//Handle progress bar here, make sure they are stopped and if an error occurs display the error.
 | 
						|
			if ( $retval === false ) {
 | 
						|
				//Try to show detailed validation error messages if at all possible.
 | 
						|
				// Check for $details[0] because returnHandlers that lead into this seem to force an array with '0' key as per:
 | 
						|
				//   $this->returnHandler( FALSE, 'VALIDATION', TTi18n::getText('INVALID DATA'), array( 0 => $validation_obj->getErrorsArray() ), array('total_records' => 1, 'valid_records' => 0 ) );
 | 
						|
				if ( isset( $details ) && is_array( $details ) && isset( $details[0] ) ) {
 | 
						|
					$validator = new Validator();
 | 
						|
					$description .= "<br>\n<br>\n" . $validator->getTextErrors( true, $details[0] );
 | 
						|
					unset( $validator );
 | 
						|
				}
 | 
						|
				if ( $this->isProgressBarStarted() ) {
 | 
						|
					$this->getProgressBarObject()->error( $this->getAPIMessageID(), $description );
 | 
						|
				}
 | 
						|
			} else {
 | 
						|
				if ( $this->isProgressBarStarted() ) {
 | 
						|
					$this->getProgressBarObject()->stop( $this->getAPIMessageID() );
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			//Debug::Arr($retarr, 'returnHandler: '. (int)$retval, __FILE__, __LINE__, __METHOD__, 10);
 | 
						|
			return $retarr;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @param mixed $retarr
 | 
						|
	 * @return mixed
 | 
						|
	 */
 | 
						|
	function stripReturnHandler( $retarr ) {
 | 
						|
		if ( isset( $retarr['api_retval'] ) ) {
 | 
						|
			return $retarr['api_retval'];
 | 
						|
		}
 | 
						|
 | 
						|
		return $retarr;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Bridge to main class getVariableToFunctionMap factory.
 | 
						|
	 * @param string $name
 | 
						|
	 * @param string|int $parent
 | 
						|
	 * @return array
 | 
						|
	 */
 | 
						|
	function getVariableToFunctionMap( $name, $parent = null ) {
 | 
						|
		return $this->getMainClassObject()->getVariableToFunctionMap( $name, $parent );
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Take a API ReturnHandler array and pulls out the Validation errors/warnings to be merged back into another Validator
 | 
						|
	 * This is useful for calling one API function from another one when their are sub-classes.
 | 
						|
	 * @param $api_retarr
 | 
						|
	 * @param bool $validator_obj
 | 
						|
	 * @return bool|Validator
 | 
						|
	 */
 | 
						|
	function convertAPIReturnHandlerToValidatorObject( $api_retarr, $validator_obj = false ) {
 | 
						|
		if ( is_object( $validator_obj ) ) {
 | 
						|
			$validator = $validator_obj;
 | 
						|
		} else {
 | 
						|
			$validator = new Validator;
 | 
						|
		}
 | 
						|
 | 
						|
		if ( isset( $api_retarr['api_retval'] ) && $api_retarr['api_retval'] === false && isset( $api_retarr['api_details']['details'] ) ) {
 | 
						|
			foreach ( $api_retarr['api_details']['details'] as $tmp_validation_error_label => $validation_row ) {
 | 
						|
				if ( isset( $validation_row['error'] ) ) {
 | 
						|
					foreach ( $validation_row['error'] as $validation_error_label => $validation_error_msg ) {
 | 
						|
						$validator->Error( $validation_error_label, $validation_error_msg[0] );
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				if ( isset( $validation_row['warning'] ) ) {
 | 
						|
					foreach ( $validation_row['warning'] as $validation_warning_label => $validation_warning_msg ) {
 | 
						|
						$validator->Warning( $validation_warning_label, $validation_warning_msg[0] );
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				//Before warnings were added, validation errors were just directly in the details array, so try to handle those here.
 | 
						|
				//  This is used by TimeTrexPaymentServices API, since it doesn't use warnings.
 | 
						|
				if ( !isset( $validation_row['error'] ) && !isset( $validation_row['warning'] ) ) {
 | 
						|
					foreach ( $validation_row as $tmp_validation_error_label_b => $validation_error_msg ) {
 | 
						|
						if ( is_array( $validation_error_msg ) && isset( $validation_error_msg[0] ) ) {
 | 
						|
							$validator->Error( $tmp_validation_error_label_b, $validation_error_msg[0] );
 | 
						|
						} else {
 | 
						|
							$validator->Error( $tmp_validation_error_label_b, $validation_error_msg );
 | 
						|
						}
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		return $validator;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @param Validator[] $validator_obj_arr Array of Validator objects.
 | 
						|
	 * @param string $record_label           Prefix for record label if performing a mass function to differentiate one record from another.
 | 
						|
	 * @return array|bool
 | 
						|
	 */
 | 
						|
	function setValidationArray( $validator_obj_arr, $record_label = null ) {
 | 
						|
		//Handle validator array objects in order.
 | 
						|
		$validator = [];
 | 
						|
 | 
						|
		if ( !is_array( $validator_obj_arr ) ) {
 | 
						|
			$validator_obj_arr = [ $validator_obj_arr ];
 | 
						|
		}
 | 
						|
 | 
						|
		foreach ( $validator_obj_arr as $key => $validator_obj ) {
 | 
						|
			//Sometimes a Factory object is passed in, so we have to pull the ->Validator property from that if it happens.
 | 
						|
			if ( is_a( $validator_obj, 'Validator' ) == false && isset( $validator_obj->Validator ) && is_a( $validator_obj->Validator, 'Validator' ) ) {
 | 
						|
				$validator_obj = $validator_obj->Validator;
 | 
						|
			}
 | 
						|
 | 
						|
			if ( $this->getProtocolVersion() == 1 ) { //Don't return any warnings and therefore don't put errors in its own array element.
 | 
						|
				if ( $validator_obj->isError() === true ) {
 | 
						|
					$validator = $validator_obj->getErrorsArray( $record_label );
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			} else {
 | 
						|
				if ( $validator_obj->isError() === true ) {
 | 
						|
					$validator['error'] = $validator_obj->getErrorsArray( $record_label );
 | 
						|
					break;
 | 
						|
				} else {
 | 
						|
					//Check for primary validator warnings next.
 | 
						|
					if ( $validator_obj->isWarning() === true ) {
 | 
						|
						$validator['warning'] = $validator_obj->getWarningsArray( $record_label );
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if ( count( $validator ) > 0 ) {
 | 
						|
			return $validator;
 | 
						|
		}
 | 
						|
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @param object|array|bool $validator
 | 
						|
	 * @param array $validator_stats
 | 
						|
	 * @param int $key
 | 
						|
	 * @param array|bool $save_result
 | 
						|
	 * @param bool $user_generic_status_batch_id
 | 
						|
	 * @return array
 | 
						|
	 */
 | 
						|
	function handleRecordValidationResults( $validator, $validator_stats, $key, $save_result, $user_generic_status_batch_id = false, $system_job_queue = false ) {
 | 
						|
		global $config_vars;
 | 
						|
		if ( isset( $config_vars['other']['enable_job_queue'] ) && $config_vars['other']['enable_job_queue'] != true ) { //If the job queue is disabled, force system_job_queue flag to always be false so we don't trigger a spinner in the UI.
 | 
						|
			$system_job_queue = false;
 | 
						|
		}
 | 
						|
 | 
						|
		if ( $validator_stats['valid_records'] > 0 && $validator_stats['total_records'] == $validator_stats['valid_records'] ) {
 | 
						|
			if ( $validator_stats['total_records'] == 1 ) {
 | 
						|
				return $this->returnHandler( $save_result[$key], true, false, false, false, $user_generic_status_batch_id, false, $system_job_queue ); //Single valid record
 | 
						|
			} else {
 | 
						|
				return $this->returnHandler( true, 'SUCCESS', TTi18n::getText( 'MULTIPLE RECORDS SAVED' ), $save_result, $validator_stats, $user_generic_status_batch_id, false, $system_job_queue ); //Multiple valid records
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			return $this->returnHandler( false, 'VALIDATION', TTi18n::getText( 'INVALID DATA' ), $validator, $validator_stats, $user_generic_status_batch_id, false, $system_job_queue );
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	//
 | 
						|
	// **IMPORTANT** Functions below this are the only functions that are designed to be called remotely from the API, and therefore should be whitelisted.
 | 
						|
	//               These need to be added to API.inc.php in isWhiteListedAPICall()
 | 
						|
	//
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Bridge to main class getOptions factory.
 | 
						|
	 * @param bool $name
 | 
						|
	 * @param string|int $parent
 | 
						|
	 * @return array|bool
 | 
						|
	 */
 | 
						|
	function getOptions( $name = false, $parent = null ) {
 | 
						|
		if ( $name != '' ) {
 | 
						|
			if ( method_exists( $this->getMainClassObject(), 'getOptions' ) ) {
 | 
						|
				return $this->getMainClassObject()->getOptions( $name, $parent );
 | 
						|
			} else {
 | 
						|
				Debug::Text( 'getOptions() function does not exist for object: ' . get_class( $this->getMainClassObject() ), __FILE__, __LINE__, __METHOD__, 10 );
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			Debug::Text( 'ERROR: Name not provided, unable to return data...', __FILE__, __LINE__, __METHOD__, 10 );
 | 
						|
		}
 | 
						|
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Bridge multiple batched requests to main class getOptions factory.
 | 
						|
	 * @param array $requested_options
 | 
						|
	 * @return array
 | 
						|
	 */
 | 
						|
	function getOptionsBatch( $requested_options = [] ) {
 | 
						|
		$retarr = [];
 | 
						|
 | 
						|
		if ( is_array( $requested_options ) && count( $requested_options ) > 0 ) {
 | 
						|
			foreach ( $requested_options as $option => $parent ) {
 | 
						|
				$retarr[$option] = $this->getOptions( $option, $parent );
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		return $retarr;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Download a result_set as a csv.
 | 
						|
	 * @param string $format
 | 
						|
	 * @param string $file_name
 | 
						|
	 * @param array $result
 | 
						|
	 * @param array $filter_columns
 | 
						|
	 * @return array|bool
 | 
						|
	 */
 | 
						|
	function exportRecords( $format, $file_name, $result, $filter_columns ) {
 | 
						|
		if ( isset( $result[0] ) && is_array( $result[0] ) && is_array( $filter_columns ) && count( $filter_columns ) > 0 ) {
 | 
						|
			$columns = Misc::arrayIntersectByKey( array_keys( $filter_columns ), Misc::trimSortPrefix( $this->getOptions( 'columns' ) ) );
 | 
						|
 | 
						|
			$file_extension = $format;
 | 
						|
			$mime_type = 'application/' . $format;
 | 
						|
			$output = '';
 | 
						|
 | 
						|
			if ( $format == 'csv' ) {
 | 
						|
				$output = Misc::Array2CSV( $result, $columns, false );
 | 
						|
			}
 | 
						|
 | 
						|
			$this->getProgressBarObject()->stop( $this->getAPIMessageID() );
 | 
						|
			if ( $output !== false ) {
 | 
						|
				Misc::APIFileDownload( $file_name . '.' . $file_extension, $mime_type, $output );
 | 
						|
				return null; //Do not send return value (even TRUE/FALSE), otherwise it could get appending to the end of the downloaded file.
 | 
						|
			} else {
 | 
						|
				return $this->returnHandler( false, 'VALIDATION', TTi18n::getText( 'ERROR: No data to export...' ) );
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		return $this->returnHandler( true ); //No records returned.
 | 
						|
	}
 | 
						|
 | 
						|
	//
 | 
						|
	// **IMPORTANT** Functions above this are the only functions that are designed to be called remotely from the API, and therefore should be whitelisted.
 | 
						|
	//               These need to be added to API.inc.php in isWhiteListedAPICall()
 | 
						|
	//
 | 
						|
 | 
						|
}
 | 
						|
?>
 |