Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 43 additions & 24 deletions inc/list-tables/class-product-list-table.php
Original file line number Diff line number Diff line change
Expand Up @@ -200,31 +200,10 @@ public function process_single_action(): void {
$bulk_action = $this->current_action();

if ('duplicate' === $bulk_action) {
$product = wu_request('id');
$new_product = $this->duplicate_product(wu_request('id'));

$product = wu_get_product($product);

if ( ! $product) {
WP_Ultimo()->notices->add(__('Product not found.', 'ultimate-multisite'), 'error', 'network-admin');

return;
}

$new_product = $product->duplicate();

// translators: the %s is the thing copied.
$new_name = sprintf(__('Copy of %s', 'ultimate-multisite'), $product->get_name());

$new_product->set_name($new_name);

$new_product->set_slug(sanitize_title($new_name . '-' . time()));

$new_product->set_date_created(wu_get_current_time('mysql', true));

$result = $new_product->save();

if (is_wp_error($result)) {
WP_Ultimo()->notices->add($result->get_error_message(), 'error', 'network-admin');
if (is_wp_error($new_product)) {
WP_Ultimo()->notices->add($new_product->get_error_message(), 'error', 'network-admin');

return;
}
Expand All @@ -243,6 +222,46 @@ public function process_single_action(): void {
}
}

/**
* Duplicates a product and persists the copy.
*
* @since 2.5.2
*
* @param int $product_id Product ID to duplicate.
* @return \WP_Ultimo\Models\Product|\WP_Error The duplicated product or error on failure.
*/
protected function duplicate_product($product_id) {

$product = wu_get_product(absint($product_id));

if ( ! $product) {
return new \WP_Error('wu_product_not_found', __('Product not found.', 'ultimate-multisite'));
}

$new_product = $product->duplicate();

// translators: the %s is the thing copied.
$new_name = sprintf(__('Copy of %s', 'ultimate-multisite'), $product->get_name());

$new_product->set_name($new_name);

$new_product->set_slug(sanitize_title($new_name . '-' . time()));

$new_product->set_date_created(wu_get_current_time('mysql', true));

$result = $new_product->save();

if (is_wp_error($result)) {
return $result;
}

if ( ! $result || ! $new_product->get_id()) {
return new \WP_Error('wu_product_duplicate_failed', __('Product could not be duplicated.', 'ultimate-multisite'));
}

return $new_product;
}

/**
* Returns the list of columns for this particular List Table.
*
Expand Down
46 changes: 39 additions & 7 deletions tests/WP_Ultimo/List_Tables/Product_List_Table_Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ public function test_get_views_entries_have_required_keys(): void {
public function test_column_type_returns_span_with_label(): void {

$item = $this->getMockBuilder( \stdClass::class )
->addMethods( [ 'get_type_label', 'get_type_class' ] )
->addMethods( ['get_type_label', 'get_type_class'] )
->getMock();
$item->method( 'get_type_label' )->willReturn( 'Plan' );
$item->method( 'get_type_class' )->willReturn( 'wu-bg-blue-200' );
Expand All @@ -176,7 +176,7 @@ public function test_column_type_returns_span_with_label(): void {
public function test_column_slug_returns_slug_in_span(): void {

$item = $this->getMockBuilder( \stdClass::class )
->addMethods( [ 'get_slug' ] )
->addMethods( ['get_slug'] )
->getMock();
$item->method( 'get_slug' )->willReturn( 'pro-plan' );

Expand All @@ -197,7 +197,7 @@ public function test_column_slug_returns_slug_in_span(): void {
public function test_column_amount_returns_free_for_zero_amount(): void {

$item = $this->getMockBuilder( \stdClass::class )
->addMethods( [ 'get_pricing_type', 'get_amount', 'is_recurring' ] )
->addMethods( ['get_pricing_type', 'get_amount', 'is_recurring'] )
->getMock();
$item->method( 'get_pricing_type' )->willReturn( 'paid' );
$item->method( 'get_amount' )->willReturn( 0 );
Expand All @@ -213,7 +213,7 @@ public function test_column_amount_returns_free_for_zero_amount(): void {
public function test_column_amount_returns_none_for_contact_us(): void {

$item = $this->getMockBuilder( \stdClass::class )
->addMethods( [ 'get_pricing_type', 'get_amount', 'is_recurring' ] )
->addMethods( ['get_pricing_type', 'get_amount', 'is_recurring'] )
->getMock();
$item->method( 'get_pricing_type' )->willReturn( 'contact_us' );

Expand All @@ -229,7 +229,7 @@ public function test_column_amount_returns_none_for_contact_us(): void {
public function test_column_amount_returns_one_time_for_non_recurring(): void {

$item = $this->getMockBuilder( \stdClass::class )
->addMethods( [ 'get_pricing_type', 'get_amount', 'is_recurring', 'get_currency' ] )
->addMethods( ['get_pricing_type', 'get_amount', 'is_recurring', 'get_currency'] )
->getMock();
$item->method( 'get_pricing_type' )->willReturn( 'paid' );
$item->method( 'get_amount' )->willReturn( 50 );
Expand All @@ -251,7 +251,7 @@ public function test_column_amount_returns_one_time_for_non_recurring(): void {
public function test_column_setup_fee_returns_no_setup_fee(): void {

$item = $this->getMockBuilder( \stdClass::class )
->addMethods( [ 'get_pricing_type', 'has_setup_fee' ] )
->addMethods( ['get_pricing_type', 'has_setup_fee'] )
->getMock();
$item->method( 'get_pricing_type' )->willReturn( 'paid' );
$item->method( 'has_setup_fee' )->willReturn( false );
Expand All @@ -267,12 +267,44 @@ public function test_column_setup_fee_returns_no_setup_fee(): void {
public function test_column_setup_fee_returns_none_for_contact_us(): void {

$item = $this->getMockBuilder( \stdClass::class )
->addMethods( [ 'get_pricing_type', 'has_setup_fee' ] )
->addMethods( ['get_pricing_type', 'has_setup_fee'] )
->getMock();
$item->method( 'get_pricing_type' )->willReturn( 'contact_us' );

$output = $this->table->column_setup_fee( $item );

$this->assertStringContainsString( 'None', $output );
}

/**
* Test duplicate_product creates a persisted editable copy.
*/
public function test_duplicate_product_creates_persisted_copy(): void {

$product = wu_create_product(
[
'name' => 'Duplicate Action Product',
'slug' => 'duplicate-action-product-' . wp_rand(),
'type' => 'plan',
'amount' => 49.99,
'skip_validation' => true,
]
);

$this->assertNotWPError( $product );

$table = new class() extends Product_List_Table {
public function duplicate_product_for_test($product_id) {
return $this->duplicate_product( $product_id );
}
};

$duplicate = $table->duplicate_product_for_test( $product->get_id() );

$this->assertNotWPError( $duplicate );
$this->assertNotSame( $product->get_id(), $duplicate->get_id() );
$this->assertSame( 'Copy of Duplicate Action Product', $duplicate->get_name() );
$this->assertSame( 'plan', $duplicate->get_type() );
$this->assertNotFalse( wu_get_product( $duplicate->get_id() ) );
}
}
Loading