Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release 3.3.3 #2868

Merged
merged 18 commits into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
7 changes: 5 additions & 2 deletions .github/workflows/php-unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ concurrency:

jobs:
UnitTests:
name: PHP unit tests - PHP ${{ matrix.php }}, WP ${{ matrix.wp-version }}
name: PHP unit tests - PHP ${{ matrix.php }}, WP ${{ matrix.wp-version }}, WC ${{ matrix.wc-version }}
runs-on: ubuntu-latest
env:
WP_CORE_DIR: "/tmp/wordpress/src"
Expand All @@ -32,6 +32,7 @@ jobs:
matrix:
php: [7.4, 8.3]
wp-version: [latest]
wc-version: [latest]

steps:
- name: Install SVN
Expand All @@ -49,7 +50,9 @@ jobs:
uses: woocommerce/grow/prepare-mysql@actions-v1

- name: Install WP tests
run: ./bin/install-wp-tests.sh wordpress_test root root localhost ${{ matrix.wp-version }}
run: |
chmod +x ./bin/install-wp-tests.sh
./bin/install-wp-tests.sh wordpress_test root root localhost ${{ matrix.wp-version }} ${{ matrix.wc-version }}

- name: Run PHP unit tests
run: composer test-unit
938,056 changes: 938,055 additions & 1 deletion bin/fb_google_category_to_attribute_mapping.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* Tweak - Add custom fields to product data structure by @devbodaghe in #2836
* Fix - Multiple issues with Fixed FB Attribute Syncing by @vinkmeta in #2860

= 3.3.1 - 2024-12-06 =
= 3.3.1 - 2024-12-06 =
* Fix - Sale price effective date by @vinkmeta in #2809
* Fix - Sync stock quantity when available by @mshymon in #2811
* Fix - Sync product GTIN when available by @mshymon in #2810
Expand Down
22 changes: 22 additions & 0 deletions facebook-commerce.php
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,7 @@ public function on_product_save( int $wp_id ) {

$products_to_delete_from_facebook = $this->get_removed_from_sync_products_to_delete();
if ( $product->is_type( 'variable' ) ) {
$this->save_variable_product_settings( $product );
// check variations for deletion
foreach ( $products_to_delete_from_facebook as $delete_product_id ) {
$delete_product = wc_get_product( $delete_product_id );
Expand Down Expand Up @@ -841,6 +842,19 @@ public function on_product_save( int $wp_id ) {
}
}

/**
* Saves the submitted Facebook settings for a variable product.
*
*
* @param \WC_Product $product The variable product object.
*/
private function save_variable_product_settings( WC_Product $product ) {
$woo_product = new WC_Facebook_Product( $product->get_id() );
if ( isset( $_POST[ WC_Facebook_Product::FB_VARIABLE_BRAND ] ) ) {
$woo_product->set_fb_brand( sanitize_text_field( wp_unslash( $_POST[ WC_Facebook_Product::FB_VARIABLE_BRAND ] ) ) );
}
}

/**
* Saves the submitted Facebook settings for a product.
*
Expand Down Expand Up @@ -868,6 +882,14 @@ private function save_product_settings( WC_Product $product ) {
if ( isset( $_POST[ WC_Facebook_Product::FB_PRODUCT_IMAGE ] ) ) {
$woo_product->set_product_image( sanitize_text_field( wp_unslash( $_POST[ WC_Facebook_Product::FB_PRODUCT_IMAGE ] ) ) );
}

if ( isset( $_POST[ WC_Facebook_Product::FB_BRAND ] ) ) {
$woo_product->set_fb_brand( sanitize_text_field( wp_unslash( $_POST[ WC_Facebook_Product::FB_BRAND ] ) ) );
}

if ( isset( $_POST[ WC_Facebook_Product::FB_MPN ] ) ) {
$woo_product->set_fb_mpn( sanitize_text_field( wp_unslash( $_POST[ WC_Facebook_Product::FB_MPN ] ) ) );
}
// phpcs:enable WordPress.Security.NonceVerification.Missing
}

Expand Down
13 changes: 6 additions & 7 deletions includes/API.php
Original file line number Diff line number Diff line change
Expand Up @@ -250,18 +250,17 @@ public function get_user( string $user_id = '' ): API\User\Response {


/**
* Deletes user API permission.
* Deletes FBE/MBE connection API.
*
* This is their form of "revoke".
*
* @param string $user_id user ID. Defaults to the currently authenticated user
* @param string $permission permission to delete
* @return API\Response|API\User\Permissions\Delete\Response
* @param string $external_business_id external business ID
* @return API\Response|API\FBE\Installation\Delete\Response
* @throws ApiException
*/
public function delete_user_permission( string $user_id, string $permission ): API\User\Permissions\Delete\Response {
$request = new API\User\Permissions\Delete\Request( $user_id, $permission );
$this->set_response_handler( API\User\Permissions\Delete\Response::class );
public function delete_mbe_connection( string $external_business_id): API\FBE\Installation\Delete\Response {
$request = new API\FBE\Installation\Delete\Request( $external_business_id);
$this->set_response_handler( API\FBE\Installation\Delete\Response::class );
return $this->perform_request( $request );
}

Expand Down
35 changes: 35 additions & 0 deletions includes/API/FBE/Installation/Delete/Request.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*
* @package FacebookCommerce
*/

namespace WooCommerce\Facebook\API\FBE\Installation\Delete;

defined( 'ABSPATH' ) || exit;

use WooCommerce\Facebook\API\FBE\Installation;

/**
* FBE installation API delete request object.
*
* @since 3.3.3
*/
class Request extends Installation\Request {
/**
* API request constructor.
*
* @since 3.3.3
*
* @param string $external_business_id external business_id
*/
public function __construct( $external_business_id ) {
// include the business ID in the request body
parent::__construct( 'fbe_installs', 'DELETE' );
$this->data['fbe_external_business_id'] = $external_business_id;
}
}
13 changes: 13 additions & 0 deletions includes/API/FBE/Installation/Delete/Response.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php
declare( strict_types=1 );

namespace WooCommerce\Facebook\API\FBE\Installation\Delete;

defined( 'ABSPATH' ) || exit;

use WooCommerce\Facebook\API;

/**
* FBE Installation API delete response object.
*/
class Response extends API\Response {}
49 changes: 49 additions & 0 deletions includes/Admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -1185,6 +1185,8 @@ public function add_product_settings_tab_content() {
$price = get_post_meta( $post->ID, \WC_Facebook_Product::FB_PRODUCT_PRICE, true );
$image_source = get_post_meta( $post->ID, Products::PRODUCT_IMAGE_SOURCE_META_KEY, true );
$image = get_post_meta( $post->ID, \WC_Facebook_Product::FB_PRODUCT_IMAGE, true );
$fb_brand = get_post_meta( $post->ID, \WC_Facebook_Product::FB_BRAND, true ) ? get_post_meta( $post->ID, \WC_Facebook_Product::FB_BRAND, true ) : get_post_meta( $post->ID, '_wc_facebook_enhanced_catalog_attributes_brand', true );
$fb_mpn = get_post_meta( $post->ID, \WC_Facebook_Product::FB_MPN, true );

if ( $sync_enabled ) {
$sync_mode = $is_visible ? self::SYNC_MODE_SYNC_AND_SHOW : self::SYNC_MODE_SYNC_AND_HIDE;
Expand Down Expand Up @@ -1270,14 +1272,46 @@ public function add_product_settings_tab_content() {
)
);

woocommerce_wp_text_input(
array(
'id' => \WC_Facebook_Product::FB_BRAND,
'label' => __( 'Brand', 'facebook-for-woocommerce' ),
'value' => $fb_brand,
'class' => 'enable-if-sync-enabled',
)
);

woocommerce_wp_text_input(
array(
'id' => \WC_Facebook_Product::FB_MPN,
'label' => __( 'Manufacturer Parts Number (MPN)', 'facebook-for-woocommerce' ),
'value' => $fb_mpn,
'class' => 'enable-if-sync-enabled',
)
);

woocommerce_wp_hidden_input(
array(
'id' => \WC_Facebook_Product::FB_REMOVE_FROM_SYNC,
'value' => '',
)
);
?>
</div>
<div class='options_group show_if_variable'>
<?php
woocommerce_wp_text_input(
array(
'id' => \WC_Facebook_Product::FB_VARIABLE_BRAND,
'label' => __( 'Brand', 'facebook-for-woocommerce' ),
'value' => $fb_brand,
'class' => 'enable-if-sync-enabled',
)
);
?>
</div>


<div class='wc-facebook-commerce-options-group options_group'>
<?php \WooCommerce\Facebook\Admin\Products::render_google_product_category_fields_and_enhanced_attributes( $product ); ?>
</div>
Expand Down Expand Up @@ -1318,6 +1352,7 @@ public function add_product_variation_edit_fields( $index, $variation_data, $pos
$price = $this->get_product_variation_meta( $variation, \WC_Facebook_Product::FB_PRODUCT_PRICE, $parent );
$image_url = $this->get_product_variation_meta( $variation, \WC_Facebook_Product::FB_PRODUCT_IMAGE, $parent );
$image_source = $variation->get_meta( Products::PRODUCT_IMAGE_SOURCE_META_KEY );
$fb_mpn = $this->get_product_variation_meta( $variation, \WC_Facebook_Product::FB_MPN, $parent );

if ( $sync_enabled ) {
$sync_mode = $is_visible ? self::SYNC_MODE_SYNC_AND_SHOW : self::SYNC_MODE_SYNC_AND_HIDE;
Expand Down Expand Up @@ -1405,6 +1440,17 @@ public function add_product_variation_edit_fields( $index, $variation_data, $pos
'wrapper_class' => 'form-row form-full',
)
);

woocommerce_wp_text_input(
array(
'id' => sprintf( 'variable_%s%s', \WC_Facebook_Product::FB_MPN, $index ),
'name' => sprintf( "variable_%s[$index]", \WC_Facebook_Product::FB_MPN ),
'label' => __( 'Manufacturer Parts Number (MPN)', 'facebook-for-woocommerce' ),
'value' => $fb_mpn,
'class' => 'enable-if-sync-enabled',
)
);

}


Expand Down Expand Up @@ -1456,6 +1502,8 @@ public function save_product_variation_edit_fields( $variation_id, $index ) {
Products::set_product_visibility( $variation, self::SYNC_MODE_SYNC_AND_HIDE !== $sync_mode );
$posted_param = 'variable_' . \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION;
$description = isset( $_POST[ $posted_param ][ $index ] ) ? sanitize_text_field( wp_unslash( $_POST[ $posted_param ][ $index ] ) ) : null;
$posted_param = 'variable_' . \WC_Facebook_Product::FB_MPN;
$fb_mpn = isset( $_POST[ $posted_param ][ $index ] ) ? sanitize_text_field( wp_unslash( $_POST[ $posted_param ][ $index ] ) ) : null;
$posted_param = 'variable_fb_product_image_source';
$image_source = isset( $_POST[ $posted_param ][ $index ] ) ? sanitize_key( wp_unslash( $_POST[ $posted_param ][ $index ] ) ) : '';
$posted_param = 'variable_' . \WC_Facebook_Product::FB_PRODUCT_IMAGE;
Expand All @@ -1464,6 +1512,7 @@ public function save_product_variation_edit_fields( $variation_id, $index ) {
$price = isset( $_POST[ $posted_param ][ $index ] ) ? wc_format_decimal( wc_clean( wp_unslash( $_POST[ $posted_param ][ $index ] ) ) ) : '';
$variation->update_meta_data( \WC_Facebookcommerce_Integration::FB_PRODUCT_DESCRIPTION, $description );
$variation->update_meta_data( Products::PRODUCT_IMAGE_SOURCE_META_KEY, $image_source );
$variation->update_meta_data( \WC_Facebook_Product::FB_MPN, $fb_mpn );
$variation->update_meta_data( \WC_Facebook_Product::FB_PRODUCT_IMAGE, $image_url );
$variation->update_meta_data( \WC_Facebook_Product::FB_PRODUCT_PRICE, $price );
$variation->save_meta_data();
Expand Down
2 changes: 1 addition & 1 deletion includes/Admin/Enhanced_Catalog_Attribute_Fields.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class Enhanced_Catalog_Attribute_Fields {
*/
private $category_handler;

public function __construct( $page_type, \WP_Term $term = null, \WC_Product $product = null ) {
public function __construct( $page_type, ?\WP_Term $term = null, ?\WC_Product $product = null ) {
$this->page_type = $page_type;
$this->term = $term;
$this->product = $product;
Expand Down
11 changes: 6 additions & 5 deletions includes/Admin/Settings_Screens/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -288,13 +288,14 @@ private function render_facebook_box( $is_connected ) {

<?php if ( $is_connected ) : ?>

<a href="<?php echo esc_url( facebook_for_woocommerce()->get_connection_handler()->get_manage_url() ); ?>" class="button button-primary">
<?php esc_html_e( 'Manage Connection', 'facebook-for-woocommerce' ); ?>
</a>

<a href="<?php echo esc_url( facebook_for_woocommerce()->get_connection_handler()->get_disconnect_url() ); ?>" class="uninstall">
<a href="<?php echo esc_url( facebook_for_woocommerce()->get_connection_handler()->get_disconnect_url() ); ?>" class="button button-primary uninstall" onclick="return confirmDialog();">
<?php esc_html_e( 'Disconnect', 'facebook-for-woocommerce' ); ?>
</a>
<script>
function confirmDialog() {
return confirm( 'Are you sure you want to disconnect from Facebook?' );
}
</script>

<?php else : ?>

Expand Down
33 changes: 14 additions & 19 deletions includes/Handlers/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -381,13 +381,22 @@ public function handle_disconnect() {
wp_die( esc_html__( 'You do not have permission to uninstall Facebook Business Extension.', 'facebook-for-woocommerce' ) );
}
try {
$response = facebook_for_woocommerce()->get_api()->get_user();
$id = $response->get_id();
if ( null !== $id ) {
$response = facebook_for_woocommerce()->get_api()->delete_user_permission( (string) $id , 'manage_business_extension' );
$external_business_id = $this->get_external_business_id();
if ( null != $external_business_id ) {
$response = facebook_for_woocommerce()->get_api()->delete_mbe_connection((string) $external_business_id );
facebook_for_woocommerce()->get_message_handler()->add_message( __( 'Disconnection successful.', 'facebook-for-woocommerce' ) );

$body = wp_remote_retrieve_body( $response );
$body = json_decode( $body, true );
if ( ! is_array( $body ) || empty( $body['data'] ) || 200 !== (int) wp_remote_retrieve_response_code( $response ) ) {
facebook_for_woocommerce()->log( 'Failed to disconnect' );
facebook_for_woocommerce()->log( print_r( $body, true ) );
throw new ApiException(
sprintf(wp_remote_retrieve_response_message( $response ))
);
}
} else {
facebook_for_woocommerce()->log( 'User id not found for the disconnection procedure, connection will be reset.' );
facebook_for_woocommerce()->log( 'External business id not found for the disconnection procedure, connection will be reset.' );
}
} catch ( ApiException $exception ) {
facebook_for_woocommerce()->log( sprintf( 'An error occurred during disconnection: %s.', $exception->getMessage() ) );
Expand Down Expand Up @@ -592,20 +601,6 @@ public function get_commerce_connect_url() {
}


/**
* Gets the URL to manage the connection.
*
* @since 2.0.0
*
* @return string
*/
public function get_manage_url() {
$app_id = $this->get_client_id();
$business_id = $this->get_external_business_id();
return "https://www.facebook.com/facebook_business_extension?app_id={$app_id}&external_business_id={$business_id}";
}


/**
* Gets the URL for disconnecting.
*
Expand Down
11 changes: 10 additions & 1 deletion includes/Products/FBCategories.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
*/
class FBCategories {

private $keys_to_exclude = ['brand' => true];

/**
* Fetches the attribute from a category using attribute key.
*
Expand Down Expand Up @@ -129,7 +131,14 @@ public function get_attributes( $category_id ) {
$return_attributes = array();
foreach ( $category['attributes'] as $attribute_hash ) {
// Get attribute array from the stored hash version
$return_attributes[] = $this->get_attribute_field_by_hash( $attribute_hash );
$attribute = $this->get_attribute_field_by_hash( $attribute_hash );

// Skip if attribute is invalid or its key is in the exclude list
if ( ! is_array( $attribute ) || empty( $attribute['key'] ) || isset( $this->keys_to_exclude[ $attribute['key'] ] ) ) {
continue;
}

$return_attributes[] = $attribute;
}

return $return_attributes;
Expand Down
Loading