Current File : /home/quantums/bodyguardslosangeles.net/wp-content/plugins/woocommerce-square/includes/AJAX.php
<?php
/**
 * WooCommerce Square
 *
 * This source file is subject to the GNU General Public License v3.0
 * that is bundled with this package in the file license.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0 or later
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@woocommerce.com so we can send you a copy immediately.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade WooCommerce Square to newer
 * versions in the future. If you wish to customize WooCommerce Square for your
 * needs please refer to https://docs.woocommerce.com/document/woocommerce-square/
 *
 * @author    WooCommerce
 * @copyright Copyright: (c) 2019, Automattic, Inc.
 * @license   http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0 or later
 */

namespace WooCommerce\Square;

defined( 'ABSPATH' ) || exit;

use WooCommerce\Square\Handlers\Product;
use WooCommerce\Square\Sync\Records;

/**
 * AJAX handler.
 *
 * @since 2.0.0
 */
class AJAX {


	/**
	 * Adds AJAX action callbacks.
	 *
	 * @since 2.0.0
	 */
	public function __construct() {

		// check an individual product sync status
		add_action( 'wp_ajax_wc_square_get_quick_edit_product_details', array( $this, 'get_quick_edit_product_details' ) );

		// fetch product stock from Square
		add_action( 'wp_ajax_wc_square_fetch_product_stock_with_square', array( $this, 'fetch_product_stock_with_square' ) );

		add_action( 'wp_ajax_wc_square_import_products_from_square', array( $this, 'import_products_from_square' ) );

		// sync all products with Square
		add_action( 'wp_ajax_wc_square_sync_products_with_square', array( $this, 'sync_products_with_square' ) );

		// handle sync records
		add_action( 'wp_ajax_wc_square_handle_sync_records', array( $this, 'handle_sync_records' ) );

		// get the status of a sync job
		add_action( 'wp_ajax_wc_square_get_sync_with_square_status', array( $this, 'get_sync_with_square_job_status' ) );

		// rest end point to import products
		add_action( 'rest_api_init', array( $this, 'register_rest_endpoints' ) );
	}

	/**
	 * Register REST API endpoints.
	 *
	 * @since 2.1.6
	 */
	public function register_rest_endpoints() {
		register_rest_route(
			'wc/v3',
			'wc_square/import-products',
			array(
				'methods'             => 'POST',
				'callback'            => array( $this, 'import_products_from_square' ),
				'permission_callback' => array( $this, 'check_permission' ),
			)
		);

		register_rest_route(
			'wc/v3',
			'wc_square/connected_page_visited',
			array(
				'methods'             => 'POST',
				'callback'            => array( $this, 'update_connected_page_visited' ),
				'permission_callback' => array( $this, 'check_permission' ),
			)
		);
	}

	/**
	 * Verify access.
	 *
	 * Override this method if custom permissions required.
	 */
	public function check_permission() {
		return current_user_can( 'manage_woocommerce' ); // phpcs:ignore WordPress.WP.Capabilities.Unknown
	}

	/**
	 * Fetches product stock data from Square.
	 *
	 * @internal
	 *
	 * @since 2.0.0
	 */
	public function fetch_product_stock_with_square() {
		check_ajax_referer( 'fetch-product-stock-with-square', 'security' );

		if ( ! current_user_can( 'edit_products' ) ) {
			wp_send_json_error( __( 'Invalid permissions.', 'woocommerce-square' ) );
		}

		$fix_error = __( 'Please mark product as un-synced and save, then synced again.', 'woocommerce-square' );
		$product   = isset( $_REQUEST['product_id'] ) ? wc_get_product( (int) $_REQUEST['product_id'] ) : false;

		if ( $product ) {

			try {
				$product  = Product::update_stock_from_square( $product );
				$response = array(
					'quantity'     => $product->get_stock_quantity(),
					'manage_stock' => $product->get_manage_stock(),
				);

				wp_send_json_success( $response );

			} catch ( \Exception $exception ) {

				/* translators: Placeholders: %1$s = error message, %2$s = help text */
				wp_send_json_error( sprintf( __( 'Unable to fetch inventory: %1$s. %2$s', 'woocommerce-square' ), $exception->getMessage(), $fix_error ) );
			}
		}

		/* translators: Placeholders: %s = help text */
		wp_send_json_error( sprintf( __( 'Error finding item in Square. %s', 'woocommerce-square' ), $fix_error ) );
	}


	/**
	 * Starts importing products from Square.
	 *
	 * @param \WP_REST_Request $request The REST request object.
	 *
	 * @internal
	 *
	 * @since 2.0.0
	 */
	public function import_products_from_square( $request = null ) {

		$api_callback = $request ? $request->get_param( 'api_callback' ) : false;

		$update_during_import = $request
			? $request->get_param( 'update_during_import' )
			: ( ! empty( $_POST['update_during_import'] ) && 'true' === $_POST['update_during_import'] );

		if ( ! $api_callback ) {
			check_ajax_referer( 'import-products-from-square', 'security' );
		}

		// The edit_others_shop_orders capability is used to determine if the user can access these settings.
		if ( ! current_user_can( 'edit_others_shop_orders' ) ) {
			wp_send_json_error( __( 'Could not start import. Invalid permissions.', 'woocommerce-square' ) );
		}

		$started = wc_square()->get_sync_handler()->start_product_import( $update_during_import );

		if ( ! $started ) {
			wp_send_json_error( __( 'Could not start import. Please try again.', 'woocommerce-square' ) );
		}

		wp_send_json_success( __( 'Your products are being imported in the background! This may take some time to complete.', 'woocommerce-square' ) );
	}

	/**
	 * Update the connected_page_visited option.
	 * This will be used to show a message to visit the onboarding
	 * wizard and the onboarding wizard submenu until found `true`.
	 *
	 * @since 4.7.0
	 */
	public function update_connected_page_visited() {
		update_option( 'wc_square_connected_page_visited', true, false );
		wp_send_json_success();
	}

	/**
	 * Starts syncing products with Square.
	 *
	 * @internal
	 *
	 * @since 2.0.0
	 */
	public function sync_products_with_square() {

		check_ajax_referer( 'sync-products-with-square', 'security' );

		// The edit_others_shop_orders capability is used to determine if the user can access these settings.
		if ( ! current_user_can( 'edit_others_shop_orders' ) ) {
			wp_send_json_error();
		}

		$started = wc_square()->get_sync_handler()->start_manual_sync();

		if ( ! $started ) {
			wp_send_json_error();
		}

		wp_send_json_success();
	}


	/**
	 * Handles sync records actions.
	 *
	 * @internal
	 *
	 * @since 2.0.0
	 */
	public function handle_sync_records() {

		check_ajax_referer( 'handle-sync-with-square-records', 'security' );

		if ( ! current_user_can( 'edit_others_shop_orders' ) ) {
			wp_send_json_error( __( 'An error occurred. Invalid permissions.', 'woocommerce-square' ) );
		}

		$error = '';

		if ( isset( $_POST['id'], $_POST['handle'] ) ) {

			$id     = sanitize_key( $_POST['id'] );
			$action = sanitize_key( $_POST['handle'] );

			if ( 'all' === $id && 'delete' === $action ) {

				$outcome = Records::clean_records();
				$error   = esc_html__( 'Could not delete records.', 'woocommerce-square' );

			} elseif ( is_string( $id ) && '' !== $id ) {

				switch ( $action ) {

					case 'delete':
						$outcome = Records::delete_record( $id );
						$error   = esc_html__( 'Could not delete record.', 'woocommerce-square' );

						break;

					case 'resolve':
						$record = Records::get_record( $id );
						if ( $record ) {
							$record->resolve();
							$outcome = $record->save();
						}

						$error = esc_html__( 'Could not resolve record.', 'woocommerce-square' );

						break;

					case 'unsync':
						$record  = Records::get_record( $id );
						$product = $record ? $record->get_product() : null;
						if ( $product ) {
							$record->resolve();
							$outcome = Product::unset_synced_with_square( $product ) && $record->save();
						}

						$error = esc_html__( 'Could not unsync product.', 'woocommerce-square' );

						break;
				}
			}

			if ( ! empty( $outcome ) ) {
				wp_send_json_success( $outcome );
			}
		}

		/* translators: Placeholder: %s - error message */
		wp_send_json_error( sprintf( __( 'An error occurred. %s', 'woocommerce-square' ), $error ) );
	}


	/**
	 * Gets a sync job status.
	 *
	 * Also bumps the job progression (useful for when background processing isn't available).
	 *
	 * @internal
	 *
	 * @since 2.0.0
	 */
	public function get_sync_with_square_job_status() {
		check_ajax_referer( 'get-sync-with-square-status', 'security' );

		if ( ! current_user_can( 'edit_others_shop_orders' ) ) {
			wp_send_json_error( __( 'An error occurred. Invalid permissions.', 'woocommerce-square' ) );
		}

		$job_id = isset( $_POST['job_id'] ) ? sanitize_key( $_POST['job_id'] ) : null;

		if ( $job_id ) {
			try {
				$handler         = wc_square()->get_background_job_handler();
				$job_in_progress = $handler ? $handler->get_job( $job_id ) : false;
				if ( $job_in_progress ) {

					$result = array(
						'action'                  => $job_in_progress->action,
						'id'                      => $job_in_progress->id,
						'job_products_count'      => count( $job_in_progress->product_ids ),
						'percentage'              => ( (float) count( $job_in_progress->processed_product_ids ) / max( 1, count( $job_in_progress->product_ids ) ) ) * 100,
						'imported_products_count' => count( $job_in_progress->processed_product_ids ),
						'updated_products_count'  => count( $job_in_progress->updated_product_ids ),
						'skipped_products_count'  => count( $job_in_progress->skipped_products ),
						'status'                  => $job_in_progress->status,
					);

					wp_send_json_success( $result );
				}
			} catch ( \Exception $e ) {

				wp_send_json_error( $e->getMessage() );
			}
		}

		/* translators: Placeholder: %s - sync job ID */
		wp_send_json_error( sprintf( esc_html__( 'No sync job in progress found %s', 'woocommerce-square' ), is_string( $job_id ) ? $job_id : null ) );
	}

	/**
	 * Get sync status, variable status, and edit url for product
	 *
	 * Used to manipulate quick edit menu for product
	 *
	 * @since 2.1.6
	 */
	public function get_quick_edit_product_details() {

		check_ajax_referer( 'get-quick-edit-product-details', 'security' );

		if ( ! current_user_can( 'edit_products' ) ) {
			wp_send_json_error( 'invalid_permission' );
		}

		$product = isset( $_POST['product_id'] ) ? wc_get_product( (int) $_POST['product_id'] ) : false;

		if ( $product ) {
			$is_variable = $product->is_type( 'variable' );

			if ( ! Product::has_sku( $product ) ) {
				if ( $is_variable ) {
					wp_send_json_error( 'missing_variation_sku' );
				} else {
					wp_send_json_error( 'missing_sku' );
				}
			}

			$is_synced_with_square = Product::is_synced_with_square( $product ) ? 'yes' : 'no';
			$is_woocommerce_sor    = wc_square()->get_settings_handler()->is_system_of_record_woocommerce();

			wp_send_json_success(
				array(
					'edit_url'              => $is_woocommerce_sor ? get_edit_post_link( (int) $_POST['product_id'] ) : null,
					'i18n'                  => $is_woocommerce_sor ? __( 'Stock must be fetched from Square before editing stock quantity', 'woocommerce-square' ) : null,
					'is_synced_with_square' => $is_synced_with_square,
					'is_variable'           => $is_variable,
				)
			);
		}
		wp_send_json_error( 'invalid_product' );
	}

}