woocommerce/woocommerce

View on GitHub
includes/class-wc-customer-download.php

Summary

Maintainability
C
1 day
Test Coverage
<?php
/**
 * Class for customer download permissions.
 *
 * @package WooCommerce\Classes
 * @version 3.0.0
 * @since   3.0.0
 */

defined( 'ABSPATH' ) || exit;

/**
 * Customer download class.
 */
class WC_Customer_Download extends WC_Data implements ArrayAccess {

    /**
     * This is the name of this object type.
     *
     * @var string
     */
    protected $object_type = 'customer_download';

    /**
     * Download Data array.
     *
     * @since 3.0.0
     * @var array
     */
    protected $data = array(
        'download_id'         => '',
        'product_id'          => 0,
        'user_id'             => 0,
        'user_email'          => '',
        'order_id'            => 0,
        'order_key'           => '',
        'downloads_remaining' => '',
        'access_granted'      => null,
        'access_expires'      => null,
        'download_count'      => 0,
    );

    /**
     * Constructor.
     *
     * @param int|object|array $download Download ID, instance or data.
     */
    public function __construct( $download = 0 ) {
        parent::__construct( $download );

        if ( is_numeric( $download ) && $download > 0 ) {
            $this->set_id( $download );
        } elseif ( $download instanceof self ) {
            $this->set_id( $download->get_id() );
        } elseif ( is_object( $download ) && ! empty( $download->permission_id ) ) {
            $this->set_id( $download->permission_id );
            $this->set_props( (array) $download );
            $this->set_object_read( true );
        } else {
            $this->set_object_read( true );
        }

        $this->data_store = WC_Data_Store::load( 'customer-download' );

        if ( $this->get_id() > 0 ) {
            $this->data_store->read( $this );
        }
    }

    /*
    |--------------------------------------------------------------------------
    | Getters
    |--------------------------------------------------------------------------
    */

    /**
     * Get download id.
     *
     * @param  string $context What the value is for. Valid values are 'view' and 'edit'.
     * @return string
     */
    public function get_download_id( $context = 'view' ) {
        return $this->get_prop( 'download_id', $context );
    }

    /**
     * Get product id.
     *
     * @param  string $context What the value is for. Valid values are 'view' and 'edit'.
     * @return integer
     */
    public function get_product_id( $context = 'view' ) {
        return $this->get_prop( 'product_id', $context );
    }

    /**
     * Get user id.
     *
     * @param  string $context What the value is for. Valid values are 'view' and 'edit'.
     * @return integer
     */
    public function get_user_id( $context = 'view' ) {
        return $this->get_prop( 'user_id', $context );
    }

    /**
     * Get user_email.
     *
     * @param  string $context What the value is for. Valid values are 'view' and 'edit'.
     * @return string
     */
    public function get_user_email( $context = 'view' ) {
        return $this->get_prop( 'user_email', $context );
    }

    /**
     * Get order_id.
     *
     * @param  string $context What the value is for. Valid values are 'view' and 'edit'.
     * @return integer
     */
    public function get_order_id( $context = 'view' ) {
        return $this->get_prop( 'order_id', $context );
    }

    /**
     * Get order_key.
     *
     * @param  string $context What the value is for. Valid values are 'view' and 'edit'.
     * @return string
     */
    public function get_order_key( $context = 'view' ) {
        return $this->get_prop( 'order_key', $context );
    }

    /**
     * Get downloads_remaining.
     *
     * @param  string $context What the value is for. Valid values are 'view' and 'edit'.
     * @return integer|string
     */
    public function get_downloads_remaining( $context = 'view' ) {
        return $this->get_prop( 'downloads_remaining', $context );
    }

    /**
     * Get access_granted.
     *
     * @param  string $context What the value is for. Valid values are 'view' and 'edit'.
     * @return WC_DateTime|null Object if the date is set or null if there is no date.
     */
    public function get_access_granted( $context = 'view' ) {
        return $this->get_prop( 'access_granted', $context );
    }

    /**
     * Get access_expires.
     *
     * @param  string $context What the value is for. Valid values are 'view' and 'edit'.
     * @return WC_DateTime|null Object if the date is set or null if there is no date.
     */
    public function get_access_expires( $context = 'view' ) {
        return $this->get_prop( 'access_expires', $context );
    }

    /**
     * Get download_count.
     *
     * @param  string $context What the value is for. Valid values are 'view' and 'edit'.
     * @return integer
     */
    public function get_download_count( $context = 'view' ) {
        // Check for count of download logs.
        $data_store       = WC_Data_Store::load( 'customer-download-log' );
        $download_log_ids = $data_store->get_download_logs_for_permission( $this->get_id() );

        $download_log_count = 0;
        if ( ! empty( $download_log_ids ) ) {
            $download_log_count = count( $download_log_ids );
        }

        // Check download count in prop.
        $download_count_prop = $this->get_prop( 'download_count', $context );

        // Return the larger of the two in case they differ.
        // If logs are removed for some reason, we should still respect the
        // count stored in the prop.
        return max( $download_log_count, $download_count_prop );
    }

    /*
    |--------------------------------------------------------------------------
    | Setters
    |--------------------------------------------------------------------------
    */

    /**
     * Set download id.
     *
     * @param string $value Download ID.
     */
    public function set_download_id( $value ) {
        $this->set_prop( 'download_id', $value );
    }
    /**
     * Set product id.
     *
     * @param int $value Product ID.
     */
    public function set_product_id( $value ) {
        $this->set_prop( 'product_id', absint( $value ) );
    }

    /**
     * Set user id.
     *
     * @param int $value User ID.
     */
    public function set_user_id( $value ) {
        $this->set_prop( 'user_id', absint( $value ) );
    }

    /**
     * Set user_email.
     *
     * @param int $value User email.
     */
    public function set_user_email( $value ) {
        $this->set_prop( 'user_email', sanitize_email( $value ) );
    }

    /**
     * Set order_id.
     *
     * @param int $value Order ID.
     */
    public function set_order_id( $value ) {
        $this->set_prop( 'order_id', absint( $value ) );
    }

    /**
     * Set order_key.
     *
     * @param string $value Order key.
     */
    public function set_order_key( $value ) {
        $this->set_prop( 'order_key', $value );
    }

    /**
     * Set downloads_remaining.
     *
     * @param integer|string $value Amount of downloads remaining.
     */
    public function set_downloads_remaining( $value ) {
        $this->set_prop( 'downloads_remaining', '' === $value ? '' : absint( $value ) );
    }

    /**
     * Set access_granted.
     *
     * @param string|integer|null $date UTC timestamp, or ISO 8601 DateTime. If the DateTime string has no timezone or offset, WordPress site timezone will be assumed. Null if their is no date.
     */
    public function set_access_granted( $date = null ) {
        $this->set_date_prop( 'access_granted', $date );
    }

    /**
     * Set access_expires.
     *
     * @param string|integer|null $date UTC timestamp, or ISO 8601 DateTime. If the DateTime string has no timezone or offset, WordPress site timezone will be assumed. Null if their is no date.
     */
    public function set_access_expires( $date = null ) {
        $this->set_date_prop( 'access_expires', $date );
    }

    /**
     * Set download_count.
     *
     * @param int $value Download count.
     */
    public function set_download_count( $value ) {
        $this->set_prop( 'download_count', absint( $value ) );
    }

    /**
     * Track a download on this permission.
     *
     * @since 3.3.0
     * @throws Exception When permission ID is invalid.
     * @param int    $user_id         Id of the user performing the download.
     * @param string $user_ip_address IP Address of the user performing the download.
     */
    public function track_download( $user_id = null, $user_ip_address = null ) {
        global $wpdb;

        // Must have a permission_id to track download log.
        if ( ! ( $this->get_id() > 0 ) ) {
            throw new Exception( __( 'Invalid permission ID.', 'woocommerce' ) );
        }

        // Increment download count, and decrement downloads remaining.
        // Use SQL to avoid possible issues with downloads in quick succession.
        // If downloads_remaining is blank, leave it blank (unlimited).
        // Also, ensure downloads_remaining doesn't drop below zero.
        $query = $wpdb->prepare(
            "
UPDATE {$wpdb->prefix}woocommerce_downloadable_product_permissions
SET download_count = download_count + 1,
downloads_remaining = IF( downloads_remaining = '', '', GREATEST( 0, downloads_remaining - 1 ) )
WHERE permission_id = %d",
            $this->get_id()
        );
        $wpdb->query( $query ); // WPCS: unprepared SQL ok.

        // Re-read this download from the data store to pull updated counts.
        $this->data_store->read( $this );

        // Track download in download log.
        $download_log = new WC_Customer_Download_Log();
        $download_log->set_timestamp( current_time( 'timestamp', true ) );
        $download_log->set_permission_id( $this->get_id() );

        if ( ! is_null( $user_id ) ) {
            $download_log->set_user_id( $user_id );
        }

        if ( ! is_null( $user_ip_address ) ) {
            $download_log->set_user_ip_address( $user_ip_address );
        }

        $download_log->save();
    }

    /*
    |--------------------------------------------------------------------------
    | ArrayAccess/Backwards compatibility.
    |--------------------------------------------------------------------------
    */

    /**
     * OffsetGet.
     *
     * @param string $offset Offset.
     * @return mixed
     */
    public function offsetGet( $offset ) {
        if ( is_callable( array( $this, "get_$offset" ) ) ) {
            return $this->{"get_$offset"}();
        }
    }

    /**
     * OffsetSet.
     *
     * @param string $offset Offset.
     * @param mixed  $value  Value.
     */
    public function offsetSet( $offset, $value ) {
        if ( is_callable( array( $this, "set_$offset" ) ) ) {
            $this->{"set_$offset"}( $value );
        }
    }

    /**
     * OffsetUnset
     *
     * @param string $offset Offset.
     */
    public function offsetUnset( $offset ) {
        if ( is_callable( array( $this, "set_$offset" ) ) ) {
            $this->{"set_$offset"}( '' );
        }
    }

    /**
     * OffsetExists.
     *
     * @param string $offset Offset.
     * @return bool
     */
    public function offsetExists( $offset ) {
        return in_array( $offset, array_keys( $this->data ), true );
    }

    /**
     * Magic __isset method for backwards compatibility. Legacy properties which could be accessed directly in the past.
     *
     * @param  string $key Key name.
     * @return bool
     */
    public function __isset( $key ) {
        return in_array( $key, array_keys( $this->data ), true );
    }

    /**
     * Magic __get method for backwards compatibility. Maps legacy vars to new getters.
     *
     * @param  string $key Key name.
     * @return mixed
     */
    public function __get( $key ) {
        if ( is_callable( array( $this, "get_$key" ) ) ) {
            return $this->{"get_$key"}( '' );
        }
    }
}