/var/www/html/wp-content/plugins/woocommerce/includes/admin/class-wc-admin-addons.php


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
<?php
/**
 * Addons Page
 *
 * @package  WooCommerce\Admin
 * @version  2.5.0
 */

use Automattic\Jetpack\Constants;
use 
Automattic\WooCommerce\Admin\RemoteInboxNotifications as PromotionRuleEngine;
use 
Automattic\WooCommerce\Admin\RemoteSpecs\RuleProcessors\RuleEvaluator;

if ( ! 
defined'ABSPATH' ) ) {
    exit;
}

/**
 * WC_Admin_Addons Class.
 */
class WC_Admin_Addons {

    
/**
     * Fetch featured products from WCCOM's the Featured 3.0 Endpoint and cache the data for a day.
     *
     * @return array|WP_Error
     */
    
public static function fetch_featured() {
        
$transient_name 'wc_addons_featured';
        
// Important: WCCOM Extensions API v3.0 is used.
        
$url      'https://woocommerce.com/wp-json/wccom-extensions/3.0/featured';
        
$locale   get_user_locale();
        
$featured self::get_locale_data_from_transient$transient_name$locale );

        if ( 
false === $featured ) {
            
$fetch_options = array(
                
'auth'    => true,
                
'locale'  => true,
                
'country' => true,
            );
            
$raw_featured  self::fetch$url$fetch_options );

            if ( 
is_wp_error$raw_featured ) ) {
                
do_action'woocommerce_page_wc-addons_connection_error'$raw_featured->get_error_message() );

                
$message self::is_ssl_error$raw_featured->get_error_message() )
                    ? 
__'We encountered an SSL error. Please ensure your site supports TLS version 1.2 or above.''woocommerce' )
                    : 
$raw_featured->get_error_message();

                return new 
WP_Error'wc-addons-connection-error'$message );
            }

            
$response_code = (int) wp_remote_retrieve_response_code$raw_featured );
            if ( 
200 !== $response_code ) {
                
do_action'woocommerce_page_wc-addons_connection_error'$response_code );

                
/* translators: %d: HTTP error code. */
                
$message sprintf(
                    
esc_html(
                        
/* translators: Error code  */
                        
__(
                            
'Our request to the featured API got error code %d.',
                            
'woocommerce'
                        
)
                    ),
                    
$response_code
                
);

                return new 
WP_Error'wc-addons-connection-error'$message );
            }

            
$featured json_decodewp_remote_retrieve_body$raw_featured ) );
            if ( empty( 
$featured ) || ! is_array$featured ) ) {
                
do_action'woocommerce_page_wc-addons_connection_error''Empty or malformed response' );
                
$message __'Our request to the featured API got a malformed response.''woocommerce' );

                return new 
WP_Error'wc-addons-connection-error'$message );
            }

            if ( 
$featured ) {
                
self::set_locale_data_in_transient$transient_name$featured$localeDAY_IN_SECONDS );
            }
        }

        return 
$featured;
    }

    
/**
     * Check if the error is due to an SSL error
     *
     * @param string $error_message Error message.
     *
     * @return bool True if SSL error, false otherwise
     */
    
public static function is_ssl_error$error_message ) {
        return 
false !== stripos$error_message'cURL error 35' );
    }

    
/**
     * Get sections for the addons screen
     *
     * @return array of objects
     */
    
public static function get_sections() {
        
$locale         get_user_locale();
        
$addon_sections self::get_locale_data_from_transient'wc_addons_sections'$locale );
        if ( 
false === ( $addon_sections ) ) {
            
$parameter_string '?' http_build_query( array( 'locale' => get_user_locale() ) );
            
$raw_sections     wp_safe_remote_get(
                
'https://woocommerce.com/wp-json/wccom-extensions/1.0/categories' $parameter_string,
                array(
                    
'user-agent' => 'WooCommerce/' WC()->version '; ' get_bloginfo'url' ),
                )
            );
            if ( ! 
is_wp_error$raw_sections ) ) {
                
$addon_sections json_decodewp_remote_retrieve_body$raw_sections ) );
                if ( 
$addon_sections ) {
                    
self::set_locale_data_in_transient'wc_addons_sections'$addon_sections$localeWEEK_IN_SECONDS );
                }
            }
        }
        return 
apply_filters'woocommerce_addons_sections'$addon_sections );
    }

    
/**
     * Get section for the addons screen.
     *
     * @param  string $section_id Required section ID.
     *
     * @return object|bool
     */
    
public static function get_section$section_id ) {
        
$sections self::get_sections();
        if ( isset( 
$sections$section_id ] ) ) {
            return 
$sections$section_id ];
        }
        return 
false;
    }

    
/**
     * Returns in-app-purchase URL params.
     */
    
public static function get_in_app_purchase_url_params() {
        
// Get url (from path onward) for the current page,
        // so WCCOM "back" link returns user to where they were.
        
$back_admin_path add_query_arg( array() );
        return array(
            
'wccom-site'          => site_url(),
            
'wccom-back'          => rawurlencode$back_admin_path ),
            
'wccom-woo-version'   => Constants::get_constant'WC_VERSION' ),
            
'wccom-connect-nonce' => wp_create_nonce'connect' ),
        );
    }

    
/**
     * Add in-app-purchase URL params to link.
     *
     * Adds various url parameters to a url to support a streamlined
     * flow for obtaining and setting up WooCommerce extensons.
     *
     * @param string $url    Destination URL.
     */
    
public static function add_in_app_purchase_url_params$url ) {
        return 
add_query_arg(
            
self::get_in_app_purchase_url_params(),
            
$url
        
);
    }

    
/**
     * Outputs a button.
     *
     * @param string $url    Destination URL.
     * @param string $text   Button label text.
     * @param string $style  Button style class.
     * @param string $plugin The plugin the button is promoting.
     */
    
public static function output_button$url$text$style$plugin '' ) {
        
$style __'Free''woocommerce' ) === $text 'addons-button-outline-purple' $style;
        
$style is_plugin_active$plugin ) ? 'addons-button-installed' $style;
        
$text  is_plugin_active$plugin ) ? __'Installed''woocommerce' ) : $text;
        
$url   self::add_in_app_purchase_url_params$url );
        
?>
        <a
            class="addons-button <?php echo esc_attr$style ); ?>"
            href="<?php echo esc_url$url ); ?>">
            <?php echo esc_html$text ); ?>
        </a>
        <?php
    
}

    
/**
     * Process requests to legacy marketplace menu and redirect to correct in-app pages.
     *
     * @return void
     */
    
public static function handle_legacy_marketplace_redirects() {
        
$section = isset( $_GET['section'] ) ? sanitize_text_fieldwp_unslash$_GET['section'] ) ) : '_featured';
        
$search  = isset( $_GET['search'] ) ? sanitize_text_fieldwp_unslash$_GET['search'] ) ) : '';

        if ( 
'helper' === $section ) {
            
$url admin_url'admin.php?page=wc-admin&tab=my-subscriptions&path=%2Fextensions' );

            if ( isset( 
$_GET['connect'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
                
$url .= '&connect';
            }

            
wp_safe_redirect$url );
            exit();
        }

        if ( 
'search' === $section || ! empty( $search ) ) {
            
wp_safe_redirectadmin_url'admin.php?page=wc-admin&term=' $search '&tab=search&path=%2Fextensions' ) );
            exit();
        }

        
$sections         self::get_sections();
        
$allowed_sections array_map( fn( $section_object ) => $section_object->slug$sections );
        
// Validate if the category is supported.
        
$section in_array$section$allowed_sectionstrue ) ? $section '_featured';

        if ( 
'_featured' === $section ) {
            
wp_safe_redirectadmin_url'admin.php?page=wc-admin&path=%2Fextensions' ) );
            exit();
        }

        
wp_safe_redirectadmin_url'admin.php?page=wc-admin&tab=extensions&path=%2Fextensions&category=' $section ) );
        exit();
    }

    
/**
     * We're displaying page=wc-addons and page=wc-addons&section=helper as two separate pages.
     * When we're on those pages, add body classes to distinguishe them.
     *
     * @param string $admin_body_class Unfiltered body class.
     *
     * @return string Body class with added class for Marketplace or My Subscriptions page.
     */
    
public static function filter_admin_body_classesstring $admin_body_class '' ): string {
        if ( isset( 
$_GET['section'] ) && 'helper' === $_GET['section'] ) {
            return 
$admin_body_class woocommerce-page-wc-subscriptions ";
        }

        return 
$admin_body_class woocommerce-page-wc-marketplace ";
    }

    
/**
     * Take an action object and return the URL based on properties of the action.
     *
     * @param object $action Action object.
     * @return string URL.
     */
    
public static function get_action_url$action ): string {
        if ( ! isset( 
$action->url ) ) {
            return 
'';
        }

        if ( isset( 
$action->url_is_admin_query ) && $action->url_is_admin_query ) {
            return 
wc_admin_url$action->url );
        }

        if ( isset( 
$action->url_is_admin_nonce_query ) && $action->url_is_admin_nonce_query ) {
            if ( empty( 
$action->nonce ) ) {
                return 
'';
            }
            return 
wp_nonce_url(
                
admin_url$action->url ),
                
$action->nonce
            
);
        }

        return 
$action->url;
    }

    
/**
     * Retrieves the locale data from a transient.
     *
     * Transient value is an array of locale data in the following format:
     * array(
     *    'en_US' => ...,
     *    'fr_FR' => ...,
     * )
     *
     * If the transient does not exist, does not have a value, or has expired,
     * then the return value will be false.
     *
     * @param string $transient Transient name. Expected to not be SQL-escaped.
     * @param string $locale  Locale to retrieve.
     * @return mixed Value of transient.
     */
    
private static function get_locale_data_from_transient$transient$locale ) {
        
$transient_value get_transient$transient );
        
$transient_value is_array$transient_value ) ? $transient_value : array();
        return 
$transient_value$locale ] ?? false;
    }

    
/**
     * Sets the locale data in a transient.
     *
     * Transient value is an array of locale data in the following format:
     * array(
     *    'en_US' => ...,
     *    'fr_FR' => ...,
     * )
     *
     * @param string $transient  Transient name. Expected to not be SQL-escaped.
     *                           Must be 172 characters or fewer in length.
     * @param mixed  $value      Transient value. Must be serializable if non-scalar.
     *                           Expected to not be SQL-escaped.
     * @param string $locale  Locale to set.
     * @param int    $expiration Optional. Time until expiration in seconds. Default 0 (no expiration).
     * @return bool True if the value was set, false otherwise.
     */
    
private static function set_locale_data_in_transient$transient$value$locale$expiration ) {
        
$transient_value            get_transient$transient );
        
$transient_value            is_array$transient_value ) ? $transient_value : array();
        
$transient_value$locale ] = $value;
        return 
set_transient$transient$transient_value$expiration );
    }

    
/**
     * Make wp_safe_remote_get request to WooCommerce.com endpoint.
     * Optionally pass user auth token, locale or country.
     *
     * @param string $url     URL to request.
     * @param ?array $options Options for the request. For example, to pass auth token, locale and country,
     *                        pass array( 'auth' => true, 'locale' => true, 'country' => true, ).
     *
     * @return array|WP_Error
     */
    
public static function fetch$url$options = array() ) {
        
$headers = array();

        if ( isset( 
$options['auth'] ) && $options['auth'] ) {
            
$auth WC_Helper_Options::get'auth' );

            if ( isset( 
$auth['access_token'] ) && ! empty( $auth['access_token'] ) ) {
                
$headers['Authorization'] = 'Bearer ' $auth['access_token'];
            }
        }

        
$parameters = array();

        if ( isset( 
$options['locale'] ) && $options['locale'] ) {
            
$parameters['locale'] = get_user_locale();
        }

        if ( isset( 
$options['country'] ) && $options['country'] ) {
            
$country WC()->countries->get_base_country();
            if ( ! empty( 
$country ) ) {
                
$parameters['country'] = $country;
            }
        }

        
$query_string = ! empty( $parameters ) ? '?' http_build_query$parameters ) : '';

        return 
wp_safe_remote_get(
            
$url $query_string,
            array(
                
'headers'    => $headers,
                
'user-agent' => 'WooCommerce/' WC()->version '; ' get_bloginfo'url' ),
            )
        );
    }
}