Commit inicial - WordPress Análisis de Precios Unitarios

- WordPress core y plugins
- Tema Twenty Twenty-Four configurado
- Plugin allow-unfiltered-html.php simplificado
- .gitignore configurado para excluir wp-config.php y uploads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-11-03 21:04:30 -06:00
commit a22573bf0b
24068 changed files with 4993111 additions and 0 deletions

View File

@@ -0,0 +1,144 @@
<?php
/**
* Abstract Job Callback
*
* @package restrict-content-pro
* @copyright Copyright (c) 2018, Restrict Content Pro team
* @license GPL2+
* @since 3.0
*/
namespace RCP\Utils\Batch;
/**
* Class Abstract_Job_Callback
*
* @package RCP\Utils\Batch
*/
abstract class Abstract_Job_Callback implements Job_Callback_Interface {
/**
* Maximum number of seconds the job can run during each pass
*
* @var int $time_limit
*/
protected $time_limit = 15;
/**
* The time this job started.
*
* @var int $start_time
*/
protected $start_time;
/**
* The job object
*
* @var Job $job
*/
protected $job;
/**
* The offset to use when querying records on each pass.
*
* @var int $offset
*/
protected $offset;
/**
* The number of records to query on each pass.
*
* @var int $size
*/
protected $size = 20;
/**
* Abstract_Job_Callback constructor.
*
* @param Job $job
*
* @since 3.0
*/
public function __construct( $job ) {
$this->job = $job;
}
/**
* Executes the job.
*
* @since 3.0
*/
public function execute() {
/**
* Make sure to call this method via parent::execute() in your extending class
* to update the offset during each batch.
*/
$this->offset = absint( $this->size * ( $this->get_job()->get_step() ) );
}
/**
* Retrieves the amount of records processed per step.
*
* @return int
*/
public function get_amount_per_step() {
return $this->size;
}
/**
* Count the total number of results and save the value.
*
* @since 3.1
* @return int
*/
public function get_total_count() {
$total_count = $this->get_job()->get_total_count();
if ( $total_count ) {
$total_count = 0; // count total here
$this->get_job()->set_total_count( $total_count );
}
return $total_count;
}
/**
* Runs any tasks required to finish a job.
*
* @since 3.0
* @return mixed|void
*/
abstract public function finish();
/**
* Get the associated job object.
*
* @since 3.0
* @return Job
*/
public function get_job() {
return $this->job;
}
/**
* Message to display upon job completion.
*
* @since 3.0
* @return string
*/
public function get_complete_message() {
return sprintf( __( 'Successfully processed %d/%d items. You may now leave the page.', 'rcp' ), $this->get_job()->get_current_count(), $this->get_job()->get_total_count() );
}
/**
* Determines if the job has exceeded its allowed time limit.
*
* @since 3.0
* @return bool
*/
protected function time_exceeded() {
return ( ( time() - $this->start_time ) >= $this->time_limit );
}
}

View File

@@ -0,0 +1,279 @@
<?php
/**
* Batch Functions
*
* @package restrict-content-pro
* @copyright Copyright (c) 2018, Restrict Content Pro team
* @license GPL2+
* @since 3.0
*/
namespace RCP\Utils\Batch;
/**
*
* Adds a batch job for later processing.
*
* @since 3.0
*
* @param array $config {
*
* @type string $name Job name.
* @type string $description Job description.
* @type string $callback Callback used to process the job.
* @type array $data Any extra data needed for processing.
* }
* @return int|\WP_Error Job ID if the job was registered, WP_Error if an invalid configuration was given or the job
* otherwise failed to be added.
*/
function add_batch_job( array $config ) {
$default_args = array(
'queue' => 'rcp_core',
'status' => 'incomplete',
'date_created' => current_time( 'mysql' )
);
$config = wp_parse_args( $config, $default_args );
$error_message = sprintf( __( 'You must supply a valid queue, name, and callback in %s', 'rcp' ), __FUNCTION__ );
try {
$clean_config = array(
'queue' => 'rcp_core',
'name' => '',
'description' => '',
'callback' => '',
'total_count' => 0,
'current_count' => 0,
'step' => 0,
'status' => 'incomplete',
'data' => '',
'date_created' => current_time( 'mysql' ),
'date_completed' => null
);
foreach ( [ 'name', 'callback' ] as $key ) {
if ( empty( $config[$key] ) ) {
throw new \InvalidArgumentException( $error_message );
}
}
foreach ( $config as $key => $value ) {
switch ( $key ) {
case 'queue' :
$clean_config['queue'] = sanitize_text_field( $value );
break;
case 'name':
$clean_config['name'] = sanitize_text_field( $value );
break;
case 'description':
$clean_config['description'] = wp_kses_post( $value );
break;
case 'callback':
$clean_config['callback'] = sanitize_text_field( $value );
break;
case 'total_count' :
case 'current_count' :
case 'step' :
$clean_config[$key] = absint( $value );
break;
case 'status':
$clean_config['status'] = sanitize_key( $config['status'] );
break;
case 'data' :
$clean_config['data'] = is_array( $config['data'] ) ? maybe_serialize( $config['data'] ) : '';
break;
case 'date_created' :
$clean_config['date_created'] = sanitize_text_field( $value );
break;
}
}
$queue_query = new \RCP\Database\Queries\Queue();
$existing = $queue_query->query( array(
'name' => $clean_config['name'],
'queue' => $clean_config['queue']
) );
if ( ! empty( $existing ) ) {
throw new \InvalidArgumentException( sprintf( __( 'Unable to add job %s to the %s queue. It already exists.', 'rcp' ), $clean_config['name'], $clean_config['queue'] ) );
}
$job_id = $queue_query->add_item( array(
'queue' => $clean_config['queue'],
'name' => $clean_config['name'],
'description' => $clean_config['description'],
'callback' => $clean_config['callback'],
'total_count' => $clean_config['total_count'],
'current_count' => $clean_config['current_count'],
'step' => $clean_config['step'],
'status' => $clean_config['status'],
'data' => $clean_config['data'],
'date_created' => $clean_config['date_created'],
'date_completed' => null
) );
} catch ( \InvalidArgumentException $exception ) {
add_action( 'admin_notices', function () use ( $config, $exception ) {
echo '<div class="error"><p>' . sprintf( __( 'There was an error adding job: %s. Error message: %s If this issue persists, please contact the Restrict Content Pro support team.', 'rcp' ), $config['name'], $exception->getMessage() ) . '</p></div>';
} );
return new \WP_Error( 'invalid_job_config', sprintf( __( 'Invalid job configuration: %s', 'rcp' ), $exception->getMessage() ) );
}
if ( empty( $job_id ) ) {
return new \WP_Error( 'error_inserting_job', __( 'Failed to add job to queue', 'rcp' ) );
}
return $job_id;
}
/**
* Retrieve a single job by ID.
*
* @param int $job_id ID of the job to retrieve.
*
* @since 3.0
* @return Job|false
*/
function get_job( $job_id ) {
$queue_query = new \RCP\Database\Queries\Queue();
$job_config = $queue_query->get_item( absint( $job_id ) );
return $job_config;
}
/**
* Update a job
*
* @param int $job_id ID of the job to update.
* @param array $data Data to update.
*
* @since 3.1
* @return bool
*/
function update_job( $job_id, $data = array() ) {
$queue_query = new \RCP\Database\Queries\Queue();
// Maybe serialize the data.
if ( ! empty( $data['data'] ) ) {
$data['data'] = maybe_serialize( $data['data'] );
}
return $queue_query->update_item( $job_id, $data );
}
/**
* Get a single job by a field/value pair.
*
* @param string $field Column to search in.
* @param string $value Value of the row.
*
* @since 3.1
* @return Job|false
*/
function get_job_by( $field = '', $value = '' ) {
$queue_query = new \RCP\Database\Queries\Queue();
return $queue_query->get_item_by( $field, $value );
}
/**
* Get jobs
*
* @param array $args Query arguments to override the defaults.
*
* @since 3.0
* @return array Array of Job objects.
*/
function get_jobs( $args = array() ) {
$args = wp_parse_args( $args, array(
'queue' => 'rcp_core'
) );
$queue_query = new \RCP\Database\Queries\Queue();
return $queue_query->query( $args );
}
/**
* Deletes a job.
*
* @since 3.0
*
* @param int $job_id ID of job to delete.
*
* @return bool True if the job is deleted, false if not.
*/
function delete_batch_job( $job_id ) {
$queue_query = new \RCP\Database\Queries\Queue();
$deleted = $queue_query->delete_item( absint( $job_id ) );
return ! empty( $deleted );
}
/**
* Processes the specified batch job.
*
* @since 3.0
*
* @param int $job_id The ID job to process.
*
* @return bool|\WP_Error True if the batch was successful, WP_Error if not.
*/
function process_batch( $job_id ) {
$job = get_job( $job_id );
if ( empty( $job ) ) {
return new \WP_Error( 'invalid_job_id', __( 'Invalid job ID.', 'rcp' ) );
}
return $job->process_batch();
}
/**
* Display admin notices.
*
* @since 3.0
*
* @return void
*/
function display_admin_notice() {
// Don't show notices on batch processing page.
if ( isset( $_GET['rcp-queue'] ) ) {
return;
}
$queue = get_jobs( array(
'status' => 'incomplete'
) );
if ( ! empty( $queue ) ) {
echo '<div class="notice notice-info"><p>' . __( 'Restrict Content Pro needs to perform system maintenance. This maintenance is <strong>REQUIRED</strong>.', 'rcp' ) . '</p>';
echo '<p>' . sprintf( __( '<a href="%s">Click here</a> to learn more and start the upgrade.', 'rcp' ), esc_url( admin_url( 'admin.php?page=rcp-tools&tab=batch&rcp-queue=rcp_core' ) ) );
echo '</div>';
}
}
add_action( 'admin_notices', __NAMESPACE__ . '\display_admin_notice' );

View File

@@ -0,0 +1,151 @@
"use strict";
let table,
form,
submit_button,
progress_bar,
progress_percent,
message,
error_wrap;
let RCP_Batch = {
/**
* Listens for job submissions and initiates job processing.
*/
listen: function () {
let batch = this;
form = jQuery( '.rcp-batch-form' );
form.on( 'submit', function ( event ) {
event.preventDefault();
table = jQuery( this ).parents( '.rcp-batch-processing-job-table' );
batch.set_vars( table );
submit_button.prop( 'disabled', true ).hide();
table.find( '.spinner' ).addClass( 'is-active' ).show();
error_wrap.hide();
message.text( '' );
let data = {
action: 'rcp_process_batch',
job_id: table.find( '.rcp-batch-processing-job-id' ).val(),
rcp_batch_nonce: rcp_batch_vars.batch_nonce
};
batch.process( data, table.find( '.rcp-batch-processing-job-step' ).val(), table );
} );
if ( form.find( '#rcp-job-autostart' ).length ) {
form.trigger( 'submit' );
}
},
/**
* Set variables.
*
* @param object table
*/
set_vars: function ( table ) {
submit_button = table.find( '.button-primary' );
progress_bar = table.find( '.rcp-batch-processing-job-progress-bar span' );
progress_percent = table.find( '.rcp-batch-processing-job-percent-complete' );
message = table.find( '.rcp-batch-processing-message' );
error_wrap = table.find( '.rcp-batch-processing-errors' );
},
/**
* Process the specified job.
*
* @param {object} data Job data
* @param {int} step Step to process
* @param {object} table Table object for modifying the DOM
*/
process: function ( data, step, table ) {
let batch = this;
data.step = step;
batch.set_vars( table );
jQuery.ajax( {
dataType: 'json',
data: data,
type: 'POST',
url: ajaxurl,
success: function ( response ) {
if ( false === response.success ) {
submit_button.prop( 'disabled', false ).show();
table.find( '.spinner' ).removeClass( 'is-active' ).hide();
message.text( response.data.message + ' ' + rcp_batch_vars.i18n.job_retry );
return;
}
if ( response.data.complete ) {
progress_bar.css( 'width', response.data.percent_complete + '%' );
progress_percent.text( response.data.percent_complete );
table.find( '.spinner' ).removeClass( 'is-active' ).hide();
message.html( response.data.complete_message ).show();
if ( response.data.has_errors ) {
error_wrap.find( 'td' ).empty().html( response.data.errors );
error_wrap.show();
}
} else if ( response.data.error ) {
// TODO: show errors
console.log( 'error processing batch' );
console.log( response.data.error );
} else if ( response.data.next_step ) {
progress_bar.css( 'width', response.data.percent_complete + '%' );
progress_percent.text( response.data.percent_complete );
batch.process( data, response.data.next_step, table );
} else {
// wtf happened
console.log( response );
message.text( response.data.message + ' ' + rcp_batch_vars.i18n.job_retry );
}
},
error: function ( response ) {
console.log( new Date() + ' error' );
console.log( response );
}
} );
}
};
/**
* Loads the job listener.
*/
jQuery( document ).ready( function () {
RCP_Batch.listen();
} );

View File

@@ -0,0 +1,534 @@
<?php
/**
* Job Object
*
* @package restrict-content-pro
* @copyright Copyright (c) 2018, Restrict Content Pro team
* @license GPL2+
* @since 3.0
*/
namespace RCP\Utils\Batch;
use RCP\Base_Object;
// Exit if accessed directly
defined( 'ABSPATH' ) || exit;
/**
* Class Job
*
* @package RCP\Utils\Batch
* @since 3.0
*/
final class Job extends Base_Object {
/**
* @var int
*/
protected $id = 0;
/**
* @var string
*/
protected $queue = 'rcp_core';
/**
* @var string
*/
protected $name = '';
/**
* @var string
*/
protected $description = '';
/**
* @var string
*/
protected $callback = '';
/**
* @var int
*/
protected $total_count = 0;
/**
* @var int
*/
protected $current_count = 0;
/**
* @var int
*/
protected $step = 0;
/**
* @var string
*/
protected $status = 'incomplete';
/**
* @var array
*/
protected $data = array();
/**
* @var string
*/
protected $date_created = '';
/**
* @var string
*/
protected $date_completed = '';
/**
* @var string
*/
protected $error_key = '';
/**
* @var \RCP\Database\Queries\Queue
*/
private $queue_query;
/**
* Job Config constructor.
*
* @param object $config Job row from the database.
*
* @since 3.0
*/
public function __construct( $config = null ) {
if ( null === $config ) {
return;
}
try {
$this->validate( $config );
} catch ( \InvalidArgumentException $exception ) {
}
$config = $this->sanitize( $config );
parent::__construct( $config );
$this->error_key = 'rcp_job_' . $this->get_id() . '_errors';
$this->queue_query = new \RCP\Database\Queries\Queue();
}
/**
* Get the job ID
*
* @since 3.0
* @return int
*/
public function get_id() {
return $this->id;
}
/**
* Get the associated queue
*
* @since 3.0
* @return string
*/
public function get_queue() {
return $this->queue;
}
/**
* Get the job name
*
* @since 3.0
* @return string
*/
public function get_name() {
return $this->name;
}
/**
* Get the job description
*
* @since 3.0
* @return string
*/
public function get_description() {
return $this->description;
}
/**
* Get the callback class name
*
* @since 3.0
* @return string
*/
public function get_callback() {
return $this->callback;
}
/**
* Get the callback object.
*
* @since 3.0
* @return Abstract_Job_Callback|false Callback class on success, false on failure.
*/
public function get_callback_object() {
$class_name = $this->get_callback();
if ( class_exists( $class_name ) ) {
$object = new $class_name( $this );
if ( method_exists( $object, 'execute' ) ) {
return $object;
}
}
return false;
}
/**
* Get the total number of items to be processed
*
* @since 3.0
* @return int
*/
public function get_total_count() {
return $this->total_count;
}
/**
* Get the number of items processed so far
*
* @since 3.0
* @return int
*/
public function get_current_count() {
return $this->current_count;
}
/**
* Set the total count
*
* @param int $count
*
* @since 3.0
*/
public function set_total_count( $count = 0 ) {
$this->total_count = absint( $count );
$this->save();
}
/**
* Set the current count to a specific value
*
* @param int $count
*
* @since 3.1
*/
public function set_current_count( $count = 0 ) {
$this->current_count = $count;
$this->save();
}
/**
* Add to the current count
*
* @param int $amount Amount to add.
*
* @since 3.0
*/
public function adjust_current_count( $amount = 0 ) {
$this->current_count = $this->get_current_count() + (int) $amount;
$this->save();
}
/**
* Get the current step
*
* @since 3.0
* @return int
*/
public function get_step() {
return $this->step;
}
/**
* Set the next step
*
* @param int $step
*
* @since 3.0
*/
public function set_step( $step = 0 ) {
$this->step = absint( $step );
$this->save();
}
/**
* Get the job status
*
* @since 3.0
* @return string
*/
public function get_status() {
return $this->status;
}
/**
* Get job data
*
* @since 3.1
* @return array
*/
public function get_data() {
return ! empty( $this->data ) ? $this->data : array();
}
/**
* Get the date the job was created
*
* @since 3.1.2
* @return string
*/
public function get_date_created() {
return $this->date_created;
}
/**
* Get the date the job was completed
*
* @since 3.1.2
* @return string
*/
public function get_date_completed() {
return $this->date_completed;
}
/**
* Adds additional data to the existing data (merging the two arrays). It does not overwrite.
*
* To overwrite the existing data, use `update_job()`
* @see update_job()
*
* @param array $new_data New data to add.
*
* @since 3.1
*/
public function add_data( $new_data ) {
$existing_data = $this->get_data();
$new_data = array_merge( $existing_data, $new_data );
$this->data = $new_data;
$this->save();
}
/**
* Set the job status
*
* @since 3.0
*
* @param string $status
*/
public function set_status( $status = 'complete' ) {
$this->status = sanitize_key( $status );
if ( 'complete' === $status ) {
$this->date_completed = current_time( 'mysql' );
}
$this->save();
}
/**
* Determines whether or not the job has been completed.
*
* @since 3.0
* @return boolean True if the job is completed, false if not.
*/
public function is_completed() {
return 'complete' === $this->get_status();
}
/**
* Returns the job completion percentage.
*
* @since 3.0
* @return int
*/
public function get_percent_complete() {
$percent_complete = 0;
$total_count = $this->get_total_count();
if ( $total_count > 0 ) {
$percent_complete = (int) ( ( $this->get_current_count() / $total_count ) * 100 );
}
if ( $percent_complete > 100 ) {
$percent_complete = 100;
}
return $percent_complete;
}
/**
* Get error messages
*
* @since 3.0.2
* @return array
*/
public function get_errors() {
$errors = get_option( $this->error_key );
if ( ! is_array( $errors ) ) {
$errors = array();
}
return $errors;
}
/**
* Add an error message
*
* @param string $error
*
* @since 3.0.2
* @return void
*/
public function add_error( $error ) {
$errors = $this->get_errors();
$errors[] = $error;
update_option( $this->error_key, $errors );
}
/**
* Whether or not this job has any errors
*
* @since 3.0.2
* @return bool
*/
public function has_errors() {
$errors = $this->get_errors();
return ! empty( $errors );
}
/**
* Clear error messages
*
* @since 3.0.2
* @return void
*/
public function clear_errors() {
delete_option( $this->error_key );
}
/**
* Process the next batch of this job.
*
* @since 3.0
* @return true|\WP_Error True on success, WP_Error object on failure.
*/
public function process_batch() {
$object = $this->get_callback_object();
if ( empty( $object ) ) {
return new \WP_Error( 'invalid_job_callback', sprintf( __( 'Error processing %s - invalid callback.', 'rcp' ), $this->get_name() ) );
}
$object->execute();
return true;
}
/**
* Save the job
*
* @since 3.0
*/
public function save() {
$args = array(
'queue' => $this->get_queue(),
'name' => $this->get_name(),
'description' => $this->get_description(),
'callback' => $this->get_callback(),
'total_count' => $this->get_total_count(),
'current_count' => $this->get_current_count(),
'step' => $this->get_step(),
'status' => $this->get_status(),
'data' => maybe_serialize( $this->get_data() ),
'date_created' => $this->get_date_created(),
'date_completed' => $this->get_date_completed()
);
$updated = $this->queue_query->update_item( $this->get_id(), $args );
if ( $updated === false ) {
rcp_log( sprintf( 'There was an error saving the job in %s. Job config: %s', __METHOD__, var_export( $args, true ) ), true );
}
}
/**
* Validate the configuration to ensure all requirements are met.
*
* @param object $config
*
* @since 3.0
*/
private function validate( $config ) {
$error = __( 'You must supply a valid name, description, and JobInterface callback when registering a batch job.', 'rcp' );
if ( empty( $config ) ) {
throw new \InvalidArgumentException( $error );
}
if ( empty( $config->name ) || empty( $config->description ) ) {
throw new \InvalidArgumentException( $error );
}
if ( empty( $config->callback ) || ! class_exists( $config->callback ) ) {
throw new \InvalidArgumentException( $error );
}
$interfaces = class_implements( $config->callback );
if ( false === $interfaces || ! in_array( 'RCP\Utils\Batch\JobInterface', $interfaces, true ) ) {
throw new \InvalidArgumentException( $error );
}
}
/**
* Sanitize the configuration.
*
* @param object $config
*
* @since 3.0
* @return array
*/
private function sanitize( $config ) {
return [
'id' => ! empty( $config->id ) ? absint( $config->id ) : false,
'queue' => ! empty( $config->queue ) ? sanitize_key( $config->queue ) : 'rcp_core',
'name' => sanitize_text_field( $config->name ),
'description' => wp_kses_post( $config->description ),
'callback' => $config->callback, // already validated to be a callable class
'total_count' => ! empty( $config->total_count ) ? absint( $config->total_count ) : 0,
'current_count' => ! empty( $config->current_count ) ? absint( $config->current_count ) : 0,
'step' => ! empty( $config->step ) ? absint( $config->step ) : 0,
'status' => empty( $config->status ) ? 'incomplete' : $config->status,
'data' => ! empty( $config->data ) ? maybe_unserialize( $config->data ) : array(),
'date_created' => ! empty( $config->date_created ) ? sanitize_text_field( $config->date_created ) : current_time( 'timestamp' ),
'date_completed' => ! empty( $config->date_completed ) ? sanitize_text_field( $config->date_completed ) : ''
];
}
}

View File

@@ -0,0 +1,272 @@
<?php
/**
* class-batch-csv-export-base.php
*
* @package restrict-content-pro
* @copyright Copyright (c) 2020, Sandhills Development, LLC
* @license GPL2+
*/
namespace RCP\Batch\CSV_Exports;
use RCP\Utils\Batch\Abstract_Job_Callback;
use RCP\Utils\Batch\Job;
/**
* Class Base
*
* @package RCP\Batch\CSV_Exports
*/
abstract class Base extends Abstract_Job_Callback {
/**
* Type of export being performed (`memberships`, `payments`, etc.)
*
* @var string
*/
protected $export_type;
/**
* File the export data is stored in.
*
* @var string
*/
protected $file;
/**
* Settings from the UI form
*
* @var array
*/
protected $settings;
/**
* Base constructor.
*
* @param Job $job
*/
public function __construct( $job ) {
parent::__construct( $job );
$exporter = rcp_get_csv_exporter_by_callback( $this->get_job()->get_callback() );
$this->export_type = ! empty( $exporter['key'] ) ? $exporter['key'] : '';
$job_data = $this->get_job()->get_data();
// Set up the file path.
$this->file = ! empty( $job_data['filepath'] ) ? $job_data['filepath'] : false;
if ( empty( $this->file ) ) {
$this->file = tempnam( get_temp_dir(), 'rcp-' );
$this->get_job()->add_data( array(
'filepath' => tempnam( get_temp_dir(), 'rcp-' )
) );
}
// Get settings from the job data.
$this->settings = ! empty( $job_data['settings'] ) ? $job_data['settings'] : array();
}
/**
* Returns the column headers for the export.
*
* @return array
*/
abstract public function get_columns();
/**
* Returns the total number of records to be exported.
*
* @return int
*/
abstract public function get_total();
/**
* @return $this
*/
public function execute() {
rcp_log( sprintf( 'Batch Processing: Initiating RCP Batch Export %s. Current step: %d; current count: %d; total count: %d.', $this->export_type, $this->get_job()->get_step(), $this->get_job()->get_current_count(), $this->get_job()->get_total_count() ), true );
if ( $this->start_time === null ) {
$this->start_time = time();
}
if ( ! rcp_current_user_can_export() ) {
$this->get_job()->add_error( __( 'You do not have permission to perform this action.', 'rcp' ) );
return $this;
}
if ( $this->time_exceeded() || $this->get_job()->is_completed() ) {
return $this;
}
parent::execute();
// On the first step, clear errors, clean up the file, add headers, calculate total count.
if ( 0 === $this->get_job()->get_step() ) {
$this->get_job()->clear_errors();
$this->process_headers();
$this->get_job()->set_total_count( $this->get_total() );
}
// Get the data for this batch.
$batch = $this->get_batch();
// If we don't have any, we're done.
if ( empty( $batch ) ) {
$this->finish();
return $this;
}
// Otherwise, stash this batch and continue.
$this->process_batch( $batch );
$this->get_job()->adjust_current_count( count( $batch ) );
$current_step = $this->get_job()->get_step();
$current_step++;
$this->get_job()->set_step( $current_step );
return $this;
}
/**
* Retrieves the batch data from the database.
*
* @return array
*/
abstract public function get_batch();
/**
* @inheritDoc
*/
public function finish() {
// Set job to complete.
$this->get_job()->set_status( 'complete' );
$errors = $this->get_job()->get_errors();
rcp_log( sprintf( 'Batch CSV Export: Job complete. Errors: %s', var_export( $errors, true ) ), true );
}
/**
* Processes the CSV column headers and writes the resulting string to the file.
*
* @since 3.4
*/
protected function process_headers() {
$row_data = '';
$columns = $this->get_columns();
$i = 1;
foreach ( $columns as $column_key => $column_name ) {
$row_data .= sprintf( '"%s"', addslashes( $column_name ) );
$row_data .= $i === count( $columns ) ? '' : ',';
$i++;
}
$row_data .= "\r\n";
$this->stash_batch( $row_data, false );
}
/**
* Processes the current batch data to format each row and stash the resulting string in the file.
*
* @param array $batch Current batch of data.
*
* @since 3.4
*/
protected function process_batch( $batch ) {
$row_data = '';
$columns = $this->get_columns();
if ( empty( $batch ) || ! is_array( $batch ) ) {
return;
}
foreach ( $batch as $row ) {
$i = 1;
foreach ( $columns as $column_key => $column_name ) {
if ( array_key_exists( $column_key, $row ) ) {
$row_data .= sprintf( '"%s"', addslashes( preg_replace( "/\"/", "'", $row[ $column_key ] ) ) );
}
$row_data .= $i === count( $columns ) ? '' : ',';
$i++;
}
$row_data .= "\r\n";
}
$this->stash_batch( $row_data );
}
/**
* Retrieves the CSV file contents.
*
* @since 3.4
* @return string
*/
protected function get_file() {
$file = '';
if ( @file_exists( $this->file ) ) {
$file = @file_get_contents( $this->file );
} else {
@file_put_contents( $this->file, '' );
@chmod( $this->file, 0664 );
}
return $file;
}
/**
* Appends the formatted string data to the file and saves the new contents.
*
* @param string $batch Batch data formatted as a comma-separated string.
* @param bool $append Whether to append the new data (true) or override (false).
*
* @since 3.4
*/
protected function stash_batch( $batch, $append = true ) {
$file = $this->get_file();
if ( $append ) {
$file .= $batch;
} else {
$file = $batch;
}
@file_put_contents( $this->file, $file );
}
/**
* Message to display upon job completion.
*
* @since 3.4
* @return string
*/
public function get_complete_message() {
$download_url = wp_nonce_url( add_query_arg( array(
'rcp-action' => 'download_export_file',
'export_id' => urlencode( $this->get_job()->get_id() )
), admin_url() ), 'rcp_download_export_file' );
return '<a href="' . esc_url( $download_url ) . '">' . __( 'Download Export File', 'rcp' ) . '</a>';
}
}

View File

@@ -0,0 +1,166 @@
<?php
/**
* Export Memberships
*
* @package restrict-content-pro
* @copyright Copyright (c) 2020, Sandhills Development, LLC
* @license GPL2+
*/
namespace RCP\Batch\CSV_Exports;
use RCP_Membership;
/**
* Class Memberships
*
* @package RCP\Batch\CSV_Exports
*/
class Memberships extends Base {
/**
* @inheritDoc
*/
public function get_columns() {
$cols = array(
'id' => __( 'Membership ID', 'rcp' ),
'user_id' => __( 'User ID', 'rcp' ),
'user_login' => __( 'User Login', 'rcp' ),
'user_email' => __( 'User Email', 'rcp' ),
'first_name' => __( 'First Name', 'rcp' ),
'last_name' => __( 'Last Name', 'rcp' ),
'subscription' => __( 'Membership Level ID', 'rcp' ),
'membership_level_name' => __( 'Membership Level Name', 'rcp' ),
'subscription_key' => __( 'Subscription Key', 'rcp' ),
'created_date' => __( 'Created Date', 'rcp' ),
'expiration' => __( 'Expiration Date', 'rcp' ),
'status' => __( 'Status', 'rcp' ),
'times_billed' => __( 'Times Billed', 'rcp' ),
'discount_codes' => __( 'Discount Codes', 'rcp' ),
'gateway' => __( 'Gateway', 'rcp' ),
'gateway_customer_id' => __( 'Gateway Customer ID', 'rcp' ),
'profile_id' => __( 'Gateway Subscription ID', 'rcp' ),
'is_recurring' => __( 'Auto Renew', 'rcp' )
);
/**
* Filters the columns to export.
*
* @param array $cols
*
* @since 1.5
*/
return apply_filters( 'rcp_export_csv_cols_members', $cols );
}
/**
* Builds and returns an array of query args to use in count and get functions.
*
* @return array
*/
private function get_query_args() {
$number = $this->get_amount_per_step();
if ( ! empty( $this->settings['number'] ) && $number > $this->settings['number'] ) {
$number = $this->settings['number'];
}
$args = array(
'number' => $number,
'offset' => $this->offset
);
if ( ! empty( $this->settings['level_id'] ) ) {
$args['object_id'] = absint( $this->settings['level_id'] );
}
if ( ! empty( $this->settings['status'] ) && 'all' !== $this->settings['status'] ) {
$args['status'] = strtolower( $this->settings['status'] );
}
return $args;
}
/**
* @inheritDoc
*/
public function get_batch() {
// Bail with no results if a "Maximum Number" has been specified and we've exceeded that.
if ( ! empty( $this->settings['number'] ) && ( $this->get_amount_per_step() * $this->offset ) > $this->settings['number'] ) {
return array();
}
$memberships = rcp_get_memberships( $this->get_query_args() );
$batch = array();
foreach ( $memberships as $membership ) {
$user = get_userdata( $membership->get_user_id() );
$discounts = get_user_meta( $membership->get_user_id(), 'rcp_user_discounts', true );
if ( ! empty( $discounts ) && is_array( $discounts ) && ! $discounts instanceof \stdClass ) {
foreach ( $discounts as $key => $code ) {
if ( ! is_string( $code ) ) {
unset( $discounts[ $key ] );
}
}
$discounts = implode( ' ', $discounts );
}
$membership_data = array(
'id' => $membership->get_id(),
'user_id' => $membership->get_customer()->get_user_id(),
'user_login' => $user->user_login,
'user_email' => $user->user_email,
'first_name' => $user->first_name,
'last_name' => $user->last_name,
'subscription' => $membership->get_object_id(),
'membership_level_name' => $membership->get_membership_level_name(),
'subscription_key' => $membership->get_subscription_key(),
'created_date' => $membership->get_created_date( false ),
'expiration' => $membership->get_expiration_date( false ),
'status' => $membership->get_status(),
'times_billed' => $membership->get_times_billed(),
'discount_codes' => $discounts,
'gateway' => $membership->get_gateway(),
'gateway_customer_id' => $membership->get_gateway_customer_id(),
'profile_id' => $membership->get_gateway_subscription_id(),
'is_recurring' => $membership->is_recurring()
);
if ( has_filter( 'rcp_export_members_get_data_row' ) ) {
$membership_data = apply_filters_deprecated( 'rcp_export_members_get_data_row', array(
$membership_data,
new \RCP_Member( $user->ID )
), '3.0', 'rcp_export_memberships_get_data_row' );
}
/**
* Filters the data row.
*
* @param array $membership_data Membership data for this row.
* @param RCP_Membership $membership Membership object.
*
* @since 3.0
*/
$batch[] = apply_filters( 'rcp_export_memberships_get_data_row', $membership_data, $membership );
}
$batch = apply_filters_deprecated( 'rcp_export_get_data', array( $batch ), '3.4' );
$batch = apply_filters_deprecated( 'rcp_export_get_data_members', array( $batch ), '3.4' );
return $batch;
}
/**
* Counts the total number of expected results.
*
* @return int
*/
public function get_total() {
return rcp_count_memberships( $this->get_query_args() );
}
}

View File

@@ -0,0 +1,152 @@
<?php
/**
* class-export-payments.php
*
* @package restrict-content-pro
* @copyright Copyright (c) 2020, Ashley Gibson
* @license GPL2+
*/
namespace RCP\Batch\CSV_Exports;
use RCP_Payments;
/**
* Class Payments
*
* @package RCP\Batch\CSV_Exports
*/
class Payments extends Base {
/**
* @inheritDoc
*/
public function get_columns() {
$cols = array(
'id' => __( 'ID', 'rcp' ),
'status' => __( 'Status', 'rcp' ),
'object_type' => __( 'Purchase Type', 'rcp' ),
'object_id' => __( 'Membership Level ID', 'rcp' ),
'subscription' => __( 'Membership Level Name', 'rcp' ),
'amount' => __( 'Total Amount', 'rcp' ),
'subtotal' => __( 'Subtotal', 'rcp' ),
'credits' => __( 'Credits', 'rcp' ),
'fees' => __( 'Fees', 'rcp' ),
'discount_amount' => __( 'Discount Amount', 'rcp' ),
'discount_code' => __( 'Discount Code', 'rcp' ),
'user_id' => __( 'User ID', 'rcp' ),
'user_login' => __( 'User Login', 'rcp' ),
'user_email' => __( 'User Email', 'rcp' ),
'customer_id' => __( 'Customer ID', 'rcp' ),
'membership_id' => __( 'Membership ID', 'rcp' ),
'payment_type' => __( 'Payment Type', 'rcp' ),
'gateway' => __( 'Gateway', 'rcp' ),
'subscription_key' => __( 'Subscription Key', 'rcp' ),
'transaction_id' => __( 'Transaction ID', 'rcp' ),
'transaction_type' => __( 'Transaction Type', 'rcp' ),
'date' => __( 'Date', 'rcp' )
);
/**
* Filters the columns to export.
*
* @param array $cols
*
* @since 1.5
*/
return apply_filters( 'rcp_export_csv_cols_payments', $cols );
}
/**
* Builds and returns an array of query args to use in count and get functions.
*
* @return array
*/
private function get_query_args() {
$args = array(
'number' => $this->get_amount_per_step(),
'offset' => $this->offset
);
if ( ! empty( $this->settings['year'] ) ) {
$args['date']['year'] = absint( $this->settings['year'] );
}
if ( ! empty( $this->settings['month'] ) ) {
$args['date']['month'] = absint( $this->settings['month'] );
}
return $args;
}
/**
* @inheritDoc
*/
public function get_batch() {
$batch = array();
$rcp_db = new RCP_Payments;
$payments = $rcp_db->get_payments( $this->get_query_args() );
foreach ( $payments as $payment ) {
$user = get_userdata( $payment->user_id );
$payment_data = array(
'id' => $payment->id,
'status' => $payment->status,
'object_type' => $payment->object_type,
'object_id' => $payment->object_id,
'subscription' => $payment->subscription,
'amount' => $payment->amount,
'subtotal' => $payment->subtotal,
'credits' => $payment->credits,
'fees' => $payment->fees,
'discount_amount' => $payment->discount_amount,
'discount_code' => $payment->discount_code,
'user_id' => $payment->user_id,
'user_login' => isset( $user->user_login ) ? $user->user_login : '',
'user_email' => isset( $user->user_email ) ? $user->user_email : '',
'customer_id' => ! empty( $payment->customer_id ) ? $payment->customer_id : '',
'membership_id' => ! empty( $payment->membership_id ) ? $payment->membership_id : '',
'payment_type' => $payment->payment_type,
'gateway' => $payment->gateway,
'subscription_key' => $payment->subscription_key,
'transaction_id' => $payment->transaction_id,
'transaction_type' => ! empty( $payment->transaction_type ) ? $payment->transaction_type : '',
'date' => $payment->date
);
/**
* Filters the payment information that's exported to this row.
*
* @param array $payment_data
* @param object $payment
*/
$payment_data = apply_filters( 'rcp_export_payments_get_data_row', $payment_data, $payment );
$batch[] = $payment_data;
}
$batch = apply_filters_deprecated( 'rcp_export_get_data', array( $batch ), '3.4' );
$batch = apply_filters_deprecated( 'rcp_export_get_data_payments', array( $batch ), '3.4' );
return $batch;
}
/**
* Counts the total number of expected results.
*
* @return int
*/
public function get_total() {
$rcp_db = new RCP_Payments;
return $rcp_db->count( $this->get_query_args() );
}
}

View File

@@ -0,0 +1 @@
<?php // Silence is golden.

View File

@@ -0,0 +1,307 @@
<?php
/**
* Batch CSV Import Base Class
*
* @since 3.1
* @copyright Copyright (c) 2019, Restrict Content Pro team
* @license GPL2+
* @package restrict-content-pro
*/
use RCP\Utils\Batch\Abstract_Job_Callback;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class RCP_Batch_Callback_CSV_Import_Base
*
* @since 3.1
*/
class RCP_Batch_Callback_CSV_Import_Base extends Abstract_Job_Callback {
/**
* Current batch of rows
*
* @var array
*/
protected $rows;
/**
* Path to the file being imported.
*
* @var string
*/
protected $file;
/**
* The parsed CSV file being imported.
*
* @var array
*/
protected $csv;
/**
* Map of CSV column headers -> database fields
*
* @var array
*/
protected $field_map;
/**
* Settings from the UI form
*
* @var array
*/
protected $settings;
/**
* RCP_Batch_Callback_CSV_Import_Base constructor.
*
* @param \RCP\Utils\Batch\Job $job
*/
public function __construct( $job ) {
parent::__construct( $job );
$file = ! empty( $this->get_job()->get_data()['file_path'] ) ? $this->get_job()->get_data()['file_path'] : '';
if ( ! empty( $file ) ) {
$this->set_csv_file( $file );
}
$this->field_map = ! empty( $this->get_job()->get_data()['field_map'] ) ? $this->get_job()->get_data()['field_map'] : array();
$this->settings = ! empty( $this->get_job()->get_data()['settings'] ) ? $this->get_job()->get_data()['settings'] : array();
}
/**
* Set the CSV file path. This then also parses the CSV and populates that property.
*
* @param string $file
*/
public function set_csv_file( $file ) {
$uploads = wp_get_upload_dir();
$this->file = path_join( $uploads['basedir'], $file );
if ( empty( $this->file ) || ! file_exists( $this->file ) ) {
return;
}
$csv = array_map( 'str_getcsv', file( $this->file ) );
array_walk( $csv, function ( &$a ) use ( $csv ) {
/*
* Make sure the two arrays have the same lengths.
* If not, we trim the larger array to match the smaller one.
*/
$min = min( count( $csv[0] ), count( $a ) );
$headers = array_slice( $csv[0], 0, $min );
$values = array_slice( $a, 0, $min );
$a = array_combine( $headers, $values );
} );
array_shift( $csv );
$this->csv = $csv;
}
/**
* Get the column header names
*
* @return array
*/
public function get_columns() {
$columns = array();
if ( isset( $this->csv[0] ) && is_array( $this->csv[0] ) ) {
$columns = array_keys( $this->csv[0] );
}
return $columns;
}
/**
* Get the first row of the CSV file
*
* This is used for showing an example of what the import will look like.
*
* @return array The first row after the header.
*/
public function get_first_row() {
if ( ! is_array( $this->csv ) ) {
return array();
}
return array_map( array( $this, 'trim_preview' ), current( $this->csv ) );
}
/**
* Map the CSV column headers to database fields
*
* @param array $fields
*/
public function map_fields( $fields ) {
$this->field_map = array_map( 'sanitize_text_field', $fields );
$this->get_job()->add_data( array(
'field_map' => $this->field_map
) );
}
/**
* Trims a column value for preview
*
* @param string $string
*
* @return string
*/
protected function trim_preview( $string ) {
if ( ! is_numeric( $string ) ) {
$long = strlen( $string ) >= 30;
$string = substr( $string, 0, 30 );
$string = $long ? $string . '...' : $string;
}
return $string;
}
/**
* @inheritdoc
*/
public function execute() {
rcp_log( sprintf( 'Batch Processing: Initiating RCP_Batch_Callback_CSV_Import_Base. Current step: %d; current count: %d; total count: %d.', $this->get_job()->get_step(), $this->get_job()->get_current_count(), $this->get_job()->get_total_count() ), true );
if ( $this->start_time === null ) {
$this->start_time = time();
}
if ( $this->time_exceeded() || $this->get_job()->is_completed() ) {
return $this;
}
parent::execute();
if ( 0 === $this->get_job()->get_step() ) {
$this->get_job()->clear_errors();
}
if ( ! file_exists( $this->file ) ) {
$this->get_job()->add_error( __( 'Import file not found.', 'rcp' ) );
$this->finish();
return $this;
}
$this->rows = $this->get_batch();
if ( empty( $this->rows ) || $this->get_job()->is_completed() ) {
$this->finish();
return $this;
}
$this->process_batch();
$current_step = $this->get_job()->get_step();
$current_step++;
$this->get_job()->set_step( $current_step );
return $this;
}
/**
* Get the next batch of CSV rows.
*
* @since 3.1
* @return array
*/
private function get_batch() {
$rows = array();
$i = 1;
if ( is_array( $this->csv ) ) {
if ( empty( $this->get_job()->get_total_count() ) ) {
$this->get_job()->set_total_count( count( $this->csv ) );
rcp_log( sprintf( 'Batch CSV Import: Total count: %d.', count( $this->csv ) ), true );
}
foreach ( $this->csv as $row_number => $row ) {
// Skip all rows until we pass our offset.
if ( $row_number + 1 <= $this->offset ) {
continue;
}
// Bail if we're done with this batch.
if ( $i > $this->size ) {
break;
}
/*
* The first real row number will be `0`, which isn't user friendly for most people. So we add `+2` to
* achieve the following:
*
* 1. First, start at `1` instead of `0`.
* 2. Account for the header, which should be row #1. The first actual value starts at #2.
*
* This just improves row number display during error logging.
*/
$adjusted_row_number = $row_number + 2;
$rows[ $adjusted_row_number ] = $row;
$i ++;
}
}
rcp_log( sprintf( 'Batch CSV Import: %d results found in query for LIMIT %d OFFSET %d.', count( $rows ), $this->size, $this->offset ) );
return $rows;
}
/**
* Overwrite this.
*/
public function process_batch() {
global $rcp_options;
// Disable debug mode.
$rcp_options['debug_mode'] = false;
}
/**
* Complete the import
* - Set status to "complete".
* - Delete the CSV file.
*
* @return void
*/
public function finish() {
// Set job to complete.
$this->get_job()->set_status( 'complete' );
$errors = $this->get_job()->get_errors();
rcp_log( sprintf( 'Batch CSV Import: Job complete. Errors: %s', var_export( $errors, true ) ), true );
// Delete the uploaded file.
wp_delete_file( $this->file );
}
}

View File

@@ -0,0 +1,402 @@
<?php
/**
* Import Memberships
*
* @since 3.1
* @copyright Copyright (c) 2019, Restrict Content Pro team
* @license GPL2+
* @package restrict-content-pro
*/
use RCP\Membership_Level;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class RCP_Batch_Callback_Import_Memberships
*
* @since 3.1
*/
class RCP_Batch_Callback_Import_Memberships extends RCP_Batch_Callback_CSV_Import_Base {
/**
* Process batch
*/
public function process_batch() {
parent::process_batch();
// Maybe disable notification emails.
if ( ! empty( $this->settings['disable_notification_emails'] ) ) {
remove_action( 'rcp_membership_post_activate', 'rcp_email_on_membership_activation', 10 );
remove_action( 'rcp_membership_post_cancel', 'rcp_email_on_membership_cancellation', 10 );
remove_action( 'rcp_transition_membership_status_expired', 'rcp_email_on_membership_expiration', 10 );
}
$processed = [];
if ( empty( $this->rows ) || $this->time_exceeded() ) {
$this->get_job()->adjust_current_count( count( $processed ) );
return;
}
foreach ( $this->rows as $row_number => $row ) {
/**
* Parse user account.
*/
$user = $this->get_user( $row );
if ( is_wp_error( $user ) ) {
$this->get_job()->add_error( sprintf( __( 'Skipping row #%d. Error code: %s; Error message: %s', 'rcp' ), $row_number, $user->get_error_code(), $user->get_error_message() ) );
continue;
}
/**
* Retrieve or create customer record.
*/
$customer = rcp_get_customer_by_user_id( $user->ID );
if ( empty( $customer ) ) {
$customer_id = rcp_add_customer( array(
'user_id' => absint( $user->ID )
) );
if ( ! empty( $customer_id ) ) {
$customer = rcp_get_customer( $customer_id );
}
}
if ( empty( $customer ) ) {
$this->get_job()->add_error( sprintf( __( 'Skipping row #%d. Error creating or retrieving customer record for user #%d.', 'rcp' ), $row_number, $user->ID ) );
continue;
}
/**
* Gather membership information.
*/
$membership_data = $this->get_membership_data( $row );
if ( is_wp_error( $membership_data ) ) {
$this->get_job()->add_error( sprintf( __( 'Skipping row #%d (user ID #%d). Error code: %s; Error message: %s', 'rcp' ), $row_number, $user->ID, $membership_data->get_error_code(), $membership_data->get_error_message() ) );
continue;
}
$membership_data['customer_id'] = $customer->get_id();
$existing_membership = rcp_get_customer_single_membership( $customer->get_id() );
$new = false;
/*
* Add a new membership if:
* - Multiple memberships is enabled and they've opted to add new records; or
* - The customer doesn't have any membership records yet; or
* - The customer does have an existing membership, but it's for a different level.
*/
if (
( rcp_multiple_memberships_enabled() && ! empty( $this->settings['existing_customers'] ) && 'new' == $this->settings['existing_customers'] )
||
empty( $existing_membership )
||
( ! empty( $existing_membership ) && $existing_membership->get_object_id() != $membership_data['object_id'] )
) {
$new = true;
}
if ( $new ) {
/**
* Insert new membership records.
*/
$membership_id = $customer->add_membership( $membership_data );
if ( empty( $membership_id ) ) {
$this->get_job()->add_error( sprintf( __( 'Error creating membership record for row #%d (user ID #%d).', 'rcp' ), $row_number, $user->ID ) );
continue;
}
} else {
/**
* Update existing membership.
*/
$membership_id = $existing_membership->get_id();
$updated = $existing_membership->update( $membership_data );
if ( empty( $updated ) ) {
$this->get_job()->add_error( sprintf( __( 'Error updating membership record #%d for row #%d.', 'rcp' ), $existing_membership->get_id(), $row_number ) );
continue;
}
}
$new_membership = rcp_get_membership( $membership_id );
/**
* Deprecated action hook from old CSV User Import add-on.
*/
do_action_deprecated( 'rcp_user_import_user_added', array(
$user->ID,
$user,
$new_membership->get_object_id(),
$new_membership->get_status(),
$new_membership->get_expiration_date( false ),
$row
), '3.1.2', 'rcp_csv_import_membership_processed' );
/**
* Triggers after a membership record has been imported / updated.
*
* @param RCP_Membership $new_membership The membership that was imported or updated.
* @param WP_User $user User object associated with the new membership.
* @param array $row Entire CSV row for this membership.
*/
do_action( 'rcp_csv_import_membership_processed', $new_membership, $user, $row );
$processed[] = $row_number;
unset( $this->rows[$row_number] );
}
$this->get_job()->adjust_current_count( count( $processed ) );
if ( $this->get_job()->is_completed() ) {
$this->finish();
}
}
/**
* Parse existing user from the row or create a new one.
*
* @param array $row
*
* @since 3.1
* @return WP_User|WP_Error
*/
private function get_user( $row ) {
$user = false;
$user_login = $email = $first_name = $last_name = $password = '';
// Get user by email.
if ( ! empty( $this->field_map['user_email'] ) && ! empty( $row[$this->field_map['user_email']] ) ) {
$email = $row[$this->field_map['user_email']];
$user = get_user_by( 'email', $email );
}
// Get user by login.
if ( empty( $user ) && ! empty( $this->field_map['user_login'] ) && ! empty( $row[$this->field_map['user_login']] ) ) {
$user_login = $row[$this->field_map['user_login']];
$user = get_user_by( 'login', $user_login );
}
// First name.
if ( ! empty( $this->field_map['first_name'] ) && ! empty( $row[$this->field_map['first_name']] ) ) {
$first_name = $row[$this->field_map['first_name']];
}
// Last name.
if ( ! empty( $this->field_map['last_name'] ) && ! empty( $row[$this->field_map['last_name']] ) ) {
$last_name = $row[$this->field_map['last_name']];
}
// Password
if ( ! empty( $this->field_map['user_password'] ) && ! empty( $row[$this->field_map['user_password']] ) ) {
$password = $row[$this->field_map['user_password']];
} else {
$password = '';
}
if ( ! empty( $user ) ) {
/**
* Check to see if we have a data mismatch.
* For example: if the email in the CSV file doesn't match the email we have for the fetched user.
*/
if ( ! empty( $email ) && $email != $user->user_email ) {
return new WP_Error( 'email_mismatch', sprintf( __( 'Email address provided in the CSV does not match the user email on record for user ID #%d.', 'rcp' ), $user->ID ) );
}
if ( ! empty( $user_login ) && $user_login != $user->user_login ) {
return new WP_Error( 'user_login_mismatch', sprintf( __( 'User login provided in the CSV does not match the user login on record for user ID #%d.', 'rcp' ), $user->ID ) );
}
/**
* Update existing account with new data.
*/
$data_to_update = array();
if ( ! empty( $password ) ) {
$data_to_update['user_pass'] = $password;
}
if ( ! empty( $first_name ) ) {
$data_to_update['first_name'] = sanitize_text_field( $first_name );
}
if ( ! empty( $last_name ) ) {
$data_to_update['last_name'] = sanitize_text_field( $last_name );
}
if ( ! empty( $data_to_update ) ) {
$data_to_update['ID'] = $user->ID;
// Don't update the password of the current user.
if ( $user->ID == get_current_user_id() && isset( $data_to_update['user_pass'] ) ) {
unset( $data_to_update['user_pass'] );
}
wp_update_user( $data_to_update );
}
} else {
/**
* Create new account.
*/
$user_data = array(
'user_login' => ! empty( $user_login ) ? sanitize_text_field( $user_login ) : sanitize_text_field( $email ),
'user_email' => sanitize_text_field( $email ),
'first_name' => sanitize_text_field( $first_name ),
'last_name' => sanitize_text_field( $last_name ),
'user_pass' => ! empty( $password ) ? $password : wp_generate_password( 24 )
);
$user_id = wp_insert_user( $user_data );
if ( is_wp_error( $user_id ) ) {
return $user_id;
}
if ( ! empty( $user_id ) && ! is_wp_error( $user_id ) ) {
$user = get_user_by( 'ID', $user_id );
if ( ! empty( $this->settings['send_set_password_emails'] ) ) {
wp_new_user_notification( $user->ID, null, 'user' );
}
}
}
if ( empty( $user ) ) {
$user = new WP_Error( 'user_error', __( 'Error creating or retrieving user account.', 'rcp' ) );
}
return $user;
}
/**
* Get membership data from the row
*
* @param array $row
*
* @return array|WP_Error
*/
private function get_membership_data( $row ) {
$data = array();
// Membership level.
if ( ! empty( $this->field_map['membership_level_name'] ) && ! empty( $row[$this->field_map['membership_level_name']] ) ) {
$membership_level = rcp_get_membership_level_by( 'name', $row[$this->field_map['membership_level_name']] );
if ( $membership_level instanceof Membership_Level ) {
$data['object_id'] = $membership_level->get_id();
}
} elseif ( ! empty( $this->settings['object_id'] ) ) {
// Get value from settings UI.
$data['object_id'] = absint( $this->settings['object_id'] );
}
if ( empty( $data['object_id'] ) ) {
return new WP_Error( 'missing_membership_level', __( 'Unable to determine membership level.', 'rcp' ) );
}
// Membership status.
if ( ! empty( $this->field_map['status'] ) && ! empty( $row[$this->field_map['status']] ) ) {
$status = sanitize_text_field( strtolower( $row[$this->field_map['status']] ) );
if ( in_array( $status, array( 'active', 'cancelled', 'expired', 'pending' ) ) ) {
$data['status'] = $status;
}
} elseif ( ! empty( $this->settings['status'] ) ) {
// Get value from settings UI.
$data['status'] = sanitize_text_field( $this->settings['status'] );
}
// Created Date
if ( ! empty( $this->field_map['created_date'] ) && ! empty( $row[$this->field_map['created_date']] ) ) {
$data['created_date'] = date( 'Y-m-d H:i:s', strtotime( $row[$this->field_map['created_date']], current_time( 'timestamp' ) ) );
}
// Expiration date.
$expiration_date = $this->settings['expiration_date'];
if ( ! empty( $this->field_map['expiration_date'] ) && ! empty( $row[$this->field_map['expiration_date']] ) ) {
if ( 'none' == strtolower( $row[$this->field_map['expiration_date']] ) ) {
$expiration_date = 'none';
} else {
$expiration_date = $row[$this->field_map['expiration_date']];
}
}
if ( ! empty( $expiration_date ) && 'none' !== strtolower( $expiration_date ) ) {
$data['expiration_date'] = date( 'Y-m-d 23:59:59', strtotime( $expiration_date, current_time( 'timestamp' ) ) );
} elseif ( 'none' === strtolower( $expiration_date ) ) {
$data['expiration_date'] = 'none';
}
// Auto Renew
if ( ! empty( $this->field_map['auto_renew'] ) && ! empty( $row[$this->field_map['auto_renew']] ) ) {
$data['auto_renew'] = 1;
} else {
$data['auto_renew'] = 0;
}
// Times Billed
if ( ! empty( $this->field_map['times_billed'] ) && ! empty( $row[$this->field_map['times_billed']] ) ) {
$data['times_billed'] = absint( $row[$this->field_map['times_billed']] );
}
// Gateway
if ( ! empty( $this->field_map['gateway'] ) && ! empty( $row[$this->field_map['gateway']] ) ) {
$gateway_slug = strtolower( $row[$this->field_map['gateway']] );
if ( array_key_exists( $gateway_slug, rcp_get_payment_gateways() ) ) {
$data['gateway'] = sanitize_text_field( $gateway_slug );
}
}
// We can loop through all the rest of the data.
$fields = array(
'gateway_customer_id',
'gateway_subscription_id',
'subscription_key'
);
foreach ( $fields as $field ) {
if ( ! empty( $this->field_map[$field] ) && ! empty( $row[$this->field_map[$field]] ) ) {
$data[$field] = sanitize_text_field( $row[$this->field_map[$field]] );
}
}
// If gateway value doesn't exist, but we have a gateway_customer_id or gateway_subscription_id, then attempt to guess it.
if ( empty( $data['gateway'] ) && ( ! empty( $data['gateway_customer_id'] ) || ! empty( $data['gateway_subscription_id'] ) ) ) {
$data['gateway'] = rcp_get_gateway_slug_from_gateway_ids( $data );
}
return $data;
}
}

View File

@@ -0,0 +1,247 @@
<?php
/**
* Import Payments
*
* @since 3.4
* @copyright Copyright (c) 2020, Restrict Content Pro team
* @license GPL2+
* @package restrict-content-pro
*/
use RCP\Membership_Level;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Class RCP_Batch_Callback_Import_Payments
*
* @since 3.4
*/
class RCP_Batch_Callback_Import_Payments extends RCP_Batch_Callback_CSV_Import_Base {
/**
* Process batch
*
* @since 3.4
*/
public function process_batch() {
parent::process_batch();
// Disable notification emails.
remove_action( 'rcp_update_payment_status_complete', 'rcp_email_payment_received', 100 );
remove_action( 'rcp_process_manual_signup', 'rcp_email_admin_on_manual_payment', 10 );
$processed = [];
if ( empty( $this->rows ) || $this->time_exceeded() ) {
$this->get_job()->adjust_current_count( count( $processed ) );
return;
}
$payments = new RCP_Payments();
foreach ( $this->rows as $row_number => $row ) {
/**
* Parse user account.
*/
$user = $this->get_user( $row );
if ( is_wp_error( $user ) ) {
$this->get_job()->add_error( sprintf( __( 'Skipping row #%d. Error code: %s; Error message: %s', 'rcp' ), $row_number, $user->get_error_code(), $user->get_error_message() ) );
continue;
}
/**
* Gather payment information.
*/
$payment_data = $this->get_payment_data( $row );
if ( is_wp_error( $payment_data ) ) {
$this->get_job()->add_error( sprintf( __( 'Skipping row #%d. Error code: %s; Error message: %s', 'rcp' ), $row_number, $payment_data->get_error_code(), $payment_data->get_error_message() ) );
continue;
}
// Add user ID.
$payment_data['user_id'] = absint( $user->ID );
// If we don't have a customer record at this point, make one.
if ( empty( $payment_data['customer_id'] ) ) {
$customer = rcp_get_customer_by_user_id( $user->ID );
if ( empty( $customer ) ) {
$customer_id = rcp_add_customer( array(
'user_id' => absint( $user->ID )
) );
if ( ! empty( $customer_id ) ) {
$customer = rcp_get_customer( $customer_id );
}
}
if ( empty( $customer ) ) {
$this->get_job()->add_error( sprintf( __( 'Skipping row #%d. Error creating or retrieving customer record for user #%d.', 'rcp' ), $row_number, $user->ID ) );
continue;
} else {
$payment_data['customer_id'] = $customer->get_id();
}
}
$result = $payments->insert( $payment_data );
if ( empty( $result ) ) {
$this->get_job()->add_error( sprintf( __( 'Skipping row #%d. Error creating payment record.', 'rcp' ), $row_number ) );
continue;
}
$processed[] = $row_number;
unset( $this->rows[ $row_number ] );
}
$this->get_job()->adjust_current_count( count( $processed ) );
if ( $this->get_job()->is_completed() ) {
$this->finish();
}
}
/**
* Parse existing user from the row or create a new one.
*
* @param array $row
*
* @since 3.4
* @return WP_User|WP_Error
*/
private function get_user( $row ) {
$user = false;
// Get user by email.
if ( ! empty( $this->field_map['user_email'] ) && ! empty( $row[ $this->field_map['user_email'] ] ) ) {
$email = $row[ $this->field_map['user_email'] ];
$user = get_user_by( 'email', $email );
}
// Get user by login.
if ( empty( $user ) && ! empty( $this->field_map['user_login'] ) && ! empty( $row[ $this->field_map['user_login'] ] ) ) {
$user_login = $row[ $this->field_map['user_login'] ];
$user = get_user_by( 'login', $user_login );
}
if ( empty( $user ) ) {
$user = new WP_Error( 'user_error', __( 'Error retrieving user account.', 'rcp' ) );
}
return $user;
}
/**
* Get payment data from the row
*
* @param array $row
*
* @since 3.4
* @return array|WP_Error
*/
private function get_payment_data( $row ) {
$data = array();
// Date.
if ( ! empty( $this->field_map['date'] ) && ! empty( $row[ $this->field_map['date'] ] ) ) {
$date = $row[ $this->field_map['membership_level_name'] ];
// Convert it to our desired format.
$data['date'] = date( 'Y-m-d H:i:s', strtotime( $date, current_time( 'timestamp' ) ) );
}
// Membership & object ID
if ( ! empty( $this->field_map['membership_id'] ) && ! empty( $row[ $this->field_map['membership_id'] ] ) ) {
$membership = rcp_get_membership( absint( $row[ $this->field_map['membership_id'] ] ) );
if ( ! empty( $membership ) ) {
$data['membership_id'] = $membership->get_id();
$data['customer_id'] = $membership->get_customer_id();
$data['object_id'] = $membership->get_object_id();
$data['subscription'] = sanitize_text_field( $membership->get_membership_level_name() );
}
}
// Get object ID from explicit value.
if ( empty( $data['object_id'] ) && ! empty( $this->field_map['object_id'] ) && ! empty( $row[ $this->field_map['object_id'] ] ) ) {
$membership_level = rcp_get_membership_level( $row[ $this->field_map['object_id'] ] );
if ( $membership_level instanceof Membership_Level ) {
$data['object_id'] = $membership_level->get_id();
$data['subscription'] = sanitize_text_field( $membership_level->get_name() );
}
}
// Get object ID from membership level name.
if ( empty( $data['subscription'] ) && ! empty( $this->field_map['subscription'] ) && ! empty( $row[ $this->field_map['subscription'] ] ) ) {
$membership_level = rcp_get_membership_level_by( 'name', $row[ $this->field_map['subscription'] ] );
if ( $membership_level instanceof Membership_Level ) {
$data['object_id'] = $membership_level->get_id();
$data['subscription'] = sanitize_text_field( $membership_level->get_name() );
} else {
$data['subscription'] = sanitize_text_field( $row[ $this->field_map['subscription'] ] );
}
}
// Gateway
if ( ! empty( $this->field_map['gateway'] ) && ! empty( $row[ $this->field_map['gateway'] ] ) ) {
$gateway_slug = strtolower( $row[ $this->field_map['gateway'] ] );
if ( array_key_exists( $gateway_slug, rcp_get_payment_gateways() ) ) {
$data['gateway'] = sanitize_text_field( $gateway_slug );
}
}
// Transaction Type
if ( ! empty( $this->field_map['transaction_type'] ) && ! empty( $row[ $this->field_map['transaction_type'] ] ) ) {
$transaction_type = strtolower( $row[ $this->field_map['transaction_type'] ] );
$all_types = array(
'new',
'renewal',
'upgrade',
'downgrade'
);
if ( in_array( $transaction_type, $all_types ) ) {
$data['transaction_type'] = sanitize_text_field( $transaction_type );
}
}
// We can loop through all the rest of the data.
$fields = array(
'amount',
'subtotal',
'credits',
'fees',
'discount_amount',
'discount_code',
'transaction_id'
);
foreach ( $fields as $field ) {
if ( ! empty( $this->field_map[ $field ] ) && ! empty( $row[ $this->field_map[ $field ] ] ) ) {
$data[ $field ] = sanitize_text_field( $row[ $this->field_map[ $field ] ] );
}
}
return $data;
}
}

View File

@@ -0,0 +1 @@
<?php // Silence is golden.

View File

@@ -0,0 +1 @@
<?php // Silence is golden.

View File

@@ -0,0 +1,39 @@
<?php
/**
* Job Callback Interface
*
* @package restrict-content-pro
* @copyright Copyright (c) 2018, Restrict Content Pro team
* @license GPL2+
*/
namespace RCP\Utils\Batch;
/**
* Interface Job_Callback_Interface
*
* @package RCP\Utils\Batch
* @since 3.0
*/
interface Job_Callback_Interface {
/**
* Execute the job
*
* @access public
* @since 3.0
* @return mixed
*/
public function execute();
/**
* Runs any tasks required to finish a job.
*
* @access public
* @since 3.0
* @return mixed|void
*/
public function finish();
}

View File

@@ -0,0 +1,535 @@
<?php
/**
* Migrate Memberships
*
* @package restrict-content-pro
* @copyright Copyright (c) 2018, Restrict Content Pro team
* @license GPL2+
* @since 3.0
*/
use RCP\Membership_Level;
use RCP\Utils\Batch\Abstract_Job_Callback;
/**
* Class Migrate_Memberships_v3
*
* Migrates membership data to the custom memberships table.
*
* @package RCP\Utils\Batch
* @since 3.0
*/
final class RCP_Batch_Callback_Migrate_Memberships_v3 extends Abstract_Job_Callback {
/**
* @var array $memberships The membership data from usermeta
*/
private $memberships;
/**
* @var bool Whether or not RCP is network activated and using global tables.
*/
private $network_active = false;
/**
* @inheritdoc
*/
public function execute() {
rcp_log( sprintf( 'Batch Member Migration: Initiating RCP_Batch_Callback_Migrate_Memberships_v3. Current step: %d; current count: %d; total count: %d.', $this->get_job()->get_step(), $this->get_job()->get_current_count(), $this->get_job()->get_total_count() ), true );
if ( $this->start_time === null ) {
$this->start_time = time();
}
if ( $this->time_exceeded() || $this->get_job()->is_completed() ) {
return $this;
}
parent::execute();
if ( 0 === $this->get_job()->get_step() ) {
$this->get_job()->clear_errors();
}
if (
is_multisite() &&
is_plugin_active_for_network( plugin_basename( RCP_PLUGIN_FILE ) ) &&
( ! defined( 'RCP_NETWORK_SEPARATE_SITES' ) || ! RCP_NETWORK_SEPARATE_SITES )
) {
$this->network_active = true;
}
$this->disable_membership_actions();
$this->memberships = $this->get_batch();
if ( empty( $this->memberships ) || $this->get_job()->is_completed() ) {
$this->finish();
return $this;
}
$this->process_batch();
$current_step = $this->get_job()->get_step();
$current_step++;
$this->get_job()->set_step( $current_step );
return $this;
}
/**
* @inheritdoc
*/
public function finish() {
// Update membership counts.
rcp_check_member_counts();
// Set job to complete.
$this->get_job()->set_status( 'complete' );
$errors = $this->get_job()->get_errors();
rcp_log( sprintf( 'Batch Member Migration: Job complete. Errors: %s', var_export( $errors, true ) ), true );
}
/**
* Disable all membership-related hooks during the migration.
*
* @since 3.0
* @return void
*/
public function disable_membership_actions() {
// Disable activation sequence, as memberships were already activated.
remove_action( 'rcp_new_membership_added', 'rcp_activate_membership_on_insert', 10 );
// Prevent status-related emails from being sent.
remove_action( 'rcp_membership_post_activate', 'rcp_email_on_membership_activation', 10 );
remove_action( 'rcp_membership_post_cancel', 'rcp_email_on_membership_cancellation', 10 );
remove_action( 'rcp_transition_membership_status_expired', 'rcp_email_on_membership_expiration', 10 );
}
/**
* Count the total number of results and save the value.
*
* @since 3.1
* @return int
*/
public function get_total_count() {
global $wpdb;
$usermeta_table = $wpdb->usermeta;
$total_count = $this->get_job()->get_total_count();
if ( empty( $total_count ) ) {
if ( is_multisite() && ! $this->network_active ) {
$blog_id = get_current_blog_id();
$capabilities_key = 1 == $blog_id ? $wpdb->base_prefix . 'capabilities' : $wpdb->base_prefix . $blog_id . '_capabilities';
$total_count = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$usermeta_table} m1, {$usermeta_table} m2 WHERE m1.user_id = m2.user_id AND m1.meta_key = 'rcp_status' AND m2.meta_key = %s", $capabilities_key ) );
} else {
$total_count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$usermeta_table} WHERE meta_key = 'rcp_status'" );
}
$this->get_job()->set_total_count( $total_count );
rcp_log( sprintf( 'Batch Member Migration: Total count: %d.', $total_count ), true );
}
return $total_count;
}
/**
* Retrieves the batch of membership records from usermeta.
*
* @since 3.0
* @return array Membership data from usermeta.
*/
private function get_batch() {
if ( ! empty( $this->memberships ) ) {
return $this->memberships;
}
global $wpdb;
$usermeta_table = $wpdb->usermeta;
$this->get_total_count();
if ( is_multisite() && ! $this->network_active ) {
$blog_id = get_current_blog_id();
$capabilities_key = 1 == $blog_id ? $wpdb->base_prefix . 'capabilities' : $wpdb->base_prefix . $blog_id . '_capabilities';
$results = $wpdb->get_results( $wpdb->prepare( "SELECT m1.* FROM {$usermeta_table} m1, {$usermeta_table} m2 WHERE m1.user_id = m2.user_id AND m1.meta_key = 'rcp_status' AND m2.meta_key = %s LIMIT %d OFFSET %d", $capabilities_key, $this->size, $this->offset ) );
} else {
$results = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$usermeta_table} WHERE meta_key = 'rcp_status' LIMIT %d OFFSET %d", $this->size, $this->offset ) );
}
rcp_log( sprintf( 'Batch Member Migration: %d results found in query for LIMIT %d OFFSET %d.', count( $results ), $this->size, $this->offset ) );
if ( empty( $results ) ) {
return [];
}
return $results;
}
/**
* Processing the batch of memberships.
*/
private function process_batch() {
global $rcp_options;
$rcp_options['debug_mode'] = false;
global $wpdb;
$rcp_payments = new \RCP_Payments();
$processed = [];
if ( empty( $this->memberships ) || $this->time_exceeded() ) {
$this->get_job()->adjust_current_count( count( $processed ) );
return;
}
foreach ( $this->memberships as $key => $membership ) {
$meta_items = $wpdb->get_results( $wpdb->prepare( "SELECT meta_key, meta_value FROM {$wpdb->usermeta} WHERE user_id = %d", $membership->user_id ) );
$user_meta = array();
foreach ( $meta_items as $meta_item ) {
$user_meta[$meta_item->meta_key] = maybe_unserialize( $meta_item->meta_value );
}
$user = get_userdata( $membership->user_id );
$membership_level_id = 0;
$pending_payment = false;
if ( empty( $user ) ) {
$this->get_job()->add_error( sprintf( __( 'Skipped user #%d - unable to locate user account. Possible orphaned user meta.', 'rcp' ), $membership->user_id ) );
continue;
}
if ( ! empty( $user_meta['rcp_subscription_level'] ) ) {
$membership_level_id = $user_meta['rcp_subscription_level'];
} elseif ( ! empty( $user_meta['rcp_pending_payment_id'] ) ) {
$pending_payment = $rcp_payments->get_payment( $user_meta['rcp_pending_payment_id'] );
$membership_level_id = ! empty( $pending_payment->object_id ) ? $pending_payment->object_id : 0;
}
if ( empty( $membership_level_id ) ) {
$this->get_job()->add_error( sprintf( __( 'Skipped user #%d - unable to determine membership level.', 'rcp' ), $user->ID ) );
continue;
}
$membership_level = rcp_get_membership_level( $membership_level_id );
if ( ! $membership_level instanceof Membership_Level ) {
$this->get_job()->add_error( sprintf( __( 'Skipped user #%d - unable to get membership level details for ID #%d.', 'rcp' ), $user->ID, $membership_level_id ) );
continue;
}
/**
* First retrieve or create a new customer record.
*/
// Check email verification status.
if ( ! empty( $user_meta['rcp_pending_email_verification'] ) ) {
// Pending verification.
$email_verification = 'pending';
} elseif ( ! empty( $user_meta['rcp_email_verified'] ) ) {
// Already verified.
$email_verification = 'verified';
} else {
// Verification not required.
$email_verification = 'none';
}
$existing_customer = rcp_get_customer_by_user_id( $user->ID );
if ( ! $existing_customer instanceof RCP_Customer ) {
// Create a new customer.
$customer_data = array(
'user_id' => $user->ID,
'date_registered' => $user->user_registered,
'email_verification' => $email_verification,
'last_login' => '',
'ips' => '',
'notes' => ''
);
$customer_id = rcp_add_customer( $customer_data );
$customer = rcp_get_customer( $customer_id );
} else {
// Update customer if their email verification status has changed.
if ( $email_verification != $existing_customer->get_email_verification_status() ) {
$existing_customer->update( array(
'email_verification' => $email_verification
) );
}
$customer = $existing_customer;
}
if ( ! isset( $customer ) || ! $customer instanceof RCP_Customer ) {
$this->get_job()->add_error( sprintf( __( 'Error inserting customer record for user #%d.', 'rcp' ), $user->ID ) );
continue;
}
/**
* Disable all the customer's memberships. This ensures we don't wind up with any duplicates if
* this migration is run more than once.
*/
$customer->disable_memberships();
/**
* Update all this user's payments to add the new customer ID.
*/
$wpdb->update(
rcp_get_payments_db_name(),
array( 'customer_id' => absint( $customer->get_id() ) ),
array( 'user_id' => $user->ID ),
array( '%d' ),
array( '%d' )
);
/**
* Get the first payment of this membership, if it exists.
*/
$first_membership_payment = $rcp_payments->get_payments( array(
'user_id' => $membership->user_id,
'number' => 1,
'order' => 'ASC',
'subscription' => $membership_level->get_name(),
) );
if ( ! empty( $first_membership_payment ) ) {
$first_membership_payment = reset( $first_membership_payment );
}
$last_membership_payment = $rcp_payments->get_payments( array(
'user_id' => $membership->user_id,
'number' => 1,
'order' => 'DESC',
'subscription' => $membership_level->get_name()
) );
if ( ! empty( $last_membership_payment ) ) {
$last_membership_payment = reset( $last_membership_payment );
}
$times_billed = $rcp_payments->count( [
'user_id' => $membership->user_id,
'status' => 'complete',
'object_id' => $membership_level_id
] );
$gateway_info = $this->get_gateway( $user_meta, $last_membership_payment );
if ( $pending_payment ) {
$expiration_date = rcp_calculate_subscription_expiration( $membership_level_id );
} else {
$expiration_date = ! empty( $user_meta['rcp_expiration'] ) ? $user_meta['rcp_expiration'] : 'none';
}
if ( empty( $expiration_date ) || strtolower( $expiration_date ) === 'none' ) {
$expiration_date = null;
}
if ( ! empty( $expiration_date ) ) {
$date = new \DateTime( $expiration_date, new \DateTimeZone( 'UTC' ) );
$expiration_date = $date->format( 'Y-m-d H:i:s' );
}
// Determine the user's status.
$status = 'free' == $user_meta['rcp_status'] ? 'active' : $user_meta['rcp_status'];
$allowed_statuses = array( 'active', 'expired', 'cancelled', 'pending' );
if ( ! in_array( $status, $allowed_statuses ) ) {
if ( strtotime( 'NOW', current_time( 'timestamp' ) ) > strtotime( $expiration_date, current_time( 'timestamp' ) ) ) {
$status = 'expired';
} else {
$status = 'active';
}
}
// Explicitly set amounts to `0.00` if empty.
$initial_amount = ! empty( $first_membership_payment->amount ) ? $first_membership_payment->amount : ( $membership_level->get_price() + $membership_level->get_fee() );
if ( empty( $initial_amount ) ) {
$initial_amount = 0.00;
}
$recurring_amount = $membership_level->get_price();
if ( empty( $recurring_amount ) ) {
$recurring_amount = 0.00;
}
// Determine the user's join date.
$join_date_meta_key = 'rcp_joined_date_' . $membership_level_id;
$join_date = ! empty( $user_meta[$join_date_meta_key] ) ? $user_meta[$join_date_meta_key] : false;
if ( empty( $join_date ) && ! empty( $first_membership_payment->date ) ) {
$join_date = $first_membership_payment->date;
} elseif ( empty( $join_date ) ) {
$join_date = current_time( 'mysql' );
}
// See if we have a renewal date.
$renewal_date_meta_key = 'rcp_renewed_date_' . $membership_level_id;
$renewal_date = ! empty( $user_meta[$renewal_date_meta_key] ) ? $user_meta[$renewal_date_meta_key] : '';
// Get the subscription key.
if ( $pending_payment ) {
$subscription_key = $pending_payment->subscription_key;
} elseif ( ! empty( $user_meta['rcp_subscription_key'] ) ) {
$subscription_key = $user_meta['rcp_subscription_key'];
} else {
$subscription_key = '';
}
$data = array(
'customer_id' => $customer->get_id(),
'user_id' => $user->ID,
'object_id' => $membership_level_id,
'object_type' => 'membership',
'initial_amount' => $initial_amount,
'recurring_amount' => $recurring_amount,
'created_date' => $join_date,
'trial_end_date' => ! empty( $user_meta['rcp_is_trialing'] ) ? $expiration_date : '',
'renewal_date' => $renewal_date,
'cancellation_date' => '',
'expiration_date' => $expiration_date,
'auto_renew' => ( ! empty( $user_meta['rcp_recurring'] ) && 'no' != $user_meta['rcp_recurring'] ) ? 1 : 0,
'times_billed' => $times_billed,
'maximum_renewals' => 0,
'status' => $status,
'gateway' => $gateway_info['gateway'],
'gateway_customer_id' => $gateway_info['gateway_customer_id'],
'gateway_subscription_id' => $gateway_info['gateway_subscription_id'],
'signup_method' => ! empty( $user_meta['rcp_signup_method'] ) ? $user_meta['rcp_signup_method'] : 'live',
'subscription_key' => $subscription_key,
'notes' => ! empty( $user_meta['rcp_notes'] ) ? $user_meta['rcp_notes'] : '',
);
$membership_id = rcp_add_membership( $data );
if ( empty( $membership_id ) ) {
$this->get_job()->add_error( sprintf( __( 'Error inserting membership record for user #%d.', 'rcp' ), $user->ID ) );
continue;
}
/**
* Update all this user's payments to add this membership ID.
*/
$wpdb->update(
rcp_get_payments_db_name(),
array( 'membership_id' => absint( $membership_id ) ),
array(
'user_id' => $membership->user_id,
'subscription' => $membership_level->get_name()
),
array( '%d' ),
array( '%d', '%s' )
);
$processed[] = $membership_id;
unset( $this->memberships[$key] );
}
$this->get_job()->adjust_current_count( count( $processed ) );
if ( $this->get_job()->is_completed() ) {
$this->finish();
}
}
/**
* Determine the payment gateway used for membership.
*
* @param array $user_meta Array of user meta.
* @param object $payment Payment object.
*
* @access private
* @since 3.0
* @return array
*/
private function get_gateway( $user_meta, $payment ) {
$profile_id = ! empty( $user_meta['rcp_payment_profile_id'] ) ? $user_meta['rcp_payment_profile_id'] : '';
$merchant_sub_id = ! empty( $user_meta['rcp_merchant_subscription_id'] ) ? $user_meta['rcp_merchant_subscription_id'] : '';
$gateway_info = array(
'gateway' => '',
'gateway_customer_id' => '',
'gateway_subscription_id' => ''
);
if ( 'free' == $user_meta['rcp_status'] ) {
return $gateway_info;
}
// Check for Stripe.
if ( false !== strpos( $profile_id, 'cus_' ) ) {
$gateway_info['gateway'] = 'stripe';
$gateway_info['gateway_customer_id'] = $profile_id;
$gateway_info['gateway_subscription_id'] = $merchant_sub_id;
return $gateway_info;
}
// Check for 2Checkout.
if ( false !== strpos( $profile_id, '2co_' ) ) {
$gateway_info['gateway'] = 'twocheckout';
$gateway_info['gateway_subscription_id'] = $profile_id;
return $gateway_info;
}
// Check for Authorize.net.
if ( false !== strpos( $profile_id, 'anet_' ) ) {
$gateway_info['gateway'] = 'authorizenet';
$gateway_info['gateway_subscription_id'] = $profile_id;
return $gateway_info;
}
// Check for Braintree.
if ( false !== strpos( $profile_id, 'bt_' ) ) {
$gateway_info['gateway'] = 'braintree';
$gateway_info['gateway_customer_id'] = $profile_id;
$gateway_info['gateway_subscription_id'] = $merchant_sub_id;
return $gateway_info;
}
// Check for PayPal.
if ( false !== strpos( $profile_id, 'I-' ) ) {
$gateway_info['gateway'] = 'paypal';
$gateway_info['gateway_subscription_id'] = $profile_id;
// Determine which PayPal gateway was used from last payment.
if ( ! empty( $payment->gateway ) ) {
$gateway_info['gateway'] = $payment->gateway;
}
return $gateway_info;
}
// Check for third party gateways.
if ( ! empty( $payment->gateway ) && 'free' != $payment->gateway ) {
$gateway_info['gateway'] = $payment->gateway;
}
$gateway_info['gateway_subscription_id'] = $profile_id;
return $gateway_info;
}
}

View File

@@ -0,0 +1 @@
<?php // Silence is golden.