Files
2026-04-28 15:13:50 +02:00

395 lines
14 KiB
PHP

<?php
namespace QuadLayers\WOOCCM\Controller;
use QuadLayers\WOOCCM\Upload;
use QuadLayers\WOOCCM\Plugin;
/**
* Order Class
*/
class Order extends Upload {
protected static $_instance;
public function __construct() {
add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts' ) );
add_action( 'wp_enqueue_scripts', array( $this, 'frontend_scripts' ) );
add_action( 'wp_ajax_wooccm_order_attachment_upload', array( $this, 'ajax_order_attachment_upload' ) );
add_action( 'wp_ajax_nopriv_wooccm_order_attachment_upload', array( $this, 'ajax_order_attachment_upload' ) );
// Order
// --------------------------------------------------------------------------
add_action( 'add_meta_boxes', array( $this, 'add_metabox' ) );
add_action( 'add_meta_boxes', array( $this, 'add_metabox_woocommerce_page_wc_orders' ) );
// Panel
// -------------------------------------------------------------------------
add_action( 'wooccm_sections_header', array( $this, 'add_header' ) );
add_action( 'woocommerce_sections_' . WOOCCM_PREFIX, array( $this, 'add_section' ), 99 );
add_action( 'woocommerce_settings_save_' . WOOCCM_PREFIX, array( $this, 'save_settings' ) );
// Frontend
// -------------------------------------------------------------------------
add_action( 'woocommerce_thankyou', array( $this, 'add_upload_files' ) );
add_action( 'woocommerce_view_order', array( $this, 'add_upload_files' ) );
add_action( 'woocommerce_thankyou', array( $this, 'add_custom_fields' ) );
add_action( 'woocommerce_view_order', array( $this, 'add_custom_fields' ) );
// Compatibility
// -------------------------------------------------------------------------
add_filter( 'default_option_wooccm_order_upload_files', array( $this, 'enable_file_upload' ) );
add_filter( 'default_option_wooccm_order_upload_files_order_status', array( $this, 'upload_os' ) );
add_filter( 'default_option_wooccm_order_upload_files_title', array( $this, 'upload_title' ) );
}
public static function instance() {
if ( is_null( self::$_instance ) ) {
self::$_instance = new self();
}
return self::$_instance;
}
public function frontend_scripts() {
if ( is_account_page() || ! empty( is_wc_endpoint_url( 'order-received' ) ) ) {
Plugin::instance()->register_scripts();
wp_enqueue_style( 'wooccm-checkout-css' );
wp_enqueue_style( 'dashicons' );
wp_enqueue_script( 'wooccm-frontend-js' );
}
}
public function admin_scripts() {
$screen = get_current_screen();
if ( is_admin() && $screen ) {
if ( in_array( $screen->id, array( /* 'product', 'edit-product', */'shop_order', 'edit-shop_order', 'woocommerce_page_wc-orders' ) ) ) {
Plugin::instance()->register_scripts();
wp_enqueue_script( 'wooccm-frontend-js' );
}
}
}
public function ajax_order_attachment_upload() {
// Security Fix: CVE-2025-13965 - Added order key validation for guest orders
// Step 1: Verify nonce for CSRF protection
if ( ! check_admin_referer( 'wooccm_upload', 'nonce' ) ) {
wp_send_json_error( array( 'message' => esc_html__( 'Security check failed.', 'woocommerce-checkout-manager' ) ) );
}
if ( empty( $_REQUEST ) ) {
wp_send_json_error( array( 'message' => esc_html__( 'Invalid request.', 'woocommerce-checkout-manager' ) ) );
}
// Step 2: Validate order ID
$order_id = ( isset( $_REQUEST['order_id'] ) ? absint( $_REQUEST['order_id'] ) : false );
if ( empty( $order_id ) ) {
wp_send_json_error( array( 'message' => esc_html__( 'Empty order id.', 'woocommerce-checkout-manager' ) ) );
}
$order = wc_get_order( $order_id );
if ( ! $order ) {
wp_send_json_error( array( 'message' => esc_html__( 'Invalid order.', 'woocommerce-checkout-manager' ) ) );
}
// Step 3: Authorization check - distinguish between logged-in users and guests
$current_user = wp_get_current_user();
$is_user_logged = 0 !== $current_user->ID;
if ( ! $is_user_logged ) {
// GUEST ORDER VALIDATION
// CRITICAL: Add order key validation for guests (CVE-2025-13965 fix)
// The order key is the secret token in URLs like /view-order/123/?key=wc_order_ABC123xyz
// This prevents attackers from uploading files to arbitrary guest orders
$order_key = isset( $_REQUEST['order_key'] ) ? wc_clean( wp_unslash( $_REQUEST['order_key'] ) ) : '';
if ( empty( $order_key ) ) {
wp_send_json_error( array( 'message' => esc_html__( 'Order key is required for guest orders.', 'woocommerce-checkout-manager' ) ) );
}
// Validate order key matches using timing-safe comparison
if ( ! hash_equals( $order->get_order_key(), $order_key ) ) {
wp_send_json_error( array( 'message' => esc_html__( 'Invalid order key.', 'woocommerce-checkout-manager' ) ) );
}
// Defense in depth: Also verify session email matches (additional security layer)
$session_handler = WC()->session;
if ( $session_handler && ! empty( $session_handler->get( 'customer' )['email'] ) ) {
$order_email = $order->get_billing_email();
$session_customer_email = $session_handler->get( 'customer' )['email'];
if ( $order_email !== $session_customer_email ) {
wp_send_json_error( array( 'message' => esc_html__( 'Email verification failed.', 'woocommerce-checkout-manager' ) ) );
}
}
} else {
// LOGGED-IN USER VALIDATION
$order_user_id = $order->get_user_id();
// Check if user has admin/shop manager capabilities
$user_has_capabilities = current_user_can( 'administrator' ) || current_user_can( 'edit_others_shop_orders' ) || current_user_can( 'delete_others_shop_orders' );
// Check if the logged-in user owns this order
$is_current_user_order_equal_user_id = $current_user->ID === $order_user_id;
if ( ! $user_has_capabilities && ! $is_current_user_order_equal_user_id ) {
wp_send_json_error( array( 'message' => esc_html__( 'This is not your order.', 'woocommerce-checkout-manager' ) ) );
}
}
// Step 4: Validate files are present
// It cannot be wp_unslash becouse it has images paths.
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash
$files = isset( $_FILES['wooccm_order_attachment_upload'] ) ? wc_clean( $_FILES['wooccm_order_attachment_upload'] ) : false;
if ( empty( $files ) ) {
wp_send_json_error( array( 'message' => esc_html__( 'No uploads were recognized. Files were not uploaded.', 'woocommerce-checkout-manager' ) ) );
}
$post = get_post( $order_id );
if ( ! $post ) {
wp_send_json_error( array( 'message' => esc_html__( 'Invalid order id.', 'woocommerce-checkout-manager' ) ) );
}
// Step 5: Process file uploads
$attachment_ids = $this->process_uploads( $files, 'wooccm_order_attachment_upload', $order_id );
if ( count( $attachment_ids ) ) {
ob_start();
if ( ! empty( $_REQUEST['metabox'] ) ) {
$this->add_metabox_content( $post );
} else {
$this->add_upload_files( $post->ID );
}
wp_send_json_success( ob_get_clean() );
}
wp_send_json_error( array( 'message' => esc_html__( 'Unknown error.', 'woocommerce-checkout-manager' ) ) );
}
public function add_upload_files( $order_id ) {
if ( get_option( 'wooccm_order_upload_files', 'no' ) === 'yes' ) {
$order = wc_get_order( $order_id );
if ( $order ) {
if ( in_array( "wc-{$order->get_status()}", array_values( get_option( 'wooccm_order_upload_files_order_status', array() ) ) ) ) {
$attachments = get_posts(
array(
'fields' => 'ids',
'post_type' => 'attachment',
'numberposts' => -1,
'post_status' => null,
'post_parent' => $order->get_id(),
)
);
wc_get_template(
'templates/order/order-upload-files.php',
array(
'order' => $order,
'attachments' => $attachments,
),
'',
WOOCCM_PLUGIN_DIR
);
}
}
}
}
public function add_custom_fields( $order_id ) {
if ( get_option( 'wooccm_order_custom_fields', 'yes' ) === 'yes' ) {
$order = wc_get_order( $order_id );
if ( $order ) {
if ( in_array( "wc-{$order->get_status()}", array_values( get_option( 'wooccm_order_custom_fields_status', array_keys( wc_get_order_statuses() ) ) ) ) ) {
wc_get_template( 'templates/order/order-custom-fields.php', array( 'order' => $order ), '', WOOCCM_PLUGIN_DIR );
}
}
}
}
public function add_metabox_content( $post ) {
$order = null;
if ( method_exists( $post, 'get_id' ) ) {
$order = wc_get_order( $post->get_id() );
} else {
$order = wc_get_order( $post->ID );
}
if ( $order ) {
$attachments = get_posts(
array(
'fields' => 'ids',
'post_type' => 'attachment',
'numberposts' => -1,
'post_status' => null,
'post_parent' => $order->get_id(),
)
);
include WOOCCM_PLUGIN_DIR . 'lib/view/backend/meta-boxes/html-order-uploads.php';
}
}
// Admin
// -------------------------------------------------------------------------
public function add_metabox() {
add_meta_box( 'wooccm-order-files', esc_html__( 'Order Files', 'woocommerce-checkout-manager' ), array( $this, 'add_metabox_content' ), 'shop_order', 'normal', 'default' );
}
public function add_metabox_woocommerce_page_wc_orders() {
add_meta_box( 'wooccm-order-files', esc_html__( 'Order Files', 'woocommerce-checkout-manager' ), array( $this, 'add_metabox_content' ), 'woocommerce_page_wc-orders', 'normal', 'default' );
}
// Panel
// ---------------------------------------------------------------------------
public function get_settings() {
return array(
array(
'type' => 'title',
'id' => 'section_title',
),
array(
'name' => esc_html__( 'Add upload files', 'woocommerce-checkout-manager' ),
'desc_tip' => esc_html__( 'Allow customers to upload files in the order.', 'woocommerce-checkout-manager' ),
'id' => 'wooccm_order_upload_files',
'type' => 'select',
'class' => 'chosen_select',
'options' => array(
'yes' => esc_html__( 'Yes', 'woocommerce-checkout-manager' ),
'no' => esc_html__( 'No', 'woocommerce-checkout-manager' ),
),
'default' => 'no',
),
array(
'name' => esc_html__( 'Add for this order status', 'woocommerce-checkout-manager' ),
'desc_tip' => esc_html__( 'Allow customers to upload files in the order.', 'woocommerce-checkout-manager' ),
'id' => 'wooccm_order_upload_files_order_status',
'type' => 'multiselect',
'class' => 'chosen_select',
'options' => wc_get_order_statuses(),
'default' => array_keys( wc_get_order_statuses() ),
),
array(
'name' => esc_html__( 'Add upload files title', 'woocommerce-checkout-manager' ),
'desc_tip' => esc_html__( 'Add custom title for the uploads files table.', 'woocommerce-checkout-manager' ),
'id' => 'wooccm_order_upload_files_title',
'type' => 'text',
'placeholder' => esc_html__( 'Uploaded files', 'woocommerce-checkout-manager' ),
),
array(
'name' => esc_html__( 'Add custom fields', 'woocommerce-checkout-manager' ),
'desc_tip' => esc_html__( 'Show the selected fields in the order.', 'woocommerce-checkout-manager' ),
'id' => 'wooccm_order_custom_fields',
'type' => 'select',
'class' => 'chosen_select',
'options' => array(
'yes' => esc_html__( 'Yes', 'woocommerce-checkout-manager' ),
'no' => esc_html__( 'No', 'woocommerce-checkout-manager' ),
),
'default' => 'yes',
),
array(
'name' => esc_html__( 'Add for this order status', 'woocommerce-checkout-manager' ),
'desc_tip' => esc_html__( 'Allow customers to upload files in the order.', 'woocommerce-checkout-manager' ),
'id' => 'wooccm_order_custom_fields_status',
'type' => 'multiselect',
'class' => 'chosen_select',
'options' => wc_get_order_statuses(),
'default' => array_keys( wc_get_order_statuses() ),
),
array(
'name' => esc_html__( 'Add custom fields title', 'woocommerce-checkout-manager' ),
'desc_tip' => esc_html__( 'Add custom title for the uploads files table.', 'woocommerce-checkout-manager' ),
'id' => 'wooccm_order_custom_fields_title',
'type' => 'text',
'placeholder' => esc_html__( 'Order extra', 'woocommerce-checkout-manager' ),
),
array(
'type' => 'sectionend',
'id' => 'section_end',
),
);
}
public function add_header() {
global $current_section;
?>
<li><a href="<?php echo esc_url( admin_url( 'admin.php?page=wc-settings&tab=wooccm&section=order' ) ); ?>" class="<?php echo ( 'order' == $current_section ? 'current' : '' ); ?>"><?php esc_html_e( 'Order', 'woocommerce-checkout-manager' ); ?></a> | </li>
<?php
}
public function add_section() {
global $current_section;
if ( 'order' == $current_section ) {
$settings = $this->get_settings();
include_once WOOCCM_PLUGIN_DIR . 'lib/view/backend/pages/order.php';
}
}
public function save_settings() {
global $current_section;
if ( 'order' == $current_section ) {
woocommerce_update_options( $this->get_settings() );
}
}
// Compatibility
// -------------------------------------------------------------------------
public function enable_file_upload( $value ) {
$options = get_option( 'wccs_settings' );
if ( ! empty( $options['checkness']['enable_file_upload'] ) ) {
return 'yes';
}
return $value;
}
public function upload_os( $value ) {
$options = get_option( 'wccs_settings' );
if ( ! empty( $options['checkness']['upload_os'] ) ) {
$upload_os_option = $options['checkness']['upload_os'];
if ( ! is_array( $upload_os_option ) ) {
$upload_os_option = array( $upload_os_option );
}
return (array) @implode( ',', $upload_os_option );
}
return $value;
}
public function upload_title( $value ) {
$options = get_option( 'wccs_settings' );
if ( ! empty( $options['checkness']['upload_title'] ) ) {
return $options['checkness']['upload_title'];
}
return $value;
}
}